在这个文章里,将通过数据的变化,来演示卷积神经网络中最大池化(MaxPooling),
最大池化(MaxPooling)是卷积神经网络的第二步,
而本文的目的仅仅只是为了说明ksize、strides与padding参数
在 TensorFlow实例(5.3)--MNIST手写数字进阶算法(卷积神经网络CNN) 文章中,
第一层经网络用到下列句子
tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')
第二个参数x,为传入量,也就是卷积神经网络的第一步卷积(Convolution)的输出量
x的shape必须为[batch, height, width, channels],
batch:在卷积的传入量可能是一张图片,也可能是N张图片,这里可以理解为图片数量,后面的例子都为1
height:图片的高度
width:图片的宽度
channels:通道数量,在Mnist中的第一步卷积,产生的是32个通道数量
本文的目的只是为了讲解MaxPooling,而不是神经网络,因为我会用一个函数构建x
第二个参数ksize:池化窗口的大小,shape也是[batch, height, width,channels],
在batch和channels上我们都设为1,当后面理解了height, width所代表的意思,batch和channels也就明白了
第三个参数strides:步长,一般也是[1, stride,stride, 1],参数和ksize差不多,功能不同
第四个参数padding:只能取'VALID' 或者'SAME'这两个值,但有什么差别,在后面的例子中演示
# -*- coding: utf-8 -*-
import numpy as np
import tensorflow as tf
def createData(height,width,channels):
''' 辅助函数一
为了能更清晰的演示,建立这个函数,目的创建数据,即第一个参数x
因为多张图片的传入量不好表示,batch值就固定为1
返回值类型为numpy.array
'''
size=height*width
tmp=[]
for i in range(size):
item=[]
for j in range(channels):
item.append(j*size+i+1)
tmp.append(item)
tmp=np.array([tmp]).reshape(-1,height,width,channels)
return tmp
def printData(npArr,message=""):
'''辅助函数二
打印出图片数据
多通道图片都是模排显示,如果宽度及通道数太大,
会每行宽度的原因自动换行,
'''
npArr=npArr[0]
print("-"*40 + " 开始:"+message)
print("图像信息:高:%d; 宽:%d; 通道:%d。以下为图像数据"%(len(npArr),len(npArr[0]),len(npArr[0][0])))
print("");
for i in npArr:
tmp=i.T
strtmp='';
for j in tmp:
strItem=''.join([x.rjust(4) for x in j.astype(np.str).tolist()])
strtmp=strtmp+("" if strtmp=='' else " |")+strItem
print(strtmp)
print("-"*40 + " 结束:"+message)
print("")
def max_pool(data,ksize,strides,padding):
'''辅助函数三,目的是最大池化后返回numpy.array'''
tmp=tf.nn.max_pool(data,ksize=ksize,strides=strides,padding=padding)
return tf.Session().run(tmp)
#printData(createData(3,4,2));#测试以上两个函数结果如下:用|分割每个通道
#1 2 3 4 | 13 14 15 16
#5 6 7 8 | 17 18 19 20
#9 10 11 12 | 21 22 23 24
#以下代码不再列出计算结果,要不整个代码显示很零散,自行运行后理解
data=createData(4,7,2)
printData(data,'案例一数据构建')
pool=max_pool(data,ksize=[1,2,2,1],strides=[1,1,1,1],padding="SAME")
printData(pool,"设置ksize值,可以看到每个通到都从图片的(2,2)的位值开始取值")
pool=max_pool(data,ksize=[1,1,1,1],strides=[1,2,2,1],padding="SAME")
printData(pool,"设置strides步长值,ksize从图片的(1,1) 每隔两位取值")
#从步长的取值,就可以知道,图片的缩放是由strides这个参数决定的
pool=max_pool(data,ksize=[1,2,2,1],strides=[1,3,3,1],padding="SAME")
printData(pool,"请先自行验证你推出的数据是否与显示的相同")
#错了吧,
#如最开始的
# 9 12 14 | 37 40 42
#23 26 28 | 51 54 56
#data中宽为 7 ,步长为3,所以应该 取3个值 int(7/3)+1=3 整除就不有+1了
# 因为我们从2位开始取,,第3个值应该是8,超出了宽,第3个值就用最后一列补
#同理
#高为4 ,步长为3,取2个值,从2行开始取,第2个值应取第5行,超出了高,第2个值就用最后一行补
pool=max_pool(data,ksize=[1,2,2,1],strides=[1,3,3,1],padding="VALID")
printData(pool,"这才是你算出来的值,VALID简单的说就是超出不补")
data=createData(2,2,6)
printData(data,'案例二简单试试通道')
#注意,通道的取值方式和高宽一致但有地方不同
#1、当ksize中的channel=1,一定是取全部通道,
#2、padding没有什么用
#当ksize中的 channel!=1 时两个条件,
#1、输入的通道数 能整除ksize中的channel, 即12 mod 2=0
#2、产生的ksize中的channel,与 strides 中channel必须相同
pool=max_pool(data,ksize=[1,1,1,2],strides=[1,1,1,2],padding="SAME")
printData(pool)