斯坦福机器学习课程 第五周

(1) 训练神经网络

代价函数

接下来介绍一种在给定训练集下为神经网络拟合参数的学习算法。我们从拟合神经网络参数的代价函数开始讲起

以神经网络在分类问题中的应用为例:

假设我们有一个如上图所示的神经网络结构,然后假设我们有一个这样的训练集:
$$
(x^{(1)}, y^{(1)}), (x^{(2)}, y^{(2)}), …, (x^{(m)}, y^{(m)})
$$
一共有m个训练样本$$(x^{(i)}, y^{(i)})$$。

用大写字母$L$表示这个神经网络结构的总层数:
$$
L = total~no.~of~layers~in~networks(神经网络总层数)
$$
所以我们得到$L = 4$。

然后,我们用$S_l​$表示第​$l​$层的神经元的数量,这其中不包括L层的偏执单元

  • $S_l = 3(也就是输入层)$
  • $S_2 = 5$
  • $S_3 = 5$
  • $S_4 = S_l = 4(因为L=4)$

接下来讨论两种分类问题,分别是二元分类多类别分类

二元分类(Binary classification)

在二元分类中$y$只能是0或1:
$$
y = 0~or~1
$$
在这个例子中,我们有一个输出单元(不同于上面的神经网络有4个输出单元)。神经网络的输出会是一个实数:
$$
h_\Theta(x) \in \mathbb{R}
$$
输出单元的个数:
$$
S_L = 1
$$

在这类问题里,为了简化记法,我们把$K$设为1,这样就可以把$K$看作输出层的单元数目。

多类别分类(Multi-class classification)

在多类别分类问题中,会有$K$个不同的类,比如说如果我们有四类的话,我们就用下面这种表达形式来代表$y$。在这类问题里,我们就会有$K$个输出单元。

我们的输出假设就是一个$K$维向量:
$$
h_\Theta(x) \in \mathbb{R}^K
$$
输出单元的个数就是$K$:
$$
S_L = K
$$

定义代价函数

我们在神经网络里,使用的代价函数,应该是逻辑回归里使用的代价函数的一般形式。

逻辑回归的代价函数:
$$
J(\theta) = -\frac{1}{m}[\sum_{i=1}^my^{(i)}logh_\theta(x^{(i)}) + (1 - y^{(i)})log(1 - h_\theta(x^{(i)}))] + \frac{\lambda}{2m}\sum_{j=1}^n\theta_j^2
$$
其中$\frac{\lambda}{2m}\sum_{j=1}^n\theta_j^2$这一项是个额外的正则化项,是一个$j$从1到$n$的求和形式。因为我们并没有把偏置项0正则化。

对于神经网络,我们使用的代价函数是这个例子的一般化形式。
$$
J(\Theta) = -\frac{1}{m}[\sum_{i=1}^m \sum_{k=1}^K y^{(i)}log(h_\Theta(x^{(i)})_k + (1 - y^{(i)})k)log(1 - h\Theta(x^{(i)}))k] + \frac{\lambda}{2m} \sum{l=1}^{L-1} \sum_{i=1}^{S_l} \sum_{j=1}^{S_{l+1}}(\Theta_{ji}^{(l)})^2
$$
神经网络现在输出了在$K$维的向量$h_\Theta(x)$:
$$
h_\Theta(x) \in \mathbb{R}^K
$$
用$(h_\Theta(x))_i$来表示第$i$个输出。

其中$\sum_{k=1}^K$这个求和项是$K$个输出单元的求和,比如你有4个输出单元在神经网络的最后一层,那么这个求和项就是$k$从1到4所对应的每一个逻辑回归算法的代价函数之和。

最后式子中的这一项$ \frac{\lambda}{2m} \sum_{l=1}^{L-1} \sum_{i=1}^{S_l} \sum_{j=1}^{S_{l+1}}(\Theta_{ji}^{(l)})$类似于我们在逻辑回归里所用的正则化项,它所做的就是把这些项全部加起来,也就是对所有的$\Theta_{ji}^{(l)}$的值都相加。正如我们在逻辑回归里的一样,这里要除去那些对应于偏差值的项。

反向传播(B-P)

下面是上面写好的代价函数:

我们要做的就是试图找到代价函数$J(\Theta)$最小的$\Theta$值:
$$
min~J(\Theta)
$$
为了使用梯度下降法,我们需要做的就是写好一个通过输入参数$\Theta$,然后计算:
$$
\begin{align}
& J(\Theta) \
& \frac{\alpha}{\alpha\Theta_{ij}^{(l)}}J(\Theta)
\end{align}
$$
下面将会讲解如何计算这两项。


梯度下降计算

接下来,为了计算导数项,我们将采用一种叫做反向传播(Backpropagation)的算法。

反向传播算法从直观上说就是堆每一个节点求下面这一个误差项:
$$
\delta_j^{(l)} = \text{“error” of node}~j ~ \text{in layer}~l.
$$

$\delta_j^{(l)}$这种形式代表了第$l$层的第$j$个结点的误差

具体来讲,用上面那个四层的神经网络结构做例子:

每一项的输出单元(layer L = 4)
$$
\delta_j^{(4)} = a_j^{(4)} - y_j
$$

对于每一个输出单元,我们准备计算$\delta$项,所以第四层的第$j$个单元的$\delta$就等于这个单元的激励值减去训练样本里的真实值。所以$a_j^{(4)}$这一项同样可以写成$h_\Theta(x)_j$:

$$
\delta_j^{(4)} = h_\Theta(x)_j - y_j
$$

另外,如果把$\delta$、$a$和$y$这三项都看作向量的话,那么上面的式子也可以向量化:
$$
\delta^{(4)} = a^{(4)}-y
$$
这里的$\delta^{(4)}$、$a^{(4)}$和$y$都是一个向量,并且向量维数等于输出单元的数目。

所以现在计算出网络结构的误差项$\delta^{(4)}$,下一步就是计算网络中前面几层的误差项$\delta$。

这就是$\delta^{(3)}$的计算公式:
$$
\delta^{(3)} = (\Theta^{(3)})^T\delta^{(4)}.*g’(z^{(3)})
$$

很容易$g’(z^{(3)})$这一项的值是:
$$
a^{(3)}.*(1-a^{(3)})
$$

这里的1是元素都为1的向量。

接下来可以应用一个相似的公式来求得$\delta^{(2)}$:
$$
\delta^{(2)} = (\Theta^{(2)})^T\delta^{(3)}.*g’(z^{(2)})
$$
值得注意的是,这里我们没有$\delta^{(1)}$项,因为第一层是输入层,不存在误差。所以在这个例子中,我们的$\delta$项就只有第二层和第三层。


反向传播法这个名字源于我们从输出层开始计算$\delta$项,然后再往前计算。

所以我们是类似于把输出层的误差反响传播给了第三层,然后再传到第二层。这就是反向传播的意思。

如果忽略标准化所产生的项,我们可以证明我们想要的偏导项恰好就是下面这个表达式:
$$
\frac{\alpha}{\alpha\Theta_{ij}^{(l)}}J(\Theta) = a_j^{(l)}\delta_i^{(l+1)} \
(ignore~\lambda;~if~\lambda = 0)
$$

这里暂时忽略了$\lambda$,在之后将会完善这一个关于正则化项。

到现在,我们通过反向传播计算这些$\delta$项,可以非常快速的计算出所有参数的偏导数项。


总结

假设我们有m个样本的训练集:

$Training~set~(x^{(1)},y^{(1)}),…, (x^{(m)}, y^{(m)})$

首先初始化这些值:
$$
\Delta_{ij}^{(l)} = 0~(\text{for all}~l,i,j)
$$

这里的$\Delta_{ij}^{(l)}$将被用来计算偏导数项$\frac{\alpha}{\alpha\Theta_{ij}^{(l)}}J(\Theta)$

所以这些$\Delta_{ij}^{(l)}$将被作为累加项,慢慢增加,以计算出这些偏导数。

接下来要执行的操作是:

在这里将遍历我们的训练集。

我们要做的第一件事就是设定$a^{(1)}$

接下来运用正向传播,计算第2,3,4,…,L层的激励值$a^l$

接下来用$y^{(i)}$来计算$\delta^{(L)} = a^{(L)} - y^{(i)}$

再接下来,使用反向传播算法来计算$\delta^{(L-1)},\delta^{(L-2)},…\delta^{(2)}$

最终,用$\Delta_{ij}^{(i)}$来积累前面写好的偏导数项:
$$
\Delta_{ij}^{(l)} := \Delta_ij^{(l)} + a_j^{(l)}\delta_i^{(l+1)}
$$
上面这个表达式也可以写成向量形式:

具体来说,如果把$\Delta$看作一个矩阵,$ij$代表矩阵中的位置,那么上面的式子就可以写成:
$$
\Delta^{(l)} := \Delta^{(l)} + \delta^{(l+1)}(a^{(l)})^T
$$
最后,执行这个for循环体之后我们跳出这个循环,然后计算下面这些式子:
$$
D_{ij}^{(l)} := \frac{1}{m}\Delta_{ij}^{(l)} + \lambda\Theta_{ij}^{(l)}~(if~j\ne0) \
D_{ij}^{(l)} := \frac{1}{m}\Delta_{ij}^{(l)}~(if~j = 0)
$$

这里分$j \ne 0$和$j = 0$两种情况来讨论,在$j = 0$的情况下对应的是偏差项,所以这也是为什么在$j=0$的情况下没有写额外的标准化的原因。

最后,尽管严格证明很复杂,但是一旦计算出这些值,这就正好是代价函数关于每一个参数的偏导数:
$$
\frac{\alpha}{\alpha\Theta_{ij}^{(l)}}J(\Theta) = D_{ij}^{(l)}
$$
所以,你可以把它用在梯度下降算法,或者其他更高级的算法中。

反向传播算法的直观介绍

距离说明神经网络计算过程

为了更好地理解反向传播算法,先仔细研究一下向前传播的原理。

这里有一个包含两个输入单元(不包含偏差单元)的神经网络,在第二层有两个隐藏单元(不包含偏差单元),第三层也有两个隐藏单元(不包含偏差单元),最后一层有一个输出单元。

向前传播

下图展示了这个神经网络向前传播的运算过程:

事实上,反向传播算法的运算过程非常类似于此,只有计算方向不同而已。

代价函数

为了更好的理解反向传播算法的原理,我们把目光转向代价函数:

这个代价函数对应的情况是只有一个输出单元,如果我们有不止一个输出单元的话,只需要对所有的输出单元进行一次求和运算。

注意这种只有一个输出单元的情况,如果不考虑正则化即$\lambda=0$,因此最后的正则化项就没有了。

这个求和运算括号里面与第i个训练样本对应的代价项,也就是说$(x^{(i)}, y^{(i)})$ 对应的代价项,将有下面这个式子决定:
$$
cost(i) = y{(i)}logh_\Theta(x^{(i)})+(1-y^{(i)})logh_\Theta(x^{(i)})
$$
而这个代价函数所扮演的角色可以看做是平方误差,也可以把$cost(i)$ 想象成:
$$
cost(i) \approx (h_\Theta(x^{(i)}) - y^{(i)})^2
$$
因此,这里的$cost(i)$表征了该神经网络是否能准确地预测样本$i$的值,也就是输出值和实际观测值$y^{(i)}$的接近程度。

反向传播

现在我们来看看反向传播是怎么做的。

一种直观的理解是反向传播就是在计算所有这些$\delta$项:
$$
\delta_j^{(l)} = “error” ~of~ cost~ for ~a_j^{(l)}~(unit~j~in~layer~l)
$$
并且我们可以把它们看做是这些激励值的“误差”()