TensorFlow学习笔记
问题描述
TensorFlow是一个非常强大的用来做大规模数值计算的库。其所擅长的任务之一就是实现以及训练深度神经网络。MINIST是一个入门级的计算机视觉数据集,包含了60000行的训练数据集和10000行的测试数据集。
本文将会基于Tensorflow框架,构建
- 单一线性层的SoftMax回归模型
- 多层卷积神经网络的SoftMax回归模型
两个模型来实现对MNIST数据集的学习和预测。并会对上述两个模型的学习效果进行比较和讨论。
方法过程
关于MNIST的模型抽象
- MNIST是一个入门级的计算机视觉数据集,每个数据单元由两部分组成:一张包含手写数字的图片和一个对应的标签。我们把这些图片设为“xs”,把这些标签设为“ys”.每张图片包含 28 × 28 28\times28 28×28像素,因此我们可以用一个长度为784的数组来表示每张图片。
- 因此,MNIST训练数据集的图片是一个形状为[60000,784]的张量,第一个维度数字用来索引图片,第二个维度数字用来索引每张图片中的像素点。在此张量里的每一个元素,都表示某张图片里的某个像素的强度值,值介于0和1之间。
- 相对应MNIST数据集的标签是介于0—9之间的数字,用于描述图片中给的数字。我们采用one-hot向量表示标签,即数字n表示只有在第n个维度数字为1的10维向量。因此MNIST数据集的标签是一个[60000,10]的数字矩阵。
Softmax回归介绍
-
为了得到一张给定图片属于某个特定数字类的证据,我们对图片像素值进行加权求和。若权值为正则属于,反之不属于。
-
我们定义第 w i , j w_i,_j wi,j表示第 j j j个像素块会被识别为数字 i i i的概率大小。同时,定义 b i b_i bi代表第 i i i类数字的偏置量。则对于给定的图片 x x x它代表数字 i i i的证据可以表示为:
e v i d e n c e i = ∑ j w i , y × x j + b i evidence_i = \sum\limits_{j}w_i,_y\times x_j+b_i evidencei=j∑wi,y×xj+bi
之后我们便可以通过softmax函数将这些证据表示成概率 y i y_i yi:
y i = s o f t m a x ( e v i d e n c e i ) y_i = softmax(evidence_i) yi=softmax(evidencei) -
为了提高计算效率,我们采取向量的表示方法表示这个计算的过程,得到更加紧凑的形式。
y = s o f t m a x ( w × x + b ) y = softmax(w\times x+b) y=softmax(w×x+b)
其中, w = ( w 1 , 0 w 1 , 1 ⋯ w 1 , 9 w 2 , 0 w 2 , 1 ⋯ w 2 , 9 ⋮ ⋮ ⋱ ⋮ w 784 , 0 w 784 , 1 ⋯ w 784 , 9 ) w = \left( \begin{matrix} w_1,_0&w_1,_1&\cdots&w_1,_9 \\ w_2,_0&w_2,_1&\cdots&w_2,_9 \\ \vdots & \vdots & \ddots & \vdots \\ w_{784},_0&w_{784},_1&\cdots&w_{784},_9 \end{matrix} \right) w=⎝⎜⎜⎜⎛w1,0w2,0⋮w784,0w1,1w2,1⋮w784,1⋯⋯⋱⋯w1,9w2,9⋮w784,9⎠⎟⎟⎟⎞b = ( b 0 , b 1 , . . . , b 9 ) \left(b_0,b_1,...,b_9\right) (b0,b1,...,b9)
为该图片是经过softmax后被认为是各个数字的概率分布。
-
下面我们要来训练我们的模型。在机器学习,我们通常定义指标来表示一个模型是坏的,这个指标称为成本或损失,然后尽量最小化这个指标。这里我们采用交叉熵来作为损失函数:
H y ′ ( y ) = − ∑ i y i ′ × l o g ( y i ) H_y'(y) = - \sum\limits_{i}y_i'\times log(y_i) Hy′(y)=−i∑yi′×log(yi)
交叉熵越小,则说明我们基于模型预测的概率分布 y y y与实际的分布 y ’ y’ y’越接近,即学习效果越好。之后,我们可以通过梯度下降算法来最小化我们的交叉熵。 -
训练结束后,我们将通过预测来测试我们的模型的正确性,
tf.equals
可以用来检测我们的预测是否与真实标签匹配。通过对正确预测项的统计可以得到我们学习的模型在测试数据集上的正确率,也即学习的效果。
单一线性层的SoftMax回归模型
此模型为以一个单层的神经网络,包含一个输入层和一个输出层。
- 输入层
- 读入MNIST数据并设置为独热码存储。
from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf mnist = input_data.read_data_sets("MINST_data/", one_hot=True)
- 设置输入层张量,初始化权重与偏置量。
这里输入层张量使用了占位符来表示,可以在TensorFlow运行某一计算时根据该占位符输入具体的值。x = tf.placeholder("float", shape = [None, 784]) W = tf.Variable(tf.zeros([784,10])) b = tf.Variable(tf.zeros([10]))
shape
代表图片数量不定,但是每个图片都用长度为784的向量表示。 - 输出层
- 输出层负责计算,设置输出层张量。我们把向量化后的图片 x x x和权重矩阵 w w w相乘,加上偏置 b b b,然后计算每个分类的 s o f t m a x softmax softmax概率值。
y_ = tf.placeholder("float", shape = [None, 10]) y = tf.nn.softmax(tf.matmul(x,W) + b)
- 通过梯度下降算法以0.01的学习率最小化交叉熵。
cross_entropy = -tf.reduce_sum(y_*tf.log(y)) train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
- 模型评估
由此我们可以得到该模型在测试数据上的准确率。correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
多层卷积神经网络的SoftMax回归模型
此模型为一个多层的卷积神经网络,由两个“卷积层+池化层”,再加上全连接层组成。
-
权重初始化
为了创建这个模型,我们需要创建大量的权重和偏置项。为了避免duplicate code,我们定义两个函数用于初始化。def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial)
其中,我们在权重初始化时加入了少量的噪声来打破对称性以及避 免零梯度的问题。另外,由于我们使用的是ReLU神经元,因此比较好的做法是用一个较小的正数来初始化偏置项,以避免神经元节点输出恒为0的问题。
-
卷积与池化
我们的卷积使用1步长,0边距的模板,保证输出和输入是同一个大小。我们的池化用简单传统的2x2大小的模板做maxpooling。为了代码更简洁,我们把这部分也抽象成一个函数。def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
其中,对于
tf.nn.conv2d
函数x
指的是需要进行卷积运算的图像。它是一个长度为4 的一维张量。四个分量分别为:训练图像数目,图像高度,图像宽 度和图像通道数。W
是卷积神经网络中的卷积核,它也是长度为4 的一维张量。四个分量分别为:卷积核高度,卷积核宽度,图像通道数和卷积核个数strides
是指卷积时的步长,padding
是指卷积边缘的填充方式。
-
第一层“卷积+池化”
首先进行权重和偏置的初始化。W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]
为了用这一层,我们把 x x x变成一个4维向量,其第2、3维对应图片的宽、高,最后一维代表图片的颜色通道数(因为是灰度图所以这里的通道数为1)。
x_image = tf.reshape(x, [-1,28,28,1])
我们把
x_image
和权值向量进行卷积,加上偏置项,然后应用ReLU激活函数,最后进行max pooling池化。h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1)
-
第二层“卷积+池化”
将第一层神经网络池化后的数据输入到第二层神经网络中,再依次进行卷积,激活,池化操作。W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2)
-
全连接层
现在,图片尺寸减小到7x7,我们加入一个有1024个神经元的全连接层,用于处理整个图片。我们把池化层输出的张量reshape
成一些向量,乘上权重矩阵,加上偏置,然后对其使用ReLU。W_fc1 = weight_variable([7 * 7 * 64, 1024]) b_fc1 = bias_variable([1024]) h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
-
Dropout减少过拟合
在机器学习中,又可能由于训练集选取的不随机,导致模型对当前训练集拟合程度很好,对新数据集预测效果不好的情况(我们称之为过拟合)。因此,我们可以采取dropout机制随机屏蔽一些神经元的输出来减少过拟合现象。keep_prob = tf.placeholder("float") h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
其中,
keep_prob
指的是每个神经元的结果被使用的概率。 -
输出层
最后,我们添加一个softmax层,就像前面的单层softmax regression一样。W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
-
模型评估
为了进行训练和评估,我们使用与之前简单的单层SoftMax神经网络模型几乎相同的一套代码,只是我们会用更加复杂的ADAM优化器来做梯度最速下降,在feed_dict
中加入额外的参数keep_prob
来控制dropout
比例。然后每100次迭代输出一次日志。cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) sess.run(tf.global_variables_initializer()) for i in range(20000): batch = mnist.train.next_batch(50) if i%100 == 0: train_accuracy = accuracy.eval(feed_dict={ x:batch[0], y_: batch[1], keep_prob: 1.0}) print("step %d, training accuracy %g"%(i, train_accuracy)) train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5}) print("test accuracy %g"%accuracy.eval(feed_dict={ x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
结果展示
单一线性层的SoftMax回归模型
-
模型实现
from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf if __name__ == '__main__': mnist = input_data.read_data_sets("MINST_data/", one_hot=True) sess = tf.InteractiveSession() x = tf.placeholder("float", shape = [None, 784]) y_ = tf.placeholder("float", shape = [None, 10]) W = tf.Variable(tf.zeros([784,10])) b = tf.Variable(tf.zeros([10])) y = tf.nn.softmax(tf.matmul(x,W) + b) cross_entropy = -tf.reduce_sum(y_*tf.log(y)) train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) sess.run(tf.global_variables_initializer()) for i in range(1000): batch = mnist.train.next_batch(50) train_step.run(feed_dict={x: batch[0], y_: batch[1]}) print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
-
模型评估
单一线性层的SoftMax回归模型的预测准确率为91.1%。
多层卷积神经网络的SoftMax回归模型
-
模型实现
from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial) def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') if __name__ == '__main__': mnist = input_data.read_data_sets("MINST_data/", one_hot=True) sess = tf.InteractiveSession() x = tf.placeholder("float", shape = [None, 784]) y_ = tf.placeholder("float", shape = [None, 10]) #第一层 W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]) x_image = tf.reshape(x, [-1,28,28,1]) h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1) #第二层 W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2) #全连接层 W_fc1 = weight_variable([7 * 7 * 64, 1024]) b_fc1 = bias_variable([1024]) h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) #dropout防止过拟合 keep_prob = tf.placeholder("float") h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) #softmax输出 W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2) cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) sess.run(tf.global_variables_initializer()) for i in range(20000): batch = mnist.train.next_batch(50) if i%100 == 0: train_accuracy = accuracy.eval(feed_dict={ x:batch[0], y_: batch[1], keep_prob: 1.0}) print("step %d, training accuracy %g"%(i, train_accuracy)) train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5}) print("test accuracy %g"%accuracy.eval(feed_dict={ x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
-
模型评估
多层卷积神经网络的SoftMax回归模型的预测准确率为99.1%。
讨论总结
-
两种模型的对比
两种模型在准确率上显著不同,多层神经网络训练的模型在对新数据集进行预测时会表现出显著的优势。
-
为什么两层神经网络的效果更好?
理论证明,两层神经网络可以无限逼近任意连续函数。也就是说,面对复杂的非线性分类任务,两层(带一个隐藏层)神经网络可以分类的很好。但是,单层网络只能做线性分类任务,而两层神经网络中的后一层也是线性分类层,应该只能做线性分类任务,为何两层神经网络就可以对任意非线性函数有如此号的拟合效果呢?
关键的一点是,从输入层到隐藏层时数据发生了空间变换。也就是说,两层神经网络中,隐藏层对原始的数据进行了一个空间变换,使其可以被线性分类,然后输出层的决策分界划出了一个线性分类分界线,对其进行分类。
这样就导出了两层神经网络可以做非线性分类的关键–隐藏层。两层神经网络通过两层的线性模型模拟了数据内真实的非线性函数。因此,多层的神经网络的本质就是复杂函数拟合。
-
总结
通过这次实验,我们使用了tensorflow这一强大的用来做大规模计算的库,基于MNIST数据集,搭建、训练和评估了单一线性层的SoftMax回归模型和多层卷积神经网络的SoftMax回归模型。我们发现多层卷积神经网络能够提升模型的预测准确率,达到对数据集更好的学习效果。