引言
通过前文「深度学习|感知机:神经网络之始」中 XOR Gate 的示例,我们知道叠加层可以增强感知机的表达能力。神奇的是,实际上通过简单的 NAND Gate 叠加就可以实现计算机这样复杂的系统。理论上只要知道怎么设置多层感知机的层次结构、层次间的运算权重,我们可以通过感知机表达任意计算机可以编码完成的逻辑。
但不幸的是,如何找到合适的层次结构与运算权重是一个相当复杂的过程,如果完全由人工来设置,这将几乎是不可能完成的工作,这也是神经网络第一次遇冷的原因。
端到端的能力
神经网络是一种机器学习算法,它的目的是自动学会端到端任务的处理逻辑。这里说的端到端,便是指任务的最初输入端到最终输出端。
我们把计算机上运行的任意任务看作端到端的任务,正常实现这样的任务,需要以显式编程的方式实现输入端到输出端的固定处理逻辑。当处理逻辑发生改变,对应的编程实现也需要随之改变。
学习过程:若以神经网络来实现这一任务,以任务的处理逻辑为神经网络的学习目标,通过神经网络的学习(即在某个适当的网络层次结构上自适应找到一组正确的权重参数)就能自动实现该任务从输入端到输出端的处理逻辑。这一过程就是神经网络模型的学习过程
,详情我们将在后续的篇章中展开探讨。
神经网络的学习过程需要根据训练数据(任务的输入数据与输出数据)不停调整神经元之间的权重参数(连接权和阈值,统称权重参数,神经网络学到的东西都在权重参数中),直至达到一个“理想状态”。
泛化能力:这种通过学习获得的处理逻辑,往往能够覆盖一些人工编码覆盖不到的逻辑,甚至处理一些同类但模型未曾见过的案例,也即有了泛化能力
。
即使端到端任务的处理逻辑发生改变,某种程度上,也只需通过新的训练数据学得新的处理逻辑,就能适应新的任务。不再需要通过人工改变编码的方式来适配无休止变化的新规则。
任务与模型简介
上文介绍了神经网络模型的端到端处理能力,接下来我们以手写数字识别
任务为例,尝试使用神经网络的推理
能力来处理该问题,从而更一般地演示和梳理神经网络的推理过程。
神经网络的推理,就是利用已经学习好的神经网络模型,计算新的输入样例的对应输出。也即在已知网络层次结构、已知网络权重参数的固定神经网络计算公式上,进行固定函数的计算的过程。
手写数字识别
手写数字识别任务是一项计算机视觉任务,其目的是使用训练数据(这里采用的是 MNIST 数据集
)建立数字图像识别模型,从而识别任意图像中的数字。
这项任务的主要挑战在于训练模型能够准确地识别手写数字的特征和模式,在输入的图像中找出对应的数字。通过训练模型,它可以学习到不同数字之间的差异,并根据输入图像中的像素值进行预测和分类,从而实现手写数字的自动识别。
这项任务在许多领域有广泛的应用,如 ORC(Optical Character Recognition,光学字符识别)、智能表单处理、邮政编码识别等。
数据准备
MNIST 数据集是一组由美国高中生和人口调查局员工手写的 70000 个数字图片,每张图片都用其代表的数字标记。因广泛被应用于机器学习入门,被称作机器学习领域的 “Hello World!”。因其适应性,也常被用于测试新分类算法的效果。
首先我们需要下载与加载 MNIST 数据,使用 Scikit-Learn 实现如下:
前置准备:
import ssl ssl._create_default_https_context = ssl._create_unverified_context
Scikit-Learn
使用 Python 的urllib
包通过HTTPS
协议下载数据集,这里全局取消证书验证(否则Scikit-Learn
可能无法建立 ssl 连接);
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1)
print(mnist.keys())
# dict_keys(['data', 'target', 'frame', 'categories', 'feature_names', 'target_names', 'DESCR', 'details', 'url'])
简单认识 MNIST 数据:
### 查看数据集
X, y = mnist["data"], mnist["target"]
print(X.shape)
# (70000, 784)
print(y.shape)
# (70000,)
print(y[:1][0])
# 5
print(X[:1].shape)
# (1, 784)
从此处我们可以看到,MNIST 数据集 X 共有 70000 个图片数据,每个图片数据包含 784 个数值表示的 28 × \times × 28 的位图值,y 是这 70000 个图片数据对应代表的数字。
第一张图片数据对应的数字是 5。使用 Matplotlib 可以轻易将训练集的第一个输入数据转化为位图打印出来:
import matplotlib.pyplot as plt
import matplotlib as mpl
def plot_digit(data):
image = data.reshape(28, 28)
plt.imshow(image, cmap = mpl.cm.binary, interpolation="nearest")
plt.axis("off")
some_digit = X[:1].to_numpy()
plot_digit(some_digit)
plt.show()
训练集与测试集
我们可以将 MNIST 数据集分成训练集(前 6 万张图片)和测试集(最后 1 万张图片)。
也可以对训练集进行混洗,保障在做交叉验证时所有折叠的实例分布相当。有一些算法对训练实例的顺序敏感,连续输入相同的实例可能导致性能不佳,也有一些情况时间序列也是实例特征(如股市架构或天气状态),则不可混洗数据集。
x_train, x_test, t_train, t_test = X[:60000], X[60000:], y[:60000], y[60000:]
将数据分为训练集输入数据
x_train,测试集输入数据
x_test,训练集输出标签
t_train,测试集输出标签
t_test。
其中 x_train 包含 60000 个图片数据,每个图片数据包含 784 个数值表示的 28 × \times × 28 的位图值,t_train 则是这 60000 个图片数据对应代表的数字。x_test 包含 10000 个与 x_train 相同结构的图片数据,t_test 是这 10000 个图片数据对应代表的数字。
print('训练集输入数据的形状:', x_train.shape) # (60000, 784)
print('训练集输出标签的形状:', t_train.shape) # (60000,)
print('测试集输入数据的形状:', x_test.shape) # (10000, 784)
print('测试集输出标签的形状:', t_test.shape) # (10000,)
# 训练集输入数据的形状: (60000, 784)
# 训练集输出标签的形状: (60000,)
# 测试集输入数据的形状: (10000, 784)
# 测试集输出标签的形状: (10000,)
模型介绍
我们假设已有一个学好的手写数字图像识别神经网络模型(这里先跳过神经网络的学习过程,先掌握如何使用模型,再去了解如何训练制作该模型),如图 2,现在我们的目的就是使用该模型识别 MNIST 数据集中的任意图片中的数字。
图 2 所示是一个三层神经网络
,输入层有 784 个神经元,第 1、2 层隐层分别有 50 和 100 个功能神经元对输入信号进行处理,输出层有 10 个神经元,输出层的 10 个输出分别对应输入图片被该网络推理判定为数字 0 ~ 9 的概率。
我们统一一下神经网络中权重、偏置、输入层神经元、隐藏层神经元等的符号表示:
- x i x_{i} xi 表示输入层的第 i 个神经元的输入;假设输入层有 n 个神经元, X X X 表示 n 个 x 组成的形状为 (n,) 的矩阵。
- w i j ( k ) w_{ij}^{(k)} wij(k) 表示第 k-1 层网络的第 j 个神经元到第 k 层网络的第 i 个神经元之间的计算权重;假设第 k-1 层有 n 个神经元,第 k 层有 m 个神经元, W ( k ) W^{(k)} W(k) 表示第 k-1 层到第 k 层间 n × \times × m 个 w 组成的形状为 (n, m) 的矩阵。
- b i ( k ) b_{i}^{(k)} bi(k) 表示第 k-1 层到第 k 层第 i 个神经元的偏置,每一层(输入层除外)的每个神经元只有一个偏置,每层的偏置个数取决于下一层神经元的个数;假设第 k 层有 m 个神经元, B ( k ) B^{(k)} B(k) 表示第 k 层 m 个 b 组成的形状为 (m,) 的矩阵。
- a i ( k ) a_{i}^{(k)} ai(k) 表示第 k 层网络的第 i 个神经元接收的加权和经过激活函数转换后的值;假设第 k 层有 m 个神经元, A ( k ) A^{(k)} A(k) 表示第 k 层 m 个 a 组成的形状为 (m,) 的矩阵。
- y i y_{i} yi 表示输出层的第 i 个神经元;假设输出层有 n 个神经元, Y Y Y 表示 n 个 y 组成的形状为 (n,) 的矩阵。
例如图 2 所示, w 21 ( 1 ) w_{21}^{(1)} w21(1) 表示第 0 层网络的第 1 个神经元 x 1 x_1 x1 到第 1 层网络的第 2 个神经元 a 2 ( 1 ) a_2^{(1)} a2(1) 之间的权重; b 2 ( 1 ) b_2^{(1)} b