文章目录
引言
从前文「深度学习|梯度下降法:误差最小化的权重参数」,我们知道了神经网络的学习就是“找寻使损失函数的值尽可能小的权重参数”的过程,又掌握了找寻的方法(梯度下降法)。凭借这些信息,我们可以以纯手写 Python 代码的方式,实现一个简单的神经网络 SimpleNet,使用这个 SimpleNet 来演示神经网络的整个训练过程,并验证它的推理效果。
SimpleNet 网络结构
我们依旧以手写数字识别
为任务目标,实现一个可用于该任务的形如图 1 所示的 SimpleNet
,亲身体验一下神经网络学会
识别这些图片所代表数字的数学过程。
如图 1 所示,SimpleNet 是一个两层神经网络,它的输入层有 784 个神经元,分别代表 28 × \times × 28 个像素值,第 1 层隐层有 50 个功能神经元,输出层有 10 个神经元,分别代表预测结果为 0 ~ 9 的概率。
从前文对神经网络的介绍中我们知道,要实现一个神经网络的基本功能,除了要确定神经网络的结构
,我们还需要确定它每一层所使用的激活函数
,以及在进行梯度下降法
优化权重参数
时所使用的损失函数
以及梯度函数
。
激活函数
SimpleNet 的第 1 层隐层我们使用 Sigmoid 函数作为激活函数,Sigmoid 函数是一个 S 型函数,它将输入值映射到 0 到 1 之间,有助于神经网络的非线性表达。输出层我们使用 Softmax 函数作为激活函数,Softmax 函数将输入值映射成 0 到 1 之间的概率值,它输出值归一化,使得输出值之和为 1,用于此类多分类任务正好合适:
import numpy as np
def sigmoid(x):
"""S 型函数"""
return 1 / (1 + np.exp(-x))
def softmax(x):
"""归一化指数函数"""
if x.ndim == 2:
x = x.T
x = x - np.max(x, axis=0)
y = np.exp(x) / np.sum(np.exp(x), axis=0)
return y.T
x = x - np.max(x) # 溢出对策
return np.exp(x) / np.sum(np.exp(x))
关于激活函数的更多详细介绍可以参见前文「深度学习|激活函数:网络表达增强」。
损失函数
SimpleNet 的损失函数我们可以选择使用交叉熵误差
(cross entropy error
),交叉熵误差可用于评估类别的概率分布,常用于此类多分类任务。
交叉熵误差计算的是对应正确解神经元的输出的自然对数,用式 1 表示:
E = − ∑ k t k log y k (1) E = - \sum_{k}t_k\log{y_k} \tag{1} E=−k∑tklogyk(1)
其中 y k y_k yk 表示神经网络输出层的第 k 个神经元的输出值, t k t_k tk 表示监督数据的 one-hot
表示。
因为 t k t_k tk 中只有正确解索引位的值为 1,其他均为 0,式 1 实际只计算了对应正确解神经元输出的自然对数。
因此交叉熵误差的图形可以等价于自然对数函数图形:
以 y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
,t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
为例,推理结果 y 相对实际结果 t 的交叉熵误差
为:
E = − log y 2 = − log 0.6 = 0.51 。 E = -\log{y_2} \\ = -\log{0.6} \\ = 0.51。 E=−logy2=−log0.6=0.51。
更一般的,我们可以将单个 (y, t) 数据样例的交叉熵误差计算推广到求一批包含 n 个样例的训练集的交叉熵误差计算(用于 mini-batch 的梯度计算),用式 2 表示:
E = − 1 n ∑ i = 1 n ∑ k t i k log y i k (2) E =