使用scipy实现简单神经网络
文章目錄
- 1、準備工作
- (1) 導入必要的庫:
- (2) 數據準備
- 2、實現主要的函數
- (1) `Logistic function`
- (2) `Forward`函數
- (3) 損失函數
- (4) `mse_loss`
- 3、訓練模型
- 4、模型評估
- 5、訓練結果可視化
- 6、小結
1、準備工作
(1) 導入必要的庫:
- numpy - 用于基本的數據操作
- scipy.optimize 中導入 minimize函數,用于訓練模型
- matplotlib 用于數據可視化
(2) 數據準備
首先需要設置的參數:
- N– 樣本個數
- d– 輸入樣本的維度
- num_hidden --隱函層的個數
接著生成數據:
- x– 區間 [?10,10][-10,10][?10,10] 上取 N 個點,這里我們的 N=100。另外將其設為列向量,即x.shape=(N,1)
- y_true– Mexican Hat 函數
y=sin?xxy=\frac{\sin x}{x} y=xsinx? - y– Mexican Hat函數的樣本點,這里我們使用了 [?0.05,0.05][-0.05,0.05][?0.05,0.05] 上的噪聲
數據生成完成后出圖看一下樣子:
x = np.linspace(-10,10,N).reshape(-1,1) y_true = np.sin(x)/x y = np.sin(x)/x + (np.random.rand(N,1)*0.1-0.05)plt.plot(x,y,'-b') plt.plot(x,y,'oy') plt.show()2、實現主要的函數
(1) Logistic function
這里我們采用 Logistic function 作為激活函數,定義為:
y=11+e?xy = \frac{1}{1+\mathrm{e}^{-x}} y=1+e?x1?
(2) Forward函數
這里我們采用簡單的三層神經網絡,因此前饋函數的公式為:
Y^=(w?×1(2))Tf((w?×d(1))Txd×N(2)+b(1))+b(2)\hat{Y}=\left(\boldsymbol{w}_{\ell \times 1}^{(2)}\right)^{T} f\left(\left(\boldsymbol{w}_{\ell \times d}^{(1)}\right)^{T} \boldsymbol{x}_{d \times N}^{(2)}+\boldsymbol{b}^{(1)}\right)+b^{(2)} Y^=(w?×1(2)?)Tf((w?×d(1)?)Txd×N(2)?+b(1))+b(2)
這里需要特別說明一下,由于scipy.optimize只能優化第一個 positional argument,因此不能將每層的參數分開傳給loss。這里我們為了方便,就在把所有參數放在一個向量 θ\boldsymbol{\theta}θ 中,并且在forward函數中拆取各層參數。
(3) 損失函數
這里直接采用 MSE作為損失函數:
MSE=1N∥Y?Y^∥2MSE = \frac{1}{N} \|Y-\hat{Y}\|^2 MSE=N1?∥Y?Y^∥2
(4) mse_loss
這個函數有那么一點多余,不過在后面計算loss時會比較方便。
def logi_func(x):return 1/(1+np.exp(-x))def forward(x,theta,d,num_hidden):w1 = theta[:d*num_hidden].reshape(d,num_hidden)w2 = theta[d*num_hidden:d*num_hidden+num_hidden].reshape(num_hidden,1)b1 = theta[d*num_hidden+num_hidden:-1].reshape(num_hidden,1)b2 = theta[-1]return logi_func(x.dot(w1)+b1.T).dot(w2)+b2def nn_loss(theta,x,y,d,num_hidden):return ((forward(x,theta,d,num_hidden)-y)**2).mean()def mse_loss(y,y_pred):return ((y-y_pred)**2).mean()寫完之后簡單測試一下,沒有什么問題:
theta = np.random.rand(d*num_hidden+num_hidden + num_hidden+1)forward(x, theta, d, num_hidden) print(nn_loss(theta,x,y,d,num_hidden)) 32.923287756238463、訓練模型
minimize函數的使用非常簡單,第一個參數為loss的函數名,第二個參數為網絡參數的初始值,第三個參數args是loss中第一個參數之外的所有參數組成的tuple。
minize這個函數默認的優化方法為BFGS,這個算法具有很好的收斂性,不過直接用于神經網絡的訓練時速度稍慢一點。
本次實驗在 MacBook Pro (m1) 上進行,運行時間3.7s,可見比梯度下降要慢很多(通常應該比簡單梯度精度要多,但在loss上體現并不見得明顯)。
res = minimize(nn_loss, theta, args=(x, y, d, num_hidden))返回值res是優化結果的集合,其中參數的最優值在 res.x中,因此將該參數傳給forward即可算出擬合值。
y_pred = forward(x,res.x,d,num_hidden)4、模型評估
MSE值為 0.00055,可見訓練效果還是不錯的:
print(mse_loss(y,y_pred)) 0.0005521707329183585、訓練結果可視化
這里直接使用matploblib即可,用三種線型分別表示:函數真實值、樣本值、模型擬合值。
可以看到擬合效果是很不錯的。
plt.plot(x,y,'oy') plt.plot(x, y_true, '-r') plt.plot(x,y_pred,'-b')plt.legend(['y_samples', 'y_true', 'y_pred']) plt.show()為了進一步驗證模型的泛化性能,我們在 [?5,5][-5,5][?5,5] 隨機采100個點,再看一下模型的預測效果。
可以看到在中間這一段的 MSE 僅有0.00033,可見這種單層神經網絡在函數擬合問題上的泛化性能還是很不錯的(注意到這里我們只用了20個隱含層節點)。
x_test = np.sort(np.random.rand(100,1)*10-5,axis=0) y_test_true = np.sin(x_test)/x_test y_test_pred = forward(x_test, res.x, d, num_hidden)plt.plot(x_test, y_test_true, 'oy') plt.plot(x_test, y_test_pred, '-b')plt.legend(['y_test_true', 'y_test_pred']) plt.title('mse={0}'.format(mse_loss(y_test_true,y_test_pred))) plt.show()6、小結
- 用 scipy.optimize.minimize 完全可以實現簡單的神經網絡
- 本文構造的網絡僅有20個隱含層,但擬合效果和泛化性能均較好,可見單隱含層神經網絡在函數擬合上具有較好的性能
- 實現細節的關鍵在于靈活調用miminize函數,尤其是對loss的傳參
- 本文在實現forward時對參數的重構方式有點麻煩,其實還可以更為簡便,讀者可自行思考完成
- 本文使用的算法效率并不高,可見針對具體的訓練問題仍需重視算法的選擇
總結
以上是生活随笔為你收集整理的使用scipy实现简单神经网络的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python函数作为参数传递给函数
- 下一篇: 余华《兄弟》