Featured image of post 鱼书笔记-神经网络的学习

鱼书笔记-神经网络的学习

deep learning note

从数据中学习

神经网络的特征就是从数据中学习(由数据自动决定权重参数的值)

数据驱动

我们接着上一章最后手写数字识别的话题,思考一下会发现如果设计一个能自动识别5的算法还是挺困难的(至少我是这样认为的),所以我们应该考虑通过有效利用数据来解决这个问题,一种方案是从图像中提取特征量,再用机器学习技术学习这些特征量的模式

机器学习的方法中,由机器从收集到的数据中找到规律性。但是将图像转换为向量时使用的特征量仍是由人设计的,对于不同的问题,必须使用合适的特征量,才能得到好的结果

还有一种是神经网络(深度学习)的方法,该方法不存在人为介入,神经网络会直接学习图像本身

训练数据和测试数据

机器学习中把数据分成训练数据和测试数据两部分,首先用训练数据进行学习,寻找最优的参数,然后用测试数据评价训练得到的模型的实际能力,为了正确评价模型的泛化能力,就必须划分训练数据和测试数据,训练数据也被称作监督数据

泛化能力是指处理未被观察过的数据的能力。机器学习的目标就是为了提高泛化能力

因此,仅仅用一个数据集去学习和评价参数,无法正确评价,只用某个数据集过度拟合的状态称为过拟合

损失函数

神经网络的学习通过某个指标来表示现在的状态。然后以这个指标为基准,寻找最优权重参数。这个指标被称为损失函数。损失函数可以使用任意参数,但一般用均方误差和交叉熵误差等。

均方误差

如下式

$$ E = \frac{1}{2} \sum_k (y_k - t_k) ^ 2 \tag{1} $$

这里$y_k$是表示神经网络的输出,$t_k$是表示监督数据,$k$表示数据的维数,如式(1)所示,均方误差会计算神经网络的输出和正确解监督数据的各个元素之差的平方,再求总和。python实现均方误差的实现方式如下所示

1
2
def mean_squared_error(y, t):
  return 0.5 * np.sum((y - t)**2)

交叉熵误差

交叉熵误差如下式所示

$$ E = - \sum_k (t_k \log{y_k}) \tag{2} $$

$y_k$是神经网络的输出,$t_k$是正确解标签(采用one-hot表示)。交叉熵误差的值是由正确解标签所对应的输出结果决定的。

根据对数函数的性质我们可以知道,正确解标签对应的输出越大,式(2)的值就越靠近0;输出为1时,交叉熵的误差为0。如果正确解标签对应的输出较小,(2)的值就越大。

下面实现一下交叉熵误差

1
2
3
def cross_entropy_error(y,t):
  delta = 1e - 7
  return -np.sum(t * np.log(y + delta))

y和t在这里是NumPy数组,加上一个delta是为了防止-inf的发生

mini-batch学习

前面说的都是单个数据的损失函数。如果要求所有训练数据的损失函数的总和,以交叉熵误差为例,可以写成下面的式(3)

$$ E = -\frac{1}{N} \sum_{n} \sum_{k} t_{nk}\,\log y_{nk} \tag{3} $$

假设一共有N个数据,$t_{nk}$表示第n个数据的第k个元素的值

这个式子就是把单个数据的损失函数的式扩大到了N份数据,不过最后还要除以N进行正规化。

MNIST数据集的训练数据有60000个,用全部数据来计算损失函数的值所花费的时间太长,所以我们从中选取一部分。神经网络的学习也是从训练数据中选出一批数据(称为mini-batch),然后对每个mini-batch进行学习。

mini-batch版交叉熵误差的实现

对于mini-batch的交叉熵误差,只要改良一下之前实现对应单个数据的交叉熵误差就可以。这里实现一个可以同时处理单个数据和批量数据两种情况的函数

1
2
3
4
5
6
7
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

这里,y是神经网络的输出,t是监督数据。y的维度为1时,即求单个数据的交叉熵误差时,需要改变数据的形状。并且,当输入为mini-batch时,要用batch的个数进行正规化,计算单个数据的平均交叉熵误差

此外,当监督数据时标签形式(非one-hot表示,而是像"2" “7"这样的)交叉熵误差可以如下实现

1
2
3
4
5
6
7
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.sum(np.log(y[np.arrange(batch_size),t] + 1e - 7)) / batch_size

由于one-hot表示中t为0的元素的交叉熵误差也为0,因此针对这些元素的计算可以忽略。只要可以获得神经网络在正确解标签的输出,就可以计算交叉熵误差,t为one-hot表示时通过t * np.log(y)计算的地方t为标签形式时,可以用np.log(y[np.arange(batch_size),t])表示实现相同的处理

为什么要设定损失函数

假设有一个神经网络,对其中一个权重参数的损失函数求导,如果这个导数的值为负,说明使该权重参数向正正方向改变,可以减小损失函数的值;反之亦然,以及当导数的值为0时候,无论权重参数往哪个方向,损失函数的值都不会改变。而如果用识别精度作为指标,则参数的导数在绝大多数地方都为0

梯度法

梯度的方向不一定指向最小值,但是沿着梯度的方向能够最大限度地减小函数的值

梯度法是什么,就是让函数的取值沿着梯度的方向前进一段距离,在新的地方重新求梯度,然后再沿着梯度方向前进,像这样反复,逐渐减小函数值,然后我们用数学式来表示梯度法,如下式(4)

$$ x_0 = x_0 - \eta \frac{\partial f}{\partial x_0}\\ x_1 = x_1 - \eta \frac{\partial f}{\partial x_1}\tag{4} $$

上式的$\eta$表示更新量,在神经网络的学习中,称为学习率,决定了在一次学习中,应该学习多少,以及在多大程度上更新参数

接下来用python实现下梯度下降法

1
2
3
4
5
6
7
def gradient_descet(f,init_x,lr = 0.01,step_num = 100):
  x = init_x;
  for i in range(step_num):
    grad = numerical_gradient(f,x)
    x -= lr * grad

  return x

参数f是要进行最优化的函数,init_x是初始值,lr是学习率,step_num是梯度法的重复次数,numerical_gradient(f,x)会求函数的梯度

神经网络的梯度

神经网络的学习也要求梯度,这里所说的梯度是指损失函数关于权重参数的梯度,例如一个形状2x3的权重$W$的神经网络,损失函数用L表示。此时,梯度可以用$\frac{\partial L}{\partial \mathbf{W}}$表示

$$ \mathbf{W} = \begin{pmatrix} w_{11} & w_{12} & w_{13} \\ w_{21} & w_{22} & w_{23} \end{pmatrix}\\ \frac{\partial L}{\partial \mathbf{W}} = \begin{pmatrix} \frac{\partial L}{\partial w_{11}} & \frac{\partial L}{\partial w_{12}} & \frac{\partial L}{\partial w_{13}} \\ \frac{\partial L}{\partial w_{21}} & \frac{\partial L}{\partial w_{22}} & \frac{\partial L}{\partial w_{23}} \end{pmatrix}\tag{5} $$
使用 Hugo 构建
主题 StackJimmy 设计