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