通道数目的不同

单通道的卷积

下面的代码测试了仅仅一个属性(depth是1)的深度卷积,其结果和普通卷积是一样的:

async function depthwiseConv2dTestSingleDepth() {const fSize = 2;const pad = 'valid';const stride = 1;const chMul = 1;const inDepth = 1;const x = tf.tensor4d([0, 1, 2, 5, 0, 5,0, 1, 5],[1, 3, 3, inDepth]);const w = tf.tensor4d([1.0, 2.0, 2.0, 2.0],[fSize, fSize, inDepth, chMul],);   const result = tf.depthwiseConv2d(x, w, stride, pad);result.print();
}

输出是:

Tensor[[[[12],[15]],[[7 ],[22]]]]​

如下图(依次是:输入数据,滤波器,输出)演示了输出12的参与卷积的数据:

双通道的卷积

下面是深度是2的情况(NHWC)。输入数据是两个通道(假设是红色,黄色),每个通道大小是2x2。因此输入数据的形状是:1x2x2x2。卷积提供了两个窗口(假设是红色,黄色),每个窗口提取一个通道的属性。滤波器的形状是2x2x2x1。因此输出数据是两个通道。同时pading模式是valid,因此每个通道输出数据的大小是1x1。输出数据的形状是:1x1x2。

/*
[ [ [[6, 2],]]]
*/
async function depthwiseConv2dTestMultipleDepth3() {const fSize = 2;const pad = 'valid';const stride = 1;//const dilation = 2;const inDepth = 2;const x = tf.tensor4d([0, 1, 3, 1,0, 2, 2, 1,],[1, fSize, fSize, inDepth]);const w =tf.stack([tf.tensor2d([2,0,1,3], [fSize, fSize]),tf.tensor2d([1,0,0,1], [fSize, fSize])],2).expandDims(3);// as tf.Tensor4D;const result = tf.depthwiseConv2d(x, w, stride, pad);result.print();
}

输入数据是下面的:

  [0, 1, 3, 1,0, 2, 2, 1,],

但是实际上,由于深度为2,而且数据格式是NHWC(默认的数据格式),即输入数据会被解释为:通道1:通道2;通道1:通道2;…,所以,如果逐个通道来看,数据是这样的:

  • 通道1:0,3,0,2;
  • 通道2:1,1,2,1。

滤波器w则是按照两个通道存储的。所以对于滤波器:

  • 通道1:2,0,1,3;
  • 通道2:1,0,0,1。


要注意的是: 虽然深度可分离卷积内部是逐个通道计算卷积的。但是,对于depthwiseConv2d算子而言,其输入是所有通道的数据!

stack与否

stack方式定义的滤波器(fSize=2):

  const w =tf.stack([tf.tensor2d([2,0,1,3], [fSize, fSize]),tf.tensor2d([1,0,0,1], [fSize, fSize])],2).expandDims(3);// as tf.Tensor4D;

其解析出来的两个通道的滤波器如下:

容易得出:如果是stack的方式,数据是逐通道排布的:即通道1的所有数据;通道2的所有数据。

非stack方式定义的滤波器:

  const fSize = 2;const pad = 'valid';const stride = 1;const chMul = 1;const inDepth = 2;const w = tf.tensor4d([2,0,1,3,1,0,0,1,],[fSize, fSize, inDepth, chMul]);

如果不是stack的方式存储的数据,那么,其数据排列默认依旧是NHWC格式的,即通道1:通道2:通道1:通道2的数据交织分布。

测试代码:

async function testDepthwiseConv2dChannelmul1() {const fSize = 2;const pad = 'valid';const stride = 1;const chMul = 1;const inDepth = 2;const x = tf.tensor4d([0, 1, 3, 1, 0, 3, 1,0, 1, 2, 1, 2, 0, 2,0, 0, 1, 1,],[1, 3, 3, inDepth]);const w = tf.tensor4d([2,0,1,3,1,0,0,1,],[fSize, fSize, inDepth, chMul]);const result = tf.depthwiseConv2d(x, w, stride, pad);result.print();
}

一个复杂的例子

输入数据
再看一个例子。下面这段代码存储的是4个通道,每个通道的数据是:0-24. 5x5.
实际输入的数据格式是:
0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,

23, 23, 23, 23, 24, 24, 24, 24

逐通道逻辑展开,其每个通道的输入数据是这样的(注意:每个通道的数据都是一样的):


实际存储到纹理的数据,是这个样子的,是一个4x25的纹理:


getX用于从实际存储的纹理数据里面获取输入数据:

float getX(int row, int col, int depth) {int texR = row * 5 + col;int texC = depth;ivec2 uv = ivec2(texC, texR);return sampleTexture(x, uv);
}

depth指向当前数据所在的深度,能够通过gl_GlobalInvocationID.x来获取,也可以通过d1(int d1 = d2 / ${channelMul};)来得到。

int tempRow = int(gl_GlobalInvocationID.y)/5;
int tempCol = int(gl_GlobalInvocationID.y)- tempRow*5;
dotProd = getX(batch, tempRow, tempCol, gl_GlobalInvocationID.x);

滤波器
实际的滤波器是stack模式,即输入数据是一个通道一个通道存储,而不是交织存储的,其按通道的逻辑布局和实际数据布局是一样的:
[1,0,0,0,0,0,0,0,1],//通道1
[0,0,0,1,0,0,0,0,0],//通道2
[0,0,0,0,0,0,0,0,1],//通道3
[1,0,0,0,0,0,0,0,0],//通道4

注意:stride是2。
输入形状是:1 5 5 4
滤波器形状是:3 3 4 1
输出形状是:1 2 2 4。

对于计算着色器而言,输出的形状,决定了输出线程的数量。
输出坐标
输出的UV坐标计算代码是:

    ivec4 getOutputCoords() {int index = int(gl_GlobalInvocationID.y) * 4 +int(gl_GlobalInvocationID.x);int r = index / 16; index -= r * 16;int c = index / 8; index -= c * 8;int d = index / 4; int d2 = index - d * 4;return ivec4(r, c, d, d2);}

代码里面,“int(gl_GlobalInvocationID.y) * 4”的4,对应的是输入的depth。
结合前面讨论的输入数据的格式,gl_GlobalInvocationID.y对应的是列0-24。gl_GlobalInvocationID.x对应的是深度,对应0-3.

gl_GlobalInvocationID = gl_WorkGroupID * gl_WorkGroupSize + gl_LocalInvocationID;

容易得到,通道0对应的输出是:12,16,32,36

async function depthwiseConv2dTestArrayPad0(size) {const fSize = 3;const pad = 'valid';const stride = 2;//const dilation = 2;const inDepth = 4;const SQRT = size;const ARRAY_SIZE = SQRT*SQRT;//400, 20 20//let arr2: boolean[] = new Array();let array1 = new Array();let arrayd0 = new Array();let arrayd1 = new Array();let arrayd2 = new Array();let arrayd3 = new Array();for (let i=0; i < ARRAY_SIZE; i++) {for (let j =0; j <inDepth; j++) {array1[i*inDepth+j] = i;}}console.log(array1);let j =0;for (let i=0; i < ARRAY_SIZE*inDepth; i+= inDepth) {arrayd0 [j] = array1[i];arrayd1 [j] = array1[i+1];arrayd2 [j] = array1[i+2];arrayd3 [j] = array1[i+3];j++;}console.log(arrayd0);console.log(arrayd1);console.log(arrayd2);console.log(arrayd3);const x = tf.tensor4d(array1,[1, SQRT,SQRT, inDepth]);const w =tf.stack([tf.tensor2d([1,0,0,0,0,0,0,0,1], [fSize, fSize]),tf.tensor2d([0,0,0,1,0,0,0,0,0], [fSize, fSize]),tf.tensor2d([0,0,0,0,0,0,0,0,1], [fSize, fSize]),tf.tensor2d([1,0,0,0,0,0,0,0,0], [fSize, fSize])],2).expandDims(3);// as tf.Tensor4D;          const result = tf.depthwiseConv2d(x, w, stride, pad);result.print();
}

Channel Multiplier

这个参数其实有点难以理解。幸运的是,MobileNet里面,这个参数是1。但是,本节还是准备对这个参数的意义进行展开解释。
考虑下面的代码为什么产生[7, 0, 5, 6]的输出?

//[ [ [[7, 0, 5, 6],]]]
async function testDepthwiseConv2dChannelmul2_d2() {const fSize = 2;const pad = 'valid';const stride = 1;const chMul = 2;const inDepth = 2;const x = tf.tensor4d([0, 1, 3, 1,0, 1, 2, 1,],[1, 2, 2, inDepth]);const w = tf.tensor4d([2, 0, 1, 3, 1, 0, 0, 1,0, 1, 2, 1, 2, 0, 2, 1,],[fSize, fSize, inDepth, chMul]);const result = tf.depthwiseConv2d(x, w, stride, pad);result.print();
}

关于Channel Multiplier的规则:

  • Channel Multiplier影响的是滤波器的数据的组织形式。譬如说inDepth = 2, chMul = 2, 那么,滤波器的数据要被解析为,每个通道被重复一次,一共4个通道:CH0; CH0; CH1; CH1;
  • 对输入没有任何影响。只影响当前Tensor(滤波器)的数据组织。

有了 这两条规则,容易得到输入,和滤波器的数据排布如下表。相应的输出也容易得到了:

下面的文献谈到了其他的depth multiplier:
https://towardsdatascience.com/a-basic-introduction-to-separable-convolutions-b99ec3102728
https://towardsdatascience.com/review-mobilenetv1-depthwise-separable-convolution-light-weight-model-a382df364b69

深度可分离卷积的Depth,Stack,Channel Multiplier相关推荐

  1. 可分离卷积及深度可分离卷积详解

    可分离卷积 再来看一下nn.Conv2d(): torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, ...

  2. 分组卷积/转置卷积/空洞卷积/反卷积/可变形卷积/深度可分离卷积/DW卷积/Ghost卷积/

    文章目录 1. 常规卷积 2. 分组卷积 3. 转置卷积 4. 空洞卷积 5. 可变形卷积 6. 深度可分离卷积(Separable Convolution) 6.1 Depthwise Convol ...

  3. 深度学习中的depthwise convolution,pointwise convolution,SeparableConv2D深度可分离卷积

    DepthwiseConv2D深度方向的空间卷积 pointwise convolution, SeparableConv2D深度可分离卷积 SeparableConv2D实现整个深度分离卷积过程,即 ...

  4. YOLOv5改进——使用深度可分离卷积轻量化C3

    一.C3模块   在原版YOLOv5网络中,C3模块的结构如图1-1所示,C3结构中的ConvBNSiLU和BottleNeck的结构如图1-2所示: 图1-1 C3结构图 图1-2 BottleNe ...

  5. 【深度学习】利用深度可分离卷积减小计算量及提升网络性能

    [深度学习]利用深度可分离卷积减小计算量及提升网络性能 文章目录 1 深度可分离卷积 2 一个深度可分离卷积层的代码示例(keras) 3 优势与创新3.1 Depthwise 过程3.2 Point ...

  6. Lesson 16.1016.1116.1216.13 卷积层的参数量计算,1x1卷积核分组卷积与深度可分离卷积全连接层 nn.Sequential全局平均池化,NiN网络复现

    二 架构对参数量/计算量的影响 在自建架构的时候,除了模型效果之外,我们还需要关注模型整体的计算效率.深度学习模型天生就需要大量数据进行训练,因此每次训练中的参数量和计算量就格外关键,因此在设计卷积网 ...

  7. 【Tensorflow】tf.nn.depthwise_conv2d如何实现深度卷积?+深度可分离卷积详解

    目录 常规卷积操作 深度可分离卷积 = 逐通道卷积+逐点卷积 1.逐通道卷积 2.逐点卷积 参数对比 介绍 实验 代码清单 一些轻量级的网络,如mobilenet中,会有深度可分离卷积depthwis ...

  8. 【CV】MobileNet:使用深度可分离卷积实现用于嵌入式设备的 CNN 架构

    论文名称:MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications 论文下载:https:/ ...

  9. [Pytorch]torch.nn.functional.conv2d与深度可分离卷积和标准卷积

    torch.nn.functional.conv2d与深度可分离卷积和标准卷积 前言 F.conv2d与nn.Conv2d F.conv2d 标准卷积考虑Batch的影响 深度可分离卷积 深度可分离卷 ...

最新文章

  1. java静态/动态成员变量、初始化块,父类/子类构造函数执行顺序问题
  2. qt在表格中如何画线_如何把EXCEL的表格完整的粘贴到WORD文件中
  3. libSVM在matlab下的使用安装
  4. linux vi删除操作,vi删除操作
  5. Windows 下 Conda install 安装出现CondaHTTPError: HTTP 000 CONNECTION FAILED for url解决措施
  6. matlab如何将相近的数据,matlab新手,求帮助!主要是如何将数据和公式导入
  7. 美国Palmbeach大学服务器整合改造案例
  8. python通讯录管理系统 tk_通讯录管理系统课程设计
  9. ASP.NET Core应用的错误处理[2]:DeveloperExceptionPageMiddleware中间件如何呈现“开发者异常页面”...
  10. poj3041 Asteroids
  11. tesseract 提升 速度_京东研发效能提升,从正确地实施度量开始
  12. 使用shell脚本将mysql数据导入HIve中
  13. C语言的代码内存布局
  14. Windows Server 2012 R2服务器集群测试
  15. 开发工具总结(8)之图文并茂全面总结上百个AS好用的插件(下)
  16. mysql dcn_Tdsql DCN同步技术原理介绍
  17. hdu 4939 Stupid Tower Defense 2014多校七 DP
  18. mac 显示器分辨率_如何在Mac上选择精确的显示分辨率
  19. 微信公众平台接口程序语音天气
  20. 车牌定位系统matlab,基于Matlab的车牌定位系统的研究与设计论文

热门文章

  1. 动手画混淆矩阵(Confusion Matrix)(含代码)
  2. 动画设计的12条基本原理
  3. unity沙子堆积_Unity Shader 之 简单实现沙漠干旱热浪的效果
  4. 腾讯云mysql安装
  5. 导航星历的钟差,TGD问题
  6. 参加python全栈开发培训需要多少钱?
  7. 嵌入式开发需要学习哪些东西
  8. 苹果零售店十大秘诀:禁止纠正顾客发音错误
  9. ResNet残差网络——Deep Residual Learning for image recongnition
  10. RS232、RS485及RS422有什么区别