Loading

吴恩达机器学习笔记-9.2(神经网络2)

神经网络2

以分类问题为例子。
我们有这样一个神经网络,以及这样一个训练集:
image
image
令L表示网络的总层数,显然此处\(L=4\)
\(s_l\)表示第l层的单元数(神经元数),显然此处\(s_1=3、s_2=5、s_3=5、s_4=4=s_L\)

二分类问题时

在二分类问题中,\(y=0 or 1\),且只会有一个输出单元,此时输出的值也就是\(h_\Theta(x)\)是一个实数(往往在0到1之间),此时\(s_L=1\),或者可以按照约定记为\(K=1\)

多类别分类问题

而现实生活中绝大多数深度学习问题都是多类别分类问题(K个不同的类),多分类问题中,\(y \in R^K(K维向量)\)\(s_L=K\)
譬如,如果有4类,那么\(y=\begin{bmatrix}1\\\\0\\\\0\\\\0\end{bmatrix}\)or\(\begin{bmatrix}0\\\\1\\\\0\\\\0\end{bmatrix}\)or\(\begin{bmatrix}0\\\\0\\\\1\\\\0\end{bmatrix}\)or\(\begin{bmatrix}0\\\\0\\\\0\\\\1\end{bmatrix}\)
我们会有K个输出单元,这类问题中我们的假设函数值同样会是个K维向量。
(当\(K\ge3\)时才会是多类别分类问题)

代价函数

根据之前所学内容,神经网络模型实质上是多个逻辑回归模型依靠前向传播结合到一起。
那么首先回顾逻辑回归中的代价函数:

\[J(θ)={-}{\frac{1}{m}}[\sum^{m}_{i=1}{y^i}\log(h_\theta{(x^i)}+(1-y^i)\log(1-h_\theta{(x^i)})]+\frac{\lambda}{2m}\sum^n_j\theta_j^2 \qquad j=1,2,...,n \]

在K类别分类问题中,不再仅有1个逻辑回归输出单元,而是K个,容易得出以逻辑回归为激励的神经网络的代价函数如下:
image
需要注意的是,这里的\(h_\Theta(x)\)是一个K维向量,\({(h_\Theta(x))}_i\)表示\(h_\Theta(x)\)中的第i个元素。

反向传播

因为梯度下降算法中涉及到了代价函数的偏导项,反向传播算法就是用于求解这些偏导项的算法。

image
以上图的神经网络模型为例。

假设只有一个样本

假设现有的一个训练集中只有一个训练样本\((x,y)\),也就是一个实数对。
首先回顾一下前向传播的过程:
image
第3步和第5步分别加入了偏置单元。

直观地说,反向传播是需要计算一个“误差项”(error)
我们记\(\delta_j^{(l)}\)为第l层的第j个结点(单元)的误差项。
我们还记得\(a_j^{(l)}\)表示第l层的第j个结点(单元)的激励值(或者说l层新的输入特征),那么“误差项”可以捕捉到在该结点激励值与所谓真实值的误差。
那么容易得到在该神经网络模型中的\(\delta_j^{(4)}=a_j^{(4)}-y_j\),其中\(a_j^{(4)}={h_\Theta(x)}_j\)。也就是假设输出值和真实值之间的误差。
按照惯例进行向量化,也可以表示为:\(\delta^{(4)}=a^{(4)}-y\)
得到了输出层的“误差”,反向传播才能工作,去求前面几层的“误差”。
image
上式中的“.”运算符,在Octave\matlab中的矩阵运算中表示按元素位进行运算。
经过求导可以得出\(g'(z^{(3)})=a^{(3)}.{\times}(1-a^{(3)})\)。其他导数项以此类推。求导过程不再赘述。
需要注意的是,不会有\(\delta^{(1)}\)的存在,因为输入层就是我们在训练集中观察到的,不会存在误差,误差只会存在于经过激励函数计算之后的激励值当中。
这种从输出层一直运算到输入层的算法,对比于前向传播,命名为反向传播。
最终的结论就是,再不是特别严谨的情况下,梯度下降算法中的偏导项\(\frac{∂J(\Theta)}{∂{\Theta}_{ij}^{(l)}}=a_j^{(l)} {\delta}_i^{(l+1)}\)

有许多样本

以上是只有一个样本时的计算方法。
当有多个样本时,偏导是这样计算的。
\(\Delta_{ij}^{(l)}=0 \quad{(for\quad{all}\quad{l,i,j})}\)(这个\(\Delta_{ij}^{(l)}\)用于计算偏导)
计算过程如下(for循环,i=1 to m,m为样本数):

  1. \(a^{(1)}=x^{(i)}\)
  2. 执行前向传播计算激励值\(a^{(l)} \quad{(l=2,3,...,L)}\)
  3. \(y^{(i)}\)计算输出层的“误差项”,\(\delta^{(L)}=a^{(L)}-y^{(i)}\)
  4. 反向传播计算\(\delta^{(L-1)},\delta^{(L-2)},...,\delta^{(2)}\)
  5. \(\Delta_{ij}^{(l)}:=\Delta_{ij}^{(l)}+a^{(l)}_j{\delta_i^{(l+1)}}\),向量化之后为\(\Delta^{(l)}:=\Delta^{(l)}+{\delta^{(l+1)}}{(a^{(l)})}^T\)
  6. \[D_{ij}^{(l)}:=\begin{cases} \frac{1}{m}\Delta_{ij}^{(l)}+\frac{\lambda}{m}\Theta_{ij}^{(l)}& \text{if j≠0}\\\\\frac{1}{m}\Delta_{ij}^{(l)} &\text{if j=0} \end{cases} \]

  7. \(\frac{∂J(\Theta)}{∂{\Theta}_{ij}^{(l)}}=D_{ij}^{(l)}\)
    能够看出,这些计算出来的\(D_{ij}^{(l)}\),就是梯度下降算法中所要用到的偏导数的值。

反向传播计算过程

反向传播其实和前向传播极其类似。
先回顾一下前向传播的计算过程
image
以这个神经网络模型为例子。

首先需要知道9.1里定义过的变量:

\[a_1^{(2)}=g(\Theta_{10}^{(1)}x_0+\Theta_{11}^{(1)}x_1+\Theta_{12}^{(1)}x_2) \]

\[z_1^{(2)}=\Theta_{10}^{(1)}x_0+\Theta_{11}^{(1)}x_1+\Theta_{12}^{(1)}x_2 \]

需要注意的是,这是第二层的变量。

我们知道神经网络具有自我训练的特性。
前一层的激励值作为输入到下一层的特征值。
那么容易得到在此例中:

\[z_1^{(3)}=\Theta_{10}^{(2)}{\times}{1}+\Theta_{11}^{(2)}a_1^{(2)}+\Theta_{12}^{(2)}a_2^{(2)} \]

image
这就是前向传播的具体过程。

下面来看反向传播。
我们关注只有一个训练样本的特殊情况,并且忽略正则项(在此情况下,代价函数会更简单,理解起来更为清楚)

此时代价函数为:\(cost(i)={y^i}\log(h_\theta{(x^i)}+(1-y^i)\log(1-h_\theta{(x^i)})\)

(这其实可以理解为神经网络输出值和实际值之间的方差,我们可以把这个函数近似地看作\(cost(i)={(h_\Theta{(x^i)}-y^{(i)})}^2\)

我们记\(\delta_j^{(l)}\)为第l层的第j个结点(单元)的误差项。
(经过求导运算是可以证明\(\delta_j^{(l)}=\frac{∂{cost(i)}}{∂z_j^{(l)}}\)的,这里不再对求导进行赘述。)

类似于前向传播,我们有如下计算过程:
image
前一层误差值等于后一层各误差值与各自权重值之积的和。

矩阵展开为向量

前面讲过计算代价函数最小值的高级优化算法。
其中使用的参数是向量,然而对于一个完整的神经网络来说,参数是一个矩阵,那么输入到高级算法中的时候就需要将矩阵展开为向量。

假设我们有一个3层的神经网络。
第一层有10个单元。
第二层有10个单元。
第三层有1个单元。
那么第一层的参数(权重)\(\Theta^{(1)}\)是一个\(10\times11\)阶矩阵。
第二层的参数(权重)\(\Theta^{(2)}\)是一个\(10\times11\)阶矩阵。
第三层的参数(权重)\(\Theta^{(3)}\)是一个\(1\times11\)阶向量。
image
在octave\matlab的矩阵运算中“:”运算符的意思是“所有”,比如Theta1(:)就是指Theta1矩阵中的所有元素以列向量形式输出,如果是Theta1(1,:)就是指Theta1矩阵第一行的所有元素以原有形式输出。

第一行代码就将三个矩阵转换成了一个长向量。
第四五六行代码将向量又转回矩阵。
第四行代码的意思是取向量的前110个元素,然后转换成10行11列的矩阵。

梯度检测

反向传播含有许多细节,实现起来会比较困难,但是反向传播算法在于梯度下降算法或其他一些算法一起工作时会出现一些极细小的bug。看起来确实能正常运行,代价函数值也在不断减小,但是到了最后得到的神经网络模型会比无bug的模型的误差高一个量级。

因此引入一个思想,叫做梯度检测。可以解决几乎所有这类问题。
考虑这样一个例子:
image
坐标系中有这样一个代价函数\(J(\Theta)\)
\(\Theta\)是一个实数,该点的导数就是函数在该点的斜率
image

考虑点\(\Theta\)的邻域\((\Theta-\epsilon,\Theta)\quad(\Theta,\Theta+\epsilon)\)
那么点\(\Theta-\epsilon\)与点\(\Theta+\epsilon\)的连线的斜率应该等于\(\frac{J(\Theta+\epsilon)-J(\Theta-\epsilon)}{2\epsilon}\)
image
\(\epsilon\)的值足够小时,就可以近似地认为红线斜率约等于\(J(\Theta)\)的真实斜率。
梯度检测的精髓,就在于检测每一次迭代的梯度值与真实的梯度值的差别是否会由于bug而过大。

在octave\matlab中实现如下:
image

那么对于神经网络中有多个参数时,相应地就会变成求偏导,大同小异
image
\(\theta\)是n维向量,是\(\Theta\)展开的向量。

image
整个算法实现如上,最终目的就是要检测反向传播求出来的导数值和真实导数值是否近似。

首先要实现反向传播得到偏导值。
然后实现梯度检测得到真实导数值。
保证这两个值近似。
关闭梯度检测,将反向传播用于学习过程。

PS:确保在训练分类器之前关闭了梯度检测算法,因为梯度检测算法非常占用资源,会导致整个训练过程异常慢。

随机初始化

在运用梯度下降算法和其他高级算法之前,我们需要为参数(权重)\(\Theta\)选取初始值。

对称权重问题

一种想法是将\(\Theta\)的值全部设置为0,在逻辑回归中,这样的初值是可行的,但是在神经网络中,全为0的初值起不到任何作用。
以下面的神经网络为例子:
image
如果权重\(\Theta\)的初值全为0,那么很容易得出\(a_1^{(2)}=a_2^{(2)} \qquad \delta_1^{(2)}=\delta_2^{(2)}\)

image
同时容易得到关于权重的偏导数也是相等的
那么在每次梯度下降更新权重时,更新之后的权重永远都是相等的。
那么在前向传播时,新的特征值将会永远相等,神经网络将失去自我学习的特性。

随机初值

通常情况下会将初值设置为范围在\([-\epsilon,\epsilon]\)
image

总结

  1. 选择合适的神经网络结构
    image
    首先要根据样本特征集的维度确定输入单元个数
    然后根据分类问题(多分类、单分类)确定输出单元个数
    至于隐藏层的数量,一般默认有1层隐藏层,如果有多个隐藏层,每个隐藏层的单元都应该是一样的。一般来说隐藏层的单元数越多越好。
  2. 给权重赋随机的初值
  3. 执行前向传播,得到每个样本对应\(h_\Theta(x^{(i)})\)
  4. 计算代价函数
  5. 执行后向传播计算代价函数的偏导值
  6. 用梯度检测去对比通过后向传播计算出的偏导值与用数学估计得出的\(J(\Theta)\)的梯度,确保两个值基本接近,然后禁用梯度检测的代码。
  7. 用梯度下降或者其他高级算法,与后向传播一起工作。来最小化关于参数\(\Theta\)的代价函数\(J(\Theta)\)
posted @ 2021-06-09 10:58  mmmhongyu  阅读(162)  评论(0)    收藏  举报