本文总结的是课程一《神经网络和深度学习》的第二周《神经网络基础》,共18小节,本文涵盖其中的14小节。视频请访问deeplearning.ai或者网易云课堂。
2.1 二分类
本周的内容从逻辑回归开始说起。逻辑回归通常用来解决二分类问题,例如下图就是判别图片中是否为一只猫的二分类器,输出为1表示有猫,为0表示没有猫。
计算机中使用三个独立的矩阵保存一张图片,对应于RGB三个通道。通常将三个矩阵的数值写入一个特征向量中。假设输入的图像的大小是64*64*3,那么输入的特征向量的大小nx是64*64*3=12288(nx也可以用n代替)。
将上述例子一般化,用(x,y)表示输入,x∈Rn,标签y∈{0,1},训练集包括m个样本,(x(1),y(1))表示样本1,(x(2),y(2))表示样本2,…,(x(m),y(m))表示样本m。那么训练集的输入可以表示为{(x(1),y(1)),(x(2),y(2)),…,(x(m),y(m))},也可以表示为矩阵形式X=[x(1) x(2) … x(m)] ,X的大小是nx*m。
训练集的标签可以表示成类似的矩阵形式Y=[ y(1) y(2) … ym)],Y的大小是1*m。
2.2 逻辑回归
将2.1节的问题抽象为:需要一个能针对输入x给出预测值y^的算法(y^表示对y的预测)。用概率表达y^,可以写成y^=P(y=1|x)。
逻辑回归算法里,y^的表达式可以写作y^=σ(wTx+b), w是权重,b是偏置。2.1节已知x∈Rn,这里令参数w的大小同x,即w∈Rn。参数b是一个实数。
σ表示sigmoid函数
如果z很大,那么e-z就接近于0,σ(z)接近1。如果z很小,那么e-z就接近于无穷大,σ(z)接近0。sigmoid函数的特性如图所示。
逻辑回归的任务是,学习参数w和b,使得y^成为对y的一个很好的估计。
2.3 逻辑回归的代价函数
为了训练w和b,需要定义一个cost function。用上标(i)表示第i个样本,那么
y^(i)=σ(wTx(i)+b)
损失函数Loss function,或者叫误差函数error function有多种定义形式,一种常见的形式为
L(y^,y)=(y^-y)2/2
但是逻辑回归通常并不会采用这种形式,因为这种形式后续优化会变成非凸(non-convex),最后会得到很多局部最优解,梯度下降法得不到全局最优值。因此,在逻辑回归中我们定义的损失函数不是误差平方,而是一个能够得到全局最优解的形式
损失函数取得最小值时,即找到最优解,具体的解释过程略。
Loss function是针对单个样本的,cost function则是针对所有样本的。定义cost function为所有训练样本的损失函数之和:
2.4 梯度下降法
逻辑回归算法的目标是,找到合适的w和b使得cost function取得最小值。梯度下降法可以达成此目的。
下图中的J(w,b)是一个凸函数,有全局最优解。正是因为代价函数是凸函数,梯度下降法才会奏效。先初始化w和b,从初始点开始,朝着最陡的下坡方向走一步,即为一次迭代,然后继续朝着最陡的下坡方向走一步,直到找到或者接近全局最优解,如图中红点及其连线所示。
用伪代码表示梯度下降法的过程为
α是学习率;导数项是对w的更新,在python代码中写为dw,上式简化为w:=w-αdw。b的更新方式同w。如果有两个或两个以上的参数,则需要将导数符号改成偏导符号:
2.5 、2.6导数(略)
2.7 计算图
计算图解释了神经网络的计算为什么按照“前向传播计算网络输出、反向传播计算导数或梯度”的方式进行的。例如,对于J(a,b,c)=3(a+bc)来说,计算分为三步:首先,计算u=bc,其次计算v=a+u,再次计算J=3v。
图中蓝色箭头表明了“前向传播计算网络输出”的过程,红色箭头表明了“反向传播计算导数”的过程。
2.8 计算图导数的计算
根据微积分知识易得dJ/dv=3,dJ/da的值呢?根据微积分中的链式法则,dJ/da=dJ/dv*dv/da=3*1=3。
将上述问题一般化,经常会遇到求解d FinaloutputVar/d var的问题。在python编程中,常用dvar这个变量表示d FinaloutputVar/d var,即中间量的导数。
du的值如何计算呢?根据链式法则,有dJ/du= dJ/dv*dv/du=3*1=3。
2.9 逻辑回归的梯度下降计算
逻辑回归中的公式如上图所示。首先计算z,然后计算a,最后计算L。逻辑回归需要做的事情是,迭代参数w和b的值,来最小化损失函数。
首先要计算L对a的偏导数,代码中用da表示。接着计算dz,也就是L对z的偏导数,求解过程用到链式法则。最后,会计算到dw和db。计算出单个样本的dw1、dw2和db之后,根据w1:=w1-αdw1、w2:=w2-αdw2、b:=b-αdb更新参数的值。
2.10 逻辑回归m个样本的梯度下降
假设初始化时的代价函数J=0,dw1=0,dw2=0,db=0。用一个for循环遍历训练集,计算相应的每个训练样本的导数,然后加起来。假设n为2,伪代码如下:
使用dw1 、dw2 、db为累加器,因此没有上标i,dz(i)是对于单个样本的计算值,因此有上标i。计算完上述这些值后,运用梯度下降法根据w1:=w1-αdw1、w2:=w2-αdw2、b:=b-αdb更新参数的值。
上述方法存在缺陷:代码中涉及两个for循环,第一个for循环针对m个样本,第二个for循环则是针对所有特征的累积和计算(这里n为2)。在代码中显式使用for循环会使得运算低效,尤其是训练集庞大、特征众多的场景。向量化技术可以摆脱这些for循环。
2.11 向量化
以z(i)=wTx(i)+b为例,非向量化的python实现为
z=0
for i=1 to range(n_x):
z+=w[i]*x[i]
z+=b
向量化的python实现为
import numpy as np
z=np.dot(w,x)+b
python的numpy可以充分利用并行化去更快地计算,这对于GPU和CPU都适用。
2.12 向量化的更多例子
除了dot之外,numpy的exp、log、abs、maximum函数也支持向量化。
m个样本的逻辑回归python代码如下图左侧所示,其中包含两个for循环,第一个for循环是针对m个样本,第二个for循环则是针对所有特征的累积和计算。
去掉第二个for循环的做法是,不显式地将dw、dw2初始化为0,而是把dw变为一个向量:dw=np.zeros((n_x,1)),更新后的代码如上图绿色代码所示,用dw+=x(i)dz(i)代替dw1和dw2。
2.13、2.14 向量化的逻辑回归
本节介绍如何不显式地编写python代码实现逻辑回归。
2.1节介绍过,输入样本X的大小是nx*m。那么z(i)=wTx(i)+b如何向量化表示呢?将z(1) 到 z(m)合成一个矩阵,可以写成Z=[z(1) z(2) … z(m)]=[ wTx(1)+b wTx(2)+b …wTx(m)+b]=wTX+[b b … b],这样使用numpy语句可以写成Z=np.dot(w.T,X)+b。虽然b是实数,但是根据python的广播机制,运算时b会自动扩展为[b b … b]形式的向量。
同样地,a(i)= σ(z(i))被向量化后可以写为A=σ(Z) 。dZ可以向量化表示为A-Y。其余前向传播的公式都可以采用类似的方法向量化。如下图左侧公式所示。
2.12节可以去除内部的for循环,也就是针对特征的循环。如何去掉针对样本数m的外部的for循环呢?去除这一层for循环的关键在于重写dw和db。db可用矩阵形式表示为db=np.sum(dZ),dw则可以表示为X和dZ的转置相乘的形式,详细推导过程如下图右侧紫色公式。
将逻辑回归的向量化过程numpy代码整理,如下图所示,左侧是使用for循环的代码,右侧是向量化后的代码。计算出dw和db后运用梯度下降法根据w:=w-αdw、b:=b-αdb更新参数的值。
梯度下降法需要迭代,迭代的代码必须以for循环的形式给出,例如for iter in range(1000)。
2.15 python的广播机制
Python中常用的广播形式如图所示。当运算符左右两边的矩阵维度不等时,广播机制会自动copy一些行或列,使得运算符左右两边的矩阵维度相等。
python编程时,经常使用reshape命令是一个好习惯,可以减少错误发生,同时便于调试。
2.16 numpy编程技巧
Python编程时,避免使用shape为(n,1)的数组,针对array,需要用np.reshape函数将其转换为vector后再计算。
例如np.random.randn(5)的结果就是一个秩为1的array,并不是vector。然而np.random.randn(5,1)的结果是一个列向量,np.random.randn(1,5)的结果是一个行向量。使用assert指令可以声明一个变量的shape。对于秩为1的array,需要用np.reshape将其转换为vector后再计算。
2.17、2.18 略