Mini-patch:從零開始的反向傳播(附詳細代碼)

文章將重點介紹mini-patch逐步實現的反向傳播算法。有許多教程和博客詳細介紹了反向傳播算法,以及演算和代數背後的所有邏輯。因此,我將跳過這一部分,並在數學和使用Python的實現中切入方程式。

關於為什麼我們應該從頭開始實現一個算法,即使幾乎所有框架都已經可以使用它,這也是一個長期存在的普遍問題。顯然,在使用某些高級框架時,你甚至都不會注意到反向傳播確實發揮了魔力。要完全顛倒地理解它,一次嘗試肯定是不夠的。反向傳播在遊戲中也是可以進行實驗的。

為什麼要使用mini-patch?

mini-patch的原理很簡單。通過將數據分成小批處理,並在訓練循環的每次迭代中為算法提供一部分數據集,可以節省內存和處理時間。一次饋入10000x10000矩陣不僅會消耗內存,還會花費很長時間才能運行。相反,每次迭代將其降低到50個不僅會減少內存使用量,而且可以跟蹤進度。

注意:這與隨機方法不同,在隨機方法中,我們從每個類別的數據中抽取了分層樣本,並在假設模型可以推廣的基礎上進行訓練。

開始實驗

這是將用於實驗的數據頭。

此處的目標變量是佔用率,它是類別變量(0/1)。這將是我們將要編碼的架構。

算法:

對於i:= 1到i:= m:

執行正向傳播或正向傳遞以計算每一層中神經元的激活值。

反向傳播步驟:

使用數據中的標籤計算誤差項(MSE或LogLoss或您的期望):

隱藏層中的誤差項使用以下公式計算:

設置梯度:
初始化Δ= 0

3.梯度下降和權重更新步驟:

代碼展示:

<code>weight_dim = [5,H,1] #[number of input features, number of hidden units, number of output units] print("Initializing using He initialization") np.random.seed(3) w1 = np.random.randn(weight_dim[1],weight_dim[0]) * np.sqrt(2/weight_dim[0]) # 0.01 b1 = np.zeros((weight_dim[1],1)) w2 = np.random.randn(weight_dim[2],weight_dim[1]) * np.sqrt(2/weight_dim[1]) # 0.01 b2 = np.zeros((weight_dim[2],1))/<code>

如前所述,這將是一個三層網絡。為了使梯度和誤差方程更好,更容易地識別,我們保持層的數量簡潔。之後,我們將定義一個函數,該函數將用作網絡中的轉發器。

<code>def forward(X,w1,w2,b1,b2): z1 = np.matmul(w1,np.transpose(np.asmatrix(X))) + b1 a1 = sigmoid(z1) z2 = np.matmul(w2, a1) + b2 a2 = sigmoid(z2) return z1, a1, z2, a2/<code>

這裡要注意的一件事是,已經將Input層視為我的第0層。可能還有其他博客/教程被認為是第一名。因此,絕對要為所需的索引編制索引。

因此,現在,在初始化權重和偏差並定義前向傳播函數之後,我們將在size = data-of-setset / N的mini-patch上定義反向傳播函數。通過調整N來調整所需的批次大小。

<code>def backprop(w1,b1,w2,b2,X_train,X_test): for i in range(epoch): no_of_batches = len(X_train) // N for j in range(no_of_batches): # Initilazing gradients delta1 = np.zeros(w1.shape) #(5,5) delta2 = np.zeros(w2.shape) #(1,5) db1 = 0.0 db2 = 0.0 for row in range(j*N,(j+1)*N): # Drop the date column and the index column X = X_train[row, 2:7] Y = X_train[row, 7] #feed forward z1 , a1 , z2 , a2 = forward(X,w1,w2,b1,b2) #(5,1) (5,1) (1,1) (1,1) h = a2 # (1,1) # initializations d3 = a2 - Y #(1,1) delta2 += d3 * np.transpose(a2) #(1,1) db2 += d3 d2 = np.multiply((np.transpose(w2) * d3), sigmoid_gradient(z1),dtype=float) #(5,1) delta1 += d2 * np.transpose(a1) #(5,5) db1 += d2 # Gradient Descent Step #updating weights after every batch by averaging the gradients w1 = w1 - lr * 1.0/N * delta1 #taking the average of gradients b1 = b1 - lr * 1.0/N * db1 w2 = w2 - lr * 1.0/N * delta1 b2 = b2 - lr * 1.0/N * db2 print("************************************************") print("Train error after epoch {} is: ".format(i), np.sum(error(calc_out(X_train[:,2:7],w1,b1,w2,b2),X_train[:,7])) / len(X_train) * 100) print("Test error after epoch {} is: ".format(i), np.sum(error(calc_out(X_test[:,2:7],w1,b1,w2,b2),X_test[:,7])) / len(X_test) * 100) print("************************************************") print() train_error[i] = np.sum(error(calc_out(X_train[:,2:7],w1,b1,w2,b2),X_train[:,7])) / len(X_train) * 100 test_error[i] = np.sum(error(calc_out(X_test[:,2:7],w1,b1,w2,b2),X_test[:,7])) / len(X_test) * 100 return [w1,b1,w2,b2]/<code>

步驟分解

如前所述,Ist循環會遍歷您想要使模型遍歷數據的次數,只需將其放在神經網絡術語“時代”中即可。第二次循環:指定了批次數量後,此循環針對每個時期“ i”遍歷每個微型批次第三循環遍歷該小批量中的每個訓練示例,並計算梯度和誤差值最後,對於每個批次,都執行梯度下降步驟,並對權重矩陣進行更改。

這就是mini-patch的反向傳播實現。需要注意的是,此實驗為網絡中的每一層使用了一個矩陣變量,當網絡規模擴大時,這是一種不明智的做法,我們這樣做是為了瞭解它的實際工作原理。如果要增加“隱藏層”的數量,可以簡單地使用3d矩陣進行誤差和梯度計算,其中第3維將保存該層的值。