#
#作者:韦访
#博客:https://www.jianshu.com/u/c27f1f849226
#微信:1007895847
#添加微信的备注一下是CSDN的
#欢迎大家进微信群一起学习
#码字不易,转载请说明出处,谢谢
#
1、概述
上一讲,我们安装了Windows版本的TensorFlow GPU版,这一讲开始,我们就来学习TensorFlow的用法和深度学习的知识。首先从深度学习的“Hello World”开始,MNIST是一个入门级的计算机视觉数据集,Softmax回归是一个很简单的数学模型,用Softmax模型来对MNIST分类基本上可以看做是深度学习界的“Hello World”。
环境配置:
操作系统:Win10 64位
显卡:GTX 1080ti
Python:Python3.7
TensorFlow:1.15.0
2、MNIST数据集
首先来介绍一下MNIST数据集,MNIST数据集主要由一些手写数字的图片和相应标签组成,图片总共分为10类,分别对应0~9十个数字。
如上图所示,每张图片的大小为28×28像素。而标签则由one-hot向量表示,一个one-hot向量除了某一位数字为1外,其余各位都是0。比如[1,0,0,0,0,0,0,0,0,0,0]表示数字“0”, [0,0,0,0,0,0,0,0,0,0,1]表示数字“9”,以此类推。
MNIST数据集共分为两部分,一部分是包含6万张图片的训练集,另一部分是包含1万张图片的测试集。深度学习的数据集基本上都会分为训练集和测试集,训练集用于模型的训练,测试集用来评估这个模型的性能。
MNIST的每一个数字并不是以我们常见的图片的形式保存,而是一个长度为28*28=784的向量的形式保存。所以一般会将训练数据集图片数据读进一个形状为[60000, 784]的张量,其中第一维度的数字用来索引图片,第二维度的数据则对应每张图片中的像素点,如下图所示,
将训练集的标签数据读进一个形状为[60000, 10]的张量,第一维度同样用来索引图片,第二维度则是一个one-hot向量,分别对应于上面图片数据中的数字,图片和标签是一一对应的,如下图所示,
3、下载MNIST数据集
TensorFlow集成了下载和读取MNIST数据集的接口,可以使用如下代码下载MNIST数据集到mnist_data文件夹,
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("mnist_data", one_hot=True)
下载成功后,可以发现mnist_data文件夹下有以下四个文件:
train-images-idx3-ubyte.gz
t10k-images-idx3-ubyte.gz
train-labels-idx1-ubyte.gz
t10k-labels-idx1-ubyte.gz
文件 | 内容 |
train-images-idx3-ubyte.gz | 训练集图片,55000张训练图片, 5000张验证图片 |
train-labels-idx1-ubyte.gz | 训练集图片对应的数字标签 |
t10k-images-idx3-ubyte.gz | 测试集图片,10000张图片 |
t10k-labels-idx1-ubyte.gz | 测试集图片对应的数字标签 |
4、将MNIST数据集保存为图片
为了更直观的了解MNIST数据集,我们可以把上面的训练集的前50张训练图保存成图片,代码如下,
from tensorflow.examples.tutorials.mnist import input_data
import os
import scipy.misc as sm
import numpy as np
# 下载并加载MNIST数据集
mnist = input_data.read_data_sets("mnist_data", one_hot=True)
# 转换后的图片保存的路径
save_dir = 'mnist_data/images/'
# 如果不存在这个路径则新建该文件夹
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 读取50张图片
for i in range(50):
# 获取第i张图片的数据
image_array = mnist.train.images[i, :]
# 获取第i张图片对应的 one-hot 数据
one_hot_label = mnist.train.labels[i, :]
# 从 one-hot 数据中获取真正的数字标签,
# np.argmax 函数取出列表中最大值的索引,即得到它表示的数字
label = np.argmax(one_hot_label)
# 将长为784的向量转成28*28的矩阵,即二维的图片数据
image_array = image_array.reshape(28, 28)
# 要保存的图片名
filename = save_dir + 'image_train_%d_%d.jpg' % (i, label)
# 保存成图片
sm.toimage(image_array).save(filename)
执行上面代码,结果出错,错误提示如下,
Traceback (most recent call last):
File ".\demo2.py", line 29, in <module>
sm.toimage(image_array).save(filename)
AttributeError: module 'scipy.misc' has no attribute 'toimage'
出现这个错误是因为新的scipy版本已经去掉toimage这个函数了,解决方法是,将scipy降版本,执行以下命令,
pip install scipy==1.2.1
降版本以后重新执行上面的代码,运行结果,有如下提示,
.\demo2.py:29: DeprecationWarning: `toimage` is deprecated!
`toimage` is deprecated in SciPy 1.0.0, and will be removed in 1.2.0.
Use Pillow's ``Image.fromarray`` directly instead.
sm.toimage(image_array).save(filename)
这只是警告,代码已经顺利执行完毕,但是,这淘汰的东西终归要淘汰,所以,我们将scipy版本升回去,根据这个警告,修改我们的代码。
执行以下命令升级scipy到最新版本,
pip install -U scipy
将上面代码中的
sm.toimage(image_array).save(filename)
改成
Image.fromarray(image_array).convert('L').save(filename)
再运行,效果如下,
怎么全是黑色的呢?别急,因为通过TensorFlow的input_data读取的MNIST图片经过了归一化处理,所有的像素值都在0-1之间,所以如果直接转换,就都是黑色的了。所以,再将上面,
image_array = image_array.reshape(28, 28)
改为
image_array = image_array.reshape(28, 28) * 255
再运行,得到如下结果,
5、Softmax
Softmax回归是一个多分类模型。对于MNIST数据集的分类问题中,一个有10个类别(0~9),我们希望对输入的图像计算出它属于某个类别的概率,比如属于9的概率是80%,属于1的概率是5%等等,最后模型预测的结果就是概率最大的那个类别。
Softmax公式如下,
Softmax函数的主要功能是将各个类别的“打分”转化成合理的概率值,它将所有的类别转化为0~1之间的概率,而所有类别的概率加起来为1。
假设x是单个样本的特征,W、b是Softmax模型的参数,则计算x属于数字i类别的公式如下:
则整个Softmax模型可以用下面的式子表示:
Softmax回归模型可以用下图解释,
对于输入的x加权求和,再分别加上一个偏置,最后输入到Softmax函数中。可以将上图写成下图的等式,
用矩阵乘法和向量加法表示则如下图所示,
6、概率与信息论
这里要插讲一下概率论和信息论的知识点,如果觉得谈理论和数学公式太伤感情,这里也可以跳过。
随机变量
随机变量是可以随机地取不同值的变量,例如,和都是随机变量x可能的取值。随机变量可以是离散的或者连续的。
概率分布
概率分布用来描述随机变量或一簇随机变量在每一个可能取到的状态的可能性大小。
标准差
方差的平方根被称作标准差。
7、损失函数
训练模型的输出值和实际值肯定存在一定偏差,这种偏差越小,表示模型预测越准确,而衡量这种偏差的函数我们称为损失函数。
上面噼里啪啦说了一堆,其实就是想说,我们这里的损失函数用交叉熵,交叉熵越小说明我们的预测越准确。
8、梯度下降法
这里也先来插讲一些理论和数学公式。
9、代码实现
加载MNIST数据集
TensorFlow集成了MNIST数据集的下载和加载操作,使用很方便,代码如下,
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 下载并加载MNIST数据集
mnist = input_data.read_data_sets('mnist_data', one_hot=True)
创建占位符和变量
占位符和变量,用于存放图片数据和权重,以及偏置,至于为什么这么做,我们后面的博客会说明,要不然这篇博客的篇幅就太长了,代码如下,
# 创建x占位符,用于临时存放MNIST图片的数据,
# [None, 784]中的None表示不限长度,而784则是一张图片的大小(28×28=784)
x = tf.placeholder(tf.float32, [None, 784])
# W和b是变量,W存放的是模型的参数,也就是权重,一张图片有784个像素作为输入数据,
# 而输出为10,因为(0~9)有10个结果
W = tf.Variable(tf.zeros([784, 10]))
# b则存放偏置项
b = tf.Variable(tf.zeros(10))
定义模型
我们这里的模型很简单,就是上面介绍的Softmax,代码如下,
# y表示Softmax回归模型的输出,是我们模型的预测值
y = tf.nn.softmax(tf.matmul(x, W) + b)
定义损失函数
损失函数呢,用的也是我们上面介绍的交叉熵,代码如下,
# y_存的是实际图像的标签,即对应于每张输入图片实际的真实值,是我们直接从数据集中获取的标签值
y_ = tf.placeholder(tf.float32, [None, 10])
# 定义损失函数,这里用交叉熵来做损失函数,y存的是我们训练的结果,而y_存的是实际标签的值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y)))
优化器
优化器呢,用梯度下降法,上面噼里啪啦讲了一大堆,放心,不用我们自己写代码实现,TensorFlow也是集成了,我们直接调用它的接口就可以了,代码如下,
# 优化器,这里我们使用梯度下降法进行优化,0.01表示学习率
optimizer = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
保存模型
模型训练完了,要保存下来,下次使用的时候才能用,代码如下,
# 将训练得到的模型保存,如果不保存,
# 我们这次训练得到的模型也随着程序运行结束而释放了
saver = tf.train.Saver()
创建会话
上面所做的只是定义算法,并没有真的运行,tensorflow的运行都是在会话(Session)中进行的,代码如下,
with tf.Session() as sess:
初始化变量
初始化变量以后,上面的变量才会真正的有初始值,
# 初始化所有变量,这一步一定不能少
tf.global_variables_initializer().run()
开始训练
深度学习一般不会一次性加载所有的数据进行训练,而是使用mini batch的方式,这个以后的博客中也会说到,下面的代码调用sess.run以后,才是真的开始训练模型了,代码如下,
# 开始训练,这里训练一千次
for _ in range(1000):
# 每次取100张图片数据和对应的标签用于训练
batch_xs, batch_ys = mnist.train.next_batch(100)
# 将取到的数据进行训练
sess.run(optimizer, feed_dict={x:batch_xs, y_:batch_ys})
检验模型
模型训练完以后,我们再在测试集中检验模型的准确率,因为测试集是不参与训练的,代码如下,
# 检测训练结果,tf.argmax取出数组中最大值的下标,tf.equal再对比下标是否一样即可知道预测是否正确
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
# correct_prediction得到的结果是True或者False的数组
# 我们再经过tf.cast将其转为数字的形式,即将[True, True, Flase, Flase]转成[1, 1, 0, 0]
# 最后用tf.reduce_mean计算数组中所有元素的平均值,即预测的准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 开始预测运算,并将准确率输出
print (sess.run(accuracy, feed_dict={x:mnist.test.images, y_:mnist.test.labels}))
保存模型
最后就是保存模型了,上面的saver只是拿到了保存模型的接口,并没有真的保存模型,现在模型训练完了,我们就将模型保存成ckpt各式的,关于模型的保存,以后也会有专门的博客讲这点的。代码如下,
#最后,将模型保存下来
saver.save(sess, './saver/mnist.ckpt')
运行结果
代码写好了,就用Python执行就可以了,执行结果如下,
上面打印的就是我们模型对MNIST数据集预测的准确率。
9、完整代码
完整代码链接如下,
https://mianbaoduo.com/o/bread/YpabmJo=
下一讲,我们来看看深度学习的一些基本概念和知识。