很多人說深度學習就是個黑箱子,把圖像預處理之後丢進 tensorflow 就能出來預測結果,簡單有效又省時省力。但正如我在上一篇推送中所說,如果你已是一名功力純厚的深度學習工程師,這麼做當然沒問題。但我想大多數人也和我一樣,都是走在學習深度學習的路上,一上來就上架構并沒有什麼特别不妥之處,但總歸是對你了解深度學習的黑箱機制是了無裨益的。是以,我建議在學習深度學習的路上,從最簡單的感覺機開始寫起,一步一步捋清神經網絡的結構,以至于激活函數怎麼寫、采用何種損失函數、前向傳播怎麼寫、後向傳播又怎麼寫,權值如何疊代更新,都需要你自己去實作。若在一開始就直接調用架構,小的 demo 可以跑起來,糊弄一時,看起來就像是鸠摩智在内力未到的情形下強行練習少林寺的 72 絕技,最後走火入魔。
無論你是在看那本深度學習的花書,還是在學習 Adrew NG 的 deeplearningai,或者是在cs231n 課程,對神經網絡的基本理論了如指掌的你一定想親手用 python 來實作它。筆記1就在不借助任何深度學習架構的基礎上,利用 python 的科學計算庫 numpy 由最初級的感覺機開始,從零搭建一個神經網絡模型。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLwQmM0IWMkRDOlZmZ3AzYxUGOkBDO3kTMxcTZzcTNxQDOwYmN5U2Mi9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
感覺機結構
對于感覺機模型、神經網絡理論這裡就不再叙述,相信志在精通深度學習的你對此一定很熟練了。至于對于神經網絡中的輸入層、隐藏層、輸出層、權重與偏置、激活函數、損失函數、前向傳播、反向傳播、權值更新、梯度下降、微積分中的鍊式求導、方向梯度等概念,我也假設你很熟練了。是以,接下來就讓我們從零搭建一個最初級的神經網絡模型。
在寫代碼前,必須先捋一下思路,咱們先要什麼,然後再寫什麼,你心中必須有個數。要從零開始寫一個神經網絡,通常的方法是:
● 定義網絡結構(指定輸出層、隐藏層、輸出層的大小)
● 初始化模型參數
● 循環操作:執行前向傳播/計算損失/執行後向傳播/權值更新
有了上面這個思路,我們就可以開始寫了。當然了,本節是寫一個最簡單的感覺機模型,是以網絡結構就無需特别定義。首先來定義我們的激活函數,激活函數有很多種,這裡我們使用大名鼎鼎的 sigmoid 函數:
直接利用
numpy
進行定義 sigmoid()
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
在無需定義網絡結構的情形下,第二步我們就可以直接對模型參數進行初始化。模型參數主要包括權值
w
和偏置
b
,這也是神經網絡學習過程要學的東西。繼續利用
numpy
對參數進行初始化:
def initilize_with_zeros(dim):
w = np.zeros((dim, 1))
b = 0.0 #assert(w.shape == (dim, 1)) #assert(isinstance(b, float) or isinstance(b, int)) return w, b
接下來就要進入模型的主體部分,執行最後一步那個大的循環操作,這個循環中包括前向傳播和計算損失、反向傳播和權值更新。這也是神經網絡訓練過程中每一次需要疊代的部分。這裡簡單說一下,很多初學者容易被這兩個概念繞住,前向傳播簡單而言就是計算預測
y
的過程,而後向傳播則是根據預測值和實際值之間的誤差不斷往回推更新權值和偏置的過程。
前後傳播與後向傳播
下面我們來定義一個大的前向傳播函數,預測值
y
為模型從輸入到經過激活函數處理後的輸出的結果。損失函數我們采用交叉熵損失,利用
numpy
定義如下函數:
def propagate(w, b, X, Y):
m = X.shape[1]
A = sigmoid(np.dot(w.T, X) + b)
cost = -1/m * np.sum(Y*np.log(A) + (1-Y)*np.log(1-A))
dw = np.dot(X, (A-Y).T)/m
db = np.sum(A-Y)/m
assert(dw.shape == w.shape)
assert(db.dtype == float)
cost = np.squeeze(cost)
assert(cost.shape == ())
grads = { 'dw': dw,
'db': db
}
return grads, cost
在上面的前向傳播函數中,我們先是通過激活函數直接表示了感覺機輸出的預測值,然後通過定義的交叉熵損失函數計算了損失,最後根據損失函數計算了權值
w
b
的梯度,将參數梯度結果以字典和損失一起作為函數的輸出進行傳回。這就是前向傳播的編寫思路。
接下來循環操作的第二步就是進行反向傳播操作,計算每一步的目前損失根據損失對權值進行更新。同樣定義一個函數
backward_propagation
:
def backward_propagation(w, b, X, Y, num_iterations, learning_rate, print_cost=False):
cost = []
for i in range(num_iterations):
grad, cost = propagate(w, b, X, Y)
dw = grad['dw']
db = grad['db']
w = w - learing_rate * dw
b = b - learning_rate * db
if i % 100 == 0:
cost.append(cost)
if print_cost and i % 100 == 0:
print("cost after iteration %i: %f" %(i, cost))
params = {"dw": w,
"db": b
}
grads = {"dw": dw,
"db": db
}
return params, grads, costs
在上述函數中,我們先是建立了一個損失清單容器,然後将前一步定義的前向傳播函數放進去執行疊代操作,計算每一步的目前損失和梯度,利用梯度下降法對權值進行更新,并用字典封裝疊代結束時的參數和梯度進行傳回。
如上所示,一個簡單的神經網絡模型(感覺機)就搭建起來了。通常模型建好之後我們還需要對測試資料進行預測,是以我們也定義一個預測函數
predict
,将模型的機率輸出轉化為0/1值。
def predict(w, b, X):
m = X.shape[1]
Y_prediction = np.zeros((1, m))
w = w.reshape(X.shape[0], 1)
A = sigmoid(np.dot(w.T, X)+b)
for i in range(A.shape[1]):
if A[:, i] > 0.5:
Y_prediction[:, i] = 1 else:
Y_prediction[:, i] = 0 assert(Y_prediction.shape == (1, m))
return Y_prediction
到這裡整個模型算是寫完了,但是我們定義了這麼多函數,調用起來太麻煩,是以緻力于要寫出
pythonic
的代碼的你們肯定想對這些函數進行一下簡單的封裝:
def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):
# initialize parameters with zeros (≈ 1 line of code)
w, b = initialize_with_zeros(X_train.shape[0]) # Gradient descent (≈ 1 line of code)
parameters, grads, costs = backwize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost) # Retrieve parameters w and b from dictionary "parameters"
w = parameters["w"]
b = parameters["b"] # Predict test/train set examples (≈ 2 lines of code)
Y_prediction_train = predict(w, b, X_train)
Y_prediction_test = predict(w, b, X_test) # Print train/test Errors
print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))
d = {"costs": costs,
"Y_prediction_test": Y_prediction_test,
"Y_prediction_train" : Y_prediction_train,
"w" : w,
"b" : b,
"learning_rate" : learning_rate,
"num_iterations": num_iterations}
return d
如此這般一個簡易的神經網絡就被你用
numpy
就寫出來了。現在社會浮躁,很多人學習都沒有耐心,總是抱着鸠摩智的心态想要一步登天。學習機器學習和深度學習方法很多,但我相信,隻有對基本的算法原理每一步都捋清楚,每一步都用最基礎的庫去實作,你成為一名優秀的機器學習工程師隻是時間問題。深度學習第一次推送筆記,加油吧各位!
原文釋出時間為:2018-08-27
本文作者:louwill
本文來自雲栖社群合作夥伴“
Python愛好者社群”,了解相關資訊可以關注“
”。