深度学习工程师之神经网络和深度学习2:神经网络基础

毛主席曾经说过:

情况是在不断的变化,要使自己的思想适应新的情况,就得学习。

所以!继续学习!!!

以上。

———————————————————————————————————

2.1 二分分类

整个第二周的课程会以逻辑回归-Logistic Regression为例进行讲解,逻辑回归是一个用于二分分类的算法,对该算法不太了解的同学可以去看一下吴老师的机器学习课程,里面讲的超级简单易懂。附上网址:吴恩达机器学习

分类问题是机器学习中的一个重要任务,分类问题的目的是通过神经网络学习一个函数来判断输入数据所属的类别,包括;

  • 二分类问题:是或不是。比如对一幅图片判别它是猫or不是猫。
  • 多分类问题:在多个类别中判断输入数据具体属于哪一个类别。

二分类是所有分类问题的基础,即便是多类别的分类也可以转化为二分类问题进行求解。分类问题的预测输出结果是离散的标签值,比如:

  • 是猫,则输出1;不是猫,则输出0;
  • 是肿瘤,则输出1;和不是肿瘤,则输出0。

2.1.1 分类器的输入与输出

以一副图片作为输入,我们希望训练一个分类器来达到辨别该图片属于哪种类别的目的:
1

所以对于该分类器而言:

  • 输入x,为image
  • 输出y,为标签(离散值)

那么如何令image作为输入呢?考虑到图像在计算机中是以矩阵形式存储的,上述图片在计算机中是一个RGB三通道的矩阵,矩阵中的元素代表了像素强度值,因此只需要将图片中的像素强度值提取出来放在一个特征向量 x x x 中即可。在模式识别与机器学习中,一个特征向量代表着一个对象,那么在该问题中,它代表了是猫或不是猫。

2
容易得到, x x x 的维度为 ( n = ) n x = 64 × 64 × 3 = 12288 (n=)n_x=64\times64\times3=12288 (n=)nx=64×64×3=12288,即输入的特征向量的维度为12288.

因此二分类问题的目标就是训练一个分类器,其输入与输出分别为:

  • 输入:以特征向量 x x x 表示的图像;
  • 输出:离散标签 y = 1 / 0 y=1/0 y=1/0 ,1 表示图片是猫,0 表示图片不是猫。

2.1.2 符号注记

  • ( x , y ) (x,y) (x,y) 表示一个单独的样本,其中 x ∈ R n x x{\in}R^{n_x} xRnx y ∈ { 0 , 1 } y{\in}\{0, 1\} y{0,1}

  • m m m 表示训练样本的总个数,即 t r a i n i n g   s e t = { ( x ( 1 ) , y ( 1 ) ) , ( x ( 2 ) , y ( 2 ) ) , ⋯   , ( x ( m ) , y ( m ) ) } training\ set=\{(x^{(1)}, y^{(1)}), (x^{(2)}, y^{(2)}), \cdots, (x^{(m)}, y^{(m)})\} training set={(x(1),y(1)),(x(2),y(2)),,(x(m),y(m))},在一些情况下为区分训练集与测试集会进行标注 m t r a i n , m t e s t m_{train}, m_{test} mtrain,mtest

  • 为简化标注,引入 X X X作为所有训练样本(特征向量 x x x)的组合:
    3
    容易知道, X ∈ R n x × m X{\in}R^{n_x{\times}m} XRnx×m

  • 同样对于标签 y y y,将所有的标签记为 Y Y Y,即 Y = [ y ( 1 ) , ⋯   , y ( m ) ] Y=[y^{(1)}, \cdots, y^{(m)}] Y=[y(1),,y(m)],易知, Y ∈ R 1 × m Y{\in}R^{1{\times}m} YR1×m

2.2 Logistic回归模型

Logistic回归是用在监督学习中当输出 y = 0 / 1 y=0/1 y=0/1时的一种算法,它的目标是最小化预测值与真实标签之间的误差(需要定义一个损失函数来最小化误差,且看下节笔记)。

以上一节课的二分类问题为例,Logistic回归的算法流程如下:

4
用一句话来表示Logistic回归就是:

5

其中的参数意义分别为:

  • 输入特征向量: x ∈ R n x x{\in}R^{n_x} xRnx,其中 n x n_x nx为特征的数量;
  • 训练标签: y = 0 , 1 y={0, 1} y=0,1
  • 权重: w ∈ R n x w{\in}R^{n_x} wRnx,其中 n x n_x nx为特征的数量;
  • 残差: b ∈ R b{\in}R bR
  • 输出概率值: y ^ = σ ( w T x + b ) \hat{y}=\sigma(w^Tx+b) y^=σ(wTx+b)
  • 其中 σ ( z ) = 1 1 + e − z \sigma(z)=\frac{1}{1+e^{-z}} σ(z)=1+ez1是第一周的课程中提到的sigmoid函数

需要注意的是:

  • 在这个流程中,Logistic回归的输入 x x x 仍是一幅图像对应的特征向量,但输出 y ^ \hat{y} y^ 不再是 0 或 1,而是 [ 0 , 1 ] [0, 1] [0,1]之间的一个实数,它表达的意义是输入图片是猫的概率,比如最终输出 y ^ = 0.8 \hat{y}=0.8 y^=0.8,那么我们就认为输入图片是猫的概率为0.8。
  • 之所以产生这样的结果,是因为引入了sigmoid函数,该函数可以将函数值压缩至 [ 0 , 1 ] [0,1 ] [0,1]区间内。

6

那么为什么要引入sigmoid函数呢?原因在于 w T x + b w^Tx+b wTx+b是一个线性函数,而概率的取值范围限制了我们希望得到的函数值落在 [ 0 , 1 ] [0,1 ] [0,1]区间上,所以我们需要引入sigmoid函数。从图中可以看出sigmoid函数的一些性质:

  • z z z足够大时, σ ( z ) = 1 \sigma(z)=1 σ(z)=1
  • z z z非常小时, σ ( z ) = 0 \sigma(z)=0 σ(z)=0
  • z = 0 z=0 z=0时, σ ( z ) = 0.5 \sigma(z)=0.5 σ(z)=0.5

所以呢?Logistic回归实际上做了什么工作???

百年不变的答案——那就是学习参数 w w w b b b,使得 y ^ \hat{y} y^成为一个比较好的估计。

2.3 Logistic回归损失函数

上一节课学习了Logistic回归的模型,并且知道了它的目标是最小化预测值和真实值之间的误差,这个最小化的过程我感觉就是模型学习参数 w w w b b b 的过程,因此为了让模型能够学到最合适的参数,我们需要定义一个损失函数

Logistic回归的模型是这样的:

  • y ^ ( i ) = σ ( w T x ( i ) + b ) \hat{y}^{(i)}=\sigma(w^Tx^{(i)}+b) y^(i)=σ(wTx(i)+b),其中 σ ( z ( i ) ) = 1 1 + e − z ( i ) \sigma(z^{(i)})=\frac{1}{1+e^{-z^{(i)}}} σ(z(i))=1+ez(i)1
  • 对于训练样本 { ( x ( 1 ) , y ( 1 ) ) , ( x ( 2 ) , y ( 2 ) ) , ⋯   , ( x ( m ) , y ( m ) ) } \{(x^{(1)}, y^{(1)}), (x^{(2)}, y^{(2)}), \cdots, (x^{(m)}, y^{(m)})\} {(x(1),y(1)),(x(2),y(2)),,(x(m),y(m))},我们希望得到 y ^ ( i ) ≈ y ( i ) \hat{y}^{(i)}\approx{y^{(i)}} y^(i)y(i)
  • 其中 ( i ) (i) (i)表示第 i i i个训练样本。

2.3.1 针对单一训练样本的Loss Function

loss function衡量了预测输出 y ^ ( i ) \hat{y}^{(i)} y^(i)与真实值 y ( i ) y^{(i)} y(i)之间的接近程度,换句话说即计算了单个训练样本的误差,它反应的是模型针对单个样本的表现。课堂上吴老师给出了两种loss function:

  • 平方损失函数: L ( y ^ ( i ) , y ( i ) ) = 1 2 ( y ^ ( i ) − y ( i ) ) 2 L(\hat{y}^{(i)},y^{(i)})=\frac{1}{2}(\hat{y}^{(i)}-y^{(i)})^2 L(y^(i),y(i))=21(y^(i)y(i))2
    但是在Logistic回归中很少会选用该函数,原因在于学习参数时该损失函数会使得后面的优化问题成为非凸的,即会出现很多局部最优解。比如对于梯度下降算法而言,就难以找到一个全局最优解了。
  • 交叉熵损失函数: L ( y ^ ( i ) , y ( i ) ) = − [ y ( i ) l o g ( y ^ ( i ) ) + ( 1 − y ( i ) ) l o g ( 1 − y ^ ( i ) ) ] L(\hat{y}^{(i)},y^{(i)})=-[y^{(i)}log(\hat{y}^{(i)})+(1-y^{(i)})log(1-\hat{y}^{(i)})] L(y^(i),y(i))=[y(i)log(y^(i))+(1y(i))log(1y^(i))]
    而交叉熵损失函数则是比较常用的一种类型,原因在于该函数能够使得优化问题具有凸性,从而可以取到全局最优解,简单描述:
    (1) 当 y ( i ) = 1 y^{(i)}=1 y(i)=1时,有 L ( y ^ ( i ) , y ( i ) ) = − l o g ( y ^ ( i ) ) L(\hat{y}^{(i)},y^{(i)})=-log(\hat{y}^{(i)}) L(y^(i),y(i))=log(y^(i)),我们的目的是最小化损失函数,这等价于最大化 l o g ( y ^ ( i ) ) log(\hat{y}^{(i)}) log(y^(i)),即使得 y ^ ( i ) \hat{y}^{(i)} y^(i) 尽可能的大,又因为 0 ≤ y ^ ( i ) ≤ 1 0{\leq}\hat{y}^{(i)}{\leq}1 0y^(i)1 ,所以最终实现的恰好是令 y ^ ( i ) \hat{y}^{(i)} y^(i) 接近于 1。
    (2) 当 y ( i ) = 0 y^{(i)}=0 y(i)=0时,有 L ( y ^ ( i ) , y ( i ) ) = − l o g ( 1 − y ^ ( i ) ) L(\hat{y}^{(i)},y^{(i)})=-log(1-\hat{y}^{(i)}) L(y^(i),y(i))=log(1y^(i)),同理要想最小化损失函数,等价于最大化 l o g ( 1 − y ^ ( i ) ) log(1-\hat{y}^{(i)}) log(1y^(i)),即使得 y ^ ( i ) \hat{y}^{(i)} y^(i) 尽可能的小,又因为 0 ≤ y ^ ( i ) ≤ 1 0{\leq}\hat{y}^{(i)}{\leq}1 0y^(i)1 ,所以最终实现的恰好是令 y ^ ( i ) \hat{y}^{(i)} y^(i) 接近于 0。

2.3.2 针对整体训练集的Cost Function

代价函数可以看作是损失函数在整个训练集上的一个平均值,最终目的就是通过最小化整体的代价函数来学习参数 w w w b b b

根据交叉熵损失函数,我们能够定义这样的一个代价函数 J ( w , b ) J(w, b) J(w,b)

  • J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) = − 1 m ∑ i = 1 m [ y ( i ) l o g ( y ^ ( i ) ) + ( 1 − y ( i ) ) l o g ( 1 − y ^ ( i ) ) ] J(w, b)=\frac{1}{m}\sum_{i=1}^{m}{L(\hat{y}^{(i)},y^{(i)})}=-\frac{1}{m}\sum_{i=1}^{m}[y^{(i)}log(\hat{y}^{(i)})+(1-y^{(i)})log(1-\hat{y}^{(i)})] J(w,b)=m1i=1mL(y^(i),y(i))=m1i=1m[y(i)log(y^(i))+(1y(i))log(1y^(i))]
  • 其中的 y ^ ( i ) \hat{y}^{(i)} y^(i)是通过参数 w w w b b b计算得到的预测值。

定义了损失函数,下一步就是选择一个优化算法!가자!열심히 공부해라!!!

2.4 梯度下降法与计算流程图

2.4.1 梯度下降法

上学期学的非线性问题解法看来还是有点用的,至少学了各种有关梯度下降的优化算法,考试虽然难了又难,看到自己熟悉的东西还是蛮开心der~ 感觉吴老师课上讲的只是很基础的东西,要是想了解的更深还需要靠自己去查阅一些其他的资料。

2.4.1.1 有关凸优化

假设存在这样的一个无约束优化问题:

  • m i n i m i z e   f ( x ) minimize\ f(x) minimize f(x)
  • 其中 f ( x ) f(x) f(x) 是一个凸函数,并且是二阶连续可微的(从而 d o m ( f ) dom(f) dom(f) 是开的),且其最优解 i n f x f ( x ) inf_xf(x) infxf(x) 能够取到。

求解该类问题的基本思想是在 d o m ( f ) dom(f) dom(f) 中产生一个序列 x ( k ) , k = 0 , 1 , ⋯ {x^{(k)}, k=0, 1, \cdots} x(k),k=0,1,,使得 l i m f ( x ( k ) ) = i n f x f ( x ) limf(x^{(k)})=inf_xf(x) limf(x(k))=infxf(x),此时这种迭代思想就等价于求解最优性条件,因为 f ( x ) f(x) f(x) 凸且二阶连续可微:

  • ∇ f ( x ∗ ) = 0 {\nabla}f(x^*)=0 f(x)=0

进而就有最基本的下降方法:

  • x ( k + 1 ) = x ( k ) + t ( k ) Δ x ( k ) x^{(k+1)}=x^{(k)}+t^{(k)}\Delta{x}^{(k)} x(k+1)=x(k)+t(k)Δx(k) with f ( x ( k + 1 ) ) < f ( x k ) f(x^{(k+1)})<f(x^{k}) f(x(k+1))<f(xk)

其中 Δ x \Delta{x} Δx 是搜索方向, t t t 为步长,根据 f ( x ) f(x) f(x) 的凸性可以推出 ∇ f ( x ) T Δ x < 0 {\nabla}f(x)^T{\Delta}x<0 f(x)TΔx<0
因此只要取 Δ x = − ∇ x \Delta{x}=-\nabla{x} Δx=x,那么搜索方向一定是下降方向。
这就是梯度下降算法中取梯度的负方向为下降方向的原因。
贴个之前的算法流程吧。

7

2.4.1.2 有关逻辑回归的梯度下降

前面几节课中学习了用于二分类问题的Logistic回归模型和该算法对应的损失函数与代价函数:

  • 模型: y ^ = σ ( w T x + b ) , σ ( z ) = 1 1 + e − z \hat{y}=\sigma(w^Tx+b),\sigma(z)=\frac{1}{1+e^{-z}} y^=σ(wTx+b),σ(z)=1+ez1
  • 代价函数: J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) = − 1 m ∑ i = 1 m [ y ( i ) l o g ( y ^ ( i ) ) + ( 1 − y ( i ) ) l o g ( 1 − y ^ ( i ) ) ] J(w, b)=\frac{1}{m}\sum_{i=1}^{m}{L(\hat{y}^{(i)},y^{(i)})}=-\frac{1}{m}\sum_{i=1}^{m}[y^{(i)}log(\hat{y}^{(i)})+(1-y^{(i)})log(1-\hat{y}^{(i)})] J(w,b)=m1i=1mL(y^(i),y(i))=m1i=1m[y(i)log(y^(i))+(1y(i))log(1y^(i))]

之前也提到过,我们希望模型能够在整个训练集上学到好的参数 w w w b b b,这就可以通过最小化代价函数来实现,而基于交叉熵损失函数的代价函数 J ( w , b ) J(w,b) J(w,b)是个凸函数,因此可以用梯度下降法来训练或学习参数 w w w b b b,所以我们最终需要做的事情就是:

  • 找到一组 w w w b b b 使得 代价函数 J ( w , b ) J(w,b) J(w,b) 达到最小值。

以下图为例:

8
我们需要做的就是给定一个初始值 w 0 w_0 w0 b 0 b_0 b0,然后不断地走一个下降方向来使得 J ( w , b ) J(w,b) J(w,b) 不断减小最终达到全局最小值点。比如这样走到最小值点:

9
从图中很容易看出, J ( w , b ) J(w,b) J(w,b)具有唯一的全局最优点,这是由凸性带来的性质,因此对于Logistic回归在初始化时基本任何初始化方法都是有效的,不管怎样,最终总是可以达到大概相同的一点,通常的初始化方法是令:

  • w 0 = b 0 = 0 w_0=b_0=0 w0=b0=0

那么通过梯度下降法是怎么学习参数的呢?
假设现在只有一个参数 w w w

10
我们需要做的事情就是:

  • r e p e a t      { repeat \ \ \ \ \{ repeat    {
  • w : = w − α d J ( w ) d w w :=w-\alpha{\frac{dJ(w)}{dw}} w:=wαdwdJ(w)
  • } \} }
  • u n t i l   c o n v e r g e n c e until\ convergence until convergence

其中 α \alpha α 表示学习率,它始终是一个正数(后面的课程会讲到如何选择学习率), d J ( w ) d w \frac{dJ(w)}{dw} dwdJ(w)表示代价函数对参数 w w w的导数或者说图像中的斜率。我们可以观察一下:

  • 当初始点在最优点右侧时,此时斜率是大于 0 的,由于 α \alpha α 是正的,所以每次更新 w : = w − α d J ( w ) d w w :=w-\alpha{\frac{dJ(w)}{dw}} w:=wαdwdJ(w) 后, w w w 会朝减小的方向前进,也使得 J ( w ) J(w) J(w) 在减小;
  • 当初始点在最优点右侧时,此时斜率是小于 0 的,由于 α \alpha α 是正的,所以每次更新 w : = w − α d J ( w ) d w w :=w-\alpha{\frac{dJ(w)}{dw}} w:=wαdwdJ(w) 后, w w w 会朝增大的方向前进,这同样会使得使得 J ( w ) J(w) J(w) 减小。

这就是梯度下降法的原理,通过不断地学习参数 w w w b b b 来最小化代价函数,直到找一组参数使得代价函数达到全局最优解。

对于多参数而言,原理都是一样的,将参数 b b b 考虑在内后,我们的更新法则会变成:

  • r e p e a t      { repeat \ \ \ \ \{ repeat    {
  • w : = w − α ∂ J ( w ) ∂ w w :=w-\alpha{\frac{{\partial}J(w)}{{\partial}w}} w:=wαwJ(w)
  • b : = b − α ∂ J ( w ) ∂ b b :=b-\alpha{\frac{{\partial}J(w)}{{\partial}b}} b:=bαbJ(w)
  • } \} }
  • u n t i l   c o n v e r g e n c e until\ convergence until convergence
    温馨提示:在代码编写中,用 d w dw dw 表示代价函数对参数 w w w 的微分,用 d b db db 表示代价函数对参数 b b b 的微分。

2.4.2 计算图 Computation Graph

上节课学习了梯度下降法,也浅显的了解到使用梯度下降法可以解决最小化代价函数的最优化问题,从而使得网络能够在整个训练集上学习参数。所以这节课会简单的介绍一下神经网络中如何应用到的梯度。

一个神经网络的计算是按照前向/反向传播来实现的,即:

  • 先计算神经网络的输出;
  • 紧跟进行一个反向传输操作用于计算对应的梯度/导数。

那么为什么要用这样的方式来实现神经网络呢? 我觉得是通过这种方式能够很好的展现最终输出随输入变化而产生的变化,从而能够通过调节参数来不断地得到我们希望的输出结果。

简单的例子 假设我们有这样一个函数 J ( a , b , c ) = 3 ( a + b c ) J(a,b,c)=3(a+bc) J(a,b,c)=3(a+bc),那么计算过程就包括了三步:

  • u = b c u=bc u=bc
  • v = a + u v=a+u v=a+u
  • J = 3 v J=3v J=3v

我们可以用流程图来呈现这种计算过程:

11

  • 从左向右表示函数值 J J J 的计算,通过流程图我们能够清楚的看到每一步的计算过程;
  • 从右向左表示导数的计算,也就是反向传播,我们可以看到在这一过程中,梯度是如何一步一步地进行传输。(可以将 J ( a , b , c ) J(a,b,c) J(a,b,c) 想象成是我们的代价函数)

函数值的计算非常简单,主要就是反向传播中梯度/导数是如何进行传播的,感觉只要会求导,这里也毫无困难:

  • d v = d J d v = d   3 v d v = 3 dv=\frac{dJ}{dv}=\frac{d\ 3v}{dv}=3 dv=dvdJ=dvd 3v=3
  • d a = d J d a = d J d v d v d a = 3 d ( a + u ) d a = 3 da=\frac{dJ}{da}=\frac{dJ}{dv}\frac{dv}{da}=3\frac{d(a+u)}{da}=3 da=dadJ=dvdJdadv=3dad(a+u)=3
  • d u = d J d u = d J d v d v d u = 3 d ( a + u ) d u = 3 du=\frac{dJ}{du}=\frac{dJ}{dv}\frac{dv}{du}=3\frac{d(a+u)}{du}=3 du=dudJ=dvdJdudv=3dud(a+u)=3
  • d b = d J d b = d J d v d v d u d u d b = 3 ⋅ 1 d   b c d b = 3 c db=\frac{dJ}{db}=\frac{dJ}{dv}\frac{dv}{du}\frac{du}{db}=3\cdot1\frac{d\ bc}{db}=3c db=dbdJ=dvdJdudvdbdu=31dbd bc=3c
  • d c = d J d c = d J d v d v d u d u d b = 3 ⋅ 1 d   b c d c = 3 b dc=\frac{dJ}{dc}=\frac{dJ}{dv}\frac{dv}{du}\frac{du}{db}=3\cdot1\frac{d\ bc}{dc}=3b dc=dcdJ=dvdJdudvdbdu=31dcd bc=3b

通过偏微分求导的链式法则,很容易就能得到最终输出结果关于各个输入变量的梯度。(流程图真是个好东西呀哈哈哈哈)

2.5 Logistic回归中的梯度下降

2.5.1 单个样本

前面学习了Logistic回归的模型和损失函数:

  • z = w T x + b z=w^Tx+b z=wTx+b
  • y ^ = a = σ ( z ) \hat{y}=a=\sigma(z) y^=a=σ(z)为预测值;
  • L ( a , y ) = − [ y l o g ( a ) + ( 1 − y ) l o g ( 1 − a ) ] L(a,y)=-[ylog(a)+(1-y)log(1-a)] L(a,y)=[ylog(a)+(1y)log(1a)]为单个样本的损失函数;

假设存在两个特征 x 1 , x 2 x_1,x_2 x1,x2,那么我们有这样的一个计算图:

12
前向传播能够计算出损失函数值,反向传播能够计算梯度/导数值。在Logistic回归我们需要做的就是根据梯度来更新参数 w w w b b b的值以最小化损失函数(或者说是代价函数)。
首先计算一下关于参数的导数值:

  • d a = d L ( a , y ) d a = − y a + 1 − y 1 − a da=\frac{dL(a,y)}{da}=-\frac{y}{a}+\frac{1-y}{1-a} da=dadL(a,y)=ay+1a1y
  • d z = d L d a d a d z = ( − y a + 1 − y 1 − a ) a ( 1 − a ) = a − y dz=\frac{dL}{da}\frac{da}{dz}=(-\frac{y}{a}+\frac{1-y}{1-a})a(1-a)=a-y dz=dadLdzda=(ay+1a1y)a(1a)=ay
  • d w 1 = d L d z d z d w 1 = x 1 d z dw_1=\frac{dL}{dz}\frac{dz}{dw_1}=x_1dz dw1=dzdLdw1dz=x1dz
  • d w 2 = d L d z d z d w 2 = x 2 d z dw_2=\frac{dL}{dz}\frac{dz}{dw_2}=x_2dz dw2=dzdLdw2dz=x2dz
  • d b = d L d z d z d b = d z db=\frac{dL}{dz}\frac{dz}{db}=dz db=dzdLdbdz=dz

有了梯度以后,我们就可以利用梯度下降法来更新参数值,回顾下之前的内容即:

  • w 1 : = w 1 − α d w 1 w_1:=w_1-\alpha{dw_1} w1:=w1αdw1
  • w 2 : = w 2 − α d w 2 w_2:=w_2-\alpha{dw_2} w2:=w2αdw2
  • b : = b − α d b b:=b-\alpha{db} b:=bαdb当然这里是针对于单个样本的过程,在实际的模型训练时往往要用到整个训练集,所以…look down~

2.5.2 整个训练集

假设我们的训练集有 m m m 个样本,那么整个训练集上的代价函数为:

  • J ( w , b ) = 1 m ∑ i = 1 m L ( a ( i ) , y ( i ) ) J(w,b)=\frac{1}{m}\sum_{i=1}^{m}L(a^{(i)},y^{(i)}) J(w,b)=m1i=1mL(a(i),y(i))

其中 a ( i ) = y ^ ( i ) = σ ( w T x ( i ) + b ) a^{(i)}=\hat{y}^{(i)}=\sigma(w^Tx^{(i)}+b) a(i)=y^(i)=σ(wTx(i)+b)
此时我们需要计算的梯度值是在整个训练样本上进行的,因此全局梯度值:

  • ∂ ∂ w 1 J ( w , b ) = 1 m ∑ i = 1 m ∂ ∂ w 1 L ( a ( i ) , y ( i ) ) \frac{\partial}{\partial{w_1}}J(w,b)=\frac{1}{m}\sum_{i=1}^{m}{\frac{\partial}{\partial{w_1}}L(a^{(i)},y^{(i)})} w1J(w,b)=m1i=1mw1L(a(i),y(i))
  • ∂ ∂ w 2 J ( w , b ) = 1 m ∑ i = 1 m ∂ ∂ w 2 L ( a ( i ) , y ( i ) ) \frac{\partial}{\partial{w_2}}J(w,b)=\frac{1}{m}\sum_{i=1}^{m}{\frac{\partial}{\partial{w_2}}L(a^{(i)},y^{(i)})} w2J(w,b)=m1i=1mw2L(a(i),y(i))
  • ∂ ∂ b J ( w , b ) = 1 m ∑ i = 1 m ∂ ∂ b L ( a ( i ) , y ( i ) ) \frac{\partial}{\partial{b}}J(w,b)=\frac{1}{m}\sum_{i=1}^{m}{\frac{\partial}{\partial{b}}L(a^{(i)},y^{(i)})} bJ(w,b)=m1i=1mbL(a(i),y(i))

有了梯度值之后就可以更新参数量,吴老师给了一个算法流程,也是比较容易理解的:

13

以上就是在Logistic回归中应用梯度下降法的流程,核心思想就在于遍历整个训练集上的每一个样本计算每一个参数的梯度,一开始没太搞懂参数,画了个图就很清晰了:

14

  • 参数的个数和特征的数量一一对应;
  • 单个样本关于参数的梯度值由损失函数求导可得;
  • 进而整个训练集上的参数梯度值由遍历所有样本后,累加再求平均值得到。
  • 得到梯度值后就可以利用梯度下降法来更新参数。

但是在这部分的内容中,所有的特征共用一个权重值的,也就是上图对应的表达式为:

0
而本课程中的权重是公用的,即:

01

2.6 加快训练的技巧——向量化

2.6.1 什么是向量化

向量化是一种减轻代码负担的技巧,最初学习C语言 接触到“遍历”的时候我第一反应是要去用for loop实现,但是for循环真的超级慢,后来学了python,在它的numpy包中有很多内置函数可以代替实现for循环的功能,从而加快代码运行的速度。举个简单的例子:

实例1:向量乘法 z = w T x + b z=w^Tx+b z=wTx+b 为例,其中 w ∈ R n x , x ∈ R n x w\in{R^{n_x}}, x\in{R^{n_x}} wRnx,xRnx。我们来看一下使用for循环和向量化的两种不同的实现方式。

  • for循环
z=0
for i in range(n_x):
    z += w[i] * x[i]
z += b
  • 向量化
import numpy as np

z = np.dot(w, x) + b

这里我们只需要调用一个简单的内置点乘函数就可以实现与for循环同样的功能。可以在Jupyter Notebook里做一下测试来对比两种不同方式的实现时间相差多少。

# Jupyter Notebook实现代码

import numpy as np
import time

a = np.random.rand(1000000)
b = np.random.rand(1000000)

# 向量化实现
tic = time.time()
c = np.dot(a, b)
toc = time.time()

print(c)
print("time of vectorized version:" + str(1000*(toc-tic)) + "ms")

# for-loop 实现
c = 0
tic = time.time()
for i in range(1000000):
    c += a[i] * b[i]

print(c)
print("time of for-loop version:" + str(1000*(toc-tic)) + "ms")

# 输出结果:
# 250181.28076606157
# time of vectorized version:9.612560272216797ms
# 250181.2807660554
# time of for-loop version:495.044469833374ms

可以看到,通过调用内置函数实现的向量化与通过使用for-loop的实现所用时间相差甚大!!!所以吴老师在课堂上给出了一个经典的法则:

Whenever possible, avoid explicit for loops ! ! !

实例2:矩阵向量乘 再举个简单的例子,以 u = A v u=Av u=Av 为例,其中 u ∈ R n , A ∈ R n × n , v ∈ R n u\in{R^n}, A\in{R^{n\times{n}}, v\in{R^n}} uRn,ARn×n,vRn,那么 u i = ∑ i ∑ j A i j v j u_i=\sum_{i}{\sum_{j}}A_{ij}v_j ui=ijAijvj。再来对比一下两种不同的实现方式:

  • for loop
import numpy as np

u = np.zeros((n,1))
for i in range(n):
    for j in range(n):
        u[i] += A[i][j] * v[j]
  • 向量化
import numpy as np

u = np.dot(A, v)

实例3:对矩阵/向量施行其他运算 假设向量 v ∈ R n v\in{R^n} vRn,想要对该向量的每个元素执行指数运算,即 u = e v u=e^{v} u=ev

  • for loop
import numpy as np
import math

u = np.zeros((n, 1))
for i in range(n):
    u[i] = math.exp(v[i])
  • 向量化
import numpy as np

u = np.exp(v)
# u = np.log(v)
# u = np.maximum(v)

实例4:Logistic回归中的导数计算
上节课给了基于两个for循环的Logistic算法,现在我们先把其中一个for循环去掉:

14现在就只有一个循环啦~然后训练就会变快啦。

2.6.2 Logistic回归的向量化

前面已经学习了神经网络的训练过程包含正向传播与反向传播,为了加快训练速度,我们可以将遍历训练样本的过程也进行向量化。

2.6.2.1 实现正向传播

所谓正向传播是为了计算模型的预测值,对于有 m m m 个样本的训练集,Logistic回归的正向传播为遍历整个训练集计算预测值:

  • z ( 1 ) = w T x ( 1 ) + b z^{(1)}=w^Tx^{(1)}+b z(1)=wTx(1)+b
    a ( 1 ) = σ ( z ( 1 ) ) a^{(1)}=\sigma{(z^{(1)})} a(1)=σ(z(1))
  • z ( 2 ) = w T x ( 2 ) + b z^{(2)}=w^Tx^{(2)}+b z(2)=wTx(2)+b
    a ( 2 ) = σ ( z ( 2 ) ) a^{(2)}=\sigma{(z^{(2)})} a(2)=σ(z(2))
  • z ( m ) = w T x ( m ) + b z^{(m)}=w^Tx^{(m)}+b z(m)=wTx(m)+b
    a ( m ) = σ ( z ( m ) ) a^{(m)}=\sigma{(z^{(m)})} a(m)=σ(z(m))

之前我们定义了一个 X X X 作为所有训练样本的组合:

15
因此我们可以对于预测值也定义一些新的符号:

  • Z = [ z ( 1 ) , z ( 2 ) , ⋯   , z ( m ) ] = w T X + b = n p . d o t ( w . T , X ) + b Z=[z^{(1)}, z^{(2)}, \cdots, z^{(m)}]=w^TX+b=np.dot(w.T, X)+b Z=[z(1),z(2),,z(m)]=wTX+b=np.dot(w.T,X)+b
  • A = [ a ( 1 ) , a ( 2 ) , ⋯   , a ( m ) ] = σ Z A=[a^{(1)}, a^{(2)}, \cdots, a^{(m)}]=\sigma{Z} A=[a(1),a(2),,a(m)]=σZ

所以我们就不需要再进行一个for循环来遍历整个训练集,通过两行代码就可以实现正向传播的迭代过程:

import numpy as np

def sigmoid(x):
    return 1. / (1 + np.exp(-x))
    
Z = np.dot(w.T, X) + b
A = sigmoid(Z)
2.6.2.2 实现反向传播

反向传播是为了计算参数的梯度,通过之前的学习,对于有 m m m 个样本的训练集,我们需要计算:

  • d z ( 1 ) = a ( 1 ) − y ( 1 ) dz^{(1)}=a^{(1)}-y^{(1)} dz(1)=a(1)y(1)
  • d z ( 2 ) = a ( 2 ) − y ( 2 ) dz^{(2)}=a^{(2)}-y^{(2)} dz(2)=a(2)y(2)
  • ⋯ \cdots
  • d z ( m ) = a ( m ) − y ( m ) dz^{(m)}=a^{(m)}-y^{(m)} dz(m)=a(m)y(m)

因此我们可以引入新的符号:

  • d Z = [ d z ( 1 ) , d z ( 2 ) , ⋯   , d z ( m ) ] 1 × m = A − Y dZ=[dz^{(1)},dz^{(2)},\cdots,dz^{(m)}]_{1\times{m}}=A-Y dZ=[dz(1),dz(2),,dz(m)]1×m=AY

在最开始给出的算法中,为了计算关于参数 w , b w,b w,b 的梯度引入了for循环,即:

  • dw d w = n p . z e r o s ( ( n x , 1 ) ) dw=np.zeros((n_x, 1)) dw=np.zeros((nx,1))
    d w + = x ( 1 ) d z ( 1 ) dw+=x^{(1)}dz^{(1)} dw+=x(1)dz(1)
    d w + = x ( 2 ) d z ( 2 ) dw+=x^{(2)}dz^{(2)} dw+=x(2)dz(2)
    ⋯ \cdots
    d w   / =   m dw\ /=\ m dw /= m
  • db d w = n p . z e r o s ( ( 1 , m ) ) dw=np.zeros((1, m)) dw=np.zeros((1,m))
    d b + = d z ( 1 ) db+=dz^{(1)} db+=dz(1)
    d b + = d z ( 2 ) db+=dz^{(2)} db+=dz(2)
    ⋯ \cdots
    d b   / =   m db\ /=\ m db /= m

因此我们可以通过以下代码来实现反向传播:

import numpy as np

dw = (1/m) * np.dot(X,dZ.T)
db = (1/m) * np.sum(dZ)

最后总结如下:

import numpy as np

# 计算1000次梯度
for item in range(1000):
    # 前向传播
    Z = np.dot(w.T, X) + b
    A = sigmoid(Z)
   
    # 反向传播
    dz = A - Y
    dw = (1/m) * np.dot(X,dZ.T)
    db = (1/m) * np.sum(dZ)

# 更新参数
w := w - a * dw
b := b - a * db

2.7 小结

  • 本科学高等代数的时候就及其讨厌矩阵和带有很多下标的符号,可能源于高中数学就不好,以至于当时就都没有好好学,现在看到关于向量&矩阵、还是其他带有下标的m/n这种东西真的脑袋都要炸了。。。关于权重那块纠结了好久才发现 啊 原来权重是个固定的向量/(ㄒoㄒ)/~~
  • 总之,在各种被耽误的时间里学完了第二周的课程,了解了神经网络的学习过程是由正向传播和反向传播共同组成的,而反向传播又实现了参数的更新以获取更为准确的输出。以Logistic回归模型为例,学习了它的模型、损失函数长什么样子,又间接性地回顾了一下梯度下降法并get了如何在Logistic回归中应用。
  • 除了主要内容外,吴老师介绍了好多训练中的技巧,如向量化、广播(没有写在笔记里,因为我写到了便签上 嘻嘻,以后有时间再来补充上叭)等等。
  • 总之,收获满满。革命尚未成功,同志仍需努力!!!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值