作者:欧新宇(Xinyu OU)
本文档所展示的测试结果,均运行于:Intel Core i7-7700K CPU 4.2GHz
AlphaGo、无人驾驶、人脸识别、智能翻译……这一个个耳熟能详的名词无不预示着人工智能时代的到来。人工智能的本质,是对人的思维过程和行为方式的模拟。研究人的认知机理,引入人的神经元概念,势在必行。
人工神经网络,可以说是这一切的起源。或者说是这一切起源的核心。
人工智能领域的一个重要任务是让计算机能够像人一样对输入的信息进行判定。比如:
正是因为希望模拟人的认知,所以需要通过研究人的认知机理来指导机器提升智能。
众所周知,人对世界的感知和理解主要通过数以亿计的神经元来完成,神经元之间彼此连接构成巨大的神经元网络,输入的信号(如视网膜上的神经元感受到的光线等)经过一层层的神经元往脑部传递,不断做出决策,再通过一层层的神经元输出到反馈端(如影响手脚部动作等)。
所谓神经元(Neuron)是大脑中互相连接的神经细胞,它可以处理和传递化学和电信号。有意思的是,神经元只有两种工作状态————兴奋和抑制,这与计算机中的"1"和"0"是完全一致的。因此,我们也用1来表示神经网络神经元的激活状态,用0表示关闭状态。
下图展示了一个由多个神经元彼此连接组成的网络
1943年,逻辑学家Walter Pitts和神经生理学家Warren McCulloch联合发表文章,首次将神经元概念引入计算领域,提出了第一个人工神经元模型,开启了神经网络的大门。
1957年,知名学者Frank Rosenblatt提出了感知器
(Perceptron)的概念,该概念非常接近神经元的实际机理,通过将多层感知器前后连接,可以构成一个决策网络,从而为神经网络的研究奠定了基石。
然而好景不长,1969年,被誉为人工智能之父的Marvin Minsky和Seymour Papert出版《Perceptron》一书探讨感知器的优劣,认为仅靠局部连接的神经网络无法有效地开展训练,而全连接的神经网络则过于复杂而不实用。更重要的是,限于当时的计算方法
与能力
,复杂神经网络核心权重参数的计算时长将无法忍受。这些情况影响了学界和业界对神经网络的信心,神经网络的研究陷入了第一次低谷期。
近二十年后,当代神经网络三巨头相继发文,推动了神经网络研究的再次兴起。1986年,Geoffrey Hinton和David Rumelhart联合在Nature上发表论文,将BP算法
用于神经网络模型,实现了对权重参数的快速计算。1990年,Yann LeCun发表文章,采用BP神经网络实现对手写数字的识别,这可以被视作神经网络的"第一个"重大应用,直到上世纪九十年代末,超过10%的美国支票都采用该技术进行自动识别。
1998年,Yann LeCun又发文提出了LeNet-5的框架,即现在热火朝天的卷积神经网络
(Convolutional Neural Network)的基本框架。然而卷积要消耗大量计算资源,BP方法又会带来梯度弥散的问题,从而限制了神经网络的深度和效果。
相反,俄罗斯学者Vladmir Vapnik在1963年提出的支撑向量机
(Support Vector Machine,SVM)概念则不断深入发展。到2002年,已将手写数字识别的错误率降至0.56%,远高于同期神经网络的效果。神经网络的研究迎来了第二次寒冬。
在介绍神经网络的发展历史之前,首先介绍一下神经网络的概念。神经网络主要是指一种仿造人脑设计的简化的计算模型,这种模型中包含了大量的用于计算的神经元,这些神经元之间会通过一些带有权重的连边以一种层次化的方式组织在一起。每一层的神经元之间可以进行大规模的并行计算,层与层之间进行消息的传递。
下图展示了整个神经网络的发展历程。
神经网络的发展历史甚至要早于计算机的发展,早在上个世纪四十年代就已经出现了最早的神经网络模型。
第一代的神经元模型是验证型的,当时的设计者只是为了验证神经元模型可以进行计算,这种神经元模型既不能训练也没有学习能力,可以简单的把它看成是一个定义好的逻辑门电路,因为它的输入和输出都是二进制的,而中间层的权重都是提前定义好的。
神经网络的第二个发展时代是十九世纪五六十年代,以Rosenblatt提出的感知器模型和赫伯特学习原则等一些工作为代表。
以下几个重要的网络是神经网络一步一步发展到今天的关键
在本课程中,我们只是介绍神经网络中最基本的一种————多层感知机(Multi-layered Perceptron, MLP), 多层感知机也被称为前馈神经网络。
在前面介绍线性模型的时候,我们给出了一个线性模型的一般公式:
$\hat{y} = w[0]*x[0] + w[1]*x[1] + ... + w[p]*x[p] + b$
在该公式中 $\hat{y}$ 表示对真实 $y$ 值的估计值.
其中,$x[0],x[1],...x[p]$ 是数据集中样本的特征值;$w$ 和 $b$ 是模型的参数,其中 $w$ 具体表示为每个特征的权重,也就是指每个神经元对于模型贡献的重要程度。因此,从数学表示表达式来看,我们可以认为 $\hat{y}$ 就是每个特征的加权和, 这个过程可以用下图表示。
import mglearn
mglearn.plots.plot_logistic_regression_graph()
上图给出了线性模型求解过程,其中 $X[i]$ 称为输入层(inputs),$y$ 称为输出层(output)。当$x[i](i = 0, 1,2,3,...,n)$, 表示样本 $x$ 有 $n+1$ 个特征输入(上图表示有4个特征输入)。输入 $x$ 和输出 $y$ 之间的连线用系数 $w$ 表示,可以将这个过程理解为线性关系 $x[i]*w[i] = y[i]$。
为了便于理解,此处省略了偏移量 $b$。
在MLP中,我们在输入层(inputs)和输出层(output)增加一个隐藏层(Hidden Layer),然后模拟线性模型中加权求和的过程。
mglearn.plots.plot_single_hidden_layer_graph()
如上图所示,我们可以将这个MLP理解为一个两层的线性模型。
第一层模型的输入层是原来的输入层(inputs),输出层是新增加的隐层(hidden layer),此时输出变成了隐层单元 $h[j]$.
第二层模型的输入层变成了新增加的隐层(hidden layer),而输出层依然是原来的输出层(output),此时输入变成了隐层单元 $h[j]$.
与线性模型相似,在第一层模型中的每一个 $x[i]$和隐层单元 $h[j]$ 之间和第二层模型中的每一个隐层单元 $h[j]$ 和 $y$ 之间都有一个系数 $w$。相似的,在每个模型中也都有一个偏移量 $b$.
此时,最终的输出 $y$ 等于 $j$ 个加权和的加权和。
值得注意的是,上面所介绍的MLP模型,并非完整的MLP模型。从数学的角度来分析,不难想象,输出 $y$ 变成了 $j$ 个加权和的加权和,但其结果依然不会有什么区别,为了让模型更强大,我们还需要增加一些处理,例如:非线性(限制线性)激活、Dropout、Pooling、shortcut(ResNet)、batchNorm等。
对于MLP,我们仅引入激活函数。基本思路是在每个隐层后面增加一个激活函数。
假设,我们用 $h(x)$ 表示线性变化,$ReLU(x)$ 表示激活函数,则原来的线性变换 $y = f(x)$ 可以表示为:$y = ReLU(f(x))$.
而两层MLP就可以表示为 $y = ReLU(f_2(ReLU(f_1(x))))$
【知识点】[激活函数Activation Function](knowledgement/ActivationFunction.ipynb)
下面给出ReLU和tanh对特征进行限制激活的效果:
import numpy as np
import matplotlib.pyplot as plt
line = np.linspace(-5,5,200)
plt.plot(line, np.tanh(line),label='tanh')
plt.plot(line, np.maximum(line,0),label='relu')
plt.legend(loc='best')
plt.xlabel('x')
plt.ylabel('ReLU(x) and tanh(x)')
plt.show()
【结果分析】
从图中可以看出,双曲正切函数tanh将特征限制到了 $[-1, 1]$之间,而限制线性单元ReLU则将小于0的部分直接归0.
假设我们使用ReLU激活函数对特征进行激活矫正,则原来的公式:
$\hat{y} = w[0]*x[0] + w[1]*x[1] + ... + w[p]*x[p] + b$
将变为:
$h[0] = ReLU(w[0]*x[0] + w[1]*x[1] + ... + w[p]*x[p] + b)$
$h[1] = ReLU(w[0]*x[0] + w[1]*x[1] + ... + w[p]*x[p] + b)$
...
$h[j] = ReLU(w[0]*x[0] + w[1]*x[1] + ... + w[p]*x[p] + b)$
$\hat{y} = v[0]*h[0] + v[1]*h[1] + ... + v[j]*h[j]$
此处,MLP又多了一组权重系数 $v[i]$ 用来调节每个隐层单元 $h$ 之间的权重.
当我们继续增加隐层数量的时候,网络将变为如下的形状:
mglearn.plots.plot_two_hidden_layer_graph()
下面展示几个重要的深度神经网络,一个是5层的LeNet5, 一个是7层的AlexNet,一个是19层(16层的)VGG,一个是34层的ResNet34,更常用的还包括ResNet101.
# TODO: 1. 导入必须库 以及 定义必要的函数
import numpy as np
# 导入数据集工具包
from sklearn import datasets
from sklearn.model_selection import train_test_split
# 导入MLP神经网络包
from sklearn.neural_network import MLPClassifier
# TODO: 2. 创建/导入数据
wine = datasets.load_wine()
# TODO: 3. 数据预处理,包括训练集、测试集划分,数据正则化,数据清洗等
X = wine.data[:, :2] # 为便于可视化仍然仅使用前两个特征
y = wine.target
X_train, X_test, y_train, y_test = train_test_split(X, y,random_state=16)
# TODO: 4. 构建模型,并进行模型训练(或称为拟合数据)---
mlp_1L100 = MLPClassifier(solver='lbfgs')
mlp_1L100.fit(X_train, y_train)
【结果分析】
其他在参数在真实应用中再进行讲解和设置
导入绘图工具集并使用不同色块显示不同分类
# 导入绘图工具集
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# 使用不同色块显示不同分类
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
x_min, x_max = X_train[:, 0].min() - 1, X_train[:, 0].max() + 1
y_min, y_max = X_train[:, 1].min() - 1, X_train[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, .02),
np.arange(y_min, y_max, .02))
# TODO: 5. 输出预测结果
score_train = mlp_1L100.score(X_train, y_train)
score_test = mlp_1L100.score(X_test, y_test)
print("训练集准确率: {0:.4f}, 测试集准确率: {1:.4f}.".format(score_train, score_test))
# TODO: 6. 可视化预测结果
Z1 = mlp_1L100.predict(np.c_[xx.ravel(), yy.ravel()])
Z1 = Z1.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z1, cmap=cmap_light)
# 将数据特征用散点图绘制出来
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolor='k', s=60)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
#设置图的标题,并显示图形
plt.title("MLPClassifier: solver=lbfgs")
plt.show()
# TODO: 4. 构建模型,并进行模型训练(或称为拟合数据)---
mlp_1L10 = MLPClassifier(solver='lbfgs', hidden_layer_sizes=[10], random_state=16)
mlp_1L10.fit(X_train, y_train)
# TODO: 5. 输出预测结果
score_train = mlp_1L10.score(X_train, y_train)
score_test = mlp_1L10.score(X_test, y_test)
print("训练集准确率: {0:.4f}, 测试集准确率: {1:.4f}.".format(score_train, score_test))
# TODO: 6. 可视化预测结果
Z2 = mlp_1L10.predict(np.c_[xx.ravel(), yy.ravel()])
Z2 = Z2.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z2, cmap=cmap_light)
# 将数据特征用散点图绘制出来
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolor='k', s=60)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
#设置图的标题,并显示图形
plt.title("MLPClassifier: solver=lbfgs, nodes=10")
plt.show()
【结果分析】
相比mlp_1L100
模型mlp_1L10
模型只调整节点数$nodes = [100] -> [10] $, 可以看到输出图的边界丢失了很多细节,不是那么的平滑了。我们可以理解的是,神经元的数量代表了超平面的非线性程度,减少神经元就意味着降低了超平面的非线性程度。这样影响到了分类器的性能。
从准确率来看,不管是训练集还是测试集,都有一定程度的下降。
从以上的实验结果, 我们可以得到以下几个粗略的结论:
欠拟合
问题的发生,导致系统性能下降。除了增加单层网络神经元的数量(宽度)以外,还可以通过以下两种办法调节非线性特性:
# TODO: 4. 构建模型,并进行模型训练(或称为拟合数据)---
mlp_2L = MLPClassifier(solver='lbfgs', hidden_layer_sizes=[30,30],
random_state=16)
mlp_2L.fit(X_train, y_train)
# TODO: 5. 输出预测结果
score_train = mlp_2L.score(X_train, y_train)
score_test = mlp_2L.score(X_test, y_test)
print("训练集准确率: {0:.4f}, 测试集准确率: {1:.4f}.".format(score_train, score_test))
# TODO: 6. 可视化预测结果
Z3 = mlp_2L.predict(np.c_[xx.ravel(), yy.ravel()])
Z3 = Z3.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z3, cmap=cmap_light)
# 将数据特征用散点图绘制出来
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolor='k', s=60)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
#设置图的标题,并显示图形
plt.title("MLPClassifier: solver=lbfgs, nodes=[10, 10]")
plt.show()
【结果分析】
从输出结果,我们可以得到以下几个结论:
欠拟合
问题。此外,也有可能是因为训练并没有达到最终的收敛状态,所以在某个时刻,由于数据多样性的振荡,而产生的某个局部低谷。[所以,进行模型训练时,一般需要模型的验证集的性能在一定时间内保持稳定才能终止训练]有兴趣的同学,可以尝试调整max_iter
的次数来让模型训练得更充分一些,或者设置更深的模型
,看看能得到什么样的结论。
例如:hidden_layer_sizes=[30,30,50], max_iter={10, 50, 100, 200}
# TODO: 4. 构建模型,并进行模型训练(或称为拟合数据)---
mlp_tanh = MLPClassifier(solver='lbfgs', hidden_layer_sizes=[30,30],
activation='tanh', random_state=16)
mlp_tanh.fit(X_train, y_train)
# TODO: 5. 输出预测结果
score_train = mlp_tanh.score(X_train, y_train)
score_test = mlp_tanh.score(X_test, y_test)
print("训练集准确率: {0:.4f}, 测试集准确率: {1:.4f}.".format(score_train, score_test))
# TODO: 6. 可视化预测结果
Z4 = mlp_tanh.predict(np.c_[xx.ravel(), yy.ravel()])
Z4 = Z4.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z4, cmap=cmap_light)
# 将数据特征用散点图绘制出来
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolor='k', s=60)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
#设置图的标题,并显示图形
plt.title("MLPClassifier: solver=lbfgs, activation=tanh, nodes=[10, 10]")
plt.show()
【结果分析】
修改激活函数为tanh后,可以看到的的是分类器的决策面完全变成了平滑的曲线,这主要是因为相比于ReLU的硬边界,tanh的双曲曲线本身就更加平滑一些。
至于性能,我们并没有强调更平滑
的曲线就一定比硬边界
就好,这都需要在具体的数据集和具体的超参数设置下调整。实际上,在很多时候:
换句话说,ReLU激活函数,通过结合深度神经网络更宽、更深的特性,总是能在获得更好性能的同时,降低计算的复杂性(相比其他激活函数具有更好的计算效率)。
# TODO: 4. 构建模型,并进行模型训练(或称为拟合数据)---
mlp_alpha = MLPClassifier(solver='lbfgs', hidden_layer_sizes=[30,30],
activation='tanh', alpha=1, random_state=16)
mlp_alpha.fit(X_train, y_train)
# TODO: 5. 输出预测结果
score_train = mlp_alpha.score(X_train, y_train)
score_test = mlp_alpha.score(X_test, y_test)
print("训练集准确率: {0:.4f}, 测试集准确率: {1:.4f}.".format(score_train, score_test))
# TODO: 6. 可视化预测结果
Z5 = mlp_alpha.predict(np.c_[xx.ravel(), yy.ravel()])
Z5 = Z5.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z5, cmap=cmap_light)
# 将数据特征用散点图绘制出来
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolor='k', s=60)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
#设置图的标题,并显示图形
plt.title("MLPClassifier: solver=lbfgs, activation=tanh, nodes=[10, 10], alpha=1")
plt.show()
【结果分析】
超参数alpha用于调节模型的正则化程度,增加alpha会使模型更加简单,从可视化结果上看,即弯曲程度更大,褶皱更少。
同样,这并不代表性能更好
或更差
。
【小节】
神经网络,特别是基于卷积神经网络(CNN)及其各种变种的深度学习模型是目前人工智能领域最好的模型。也正是基于这些模型,人工智能才真正的具有实用性和可用性。
但是,对于如何训练模型,具有一定的要求,特别是对数据集的处理和模型超参数的调节。我们将在“深度学习”的相关课程中进一步讲解有关神经网络的知识。 </font>
MNIST数据集被誉为人工智能领域的“Hello World”,不仅因为其“简单”,更因为其实用,同时也因为它并不是真的那么“简单”,它既可以用来验证普通的机器学习算法,也可以被应用到深度学习领域。当然,直到卷积神经网络的出现,MNIST的性能才真正提高到了99.8%以上。
MNIST手写字体(数字)数据集目前由三驾马车之一的纽约大学教授Yann LeCun实验室维护,包含多种数据格式。整个数据集包含0-9十个数字的灰度图片共计70000张,都是分辨率为$28\times28$的图片,其中训练集样本60000张,测试集10000张。数据原始的地址为:http://yann.lecun.com/exdb/mnist/ 。
MNIST数据集都是分辨率为$28\times28$的灰度图片,每个像素的值为 0~255 之间的灰度值。为了便于建模需要将特征值转换成 0~1 之间的值。
下面使用sklearn库的fetch_mldata库来获取MNIST数据集
DeprecationWarning: Function fetch_mldata is deprecated; fetch_mldata was deprecated in version 0.20 and will be removed in version 0.22. Please use fetch_openml.
由于数据获取函数fetch_mldata()已经被弃用,因此我们改用另外一个数据函数fetch_openml(), 该函数通过一个开源的网站OpenML进行下载。
# 导入数据集获取工具
from sklearn.datasets import fetch_openml
import time
start = time.time()
# 加载MNIST手写数字数据集
mnist = fetch_openml('mnist_784', version=1, cache=True)
print("载入数据集共耗时: {:.3f}s".format(time.time() - start))
print(mnist.keys())
print('data = {}, \n target = {}'.format(mnist['data'], mnist['target']))
print('{}'.format(mnist['DESCR']))
print('样本数量: {}, 样本特征数量: {}, 样本标签数量: {}'.format(mnist.data.shape[0], mnist.data.shape[1], mnist.target.shape[0]))
前面说过,MNIST数据集都是分辨率为$28\times28$的灰度图片,每个像素的值为 0~255 之间的灰度值。为了便于建模需要将特征值转换成 0~1 之间的值。
具体方法很简单,只需要使用矩阵除法即可实现。
from sklearn.model_selection import train_test_split
X = mnist.data/255
y = mnist.target
# 为了提高训练速度,我们只提取10%的样本进行演示
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=6000, test_size=1000, random_state=60)
【特别说明】
在实际应用中,对于公共数据集(例如MNIST),由于官方已经做了
训练集
和测试集
的划分,所以一般我们都应该按照官方的划分方式进行使用。这样的好处是,当我们创造了一个新的算法或者模型时,我们所获得的性能是具有可比性的。因为,所有公开公布的评分都是按照这样的规范来进行设置。虽然,本例只是用于学习算法,我们可以任意划分数据集,但是以规范的方式进行划分是一种比较好的习惯。
当然,对于官方没有提供训练集和测试集划分方式的数据集,我们可以自己进行划分,但是应该注意的是,所有对比算法都应该采用同样的划分方式,以满足公平的原则。
该方法使用文件载入的方式进行数据集加载,是比较通用的方法。 但由于本数据集使用二进制码进行保存,因此对于初学者来说有一定的难度,所以在此处推荐各位同学使用我写的一个文件读取进行载入。
基本步骤如下:
/Datasets/mnist/
文件夹中/Modules/load_MNIST.py
文件中。下载地址:http://ouxinyu.cn/Teaching/MachineLearning/Modules/load_MNIST.pyimport sys
import os
sys.path.append(os.path.join(os.getcwd(), '..', 'Modules'))
import load_MNIST
import time
start = time.time()
train_images = load_MNIST.load_train_images()
train_labels = load_MNIST.load_train_labels()
test_images = load_MNIST.load_test_images()
test_labels = load_MNIST.load_test_labels()
print("载入数据集共耗时: {:.3f}s".format(time.time() - start))
print('训练集样本形态: {}, 训练集标签形态: {}, 测试集样本形态: {}, 测试集标签形态: {},'
.format(train_images.shape, train_labels.shape, test_images.shape, test_labels.shape))
# 查看图片及图片的标签
import matplotlib.pyplot as plt # plt 用于显示图片
id = 34
plt.imshow(test_images[id])
plt.show()
print('图片的ID = {}'.format(test_labels[id]))
三维矩阵
,即第一维是样本的数量,第二、三维是图片的长宽。因此,我们首先需要将样本的第二、三维拉成一个$28\times28$的向量,以方便后续的处理。此时,整个样本将由一个三维矩阵转换为二维矩阵。# 标准调整形态的方法
# X_train = train_images.reshape(train_images.shape[0], train_images.shape[1]*train_images.shape[2])/255
# 此处,因为我们已经知道的样本的形态,所以可以直接书写值
X_train = train_images.reshape(60000, 28*28)/255
y_train = train_labels
X_test = test_images.reshape(10000, 28*28)/255
y_test = test_labels
# 数据预处理
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
【知识点】 StandardScaler: 去均值和方差归一化。且是针对每一个特征维度来做的,而不是针对样本。 StandardScaler对每列分别标准化,因为 shape of data: [n_samples, n_features]
# 为了提高训练速度,我们只提取10%的样本进行演示
X_train_lite = X_train[0:5999,:]
y_train_lite = y_train[0:5999]
X_test_lite = X_test[0:999,:]
y_test_lite = y_test[0:999]
# 导入多层感知机MLP神经网络
from sklearn.neural_network import MLPClassifier
import time
start = time.time()
mlp = MLPClassifier(solver='lbfgs', hidden_layer_sizes=[100, 100], activation='relu', alpha=1e-5, random_state=62)
mlp.fit(X_train_lite, y_train_lite)
print('训练结束,用时{:.2f}s.'.format(time.time() - start))
print('训练集得分: {:.4f}, 测试集得分: {:.4f}'.format(mlp.score(X_train_lite, y_train_lite), mlp.score(X_test_lite, y_test_lite)))
执行结果:用时4.82s, 训练集得分: 1.000, 测试集得分: 0.9239
# 导入多层感知机MLP神经网络
from sklearn.neural_network import MLPClassifier
import time
start = time.time()
mlp = MLPClassifier(solver='lbfgs', hidden_layer_sizes=[100, 100], activation='relu', alpha=1e-5, random_state=62)
mlp.fit(X_train, y_train)
print('训练结束,用时{:.2f}s.'.format(time.time() - start))
print('训练集得分: {:.4f}, 测试集得分: {:.4f}'.format(mlp.score(X_train, y_train), mlp.score(X_test, y_test)))
执行结果:用时90.682s., 训练集得分: 0.9999, 测试集得分: 0.9764用时
# 导入多层感知机MLP神经网络
from sklearn.neural_network import MLPClassifier
import time
start = time.time()
mlp = MLPClassifier(solver='sgd', hidden_layer_sizes=[100, 100], activation='relu', alpha=1e-5, random_state=62, max_iter=2000)
mlp.fit(X_train_lite, y_train_lite)
print('训练结束,用时{:.2f}s.'.format(time.time() - start))
print('训练集得分: {:.4f}, 测试集得分: {:.4f}'.format(mlp.score(X_train_lite, y_train_lite), mlp.score(X_test_lite, y_test_lite)))
执行结果:用时71.60s, 训练集得分: 0.9985, 测试集得分: 0.9249
# 导入多层感知机MLP神经网络
from sklearn.neural_network import MLPClassifier
import time
start = time.time()
mlp = MLPClassifier(solver='sgd', hidden_layer_sizes=[100, 100], activation='relu', alpha=1e-5, random_state=62, max_iter=2000)
mlp.fit(X_train, y_train)
print('训练结束,用时{:.2f}s.'.format(time.time() - start))
print('训练集得分: {:.4f}, 测试集得分: {:.4f}'.format(mlp.score(X_train, y_train), mlp.score(X_test, y_test)))
执行结果:用时381.56s, 训练集得分: 0.9990, 测试集得分: 0.9771
# 查看图片及图片的标签
import matplotlib.pyplot as plt # plt 用于显示图片
fig, axes = plt.subplots(4, 4)
# use global min / max to ensure all weights are shown on the same scale
vmin, vmax = mlp.coefs_[0].min(), mlp.coefs_[0].max()
for coef, ax in zip(mlp.coefs_[0].T, axes.ravel()):
ax.matshow(coef.reshape(28, 28), cmap=plt.cm.gray, vmin=.5 * vmin, vmax=.5 * vmax)
ax.set_xticks(())
ax.set_yticks(())
plt.show()
import os
import numpy as np
from PIL import Image
id = 342
# 将numpy数组转换为PIL图像
img = Image.fromarray(np.uint8(test_images[id]))
# 保存PIL图像到磁盘指定路径
filename = 'mnist' + str(id) + '.png'
img.save(os.path.join(os.getcwd(), 'tmp', filename))
# 所保存图片的标签label
print('图片的ID = {:d}'.format(int(test_labels[id])))
import os
from PIL import Image
id = 342
filename = 'mnist' + str(id) + '.png'
image=Image.open(os.path.join(os.getcwd(), 'tmp', filename)).convert('F')
image=image.resize((28,28))
# 显示图像
# image.show()
# 将PIL的Image图像转换为numpy的数组
img = np.asarray(image)
# 将二维矩阵转换为一维向量以便于预测
im = img.reshape(1,28*28)
# 输出预测结果
print('图片中的数字是:{:.0f}'.format(mlp.predict(im)[0]))
# 在Notebook中显示PIL图像
# image.show()
# 载入保存和载入模型的库工具
import joblib
# 导入多层感知机MLP神经网络
from sklearn.neural_network import MLPClassifier
import time
start = time.time()
mlp = MLPClassifier(solver='lbfgs', hidden_layer_sizes=[100, 100], activation='relu', alpha=1e-5, random_state=62)
mlp.fit(X_train_lite, y_train_lite)
print('训练结束,用时{:.2f}s.'.format(time.time() - start))
print('训练集得分: {:.4f}, 测试集得分: {:.4f}'.format(mlp.score(X_train_lite, y_train_lite), mlp.score(X_test_lite, y_test_lite)))
ModelPath = os.path.join(os.getcwd(), '..', 'Models', 'Ch08MNIST_lbfgs_lite.pkl')
joblib.dump(mlp, ModelPath)
import os
import numpy as np
import time
# 导入多层感知机MLP神经网络
from PIL import Image
import joblib
# TODO: 1: 载入模型
start = time.time()
ModelPath = os.path.join(os.getcwd(), '..', 'Models', 'Ch08MNIST_lbfgs.pkl')
model = joblib.load(ModelPath)
print("载入模型共耗时: {:.3f}s".format(time.time() - start))
# TODO: 2. 预测给定图像
# 此处我们属于自己的手写字体进行测试
filename = 'mydigital03.png'
filepath = os.path.join(os.getcwd(), '..', 'Attachments', filename)
image = Image.open(filepath).convert('F')
image = image.resize((28, 28))
# 将PIL的Image图像转换为numpy的数组
img = np.asarray(image)
# 将二维矩阵转换为一维向量以便于预测
im = img.reshape(1, 28*28)
# 输出预测结果
pred = model.predict(im)[0]
print('预测结果是:{:.0f}'.format(pred))