本文总结的是课程一《神经网络和深度学习》的第三周《浅层神经网络》,共11小节,本文涵盖其中的10小节。视频请访问deeplearning.ai或者网易云课堂。
3.1 概览
引入上标方括号表示网络层,圆括号依旧表示训练样本。如下图是两层网络构成的神经网络,和逻辑回归不同的是,这里需要反复计算a和z的值。此外,图中红色的箭头表示反向传播梯度的计算。
3.2 神经网络表示
如下图是一个双层的神经网络,x1、x2和x3是输入层,包含神经网络的输入。中间一层有4个结点,叫隐藏层。最后一层仅有一个结点,叫输出层,负责输出预测值y^。隐藏层的值在训练集中是看不见的,所以得名隐藏层。
输入特征还有另外一种表示方式,即为图中的a[0]、a[1]、a[2]。a也表示激活的意思,当前层的值会被传递给后面的层。
约定输入层为第零层,不计入神经网络的层数中。隐藏层和输出层是带有参数的,比如w和b,见图中绿色的符号。
3.3 计算神经网络的输出
如下图所示,圆圈代表两个意思,一是计算z,二是计算激活函数a。神经网络不过是重复以上步骤很多次而已。以隐藏层的第一个结点为例,带方括号的上标表示结点所在的layer,不带符号的下标表示layer里面的结点数。隐藏层前两个结点的计算过程如图所示。
将计算过程向量化,如图所示。z[1]、a[1] 、b[1]的维度是(4,1),z[2]、a[2]、b[2]的维度是(1,1)。W[1]的维度是(3,4),W[2]的维度是(4,1)。
3.4 多样本的向量化
对m个样本的上述3.3节计算过程向量化,如下图所示。通过将向量横向堆叠,可以从小写的z[1](1) z[1](2) … z[1](m)得到大写的Z[1]矩阵,从小写的a[1](1)a[1](2)…a[1](m)得到大写的A[1]矩阵。对于Z[1]和A[1]矩阵,横向扫描会扫到不同的样本,纵向扫描会扫到同一层不同的结点。对于X矩阵,横向扫描会扫到不同的样本,纵向扫描会扫到不同的特征。
3.5 向量化实现的解释(略)
3.6 激活函数
之前使用的激活函数都是σ/sigmoid函数。有个激活函数表现比sigmoid好,即tanh函数,是sigmoid函数平移后的版本,其公式为a=g(z)=tanh(z)=(ez-e-z)/(ez+e-z)。对于隐藏层的单元来说,tanh的效果比sigmoid好,因为数据的均值为0,值在-1到1之间,对于下一层的学习更有利。
但是对于输出层来说,希望y^在0到1之间更合理,sigmoid函数仍然可用。其他层中,一般用的是tanh而不是sigmoid。
Tanh和sigmoid函数有个特点,就是对于很大或者很小的z来说,激活函数的输出接近于1或-1,那么导数的梯度可能很小,函数的斜率接近于0,对于梯度下降法不利。
深度学习领域一个颇受欢迎的激活函数是Relu(修正线性单元),其公式为a=g(z)=max(0,z),只要z为正数,导数就是1,z为负数,导数就是0。
激活函数的选择有如下原则:
如果输出值是0和1,二元分类时,σ/sigmoid函数适合作为输出层的激活函数。
其他层都用relu,成为激活函数的默认选择。
Relu还有另外一个版本,叫做leaky relu,其公式为a=g(z)=max(0.01z,z)。和relu的区别是,z为负时导数不为0。
如果不知道哪种激活函数最有效,那么就在保留交叉验证数据集上都试一遍。
3.7 为什么需要非线性激活函数
如果没有非线性激活函数,那么神经网络只是把输入线性组合后再输出,线性隐藏层失去了作用,网络层数再多也不行。逻辑回归问题的输出层可以用线性激活函数,隐藏层必须用非线性激活函数。
3.8 激活函数的导数
几种常见激活函数的导数如图所示。
3.9 、3.10 单隐藏层神经网络的梯度下降法
假设神经网络的结构如3.2节所示,为单隐藏层。神经网络做二元分类,利用梯度下降法训练得到参数w和b。训练神经网络时,随机初始化参数很重要。梯度下降法的伪代码为:
Repeat {
Compute predictsy^(i),i=1~m
Compute dw[1],db[1],dw[2],db[2]
Update w[1]=w[1]-αdw[1]
Update b[1]=b[1]-αdb[1]
Update w[2]=w[2]-αdw[2]
Update 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])=σ(Z[2])
反向传播的计算过程为:
dZ[2]=A[2]-Y
dW[2]=1/m* dZ[2] A[1]T
db[2]= 1/m*np.sum(dZ[2] ,axis=1,keepdim=True)
dZ[1]= W[2]T dZ[2] *g[1]’(Z[1]) (element-wise product是逐个元素乘积,g[1]’是激活函数的导数)
dW[1]=1/m* dZ[1]XT
db[1]= 1/m*np.sum(dZ[1] ,axis=1,keepdim=True)
np.sum用于对矩阵的一个维度求和,开关keepdim防止python输出秩为1的数组。
3.11 参数初始化
对于逻辑回归而言,参数初始化为0是没有问题的,但是如果将神经网络的各参数数组全部初始化为0,再使用梯度下降法,那么梯度下降法将会完全失效。原因在于,对于如下的神经网络来说,如果w的初始值都为0,那么a[1] 1和a[1] 2完全相同,计算反向传播时,可以证明dz[1] 1和dz[1] 2也是相同的。所有结点在计算完全一样的激活函数,完全对称。用迭代法可以证明,在执行完梯度下降后,所有结点仍然在计算完全一样的激活函数,多个隐藏层单元失去了意义。
解决问题的办法是随机化参数。令W[1]=np.random.randn((2,2))*0.01,b[1]=np.zero((2,1)),这里b可以初始化为0,但是W不可以。
这里W公式里使用的是0.01,是一个较小的值。如果W一开始很大,那么z就会很大或者很小,a会落在tanh和sigmoid函数饱和区域,梯度的斜率很小,学习就会很慢。对于浅层神经网络,0.01没问题,但是对于深度神经网络来说,通常采用0.01以外的其他值。