超簡單的神經(jīng)網(wǎng)絡(luò)構(gòu)建方法,你上你也行!
本文轉(zhuǎn)載自公眾號(hào)“讀芯術(shù)”(ID:AI_Discovery)
人工智能,深度學(xué)習(xí),這些詞是不是聽起來就很高大上,充滿了神秘氣息?仿佛是只對(duì)數(shù)學(xué)博士開放的高級(jí)領(lǐng)域?
錯(cuò)啦!在B站已經(jīng)變成學(xué)習(xí)網(wǎng)站的今天,還有什么樣的教程是網(wǎng)上找不到的呢?深度學(xué)習(xí)從未如此好上手,至少實(shí)操部分是這樣。
假如你只是了解人工神經(jīng)網(wǎng)絡(luò)基礎(chǔ)理論,卻從未踏足如何編寫,跟著本文一起試試吧。你將會(huì)對(duì)如何在PyTorch 庫中執(zhí)行人工神經(jīng)網(wǎng)絡(luò)運(yùn)算,以預(yù)測(cè)原先未見的數(shù)據(jù)有一個(gè)基本的了解。
這篇文章最多10分鐘就能讀完;如果要跟著代碼一步步操作的話,只要已經(jīng)安裝了必要的庫,那么也只需15分鐘。相信我,它并不難。
長話短說,快開始吧!
導(dǎo)入語句和數(shù)據(jù)集
在這個(gè)簡單的范例中將用到幾個(gè)庫:
- Pandas:用于數(shù)據(jù)加載和處理
- Matplotlib: 用于數(shù)據(jù)可視化處理
- PyTorch: 用于模型訓(xùn)練
- Scikit-learn: 用于拆分訓(xùn)練集和測(cè)試集
如果僅僅是想復(fù)制粘貼的話,以下幾條導(dǎo)入語句可供參考:
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
- import pandas as pd
- import matplotlib.pyplot as plt
- from sklearn.model_selection import train_test_split
至于數(shù)據(jù)集,Iris數(shù)據(jù)集可以在這個(gè)URL上找到。下面演示如何把它直接導(dǎo)入
- Pandas:
- iris = pd.read_csv('https://raw.githubusercontent.com/pandas-dev/pandas/master/pandas/tests/data/iris.csv')
- iris.head()
前幾行如下圖所示:
現(xiàn)在需要將 Name列中鳶尾花的品種名稱更改或者重映射為分類值。——也就是0、1、2。以下是步驟說明:
- mappings = {
- 'Iris-setosa': 0,
- 'Iris-versicolor': 1,
- 'Iris-virginica': 2
- }iris['Name'] = iris['Name'].apply(lambda x: mappings[x])
執(zhí)行上述代碼得到的DataFrame如下:
這恭喜你,你已經(jīng)成功地邁出了第一步!
拆分訓(xùn)練集和測(cè)試集
在此環(huán)節(jié),將使用 Scikit-Learn庫拆分訓(xùn)練集和測(cè)試集。隨后, 將拆分過的數(shù)據(jù)由 Numpy arrays 轉(zhuǎn)換為PyTorchtensors。
首先,需要將Iris 數(shù)據(jù)集劃分為“特征”和“ 標(biāo)簽集” ——或者是x和y。Name列是因變量而其余的則是“特征”(或者說是自變量)。
接下來筆者也將使用隨機(jī)種子,所以可以直接復(fù)制下面的結(jié)果。代碼如下:
- X = iris.drop('Name', axis=1).values
- y = iris['Name'].valuesX_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.2, random_state=42)X_train = torch.FloatTensor(X_train)
- X_test = torch.FloatTensor(X_test)
- y_train = torch.LongTensor(y_train)
- y_test = torch.LongTensor(y_test)
如果從 X_train 開始檢查前三行,會(huì)得到如下結(jié)果:
從 y_train開始則得到如下結(jié)果:

地基已經(jīng)打好,下一環(huán)節(jié)將正式開始搭建神經(jīng)網(wǎng)絡(luò)。
定義神經(jīng)網(wǎng)絡(luò)模型
模型的架構(gòu)很簡單。重頭戲在于神經(jīng)網(wǎng)絡(luò)的架構(gòu):
- 輸入層 (4個(gè)輸入特征(即X所含特征的數(shù)量),16個(gè)輸出特征(隨機(jī)))
- 全連接層 (16個(gè)輸入特征(即輸入層中輸出特征的數(shù)量),12個(gè)輸出特征(隨機(jī)))
- 輸出層(12個(gè)輸入特征(即全連接層中輸出特征的數(shù)量),3個(gè)輸出特征(即不同品種的數(shù)量)
大致就是這樣。除此之外還將使用ReLU 作為激活函數(shù)。下面展示如何在代碼里執(zhí)行這個(gè)激活函數(shù)。
- class ANN(nn.Module):
- def __init__(self):
- super().__init__()
- self.fc1 =nn.Linear(in_features=4, out_features=16)
- self.fc2 =nn.Linear(in_features=16, out_features=12)
- self.output =nn.Linear(in_features=12, out_features=3)
- def forward(self, x):
- x = F.relu(self.fc1(x))
- x = F.relu(self.fc2(x))
- x = self.output(x)
- return x
PyTorch使用的面向?qū)ο舐暶髂P偷姆绞椒浅V庇^。在構(gòu)造函數(shù)中,需定義所有層及其架構(gòu),若使用forward(),則需定義正向傳播。
接著創(chuàng)建一個(gè)模型實(shí)例,并驗(yàn)證其架構(gòu)是否與上文所指的架構(gòu)相匹配:
- model = ANN()
- model
在訓(xùn)練模型之前,需注明以下幾點(diǎn):
- 評(píng)價(jià)標(biāo)準(zhǔn):主要使用 CrossEntropyLoss來計(jì)算損失
- 優(yōu)化器:使用學(xué)習(xí)率為0.01的Adam 優(yōu)化算法
下面展示如何在代碼中執(zhí)行CrossEntropyLoss和Adam :
- criterion = nn.CrossEntropyLoss()
- optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
令人期盼已久的環(huán)節(jié)終于來啦——模型訓(xùn)練!
模型訓(xùn)練
這部分同樣相當(dāng)簡單。模型訓(xùn)練將進(jìn)行100輪, 持續(xù)追蹤時(shí)間和損失。每10輪就向控制臺(tái)輸出一次當(dāng)前狀態(tài)——以指出目前所處的輪次和當(dāng)前的損失。
代碼如下:
- %%timeepochs = 100
- loss_arr = []for i in range(epochs):
- y_hat = model.forward(X_train)
- loss = criterion(y_hat, y_train)
- loss_arr.append(loss)
- if i % 10 == 0:
- print(f'Epoch: {i} Loss: {loss}')
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
好奇最后三行是干嘛用的嗎?答案很簡單——反向傳播——權(quán)重和偏置的更新使模型能真正地“學(xué)習(xí)”。
以下是上述代碼的運(yùn)行結(jié)果:

進(jìn)度很快——但不要掉以輕心。
如果對(duì)純數(shù)字真的不感冒,下圖是損失曲線的可視化圖(x軸為輪次編號(hào),y軸為損失):

模型已經(jīng)訓(xùn)練完畢,現(xiàn)在該干嘛呢?當(dāng)然是模型評(píng)估。需要以某種方式在原先未見的數(shù)據(jù)上對(duì)這個(gè)模型進(jìn)行評(píng)估。
模型評(píng)估
在評(píng)估過程中,欲以某種方式持續(xù)追蹤模型做出的預(yù)測(cè)。需要迭代 X_test并進(jìn)行預(yù)測(cè),然后將預(yù)測(cè)結(jié)果與實(shí)際值進(jìn)行比較。
這里將使用 torch.no_grad(),因?yàn)橹皇窃u(píng)估而已——無需更新權(quán)重和偏置。
總而言之,代碼如下:
- preds = []with torch.no_grad():
- for val in X_test:
- y_hat = model.forward(val)
- preds.append(y_hat.argmax().item())
現(xiàn)在預(yù)測(cè)結(jié)果被存儲(chǔ)在 preds陣列。可以用下列三個(gè)值構(gòu)建一個(gè)Pandas DataFrame。
- Y:實(shí)際值
- YHat: 預(yù)測(cè)值
- Correct:對(duì)角線,對(duì)角線的值為1表示Y和YHat相匹配,值為0則表示不匹配
代碼如下:
- df = pd.DataFrame({'Y': y_test, 'YHat':preds})df['Correct'] = [1 if corr == pred else 0 for corr, pred in zip(df['Y'],df['YHat'])]
df 的前五行如下圖所示:

下一個(gè)問題是,實(shí)際該如何計(jì)算精確度呢?
很簡單——只需計(jì)算 Correct列的和再除以 df的長度:
- df['Correct'].sum() / len(df)>>> 1.0
此模型對(duì)原先未見數(shù)據(jù)的準(zhǔn)確率為100%。但需注意這完全是因?yàn)镮ris數(shù)據(jù)集非常易于歸類,并不意味著對(duì)于Iris數(shù)據(jù)集來說,神經(jīng)網(wǎng)絡(luò)就是最好的算法。NN對(duì)于這類問題來講有點(diǎn)大材小用,不過這都是以后討論的話題了。
這可能是你寫過最簡單的神經(jīng)網(wǎng)絡(luò),有著完美簡潔的數(shù)據(jù)集、沒有缺失值、層次最少、還有神經(jīng)元!本文沒有什么高級(jí)深?yuàn)W的東西,相信你一定能夠掌握它。