本节将学习机器学习的分类开发应用,即MNIST手写数字的识别。对此,我们通过建立一个两层神经网络的模型来用于识别图片里面的数字。
MNIST是一个非常有名的手写体数字识别数据集,由60000个训练样本和10000个测试样本组成,每个样本都是一张28像素 × 28像素的灰度图像(1通道),各个像素的取值在0到255之间。每个图像数据都相应地标有“1”“2”“3”等标签,如下图所示。
下载以后的数据集包括:
至此,数据的准备工作已完成。
我们将在anaconda中集合的 jupyter notebook 中进行模型的训练。
tensorflow 提供了数据集读取的方法:
import tensorflow as tf
import tensorflow.examples.tutorials.mnist.input_data as input_data
mnist = input_data.read_data_sets("xxx", one_hot = True)
其中 “xxx” 是你存放数据集的路径,可通过数据集属性进行查看。
[tips]: 这个路径要自己手动输入才可以,不要忘了将路径中的’ \ ‘更换为’ / '。
在 jupyter 中运行之后结果为:
input_data.read_data_sets()
函数里面有两个参数:第一个为数据集所放的路径;第二个参数是one_hot (独热编码),设置为True,当然也可以把它设置为False,这两种情况下读进来的标签的数据格式是不一样的。当one_hot设置为True时,则对应的位置的值为1,其余都是0。
如果这个数字为3,就让下标为3的地方值为1,其他都为0。如果是8,就让下标为8的地方值为1,其他都为0。
[tips]: 如果是第一次读取MNIST数据集,在指定目录下不存在这个文件,那么它会自动去网上下载对应的文件,不过需要一定的等待时间;如果数据集已经存在了,那么它就会直接读取数据,速度会比较快。
定义全连接层函数
不管有多少层,对于每一层的定义都可以通过这个函数来解决。
def fcn_layer(inputs,
input_dim,
output_dim,
activation=None):
W = tf.Variable(tf.truncated_normal([input_dim, output_dim], stddev=0.1))
b = tf.Variable(tf.zeros([output_dim]))
XWb = tf.matmul(inputs,W) + b
if activation is None:
outputs = XWb
else:
outputs = activation(XWb)
return outputs
定义了一个函数 fcn_layer(),它有四个参数:
参数 | 解释 |
---|---|
inputs | 这一层网络的输入数据 |
input_dim | 输入神经元的数量 |
output_dim | 输出神经元的数量 |
activation | 激活函数(缺省值为None) |
x = tf.placeholder(tf.float32, [None, 784],name = 'X' )
y = tf.placeholder(tf.float32, [None, 10],name = 'Y' )
x是输入图像数据的占位符。每张图片是28×28大小的灰度图,有784列,相当于把它每⼀行像素点都拉平,形成⼀个⼀维数组。因为需要批量进行训练,每次代入的样本是⼀个批量,所以对于行先设为None。
y是标签数据的占位符,因为这个标签是转换成one_hot形式的,是⼀个十分类的标签,所以列数为10列,行跟刚才的特征值⼀样,用None保留,允许后面产生多行。
H1_NN = 256 # 第1隐藏层神经元为256个
H2_NN = 64 # 第2隐藏层神经元为64个
# 构建隐藏层1
h1 = fcn_layer(inputs=x,
input_dim=784,
output_dim=H1_NN,
activation=tf.nn.relu)
# 构建隐藏层2
h2 = fcn_layer(inputs=h1,
input_dim=H1_NN,
output_dim=H2_NN,
activation=tf.nn.relu)
# 构建输出层
forward = fcn_layer(inputs=h2,
input_dim=H2_NN,
output_dim=10,
activation=None)
pred = tf.nn.softmax(forward)
这里可以直接调用tensorflow中提供的 softmax函数 来实现分类,即把计算出来的y值通过softmax函数转换成为它属于这十个类别中哪⼀类的概率值,转换后的y是一个有十个元素的向量,其中的每个分量表示属于对应类别的概率值。
loss_function = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = forward, labels = y))
tf.nn.softmax_cross_entropy_with_logits() 函数是tensorflow中常用的求交叉熵的函数。第一个参数 logits:就是神经网络最后一层的输出,如果有batch的话,它的大小就是[batchsize,y],单样本的话,大小就是y。
train_epochs = 40
batch_size = 50
total_batch = int(mnist.train.num_examples/batch_size)
display_step = 1
learning_rate = 0.01
所谓的训练模型就是根据训练的结果去调整这些超参数。
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss_function)
tensorflow中常见的优化器有:GradientDescentOptimizer、AdagradOptimizer、AdagradDAOptimizer、MomentumOptimizer、AdamOptimizer、RMSPropOptimize.
AdamOptimizer 是梯度下降算法的一种变形,但是每次迭代参数的学习率都有一定的范围,不会因为梯度很大而导致学习率(步长)也变得很大,参数的值相对比较稳定。
from time import time
startTime=time()
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
for epoch in range(train_epochs):
for batch in range(total_batch):
xs,ys = mnist.train.next_batch(batch_size)
sess.run(optimizer, feed_dict={x:xs, y:ys})
loss,acc = sess.run([loss_function,accuracy],
feed_dict = {x:mnist.validation.images, y:mnist.validation.labels})
if(epoch+1) %display_step == 0:
print("Train Epoch:",'%02d' %(epoch+1),"Loss=","{:.9f}".format(loss),"Accuracy=","{:.4f}".format(acc))
duration = time() - startTime
print("Train Finished takes:","{:.2f}".format(duration))
训练后的结果如下图所示。
用测试集对训练好的模型进行评估,最后的结果为 97.58%。
accu_test = sess.run(accuracy,feed_dict={x:mnist.test.images, y:mnist.test.labels})
print("Test Accuracy:", accu_test)
import tensorflow as tf
import tensorflow.examples.tutorials.mnist.input_data as input_data
mnist = input_data.read_data_sets("xxx", one_hot = True)
# 定义全连接层函数
def fcn_layer(inputs, # 输入数据
input_dim, # 输入神经元数量
output_dim, # 输出神经元数量
activation=None): # 激活函数
W = tf.Variable(tf.truncated_normal([input_dim, output_dim], stddev=0.1))
b = tf.Variable(tf.zeros([output_dim]))
XWb = tf.matmul(inputs,W) + b
if activation is None: # 默认不使用激活函数
outputs = XWb
else: # 若传入激活函数,则用其对输出结果进行变换
outputs = activation(XWb)
return outputs
# 定义标签数据占位符
x = tf.placeholder(tf.float32, [None, 784],name = 'X' )
# 隐藏层神经元数量
H1_NN = 256 # 第1隐藏层神经元为256个
H2_NN = 64 # 第2隐藏层神经元为64个
# 构建隐藏层1
h1 = fcn_layer(inputs=x,
input_dim=784,
output_dim=H1_NN,
activation=tf.nn.relu)
# 构建隐藏层2
h2 = fcn_layer(inputs=h1,
input_dim=H1_NN,
output_dim=H2_NN,
activation=tf.nn.relu)
# 构建输出层
forward = fcn_layer(inputs=h2,
input_dim=H2_NN,
output_dim=10,
activation=None)
pred = tf.nn.softmax(forward)
y = tf.placeholder(tf.float32, [None, 10],name = 'Y' )
loss_function = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = forward, labels = y))
# 设置训练参数
train_epochs = 40 #训练轮数
batch_size = 50 # 单次训练样本数(批次大小)
total_batch = int(mnist.train.num_examples/batch_size) # 一轮训练有多少批次
display_step = 1 # 显示粒度
learning_rate = 0.01 # 学习率
# 梯度下降优化器
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss_function)
# 定义准确率
# 检查预测类别tf.argmax(pred,1)与实际类别tf.argmax(y,1)的匹配情况
correct_prediction = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
# 准确率,将布尔值转化为浮点数,并计算平均值
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 记录训练开始时间
from time import time
startTime=time()
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
for epoch in range(train_epochs):
for batch in range(total_batch):
xs,ys = mnist.train.next_batch(batch_size) # 读取批次数据
sess.run(optimizer, feed_dict={x:xs, y:ys})
# total_batch个批次训练完成后,使用验证数据计算误差与准确率;验证集没有分批
loss,acc = sess.run([loss_function,accuracy],
feed_dict = {x:mnist.validation.images, y:mnist.validation.labels})
# 打印训练过程中的详细信息
if(epoch+1) %display_step == 0:
print("Train Epoch:",'%02d' %(epoch+1),"Loss=","{:.9f}".format(loss),"Accuracy=","{:.4f}".format(acc))
# 显示运行总时间
duration = time() - startTime
print("Train Finished takes:","{:.2f}".format(duration))
accu_test = sess.run(accuracy,feed_dict={x:mnist.test.images, y:mnist.test.labels})
print("Test Accuracy:", accu_test)