Skip to the content.

本文是关于神经网络的学习过程的描述。本文是《深度学习入门:基于Python的理论与实现》中神经网络一章的笔记。

第四章 神经网络的学习

利用数据学习

深度学习是由数据到数据的学习方法,也被成为端到端的机器学习方法(end-to-end machine learning)。 image

损失函数

损失函数(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)