1.必知必会的可变可变VGG网络(含代码)
2.OpenCV中几种卷积的实现方式
3.更灵活、有个性的卷积积代卷积——可变形卷积(Deformable Conv)
4.3d稀疏卷积——spconv源码剖析(一)
5.3d稀疏卷积——spconv源码剖析(五)
6.FCOS:论文与源码解读
必知必会的VGG网络(含代码)
牛津大学的视觉几何组设计的VGGNet,一种经典卷积神经网络架构,源码曾在年ILSVRC分类任务中获得第二名。形卷现今,可变可变VGG依然广泛应用于图像识别、卷积积代点赞熊源码语音识别、源码机器翻译、形卷机器人等领域。可变可变VGG包含层(VGG-)和层(VGG-),卷积积代结构相似,源码由个卷积层和3个全连接层组成。形卷与之前网络相比,可变可变VGG采用3*3卷积核替代7x7卷积核,卷积积代2*3卷积核替代5*5卷积核,源码以减少参数,提升深度。
VGG-的结构图显示,包含conv(卷积层)、pool(池化层)和最后三个fc(全连接层)。VGG通过减少参数量,使得网络结构更加紧凑,从而提升模型的性能。
VGG-采用五组卷积与三个全连接层,最后使用Softmax进行分类。每个卷积层的参数量通过公式计算得出。特征图计算公式为输出图像大小(O)等于(输入图像大小(I)+2*填充(P)-卷积核大小(K))/步长(S)+1。
VGG-的代码实现可以通过构建一个Layer类,通过循环添加每个层的顺序执行来实现。具体代码可在关注公众号CV算法恩仇录后,回复VGG源码获取。
了解更多关于VGG的细节,请参阅相关链接:《VGG网络细节》 shimo.im/docs/dPkpKKErv...、《VGG网络》 blog.csdn.net/weixin_...
深入理解VGG,可参考《一文读懂VGG》/s/vWuGW4iMD1MjVDZVCqH_FA。
OpenCV中几种卷积的实现方式
自从opencv引入dnn模块后,卷积实现方式不断扩展,以适应PC、手机、边缘计算设备的部署需求。目前,可调用CUDA、OpenCL、Tengine、Vulkan实现卷积。Tengine、jadx源码阅读Vulkan特别适用于移动设备和边缘计算,它们内部是如何实现的?
Vulkan是一个渲染库,与OpenGL、DirectX等GPU渲染库相比,移动设备上使用较多,而深度学习模型又需要在移动设备上部署。因此,探索是否可以使用Vulkan实现卷积等深度学习操作。
接下来,让我们看看OpenCV是如何使用Vulkan实现深度神经网络中的卷积。
打开OpenCV源码库的modules/dnn/src目录,可以看到最后一个文件夹是vkcom。"vkcom"这个名字由"Vulkan"库本身与"comp"(glsl语言的源代码后缀)组成。glsl语言可以通过以下命令编译:“vkcom”。GLSL是OpenGL着色语言,用于编写OpenGL着色器的编程语言,通常与并行处理功能强大的GPU结合使用。深度学习操作如卷积、池化都是对图像颜色的处理,因此可以将这些操作实现为着色器,用GLSL编写,然后使用Vulkan调用GPU。
Vulkan实现的卷积代码示例如下:
代码中指定了输入输出变量(第3、6、9、行)。在第行计算了输出变量convolved_image_data的值。第行开始的for循环遍历卷积核的c、w、h,计算单个像素位置的卷积结果。显然,这个卷积仅计算一个像素位置的卷积结果,卷积核的滑动过程由Vulkan管理GPU,多个GPU计算单元并行完成。
在OpenCV中,文件conv.comp首先被编译为二进制,然后将此二进制作为字符串放入conv_spv.cpp中。cpp文件定义了conv_spv数组,其中包含编译后的卷积着色器执行代码。由OpBase::createShaderModule函数将此二进制送入vkCreateShaderModule,从而调度GPU。
通过分析代码,可以看到Vulkan实现的算子被调用的方式,这同样适用于CUDA、钻石狂潮源码OpenCL、Ngraph、Inference Engine等实现的算子。
Vulkan渲染库在OpenCV中的调用逻辑已经阐述完毕。Tengine是如何使用的?在convolution_layer.cpp的forward函数的行,调用了tengine_forward(tengine_graph)。
Tengine_forward来自teng_run_graph函数,我们只需调用库即可得到结果。传入的graph是卷积图,由create_conv_graph在第行创建。create_conv_graph使用create_conv_node、create_input_node生成卷积算子所需的图。
使用Tengine相对使用Vulkan、CUDA等库完成算子,要简单许多。调用库内的函数生成节点,使用节点构建图即可,无需自己实现算子内的计算。
本文概述了OpenCV中卷积实现方式的多样性,以下为总结:
本文详细分析了使用Vulkan用着色器实现卷积计算的方法及其调用路径,这个路径在分析其他类型实现时也很有用。本文还探讨了不同库算子的兼容性。当然,不同算子兼容还涉及更多细节,本文仅关注卷积forward函数的传递。
本文后半部分简要介绍了Tengine在OpenCV中的集成。发现集成过程相对简单,在convolution_layer.cpp中直接运行Tengine库构建的卷积计算图。这也表明,如果存在更好的边缘计算库,很容易集成到OpenCV中。
通过几天的分析,我们已经了解了OpenCL、Vulkan、Tengine的实现方式。可以预计,CUDA、Halide、Inference Engine nn、Inference Engine NGraph等实现也会类似。
更灵活、有个性的卷积——可变形卷积(Deformable Conv)
Deformable Conv:我是个会变形的个性boy
传统的卷积操作面临复杂形变物体时,效果可能不佳。为解决这一问题,Deformable Conv 出现了,jsp发送源码他灵活地引入了偏移量,使得感受野与物体形状更加贴近,无论物体如何形变,都能轻松应对。Deformable Conv 的大法在于为每个点引入偏移量,这使得输出特征图的每个点加上对应卷积核每个位置的相对坐标后,再加上自学习的偏移量。通过双线性插值,Deformable Conv 能够计算出非整数位置的像素值,最终实现可变形卷积操作。解析源码,我们看到常规操作中使用 nn.Module 的子类封装了可变形卷积,引入了可选参数 modulation,以及生成偏移量的卷积 p_conv 和实际进行卷积的卷积 conv。通过初始化权重和计算偏移后的位置,Deformable Conv 能够计算出每个位置的像素值,实现真正的卷积操作。总结,Deformable Conv 是处理复杂形变物体的有效方法,其源码解析让我们深入了解了这一技术的核心。感谢阅读,欢迎在评论区交流讨论!
3d稀疏卷积——spconv源码剖析(一)
本文主要阐述卷积的基本理论,并以spconv源码为例进行解析。首先,介绍2D与3D卷积的基础知识及其分类。随后,深入探讨3D稀疏卷积的工作原理。
2D卷积涉及卷积核在二维图像空间上的滑动操作。它分为单通道卷积与多通道卷积。单通道卷积在输入图像的单一通道上进行,得到特征图。多通道卷积在同一图像中不同通道上进行,每个通道得到一个对应的新通道,最终通过相加生成特征图。
3D卷积在此基础上扩展到三维空间,涉及单通道与多通道情况。三维单通道卷积在立方体上进行,而三维多通道卷积则处理拥有多个通道的三维图像。
2D与3D卷积计算涉及输入层、输出层与参数关系的数学公式。考虑偏置参数与计算量,FLOPS(浮点运算量)也在此阶段被计算。
稀疏卷积分为SC(Sparse Convolution)与VSC(Valid Sparse Convolution)两种类型。SC卷积计算激活站点并丢弃非激活站点,中国源码版权而VSC卷积在SC的基础上进行了简化。
卷积神经网络对三维点云数据处理时,面临计算量增加的问题,而SC与VSC卷积利用稀疏性实现高效处理。构建输入与输出哈希表,对点云数据进行快速访问。GetOffset()函数用于定位卷积操作的位置,Rulebook用于存储原子操作规则,指导稀疏卷积过程。
稀疏卷积的关键在于构建输入、输出哈希表以及建立两者之间的联系,实现对稀疏数据的有效处理。spconv库中的get_indice_pairs函数通过调用getIndicePairs实现这一过程。
3d稀疏卷积——spconv源码剖析(五)
介绍在构建的Rulebook指导下执行特定的稀疏卷积计算,关注于类SparseConvolution,其代码位于spconv/conv.py。
Fsp.indice_subm_conv和Fsp.indice_conv经过spconv/functional.py中的SubMConvFunction和SparseConvFunction对象转换,最终会调用spconv/ops.py模块中的indice_conv等函数。
专注于子流线卷积接口:indice_subm_conv,其代码位于spconv/functional.py。
通过Python接口调用底层C++函数可能不够直观,因此使用torch.autograd.Function封装算子底层调用,该类表示PyTorch中的可导函数,具备前向推理和反向传播实现时,即可作为普通PyTorch函数使用。
值得注意的是,Function类在模型部署中具有优势,若定义了symbolic静态方法,此Function在执行torch.onnx.export()时,可依据symbolic定义规则转换为ONNX算子。
apply方法是torch.autograd.Function的一部分,此方法负责在前向推理或反向传播时的调度工作。通过将indice_subm_conv = SubMConvFunction.apply简化为indice_subm_conv接口,简化了算子使用,屏蔽了SubMConvFunction的具体实现。
SubMConvFunction的前向传播方法forward调用spconv/ops.py的indice_conv函数。在src/spconv/all.cc文件中,通过PyTorch提供的OP Register对底层C++API进行注册。
通过torch.ops.load_library加载.so文件,使用torch.ops.spconv.indice_conv调用src/spconv/spconv_ops.cc文件中的indiceConv函数。
深入探索src/spconv/spconv_ops.cc文件中的indiceConv函数。
代写部分代码内容...
FCOS:论文与源码解读
FCOS:全称为全卷积单阶段目标检测,它在锚框自由领域中占有重要地位,与RetinaNet在锚框基础领域中地位相似。它沿用ResNet+FPN架构,通过实验证明,在相同backbone和neck层下,锚框自由方法可以取得比锚框基础方法更好的效果。 FCOS借鉴了语义分割的思想,成功地去除了锚框先验,实现了逐点的目标检测,是全卷积网在目标检测领域的延伸。代码比锚框基础类简单,非常适合入门。1. 动机
锚框基础类目标检测方法存在多处缺点,FCOS通过去除锚框,提出了简单、温柔且有力的目标检测模型。2. 创新点
FCOS借鉴了语义分割的思想,实现了去除锚框、逐点的目标检测。以年提出的全卷积网(FCN)为例,FCOS借鉴了FCN的思想,将其应用于目标检测,主要步骤包括生成先验、分配正负样本和设计bbox assigner。3. 模型整体结构与流程
训练时,包括生成先验和正负样本分配。FCOS的先验是将特征图上的每一点映射回原始图像,形成逐点对应关系。分配正负样本时,正样本表示预测目标,负样本表示背景。3.1 训练时
在训练阶段,先通过prior generate生成先验,然后进行bbox assign。在分配过程中,FCOS利用了FPN层解决ambigous点的问题,通过多尺度特征融合和逐层分配目标来解决。3.1.1 prior generate
FCOS通过映射特征图上的每一点回原始图像,形成点对点对应关系,生成先验。通过公式计算映射关系,其中s表示步长。3.1.2 bbox assigne
分配正负样本时,FCOS借鉴了anchor base方法的正负样本分配机制,通过设计bbox assigner解决ambigous点问题。分配流程包括计算输出值、对输出进行exp操作和引入可学习参数scale,以及使用FPN层分而治之,进一步解决ambigous问题。3.1.3 centerness
FCOS额外预测了centerness分支,以过滤远离目标中心的点,提高检测质量。centerness值范围为0~1,越靠近中心,值越大。测试时,最终score=cls_score*centerness。3.1.4 loss
损失函数包括focal loss、IoU loss和交叉熵损失,用于训练分类、定位和centerness分支。3.2 模型结构
模型继续沿用ResNet和FPN层,进行公平比较。FPN输出的特征层与RetinaNet类似,但FCOS在FPN输出的最后一层特征层上进行额外卷积,与RetinaNet在输入特征层上进行额外卷积不同。在推理阶段,注意centerness与分类分数的乘积作为最终得分,且需要进行NMS操作。4. 总结与未来方向
FCOS是一个简单、温柔、有力量的锚框自由方法,地位重要,思想借鉴于语义分割,流程类似传统目标检测,包括生成先验、正负样本匹配、bbox编码和NMS等,额外加入centerness分支以提升检测质量。 未来,FCOS的研究方向可能包括更深入的理论分析、模型优化和跨领域应用探索。5. 源码
mmdetection提供了FCOS的配置文件和代码实现,包括多个版本和改进。了解这些细节有助于深入理解FCOS的实现和优化策略。3d稀疏卷积——spconv源码剖析(三)
构建Rulebook
下面看ops.get_indice_pairs,位于:spconv/ops.py
构建Rulebook由ops.get_indice_pairs接口完成
get_indice_pairs函数具体实现:
主要就是完成了一些参数的校验和预处理。首先,对于3d普通稀疏卷积,根据输入shape大小,kernel size,stride等参数计算出输出输出shape,子流行稀疏卷积就不必计算了,输出shape和输入shape一样大小
准备好参数之后就进入最核心的get_indice_pairs函数。因为spconv通过torch.ops.load_library加载.so文件注册,所以这里通torch.ops.spconv.get_indice_pairs这种方式来调用该函数。
算子注册:在src/spconv/all.cc文件中通过Pytorch提供的OP Register(算子注册的方式)对底层c++ api进行了注册,可以python接口形式调用c++算子
同C++ extension方式一样,OP Register也是Pytorch提供的一种底层扩展算子注册的方式。注册的算子可以通过 torch.xxx或者 tensor.xxx的方式进行调用,该方式同样与pytorch源码解耦,增加和修改算子不需要重新编译pytorch源码。用该方式注册一个新的算子,流程非常简单:先编写C++相关的算子实现,然后通过pytorch底层的注册接口(torch::RegisterOperators),将该算子注册即可。
构建Rulebook实际通过python接口get_indice_pairs调用src/spconv/spconv_ops.cc文件种的getIndicePairs函数
代码位于:src/spconv/spconv_ops.cc
分析getIndicePairs直接将重心锁定在GPU逻辑部分,并且子流行3d稀疏卷积和正常3d稀疏卷积分开讨论,优先子流行3d稀疏卷积。
代码中最重要的3个变量分别为:indicePairs,indiceNum和gridOut,其建立过程如下:
indicePairs代表了稀疏卷积输入输出的映射规则,即Input Hash Table 和 Output Hash Table。这里分配理论最大的内存,它的shape为{ 2,kernelVolume,numAct},2表示输入和输出两个方向,kernelVolume为卷积核的volume size。例如一个3x3x3的卷积核,其volume size就是(3*3*3)。numAct表示输入有效(active)特征的数量。indiceNum用于保存卷积核每一个位置上的总的计算的次数,indiceNum对应中的count
代码中关于gpu建立rulebook调用create_submconv_indice_pair_cuda函数来完成,下面具体分析下create_submconv_indice_pair_cuda函数
子流线稀疏卷积
子流线稀疏卷积是调用create_submconv_indice_pair_cuda函数来构建rulebook
在create_submconv_indice_pair_cuda大可不必深究以下动态分发机制的运行原理。
直接将重心锁定在核函数:
prepareSubMGridKernel核函数中grid_size和block_size实则都是用的整形变量。其中block_size为tv::cuda::CUDA_NUM_THREADS,在include/tensorview/cuda_utils.h文件中定义,大小为。而grid_size大小通过tv::cuda::getBlocks(numActIn)计算得到,其中numActIn表示有效(active)输入数据的数量。
prepareSubMGridKernel作用:建立输出张量坐标(通过index表示)到输出序号之间的一张哈希表
见:include/spconv/indice.cu.h
这里计算index换了一种模板加递归的写法,看起来比较复杂而已。令:new_indicesIn = indicesIn.data(),可以推导得出index为:
ArrayIndexRowMajor位于include/tensorview/tensorview.h,其递归调用写法如下:
接着看核函数getSubMIndicePairsKernel3:
位于:include/spconv/indice.cu.h
看:
上述写法类似我们函数中常见的循环的写法,具体可以查看include/tensorview/kernel_utils.h
NumILP按默认值等于1的话,其stride也是gridDim.x*blockDim.x。索引最大值要小于该线程块的线程上限索引blockDim.x * gridDim.x,功能与下面代码类似:
参考: blog.csdn.net/ChuiGeDaQ...
U-Net+与FCN的区别+医学表现+网络详解+创新
UNet论文: 地址 UNet源代码: 地址 UNet和FCN的区别 UNet相比FCN,结构更加对称,解码部分采用合并操作(concatenation)而非FCN的加法操作(summation),FCN的解码部分较为简单,仅使用反卷积,UNet在反卷积后通过合并操作补充特征信息,提高了分割精度。 UNet在医学图像分割中的表现 UNet在医学图像分割中表现良好,其原因在于医学图像的特点,如高分辨率、局部特征明显等。UNet的编码部分提取局部特征,解码部分通过反卷积与合并操作恢复特征尺寸,提高了分割精度。 UNet网络模型 UNet由编码路径和解码路径组成。编码路径用于特征提取,降低分辨率以增强模型对小扰动的鲁棒性。解码路径利用编码路径的抽象特征恢复图像尺寸,合并操作补充信息,减少上采样时的信息损失。 UNet的创新点 UNet的创新点包括Overlap-tile策略,通过镜像填充预处理图像,使输出尺寸与输入尺寸相同,同时提供上下文信息提高预测精度。数据增强策略,通过随机弹性变形增加数据量。加权损失函数,通过预先计算权重图调整不同像素的损失权重,强化边缘学习。 UNet的不足与改进 UNet++作者分析了UNet的不足,并提出了改进方法,包括网络结构的优化和数据增强策略的改进。 拓展应用 UNet适用于大尺寸医学图像的分割,通过切块处理,UNet能够提供边缘信息,克服分割区域边缘处理问题。UNet家族的发展包括了UNet++等改进模型,进一步提高了分割性能。deformable变形卷积pytorch实现(第二节deformable_conv2d 实现一)
修改理解:年3月,对num_groups参数的理解进行了修正。若仍有疑问,欢迎大家指出。
内容概述:这一节将介绍deformable_conv2d的实现细节及常见坑点。旨在帮助后来者简化实现过程。如有错误,敬请指正。文章已链接。
目标实现:仅实现所需的deformable_conv2d部分,deformable roi部分未实现。复现旨在翻译原文,理解映射规则,结果易于推导。
原理说明:deformable convolution设计目的是让网络学习卷积核形状。通过额外的Conv2d层学习每个位置的位移和置信度参数。数据经过卷积后,输出用于变形卷积的offset和mask,接着进行卷积,最终输出。
参数解释:包含两个卷积核,一个用于变形卷积,一个用于学习。输入包括数据流、卷积核、offset、mask,以及固定参数如stride、padding、dilation等。关注num_groups、deformable_groups、im2col_step,理解其功能。
实现细节:实现三个cuda核函数,分别为变形卷积前的im2col、卷积后的col2im、处理坐标信息的col2im_coord。核心在于计算卷积参数位置并进行线性插值,乘以置信mask。
代码实现:主要实现forward和backward函数。forward部分需要多次生成列矩阵以匹配结果。具体细节和cuda核函数可参阅源代码,核心在于定位参数并执行插值运算。
后续内容:其余部分如backward等将在后续文章中讨论。写作过程较为匆忙,欢迎讨论交流。