欢迎关注WX公众号:【程序员管小亮】
声明
1)该文章整理自网上的大牛和机器学习专家无私奉献的资料,具体引用的资料请看参考文献。 2)本文仅供学术交流,非商用。因此每一部分具体的参考资料并无详细对应。若是某部分不当心侵犯了你们的利益,还望海涵,并联系博主删除。 3)博主才疏学浅,文中若有不当之处,请各位指出,共同进步,谢谢。 4)此属于初版本,如有错误,还需继续修正与增删。还望你们多多指点。你们都共享一点点,一块儿为祖国科研的推动添砖加瓦。html
深度学习入门笔记(四):向量化
一、向量化
向量化 是很是基础的去除代码中 for 循环的艺术。为何要去除 for 循环?python
当在深度学习安全领域、深度学习实践中应用深度学习算法时,会发如今代码中显式地使用 for 循环使算法很低效,同时在深度学习领域会有愈来愈大的数据集,由于深度学习算法处理大数据集效果很棒,因此代码运行速度很是重要,不然若是在大数据集上,代码可能花费很长时间去运行,你将要等待很是长的时间去获得结果。因此算法能应用且没有显式的 for 循环是很重要的,而且会帮助你适用于更大的数据集。因此在深度学习领域这里有一项叫作向量化的技术,是一个关键的技巧,它能够容许你的代码摆脱这些显式的 for 循环,举个栗子说明什么是向量化。程序员
在逻辑回归中,须要去计算
z
=
w
T
x
+
b
z={{w}^{T}}x+b
z = w T x + b ,其中
w
w
w 、
x
x
x 都是列向量。若是有不少的特征,那么就会有一个很是大的向量,因此
w
∈
R
n
x
w\in {{\mathbb{R}}^{{{n}_{x}}}}
w ∈ R n x ,
x
∈
R
n
x
x\in{{\mathbb{R}}^{{{n}_{x}}}}
x ∈ R n x ,那么若是想使用非向量化方法去计算
w
T
x
{{w}^{T}}x
w T x ,就须要用以下方式(基于 python 编程实现):web
z = 0
for i in range ( n_x) :
z += w[ i] * x[ i]
z += b
这是一个非向量化的实现,实践以后,你会发现这个是真的很慢,,,做为对比,向量化的实现将会很是直接计算
w
T
x
{{w}^{T}}x
w T x ,代码以下:算法
z = np. dot( w, x) + b
这是向量化方式进行计算
w
T
x
{{w}^{T}}x
w T x 的方法,你会发现这个很是快,尤为是对比以前的非向量化的实现。编程
让咱们用一个小例子说明一下,在个人我将会写一些代码(如下为教授在他的Jupyter notebook 上写的Python 代码,)数组
import time
import numpy as np
a = np. array( [ 1 , 2 , 3 , 4 ] )
print ( a)
a = np. random. rand( 1000000 )
b = np. random. rand( 1000000 )
tic = time. time( )
c = np. dot( a, b)
toc = time. time( )
print ( "Vectorized version:" + str ( 1000 * ( toc - tic) ) + "ms" )
c = 0
tic = time. time( )
for i in range ( 1000000 ) :
c += a[ i] * b[ i]
toc = time. time( )
print ( c)
print ( "For loop:" + str ( 1000 * ( toc - tic) ) + "ms" )
运行结果见下图: 在上面的代码中,使用两个方法——向量化和非向量化,计算了相同的值,其中向量化版本花费了0.968毫秒,而非向量化版本的 for 循环花费了327.997毫秒,大概是300多倍,准确倍数是 338.840 倍。仅仅在这个本身举的例子中,均可以明显看到效果。这意味着若是向量化方法须要花费一分钟去运行的数据,使用 for 循环将会花费5个小时去运行。安全
一句话总结,向量化快 !!!网络
二、深刻理解向量化
经过 numpy内置函数 和 避开显式的循环(loop) 的方式进行向量化,从而有效提升代码速度。根据经验,在写神经网络程序时,或者在写 逻辑(logistic)回归 时,或者在写其余神经网络模型时,应该避免写 循环(loop) 语句。虽然有时写 循环(loop) 是不可避免的,可是若是可使用其余办法去替代计算,程序效率老是更快。app
来看另一个例子。若是想计算向量
u
=
A
v
u=Av
u = A v ,这时根据矩阵乘法的定义,有
u
i
=
∑
j
A
ij
v
i
u_{i} =\sum_{j}^{}{A_{\text{ij}}v_{i}}
u i = ∑ j A ij v i 。
非向量化方法:用
u
=
n
p
.
z
e
r
o
s
(
n
,
1
)
u=np.zeros(n,1)
u = n p . z e r o s ( n , 1 ) , 而后经过两层循环
f
o
r
(
i
)
:
f
o
r
(
j
)
:
for(i): for(j):
f o r ( i ) : f o r ( j ) : ,能够获得:
u
[
i
]
=
u
[
i
]
+
A
[
i
]
[
j
]
∗
v
[
j
]
u[i]=u[i]+A[i][j]*v[j]
u [ i ] = u [ i ] + A [ i ] [ j ] ∗ v [ j ]
向量化方法:用
u
=
n
p
.
d
o
t
(
A
,
v
)
u=np.dot(A,v)
u = n p . d o t ( A , v )
吴恩达老师手写稿以下:
下面经过另外一个例子继续了解向量化。若是有一个向量
v
v
v ,而且想要对向量
v
v
v 的每一个元素作指数操做。
非向量化方法:初始化向量
u
=
n
p
.
z
e
r
o
s
(
n
,
1
)
u=np.zeros(n,1)
u = n p . z e r o s ( n , 1 ) ,而后经过循环依次计算每一个元素
v
i
v^{i}
v i
向量化方法:经过 python 的 numpy 内置函数,执行
u
=
n
p
.
e
x
p
(
v
)
u=np.exp(v)
u = n p . e x p ( v ) 命令
numpy 库有不少向量函数,好比 u=np.log
是按元素计算对数函数(
l
o
g
log
l o g )、 np.abs()
是按元素计算数据的绝对值函数、np.maximum(v, 0)
是按元素计算
v
v
v 中每一个元素和和0相比的最大值,v**2
是按元素计算元素
v
v
v 中每一个值的平方、 1/v
是按元素计算
v
v
v 中每一个元素的倒数等等。
PS :当想写循环时,检查 numpy 是否存在相似的内置函数。
吴恩达老师手写稿以下: 但愿你如今有一点向量化的感受了,减小一层循环可使代码更快一些!!!
三、向量化逻辑回归
如何实现逻辑回归的向量化计算?只要实现了,就能处理整个数据集了,甚至不会用一个明确的 for 循环,听起来是否是特别地 inspiring。
先回顾一下逻辑回归的前向传播,现有
m
m
m 个训练样本,而后对第一个样本进行预测,
z
(
1
)
=
w
T
x
(
1
)
+
b
z^{(1)}=w^{T}x^{(1)}+b
z ( 1 ) = w T x ( 1 ) + b ;激活函数
a
(
1
)
=
σ
(
z
(
1
)
)
a^{(1)}=\sigma (z^{(1)})
a ( 1 ) = σ ( z ( 1 ) ) ;计算第一个样本的预测值
y
y
y 。而后对第二个样本进行预测,第三个样本,依次类推。。。若是有
m
m
m 个训练样本,可能须要这样重复作
m
m
m 次。可不能够不用任何一个明确的 for 循环?
首先,定义一个
n
x
n_x
n x 行
m
m
m 列的矩阵
X
X
X 做为训练输入(以下图中蓝色
X
X
X ),numpy 形式为
(
n
x
,
m
)
(n_{x}, m)
( n x , m ) 。
吴恩达老师手稿以下: 前向传播过程当中,如何计算
z
(
1
)
z^{(1)}
z ( 1 ) ,
z
(
2
)
z^{(2)}
z ( 2 ) , ……一直到
z
(
m
)
z^{(m)}
z ( m ) ?构建一个
1
×
m
1\times m
1 × m 的行向量用来存储
z
z
z ,这样可让全部的
z
z
z 值都同一时间内完成。实际上,只用了一行代码。即
[
z
(
1
)
,
z
(
2
)
,
.
.
.
,
z
(
m
)
]
=
w
T
X
+
[
b
,
b
,
.
.
.
,
b
]
=
[
w
T
x
(
1
)
+
b
,
w
T
x
(
2
)
+
b
.
.
.
w
T
x
(
m
)
+
b
]
[z^{(1)}, z^{(2)}, ..., z^{(m)}]=w^{T}X+[b, b, ..., b]=[w^{T}x^{(1)}+b,w^{T}x^{(2)}+b...w^{T}x^{(m)}+b]
[ z ( 1 ) , z ( 2 ) , . . . , z ( m ) ] = w T X + [ b , b , . . . , b ] = [ w T x ( 1 ) + b , w T x ( 2 ) + b . . . w T x ( m ) + b ] 。为何
w
w
w 要转置呢?
但愿你尽快熟悉矩阵乘法,由于矩阵乘法的要求中有一条是,两个矩阵相乘,左面矩阵的列数须要等于右面矩阵的行数,
X
X
X 也是
(
n
x
,
m
)
(n_{x}, m)
( n x , m ) ,
w
w
w 也是
(
n
x
,
1
)
(n_{x}, 1)
( n x , 1 ) ,而
w
T
w^T
w T 是
(
1
,
n
x
)
(1, n_{x})
( 1 , n x ) ,正好符合
w
T
X
+
b
w^T X + b
w T X + b 的公式,且保证了矩阵乘法的条件。其中
w
T
x
(
1
)
+
b
w^{T}x^{(1)}+b
w T x ( 1 ) + b 这是第一个元素,
w
T
x
(
2
)
+
b
w^{T}x^{(2)}+b
w T x ( 2 ) + b 这是第二个元素, …,
w
T
x
(
m
)
+
b
w^{T}x^{(m)}+b
w T x ( m ) + b 这是第
m
m
m 个元素。分别与
z
(
1
)
z^{(1)}
z ( 1 ) ,
z
(
2
)
z^{(2)}
z ( 2 ) , …对应。因此,
X
X
X 是一次得到的一次得到所有。
可是细心的你会发现,为了计算
w
T
X
+
[
b
,
b
,
.
.
.
,
b
]
w^{T}X+[b, b, ..., b]
w T X + [ b , b , . . . , b ] ,使用 numpy 命令
Z
=
n
p
.
d
o
t
(
w
.
T
,
X
)
+
b
Z=np.dot(w.T,X)+b
Z = n p . d o t ( w . T , X ) + b 。这里有一个巧妙的地方,
n
p
.
d
o
t
(
w
.
T
,
X
)
np.dot(w.T,X)
n p . d o t ( w . T , X ) 是一个
1
×
m
1\times m
1 × m 的矩阵,而
b
b
b 是一个实数,或者能够说是一个
1
×
1
1\times 1
1 × 1 的矩阵,那么如何把一个向量加上一个实数?
这里简单说一下:Python 自动地把实数
b
b
b 扩展成一个
1
×
m
1\times m
1 × m 的行向量,只有这样才能进行矩阵相加(矩阵相加须要两个矩阵等大小)。这个操做彷佛有点难以想象,它在 Python 中被称做 广播(brosdcasting) ,目前你不用对此感到顾虑,这在博客——深度学习入门笔记(五):神经网络的编程基础中会详细讲解!
如今说一下字母规范:大写的
Z
Z
Z 是一个包含全部小写
z
(
1
)
z^{(1)}
z ( 1 ) 到
z
(
m
)
z^{(m)}
z ( m ) 的
1
×
m
1\times m
1 × m 的矩阵,而大写
A
A
A 则是包含全部小写
a
(
1
)
a^{(1)}
a ( 1 ) 到
a
(
m
)
a^{(m)}
a ( m ) 的
1
×
m
1\times m
1 × m 的矩阵。
简单小结一下,不要 for 循环,利用
m
m
m 个训练样本使用向量化的方法,一次性计算出
Z
Z
Z 和
A
A
A 。
Z
=
w
T
X
+
b
Z = w^TX + b
Z = w T X + b
A
=
σ
(
Z
)
A=\sigma (Z)
A = σ ( Z )
四、向量化逻辑回归的梯度输出
注:本节中大写字母表明向量,小写字母表明元素
如何 同时 计算
m
m
m 个数据的梯度,而且实现一个很是高效的 逻辑回归算法(Logistic Regression) ?
以前在讲梯度计算的时候(深度学习入门笔记(二):神经网络基础 ),列举过几个例子,
d
z
(
1
)
=
a
(
1
)
−
y
(
1
)
dz^{(1)}=a^{(1)}-y^{(1)}
d z ( 1 ) = a ( 1 ) − y ( 1 ) ,
d
z
(
2
)
=
a
(
2
)
−
y
(
2
)
dz^{(2)}=a^{(2)}-y^{(2)}
d z ( 2 ) = a ( 2 ) − y ( 2 ) , ……等等一系列相似公式。不过当时是单样本数据计算,如今对
m
m
m 个数据作一样的计算,能够照着上一章讲过的,定义一个新的变量
d
Z
=
[
d
z
(
1
)
,
d
z
(
2
)
.
.
.
d
z
(
m
)
]
dZ=[dz^{(1)} ,dz^{(2)} ... dz^{(m)}]
d Z = [ d z ( 1 ) , d z ( 2 ) . . . d z ( m ) ] ,每个样本的
d
z
dz
d z 横向排列,就能够获得一个
1
×
m
1\times m
1 × m 的
d
Z
dZ
d Z 矩阵了。
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
=
[
a
(
1
)
−
y
(
1
)
,
a
(
2
)
−
y
(
2
)
,
.
.
.
,
a
(
m
)
−
y
(
m
)
]
dZ=A-Y=[a^{(1)}-y^{(1)}, a^{(2)}-y^{(2)}, ..., a^{(m)}-y^{(m)}]
d Z = A − Y = [ a ( 1 ) − y ( 1 ) , a ( 2 ) − y ( 2 ) , . . . , a ( m ) − y ( m ) ] ,恰好分别对应
d
z
(
1
)
dz^{(1)}
d z ( 1 ) ,
d
z
(
2
)
dz^{(2)}
d z ( 2 ) ,……
开始向量化逻辑回归的梯度输出:
首先是
d
b
=
1
m
∑
i
=
1
m
d
z
(
i
)
db=\frac{1}{m}\sum_{i=1}^{m}dz^{(i)}
d b = m 1 i = 1 ∑ m d z ( i )
向量化代码以下:
d
b
=
1
m
∗
n
p
.
s
u
m
(
d
Z
)
db=\frac{1}{m}*np.sum(dZ)
d b = m 1 ∗ n p . s u m ( d Z )
接下来是
d
w
=
1
m
∗
X
∗
d
z
T
dw=\frac{1}{m}*X*dz^{T}
d w = m 1 ∗ X ∗ d z T
其中,
X
X
X 是一个行向量。所以展开后是
d
w
=
1
m
∗
(
x
(
1
)
d
z
(
1
)
+
x
(
2
)
d
z
(
2
)
+
.
.
.
+
x
m
d
z
m
)
dw=\frac{1}{m}*(x^{(1)}dz^{(1)}+x^{(2)}dz^{(2)}+...+x^{m}dz^{m})
d w = m 1 ∗ ( x ( 1 ) d z ( 1 ) + x ( 2 ) d z ( 2 ) + . . . + x m d z m )
向量化代码以下:
d
b
=
1
m
∗
n
p
.
s
u
m
(
d
Z
)
db=\frac{1}{m}*np.sum(dZ)
d b = m 1 ∗ n p . s u m ( d Z )
d
w
=
1
m
∗
X
∗
d
z
T
dw=\frac{1}{m}*X*dz^{T}
d w = m 1 ∗ X ∗ d z T
这样,就避免了在训练集上使用 for 循环。对比以前实现的逻辑回归,能够发现,没有向量化是很是低效的,代码量还多。。。
翻新后的计算以下:
Z
=
w
T
X
+
b
=
n
p
.
d
o
t
(
w
.
T
,
X
)
+
b
Z = w^{T}X + b = np.dot( w.T,X)+b
Z = w T X + b = n p . d o t ( w . T , X ) + b
A
=
σ
(
Z
)
A = \sigma( Z )
A = σ ( Z )
d
Z
=
A
−
Y
dZ = A - Y
d Z = A − Y
d
w
=
1
m
∗
X
∗
d
z
T
{{dw} = \frac{1}{m}*X*dz^{T}\ }
d w = m 1 ∗ X ∗ d z T
d
b
=
1
m
∗
n
p
.
s
u
m
(
d
Z
)
db= \frac{1}{m}*np.sum( dZ)
d b = m 1 ∗ n p . s u m ( d Z )
w
:
=
w
−
a
∗
d
w
w: = w - a*dw
w : = w − a ∗ d w
b
:
=
b
−
a
∗
d
b
b: = b - a*db
b : = b − a ∗ d b
前五个公式完成了前向和后向传播,后两个公式进行梯度降低更新参数。
最后的最后,终于获得了一个高度向量化的、很是高效的逻辑回归的梯度降低算法,是否是?
推荐阅读
参考文章