深度学习入门笔记(四):向量化
歡迎關注WX公眾號:【程序員管小亮】
專欄——深度學習入門筆記
聲明
1)該文章整理自網上的大牛和機器學習專家無私奉獻的資料,具體引用的資料請看參考文獻。
2)本文僅供學術交流,非商用。所以每一部分具體的參考資料并沒有詳細對應。如果某部分不小心侵犯了大家的利益,還望海涵,并聯系博主刪除。
3)博主才疏學淺,文中如有不當之處,請各位指出,共同進步,謝謝。
4)此屬于第一版本,若有錯誤,還需繼續修正與增刪。還望大家多多指點。大家都共享一點點,一起為祖國科研的推進添磚加瓦。
文章目錄
- 歡迎關注WX公眾號:【程序員管小亮】
- 專欄——深度學習入門筆記
- 聲明
- 深度學習入門筆記(四):向量化
- 1、向量化
- 2、深入理解向量化
- 3、向量化邏輯回歸
- 4、向量化邏輯回歸的梯度輸出
- 推薦閱讀
- 參考文章
深度學習入門筆記(四):向量化
1、向量化
向量化 是非常基礎的去除代碼中 for 循環的藝術。為什么要去除 for 循環?
當在深度學習安全領域、深度學習實踐中應用深度學習算法時,會發現在代碼中顯式地使用 for 循環使算法很低效,同時在深度學習領域會有越來越大的數據集,因為深度學習算法處理大數據集效果很棒,所以代碼運行速度非常重要,否則如果在大數據集上,代碼可能花費很長時間去運行,你將要等待非常長的時間去得到結果。所以算法能應用且沒有顯式的 for 循環是很重要的,并且會幫助你適用于更大的數據集。所以在深度學習領域這里有一項叫做向量化的技術,是一個關鍵的技巧,它可以允許你的代碼擺脫這些顯式的 for 循環,舉個栗子說明什么是向量化。
在邏輯回歸中,需要去計算 z=wTx+bz={{w}^{T}}x+bz=wTx+b,其中 www、xxx 都是列向量。如果有很多的特征,那么就會有一個非常大的向量,所以 w∈Rnxw\in {{\mathbb{R}}^{{{n}_{x}}}}w∈Rnx? , x∈Rnxx\in{{\mathbb{R}}^{{{n}_{x}}}}x∈Rnx?,那么如果想使用非向量化方法去計算 wTx{{w}^{T}}xwTx,就需要用如下方式(基于 python 編程實現):
z = 0 for i in range(n_x):z += w[i] * x[i] z += b這是一個非向量化的實現,實踐之后,你會發現這個是真的很慢,,,作為對比,向量化的實現將會非常直接計算 wTx{{w}^{T}}xwTx,代碼如下:
z = np.dot(w, x) + b這是向量化方式進行計算 wTx{{w}^{T}}xwTx 的方法,你會發現這個非常快,尤其是對比之前的非向量化的實現。
讓我們用一個小例子說明一下,在我的我將會寫一些代碼(以下為教授在他的Jupyter notebook上寫的Python代碼,)
import time # 導入時間庫 import numpy as np # 導入numpy庫a = np.array([1, 2, 3, 4]) # 創建一個數據a print(a) # [1 2 3 4]a = np.random.rand(1000000) b = np.random.rand(1000000) # 通過round隨機得到兩個一百萬維度的數組 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") # 打印for循環的版本的時間運行結果見下圖:
在上面的代碼中,使用兩個方法——向量化和非向量化,計算了相同的值,其中向量化版本花費了0.968毫秒,而非向量化版本的 for 循環花費了327.997毫秒,大概是300多倍,準確倍數是 338.840 倍。僅僅在這個自己舉的例子中,都可以明顯看到效果。這意味著如果向量化方法需要花費一分鐘去運行的數據,使用 for 循環將會花費5個小時去運行。
一句話總結,向量化快!!!
2、深入理解向量化
通過 numpy內置函數 和 避開顯式的循環(loop) 的方式進行向量化,從而有效提高代碼速度。根據經驗,在寫神經網絡程序時,或者在寫 邏輯(logistic)回歸 時,或者在寫其他神經網絡模型時,應該避免寫 循環(loop) 語句。雖然有時寫 循環(loop) 是不可避免的,但是如果可以使用其他辦法去替代計算,程序效率總是更快。
來看另外一個例子。如果想計算向量 u=Avu=Avu=Av,這時根據矩陣乘法的定義,有 ui=∑jAijviu_{i} =\sum_{j}^{}{A_{\text{ij}}v_{i}}ui?=∑j?Aij?vi?。
- 非向量化方法:用 u=np.zeros(n,1)u=np.zeros(n,1)u=np.zeros(n,1), 然后通過兩層循環 for(i):for(j):for(i): for(j):for(i):for(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=np.dot(A,v)u=np.dot(A,v)u=np.dot(A,v)
吳恩達老師手寫稿如下:
下面通過另一個例子繼續了解向量化。如果有一個向量 vvv,并且想要對向量 vvv 的每個元素做指數操作。
- 非向量化方法:初始化向量 u=np.zeros(n,1)u=np.zeros(n,1)u=np.zeros(n,1),然后通過循環依次計算每個元素 viv^{i}vi
- 向量化方法:通過 python 的 numpy 內置函數,執行 u=np.exp(v)u=np.exp(v)u=np.exp(v) 命令
numpy 庫有很多向量函數,比如 u=np.log 是按元素計算對數函數(logloglog)、 np.abs() 是按元素計算數據的絕對值函數、np.maximum(v, 0) 是按元素計算 vvv 中每個元素和和0相比的最大值,v**2 是按元素計算元素 vvv 中每個值的平方、 1/v 是按元素計算 vvv 中每個元素的倒數等等。
PS:當想寫循環時,檢查 numpy 是否存在類似的內置函數。
吳恩達老師手寫稿如下:
希望你現在有一點向量化的感覺了,減少一層循環可以使代碼更快一些!!!
3、向量化邏輯回歸
如何實現邏輯回歸的向量化計算?只要實現了,就能處理整個數據集了,甚至不會用一個明確的 for 循環,聽起來是不是特別地 inspiring。
先回顧一下邏輯回歸的前向傳播,現有 mmm 個訓練樣本,然后對第一個樣本進行預測,z(1)=wTx(1)+bz^{(1)}=w^{T}x^{(1)}+bz(1)=wTx(1)+b;激活函數 a(1)=σ(z(1))a^{(1)}=\sigma (z^{(1)})a(1)=σ(z(1));計算第一個樣本的預測值 yyy。然后對第二個樣本進行預測,第三個樣本,依次類推。。。如果有 mmm 個訓練樣本,可能需要這樣重復做 mmm 次。可不可以不用任何一個明確的 for 循環?
首先,定義一個 nxn_xnx? 行 mmm 列的矩陣 XXX 作為訓練輸入(如下圖中藍色 XXX),numpy 形式為 (nx,m)(n_{x}, m)(nx?,m)。
吳恩達老師手稿如下:
前向傳播過程中,如何計算 z(1)z^{(1)}z(1), z(2)z^{(2)}z(2), ……一直到 z(m)z^{(m)}z(m)?構建一個 1×m1\times m1×m 的行向量用來存儲 zzz,這樣可以讓所有的 zzz 值都同一時間內完成。實際上,只用了一行代碼。即 [z(1),z(2),...,z(m)]=wTX+[b,b,...,b]=[wTx(1)+b,wTx(2)+b...wTx(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)]=wTX+[b,b,...,b]=[wTx(1)+b,wTx(2)+b...wTx(m)+b]。為什么 www 要轉置呢?
希望你盡快熟悉矩陣乘法,因為矩陣乘法的要求中有一條是,兩個矩陣相乘,左面矩陣的列數需要等于右面矩陣的行數,XXX 也是 (nx,m)(n_{x}, m)(nx?,m),www 也是 (nx,1)(n_{x}, 1)(nx?,1),而 wTw^TwT 是 (1,nx)(1, n_{x})(1,nx?),正好符合 wTX+bw^T X + bwTX+b 的公式,且保證了矩陣乘法的條件。其中 wTx(1)+bw^{T}x^{(1)}+bwTx(1)+b 這是第一個元素,wTx(2)+bw^{T}x^{(2)}+bwTx(2)+b 這是第二個元素, …, wTx(m)+bw^{T}x^{(m)}+bwTx(m)+b 這是第 mmm 個元素。分別與 z(1)z^{(1)}z(1), z(2)z^{(2)}z(2), …對應。所以,XXX 是一次獲得的一次獲得全部。
但是細心的你會發現,為了計算 wTX+[b,b,...,b]w^{T}X+[b, b, ..., b]wTX+[b,b,...,b],使用 numpy 命令 Z=np.dot(w.T,X)+bZ=np.dot(w.T,X)+bZ=np.dot(w.T,X)+b。這里有一個巧妙的地方,np.dot(w.T,X)np.dot(w.T,X)np.dot(w.T,X) 是一個1×m1\times m1×m 的矩陣,而 bbb 是一個實數,或者可以說是一個 1×11\times 11×1 的矩陣,那么如何把一個向量加上一個實數?
這里簡單說一下:Python 自動地把實數 bbb 擴展成一個 1×m1\times m1×m 的行向量,只有這樣才能進行矩陣相加(矩陣相加需要兩個矩陣等大小)。這個操作似乎有點不可思議,它在 Python 中被稱作 廣播(brosdcasting),目前你不用對此感到顧慮,這在博客——深度學習入門筆記(五):神經網絡的編程基礎中會詳細講解!
現在說一下字母規范:大寫的 ZZZ 是一個包含所有小寫 z(1)z^{(1)}z(1) 到 z(m)z^{(m)}z(m) 的 1×m1\times m1×m 的矩陣,而大寫 AAA 則是包含所有小寫 a(1)a^{(1)}a(1) 到 a(m)a^{(m)}a(m) 的 1×m1\times m1×m 的矩陣。
簡單小結一下,不要 for 循環,利用 mmm 個訓練樣本使用向量化的方法,一次性計算出 ZZZ 和 AAA。
Z=wTX+bZ = w^TX + bZ=wTX+b
A=σ(Z)A=\sigma (Z)A=σ(Z)
4、向量化邏輯回歸的梯度輸出
注:本節中大寫字母代表向量,小寫字母代表元素
如何 同時 計算 mmm 個數據的梯度,并且實現一個非常高效的 邏輯回歸算法(Logistic Regression) ?
之前在講梯度計算的時候(深度學習入門筆記(二):神經網絡基礎),列舉過幾個例子, dz(1)=a(1)?y(1)dz^{(1)}=a^{(1)}-y^{(1)}dz(1)=a(1)?y(1), dz(2)=a(2)?y(2)dz^{(2)}=a^{(2)}-y^{(2)}dz(2)=a(2)?y(2), ……等等一系列類似公式。不過當時是單樣本數據計算,現在對 mmm 個數據做同樣的計算,可以照著上一章講過的,定義一個新的變量 dZ=[dz(1),dz(2)...dz(m)]dZ=[dz^{(1)} ,dz^{(2)} ... dz^{(m)}]dZ=[dz(1),dz(2)...dz(m)],每一個樣本的 dzdzdz 橫向排列,就可以得到一個 1×m1\times m1×m 的 dZdZdZ 矩陣了。
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)],然后就可以計算 dZ=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)}]dZ=A?Y=[a(1)?y(1),a(2)?y(2),...,a(m)?y(m)],剛好分別對應 dz(1)dz^{(1)}dz(1),dz(2)dz^{(2)}dz(2),……
開始向量化邏輯回歸的梯度輸出:
首先是
db=1m∑i=1mdz(i)db=\frac{1}{m}\sum_{i=1}^{m}dz^{(i)}db=m1?i=1∑m?dz(i)
向量化代碼如下:
db=1m?np.sum(dZ)db=\frac{1}{m}*np.sum(dZ)db=m1??np.sum(dZ)
接下來是
dw=1m?X?dzTdw=\frac{1}{m}*X*dz^{T}dw=m1??X?dzT
其中,XXX 是一個行向量。因此展開后是
dw=1m?(x(1)dz(1)+x(2)dz(2)+...+xmdzm)dw=\frac{1}{m}*(x^{(1)}dz^{(1)}+x^{(2)}dz^{(2)}+...+x^{m}dz^{m})dw=m1??(x(1)dz(1)+x(2)dz(2)+...+xmdzm)
向量化代碼如下:
db=1m?np.sum(dZ)db=\frac{1}{m}*np.sum(dZ)db=m1??np.sum(dZ)
dw=1m?X?dzTdw=\frac{1}{m}*X*dz^{T}dw=m1??X?dzT
這樣,就避免了在訓練集上使用 for 循環。對比之前實現的邏輯回歸,可以發現,沒有向量化是非常低效的,代碼量還多。。。
翻新后的計算如下:
Z=wTX+b=np.dot(w.T,X)+bZ = w^{T}X + b = np.dot( w.T,X)+bZ=wTX+b=np.dot(w.T,X)+b
A=σ(Z)A = \sigma( Z )A=σ(Z)
dZ=A?YdZ = A - YdZ=A?Y
dw=1m?X?dzT{{dw} = \frac{1}{m}*X*dz^{T}\ }dw=m1??X?dzT?
db=1m?np.sum(dZ)?db= \frac{1}{m}*np.sum( dZ)?db=m1??np.sum(dZ)?
w:=w?a?dww: = w - a*dww:=w?a?dw
b:=b?a?dbb: = b - a*dbb:=b?a?db
前五個公式完成了前向和后向傳播,后兩個公式進行梯度下降更新參數。
最后的最后,終于得到了一個高度向量化的、非常高效的邏輯回歸的梯度下降算法,是不是?
推薦閱讀
- 深度學習入門筆記(一):深度學習引言
- 深度學習入門筆記(二):神經網絡基礎
- 深度學習入門筆記(三):求導和計算圖
- 深度學習入門筆記(四):向量化
- 深度學習入門筆記(五):神經網絡的編程基礎
- 深度學習入門筆記(六):淺層神經網絡
- 深度學習入門筆記(七):深層神經網絡
- 深度學習入門筆記(八):深層網絡的原理
- 深度學習入門筆記(九):深度學習數據處理
- 深度學習入門筆記(十):正則化
- 深度學習入門筆記(十一):權重初始化
- 深度學習入門筆記(十二):深度學習數據讀取
- 深度學習入門筆記(十三):批歸一化(Batch Normalization)
- 深度學習入門筆記(十四):Softmax
- 深度學習入門筆記(十五):深度學習框架(TensorFlow和Pytorch之爭)
- 深度學習入門筆記(十六):計算機視覺之邊緣檢測
- 深度學習入門筆記(十七):深度學習的極限在哪?
- 深度學習入門筆記(十八):卷積神經網絡(一)
- 深度學習入門筆記(十九):卷積神經網絡(二)
- 深度學習入門筆記(二十):經典神經網絡(LeNet-5、AlexNet和VGGNet)
參考文章
- 吳恩達——《神經網絡和深度學習》視頻課程
總結
以上是生活随笔為你收集整理的深度学习入门笔记(四):向量化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java-反射
- 下一篇: ES6之promise天气案例