学习来源: 深度学习理论和实战
上篇文章中,我们卷积神经网络中一些基础概念做了一次理解,知道了什么叫卷积,什么叫池化,什么叫卷积神经网络。这篇文章的主要内容是卷积和池化用tensorflow的具体实现。
使用版本介绍:
tensorflow : 1.15
python : 3
卷积
卷积在tensorflow中的API 是,tf.nn.conv2d()。
参数如下图所示:
参数解释如下:
第一个参数:input,即输入数据,通常是一个(batch,h,w,channels)的4维向量:
- batch: 一次输入的图片数量
- h: 表示图片的高度
- w: 表示图片的宽度
- channels: 表示图片的通道数量,例如一张rpg图片的channels为3
第二个参数:filter,即卷积核,也是由一个(k_h,k_w,in,out)的4维向量:
- k_h: 卷积核的高度
- w_h: 卷积核的宽度
- in: 通道数量,一般图片通道数量相同
- out: 即卷积核个数
第三个参数:strides,即移动步长,也是由一个(1,s_h,s_w,1)的4维向量:
- s_h: 在高度上移动的步长
- s_w: 在宽度上移动的步长
- 关于s_h,s_w参数的位置其实和data_format有关,data_format有两种形式:’“NHWC” 和“NCHW”,如果是前者,则strdes表示为(1,s_h,s_w,1),如果是后者,则strides 表示为(1,1,s_h,s_w)
第四个参数:padding,即输出是否会补零策略
- 如果padding=“SAME”,tensorflow会对输出自动补零,保证输入和输出维度相同;
- 如果padding=“VALID”,tensorflow 就不会自动补零。
其他参数默认即可,感兴趣的同学可以自己探索。
卷积代码实现
# 导入相关模块
import tensorflow as tf
import urllib.request
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline
#导入图片
cat_url = 'https://image.ibb.co/iFGs0x/kitty.png'
cat = Image.open(urllib.request.urlopen(cat_url)).convert('L')
cat = np.array(im,dtype='float32')
# 可视化图片
plt.imshow(cat.astype('uint8'),cmap='gray')
查看图片维度
print (cat.shape[0])
print (cat.shape[1])
# 将图片矩阵转化为tensor
cat = tf.constant(cat.reshape((1,cat.shape[0],cat.shape[1],1)),name='input')
# 定义一个卷积核
sobel_kernel = np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]],dtype=np.float32)
sobel_kernel = tf.constant(sobel_kernel,shape=(3,3,1,1))
#调用tensorflow 卷积API
# padding='SAME' 的卷积
edge1 = tf.nn.conv2d(cat,sobel_kernel,[1,1,1,1],'SAME',name='same_conv')
# padding='VALID'的卷积
edge2 = tf.nn.conv2d(cat,sobel_kernel,[1,1,1,1],'VALID',name='valid_conv')
#激活tensorflow
sess = tf.InteractiveSession()
edge1_np = sess.run(edge1)
edge2_np = sess.run(edge2)
# padding = 'SAME'下的图片
plt.imshow(np.squeeze(edge1_np),cmap='gray')
plt.title('shape={}'.format(edge1_np.shape))
# padding="VALID"
plt.imshow(np.squeeze(edge2_np),cmap='gray')
plt.title('shape={}'.format(edge2_np.shape))
通过对比两种方式,我们发现了什么?我们padding为SAME的输出维度和输入图片维度是相同的。
池化
tensorflow 也已经封装了池化层的API ,在这里我们只讨论最大化池化层API: tf.nn.max_pool().
参数如下图所示:
具体解释:
value: 即输入,是一个四维tensor(batch,h,w,channels).
ksize: 池化核大小,是一个四维tensor(1,k_h,k_w,1),和卷积相同含义.
strides: 同卷积strides含义.
padding: 同卷积padding含义.
池化代码实现
# padding='SAME'
small_im1 = tf.nn.max_pool(cat,[1,2,2,1],[1,2,2,1],'SAME',name='same_max_pool')
# padding = "VALID"
small_im2 = tf.nn.max_pool(cat,[1,2,2,1],[1,2,2,1],'VALID',name = 'valid_max_pool')
# 激活tensor
small_im1_np,small_im2_np = sess.run([small_im1,small_im2])
#画图 padding='SAME'
plt.imshow(np.squeeze(small_im1_np),cmap='gray')
plt.title('shape={}'.format(small_im1_np.shape))
# 画图 padding='VALID'
plt.imshow(np.squeeze(small_im2_np),cmap='gray')
plt.title('shape={}'.format(small_im2_np.shape))
发现池化层中的补零策略,对输出结果的维度并没有影响。
总结,通过卷积和池化实现,我们发现,池化在维度下降一半的情况下,并没有丢失多少信息。