【fishing-pan:https://blog.csdn.net/u013921430 转载请注明出处】

前言

  卷积是卷积神经网络中最主要、最重要的运算。想必大家最早接触卷积的概念就是在初高中的数学当中,它是一个这样的公式;
(g∗h)(x)=∫−∞∞g(τ)⋅h(x−τ)dτ(g\ast h)(x)=\int_{-\infty }^{\infty }g(\tau )\cdot h(x-\tau )d\tau (g∗h)(x)=∫−∞∞​g(τ)⋅h(x−τ)dτ
  上面的公式是一维空间中连续函数的卷积,而在图像处理中,卷积操作是二维平面上的离散卷积;卷积的过程可以表示为下方的式子,式子中的 kernel(i,j)kernel(i,j)kernel(i,j) 表示卷积核的坐标为 (i,j)(i,j)(i,j) 处的权重,HHH 与 WWW 分别表示卷积核的高与宽。
output(x,y)=∑i=0i=H−1∑j=0j=W−1input(x+i,y+j)⋅kernel(i,j)output(x,y)=\sum_{i=0}^{i=H-1}\sum_{j=0}^{j=W-1}input(x+i,y+j)\cdot kernel(i,j)output(x,y)=i=0∑i=H−1​j=0∑j=W−1​input(x+i,y+j)⋅kernel(i,j)
  详细的计算过程可以模拟为下方的gif图中的形式。(图片来源链接

Tensorflow中的卷积函数到底做了些什么

  上面的简单介绍主要是为了让大家了解卷积的操作过程。下面将进入正题,介绍一下tf中的卷积函数。本文将着重介绍tf.nn.conv2d函数,余下的函数主要分析他们之间的区别。

tf.nn.conv2d

  这应该是tensorflow构建网络模型时最常用的卷积函数了,他的定义如下;

conv2d(input, filter, strides, padding, use_cudnn_on_gpu=True, data_format="NHWC", dilations=[1, 1, 1, 1], name=None):

  参数中带有默认值的一般不需要管,我们这里就不详细介绍了,大家想了解的可以自行查看函数定义。接下来详细介绍一下四个主要的参数:input, filter, strides, padding

   input:输入的tensor,被卷积的图像,conv2d要求input必须是四维的。四个维度分别为[batch, in_height, in_width, in_channels],即batch size,输入图像的高和宽以及单张图像的通道数。

   filter:卷积核,也要求是四维,[filter_height, filter_width, in_channels, out_channels]四个维度分别表示卷积核的高、宽,输入图像的通道数和卷积输出通道数。其中in_channels大小需要与 input 的in_channels一致。

  strides:步长,即卷积核在与图像做卷积的过程中每次移动的距离,一般定义为[1,stride_h,stride_w,1],stride_h与stride_w分别表示在高的方向和宽的方向的移动的步长,第一个1表示在batch上移动的步长,最后一个1表示在通道维度移动的步长,而目前tensorflow规定:strides[0] = strides[3] = 1,即不允许跳过bacth和通道,前面的动态图中的stride_h与stride_w均为1。

  padding:边缘处理方式,值为“SAME” 和 “VALID”,熟悉图像卷积操作的朋友应该都熟悉这两种模式;由于卷积核是有尺寸的,当卷积核移动到边缘时,卷积核中的部分元素没有对应的像素值与之匹配。此时选择“SAME”模式,则在对应的位置补零,继续完成卷积运算,在strides为[1,1,1,1]的情况下,卷积操作前后图像尺寸不变即为“SAME”。若选择 “VALID”模式,则在边缘处不进行卷积运算,若运算后图像的尺寸会变小。

  首先分别定义输入图像与卷积核。为了方便分析,将输入图像的batch size设为1,图像尺寸为10x10,通道数为3。卷积核尺寸为[5,5,3,4]。进行卷积操作,然后输出卷积结果的尺寸。

input_img=tf.Variable(tf.constant(10,dtype=tf.float32,shape=[1,10,10,3])) #定义输入图像
W1=tf.Variable(tf.truncated_normal([5,5,3,4],stddev=0.1)) #定义卷积核
conv1=tf.nn.conv2d(input_img,W1,strides=[1,2,2,1],padding='SAME') #卷积操作
init=tf.global_variables_initializer()with tf.Session() as sess:sess.run(init)print('conv1 shape is:',sess.run(tf.shape(conv1)))

  可想而知,conv1的尺寸是[1 5 5 4],因为在定义卷积核的时候就明确说明卷积核的第四个维度为输出通道数。那么问题来了,conv1中的每个元素的值是如何确定的呢?问得好。
  从表面上来看,我们只定义了一个卷积核W1,“他”将图像从3通道变成了4通道,就像下面这个过程。

  但是实际上并不是这样,W1W1W1不是一个卷积核,而是表示一个包含c个卷积核的卷积层。conv2d函数卷积的过程可以表示成下面的公式;其中iii、jjj表示像素点的位置,dididi、djdjdj表示卷积核中元素的相对位置,qqq 表示输入通道序号,ccc 表示输出通道数。
conv1[b,i,j,c]=∑di,dj,qinput[b,strides[1]∗i+di,strides[2]∗j+dj,q]∗W1[di,dj,q,c]conv1[b, i, j, c]=\sum_{di,dj,q}^{ }input[b, strides[1] * i + di, strides[2] * j + dj, q] *W_{1}[di, dj, q, c]conv1[b,i,j,c]=di,dj,q∑​input[b,strides[1]∗i+di,strides[2]∗j+dj,q]∗W1​[di,dj,q,c]
  从公式中可以看出,卷积核的前三个维度形成一个三维的卷积核,与输入图像做卷积,最终得到一个单通道的输出(这个过程可以简单理解为,把三个通道的特征压缩到了一个通道上);而卷积核的第四个维度 ccc 对应着输出的每一个通道,其实 ccc 表示的是有多少个这样的卷积核。其过程可以简化成下图;

  经过上述描述,想必大家也都明白了为何卷积核的第三个维度要是输入图像的通道数。

tf.contrib.slim.conv2d

  另一个常见的卷积函数是 tf.contrib.slim.conv2d,他的卷积过程与tf.nn.conv2d一致,其函数名和参数定义如下。

convolution(inputs,num_outputs,kernel_size,stride=1,padding='SAME',data_format=None,rate=1,activation_fn=nn.relu,normalizer_fn=None,normalizer_params=None,weights_initializer=initializers.xavier_initializer(),weights_regularizer=None,biases_initializer=init_ops.zeros_initializer(),biases_regularizer=None,reuse=None,variables_collections=None,outputs_collections=None,trainable=True,cope=None):

  其中的参数很多,就不一一介绍了,主要的参数依然是inputs,num_outputs,kernel_size,stride,padding。使用slim.conv2d函数进行卷积操作,不需要单独定义卷积层,激活函数,甚至是偏置。

input_img=tf.Variable(tf.constant(10,dtype=tf.float32,shape=[1,10,10,3]))#定义输入图像W1=tf.Variable(tf.truncated_normal([5,5,3,4],stddev=0.1))#定义卷积核
conv1=tf.nn.conv2d(input_img,W1,strides=[1,2,2,1],padding='SAME')
relu1=tf.nn.relu(conv1)conv4=slim.conv2d(input_imgg,4,[5,5],strides=2,padding='SAME')

  在上方的代码中第6行与2至4行所执行的功能是一致的。而代码量却小了很多。因此在构建网络层数较多的网络时,使用slim.conv2d以及slim库中的其他功能可以使模型的构建,训练,测试都变得更加简单。

tf.nn.depthwise_conv2d

  tf.nn.depthwise_conv2d的意思是深度二维卷积,他的定义如下。

depthwise_conv2d(input,filter,strides, padding,rate=None,name=None, data_format=None):

  与tf.nn.conv2d一样,他的主要参数也是input, filter, strides, padding,他与tf.nn.conv2d的区别在于filter的不同。在depthwise_conv2d中filter的四个维度分别为[filter_height, filter_width, in_channels, channel_multiplier],其第四个维度不再是输出通道数,而是通道倍数,最终输出的总的通道数为inchannels∗channelmultiplierin channels*channel multiplierinchannels∗channelmultiplier,因为此时分通道进行卷积与输出,不再卷积结果合并成单通道,明白了这个道理,函数的卷积过程就好理解了。可以用以下的公式表示;
conv1[b,i,j,c∗multi+q]=∑di,djinput[b,strides[1]∗i+di,strides[2]∗j+dj,c]∗W1[di,dj,c,q]conv1[b, i, j, c*multi + q]=\sum_{di,dj}^{ }input[b, strides[1] * i + di, strides[2] * j + dj, c] *W_{1}[di, dj, c, q]conv1[b,i,j,c∗multi+q]=di,dj∑​input[b,strides[1]∗i+di,strides[2]∗j+dj,c]∗W1​[di,dj,c,q]
  而卷积过程可以表示为下图所示的形式。


  这里可以验证一下上述过程。使用同样的输入与卷积核,分别用不同的卷积方式做卷积,并且对depthwise_conv2d卷积的输出的第一个、第五个、第九个通道对应位置的元素求和,与conv2d卷积第一通道的结果对比,看看有什么差异。

import tensorflow as tfinput_img=tf.Variable(tf.constant(10,dtype=tf.float32,shape=[1,10,10,3]))#定义输入图像
W1=tf.Variable(tf.truncated_normal([5,5,3,4],stddev=0.1))#定义卷积核
conv1=tf.nn.conv2d(input_img,W1,strides=[1,2,2,1],padding='SAME')
conv2=tf.nn.depthwise_conv2d(input_img,W1,strides=[1,2,2,1],padding='SAME')conv2_1=conv2[:,:,:,0]
conv2_2=conv2[:,:,:,4]
conv2_3=conv2[:,:,:,8]conv2_sum123=tf.add(tf.add(conv2_1,conv2_2),conv2_3)
init=tf.global_variables_initializer()with tf.Session() as sess:sess.run(init)print('conv1 shape is:',sess.run(tf.shape(conv1)))print('conv2 shape is:',sess.run(tf.shape(conv2)))
#    print('conv2d sum:',sess.run(tf.reduce_sum(conv1,axis=3)))
#    print('depthwise_conv2d sum:',sess.run(tf.reduce_sum(conv2,axis=3)))print(sess.run(conv2_sum123))print(sess.run(conv1[:,:,:,0]))

  最终输出如下:

  从求和结果可以看出,两者基本一致。也证明了depthwise_conv2d的计算输出是未进行通道求和之前的conv2d函数的计算结果。

tf.nn.conv2d_transpose

  tf.nn.conv2d_transpose是反卷积函数,进行的是卷积的反向操作,一般应用于上采样过程,其定义如下;

conv2d_transpose(value,filter,  output_shape,strides,padding="SAME",data_format="NHWC",name=None):

   value:指需要做反卷积的输入图像,与之前的一样,要求是一个四维的tensor。
   filter:卷积核,要求是一个四维tensor,与之前的卷积核不同,其四个维度分别是[filter_height, filter_width, out_channels, in_channels],注意输入通道数与输出通道数的位置改变了
  output_shape:反卷积操作输出tensor的shape,之前的卷积中是没有这个参数的。
  strides:反卷积时在图像每一维上的步长

  此处output_shape并不能取任意的尺寸,应该根据输入图像尺寸、卷积核尺寸以及正向卷积的过程,反推出尺寸大小。例如,下面的代码中output_shape有两个错误,第一个是输出图像的长宽不匹配,第二个就是输出通道数为5与卷积核的输出通道数3不匹配。

import tensorflow as tfinput_img=tf.Variable(tf.constant(10,dtype=tf.float32,shape=[1,10,10,3]))#定义输入图像
W1=tf.Variable(tf.truncated_normal([5,5,3,4],stddev=0.1))#定义卷积核
conv1=tf.nn.conv2d(input_img,W1,strides=[1,2,2,1],padding='SAME')conv3=tf.nn.conv2d_transpose(conv1,W1,[1,15,15,5],strides=[1,2,2,1],padding='SAME')
init=tf.global_variables_initializer()with tf.Session() as sess:sess.run(init)print('conv1 shape is:',sess.run(tf.shape(conv1)))print('conv3 shape is:',sess.run(tf.shape(conv3)))print(sess.run(conv3[:,:,:,0]))

  反卷积的过程就不展开了,感兴趣的可以自行查找资料学习。

参考

  最后,向大家推荐一个对卷积详细描述的网站(链接)。

  已完。。

【Tensorflow】Tensorflow中的卷积函数(conv2d、slim.conv2d、depthwise_conv2d、conv2d_transpose)相关推荐

  1. [TensorFlow 学习笔记-04]卷积函数之tf.nn.conv2d

    [版权说明] TensorFlow 学习笔记参考: 李嘉璇 著 TensorFlow技术解析与实战 黄文坚 唐源 著 TensorFlow实战郑泽宇  顾思宇 著 TensorFlow实战Google ...

  2. Matlab中矩阵卷积函数convn

    Matlab中矩阵卷积函数convn 最近在看CNN做手写数字识别,其中CNN中Convolution在图像处理中就涉及了矩阵卷积.因为博主有了奥本海姆<信号与系统>中一维卷积的基础,这里 ...

  3. 4.1 Tensorflow:卷积函数

    卷积 卷积神经网络的结构 其中,input为输入,conv为卷积层,由卷积核构成,pool为池层,由池化函数构成最后是全连接层与输出层,其负责对卷积层提取的特征进行处理以获得我们需要的结果 ###卷积 ...

  4. tf.nn.conv2d() / tf.nn.depthwise_conv2d() 和 Batchsize效益

    1. 卷积函数tf.nn.conv2d() tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=True, data_form ...

  5. TensorFlow学习——tf.nn.conv2d和tf.contrib.slim.conv2d的区别

    在查看代码的时候,看到有代码用到卷积层是tf.nn.conv2d,也有的使用的卷积层是tf.contrib.slim.conv2d,这两个函数调用的卷积层是否一致,在查看了API的文档,以及slim. ...

  6. tensorflow一维卷积输入_tensorflow中一维卷积conv1d处理语言序列的一点记录

    工作中用卷积方法进行自然语言处理(NLP)相关任务,用到了tensorflow中的一些函数及方法: tf.nn.conv1d tf.layters.conv1d 用cov2d实现cov1d 两种池化操 ...

  7. tensorflow中的正则化函数在_『TensorFlow』正则化添加方法整理

    一.基础正则化函数 tf.contrib.layers.l1_regularizer(scale, scope=None) 返回一个用来执行L1正则化的函数,函数的签名是func(weights). ...

  8. Tensorflow中2D卷积API使用

    TF中用于2D卷积操作的api是tf.nn.conv_2d,源码位置位于tensorflow/python/ops/gen_nn_ops.py 接口定义为: conv2d(input, filter, ...

  9. TF:tensorflow框架中常用函数介绍—tf.Variable()和tf.get_variable()用法及其区别

    TF:tensorflow框架中常用函数介绍-tf.Variable()和tf.get_variable()用法及其区别 目录 tensorflow框架 tensorflow.Variable()函数 ...

最新文章

  1. 数据蒋堂 | 内置的数据无法实现高性能
  2. js流程图:aworkflow.js
  3. python读取mtcars数据集并实现以下操作_Python可视化43 | plotnine≈R语言ggplot2,43plotnineR...
  4. 用Silverlight做广告Banner
  5. react重新渲染菜单_React实现递归组件
  6. Python:在Pandas数据框中查找缺失值
  7. Jenkins持续集成实践之java项目自动化部署
  8. 跳石板(通俗易懂的思路和方法)
  9. 深入理解计算机系统第四版_深入理解计算机系统之存储器层次结构
  10. 动态改变_Excel中如何动态改变可编辑区域?
  11. Python画图(直方图、多张子图、二维图形、三维图形以及图中图)
  12. centos7正确关机重启
  13. 字节跳动年底再招 10000 人,前端工程师非常紧缺!
  14. C# 实现打开和关闭可执行文件
  15. [圣诞大礼][android控]平板电脑HD精品游戏软件合集
  16. flask的pin码攻击——新版本下pin码的生成方式
  17. 计算机科学的主要目标,学习计算机的主要目的是什么?
  18. 深海探测机器人——“海洋一号”成功出航!
  19. 某土豪公司很糟糕的面试体验
  20. 常州大学 计算机与人工智能学院,常熟理工学院新闻网

热门文章

  1. 那些年我看过的前端书
  2. 汇编语言---函数调用栈
  3. WinCE5.0下直接写屏操作与函数 CreateDIBSection() 的使用(转)
  4. Mongodb 与sql 语句对照
  5. NGINX和NGINX Plus的速率限制
  6. 容器编排技术 -- 使用Minikube集群
  7. BGP——重分发注意点(总结+配置)
  8. Linux下安装及使用mysql
  9. layuimini 图标颜色便捷指定
  10. fcn从头开始_从头开始有营销问题