mnist是深度学习的“hello world”,谷歌、tensorflow官网都有详尽的教程介绍如何上手。之前的项目大多是在别人写好的框架上修改、发挥,理论上对原理算是熟悉的了,但是许多函数的理解有点知其然不知其所以然。因此想借这个项目,完全手动写一个CNN模型,好好地熟悉一下tensorflow框架。果然在写的过程中,踩到了许多雷。
tensorflow用的还是1.14.0版本,在使用过程中感觉许多函数功能相近,非常累赘,2.0有更加精简的框架。由于担心兼容性问题,暂时还是使用旧版本。
数据预处理
MNIST数据集来自美国国家标准与技术研究所(National Institute of Standards and Technology ,简称NIST)。包含了250个不同人的手写数字(其中50%为高中生,50%为人口普查局的工作人员)。MNIST 数据集包含四个部分:
Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 包含 60,000 个样本)
Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 包含 60,000 个标签)
Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 包含 10,000 个样本)
Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 包含 10,000 个标签)
作为tensorflow的入门级数据集,在tensorflow中已经内载了方便使用和下载的api,可以通过简单的代码下载并查看数据集内容。
def dataset_intro(args):
data_dir = args.data_dir
# read_data_sets 会自动判断当前目录下数据集是否已经存在
mnist = input_data.read_data_sets(data_dir, one_hot=True)
# 查看是否下载成功
# 784=28*28 10=10*1
print(mnist.train.images.shape, mnist.train.labels.shape) # (55000, 784) (55000, 10)
# 查看测试集数据大小
print(mnist.train.images.shape, mnist.train.labels.shape) # (10000, 784) (10000, 10)
# 打印出第0幅图片的向量表示
print(mnist.train.images[0, :])
# 打印出第0幅图片的标签
print(mnist.train.labels[0, :])
one_hot_label = np.argmax(mnist.train.labels[0, :])
print(one_hot_label)
# 使用jpg形式查看图片
save_dir = 'MNIST_data/draw/'
if os.path.exists(save_dir) is False:
os.makedirs(save_dir)
# 保存前20张图片为png形式
for i in range(20):
image_array = mnist.train.images[i, :]
image_array = image_array.reshape(28, 28)
image_array = image_array*255
filename = save_dir + 'mnist_train_%d.png' % i
img = Image.fromarray(image_array)
if img.mode == "F":
img = img.convert('RGB')
img.save(filename)
x = imgplt.imread('MNIST_data/draw/mnist_train_0.png')
plt.imshow(x)
plt.show()
每张图片是大小为28×28的二值图像,以[784×1]的数组形式存储,其label使用one-hot形式存储。可以从下面的图片中很直观地理解存储方式。
在处理过程中,有两点需要注意一下:
- 在矩阵中,存储的数值处于[0, 1]之间,如果直接用
matplotlib
包的Image.fromarray()
,save后会出现图片纯黑的情况。这是因为,png是以[0, 255]表示灰度值,需要image_array = image_array*255
将矩阵扩展。 - 使用PIL模块存储图像时,遇到了存储失败的问题:
OSError: cannot write mode F as JPEG
这是因为PIL有八种不同的颜色模块,其中灰度图像对应了F I L三种格式:
模式
1 1位像素,黑和白,存成8位的像素
L 8位像素,黑白
P 8位像素,使用调色板映射到任何其他模式
RGB 3×8位像素,真彩
RGBA 4×8位像素,真彩+透明通道
CMYK 4×8位像素,颜色隔离
YCbCr 3×8位像素,彩色视频格式
I 32位整型像素
F 32位浮点型像素
只需要将图片模式转为RGB就能顺利运行。
除此之外,无需其他特殊处理。
相关原理
卷积
卷积的概念是从猫的感受野引申过来的。在猫的视觉感受皮层中,每个神经元只和上一层的神经元相连接。可以用手电筒照墙壁来理解卷积的概念,手电筒在墙壁上探照的区域,就是感受野。手电筒接收了这一块区域的信息,就是卷积对这一块操作所提取的特征。层层卷积下去,最后只需要一个点,就能概括所有的信息。因此卷积是从将低维特征映射到高维特征的操作。
如上图所示,输入X可以看做一个5×5的灰度图像,filter大小为3×3,即卷积核,在步长为(1×1)的情况下,最终输出得到大小为3×3。卷积运算就是用卷积核覆盖在图像上,与覆盖区域进行乘加运算,例如图中黄色区域的卷积计算为:
7 ∗ 1 + 4 ∗ 1 + 8 ∗ 1 + 2 ∗ 0 + 3 ∗ 0 + 1 ∗ 0 + 0 ∗ ( − 1 ) + 4 ∗ ( − 1 ) + 7 ∗ ( − 1 ) = 8 7*1+4*1+8*1+2*0+3*0+1*0+0*(-1)+4*(-1)+7*(-1)=8 7∗1+4∗1+8∗1+2∗0+3∗0+1∗0+0∗(−1)+4∗(−1)+7∗(−1)=8
值得一提的是,上图演示的filter是图像处理中用于水平边缘检测的prewitt算子,能有效弱化中间部分。 w w w是神经网络需要学习的参数,不断优化网络提取特征的能力。
t e n s o r f l o w tensorflow tensorflow框架中提供了两种padding模式: s a m e same sa