MNIST手写识别(一)—— keras 实现多层感知机(MLP)
本文是该专题【深度学习】的开篇,稍微啰嗦点,尽量把整个运行环境,为什么用keras,如何进行学习,深度神经网络框架等做一个大体介绍,也顺便记录下心得,共勉~, 只看代码实现的话,可以直接跳到最后,代码比较简单,已经过验证。
如果是刚入坑神经网络,没必要追求动则上万的工作站,普通电脑也是可以玩的起来的,简单学习玩玩可以使用 CPU,优点是可以快速搭建,省去GPU驱动安装的时间,很多人从GPU安装环境就直接放弃,当然,如果是靠炼丹吃饭的话好一点的工具也能够提升不少的工作学习效率,因为哪怕是用最普通的GPU,训练速度甚至都会比CPU快。通常公司or学校实验室都会提供深度学习工作站,可以好好利用~~,若是再满足不了丹炉效率,再入手高级GPU也不迟(主要是今年GPU价格炒的飞起,手动狗头)
下面是本文在CPU与GPU环境下的测试
CPU: (Linux)
GPU: (Win10)
对于上层暴露给用户调用的API,只要其设计时考虑提供跨平台的能力,基本都能够在构建时生成对应平台的包,因此对上层来说,对操作系统不敏感,例如 keras、tensorflow、pycharm、anaconda 等,所有平台用法几乎一致。
本文涉及到的软件包可以参考安装
MLP:Multi-Layer Perception ,中文:多层感知器
这个模型是最基本的神经网络模型,可分为3层:输入层、隐藏层、输出层
输入层一般由输入给定,不做训练,需要训练的层是隐藏层与输出层。
在神经网络中,需要关注要素有:权值w,偏置b,激活函数a。举一个简单而又恰当的例子:
y = 2 x + 3 a = { 0 , y < 0 1 , y > = 0 \begin{aligned} &y = 2x + 3\\ &a=\left\{ \begin{aligned} &0, y <0\\ &1, y>=0 \end{aligned} \right. \end{aligned} y=2x+3a={0,y<01,y>=0
其中,计算 y y y 的式子中 2 x 2x 2x 的 2 为权值,3为偏置,这两个数是训练后得到的,用于拟合训练数据,得到最小化损失,通常被随机初始化,在训练过程中逐步被更新,MLP中,一般情况下 x 为矩阵,因此权值w也为可训练的矩阵,偏置b则为向量。这个步骤为线性计算,权值大小表示可能性的大小,偏置保证输入不能随意被激活
a 代表激活函数,激活的条件是 a 是否大于等于0。这个步骤引入非线性映射,常用的激活函数为 sigmoid 函数,可以将值域为 (-∞,+∞),映射到 (0,1)之间
训练过程:
一般 MLP 网络采用的是 BP 反向传播算法来进行训练,
训练完的模型,需要进行预测或者验证,只需执行前向传播得到预测值,即可得到输出预测结果或用于进行验证准确度
Keras 是一个用 Python 编写的高级神经网络 API,它能够以 TensorFlow, CNTK, 或者 Theano 作为后端运行。Keras 的开发重点是支持快速的实验。能够以最小的时延把你的想法转换为实验结果,是做好研究的关键。
总的来说,keras只是一个抽象了其他神经网络框架的前端API层,其本身作为前端的存在,并不是真正的神经网络框架,并不能深入的了解到后端架构的运行情况,也就无法很好的做性能、内存之类的优化。
深度学习框架有很多种,下面举例一些常用的框架,各有优劣,找一个切入即可。
以下情况适合用keras:
但是凡是皆有例外,keras 是面向人类设计的,而不是面向机器,因此在对性能优化、内存优化这一块有点捉襟见肘,在阅读完论文、比赛或需要快速验证方案时,keras的确是个不错的选择,若是工程应用,需要兼顾硬件性能,内存开销,算法开发,算子融合、算子优化等情况时,就需要了解后端的架构,以及修改开源的源码。因此单纯只掌握keras是远远不够的,但我们可用从它来进行入门。
建议:请先花点时间,粗略的过一遍keras的官方中文文档,具体的了解下keras到底是什么,提供了什么功能,并且清楚有哪些API可用,并且该怎么去用他,留一个印象,实际用到的情况下,可以快速进行信息检索 --> keras 中文文档
接下来,按照最简单的方式,模块化地按步骤来实现手写识别这个深度神经网络入门 HelloWorld 例程。
此处 MLP 先定义1层隐藏层,1层输出层,模型定义首先要导入 keras 的 Model 类,可以直接使用 Sequential 模型,按顺序进行层的堆叠
from keras import Sequential
model = Sequential()
创建了模型的实例之后,就可以往模型里面添加层,输入层就是喂入的数据,因此不需要单独创建,但是需要在模型的第一层指定输入的 shape,之后的层的 input shape 都会从上一个层的 output shape 中自动计算。
全连接层在keras中命名为 Dense
from keras.layers import Dense
model.add(Dense(units=512, input_shape=(784,), activation='relu')) # 第一层隐藏层
model.add(Dense(units=10, activation='softmax')) # 输出层
上述MLP网络模型定义好之后,需要将模型进行编译,图编译会调用后端NN框架的接口,如tensorflow 的 sess.compile(),本helloworld例程中,可暂时先不用关心。
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics='accuracy')
图编译的时候,可以选择一些参数,下面介绍一些简单的,详细的请参考 keras 中文文档
数据加载这不用说,肯定加载 MNIST 手写识别数据集,当然 keras 提供的数据集不止这一个,读者有兴趣可以自己翻翻。
第一次加载会通过网络来进行下载,我一直挂着墙,所以也没注意到需不需要科 学上网才能下载,读者可以自行试试。如果没法下载,你也可以尝试简单搭个小楼梯,就可以轻松下载。这是我一直在用的 小楼梯,比较便宜稳定,速度也还行,没给我发广告费我就不多说了,自行了解。。。
from keras.datasets import mnist
(train_x, train_y), (test_x, test_y) = mnist.load_data()
加载完数据之后,返回2个元组,分别为训练集与测试集,各个数据的 shape 如下:
train_x shape: (60000, 28, 28)
train_y shape: (60000,)
test_x shape: (10000, 28, 28)
test_y shape: (10000,)
接下来需要对特征数据进行展平以及归一化,对标签数据进行 one hot 编码
from keras.utils import np_utils
X_train = train_x.reshape(train_x.shape[0], train_x.shape[1] * train_x.shape[2]).astype('float32') / 255
Y_train = np_utils.to_categorical(train_y, num_classes=10)
X_test = test_x.reshape(test_x.shape[0], test_x.shape[1] * test_x.shape[2]).astype('float32') / 255
Y_test = np_utils.to_categorical(test_y, num_classes=10)
处理完后的shape,就可以送入模型进行训练了。
X_train shape: (60000, 784)
Y_train shape: (60000, 10)
X_test shape: (10000, 784)
Y_test shape: (10000, 10)
模型与数据都准备完成后,就可以开始训练了,这一步具体的训练时间就与机器能提供的性能有很大关系了,训练很简单,就一句话
model.fit(X_train, Y_train, epochs=5)
将训练集的特征与标签送进去,指定轮他多少遍即可。fit() 总共可接受19个训练参数,常用的参数也有下面这些,具体也可以看 keras 中文文档
训练完后就可以使用测试集来对模型进行loss、准确率的评估,来评估模型的好坏
评估:
loss, accuracy = model.evaluate(X_test, Y_test)
预测:
predict = model.predict(X_test)
到这里,深度神经网络的 Hello world 程序就已经完成了,利用 keras 来编写总共不到30行代码,入坑期间不同的网络调用的大致流程都是如此,将这个模型掌握了,对于再复杂一点的模型,可能就是需要自定义模型、层、loss函数,最好的教程其实还是keras的源码,利用IDE也可以很方便的进行源码查看,假如需要自定义一个layer,那么需要继承谁? 编写哪些类函数?那么就进 Dense 里看看,其他如loss、eval、callback、model 等等,同样如此,第一步学他,第二步用他,第三步造他
代码比较简单,已经在 win10 与 linux 上进行验证成功,同时也在 CPU 与 GPU 环境验证成功,暂未发现问题,若运行出错的话,可以优先检查环境是否安装好。
可以发现单层隐藏层也可以达到比较好的训练效果,训练精度98.9%,预测精度97.9%,但是存在过拟合的问题,读者可以在此基础上继续增加 Dropout 层,改变隐藏层单元、增加网络深度(隐藏层层数)、降低学习率、更改optimizer、loss ,修改epoch次数、修改batch_size 大小等各种操作,把这个简单的网络,训练的相对较为完美。
from keras import Sequential
from keras.layers import Dense
from keras.datasets import mnist
from keras.utils import np_utils
# 定义全连接网络模型
model = Sequential()
model.add(Dense(units=512, input_shape=(784,), activation='relu')) # 第一层隐藏层
model.add(Dense(units=10, activation='softmax')) # 输出层
# 编译静态图
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics='accuracy')
# 加载并处理 mnist 数据集,图片是 28*28=784 分辨率,对图片进行 reshape 展平
(train_x, train_y), (test_x, test_y) = mnist.load_data()
X_train = train_x.reshape(train_x.shape[0], train_x.shape[1] * train_x.shape[2]).astype('float32') / 255
Y_train = np_utils.to_categorical(train_y, num_classes=10)
X_test = test_x.reshape(test_x.shape[0], test_x.shape[1] * test_x.shape[2]).astype('float32') / 255
Y_test = np_utils.to_categorical(test_y, num_classes=10)
# 模型训练
model.fit(X_train, Y_train, epochs=5, batch_size=32)
# 精度验证
loss, accuracy = model.evaluate(X_test, Y_test)
print('Test loss:', loss)
print('Accuracy:', accuracy)
predict = model.predict(X_test)
print('预测值:', predict.argmax())
print('实际值:', test_y[0])
… 2000 years later …
调试完各种操作,结果你会发现,MLP 模型优化的精度其实有限,很多时候,全连接层作为整个网络模型的最后几层来插入,最后通过 softmax 来实现分类输出。因此,在下一篇来讲一下利用卷积神经网络 CNN 来对 MNIST 进行训练 。
【深度学习】Keras MNIST手写识别(二)—— 卷积神经网络模型(CNN)