本文是关于神经网络的学习过程的描述。本文是《深度学习入门:基于Python的理论与实现》中神经网络一章的笔记。
第四章 神经网络的学习
利用数据学习
深度学习是由数据到数据的学习方法,也被成为端到端的机器学习方法(end-to-end machine learning)。
损失函数
损失函数(loss function)描述系统输出和实际数值之间的误差。
均方误差
如果一组经过softmax函数生成的输出为 实际数值为 则通过对数组对应项相减后,计算平方和即可得到均方误差。上面实际值t的表达方式称为one-hot表示。
均方误差的python实现为:
def mean_squared_error(y, t):
return 0.5 * np.sum((y-t)**2)
交叉熵误差
如果实际值t是one-hot表示,则交叉熵实际上只计算了正确解的自然对数。
交叉熵误差的python实现为:
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta))
在函数中加入delta是为了避免y=0时函数值出现无限大。
mini-batch学习
假设数据有N个,每个数据有K个元素,则计算损失函数时,应考虑全部N×K个元素。此时,交叉熵函数可表示为: 在实际训练时,训练序列可能很多,比如上一节中MNIST的训练序列有60000个。但一次计算60000个序列的交叉熵,计算复杂度可能非常高。 mini-batch学习的方法,就是每次选出一个小批量(例如100个)训练序列进行训练,并反复进行。
在python中可以用下面的方法选取训练序列:
train_size = x_train.shape[0]
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
mini-batch交叉熵实现
如果t是one-hot表达方式,则每个mini-batch的交叉熵可以这样计算:
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(t * np.log(y + 1e-7)) / batch_size
如果t不是one-hot表达方式,而是数值表达方式,则mini-batch的交叉熵可以通过下述方法计算:
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
神经网络的优化目标
数学目标
不论损失函数是什么形式,神经网络的优化目标都是使得损失函数最小化。对于均方误差函数和交叉熵函数,函数的极小值即为函数的最小值。
优化神经网络,通过求解上述损失函数梯度为0的点即可获得。
数学上,梯度表示为:
数值解
现实中,由于损失函数的维度很高,直接求解梯度函数不现实。可以通过梯度下降法,用数值解来逼近理论最优解。
在一个函数中,某一点的梯度,表明在该点上,沿着某一个维度进行下降的最快方向。
用超参来表示每次逼近最优解的步长,的选取会影响算法性能。过大则可能不会收敛,过小,则迭代次数过多,算法性能下降。
每一轮的迭代数学上可表示为:
神经网络的梯度
神经网络中需要调整的参数为每一级的权重矩阵W和偏置向量b,则神经网络的梯度可表示为:
神经网络的Python算法实现
算法描述
前提 神经网络存在适合的权重和偏置,使得数据的学习可以获得足够小的“损失”。
步骤1:mini-batch选取数据 根据mini-batch方法,选择出一部分训练序列,通过神经网络的训练使得函数的损失值变小。
步骤2:计算数值梯度 求出各个权重参数的梯度。
步骤3:更新参数 按照梯度的方向,以的速度更新权重矩阵和偏置向量,降低损失函数。
步骤4:判断迭代是否停止或重复步骤1 如果迭代满足了一定的条件,比如到达迭代最大次数,或者损失函数足够小,或者损失函数的变化足够小,可以停止算法;否则重复步骤1~3。
代码概述
代码参见附件,几个关键类成员变量和成员函数及作用如下:
初值选取
在构造函数中可以设定网络权重矩阵和偏置向量的初始值,W初始值随机选取,b可以选取为0向量。可以将网络的这两个参数保存在类的成员函数params
中。
损失函数
loss(self, x, t)
为损失函数,用来计算预测值与真实值之间的误差评价。
预测函数
predict(self, x)
为预测函数,通过网络的参数计算对输入数据的预测值。
梯度函数
numerical_gradient(self, x, t)
为用数值方法计算的梯度函数,在后面章节中对这个函数有优化。
超参选取
以下超参需要在算法开始前选取,对应作用如下:
参数 | 说明 |
---|---|
iters_num | 定义了一次算法的迭代次数(或最大值) |
learning_rate | 即前文中的,代表了学习速度 |
train_size | 训练序列的数据量 |
batch_size | mini-batch学习时的每一批次数据量 |
mini-batch的函数实现
列举一部分代码:
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
for i in range(iters_num):
# 步骤1:进行mini-batch随机选取
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
# 步骤2:计算梯度
grad = network.numerical_gradient(x_batch, t_batch)
# 步骤3:更新网络参数
for key in ('W1', 'b1', 'W2', 'b2'):
network.params[key] -= learning_rate * grad[key]
# 步骤4:计算损失函数,判断是否需要继续迭代(这里没有判断)
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)