教程传送门:深度学习应用开发-TensorFlow实践 第七讲
完整笔记
一、问题描述
MNIST数据集由来自250个不同人手写的数字构成,包括55000个训练集,5000个验证集和10000个测试集。
二、读取数据集
1. 数据集解读
- MNIST数据集获取
#TensorFlow提供了数据集读取方法
import tensorflow as tf
import tensorflow.examples.tutorials.mnist.input_data as input_data
#下载MNIST数据集到指定目录下(指定独热编码)
mnist = input_data.read_data_sets("MNIST_data/", one_hot = True)
#查看数量
print('训练集 train 数量:', mnist.train.num_examples,
',验证集 validation 数量:', mnist.validation.num_examples,
',测试集 test 数量:', mnist.test.num_examples)
# 训练集 train 数量: 55000 ,验证集 validation 数量: 5000 ,测试集 test 数量: 10000
#查看shape
print('train images shape:', mnist.train.images.shape,
'labels shape:',mnist.train.labels.shape)
# train images shape: (55000, 784) labels shape: (55000, 10)
#图像是 28x28=784, 标签共 10 类,0-9
#具体看一副image的数据
mnist.train.images[0].shape
# (784,)
#image数据再塑形
mnist.train.images[0].reshape(28, 28) #按行优先,逐行排列
- 独热编码(one hot encoding)
一种稀疏向量,其中一个元素设为1,所有其他元素均为0。常用于表示拥有有限个可能值的字符串或标识符。
#下载MNIST数据集到指定目录下(指定独热编码)
mnist = input_data.read_data_sets("MNIST_data/", one_hot = True)
mnist.train.labels[1]
# array([0., 0., 0., 1., 0., 0., 0., 0., 0., 0.])
#表示数字3
#独热编码取便签值:使用 argmax
import numpy as np
np.argmax(mnist.train.labels[1]) #argmax返回的是最大数的索引
# 3
1.使用one hot编码,将离散特征的取值拓展到了欧式空间,离散特征的某个取值就对应欧式空间的某个点。
2.机器学习算法中,特征之间距离的计算或相似度的常用计算方法是基于欧式空间的。
(如若不用独热编码,按距离计算,说1比8更相似于3就不合理。)
3.将离散型特征使用one-hot编码,会让特征之间的距离计算更加合理。
- 非one-hot编码的标签值
mnist_no_one_hot = input_data.read_data_sets("MNIST_data/", one_hot = False)
print(mnist_no_one_hot.train.labels[0:10])
# [7 3 4 6 1 8 1 0 9 8]
#one_hot = False,直接返回标签对应的数值
2. 可视化image
#定义可视化image的方法
import matplotlib.pyplot as plt
def plot_image(image):
plt.imshow(image.reshape(28, 28), camp = 'binary')
plt.show()
#可视化images[1]
plot_image(mnist.train.images[1])
3. 数据集划分
通过将数据集划分为三个子集,可以大幅降低过拟合的发生几率:
使用验证集评估训练集的效果。
在模型“通过”验证集后,使用测试集再次检查评估结果。
4. 数据的批量读取
#传统方法,前10条
print(mnist.train.labels[0:10])
#内部会对数据集先做shuffle打乱数据
batch_images_xs, batch_labels_ys = mnist.train.next_batch(batch_size = 10)
print(batch_labels_ys)
三、分类模型构建与训练
1. 模型构建
- 定义待输入数据的占位符
#mnist 中每张图片共有28*28=784个像素点
x = tf.placeholder(tf.float32, [None, 784], name = "X")
#0-9一共10个数字 => 10个类别
y = tf.placeholder(tf.float32, [None, 10], name = "Y")
- 定义模型变量
在本案例中,以正态分布的随机数初始化权重W,以常数0初始化偏置b
#定义变量
W = tf.Variable(tf.random_normal([784, 10]), name = "W")
b = tf.Variable(tf.zeros([10]), name = "b")
- 了解一下tf.random_normal()
从“服从指定正态分布的序列”中随机取出指定个数的值
norm = tf.random_normal([100]) #生成100个随机数
with tf.Session() as sess:
norm_data = norm.eval()
print(norm_data[:10]) #打印前10个随机数
# [ 0.72286093 0.1870783 0.00341146 0.41772947 0.66194445 0.08350101
-3.12047291 -0.87533593 0.36186498 0.94298702]
import matplotlib.pyplot as plt
plt.hist(norm_data) #绘制直方图
plt.show()
- 定义前向计算
forward = tf.matmul(x, W) + b #前向计算
- 结果分类
pred = tf.nn.softmax(forward) #Softmax分类
2. 逻辑回归
许多问题的预测结果是一个数值,比如房价预测问题,可以用线性模型来描述:
Y=x_1*w_1+x_2*w_2+...+x_n*w_n+b
但也有很多场景需要输出的是概率估算值,例如:
(1)根据邮件内容判断是垃圾邮件的可能性
(2)根据医学影像判断肿瘤是恶性的可能性
(3)手写数字分别是0、1、2、3...8、9的可能性(概率)
这时需要将预测输出值控制在[0,1]区间内。
二元分类问题的目标是正确预测两个标签中的一个,逻辑回归可以用于处理这类问题。
- Sigmod函数
Sigmod函数(S型函数)生成的输出值正好具有这些特性,定义域为全体实数,值域在[0,1]之间,Z值在0点对应的结果为0.5,且sigmod函数连续可微分。
y=\frac{1}{1+e^{-z}}
z=x_1*w_1+x_2*w_2+...+x_n*w_n+b
- 逻辑回归中的损失函数
线性回归的损失函数是平方损失,而若逻辑回归也用平方损失,将Sigmod函数代入平方损失函数,将有多个极小值,如果采用梯度下降法,会容易导致陷入局部最优解。
二元逻辑回归的损失函数一般采用对数损失函数,其中(x,y)∈D是有标签样本(x,y)的数据集,y是有标签样本中的标签,取值必须是0或1,y'是对于特征集x的预测值(介于0和1之间)
J(W,b)= \sum_{(x,y)∈D}-ylog(y')-(1-y)log(1-y')
3. 多元分类和Softmax
- Softmax思想
在多分类问题中,Softmax会为每个类别分配一个用小数表示的概率。这些用小数表示的概率相加之和必须为1.0.
- Softmax方程式
p_i = \frac{e^{y_i}}{\sum_{k=1}^{C}e^{y_k}}
- 交叉熵损失函数
交叉熵是一个信息论中的概念,它原来是用来估算平均编码长度的。给定两个概率分布p和q,通过q来表示p的交叉熵为
H(p,q) = - \sum_xp(x)logq(x)
交叉熵刻画的是两个概率分布之间的距离,p代表正确答案,q代表的是预测值,交叉熵越小,两个概率的分布约接近,损失越低。
- 定义交叉熵损失函数
yi为标签值,yi'为预测值,公式如下:
Loss = - \sum_{i=1}^ny_ilogy_i'
#定义交叉熵损失函数
loss_function = tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred),
reduction_indices=1))
4. 模型构建与训练实践
- 设置训练参数
train_epochs = 50 #训练轮数
batch_size = 100 #单次训练样本数(批次大小)
total_batch = int(mnist.train.num_examples/batch_size) #一轮训练有多少批次
display_step = 1 #显示粒度
learning_rate = 0.01 #学习率
- 选择优化器
#梯度下降优化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss_function)
- argmax详解
import tensorflow as tf
import numpy as np
arr1 = np.array([1,3,2,5,7,0])
arr2 = np.array([[1.0,2,3],[3,2,1],[4,7,2],[8,3,2]])
print("arr1=", arr1)
print("arr2=\n", arr2)
# arr1= [1 3 2 5 7 0]
# arr2=
# [[ 1. 2. 3.]
# [ 3. 2. 1.]
# [ 4. 7. 2.]
# [ 8. 3. 2.]]
#返回最大值的下标
argmax_1 = tf.argmax(arr1)
#指定第二个参数为0,按第一维(行)的元素取值,即同列的每一行最大值的下标
argmax_20 = tf.argmax(arr2, 0) #指定第二个参数为1,按第二维(列)的元素取值,即同行的每一列最大值的下标
argmax_21 = tf.argmax(arr2, 1)
#指定第二个参数为-1,则第最后维的元素取值
argmax_22 = tf.argmax(arr2, -1)
with tf.Session() as sess:
print(argmax_1.eval())
print(argmax_20.eval())
print(argmax_21.eval())
print(argmax_22.eval())
# 4
# [3 2 0]
# [2 0 1 0]
# [2 0 1 0]
- 定义准确率
#检查预测类别tf.argmax(pred,1)与实际类别tf.argmax(y,1)的匹配情况,相等为1,不等为0,实际要转浮点数
correct_prediction = tf.equal(tf.argmax(pred, 1),tf.argmax(y, 1))
#准确率,将布尔值转为浮点数,并计算平均值
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
- 声明会话,初始化变量
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))
print("Train Finished!")
# Train Epoch: 01 Loss= 5.441855431 Accuracy= 0.3148
# ...(Loss趋于更小,准确率越来越高,其余轮次略)
# Train Epoch: 50 Loss= 0.655943811 Accuracy= 0.8618
# Train Finished!
- 评估模型
完成训练后,在10000条测试集上评估模型的准确率
accu_test = sess.run(accuracy,
feed_dict = {x: mnist.test.images, y: mnist.test.labels})
print("Test Accuracy:",accu_test)
# Test Accuracy: 0.8619
5. 模型应用与可视化
- 应用模型
#由于pred预测结果是one-hot编码格式,所以需要转换为0-9数字
prediction_result = sess.run(tf.argmax(pred,1),feed_dict={x: mnist.test.images})
#查看预测结果中的前10项
prediction_result[0:10]
# array([7, 6, 1, 0, 4, 1, 4, 9, 6, 9], dtype=int64)
- 定义可视化函数
#定义可视化函数
import matplotlib.pyplot as plt
import numpy as np
def plot_images_labels_prediction(images, #图像列表
labels, #标签列表
prediction, #预测值列表
index, #从第index个开始显示
num = 10): #缺省一次显示10幅
fig = plt.gcf() #获取当前图表,Get Current Figure
fig.set_size_inches(10, 12) #宽10英寸高12英寸,1英寸等于2.54cm
if num > 25:
num = 25 #最多显示25个子图
for i in range(0, num):
ax = plt.subplot(5,5, i+1) #获取当前要处理的子图,i+1 从第1个子图开始到num+1个子图
ax.imshow(np.reshape(imges[index],(28, 28)),
cmap = 'binary') #显示第index个图像
title = "label=" + str(np.argmax(labels[index])) #构建该图上要显示的title
if len(prediction)>0:
title += ",predict=" + str(prediction[index])
ax.set_title(title,fontsize = 10) #显示图上的title信息
ax.set_xticks([]); #不显示坐标轴
ax.set_yticks([])
index += 1
plt.show()
- 可视化预测结果
plot_images_labels_prediction(mnist.test.images,
mnist.test.labels,
prediction_result,10,10)