通道数目的不同

单通道的卷积

下面的代码测试了仅仅一个属性(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. 「它将改变一切」,DeepMind AI解决生物学50年来重大挑战,破解蛋白质分子折叠问题...
  2. 人脸识别技术在支付场景的机遇与挑战
  3. ubuntu20.04LTS系统的终端terminal透明化设置
  4. C# Redis实战(二)
  5. js加密代码的分析[转]
  6. 遍历 in java_[Java教程]JavaScript中遍历数组 最好不要使用 for in 遍历
  7. 美团知识图谱问答技术实践与探索
  8. iZotope RX 9 Advanced for Mac - 专业音频修复软件
  9. 把文档所有的字体都缩小一号_美观且专业的macOS字体管理工具
  10. VsCode配置Java环境
  11. Unity3D脚本语言的类型系统
  12. 文件夹变exe怎么办
  13. usemvc永远不会被使用_你永远不会离开
  14. 【职业规划】该如何选择职业方向?性能?自动化?测开?学习选择python、java?
  15. 数值微分的python实现
  16. Kronecker积及其等式性质
  17. 真正的理解setup time/hold time
  18. 京东2017校园招聘Android研发工程师编程题(二):幸运数
  19. Beautiful Soup 4.4.0 文档 — beautifulsoup 4.4.0q 文档
  20. 【电路理论】KCL、KVL、线性直流电路各大方法、定理详解

热门文章

  1. mysql limit acs_Oracle Acs资深顾问罗敏 老罗技术核心感悟:牛! 11g的自动调优和
  2. hive-对用户浏览网站的点击量按年月进行统计
  3. 【动态规划】CH_0103 最短Hamilton路径
  4. 用python计算邮费考虑是否加急,用python计算residuals
  5. 多多小程序(doodoo)发布1.0,基于node,vue开发的微信小程序系统
  6. Asp.Net Core3.1-集成Hangfire
  7. 使用idea连接阿里云RDS数据库
  8. android 联机游戏平台,游聚平台/街机平台/主机联网/街机对战平台/网络街机
  9. 1.1 嵌入式系统的定义和组成
  10. 这只“鸭子不一般”科大讯飞申请“躺倒鸭”商标