DeepLearning.ai笔记(二)

《神经网络和深度学习》第三周《浅层神经网络》。
本节的题目是浅层神经网络,开始接触多层结构,此时才真正接触到了神经网络模型。其实运用到的理论知识和上一节的逻辑回归相比,几乎没有新东西。

为了便于描述,我把前一节运算出来的a称为一个神经元(这是我的直观理解,并无出处~),该神经元与输入数据直接关联,并直接输出为二分结果。真实世界人脑的思考过程通常没有这么简单,有很多与之相关的成语比如“深思熟虑”、“三思而行”、“深谋远虑”……都是指有深度、反复的思考。本节课程正是通过增加神经元的层次、以及每一层的个数来增强思考的深度和广度。

3.1 神经网络概览

神经网络通常是具备两层以上的结构,但在原理上,仅是上一节内容的应用,并没有增加新的理论知识。在符号表示上使用方括号上标$z^{[1]}、a^{[1]}$来表示第几层,这与上一节的$x^{(i)}$是不同的,后者表示第i个样本。

在概念上,中间层的每一个节点都是应用了上一节的算法计算出来的,对于单样本逻辑回归,模型如下:

由此根据正向算法那计算神经元和成本函数:

神经网络则在逻辑回归的基础上增加了中间的z/a层,以及每一层的多个节点:

其中每个节点对上一层节点均使用了逻辑回归的正向算法,最后通过梯度下降法计算成本函数$L(a^{[2]}, y)$:

3.2 神经网络表示

这是一个最简单的神经网络,它具有双层结构,这两层指的是隐藏层和输出层:

输入层通常不作为一层,因为$a^{[1]}$是通过输入层X与构造出的参数$W^{[1]}和b^{[1]}$叠加运算而成,而$a^{[2]}$是$a^{[1]}$与构造出的参数$W^{[2]}和b^{[2]}$叠加运算而成。W和b构成了模型的本质,它们只有两层,因此说这是一个双层的神经网络模型。
对于单样本,假设输入层共$n^{[0]}$个特征,隐藏层共$n^{[1]}$个节点,输出层共$n^{[2]}$个节点,显然$n^{[2]}$=1,则

$X= \left\lgroup \matrix{x_{1} \cr x_{2} \cr … \cr x_{n_{[0]}} } \right \rgroup \
z_{1}^{[1]} = w_{1}^{[1]}·X + b^{[1]} = [w_{1}, w_{2}, …, w_{n^{[0]}}] · \left\lgroup \matrix{x_{1} \cr x_{2} \cr … \cr x_{n^{[0]}} } \right \rgroup + b^{[1]} \in \mathbb{R}$

隐藏层共有$n^{[1]}$个节点,于是:

$z^{[1]}=\left\lgroup \matrix{z_{1}^{[1]} \cr z_{2}^{[1]} \cr … \cr z_{n^{[1]}}^{[1]} } \right \rgroup \in \mathbb{R}^{n^{[1]}\,× 1}   \; w^{[1]}=\begin{bmatrix}—w_{1}—\\—w_{2}—\\\vdots\\—w_{n^{[1]}}—\end{bmatrix} \in \mathbb{R}^{n^{[1]}\,×n^{[0]}}$

3.3 计算神经网络的输出

计算神经网络的输出就是应用神经元的计算法则,逐个节点求解:

$z^{[1]}=\left\lgroup \matrix{z_{1}^{[1]} \cr z_{2}^{[1]} \cr … \cr x_{n_{[1]}}^{[1]} } \right \rgroup=\left\lgroup \matrix{w_{1}^{[1]}·X + b_{1}^{[1]} \cr w_{2}^{[1]}·X + b_{2}^{[1]} \cr … \cr w_{n^{[1]}}^{[1]}·X + b_{n^{[1]}}^{[1]} } \right \rgroup=\begin{bmatrix}—w_{1}^{[1]}— \cr —w_{2}^{[1]}— \cr … \cr —w_{n^{[1]}}^{[1]}— \end{bmatrix}·X + \begin{bmatrix}b_{1}^{[1]} \cr b_{2}^{[1]} \cr … \cr b_{n^{[1]}}^{[1]} \end{bmatrix}$

$a^{[1]} = \begin{bmatrix}a_{1}^{[1]} \cr a_{2}^{[1]} \cr … \cr a_{n^{[1]}}^{[1]} \end{bmatrix}=σ(z^{[1]})$

同理总结如下:

$z^{[1]}=w^{[1]}·x + b^{[1]} \ a^{[1]} = σ(z^{[1]}) \ z^{[2]}=w^{[2]}·a^{[1]} + b^{[2]} \ a^{[2]} = σ(z^{[2]})$

3.4 多样本的向量化

当有m个样本时,需要遍历每一个样本,当然实际操作是使用向量化而不是循环,再利用前面的方法求解ŷ。循环的伪码形式和向量化的表达分别为:

$for \, i \, in \, range(1, m):$
 $z^{[1]{(}i{)}} = w^{[1]}· x^{(i)} + b^{[1]}$    【向量化】 $Z^{[1]} = W^{[1]}·X + b^{[1]}$
 $a^{[1]{(}i{)}} = σ(z^{[1]{(}i{)}})        【向量化】A^{[1]} = σ(Z^{[1]})$
 $z^{[2]{(}i{)}} = w^{[2]}·a^{[1]{(}i{)}} + b^{[2]}   【向量化】Z^{[2]}=W^{[2]}·A^{[1]}+b^{[2]}$
 $a^{[2]{(}i{)}}=σ(z^{[2]{(}i{)}})       【向量化】A^{[2]}=σ(Z^{[2]})$

于是得出$Z^{[1]}=\begin{bmatrix}\mid&\mid&&\mid\\Z^{[1]{(}1{)}}&Z^{[1] {(}2{)}} & \cdots &Z^{[1]{(}m{)}}\\\mid&\mid&&\mid\end{bmatrix}$,横向表示不同样本的处的神经元,个数等于样本数;纵向表示不同特征的权重,个数等于上一层的节点数。

3.5 向量化实现的解释

$Z^{[1]}=\begin{bmatrix}\mid&\mid&&\mid\\Z^{[1]{(}1{)}}&Z^{[1]{(}2{)}}&\cdots&Z^{[1]{(}m{)}}\\\mid&\mid&&\mid\end{bmatrix}$
$=\begin{bmatrix}w_{1}^{[1]}·x^{(1)}+b^{[1]} & w_{1}^{[1]}·x^{(2)}+b^{[1]} & … & w_{1}^{[1]}·x^{(m)}+b^{[1]} \cr w_{2}^{[1]}·x^{(1)}+b^{[1]} & w_{2}^{[1]}·x^{(2)}+b^{[1]} & … & w_{2}^{[1]}·x^{(m)}+b^{[1]} \cr … & … & … & … \cr w_{1}^{[1]}·x^{(1)}+b^{[1]} & w_{1}^{[1]}·x^{(2)}+b^{[1]} & … & w_{1}^{[1]}·x^{(m)}+b^{[1]} \end{bmatrix} \\
=\begin{bmatrix}—w_{1}^{[1]}— \cr —w_{2}^{[1]}— \cr … \cr —w_{n^{[1]}}^{[1]}— \end{bmatrix}·\begin{bmatrix}\mid&\mid&&\mid\\x^{(1)}&x^{(2)}&\cdots&x^{(m)}\ \mid&\mid&&\mid\end{bmatrix} + b^{[1]} \\
=W^{[1]}·X+b^{[1]}$

3.6 激活函数

在以前的章节中,都采用σ函数作为激活函数,在实际应用中通常使用它的变种$tanh(z)=\frac{e^{z}-e^{z}}{e^{z}+e^{z}}$来作为激活函数,它是将σ函数向下平移0.5:

我们用g(z)来表示激活函数。

还有一个更常用的激活函数是ReLU(修正线性单元 Rectified linear unit),g(z)=max(0, z):

以及它的变种Leaky ReLU,gz(z)=max(0.01z, z):

tanh和σ函数有一个共同的缺陷是当z取值过大时,g的变化率过小,这会导致每轮训练效果不明显,学习速度很慢。ReLU系函数则没有这样的缺点。一般情况下,当不确定隐藏层的激活函数该用什么时,通常就采用ReLU。

尽管对于ReLU当z<0时斜率为0,但在实践中有足够多的隐藏单元令z>0,所以通常使用ReLU就足够了。

3.7 为什么要用非线性激活函数

$z^{[1]}=w^{[1]}·x + b^{[1]} \ a^{[1]} = g^{[1]}(z^{[1]}) \ z^{[2]}=w^{[2]}·a^{[1]} + b^{[2]} \ a^{[2]} = g^{[1]}(z^{[2]})$

$如果a^{[1]}=z^{[1]}\ 则z^{[2]}=w^{[2]}(w^{[1]}·x+b^{[1]}) + b^{[2]}=w’·x+b’$

这和单层网络是等效的,也就是说它会令神经网络的层次失效,因此激活函数需要使用非线性函数。

3.8 激活函数的导数

对于tanh函数

$g(z) = tanh(z) = \frac{e^{z} - e^{-z}}{e^{z} + e^{-z}}$

$g’(z) = (e^{z} - e^{-z})’·(e^{z} + e^{-z}) + (e^{z} - e^{-z})[(e^{z} + e^{-z})]’\\=\frac{(e^{z} - e^{-z})}{(e^{z} + e^{-z})} + (e^{z} - e^{-z})(-1)(e^{z} + e^{-z})^{-2}(e^{z} - e^{-z})\\=1-(\frac{(e^{z} - e^{-z})}{(e^{z} + e^{-z})})^{2}\\=1-tanh^{2}(z)$

此处应用的链式法则是:$[g(x)·f(x)]’=g’(x)·f(x)+g(x)·f’(x)$

对于ReLU

$g(z)=max(0, z)\\g’(z)=\begin{cases}0 z<0\\1 z≥0\end{cases}$

$g(z)=max(0.01z, z)\\g’(z)=\begin{cases}0.01 z<0\\1   z≥0\end{cases}$

当z=0时,两个函数均不可导,但在实际应用中z取0的概率非常小,此时导数取什么值,问题都不大,因此可以将此时导数定义为1。

3.9 神经网络的梯度下降法

对于成本函数$J(w^{[1]},b^{[1]},w^{[2]},b^{[2]})=\frac{1}{m}\sum_{i=1}^{m}L(ŷ, y)$,其梯度下降法可写作:

$Repeat\{
\\ dw^{[1]}=\frac{dL}{dw^{[1]}}, \; db^{[1]}=\frac{dL}{db^{[1]}}
\\ dw^{[2]}=\frac{dL}{dw^{[2]}}, \; db^{[2]}=\frac{dL}{db^{[2]}}
\\ w^{[1]}:=w^{[1]} - α·dw^{[1]}, \; b^{[1]}:=b^{[1]} - α·db^{[1]}
\\ w^{[2]}:=w^{[2]} - α·dw^{[2]}, \; b^{[2]}:=b^{[2]} - α·db^{[2]}
\\\}$

使用前向算法:

$Z^{[1]}=W^{[1]}·X + b^{[1]}\\
A^{[1]}=g^{[1]}(Z^{[1]})\\
Z^{[2]}=W^{[2]}·A^{[1]} + b^{[2]}\\
A^{[2]}=g^{[2]}(Z^{[2]})$

使用后向算法:

$dZ^{[2]}=A^{[2]}-Y,其中Y=[y^{(1)} y^{(1)} … y^{(1)}]\\
dW^{[2]}=\frac{1}{m}dZ^{[2]}·A^{[1]}\\
db^{[2]}=\frac{1}{m}np.sum(dZ^{[2]}, axis=1, keepdims=True)\\
dZ^{[1]}=W^{[2]}·dZ^{[2]}·g^{‘[1]}(z^{[1]})  ①\\
dW^{[1]}=\frac{1}{m}dZ^{[1]}X\\
db^{[1]}=\frac{1}{m}np.sum(dZ^{[1]}, axis=1, keepdims=True)$

其中①的计算思路,根据链式规则:

$dZ^{[1]}=\frac{dL}{dz^{[1]}}=\frac{dL}{da^{[2]}}·\frac{da^{[2]}}{dz^{[2]}}·\frac{dZ^{[2]}}{da^{[1]}}·\frac{da^{[1]}}{dz^{[1]}} ……前两项=dZ^{[2]},第三项=W^{[2]}\\
=dZ^{[2]}·W^{[2]}·g^{‘[1]}(z^{[1]})$

3.11 随机初始化

在设定初始化参数$w^{[1]}和b^{[1]}$时,应该注意不要把$w_{1}^{[1]}、w_{2}^{[1]}…、w_{n^{[1]}}^{[1]}$设成相等的值,因为这会导致该层神经元完全一致,这应该就是所谓的“一根筋”吧~ 其结果就是在该层设置的多个节点就失效了。这和人脑的思考也很相似,要做到“兼听则明”,多角度考虑。正确的做法是:
$W^{[1]}=np.random.rand((n^{[1]}, n^{[0]}))×0.01\\
b^{[1]}=np.zeros((n^{[1]}, 1))\\
W^{[2]}=np.random.rand((1, n^{[1]}))×0.01\\
b^{[2]}=np.zeros((n^{[1]}, 1))$

其中$W^{[1]}和W{[2]}$乘以0.01的系数是为了确保取值尽量靠近0点,是的当激活函数取σ或tanh时,有较大的学习率,从而得到较快的学习速度。

作业

本节的作业是随机生成400个点,一半是红色一半是蓝色,要求训练一个模型可以把两类点分开:

下面是采用逻辑回归计算的结果:

需要特别关注的是这个图形的绘制——它的背景是通过绘制带填充色的等高线图来实现的,它把原先的红蓝豆豆图按照步长为0.01绘制纵横网格,然后把网格每一个交叉点作为测试数据输入给模型,得到每个点的预测颜色,然后根据这些点绘制带填充色的等高线图,就是这个效果了。

接下来使用隐藏层有4个节点的双层神经网络,计算结果如下:

注意:网上的版本有个bug:load_planar_dataset()函数返回$X\in (2, 400), Y\in(1, 400)$,而在绘制这些点的时候
plt.scatter(X[0, :], X[1, :], c=y, cmap=plt.cm.Spectral)
其中的颜色参数c直接传入Y是有问题的,需要做如下转换:
plt.scatter(X[0, :], X[1, :], c=y.reshape(X.shape[1]), cmap=plt.cm.Spectral)

我开始体会到了使用神经网络是一种编程思维的切换。

本节的作业在https://github.com/palanceli/MachineLearningSample/blob/master/DeepLearningAIHomeWorks/mywork.py中的class Coding1_2

思考

神经网络一方面增加了神经元的层次,提高思考的深度,另一方面也在同一层次上布局了多个神经元,提高了思考的广度。这些神经元彼此连接便成了“神经网络”。再次申明,这都是我的直观理解,并没有理论依据。这么做增加了思考的抽象程度,但是也更加让思考过程变得越发不可理解。为什么经过这么多层的运算就比简单“神经元”有效?每一层的含义是什么?神经网络的深度、每一层的神经元个数与模型的效果是什么关系?我不知道!继续学习吧。