1.视频网站:mooc慕课https://mooc.study.163.com/university/deeplearning_ai#/c
2.详细笔记网站(中文):http://www.ai-start.com/dl2017/
3.github课件+作业+答案:https://github.com/stormstone/deeplearning.ai
2.7 计算图 Computation Graph-前向传播
一个神经网络的计算,都是按照前向或反向传播过程组织的。
首先我们计算出一个新的网络的输出(前向过程),紧接着进行一个反向传输操作。后者我们用来计算出对应的梯度或导数。
计算图解释了为什么我们用这种方式组织这些计算过程。
观察上图。
这里说明了计算成本函数 J(a,b,c)=3(a+bc) 的过程。计算这个函数实际上有三个不同的步骤
- step1: u=bc
- step2: v=a+u
- step3: J=3v
我们可以把这三步画成计算图,如上图的右下部分。
当有不同的或者一些特殊的输出变量时,例如本例中J的和逻辑回归中你想优化的代价函数J,计算图用来处理这些计算会很方便。
为了计算导数,从右到左(红色箭头,和蓝色箭头的过程相反)的过程是用于计算导数最自然的方式。
概括一下:计算图组织计算的形式是用蓝色箭头从左到右的计算。
2.8 计算图的导数计算 Derivatives with a Computation Graph-反向传播
观察下图,这是上一节介绍过的计算图,我们来看看你如何利用它计算出函数的导数。
先看最右边,由于J=3v,所以导数 d d v J ( v ) = d d v 3 v = 3 \frac{d}{dv}J(v)=\frac{d}{dv}3v=3 dvdJ(v)=dvd3v=3。
我们在反向传播术语中看到,如果你想使用你最关心的变量v计算最后输出变量J的导数,那我们就完成了一步反向传播,在这个流程图中是一个反向步骤(图中从右向左红色箭头过程)。
我们来看另一个例子, d d a J ( a ) \frac{d}{da}J(a) dadJ(a)是多少呢?换句话说,如果我们提高a的数值,对J的数值有什么影响?
假设a=5,我们让它增加到5.001,那么对v的影响就是a+u,所以v从11变为11.001,而J从33变为33.003。可以发现,a增加0.001,J增加了0.003。所以J的增量是3乘以a的增量,意味着这个导数 d d a J ( a ) = 3 \frac{d}{da}J(a)=3 dadJ(a)=3。
我们来解释这个计算过程。
其中一种方式是:如果你改变了a,那么也会改变v,通过改变v,也会改变J。当你提升a这个值0.001,J的变化量就是0.003。
a增加了,v也会增加,v增加多少呢?
这取决于
d
v
d
a
\frac{dv}{da}
dadv,然后v的变化导致J也在增加(
d
J
d
a
=
d
J
d
v
d
v
d
a
\frac{dJ}{da}=\frac{dJ}{dv}\frac{dv}{da}
dadJ=dvdJdadv),所以这在微积分里实际上叫链式法则。
如果a影响到v,v影响到J,那么当你让a变大时,J的变化量就是当你改变a时,v的变化量 乘以改变v时的 J变化量,这就是微积分里的链式法则。
上图表示了如何计算
d
J
d
a
\frac{dJ}{da}
dadJ。
d
J
d
v
\frac{dJ}{dv}
dvdJ就是J对变量v的导数(第一步反向传播),它可以帮助你计算
d
J
d
a
\frac{dJ}{da}
dadJ,所以这是另一步反向传播计算。求导过程就是反向传播过程。
符号约定
在python中,我们约定用变量名dvar来表示导数。
例如:
d
J
d
a
\frac{dJ}{da}
dadJ,用da表示;
d
J
d
v
\frac{dJ}{dv}
dvdJ,用dv表示。
到目前为止,我们通过这个流程图完成部分的后向传播算法。
如上图,类似da,我们还有一个反向传播过程,可以计算出du=3。这里的du就是 d J d u \frac{dJ}{du} dudJ。
最后我们要得到
d
J
d
b
\frac{dJ}{db}
dbdJ。根据链式法则
d
J
d
b
=
d
J
d
u
d
u
d
b
\frac{dJ}{db}=\frac{dJ}{du}\frac{du}{db}
dbdJ=dudJdbdu。那么
d
u
d
b
\frac{du}{db}
dbdu是多少呢?
b=3,u=6;如果b=3.001,那么u=6.002。所以
d
u
d
b
=
2
\frac{du}{db}=2
dbdu=2,即u的变化量是b的变化量的2倍。
所以,
d
J
d
b
=
3
∗
2
=
6
\frac{dJ}{db}=3*2=6
dbdJ=3∗2=6
简单观察一下就可以发现,如果b=3.001,那么u=6.002,v变成了11.002,J变成了33.006。这也再次证明了 d J d b = 6 \frac{dJ}{db}=6 dbdJ=6。
最后计算J对c的导数, d c ( p y t h o n ) = d J d c = d J d u d u d c = 3 ∗ 3 = 9 dc(python)=\frac{dJ}{dc}=\frac{dJ}{du}\frac{du}{dc}=3*3=9 dc(python)=dcdJ=dudJdcdu=3∗3=9
结论:在本例中,当计算所有这些导数时,最有效率的办法是从右到左计算,跟着这个红色箭头走。
总结
- 在计算流程图中,正向或者说从左到右的计算来计算成本函数J。
- 优化成本函数J,就是反向从右到左计算导数。
2.9 logistic 回归中的梯度下降法 Logistic Regression Gradient descent - 反向传播
本节中将使用计算图对梯度下降算法进行计算。
通过计算偏导数来实现逻辑回归的梯度下降算法的关键点是几个重要公式。
逻辑回归和损失函数的公式如上图。
假设单个样本有两个特征x1和x2,为了计算z,我们需要输入参数w1、w2 和b。
z
=
w
1
∗
x
1
+
w
2
∗
x
2
+
b
z=w_1*x_1+w_2*x_2+b
z=w1∗x1+w2∗x2+b
然后到下一步公式
y
^
=
a
=
σ
(
z
)
\hat y=a=\sigma(z)
y^=a=σ(z)
最后一步损失函数公式
L
(
a
,
y
)
.
.
.
省
略
L(a,y)...省略
L(a,y)...省略
其中a是逻辑回归的输出,y是样本的标签值。这里的步骤就是在单个训练样本上计算代价函数的前向步骤。
上面的逻辑回归公式可以发现,仅仅变化w和b参数就可以最小化损失函数J。现在让我们来讨论通过反向计算出导数。
因为我们想要计算出的代价函数L(a,y)的导数,首先我们需要反向计算出代价函数L(a,y)关于a的导数,在编写python代码时,你只需要用"da"来表示
d
L
(
a
,
y
)
d
a
\frac{dL(a,y)}{da}
dadL(a,y)。而这个导数计算结果是
"
d
a
"
=
d
L
(
a
,
y
)
d
a
=
−
y
a
+
1
−
y
1
−
a
"da"=\frac{dL(a,y)}{da}=-\frac ya+\frac{1-y}{1-a}
"da"=dadL(a,y)=−ay+1−a1−y
计算出"da",关于变量a的导数后,现在可以再反向一步计算"dz"。
"
d
z
"
=
d
L
d
z
=
d
L
d
a
d
a
d
z
=
(
−
y
a
+
1
−
y
1
−
a
)
∗
a
(
1
−
a
)
=
a
−
y
"dz"=\frac{dL}{dz}=\frac{dL}{da}\frac{da}{dz}=(-\frac ya+\frac{1-y}{1-a})*a(1-a)=a-y
"dz"=dzdL=dadLdzda=(−ay+1−a1−y)∗a(1−a)=a−y
以上的推导过程就是前面介绍过的链式法则。
现在进行最后一步反向推导,也就是计算w和b变化对代价函数L的影响。
"
d
w
1
"
=
d
L
d
w
1
=
x
1
“
d
z
”
"dw_1"=\frac{dL}{dw_1}=x_1“dz”
"dw1"=dw1dL=x1“dz”
"
d
w
2
"
=
d
L
d
w
2
=
x
2
“
d
z
”
"dw_2"=\frac{dL}{dw_2}=x_2“dz”
"dw2"=dw2dL=x2“dz”
"
d
b
"
=
d
L
d
b
=
“
d
z
”
"db"=\frac{dL}{db}=“dz”
"db"=dbdL=“dz”
因此,关于单个样本的梯度下降算法,你所需要做的就是如下的事情,先求曲线L的偏导数:
- 使用公式 " d z " = a − y "dz"=a-y "dz"=a−y计算"dz“
- 使用 " d w 1 " = d L d w 1 = x 1 “ d z ” "dw_1"=\frac{dL}{dw_1}=x_1“dz” "dw1"=dw1dL=x1“dz”计算"dw1"
- 使用 " d w 2 " = d L d w 2 = x 2 “ d z ” "dw_2"=\frac{dL}{dw_2}=x_2“dz” "dw2"=dw2dL=x2“dz”计算"dw2"
- 使用 " d b " = d L d b = “ d z “ "db"=\frac{dL}{db}=“dz“ "db"=dbdL=“dz“计算”db“
然后更新参数,实现梯度下降
- 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"
以上就是关于单个样本实例的梯度下降算法中参数更新一次的步骤。
现在你已经知道了怎样计算导数,并且实现针对单个训练样本的逻辑回归的梯度下降算法。
2.10 m 个样本的梯度下降 Gradient descent on m examples
本节我们要应用梯度下降在逻辑回归的m个训练样本上。
m个样本上成本函数定义复习如下
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
L
(
a
(
i
)
,
y
(
i
)
)
J(w,b)= \frac1m\sum_{i=1}^m L(a^{(i)},y^{(i)})
J(w,b)=m1i=1∑mL(a(i),y(i))
a
(
i
)
a^{(i)}
a(i)是训练样本y的预测值,前文中是用
y
^
\hat y
y^表示。
a
(
i
)
=
y
^
(
i
)
=
σ
(
z
(
i
)
)
=
σ
(
w
T
x
(
i
)
+
b
)
a^{(i)}=\hat y^{(i)}=\sigma (z^{(i)})=\sigma(w^Tx^{(i)}+b)
a(i)=y^(i)=σ(z(i))=σ(wTx(i)+b)
上一节中,我们已经知道单个样本的梯度下降第一步是获得"dw1",“dw2"和"db”。我们在"dw1","dw2"和"db"添上上标i表示你求得的相应的值,适用于单个样本(x(i),y(i)) 。
我们已经知道带有求和的全局损失(代价)函数J(w,b),实际上是1到m项各个损失 L(a(i),y(i))的平均。 所以它表明全局损失(代价)函数对w1的导数(微分)也同样是各项损失对w1导数(微分)的平均。
∂
∂
w
1
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
∂
∂
w
1
L
(
a
(
i
)
,
y
(
i
)
)
=
1
m
∑
i
=
1
m
(
"
d
w
1
(
i
)
"
)
\frac∂{∂w_1}J(w,b)= \frac1m\sum_{i=1}^m \frac∂{∂w_1}L(a^{(i)},y^{(i)})= \frac1m\sum_{i=1}^m("dw_1^{(i)}")
∂w1∂J(w,b)=m1i=1∑m∂w1∂L(a(i),y(i))=m1i=1∑m("dw1(i)")
之前我们已经演示了如何计算这项"dw_1",即上一节中演示的如何对单个训练样本进行计算。所以你现在真正需要做的是计算这些微分"dw1(i)","dw2(i)“和"db(i)”,如我们在之前的训练样本上做的,并且求平均,这会给你全局梯度值,你能够把它直接应用到梯度下降算法中。
逻辑回归和梯度下降算法实现
初始化J=0,dw1=0,dw2=0,db=0。
我们要做的就是用一个for循环遍历计算每个训练样本的导数,然后把它们加起来。
for i = 1 to m
z(i) = wx(i)+b;
a(i) = sigmoid(z(i)); #预测值
J += -[y(i)log(a(i))+(1-y(i))log(1-a(i)); #损失函数
dz(i) = a(i)-y(i);#导数,z变化对J的影响。注意:这里有上标i,是单个样本,即第i个样本的计算。
dw1 += x1(i)dz(i);#导数,参数w1变化对J的影响。注意:这里没有上标i,是累加整个训练集合上的和。
dw2 += x2(i)dz(i);#导数,参数w2变化对J的影响。注意:这里没有上标i,是累加整个训练集合上的和。
db += dz(i);#导数,参数b变化对J的影响。注意:这里没有上标i,是累加整个训练集合上的和。
J/= m;#m个样本平均
dw1/= m;#m个样本平均
dw2/= m;#m个样本平均
db/= m;#m个样本平均
w=w-alpha*dw #应用梯度单步下降
b=b-alpha*db #应用梯度单步下降
上面的代码只应用了一步梯度下降。因此你需要重复以上内容很多次,以应用多次梯度下降。
这种计算中有两个缺点,因为此方法在逻辑回归上你需要编写两个for循环。
- 第一个for循环是一个小循环遍历个训练样本
- 第二个for循环是一个遍历所有特征的for循环。上面例子中我们只有2个特征,如果你有更多特征,你开始计算你的dw1,dw2,dw3等等,这就需要一个for循环遍历所有的特征。
在代码中显式地使用for循环会使你的算法很低效。在深度学习领域会有越来越大的数据集,能够应用你的算法且没有显式的for循环会是重要的,并且会帮助你适用于更大的数据集。
向量化技术可以允许你的代码摆脱这些显式的for循环。
在深度学习时代利用向量化技术,摆脱for循环已经变得相当重要。因为我们越来越多地训练非常大的数据集,因此你真的需要你的代码变得非常高效。