手写分类识别一
MNIST手写数字识别:分类问题
一、数据准备
-
MNIST数据集来自美国国家标准与技术研究所,National Institute of Standards and Technology(NIST)
-
数据集由来自250个不同人手写的数字构成,其中50%是高中学生,50%来自人口普查局(the Census Bureau)的工作人员
-
MNIST数据集可在http://yann.lecun.com/exdb/mnist/获取
-
Tensorflow提供了数据集读取方法
import tensorflow as tf import tensorflow.examples.tutorials.mnist.input_data as input_data mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)#读取数据(数据集缩放的文件目录,标签的数据格式)
如果在读取时指定目录不存在,则会自动去下载,需要等待一定时间,如果存在了,则直接读取。
-
了解MNNIST手写数字识别数据集
print('训练集train数量:',mnist.train.num_examples,",验证集validation数量:",mnist.validatio.num_examples,"测试集test数量:",mnist.test.num_examples) print("train images shape:",mnist.train.images.shape,"labels shaple:",mnist.train.labels.shape)
-
可视化image
import matplotlib.pyplot as plt def plot_image(image): plt.imshow(image.reshape(28,28),cmap="binary") plt.show() #打印下标为20000的图片 plot_image(mnist.train.images[20000])
-
思考:以下代码会输出什么图像?
plt.imshow(mnist.train.images[20000].reshape(14,56),cmap="binary") plt.show()
二、标签数据和独热i编码
-
认识标签labeel
mnist.train.labels[1]
-
独热编码(one hot encoding)
一种稀疏向量,其中:一个元素设为1,所有其他元素均为0
独热编码常用于表示拥有有限个可能值的字符串或标示符。
例如:假设某个植物学数据集记录了15000个不同的物种,其中每个物种都用独一无二的字符串标识符来表示。在特征工程过程中,可能需要将这些字符串标识符编码为独热向量,向量的大小为15000
-
为什么要采用one hot编码?
- 将离散特征的取值扩展到了欧式空间,离散特征的某个取值就对应欧式空间的某个点。
- 机器学习算法中,特征之间距离的计算或相似度的常用计算方法都是基于欧式空间的。
- 将离散型特征使用one hot 编码,会让特征之间的距离计算更加合理。
三、数据集的划分
-
构建和训练机器学习模型是希望对新的数据做出良好预测
-
如何去保证训练的实效,可以应对前所未见的数据呢?
一种方法是将数据集分为两个子集:训练集(用于训练模型的子集),测试集(用于测试模型的子集)。
-
在测试集上表现是否良好是衡量能否在新数据上表现良好的有用指标,前提是:一、测试集足够大。二、不会反复使用相同的测试集来作假。
-
拆分数据:将当个数据集拆分为一个训练集和一个测试集。
确保测试集满足以下两个条件:一、规模足够大,可产生具有统计意义的结果。二、能代表整个数据集,测试集的特征应该与训练集的特征相同。
-
新的划分:将数据集划分为三个子集,可以大幅度降低拟合的发生几率:使用验证集评估训练集的效果。在模型”通过“验证集之后,使用测试集再次检查评估结果。
-
读取验证数据
print(" validation images:",mnist.validation.images.shape,"labels:",mnist.validation.labels.shape)
-
读取测试数据
print("test images:",mnist.test.images.shape,"labels:",mnist.test.labels.shape)
四、数据的批量读取
-
一次批量读取多条数据
print(mnist.train.labels[0:10])
-
next_batch()实现内部会对数据集先做shuffle(洗牌)/(打乱)
#数据洗牌得到的10条样本 batch_images_xs,batch_labels_ys=mnist.train.next_batch(batch_size=10) #输出两个值的shape print(batch_images_xs.shape,batch_labels_ys.shape) #输出两个值 print(batch_images_xs) print(batch_labels_ys)
五、 模型构建
-
定义带输入数据的占位符
#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")
2, 定义模型变量
在本案例中,以正态分布的随机数初始化权重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个随机数
#使用图形化模式观察 import matplotlib.pyplot as plt plt.hist(norm_data) plt.show()
-
定义前向计算
forward = tf.matmul(x,W)+b #前向计算
-
结果分类
pred = tf.nn.softmax(forward)#Softmax分类
六、逻辑回归
-
许多问题的预测结果是一个在连续空间的数值,比如房价预测问题,可以用线性模型来描述:
Y= x1w1+x2w2+…+xn*wn+b
-
但也有很多场景需要输出的是概率估算值,例如:
- 根据邮件内容判断是垃圾邮件的可能性
- 根据医学影像判断肿瘤是恶性的可能性
- 手写数字分别是0,1,…,9的可能性(概率)
这时需要将预测输出值控制在【0,1】区间内
二元分类问题的目标是正确预测两个可能的标签中的一个,逻辑回归可以用于处理这类问题。
-
问题: 逻辑回归模型如何保证输出值始终落在0和1之间?
sigmod函数(S型函数)生成的输出值正好具有这些特性,器定义如下:
y=1/(1+e^-(z))
定义域为全体实数,值域在【0,1】之间
z值在0点对应的结果为0.5
sigmoid函数连续可微分。
-
逻辑回归中的损失函数
线性回归的损失函数是平方损失,如果逻辑回归的损失函数也定义为平方损失,那么:
J(W)= 1/n(求和)(A(zi)-yi)^2
i表示第i个样本点
zi=xi*w+b
A(zi)表示对i个样本的预测值
yi表示第i个样本的标签值
如果将sigmoid函数带入上述函数,非凸函数,有多个极小值。如果采用梯度下降法,会容易导致陷入局部最优解中。
-
二元逻辑回归的损失函数一般采用对数损失函数,定义如下:
J(W,b) = 【求和】-ylog(y’) - (1 - y )log(1 - y’ )
(x,y)属于D是有标签样本(x,y) 的数据集
y是有标签样本中的标签,取值必须是0或1
y’是对于特征集x的预测值(介于0 和1之间) -
七、多元分类
在多类别问题中,softmax会为每个类别分配一个用小数表示的概率。这些用小鼠表示的概率相加之和必须是1.0。
演示softmax的使用
import tensorflow as tf
import numpy as np
x = np.array([[-3.1,1.8,9.7,-2.5]])
pred = tf.nn.softmax(x)#Softmax分类
sess = tf.Session()#声明会话
v=sess.run(pred)
print(v)
sess.close()
八、交叉熵损失函数
交叉熵是一个信息论中的概念,它原来是用来估算平均编码长度的。给定两个概率分布p和q,通过q来表示p的交叉熵为:
H(p,q) = -【求和】p(x)log q(x)\
j交叉熵刻画的是两个概率分布之间的距离,p代表正确答案,q代表的是预测值,交叉熵越小,两个概率的分布越接近。
例子:假设有一个3分类问题,某个样例的正确答案是(1,0,0)。甲模型经过softmax回归之后的预测答案是(0.5,0.2,0.3)。乙模型经过softmax回归之后的预测答案是(0.7,0.1,0.2)
H(p,q) = -【求和】p(x) log q(x)
H((1,0,0),(0.5,0.2,0.3))=-log0.5 ≈ 0.301
H((1,0,0),(0.7,0.1,0.2))= -log0.7≈0.155
九、分类模型的构建与实现
-
载入数据
import tensorflow as tf import tensorflow.examples.tutorials.mnist.input_data as input_data mnist = input_data.read_data_sets("C:/Users/grid/Desktop/MNIST_data/",one_hot=True)#读取数据(数据集缩放的文件目录,标签的数据格式)
-
构建模型
-
定义x和y的占位符
#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")
在神经网络中,权重W的初始值通常设为正态分布的随机数,偏置项b的初始值通常也设为b的随机数或常数。在Tensorflow中,通常利用以下函数实现正态分布随机数的生成:
-
用单个神经元构建神经网络
forward= tf.matmul(x,W)+b #前向计算
-
关于Softmax Regression
当我们处理多分类任务时,通常需要使用SoftmaxREgression模型
SoftmaxRegression会对每一类别估算出一个概率。
工作原理:将判定为某一类的特征相加,然后将这些特征转化为判定时这一类的概率。pred = tf.nn.softmax(forward)#Softmax分类
-
-
-
训练模型
-
设置训练参数
train_epochs = 50 #训练轮数 batch_size = 100 #单次训练样本数(批次大小 total_batch = int(mnist.train.num_examples/batch_size) #一轮训练有多少批次 display_step =1 #显示粒度 learning_rate= 0.01 #学习率
-
定义损失函数
#定义交叉熵损失函数 loss_function = tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred),reduction_indices=1))
-
选择优化器
#梯度下降优化器 optimizer= tf.train.GradientDescentOptimizer(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))
-
运行模型
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!")
-
-
评估模型
完成训练后,在测试集上评估模型的准确率
accu_test =sess.run(accuracy,feed_dict ={x: mnist.test.images, y: mnist.test.labels}) print("TestAccuracy:",accu_test)
完成训练后,在验证集上评估模型的准确性
accu_validation = sess.run(accuracy,feed_dict={x: mnist.validation.images, y: mnist.validation.labels}) print("Test Accuracy:",accu_validation)
-
进行预测
-
在建立模型并进行训练后,若认为准确率可以接受,则可以使用此模型进行预测
#由于pred预测结果是one-hot编码格式,所以需要转换为0-9数字 prediction_result = sess.run(tf.argmax(pred,1),feed_dict={x: mnist.test.images})
-
查看预测结果
#查看预测结果中的前10项 prediction_result[0:10]
-
定义可视化函数
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()#获取当前图表,GetCurrent Figure fig.set_size_inches(10,12) #1英寸等于2.54cm if num >25: num =25 #最多显示25个子图 for i in range(0,num): ax =plt.subplot(5,5,i+1) #获取当前要处理的子图 ax.imshow(np.reshape(images[index],(28,28)), #显示第index个图像 cmap='binary') 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,25)
-
整个项目的代码:
#一、 载入数据
import tensorflow as tf
import tensorflow.examples.tutorials.mnist.input_data as input_data
mnist = input_data.read_data_sets("C:/Users/grid/Desktop/MNIST_data/",one_hot=True)#读取数据(数据集缩放的文件目录,标签的数据格式)
#二、构建模型
# 1. 定义x和y的占位符
#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')
# 2. 创建变量
# 在本案例中吗,以正太分布的随机数初始化权重W,以常数0初始化偏置b
#定义变量
W = tf.Variable(tf.random_normal([784,10]),name="W")
b = tf.Variable(tf.zeros([10]),name="b")
# 用单个神经元构建神经网络
forward= tf.matmul(x,W)+b #前向计算
#关于Softmax Regression 当我们处理多分类任务时,通常需要使用SoftmaxREgression模型 SoftmaxRegression会对每一类别估算出一个概率。 工作原理:将判定为某一类的特征相加,然后将这些特征转化为判定时这一类的概率。
pred = tf.nn.softmax(forward)#Softmax分类
#三、训练模型
# 1. 设置训练参数
train_epochs = 50 #训练轮数
batch_size = 100 #单次训练样本数(批次大小
total_batch = int(mnist.train.num_examples/batch_size) #一轮训练有多少批次
display_step =1 #显示粒度
learning_rate= 0.01 #学习率
#2. 定义损失函数
#定义交叉熵损失函数
loss_function = tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred),reduction_indices=1))
#3. 选择优化器
#梯度下降优化器
optimizer= tf.train.GradientDescentOptimizer(learning_rate).minimize(loss_function)
#4. 定义准确率
#检查预测类别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))
#5. 运行模型
sess=tf.Session() #声明会话
init =tf.global_variables_initializer() #变量初始化
sess.run(init)
#6. 模型训练
# 开始训练
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!")
#四、评估模型
# 完成训练后,在测试集上评估模型的准确率
accu_test =sess.run(accuracy,feed_dict ={x: mnist.test.images, y: mnist.test.labels})
print("TestAccuracy:",accu_test)
#完成训练后,在验证集上评估模型的准确性
accu_validation = sess.run(accuracy,feed_dict={x: mnist.validation.images, y: mnist.validation.labels})
print("Test Accuracy:",accu_validation)
#五、进行预测
# 1. 在建立模型并进行训练后,若认为准确率可以接受,则可以使用此模型进行预测
#由于pred预测结果是one-hot编码格式,所以需要转换为0-9数字
prediction_result = sess.run(tf.argmax(pred,1),feed_dict={x: mnist.test.images})
# 2. 查看预测结果
#查看预测结果中的前10项
prediction_result[0:10]
# 3.定义可视化函数
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()#获取当前图表,GetCurrent Figure
fig.set_size_inches(10,12) #1英寸等于2.54cm
if num >25:
num =25 #最多显示25个子图
for i in range(0,num):
ax =plt.subplot(5,5,i+1) #获取当前要处理的子图
ax.imshow(np.reshape(images[index],(28,28)), #显示第index个图像
cmap='binary')
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()
# 4.可视化预测结果
plot_images_labels_prediction(mnist.test.images, mnist.test.labels, prediction_result, 0,15)