机器学习入坑指南(九):TensorFlow 实战——手写数字识别(MNIST 数据集)
上篇文章简要介绍了「深度学习」,接下来,我们将从最经典的例子入手进行实战。
一、背景知识简介
1 TensorFlow
Tensor:张量(即多维数组),Flow:(数据)流
TensorFlow,是一个使用数据流图(data flow graohs)技术来进行科学计算的开源软件库,它由Google Brain 团队开发,被广泛应用于各种感知和语言理解任务的机器学习。
点击访问:TensorFlow 官网,可以查阅官方文档、获取一手资讯和下载最新版本。
或者访问:GitHub - tensorflow 来达到同样的效果
英文阅读有困难的同学参考:TensorFlow 中文文档
在本次实战中我们还将使用 Keras,它是一个基于 Theano(另一个开源库)和 TensorFlow 构建的高级神经网络 API,能够在 Theano、TensorFlow 或 Microsoft Cognitive Toolkit 上运行。
点击访问:Keras 中文文档
2 MNIST 数据集
在上篇文章中我们已经提到过 MNIST 了,它是一个收录了许多 28 x 28 像素手写数字图片(以灰度值矩阵存储)及其对应的数字的数据集,可以把它理解成下图这个样子:
图片来源:3Blue1Brown 的视频,强烈推荐观看系列视频:B 站播放地址
二、Python 实现
这里我们依旧使用 Jupyter Notebook 作为开发环境,没有使用过的同学参考我的文章「机器学习入坑指南(一):Python 环境搭建」。
当然,使用 Pycharm 等 IDE 也没有问题。
1 安装并导入 TensorFlow、Keras
在 Anaconda 终端中输入命令
conda install tf-nightly
或输入(Python Shell 中也可以使用此命令)
pip install tf-nightly
Keras 已经被集成进了 TensorFlow 中。我们可以通过 tensorflow.keras
来访问它。
接下来,导入这两个库
import tensorflow.keras as keras
import tensorflow as tf
可以通过简单的代码测试导入是否成功
print(tf.__version__)
2 导入并测试 MNIST 数据集
Keras 默认从 googleapis 下载 MNIST,如果无法访问,可在 GitHub 上下载,点击这里,下载到本地后更改 mnist.py
中的引用路径。
TensorFlow 中存在多个 mnist.py
,这里我们需要修改的是 Keras 下的,我的路径为
C:\ProgramData\Anaconda3\Lib\site-packages\tensorflow\python\keras\datasets
打开之后,把
origin_folder = 'https://storage.googleapis.com/tensorflow/tf-keras-datasets/'
中的路径修改为你存放数据集的位置即可。进入正题
mnist = tf.keras.datasets.mnist #导入mnist
(x_train,y_train),(x_test,y_test) = mnist.load_data() #分割
print(x_train[0]) # 查看第一个测试数据的输入
然后你就看到了如下的输出:
让我们把这个矩阵用图像表示出来
import matplotlib.pyplot as plt
%matplotlib inline # 加上这句才能显示图像
plt.imshow(x_train[0],cmap=plt.cm.binary) # 显示黑白图像
plt.show()
可以通过以下代码对数据进行归一化处理
x_train = tf.keras.utils.normalize(x_train, axis=1)
x_test = tf.keras.utils.normalize(x_test, axis=1)
再次查看图像
plt.imshow(x_train[0],cmap=plt.cm.binary)
plt.show()
图像的像素值被限定在了 [0,1] 。可以通过
print(x_train[0])
查看矩阵数据。
3 构建与训练模型
终于进入核心环节了。在这里,我们使用 Keras 的 Sequential 模型(顺序模型)。Sequential 模型是最常用的模型,也就是一个按照顺序向前传递的神经网络。
model = tf.keras.models.Sequential()
接下来为模型添加图层。神经网络的输入层是一个一维向量,所以我们需要把输入的图像矩阵展平,从 28 x 28 变为 1 x 784 。Keras 为我们提供了如下方法:
model.add(tf.keras.layers.Flatten())
之后,为神经网络添加隐藏层。这里我们使用最简单的 Dense 层(即全连接层,每一个神经元与前后两层的所有神经元相连)
model.add(tf.keras.layers.Dense(128, activation=tf.nn.relu))
这个层有 128 个单元,激活函数选择 reLU,最初人们喜欢用 Sigmoid 函数,但后来发现 reLU 效果更好,所以可以当做一个默认的选择。
接下来再加入一个相同的层
model.add(tf.keras.layers.Dense(128, activation=tf.nn.relu))
再加入输出层
model.add(tf.keras.layers.Dense(10, activation=tf.nn.softmax))
输出层有 10 个结点,代表 10 种不同的数字。这里使用 softmax 函数作为激活函数,因为我们想要找到预测结果的概率分布。(使用 reLU 得到的数字并没有这个意义)
我们构建出的模型大概是这个样子的(示意图来自 3Blue1Brown,隐藏层只有 16 个单元,实际上我们有 128 个)
添加好所有的层后,“编译”这个模型。
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
optimizer 即优化器,我们一般默认使用 adam
。
loss 指损失函数,这里我们将其指定为 sparse_categorical_crossentropy
,即计算分类结果的交叉熵损失。
metrics 列表,参数为评估模型性能的指标,典型用法即 metrics=['accuracy']
最后,拟合这个模型
model.fit(x_train, y_train, epochs=3)
在训练的过程中,我们会发现损失值(loss)在降低,而准确度(accuracy)在提高,最后达到了一个令人满意的程度。
4 测试模型
让我们利用测试集试试这个模型是不是真的学会了识别数字
val_loss, val_acc = model.evaluate(x_test, y_test)
print(val_loss)
print(val_acc)
损失和准确度看起来还凑合,尝试识别训练集
predictions = model.predict(x_test)
print(predictions)
看不出来这是个啥?别急,用 argmax
解析一下(就是找出最大数对应的索引,即为识别出的数字)
import numpy as np
print(np.argmax(predictions[0]))
啊哈,来看看 x_test[0]
这个图像是什么样的
plt.imshow(x_test[0],cmap=plt.cm.binary)
plt.show()
OK,妥妥的,相信你也认为这就是个 7,我们的模型已经可以识别数字啦!当然,这只是一个简单的开始,后面的路还有很长,要多思考多动手,坚持学习,才能早日成为大牛!
最后附上可以跑起来的完整代码,来自Deep Learning basics with Python, TensorFlow and Keras p.1
import tensorflow as tf # 深度学习库,Tensor 就是多维数组
mnist = tf.keras.datasets.mnist # mnist 是 28x28 的手写数字图片和对应标签的数据集
(x_train, y_train),(x_test, y_test) = mnist.load_data() # 分割数据集
x_train = tf.keras.utils.normalize(x_train, axis=1) # 把数据值缩放到 0 到 1
x_test = tf.keras.utils.normalize(x_test, axis=1)
model = tf.keras.models.Sequential() # 基础的前馈神经网络模型
model.add(tf.keras.layers.Flatten()) # 把图片展平成 1x784
model.add(tf.keras.layers.Dense(128, activation=tf.nn.relu)) # 简单的全连接图层,,128 个单元,激活函数为 relu
model.add(tf.keras.layers.Dense(128, activation=tf.nn.relu))
model.add(tf.keras.layers.Dense(10, activation=tf.nn.softmax)) # 输出层 ,10 个单元, 使用 Softmax 获得概率分布
model.compile(optimizer='adam', # 默认的较好的优化器
loss='sparse_categorical_crossentropy', # 评估“错误”的损失函数,模型应该尽量降低损失
metrics=['accuracy']) # 评价指标
model.fit(x_train, y_train, epochs=3) # 训练模型
val_loss, val_acc = model.evaluate(x_test, y_test) # 评估模型对样本数据的输出结果
print(val_loss) # 模型的损失值
print(val_acc) # 模型的准确度
您的认真阅读就是对我最大的鼓励!如果觉得我的文章对您有帮助,想要和我一起不断学习新的知识、不断进步,欢迎点击头像旁边的「关注」按钮,让我好跟基友们吹个牛逼!
欢迎关注 evan 的博客