《Neural Networks and Deep Learning》课程笔记

Lesson 1 Neural Network and Deep Learning

这篇文章其实是 Coursera 上吴恩达老师的深度学习专业课程的第一门课程的课程笔记。

参考了其他人的笔记继续归纳的。

逻辑回归 (Logistic Regression)

逻辑回归的定义

神经网络的训练过程可以分为前向传播(forward propagation) 反向传播 (backward propagation) 的 过程。我们通过逻辑回归的例子进行说明。

逻辑回归是一个用于二分类 (binary clasification) 的算法。比如说,我们有一张图片作为输入,比如下图中的猫,如果识别这张图片为猫,则输出标签1作为结果;如果识别出不是猫,那么输出标签0作为结果。而我们把输出结果用 \(y\) 表示就如下图所示。

269118812ea785aee00f6ffc11b5c882

图片在计算机中保存的话,我们需要保存三个矩阵,它们分别对应图片中的红、绿、蓝三种颜色通道。如果图片是 \(64\times64\) 像素的,那么这三个矩阵的大小都是 \(64\times64\)

1e664a86fa2014d5212bcb88f1c419cf

为了把这张图片的像素值转换为特征向量 \(x\),我们需要把三个矩阵展开为一个向量,而这个向量的总维度用 \(n_x\) 表示的话,就是 \(n_x=3\times64\times64=12,288\)

符号定义:

\(x\):表示一个 \(n_x\) 维数据,为输入数据,维度为 \((n_x,1)\)

\(y\):表示输出结果,取值为 \((0,1)\)

\((x^{(i)},y^{(i)})\):表示第 \(i\) 组数据,可能是训练数据,也可能是测试数据;

\(X=[x^{(1)},x^{(2)},\dots,x^{(m)}]\):表示所有的训练数据集的输入值,放在一个 \(n_x\times m\) 的矩阵中,每一列为一组数据,其中 \(m\) 表示样本数目;

\(Y=[y^{(1)},y^{(2)},\dots,y^{(m)}]\):对应表示所有训练数据集的输出值,维度为 \(1\times m\)

其实对于逻辑回归来说,我们想要的输出结果是预测,称为 \(\hat{y}\),也就是对实际值 \(y\) 的估计。\(\hat{y}\) 表示 \(y=1\)

的一种可能性,用上面的例子来说,就是让 \(\hat{y}\) 告诉我们这是一只猫的图片的几率有多大。

逻辑回归使用的参数有两个: \(w\) (表示特征权重,维度与特征向量相同)和 \(b\) (表示偏差)。这时我们不能使用 \(\hat{y}=w^Tx+b\) 进行预测,这实际是一个线性回归,而我们需要让 \(\hat{y}\) 在 0 到 1 之间来表示对结果的预测。因此,我们使用 sigmoid 函数,这个非线性函数,即 \(\hat{y}=\sigma(w^Tx+b)\)

sigmoid 函数

函数的定义为:$ f(x) = \frac{1}{1 + e^{-x}} $,其值域为 $ (0,1) $。

函数图像如下所示

3-26

逻辑回归的代价函数

为了训练逻辑回归模型的参数,我们需要一个代价函数 (cost function),有时也翻译为成本函数。我们通过训练代价函数来得到我们需要的参数 \(w\)\(b\)

损失函数 (loss function),又称为误差函数,用来衡量算法的运行情况。一般定义为:\(L(\hat{y},y)\)

通过损失函数,我们可以衡量预测输出值和实际值有多接近。一般我们用预测值和实际值的平方差或者它们平方差的一半,但是通常在逻辑回归中不这么做。因为学习逻辑回归参数的时候,我们的优化目标不是凸优化,只能找到多个局部最优值,梯度下降法很可能找不到全局最优值。

所以,在逻辑回归中使用的损失函数是
\[ L(\hat{y},y)=-ylog(\hat{y})-(1-y)log(1-\hat{y}) \]
\(y=1\) 时,损失函数 \(L=-log(\hat{y})\),如果想要损失函数尽可能的小,那么 \(\hat{y}\) 就要尽可能大,因为 sigmoid 函数值域是 \((0,1)\),所以 \(\hat{y}\) 会无限接近 1。

\(y=0\) 时,损失函数 \(L=-log(1-\hat{y})\),如果想要损失函数尽可能的小,那么 \(\hat{y}\) 就要尽可能小,因为 sigmoid 函数值域是 \((0,1)\),所以 \(\hat{y}\) 会无限接近 0。

当然,损失函数只是对于单个训练样本定义的,它衡量的是算法在单个训练样本中表现如何。为了衡量算法在全部训练样本上的表现如何,我们需要定义一个算法的代价函数,也就是对 \(m\) 个样本的损失函数求均值:
\[ J(w,b)=\frac{1}{m} \sum^{m}_{i=1} L(\hat{y}^{(i)},y^{(i)})=\frac{1}{m} \sum^{m}_{i=1} (-y^{(i)}\log{\hat{y}^{(i)}}-(1-y^{(i)})\log{(1-\hat{y}^{(i)})}) \]
所以在训练逻辑回归模型时候,我们需要找到合适的参数来让代价函数的总代价降到最低。逻辑回归可以看作一个非常小的神经网络。

梯度下降法 (Gradient Descent)

梯度下降法可以在测试集上,通过最小化代价函数 \(J(w,b)\) 来训练参数 \(w\)\(b\)

形象化地来表示梯度下降法如下图所示。

a3c81d2c8629d674141def47dc02f312

在上图中,横轴表示参数 \(w\)\(b\),在实际操作中,\(w\) 可以是更高维度的。这里仅为了绘图需要,定义 \(w\)\(b\) 为单一实数,代价函数就是图中的曲面,因此曲面的高度就是代价函数 \(J(w,b)\) 在某一点的函数值。

236774be30d12524a2002c3c484d22d5

如上图,代价函数是一个凸函数 (convex function)

af11ecd5d72c85f777592f8660678ce6

而上图,就不太一样了。它是非凸的,而且有很多个不同的局部最小值。由于逻辑回归的代价函数的特性,我们必须定义代价函数 \(J(w,b)\) 为凸函数。初始化参数 \(w\)\(b\) 可以采用随机初始化的方法,对于逻辑回归几乎所有的初始化方法都有效,因为函数是凸函数,无论在哪里初始化,应该达到同一点或大致相同的点。

比如说下图,从最开始的小红点开始初始化,朝最陡的下坡方向走,不断地迭代,直到走到全局最优解或者接近全局最优解的地方。

c5eda5608fd2f4d846559ed8e89ed33c

细节化说明梯度下降法

假定代价函数 \(J(w)\) 只有一个参数 \(w\),即用一维曲线代替多维曲线。如下图所示。

5300d40870ec58cb0b8162747b9559b9

迭代就是不断地重复下图的公式:

6cdef1989a113fc1caaaaf6ebaaa3549

其中,

\(:=\) 表示更新参数,

\(\alpha\) 表示学习率 (learning rate),用来控制步长 (step),即向下走一步的长度 \(\frac{dJ(w)}{dw}\) 就是函数 \(J(w)\)\(w\) 求导 (derivative),在代码中我们会使用 \(dw\) 表示这个结果。

对于导数更加形象化的理解就是斜率 (slope),如图该点的导数就是这个点相切于 \(J(w)\) 的小三角形的高除宽。假设我们以如图点为初始化点,该点处的斜率的符号是正的,即 \(\frac{dJ(w)}{dw}>0\),所以接下来会向左走一步。整个梯度下降法的迭代过程就是不断地向左走,直至逼近最小值点。

4fb3b91114ecb2cd81ec9f3662434d81

那么现在把代价函数的参数重新设定为两个,\(w\)\(b\)。迭代的公式则为:
\[ w:=w-\alpha \frac{\partial{J(w,b)}}{\partial{w}}\\ b:=b-\alpha \frac{\partial{J(w,b)}}{\partial{b}} \]
其中,\(\partial\) 表示求偏导符号,\(\frac{\partial{J(w,b)}}{\partial {w}}\) 就是函数 \(J(w,b)\)\(w\) 求偏导。

逻辑回归中的梯度下降

假设样本只有两个特征 \(x_1\)\(x_2\),为了计算 \(z\),我们需要输入参数 \(w_1\)\(w2\)\(b\),除此之外还有特征值 \(x_1\)\(x_2\)。因此 \(z\) 的计算公式为:
\[ z=w_1x_1+w_2x_2+b \]
回想一下逻辑回归的公式:
\[ \hat{y}=a=\sigma(z) \]
其中 \(z=w^Tx+b\), \(\sigma(z)=\frac{1}{1+e^{-z}}\)

损失函数:
\[ L(\hat{y}^{(i)},y^{(i)})=-y^{(i)}\log{\hat{y}^{(i)}-(1-y^{(i)})\log{(1-\hat{y}^{(i)})}} \]
代价函数:
\[ J(w,b)=\frac{1}{m} \sum^m_i{L(\hat{y}^{(i)},y^{(i)})} \]
现在先只考虑单个样本的情况,单个样本的代价函数(也就是损失函数)为:
\[ L(a,y)=-(y\log{a}+(1-y)\log{(1-a)} \]
其中,\(a\) 是逻辑回归的输出,\(y\) 是样本的标签值。

前面我们已经说了如何在单个训练样本上计算代价函数的前向步骤,现在我们来通过反向计算出导数。
\[ da=\frac{dL(a,y)}{da}=-\frac{y}{a}+\frac{1-y}{1-a}\\ dz=\frac{dL(a,y)}{dz}=\frac{dL}{dz}=(\frac{dL}{da})\cdot(\frac{da}{dz})=(-\frac{y}{a}+\frac{1-y}{1-a})\cdot(a\cdot(1-a))=a-y \]
而对于参数 \(w\)\(b\) 来说:
\[ dw_1=\frac{1}{m}\sum^m_i{x_1^{(i)}(a^{(i)}-y^{(i)})}\\ dw_2=\frac{1}{m}\sum^m_i{x_2^{(i)}(a^{(i)}-y^{(i)})}\\ db=\frac{1}{m}\sum^m_i{(a^{(i)}-y^{(i)})} \]
而对于我们刚刚说的单样本情况:
\[ dw_1=x_1 \cdot dz\\ d2_2=x_2 \cdot dz\\ db=dz \]
所以,总得来说,单样本的梯度下降算法更新一次步骤如下:

  • 计算 \(dz\)
  • 计算 \(dw_1,dw_2,db\)
  • 更新 \(w_1,w_2,b\)

扩展到 m 个样本的梯度下降是类似的。给相应的值,添加上标 \(^{(i)}\) 就行了。

伪代码如下图所示。

8b725e51dcffc53a5def49438b70d925

向量化 (Vectorization)

向量化是非常基础的去除代码中 for 循环的艺术。for 循环非常没有效率而且也没美观。

向量化后的公式如下:

前向传播
\[ Z = w^{T}X + b = np.dot( w.T,X)+b\\ A = \sigma( Z )\\ \]
后向传播
\[ dZ = A - Y\\ {{dw} = \frac{1}{m}*X*dz^{T}\ }\\ db= \frac{1}{m}*np.sum( dZ) \\ w: = w - \alpha*dw\\ b:=b-\alpha*db \]
当然,我们希望多次迭代进行梯度下降,仍然还会需要使用到 for 循环。

浅层神经网络 (Shallow Neural Network)

神经网络看起来是下图这个样子。我们可以把许多个 sigmoid 单元堆叠起来形成一个神经网络。

L1_week3_2

在这个神经网络对应的 3 个节点,首先计算第一层网络中的各个节点相关的数 \(z^{[1]}\),接着计算 \(a^{[1]}\)。同理再计算下一层的网络。注意这里,我们使用了上标 \(^{[m]}\) 表示第 m 层网络中节点相关的数,这些节点的集合被称为第 m 层网络。

整个计算过程,公式如下:
\[ \left. \begin{array}{r} {x }\\ {W^{[1]}}\\ {b^{[1]}} \end{array} \right\} \implies{z^{[1]}=W^{[1]}x+b^{[1]}} \implies{a^{[1]} = \sigma(z^{[1]})}\\ \left. \begin{array}{r} \text{$a^{[1]} = \sigma(z^{[1]})$}\\ \text{$W^{[2]}$}\\ \text{$b^{[2]}$}\\ \end{array} \right\} \implies{z^{[2]}=W^{[2]}a^{[1]}+b^{[2]}} \implies{a^{[2]} = \sigma(z^{[2]})}\\ \implies{{L}\left(a^{[2]},y \right)} \]
此时 \(a^{[2]}\) 就是整个神经网络最终的输出,用 \(\hat{y}\) 表示网络的输出。

与逻辑回归类似,神经网络我们也需要反向计算。
\[ \left. \begin{array}{r} {da^{[1]} = {d}\sigma(z^{[1]})}\\ {dW^{[2]}}\\ {db^{[2]}}\\ \end{array} \right\} \impliedby{{dz}^{[2]}={d}(W^{[2]}\alpha^{[1]}+b^{[2]}}) \impliedby{{{da}^{[2]}} = {d}\sigma(z^{[2]})}\\ \impliedby{{dL}\left(a^{[2]},y \right)} \]

神经网络的表示

以下图的神经网络例子来说明一下。

L1_week3_3

其中输入特征 \(x_1,x_2,x_3\),被称为神经网络的输入层 (input layer);接着第二层的四个节点,我们称之为隐藏层 (hide layer);最后只有一个结点构成的层被称为输出层 (output layer),它负责产生预测值。在论文里,也有人把这个神经网络称为一个两层的神经网络,因为输入层不算作一个标准的层。

引入符号标记

在这里,我们用符号 \(a^{[0]}\) 表示输入特征,从而替代了向量 \(x\)\(a\) 表示激活的意思,它以为着网络中不同层的值会传递到它们后面的层中,输入层将 \(x\) 传递给隐藏层,所以我们将输入层的激活值称为 \(a^{[0]}\)。同理,下一层即隐藏层也会产生激活值,我们记为 \(a^{[1]}\)。它们是向量,具体地说,隐藏层的第一个单元我们将表示为 \(a^{[1]}_1\),以此类推。
\[ a^{[1]} = \left[ \begin{array}{ccc} a^{[1]}_{1}\\ a^{[1]}_{2}\\ a^{[1]}_{3}\\ a^{[1]}_{4} \end{array} \right] \]
隐藏层以及最后的输出层是带有参数的,这里的隐藏层将有两个参数 \(W\)\(b\)。因为隐藏层算第一层,所以我们给它们加上上标 \(^{[1]}\),即 \((W^{[1]},b^{[1]})\)。在这个例子里,参数 \(W\) 是一个 \(4\times3\) 的矩阵,而参数 \(b\) 是一个 \(4\times1\) 的向量。其中,4 源自于隐藏层有 4 个节点(隐藏层单元),3 源自于输入层有 3 个输入特征。相似地,输出层也有参数 \(W^{[2]}\)\(b^{[2]}\),它们的维数分别是 \(1\times4\)\(1\times1\)

神经网络的计算

依旧以上面的两层的神经网络为例。我们从隐藏层的第一个神经元开始计算,与逻辑回归相似,这个神经元的计算同样也分为两步:

第一步,计算 \(z_1^{[1]}\)\(z_1^{[1]}=w_1^{[1]T}x+b_1^{[1]}\)

第二步,通过激活函数计算 \(a_1^{[1]}\)\(a_1^{[1]}=\sigma(z_1^{[1]})\)

隐藏层的余下几个神经元的计算过程一样,只是符号表示不同,最终分别可以得到 \(a_2^{[1]},a_3^{[1]},a_4^{[1]}\)

向量化计算

转换成向量化之后,公式如下
\[ z^{[n]} = w^{[n]}x + b^{[n]}\\ a^{[n]}=\sigma(z^{[n]}) \]
具体到第一层隐藏层的计算则如下
\[ \left[ \begin{array}{c} z^{[1]}_{1}\\ z^{[1]}_{2}\\ z^{[1]}_{3}\\ z^{[1]}_{4}\\ \end{array} \right] = \overbrace{ \left[ \begin{array}{c} ...W^{[1]T}_{1}...\\ ...W^{[1]T}_{2}...\\ ...W^{[1]T}_{3}...\\ ...W^{[1]T}_{4}... \end{array} \right] }^{W^{[1]}} * \overbrace{ \left[ \begin{array}{c} x_1\\ x_2\\ x_3\\ \end{array} \right] }^{input} + \overbrace{ \left[ \begin{array}{c} b^{[1]}_1\\ b^{[1]}_2\\ b^{[1]}_3\\ b^{[1]}_4\\ \end{array} \right] }^{b^{[1]}} \]
也就是对于我们简单的两层神经网络来说,只需要四个公式就能计算完
\[ z^{[1]}=W^{[1]}a^{[0]}+b^{[1]}\\ a^{[1]}=\sigma(z^{[1]})\\ z^{[2]}=W^{[2]}a^{[1]}+b^{[2]}\\ a^{[2]}=\sigma(z^{[2]}) \]
而对于 m 个样本来说,只需要对每个样本计算这个四个公式就行了,使用 for 循环就能实现。当然,可以向量化的话,我们还是要使用向量化。

按列把变量都拼成矩阵,类似如下
\[ x = \left[ \begin{array}{c} \vdots & \vdots & \vdots & \vdots\\ x^{(1)} & x^{(2)} & \cdots & x^{(m)}\\ \vdots & \vdots & \vdots & \vdots\\ \end{array} \right]\\ Z^{[1]} = \left[ \begin{array}{c} \vdots & \vdots & \vdots & \vdots\\ z^{[1](1)} & z^{[1](2)} & \cdots & z^{[1](m)}\\ \vdots & \vdots & \vdots & \vdots\\ \end{array} \right]\\ A^{[1]} = \left[ \begin{array}{c} \vdots & \vdots & \vdots & \vdots\\ \alpha^{[1](1)} & \alpha^{[1](2)} & \cdots & \alpha^{[1](m)}\\ \vdots & \vdots & \vdots & \vdots\\ \end{array} \right] \]
那么计算就可以变形为如下所示
\[ \left. \begin{array}{r} \text{$z^{[1](i)} = W^{[1](i)}x^{(i)} + b^{[1]}$}\\ \text{$\alpha^{[1](i)} = \sigma(z^{[1](i)})$}\\ \text{$z^{[2](i)} = W^{[2](i)}\alpha^{[1](i)} + b^{[2]}$}\\ \text{$\alpha^{[2](i)} = \sigma(z^{[2](i)})$}\\ \end{array} \right\} \implies \begin{cases} \text{$Z^{[1]} = W^{[1]}X+b^{[1]}$}\\ \text{$A^{[1]} = \sigma(z^{[1]})$}\\ \text{$Z^{[2]} = W^{[2]}A^{[1]} + b^{[2]}$}\\ \text{$A^{[2]} = \sigma(Z^{[2]})$}\\ \end{cases} \]
其中上标 \(^{(i)}\) 代表的是第 i 个样本。

激活函数 (Activation functions)

使用一个神经网络时,需要决定使用哪种激活函数用在隐藏层上,哪种用在输出节点上。之前,我们都一直在用 sigmoid 函数,但是,有时其他的激活函数效果会更好。

常用的激活函数

(1)sigmoid 激活函数

函数的定义为:$ f(x) = \frac{1}{1 + e^{-x}} $,其值域为 $ (0,1) $。

函数图像如下:

3-26

(2)tanh 激活函数

函数的定义为:$ f(x) = tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} $,值域为 $ (-1,1) $。

函数图像如下:

3-27

(3)Relu 激活函数

函数的定义为:$ f(x) = max(0, x) $ ,值域为 $ [0,+∞) $;

函数图像如下:

3-28

(4)Leak Relu 激活函数

函数定义为: $ f(x) = \left{
\begin{aligned}
ax, \quad x<0 \
x, \quad x>0
\end{aligned}
\right. $,值域为 $ (-∞,+∞) $。

图像如下($ a = 0.5 $):

3-29

选择激活函数的经验法则

如果输出是 0、1 值(二分类问题),则输出层选择 sigmoid 函数,然后其它的所有单元都选择 Relu 函数。

这是很多激活函数的默认选择,如果在隐藏层上不确定使用哪个激活函数,那么通常会使用 Relu 函数。

有时,也会使用 tanh 函数,但 Relu 的一个优点是:当 \(z\) 值为负时,导数等于 0。在 \(z\) 的区间变动很大的情况下,激活函数的导数或者激活函数的斜率都会远大于 0,在实践中,使用 Relu 激活函数神经网络通常会比使用 sigmoid 或者 tanh 激活函数学习的更快。而且,sigmoid 和 tanh 函数的导数在正负饱和区的梯度都会接近于0,这会造成梯度弥散,Relu 和 Leaky Relu 函数大于 0 部分都为常数,不会产生梯度弥散现象(但是,Relu 进入负半区的时候,梯度为 0,神经元此时不会训练,产生所谓的稀疏性,而 Leaky Relu 不会有这问题)。

所以,总得来说:

sigmoid 函数:除了输出层是一个二分类问题,基本不会用它;

tanh 函数:几乎适合所有场合;

Relu 函数:最常用的默认函数,如果不确定用哪个激活函数,就使用 Relu 或者 Leaky Relu。其中,Leaky Relu 的参数 \(a\) 一般设为 0.01。

激活函数的导数

对常见激活函数,导数计算如下:

原函数 函数表达式 导数 备注
Sigmoid 激活函数 \(f(x)=\frac{1}{1+e^{-x}}\) \(f^{'}(x)=\frac{1}{1+e^{-x}}\left( 1- \frac{1}{1+e^{-x}} \right)=f(x)(1-f(x))\) \(x=10\),或 \(x=-10\)\(f^{'}(x) \approx0\),当 \(x=0\)\(f^{'}(x) =0.25\)
Tanh 激活函数 \(f(x)=tanh(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}\) \(f^{'}(x)=-(tanh(x))^2\) \(x=10\),或 \(x=-10\)\(f^{'}(x) \approx0\),当 \(x=0\)\(f^{`}(x) =1\)
Relu 激活函数 \(f(x)=max(0,x)\) \(f^{'}(x)=\begin{cases} 0,x<0 \\ 1,x>0 \\ undefined,x=0\end{cases}\) 通常 \(x=0\) 时,给定其导数为 1 和 0
Leaky Relu 激活函数 \(f(x)=max(0.01x,x)\) \(f^{'}(x)=\begin{cases} 0.01,x<0 \\ 1,x>0 \\ undefined,x=0\end{cases}\) 通常 \(x=0\) 时,给定其导数为 1 和 0.01

随机初始化 (Random Initialization)

训练神经网络时,权重随机初始化是很重要的。对于逻辑回归,把权重初始化为 0 当然也是可以的,但是对于一个神经网络,如果把权重或者参数都初始化为 0,那么梯度下降将不会起作用。也就是说,我们把权重都初始化为 0,由于所有的隐含单元都是对称的,都会开始计算同一个函数,所以无论运行梯度下降多久,他们都一直计算同样的函数。

所以,我们需要进行随机初始化参数。具体做法为,把 \(W^{[1]}\) 设为 np.random.randn(4,4),这样生成了一个高斯分布的随机数矩阵,通常再乘上一个小的数,比如 0.01,这样把它初始化为很小的随机数。然后 \(b\) 没有这个对称的问题 (symmetry breaking problem),所以可以把 \(b\) 初始化为 0。类似地,\(W^{[2]}\)\(b^{[2]}\) 也进行这样的初始化。

对于为什么要将参数初始化为比较小的随机数,原因是,如果我们使用 tanh 或者 sigmoid 激活函数,如果数值波动太大,\(z\) 就会很大或者很小,这种时候就很可能停在 tanh 或者 sigmoid 函数的平坦的地方,这些地方梯度很小,也就意味着梯度下降会很慢,学习也就很慢。

其实有时有比 0.01 更好的常数,当我们训练一个只有一层隐藏层的网络时,设为 0.01 可能可以。但是当训练一个非常非常深的神经网络,可能就需要试试 0.01 以外的常数了。

深层神经网络 (Deep Neural Networks)

神经网络的层数是这么定义的:从左到右,由 0 开始定义。如下图所示。

有一个隐藏层的神经网络,就是一个两层神经网络。当我们算神经网络的层数时,我们不算输入层,我们只算隐藏层和输出层。

前向传播和反向传播 (Forward and Backward Propagation)

前向传播

输入 \(a^{[l-1]}\),输出是 \(a^{[l]}\),缓存为 \(z^{[l]}\);从实践中来看,我们还可以缓存下 \(w^{[l]}\)\(b^{[l]}\),这样更容易在不同的环节中调用函数。

那么,前向传播的步骤为
\[ z^{[l]}=W^{[l]}\cdot a^{[l-1]}+b^{[l]}\\ a^{[l]}=g^{[l]}(z^{[l]}) \]
向量化的版本为
\[ Z^{[l]}=W^{[l]}\cdot A^{[l-1]}+b^{[l]}\\ A^{[l]}=g^{[l]}(Z^{[l]}) \]
前向传播需要喂入 \({A}^{[0]}\) 也就是 \(X\),来初始化;初始化的是第一层的输入值。\({a}^{[0]}\) 对应于一个训练样本的输入特征,而 \({{A}^{[0]}}\) 对应于一整个训练样本的输入特征,所以这就是这条链的第一个前向函数的输入,重复这个步骤就可以从左到右计算前向传播。

反向传播

输入为 \({{da}^{[l]}}\),输出为 \({{da}^{[l-1]}},{{dw}^{[l]}}, {{db}^{[l]}}\)

所以反向传播的步骤可以写成
\[ d{{z}^{[l]}}=d{{a}^{[l]}}*{{g}^{[l]}}'( {{z}^{[l]}})\\ d{{w}^{[l]}}=d{{z}^{[l]}}\cdot{{a}^{[l-1]}}\\ d{{b}^{[l]}}=d{{z}^{[l]}}\\ d{{a}^{[l-1]}}={{w}^{\left[ l \right]T}}\cdot {{dz}^{[l]}}\\ d{{z}^{[l]}}={{w}^{[l+1]T}}d{{z}^{[l+1]}}\cdot \text{ }{{g}^{[l]}}'( {{z}^{[l]}}) \]
向量化的版本为
\[ d{{Z}^{[l]}}=d{{A}^{[l]}}*{{g}^{\left[ l \right]}}'\left({{Z}^{[l]}} \right)\\ d{{W}^{[l]}}=\frac{1}{m}\text{}d{{Z}^{[l]}}\cdot {{A}^{\left[ l-1 \right]T}}\\ d{{b}^{[l]}}=\frac{1}{m}\text{ }np.sum(d{{z}^{[l]}},axis=1,keepdims=True)\\ d{{A}^{[l-1]}}={{W}^{\left[ l \right]T}}.d{{Z}^{[l]}} \]

核对矩阵的维数

当实现深度神经网络的时候,可以拿一张纸过一遍算法中矩阵的维数。

\(w\) 的维度是 (下一层的维数,前一层的维数),即 \(w^{[l]}:(n^{[l]},n^{[l-1]})\)

\(b\) 的维度是 (下一层的维数,1),即 \(b^{[l]}:(n^{[l]},1)\)

类似地,\(z^{[l]},a^{[l]}:(n^{[l]},1)\)

\({{dw}^{[l]}}\)\({{w}^{[l]}}\) 维度相同,\({{db}^{[l]}}\)\({{b}^{[l]}}\) 维度相同,且 \(w\)\(b\) 向量化维度不变,但 \(z\), \(a\) 以及 \(x\) 的维度会向量化后发生变化。

向量化后:

\({Z}^{[l]}\) 可以看成由每一个单独的 \({z}^{[l]}\) 叠加而得到,\({Z}^{[l]}=({{z}^{[l][1]}},{{z}^{[l][2]}},{{z}^{[l][3]}},…,{{z}^{[l][m]}})\)

\(m\) 为训练集大小,所以 \({Z}^{[l]}\) 的维度不再是 \(({{n}^{[l]}},1)\),而是 \(({{n}^{[l]}},m)\)

\({A}^{[l]}\)\(({n}^{[l]},m)\)\({A}^{[0]} = X =({n}^{[l]},m)\)

参数与超参数 (Parameters vs Hyperparameters)

什么是超参数?

比如算法中的learning rate \(\alpha\)(学习率)、iterations(梯度下降法循环的数量)、\(L\)(隐藏层数目)、\({{n}^{[l]}}\)(隐藏层单元数目)、choice of activation function(激活函数的选择)都需要你来设置,这些数字实际上控制了最后的参数 \(W\)\(b\) 的值,所以它们被称作超参数。

如何寻找超参数的最优值?

Idea—Code—Experiment—Idea 这个循环,尝试各种不同的参数,实现模型并观察是否成功,然后再迭代。

References

[1] Coursera深度学习教程中文笔记

[2] 深度学习 500 问

猜你喜欢

转载自www.cnblogs.com/IvyWong/p/11978146.html