课程信息:网易微专业:《深度学习和神经网络》
课程来源:https://mooc.study.163.com/course/2001281002
假设有以下一张图片,要判断其输出是否为猫,若是,则可以用
y
=
1
y = 1
y=1表示,否则用
y
=
0
y = 0
y=0表示:
计算机保存一张图片通常使用3个独立矩阵,分别对应红、绿、蓝三个颜色通道。如果输入像素是64×64
,则会有3个64×64
的矩阵,即输入是一个3×64×64
的高维度数据,而输出是
y
=
0
o
r
1
y=0 \ or\ 1
y=0 or 1。
与机器学习之单变量线性回归一样,可以用 ( x , y ) (x,y) (x,y)表示一个训练样本,其中 x ∈ R n x \in R^n x∈Rn,而 y = { 0 , 1 } y = \{0,1\} y={0,1},通常用 { ( x ( 1 ) , y ( 1 ) ) , ( x ( 2 ) , y ( 2 ) ) … … ( x ( m ) , y ( m ) ) } \{(x^{(1)},y^{(1)}),(x^{(2)},y^{(2)})……(x^{(m)},y^{(m)}) \} {(x(1),y(1)),(x(2),y(2))……(x(m),y(m))}表示整个训练集,其中 m m m表示训练样本的大小,以上,由 m m m个训练样本可以组成输入矩阵 X X X,且 X ∈ R n × m X \in R^{n×m} X∈Rn×m, Y Y Y是一个输出矩阵,且 Y ∈ R 1 × m Y \in R^{1×m} Y∈R1×m。
逻辑回归
对于以上分类问题,给定输入 x x x,我们想知道 y y y的输出概率, y ^ \hat{y} y^的输出概率代表着该分类问题的结果, y ^ \hat{y} y^的值表示是猫的概率可以简单表示为 y ^ = p { y = 1 ∣ x } \hat{y} = p\{y=1|x\} y^=p{y=1∣x},且 0 ≤ y ^ ≤ 1 0 \le \hat{y} \le 1 0≤y^≤1。
对于线性回归,有
y
=
w
T
x
+
b
y = w^Tx+b
y=wTx+b,而对于逻辑回归,采用激活函数,即:
y
=
δ
(
w
T
x
+
b
)
y = \delta(w^Tx+b)
y=δ(wTx+b) 表示,令
z
=
w
T
x
+
b
z = w^Tx+b
z=wTx+b,则:
δ
(
z
)
=
1
1
+
e
−
z
\delta(z) = \frac{1}{1+e^{-z}}
δ(z)=1+e−z1,其函数图像如下所示:
当
δ
(
z
)
>
0.5
\delta(z) > 0.5
δ(z)>0.5,也就是
w
T
x
+
b
<
0
w^Tx+b < 0
wTx+b<0时,其输出
y
^
=
0
\hat{y} = 0
y^=0
而
δ
(
z
)
<
0.5
\delta(z) < 0.5
δ(z)<0.5,即也就是
w
T
x
+
b
<
0
w^Tx+b < 0
wTx+b<0时,其输出
y
^
=
0
\hat{y} = 0
y^=0.
逻辑回归损失函数
对于逻辑回归问题,给定训练样本:
{
(
x
(
1
)
,
y
(
1
)
)
,
(
x
(
2
)
,
y
(
2
)
)
…
…
(
x
(
m
)
,
y
(
m
)
)
}
\{(x^{(1)},y^{(1)}),(x^{(2)},y^{(2)})……(x^{(m)},y^{(m)}) \}
{(x(1),y(1)),(x(2),y(2))……(x(m),y(m))},我们希望得到
y
^
≈
y
\hat{y} \approx y
y^≈y。逻辑回归的损失函数
L
(
y
^
,
y
)
L(\hat{y},y)
L(y^,y)可以由以下公式定义:
L ( y ^ , y ) = − y l o g ( y ^ ) − ( 1 − y ) l o g ( 1 − y ^ ) L(\hat{y},y) = -ylog(\hat{y}) -(1-y)log(1-\hat{y}) L(y^,y)=−ylog(y^)−(1−y)log(1−y^)
对于以上损失函数,有:
若
y
=
1
y = 1
y=1,则
L
(
y
,
y
^
)
=
−
y
l
o
g
(
y
^
)
L(y,\hat{y}) = -ylog(\hat{y})
L(y,y^)=−ylog(y^),而想让损失函数
L
(
y
,
y
^
)
L(y,\hat{y})
L(y,y^)尽可能小,意味着
y
^
\hat{y}
y^要尽可能大,又因为
0
≤
y
^
≤
1
0 \le\hat{y} \le 1
0≤y^≤1,所以
y
^
=
1
\hat{y} = 1
y^=1是,损失函数最小。
若 y = 0 y = 0 y=0,则 L ( y , y ^ ) = − l o g ( 1 − y ^ ) L(y,\hat{y}) = -log(1-\hat{y}) L(y,y^)=−log(1−y^),损失函数要取得最小值,意味着 y ^ \hat{y} y^需取得最大值,则需满足 y ^ = 0 \hat{y} = 0 y^=0。
以上损失函数只适用于单个样本,对于 m m m个样本的损失函数可以有如下定义:
J ( w , b ) = 1 m ∑ i = 1 m L ( y , y ^ ) = 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(y,\hat{y}) = \frac{1}{m}\sum_{i=1}^{m}-y^{(i)}log(\hat{y}^{(i)})-(1-y^{(i)})log(1-\hat{y}^{(i)}) J(w,b)=m1∑i=1mL(y,y^)=m1∑i=1m−y(i)log(y^(i))−(1−y(i))log(1−y^(i))
梯度下降法
对于以上损失函数,需要找到损失函数
J
(
w
,
b
)
J(w,b)
J(w,b)的最小值,最常用的算法就是梯度下降算法,即对于一个凸函数,总能通过梯度下降算法找到它的全局最优解,对于此损失函数的梯度下降算法,在机器学习之逻辑回归的算法介绍中已经做了较为详细的推导,在此不再过多叙述,梯度下降算法的简单实现步骤如下所示:
r
e
p
e
a
t
{
repeat \ \ \{
repeat {
w
:
=
w
−
α
∂
J
(
w
,
b
)
∂
w
w: = w- \alpha \frac{\partial J(w,b)}{\partial w}
w:=w−α∂w∂J(w,b)
}
\}
}
重复以上过程,直到损失函数收敛,以求得参数
w
w
w的值,其中,
α
\alpha
α代表学习率。
计算图
计算图介绍:
假设有一函数表达式为: 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 =3*v J=3∗v
对于以上三个步骤,用计算图可以有如下表示:
计算图的导数:
如上图所示,利用计算图从左向右的流程,一步步可以算出 J J J的值,那么,依靠从右向左的反向传播就可以算出 J J J对每个变量的导数,如下图所示:
其反向传播过程如图中红色箭头所示,根据导数定义以及链式计算法则,有如下计算:
∂ J ∂ v = 3 \frac{\partial J}{\partial v} = 3 ∂v∂J=3
∂ J ∂ u = ∂ J ∂ v ∂ v ∂ u = 3 × 1 = 3 \frac{\partial J}{\partial u} =\frac{\partial J}{\partial v} \frac{\partial v}{\partial u} = 3×1 = 3 ∂u∂J=∂v∂J∂u∂v=3×1=3
∂ J ∂ a = ∂ J ∂ v ∂ v ∂ a = 3 × 1 = 3 \frac{\partial J}{\partial a} =\frac{\partial J}{\partial v} \frac{\partial v}{\partial a} = 3×1 =3 ∂a∂J=∂v∂J∂a∂v=3×1=3
∂ J ∂ b = ∂ J ∂ v ∂ v ∂ u ∂ u ∂ b = 3 × 1 × 5 = 15 \frac{\partial J}{\partial b} =\frac{\partial J}{\partial v} \frac{\partial v}{\partial u} \frac{\partial u}{\partial b} = 3×1×5 =15 ∂b∂J=∂v∂J∂u∂v∂b∂u=3×1×5=15
∂ J ∂ c = ∂ J ∂ v ∂ v ∂ u ∂ u ∂ c = 3 × 1 × 4 = 12 \frac{\partial J}{\partial c} =\frac{\partial J}{\partial v} \frac{\partial v}{\partial u} \frac{\partial u}{\partial c} = 3×1×4 =12 ∂c∂J=∂v∂J∂u∂v∂c∂u=3×1×4=12
逻辑回归中的梯度下降算法
单个样本的逻辑回归梯度下降算法
关于逻辑回归的损失函数,有如下公式:
z
=
w
T
x
+
b
z= w^Tx+b
z=wTx+b
y
^
=
a
=
δ
(
z
)
\hat{y} = a = \delta(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)−(1−y)log(1−a)
假设有两个输入特征
x
1
,
x
2
x_1,x_2
x1,x2和两个参数
w
1
,
w
2
w_1,w_2
w1,w2,则用计算图(流程图)表示其计算过程如下所示:
依照其计算图中的反向传播过程和链式法则,其导数计算如下所示:
∂ L ( a , y ) ∂ a = − y a + 1 − y 1 − a \frac{\partial L(a,y)}{\partial a} = -\frac{y}{a}+\frac{1-y}{1-a} ∂a∂L(a,y)=−ay+1−a1−y
∂ L ( a , y ) ∂ z = ∂ L ∂ a ∂ a ∂ z = a − y \frac{\partial L(a,y)}{\partial z} = \frac{\partial L}{\partial a} \frac{\partial a}{\partial z} = a-y ∂z∂L(a,y)=∂a∂L∂z∂a=a−y
∂ L ( a , y ) ∂ w 1 = ∂ L ∂ a ∂ a ∂ z ∂ z ∂ w 1 = x 1 d z = x 1 ∗ ( a − y ) \frac{\partial L(a,y)}{\partial w_1} =\frac{\partial L}{\partial a} \frac{\partial a}{\partial z} \frac{\partial z}{\partial w_1} =x_1dz = x_1*(a-y) ∂w1∂L(a,y)=∂a∂L∂z∂a∂w1∂z=x1dz=x1∗(a−y)
∂ L ( a , y ) ∂ w 2 = ∂ L ∂ a ∂ a ∂ z ∂ z ∂ w 2 = x 2 d z = x 2 ∗ ( a − y ) \frac{\partial L(a,y)}{\partial w_2} =\frac{\partial L}{\partial a} \frac{\partial a}{\partial z} \frac{\partial z}{\partial w_2} = x_2dz = x_2*(a-y) ∂w2∂L(a,y)=∂a∂L∂z∂a∂w2∂z=x2dz=x2∗(a−y)
∂
L
(
a
,
y
)
∂
b
=
∂
L
∂
a
∂
a
∂
z
∂
z
∂
b
=
d
z
=
(
a
−
y
)
\frac{\partial L(a,y)}{\partial b} =\frac{\partial L}{\partial a} \frac{\partial a}{\partial z} \frac{\partial z}{\partial b} = dz = (a-y)
∂b∂L(a,y)=∂a∂L∂z∂a∂b∂z=dz=(a−y)
···
最后,参数
w
1
,
w
2
,
b
w_1,w_2,b
w1,w2,b的更新规律为:
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
其中,
α
\alpha
α表示学习率。
m m m个样本的逻辑回归
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)=m1∑i=1mL(a(i),y(i))
a ( i ) = y ^ ( i ) = δ ( z ( i ) ) = δ ( w T x ( i ) + b ) a^{(i)} = \hat{y}^{(i)} = \delta(z^{(i)}) = \delta(w^Tx^{(i)} +b) a(i)=y^(i)=δ(z(i))=δ(wTx(i)+b)
其梯度计算公式,可以有如下表示:
∂ J ( w , b ) ∂ w = 1 m ∑ i = 1 m ∂ ∂ w L ( a ( i ) , y ( i ) ) \frac{\partial J(w,b)}{\partial w} = \frac{1} {m}\sum_{i=1}^{m}\frac{\partial }{\partial w}L(a^{(i)},y^{(i)}) ∂w∂J(w,b)=m1∑i=1m∂w∂L(a(i),y(i))
在实际计算过程中,需要计算每一个样本的关于 w w w的梯度,最后求和取平均,在一个具体算法实现中,其为代码可以如下所示:
假设有2个特征向量,
m
m
m个样本,则有:
初始化:
J
=
0
,
d
w
1
=
0
,
d
w
2
=
0
J = 0, dw_1 = 0,dw_2 = 0
J=0,dw1=0,dw2=0
f
o
r
i
=
1
t
o
m
:
for\ \ \ i =1 \ \ to \ \ m:
for i=1 to m:
z ( i ) = w T x ( i ) + b ; \ \ \ \ \ \ \ z^{(i)} = w^Tx^{(i)} +b; z(i)=wTx(i)+b;
a ( i ) = δ ( z ( i ) ) ; \ \ \ \ \ \ \ a^{(i)} =\delta(z^{(i)}); a(i)=δ(z(i));
J + = − y ( i ) l o g ( a ( i ) ) − ( 1 − y ( i ) ) l o g ( 1 − a ( i ) ) ; \ \ \ \ \ \ \ J+ = -y^{(i)}log(a^{(i)})-(1-y^{(i)})log(1-a^{(i)}); J+=−y(i)log(a(i))−(1−y(i))log(1−a(i));
d z ( i ) = a ( i ) − y ( i ) ; \ \ \ \ \ \ \ dz^{(i)} =a^{(i)} - y^{(i)}; dz(i)=a(i)−y(i);
d w 1 + = x 1 ∗ d z ( i ) ; \ \ \ \ \ \ \ dw_1 +=x_1*dz^{(i)}; dw1+=x1∗dz(i);
d w 2 + = x 2 ∗ d z ( i ) ; \ \ \ \ \ \ \ dw_2 +=x_2*dz^{(i)}; dw2+=x2∗dz(i);
d b + = d z ( i ) ; \ \ \ \ \ \ \ db \ +=dz^{(i)}; db +=dz(i);
J / = m , d w 1 / = m , d w 2 / = m , d b / = m ; J/=m,dw_1/=m,dw_2/=m,db/=m; J/=m,dw1/=m,dw2/=m,db/=m;
以上,是应用一次梯度下降的过程,应用多次梯度下降算法之后,其参数的更新如下所示:
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
注意:以上,算法实现过程中,有两个特征和参数,分别是 x 1 , x 2 x_1,x_2 x1,x2和 w 1 , w 2 w_1,w_2 w1,w2,当有 n n n个特征和参数时,可以利用循环完成。
向量化
向量化的简单示例:
如以上算法表示,通过for
循环来遍历
m
m
m个样本和
n
n
n个特征,当在整个算法运行过程中,需要考虑运行时间的问题,当样本数量和特征足够大时,采用传统的for
循环不是一个明智的选择,为了减少算法运行时间,特地引入了向量化的实现。
将一以下代码作为示例:
import numpy as np
import random
import time
a = np.random.rand(1000000)
b = np.random.rand(1000000)
ts = time.time()
c = np.dot(a,b)
te = time.time()
print(c)
print("向量化的代码实现花费时间:"+str((te-ts)*1000)+" ms")
c = 0
ts = time.time()
for i in range(1000000):
c += a[i]*b[i]
te = time.time()
print(c)
print("for循环代码实现花费时间:"+str((te-ts)*1000)+" ms")
如上所示,同样实现两个数组(向量)相乘的过程,对于百万级别的数据,for
循环的实现方式所花费的时间差不多是向量化的400倍左右,向量化的实现可以简单的理解为是一个并行的过程,而for
循环可以简单理解为串行的过程,所以通过向量化的实现,大大节省了运行程序所耗费的时间。在算法实现过程中,应该尽量避免使用for
循环。
用向量化实现逻辑回归:
对于逻辑回归的算法,需要考虑输入向量
X
X
X和权重参数
W
W
W,其中,
X
∈
R
n
×
m
X \in R^{n×m}
X∈Rn×m,
W
∈
R
n
×
1
W \in R^{n×1}
W∈Rn×1,而根据矩阵乘法运算法则和逻辑回归的实现原理,有:
[
z
1
,
z
2
,
…
…
z
m
]
=
[
W
T
X
]
+
[
b
1
,
b
2
,
…
…
b
m
]
[z_1,z_2,……z_m] = [W^TX]+[b_1,b_2,……b_m]
[z1,z2,……zm]=[WTX]+[b1,b2,……bm]
在python
中用numpy
库,可以简单的用以下一行代码实现(一般认为
b
b
b是一个R^{1×1}的偏置常量):
z = np.dot(W.T,x)+b
根据之前的学习,对于逻辑回归利用反向传播算法计算导数,有:
d z ( i ) = a ( i ) − y ( i ) ; \ \ \ \ \ \ \ dz^{(i)} =a^{(i)} - y^{(i)}; dz(i)=a(i)−y(i);
d w 1 + = x 1 ∗ d z ( i ) ; \ \ \ \ \ \ \ dw_1 +=x_1*dz^{(i)}; dw1+=x1∗dz(i);
d w 2 + = x 2 ∗ d z ( i ) ; \ \ \ \ \ \ \ dw_2 +=x_2*dz^{(i)}; dw2+=x2∗dz(i);
. . . \ \ \ \ \ \ \ ... ...
d w n + = x n ∗ d z ( i ) ; \ \ \ \ \ \ \ dw_n +=x_n*dz^{(i)}; dwn+=xn∗dz(i);
d b + = d z ( i ) ; \ \ \ \ \ \ \ db \ +=dz^{(i)}; db +=dz(i);
J / = m , d w 1 / = m , d w 2 / = m , d b / = m ; J/=m,dw_1/=m,dw_2/=m,db/=m; J/=m,dw1/=m,dw2/=m,db/=m;
对于以上,公式,有如下定义:
d
Z
=
[
d
z
(
1
)
,
d
z
(
2
)
…
…
d
z
(
m
)
]
dZ = [dz^{(1)},dz^{(2)}……dz^{(m)}]
dZ=[dz(1),dz(2)……dz(m)]
A
=
[
a
(
1
)
,
a
(
2
)
,
…
…
a
(
m
)
]
A = [a^{(1)},a^{(2)},……a^{(m)}]
A=[a(1),a(2),……a(m)]
Y
=
[
y
(
1
)
,
y
(
2
)
…
…
y
(
m
)
]
Y = [y^{(1)},y^{(2)}……y^{(m)}]
Y=[y(1),y(2)……y(m)]
d
Z
=
[
A
−
Y
]
dZ = [A - Y]
dZ=[A−Y]
对于以上过程,摈弃传统的for
循环实现,采用向量化的实现方式可以简单表示为:
dw = np.dot(X,dZ^T)
db = 1/m*np.sum(dZ)
综合以上所有向量化的实现,可以得到利用python
实现的一个高度向量化的逻辑回归梯度下降算法(a代表学习率):
Z = np.dot(W^T,X)+b
A = np.exp(Z)
dZ = A-Y
dw = np.dot(X,dZ^T)
db = 1/m*np.sum(dZ)
w = w-a*dw
b = b-a*db
以上,只是实现一次梯度下降的伪代码,在实际算法运行过程中,我们仍然需要利用循环实现多次梯度下降。