自学Andrew Ng老师的神经网络公开课,作一些笔记,老师讲的非常详细,通俗易懂
这节讲的内容非常简单,就是对浅层神经网络的扩展,即之前第三章讲解的是2层神经网络,而logistics回归可以认为是1层神经网络。深层神经网络就是隐藏层是多层的神经网络,比如含有四层隐藏层的神经网络是五层神经网络。
本节介绍了深层神经网络中用到的标记,与浅层神经网络一致。
L L L表示一共的层数;
n [ l ] n^{[l]} n[l]表示第 l l l层的神经元数量, n [ 0 ] = n x n^{[0]}=n_x n[0]=nx认为是输入层的数量, n [ L ] = 1 n^{[L]}=1 n[L]=1表示输出层的数量,一般为1个神经元;
a [ l ] a^{[l]} a[l]表示第 l l l层的激活函数, a [ l ] = g [ l ] ( z [ l ] ) a^{[l]} = g^{[l]}(z^{[l]}) a[l]=g[l](z[l]), a [ 0 ] = x a^{[0]}=x a[0]=x表示输入状态;
w [ l ] w^{[l]} w[l]和 b [ l ] b^{[l]} b[l]表示第 l l l层的参数;
这节讲了深度神经网络中的前向传播算法,其实和浅层神经网络是一样的,对于一个4层神经网络来说,对于一个训练样本来说,主要公式有:
z [ 1 ] = w [ 1 ] x + b [ 1 ] z^{[1]}=w^{[1]}x+b^{[1]} z[1]=w[1]x+b[1]
a [ 1 ] = g [ 1 ] ( z [ 1 ] ) a^{[1]}=g^{[1]}(z^{[1]}) a[1]=g[1](z[1])
z [ 2 ] = w [ 2 ] a [ 1 ] + b [ 2 ] z^{[2]}=w^{[2]}a^{[1]}+b^{[2]} z[2]=w[2]a[1]+b[2]
a [ 2 ] = g [ 2 ] ( z [ 2 ] ) a^{[2]}=g^{[2]}(z^{[2]}) a[2]=g[2](z[2])
z [ 3 ] = w [ 3 ] a [ 2 ] + b [ 3 ] z^{[3]}=w^{[3]}a^{[2]}+b^{[3]} z[3]=w[3]a[2]+b[3]
a [ 3 ] = g [ 3 ] ( z [ 3 ] ) a^{[3]}=g^{[3]}(z^{[3]}) a[3]=g[3](z[3])
z [ 4 ] = w [ 4 ] a [ 3 ] + b [ 4 ] z^{[4]}=w^{[4]}a^{[3]}+b^{[4]} z[4]=w[4]a[3]+b[4]
a [ 4 ] = g [ 4 ] ( z [ 4 ] ) = y ^ a^{[4]}=g^{[4]}(z^{[4]})=\hat y a[4]=g[4](z[4])=y^
其中第一层的 x x x可以换成 a [ 0 ] a^{[0]} a[0],之后的写法会写作后者。
对于所有样本的向量化,公式是一个样本中,将 z z z换成 Z Z Z,将 a a a换成 A A A,向量化其实就是将每个样本按列排列起来。
这里多层虽然公式一样,但无法用向量化的方法来替代,所以需要写一个循环从1到 L L L来计算整个神经网络:
Z [ l ] = W [ l ] A [ l − 1 ] + B [ l ] Z^{[l]}=W^{[l]}A^{[l-1]}+B^{[l]} Z[l]=W[l]A[l−1]+B[l]
A [ l ] = g [ l ] ( Z [ l ] ) A^{[l]}=g^{[l]}(Z^{[l]}) A[l]=g[l](Z[l])
最后老师提及,在写代码时,反复检查各个矩阵的维度,有利于调试问题,以及避免问题。
通过检查各个矩阵的维度,有利于调试问题。
本节讲解了为什么每个矩阵是固定的维度,这里简单罗列各个矩阵的维度。
w [ l ] : ( n [ l ] , n [ l − 1 ] ) w^{[l]} : (n^{[l]}, n^{[l-1]}) w[l]:(n[l],n[l−1])
b [ l ] : ( n [ l ] , 1 ) b^{[l]} : (n^{[l]},1) b[l]:(n[l],1)
d w [ l ] : ( n [ l ] , n [ l − 1 ] ) dw^{[l]}:(n^{[l]}, n^{[l-1]}) dw[l]:(n[l],n[l−1])
d b [ l ] : ( n [ l ] , 1 ) db^{[l]} : (n^{[l]}, 1) db[l]:(n[l],1)
z [ l ] : ( n [ l ] , 1 ) z^{[l]} : (n^{[l]}, 1) z[l]:(n[l],1)
a [ l ] : ( n [ l ] , 1 ) a^{[l]} : (n^{[l]}, 1) a[l]:(n[l],1)
特别的, l = 0 l=0 l=0时
a [ 0 ] = x : ( n [ 0 ] , 1 ) a^{[0]}=x : (n^{[0]}, 1) a[0]=x:(n[0],1)
然后,当向量化之后,各个矩阵的维度是。
W [ l ] : ( n [ l ] , n [ l − 1 ] ) W^{[l]}:(n^{[l]}, n^{[l-1]}) W[l]:(n[l],n[l−1])
B [ l ] : ( n [ l ] , 1 ) B^{[l]} : (n^{[l]}, 1) B[l]:(n[l],1)
d W [ l ] : ( n [ l ] , n [ l − 1 ] ) dW^{[l]} : (n^{[l]}, n^{[l-1]}) dW[l]:(n[l],n[l−1])
d B [ l ] : ( n [ l ] , 1 ) dB^{[l]}:(n^{[l]},1) dB[l]:(n[l],1)
虽然这里 B [ l ] B^{[l]} B[l]和 d B [ l ] dB^{[l]} dB[l]是向量,维度是 ( n [ l ] , 1 ) (n^{[l]},1) (n[l],1),但在Python中,通过广播的方式,可以扩展他们为 ( n [ l ] , m ) (n^{[l]}, m) (n[l],m)维度矩阵。
Z [ l ] : ( n [ l ] , m ) Z^{[l]}:(n^{[l]}, m) Z[l]:(n[l],m)
A [ l ] : ( n [ l ] , m ) A^{[l]}:(n^{[l]}, m) A[l]:(n[l],m)
d Z [ l ] : ( n [ l ] , m ) dZ^{[l]}:(n^{[l]}, m) dZ[l]:(n[l],m)
d A [ l ] : ( n [ l ] , m ) dA^{[l]}:(n^{[l]}, m) dA[l]:(n[l],m)
这些维度是通过解析传播函数而得出的。
这一节解释了为什么要使用深度神经网络,而不是去扩展浅层神经网络中隐藏层单元的数量。
从深度神经网络能表示的意义来看,深度神经网络中的每一层的作用并不完全相同,通常较靠前的神经网络隐藏层其能处理的信息通常是低层次的,比如一副图片中的像素边缘,或者音频中的声波变化,然后在逐渐向后的隐藏层则开始处理高层次的信息,比如人脸的部位,耳朵,眼睛类,或者音频中的音位,单词等,再向后扩展的隐藏层,则能够处理更加复杂的信息,比如不同的人脸,或者不同的发音句子,最终实现人脸识别或者语音识别的功能。
另外,从深度神经网络在数学上的意义,老师举了电路网络的例子,当处理同样一个模型时,用深度神经网络要相比浅层神经网络的算法复杂度更低,通常使用浅层神经网络,随着输入特征数量的增加,隐藏层单元的数量是呈现指数级增长的。
深度神经网络最初称为多隐层神经网络,通常做一个神经网络模型设计时,还是应该先从logistics回归试起,然后逐渐增加神经网络隐层的数量,并将隐藏层层数作为一个超参数来寻找最佳的神经网络层数。
讲解了从实现角度的神经网络模块化的分析,对于第 l l l层来说,它包含一个正向传播模块和一个反向传播模块,正向传播模块的输入是 a [ l ] a^{[l]} a[l],输出是 a [ l + 1 ] a^{[l+1]} a[l+1],同时还需要缓存一个 z [ l ] z^{[l]} z[l]将用在反向传播模块中,另外该模块中需要用到 w [ l ] w^{[l]} w[l]和 b [ l ] b^{[l]} b[l];在反向传播模块中,输入是 d a [ l ] da^{[l]} da[l],输出是 d a [ l − 1 ] da^{[l-1]} da[l−1],同时 z [ l ] z^{[l]} z[l]作为从正向传播模块的输入,模块产出是 d w [ l ] dw^{[l]} dw[l]和 d b [ l ] db^{[l]} db[l],参与反向传播模块的参数是 w [ l ] w^{[l]} w[l]和 b [ l ] b^{[l]} b[l],另外会在该模块中计算 d z [ l ] dz^{[l]} dz[l]。
将所有层的正向传播模块连接,反向传播模块连接,就形成了整个算法的结构。
通过公式
w : = w − α d w w := w-\alpha dw w:=w−αdw
b : = b − α d b b := b - \alpha db b:=b−αdb
可以更新两个参数。
本节基本上是对之前讲解的深度神经网络的公式进行总结,我们简单罗列一下公式。
前向传播算法中的第 l l l层的模型是:
I n p u t : a [ l − 1 ] Input: a^{[l-1]} Input:a[l−1]
O u t p u t : a [ l ] , c a c h e ( z [ l ] ) Output: a^{[l]}, cache(z^{[l]}) Output:a[l],cache(z[l])
对应的公式为:
z [ l ] = w [ l ] a [ l − 1 ] + b [ l ] z^{[l]}=w^{[l]}a^{[l-1]}+b^{[l]} z[l]=w[l]a[l−1]+b[l]
a [ l ] = g [ l ] ( z [ l ] ) a^{[l]}=g^{[l]}(z^{[l]}) a[l]=g[l](z[l])
Z [ l ] = W [ l ] A [ l − 1 ] + B [ l ] Z^{[l]}=W^{[l]}A^{[l-1]}+B^{[l]} Z[l]=W[l]A[l−1]+B[l]
A [ l ] = g [ l ] ( Z [ l ] ) A^{[l]}=g^{[l]}(Z^{[l]}) A[l]=g[l](Z[l])
后两个公式是向量化的实现, B [ l ] B^{[l]} B[l]写作 b [ l ] b^{[l]} b[l]也是对的,都是一个向量,是通过Python的广播在计算时将向量扩展成矩阵的。
反向传播算法中的第 l l l层的模型是:
I n p u t : d a [ l ] Input: da^{[l]} Input:da[l]
O u t p u t : d a [ l − 1 ] , d W [ l ] , d b [ l ] Output: da^{[l-1]}, dW^{[l]}, db^{[l]} Output:da[l−1],dW[l],db[l]
对应的公式是:
d z [ l ] = d a [ l ] ∗ g [ l ] ′ ( z [ l ] ) dz^{[l]}=da^{[l]}*g^{[l]'}(z^{[l]}) dz[l]=da[l]∗g[l]′(z[l])
d w [ l ] = d z [ l ] a [ l − 1 ] dw^{[l]}=dz^{[l]}a^{[l-1]} dw[l]=dz[l]a[l−1]
d b [ l ] = d z [ l ] db^{[l]}=dz^{[l]} db[l]=dz[l]
d a [ l − 1 ] = w [ l ] . T d z [ l ] da^{[l-1]}=w^{[l].T}dz^{[l]} da[l−1]=w[l].Tdz[l]
推导得
d z [ l ] = w [ l + 1 ] . T d z [ l + 1 ] ∗ g [ l ] ′ ( z [ l ] ) dz^{[l]}=w^{[l+1].T}dz^{[l+1]}*g^{[l]'}(z^{[l]}) dz[l]=w[l+1].Tdz[l+1]∗g[l]′(z[l])
向量化的实现是:
d Z [ l ] = d A [ l ] ∗ g [ l ] ′ ( Z [ l ] ) dZ^{[l]}=dA^{[l]}*g^{[l]'}(Z^{[l]}) dZ[l]=dA[l]∗g[l]′(Z[l])
d W [ l ] = 1 m d Z [ l ] A [ l − 1 ] . T dW^{[l]}=\frac{1}{m}dZ^{[l]}A^{[l-1].T} dW[l]=m1dZ[l]A[l−1].T
d B [ l ] = 1 m n p . s u m ( d Z [ l ] , a x i s = 1 , k e e p d i m s = T r u e ) dB^{[l]}=\frac{1}{m}np.sum(dZ^{[l]}, axis=1, keepdims=True) dB[l]=m1np.sum(dZ[l],axis=1,keepdims=True)
d A [ l − 1 ] = W [ l ] . T d Z [ l ] dA^{[l-1]}=W^{[l].T}dZ^{[l]} dA[l−1]=W[l].TdZ[l]
最后小结一下,正向传播算法就是调用这些公式,从第一层的输入 a [ 0 ] = x a^{[0]}=x a[0]=x开始,一层层计算最后得出 a [ L ] = y ^ a^{[L]}=\hat y a[L]=y^ 结束,然后通过损失函数计算 L ( a [ L ] , y ) = L ( y ^ , y ) L(a^{[L]}, y) = L(\hat y, y) L(a[L],y)=L(y^,y) ,然后对损失函数求导,比如对于logistics回归,求导的结果是
d a [ L ] = − y a + 1 − y 1 − a da^{[L]}=-\frac{y}{a}+\frac{1-y}{1-a} da[L]=−ay+1−a1−y
其中的 a = y ^ a=\hat y a=y^ 。
然后将这个结果 d a [ L ] da^{[L]} da[L] 输入到反向传播算法中,一层层计算,得出每一层的 d w [ l ] dw^{[l]} dw[l]和 d b [ l ] db^{[l]} db[l],进而通过学习公式更新
w [ l ] : = w [ l ] + α d w [ l ] w^{[l]} := w^{[l]}+\alpha dw^{[l]} w[l]:=w[l]+αdw[l]
b [ l ] : = b [ l ] + α d b [ l ] b^{[l]} := b^{[l]}+ \alpha db^{[l]} b[l]:=b[l]+αdb[l]
上边几个公式忽略了向量化实现,向量化也同理,可以把所以训练样本一次性计算完成,提高算法效率。
以上的思想是深度神经网络计算的基石,只要通过程序实现这些公式,就可以训练出来一个完整的神经网络。
老师建议,通过写一些代码,可以熟练的掌握以上这些公式。并且提出重点的是神经网络的重点并不是算法实现,而是数据。
什么是超参数(hyperparameters)?
我们神经网络模型中训练的参数: W [ 1 ] W^{[1]} W[1], B [ 1 ] B^{[1]} B[1], W [ 2 ] W^{[2]} W[2], B [ 1 ] B^{[1]} B[1]等,这些都是参数;
超参数是如学习率 α \alpha α ,迭代次数, 隐藏层数 L L L,隐藏层的单元数 n [ 1 ] n^{[1]} n[1], n [ 2 ] n^{[2]} n[2]等,以及可以选择的激活函数,都属于超参数。另外,之后深度神经网络还会涉及到更多的超参数,这些超参数的特点是,他们的配置会影响到神经网络中的参数。
神经网络的训练时一种经验过程,需要从一开始猜测一组超参数,然后通过编码实现、数据实验来检查最终的效果,然后根据效果不断修改超参数,这个过程是闭环的,通过一遍遍的更新超参数,最后找到最佳的超参数,训练出来最合适的神经网络。
对于一些特定领域,可能会已经有了一些经验参数,我们可以去参考这些经验参数,然而,这些参数往往会随着时间而发生变化,进而变得不那么可靠,比如随着时间,CPU、GPU的性能提升,或者数据集的变化等等,会导致参数发生变化。而神经网络的训练过程,就是持续性的改进。
老师的观点是,神经网络与我们的生物神经元并没有太大关系,虽然说神经网络在早期发展时受到了生物神经元的启发,而且从简化的结构上可以看出神经元与一个logistics回归的单元有相似性,但是到现在为止,人类神经科学家也很难解释大脑中神经元的工作方式,单个神经元的激活方式,它们如何训练形成网络,是通过神经网络的反向传播、梯度下降算法吗?
随着神经网络的不断发展,它确实可以作为能够估计从输入 x x x到输出 y y y之间映射关系的方法,然而它与生物神经元的关系却并不大。老师现在也在尽少的去使用这个说法。