b站的用紙筆訓練神經網路【matlab與python實作】
- 我的作業
- 基本思路
- 黑盒是什么
- MATLAB原始碼
- Python原始碼
我的作業
之前在b站上看到小蠻大佬做的一期用紙筆訓練神經網路的視頻,關于正向傳遞和反向傳播這一塊受益匪淺,但是視頻中也存在一些公式以及繪圖錯誤的地方,所以嘗試復現了一些代碼來更清晰的展現整個程序,目前只提供matlab與python版本的代碼(PS:該博客只對視頻中提到的內容做一些淺顯的梳理,便于初學者理解),
視頻鏈接:https://www.bilibili.com/video/BV1R64y187yt/
基本思路
神經網路正向傳遞和反向傳播的程序可以看成下圖所示
正向傳遞:左邊3×4維的矩陣經過一個黑盒后會得到右邊3×2維的矩陣
- 一開始的時候,我們經過黑盒得到的3×2維的矩陣不一定如我們所愿是實際的目標結果Y,我們需要借助已知的目標結果Y對黑盒進行調整
- 輸入3×4維矩陣X,經過黑盒生成3×2維矩陣y(這里是y,不是目標矩陣Y),這樣在不斷的更新迭代后,黑盒表現的很好了
- 那么當我們再次輸入3×4維矩陣X,輸出的預測矩陣y就會和目標矩陣Y相差無幾,那么對黑盒進行調整的程序,就是反向傳播程序
反向傳播:右邊3×2維的矩陣對黑盒進行調整的程序

黑盒是什么
黑盒包括經過的各種神經元,通過一些列矩陣相乘、激活函式等操作,最終由softmax得到輸出結果,
這里參考視頻中的一張圖,紅色方框內可以看成黑盒的部分
其中的紫色線段、藍色線段、黑色線段,分別對應三個權重w1、w2、w3(這三個權重初始時是隨機生成的),比如紫色線段(對應w1),左邊是四個輸入(x1、x2、x3、x4),右邊是三個神經元輸出(s1、s2、s3),所以w1的維度就是4×3,以此類推,
所以神經網路的正向傳遞可以看成是一系列的矩陣相乘的程序

前面提到,三個權重在一開始是隨機生成的,那么反向傳遞的調整程序,就是對這三個權重進行調整,利用預測的y與實際的Y的差值,即Loss,通過Loss分別對w進行求導(視頻里用的是鏈式法則來解決),得到w的調整量g(w1、w2、w3對應g1、g2、g3),反向傳遞完后,原來的權重w減去對應的g(當然這個g一般會乘上學習率以及轉置)即可得到更新后的w,
總結來說就是反向傳播就是更新w的程序,
g1、g2、g3用鏈式法則求導的公式我會在代碼中給出,就用matlab的代碼來說一下吧,需要注意 * 和 .* 的區別,*是矩陣乘法,需要前一個陣列的列與后一個陣列的行相等,而 .*是需要兩個矩陣維度完全相等的,是矩陣對應位置相乘

MATLAB原始碼
建議用matlab來debug矩陣變化的各個程序,比較方便清晰
clc,clear,close all
%% 訓練樣本
X=[1,1,0,0;
0,0,1,1;
1,0,0,1];
Y=[1,0; %實際值
0,1;
1,0];
[Inx,Iny]=size(X);%輸入矩陣的維數
[Outx,Outy]=size(Y);%輸出矩陣的維數
Hid_wide=3;%隱藏層節點維度
D=100; %損失初始值
a=0.1;%學習率
times=1;
res=0.001;%容差
Loss=inf; %預測值-實際值
%% 回圈訓練權重
while D>res %大于容差則一直回圈
% disp(times);
disp(D);
%第一層
if times==1 %第一次隨機生成
W1=rand(Iny,Hid_wide);%第一次回圈隨機生成權重W1
end
S1=X*W1;
%S1節點經過激活函式sigmod
Z1=sigmoid(S1);
%第二層
if times==1
W2=rand(Hid_wide,Outx);%第一次回圈隨機生成權重W2
end
S2=Z1*W2;
%S2節點經過激活函式
Z2=sigmoid(S2);
%輸出層
if times==1
W3=rand(Outx,Outy);
end
Q=Z2*W3;
%輸出層節點經過激活函式
y=sigmoid(Q); %預測值
%計算損失值
sum=0;
for m=1:Outx
for n=1:Outy
sum=sum+(y(m,n)-Y(m,n))^2;
end
end
if D>sum
D=sum;
end
%% 反向傳遞程序
%loss對W3求導
g3=(y-Y)'*Z2;
%loss對W2求導
g2=(((y-Y)*W3').*(sigmoid(S2).*(1-sigmoid(S2))))'*Z1;
%loss對W1求導
g1=((((y-Y)*W3').*(sigmoid(S2).*(1-sigmoid(S2))))*W2'.*(sigmoid(S1).*(1-sigmoid(S1))))'*X;
%% 更新權重
W1=W1-a*g1';
W2=W2-a*g2';
W3=W3-a*g3';
Loss(times)=D; %記錄每次的損失值
times=times+1;
end
plot(Loss);%列印損失降低程序
disp('調整后的W1');
disp(W1);
disp('調整后的W2');
disp(W2);
disp('調整后的W3');
disp(W3);
disp('真實Y值');
disp(Y);
disp('訓練的Y值');
disp(y);
%% 激活函式sigmod
function result=sigmoid(A)
result=inf;
[A1,A2]=size(A);
for i=1:A1
for j=1:A2
result(i,j)=1/(1+exp(-A(i,j)));
end
end
end
Python原始碼
剛入門python,原諒我大量不熟練的操作,陣列計算部分相比matlab確實太累了
"""
作者:豬腳三父
日期:2022年02月01日
"""
import numpy as np
import matplotlib.pyplot as plt # 畫圖用的包
def sigmoid(A): # 激活函式sigmod
A1 = np.size(A, 0)
A2 = np.size(A, 1)
result = [[0.0 for col in range(A2)] for row in range(A1)] # 初始化一個A1*A2維度的串列
result = np.array(result) # 轉為陣列
for i in range(A1):
for j in range(A2):
result[i, j] = 1 / (1 + np.exp(-1 * A[i, j]))
return result
if __name__ == '__main__':
X = [[1,1,0,0],
[0,0,1,1],
[1,0,0,1]]
Y=[[1,0],
[0,1],
[1,0]]
X = np.array(X) # 轉成array格式
Y = np.array(Y) # 轉成array格式
Inx = np.size(X, 0) # 輸入矩陣的維數
Iny = np.size(X, 1) # 輸入矩陣的維數
Outx = np.size(Y, 0) # 輸出矩陣的維數
Outy = np.size(Y, 1) # 輸出矩陣的維數
Hid_wide = 3 # 隱藏層節點維度
D = 100 # 損失初始值
a = 0.1 # 學習率
times = 1
res = 0.001 # 容差
Loss = []
while D > res: # 大于容差則一直回圈
# 第一層
if times == 1: # 第一次隨機生成
W1 = np.random.random((Iny, Hid_wide)) # 第一次回圈隨機生成權重W1
S1 = np.dot(X, W1)
Z1 = sigmoid(S1) # S1節點經過激活函式sigmod
# 第二層
if times == 1: # 第一次隨機生成
W2 = np.random.random((Hid_wide, Outx)) # 第一次回圈隨機生成權重W2
S2 = np.dot(Z1, W2)
Z2 = sigmoid(S2) # S2節點經過激活函式sigmod
if times == 1: # 第一次隨機生成
W3 = np.random.random((Outx, Outy)) # 第一次回圈隨機生成權重W3
Q = np.dot(Z2, W3)
# 輸出層節點經過激活函式
y = sigmoid(Q) # 預測值
# 計算損失值
sum = 0
for m in range(Outx):
for n in range(Outy):
sum = sum + (y[m, n] - Y[m, n]) ** 2
if D > sum:
D = sum
# 反向傳遞程序
# loss對W3求導
g3 = np.dot(np.transpose(y - Y), Z2)
# loss對W2求導
tmp2 = np.dot(y - Y, np.transpose(W3))
tmp3 = np.multiply(sigmoid(S2), 1 - sigmoid(S2))
tmp1 = np.multiply(tmp2, tmp3)
g2 = np.dot(np.transpose(tmp1), Z1)
# loss對W1求導
tmp6 = np.multiply(sigmoid(S1), 1 - sigmoid(S1))
tmp5 = np.dot(tmp1, np.transpose(W2))
tmp4 = np.multiply(tmp5, tmp6)
g1 = np.dot(np.transpose(tmp4), X)
# 更新權重
W1 = W1 - a * np.transpose(g1)
W2 = W2 - a * np.transpose(g2)
W3 = W3 - a * np.transpose(g3)
Loss.append(D) # 記錄每次的損失值
times = times + 1
# 列印資料 & 畫圖
print('調整后的W1')
print(W1)
print('調整后的W2')
print(W2)
print('調整后的W3')
print(W3)
print('真實Y值')
print(Y)
print('訓練的Y值')
print(y)
t = range(len(Loss))
plt.figure(dpi=100, figsize=(12, 6)) # 指定影像解析度和畫板大小
plt.fill_between(t, Loss, color="skyblue", alpha=0.3)
plt.plot(t, Loss, color="blue") # 多勾勒一層藍邊
plt.xlabel('迭代次數') # x軸上的名字
plt.ylabel('Loss') # y軸上的名字
plt.rcParams['font.sans-serif'] = ['SimHei'] # 不加不能顯示中文
plt.rcParams['axes.unicode_minus'] = False # 不加不能顯示中文
plt.show() # 列印影像
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/423320.html
標籤:AI
