tensorflow中二维卷积函数tf.nn.conv2d()解析

1. 基本概念

图片通道channel:通常jpg等彩色图片由R、G、B三个图层构成,每个图层实际上就是一个二维矩阵,也就是所谓的一个通道,因此一个彩色图片通常有3个通道,而一个黑白图片就只有一个通道。
tensorfllow中的“输出通道out_channels”:实际上就是一个二维数组(或矩阵),他对应的是一张图片卷积后的输出结果,称为feature map(特征图)。
tf.nn.conv2d()是TensorFlow里面实现卷积的函数,是搭建卷积神经网络比较核心的一个方法,其调用方式为:

2. 函数解析

result = tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)

参数说明:

  • input:待卷积的一张或一组图片,是一个4维Tensor,规定其shape=[batch, in_height, in_width, in_channels],(读入图片后通常需要进行一次数组转化,满足tensorflow的格式要求后才能进行卷积计算)
    batch=图片张数
    in_height=单张图片高度
    in_width=单张图片宽度
    in_channels=图片通道数(黑白图片为1,彩色图片为3,如有必要也可以任意指定)
    例:对一张32×32的黑白图片进行卷积,必须将图片转化为input=[1,32,32,1]格式;对2张32×32的彩色图片进行卷积,必须将图片转化为input=[2,32,32,3]。
    要求类型为float32和float64其中之一。
  • filter:卷积核,是一个4维Tensor,规定其shape=[filter_height, filter_width, in_channels, out_channels]
    filter_height=单个卷积核高度
    filter_width=单个卷积核宽度
    in_channels=输入图片的通道数(必须和input的inchannels相同,否则报错)
    out_channels=输出通道数(实际就是卷积核的个数)
  • strides:卷积移动步长,是一个有四个元素的1维Tensor,strides=[b,h,w,c],
    b:决定对哪些图片进行卷积,默认为1,即对所有图片卷积;
    h:卷积核在每张图片高度方向的移动步长;
    w:卷积核在每张图片宽度方向的移动步长;
    c:决定对每张图片的哪些通道进行卷积,默认为1,即对所有通道进行卷积;
    通常都是strides=[1,h,w,1],对所有图片的所有通道卷积
  • padding:决定图像的边缘扩展,是string类型的量,只能是"SAME","VALID"其中之一(详见https://www.imooc.com/article/details/id/29525)。
  • use_cudnn_on_gpu:bool类型,是否使用cudnn加速,默认为true。
  • 返回结果result:返回一个Tensor,就是我们常说的feature map,其shape=[batch, height, width, out_channels]
    batch=图片张数=input参数的batch
    height:卷积后每个输出的高度,由图像高度、卷积核高度、strides、padding共同决定
    width:卷积后每个输出的宽度,由图像宽度、卷积核宽度、strides、padding共同决定
    out_channels=filter的out_channels。

3. tensorflow图像卷积过程

下图展示了对一张3通道jpeg图片进行两次连续卷积操作的过程:


tensorflow中二维卷积函数tf.nn.conv2d()解析_第1张图片
cgx-tensorflow二维卷积过程解析.jpg
  • 上图中,读入图片img的in_channels通常是一个固定值1(黑白图片)或者3(彩色图片)。直接读入的图片数据还不能直接喂入tensorflow进行卷积计算,必须将其reshape成tensorflow要求的格式input=[batch, in_height, in_width, in_channels]。需要注意的是,input的in_channels可以与img的in_channels设置成不相等,用户可自定义,他只用于指定将几个二维数组(通道)作为一个整体进行卷积。但实际中我们通常将input的in_channels设为与img的相同,因为我们通常都对一整张图片进行卷积操作,而不是只对一张图片的一个或几个通道进行卷积计算。
  • 卷积核filter的in_channels必须和input的in_channels相同,否则出错,tensorflow为一张图片(或一组二维数组)中的每个通道(图层)都单独设置一个二维卷积核(tensorflow规定一张图的几个通道不能共用一个二维卷积核,必须是一个通道对应一个二维卷积核),因此对于3通道图片,必须要有三个二维卷积核(如上图),但这三个二维卷积核可以相同。通常我们将与input的in_channels对应个数的“二维卷积核组”称为一个独立的“卷积核”或“三维卷积核”(如上图中一个紫色三维矩阵就是一个独立的卷积核)。
  • filter(多维卷积核)中的out_channels参数实际上就是“多维卷积核的个数”,其中具体是多少维,由input的in_channels决定,而“个数”纯粹是由用户决定,与input或输入图片没有任何关系。如上图所示,卷积核个数为2(用户决定),每个卷积核由3个(input决定)二维卷积核组成。“卷积核个数”决定了用多少个独立的卷积核对一张图片进行卷积,每一个核最终都会产生一个独立的卷积结果,也就是所谓的特征图(feature map),卷积核越多,特征图自然也越多,上图中就只有2个特征图,因为我们只用了2个三维卷积核。用不同卷积核对一张图片进行卷积,实际上就相当于用不同的方式来提取图片中不同的特征,一般卷积核越多,卷积核的差异性越大,得到的特征结果也越多。
  • 从上图中可以看出,对一个多通道的图片,tensorflow会为每一个通道(图层)都分配一个二维卷积核,最后把所有通道的卷积结果“对应求和”作为该图片的一个完整卷积结果,这个结果就是这个图片的一个特征图(feature map)。
  • 从上图中还能看出 tensorflow中无论一张图片有多少个通道,对应一个完整的卷积核而言,就只能得到一个二维的feature map,其原因在于,tensorflow最终会把所有通道的卷积结果在对应位置进行求和,从而将多维矩阵压缩成二维矩阵。
  • 上图中的结果是,将一张3通道图片通过2个3维卷积核变成了一个2通道的。
  • 第一次卷积结束后,会把一张图的所有特征图作为一个整体进行下次一卷积计算,而不会将这些特征图分开。

4. tensorflow图像卷积实例

import tensorflow as tf
import matplotlib.pyplot as plt

sess  = tf.InteractiveSession()

# 二维卷积核大小(多个二维卷积核组成一个三维卷积核)
kernel_size = 7
# 卷积步长
stride_size = 3
# 输出通道数(三维卷积核个数,或输出特征图个数)
out_channels = 4 
 
# 读入图像文件
image_value = tf.read_file('./cgx.jpg')
# 图像解码
img = tf.image.decode_jpeg(image_value, channels=3)
# print(img.eval().shape) #原始图片维度
img_hight = img.eval().shape[0] #得到图像的高度维
img_width = img.eval().shape[1] #得到图像的宽度维
img_inchannels = img.eval().shape[2] #对于jpg图片为3

# 显示图片
plt.figure(1)
plt.imshow(img.eval()) #完整图像
# plt.imshow(img.eval()[:,:,0]) #G层
# plt.imshow(img.eval()[:,:,1]) #B层
# plt.imshow(img.eval()[:,:,2]) #R层
plt.show()

# # 格式转换,转化成浮点数便于计算
img = tf.to_float(img, name='ToFloat')

# 将图片转化成适合tensorflow卷积的格式
# 第一个参数1是输入图片张数,最后一个3个RGB3个维度
batch_shape = (1,img_width,img_hight,img_inchannels)
input_img = tf.reshape(img,batch_shape)

# 生成卷积核
filter = tf.Variable(tf.random_normal([kernel_size,kernel_size,img_inchannels,out_channels]))
# 卷积步长定义
strides_shape =[ 1,stride_size,stride_size,1]
# 调用conv2d()函数计算二维卷积
my_conv2d = tf.nn.conv2d(input_img, filter, strides_shape, padding='VALID')

##############################################################################################
#执行会话
# 调用sess初始化变量
sess.run(tf.global_variables_initializer())
#调用sess.run()执行卷积操作
feature_maps = sess.run(my_conv2d) #numpy.ndarray数组,shape=[原始图片张数,卷积后的高,卷积后的宽,特征图张数]

print('原始图像维度:[原始图片张数,图片高,图片宽,图片通道数]={}'.format(input_img.shape))
print('卷积图像维度:[原始图片张数,特征图高,特征图宽,特征图张数]={}'.format(feature_maps.shape))

# 展示每所有特征图
for i in range(feature_maps.shape[3]):
    print("第{}个特征图:".format(i+1))
    plt.imshow(feature_maps[0,:,:,i],cmap='gray') #第一个特征图
    plt.show()

# 关闭会话
sess.close()

输出结果:


tensorflow中二维卷积函数tf.nn.conv2d()解析_第2张图片
卷积结果.png

5. 参考链接

https://www.cnblogs.com/welhzh/p/6607581.html
https://www.jianshu.com/p/abb7d9b82e2a
https://www.imooc.com/article/details/id/29525
https://cloud.tencent.com/developer/article/1341522
https://www.cnblogs.com/billux/p/9072173.html

你可能感兴趣的:(tensorflow中二维卷积函数tf.nn.conv2d()解析)