【深度学习】保姆级教程,用PyTorch构建第一个神经网络
PyTorch是一個基于python的科學計算包,主要針對兩類人群:
作為NumPy的替代品,可以利用GPU的性能進行計算
作為一個高靈活性、速度快的深度學習平臺
在PyTorch中搭建神經網絡并使用真實的天氣信息預測明天是否會下雨。
預處理 CSV 文件并將數據轉換為張量
使用 PyTorch 構建神經網絡模型
使用損失函數和優化器來訓練模型
評估模型并了解分類不平衡的危害
寫在前面
在開始構建神經網絡之前,首先了解一下幾個重要概念。
torch.Tensor
一個多維數組,支持諸如backward()等的自動求導操作,同時也保存了張量的梯度。nn.Module
神經網絡模塊。是一種方便封裝參數的方式,具有將參數移動到GPU、導出、加載等功能。nn.Parameter
張量的一種,當它作為一個屬性分配給一個Module時,它會被自動注冊為一個參數。autograd.Function
實現了自動求導前向和反向傳播的定義,每個Tensor至少創建一個Function節點,該節點連接到創建Tensor的函數并對其歷史進行編碼。
導入相關模塊
#?pip?install?torch import?torchimport?os import?numpy?as?np import?pandas?as?pd from?tqdm?import?tqdm import?seaborn?as?sns from?pylab?import?rcParams import?matplotlib.pyplot?as?plt from?matplotlib?import?rc from?sklearn.model_selection?import?train_test_split from?sklearn.metrics?import?confusion_matrix,?classification_report from?torch?import?nn,?optim import?torch.nn.functional?as?F%matplotlib?inline %config?InlineBackend.figure_format='retina'sns.set(style='whitegrid',?palette='muted',?font_scale=1.2) HAPPY_COLORS_PALETTE?=?["#01BEFE",?"#FFDD00",?"#FF7D00",?"#FF006D",?"#93D30C",?"#8F00FF"] sns.set_palette(sns.color_palette(HAPPY_COLORS_PALETTE)) rcParams['figure.figsize']?=?12,?6 RANDOM_SEED?=?42 np.random.seed(RANDOM_SEED) torch.manual_seed(RANDOM_SEED)數據集
我們的數據集包含來自多個澳大利亞氣象站的每日天氣信息。本次目標是要回答一個簡單的問題:明天會下雨嗎?
數據集來自Kaggle[2],也可以在公眾號「機器學習研習院」后臺回復【rain】免費自助獲取數據集。
接下來先通過Pandas讀取導入數據集。
df?=?pd.read_csv('./data/weatherAUS.csv') df.head()這里有很多特征列。也有很多NaN。下面來看看整體數據集大小。
df.shape(145460, 23)從數據集形狀看,這里數據還不少,超過14.5w條數據。
數據預處理
本節中,我們并不希望數據集和目標問題有多復雜,嘗試將通過刪除大部分數據來簡化這個問題。這里只使用4個特征來預測明天是否會下雨。在你實際案例中,根據實際問題,特征數量可以比這多,也可以比這少,只要注意下面輸入數據維度即可。
cols?=?['Rainfall',?'Humidity3pm',?'Pressure9am',?'RainToday',?'RainTomorrow'] df?=?df[cols]特征轉換
因為神經網絡只能處理數字。所以我們將把文字的?yes 和 no?分別轉換為數字1 和 0。
缺失值處理
刪除缺少值的行。也許會有更好的方法來處理這些缺失的行,但我們這里將簡單地處理,直接刪除含有缺失值的行。
樣本不平衡處理
到目前為止,我們有了一個可以使用的數據集。這里我們需要回答的一個重要問題是 --?我們的數據集是否平衡??或者?明天到底會下多少次雨?
因此通過sns.countplot函數直接定性分析整個樣本集中是否下雨分別多少次,以此判斷正負樣本(是否有雨)是否平衡。
sns.countplot(df.RainTomorrow);從結果看,下雨次數明顯比不下雨次數要少很多。再通過具體定量計算正負樣本數。
df.RainTomorrow.value_counts()?/?df.shape[0]0.0 0.778762 1.0 0.221238 Name: RainTomorrow, dtype: float64事情看起來不妙。約78%的數據點表示明天不會下雨。這意味著一個預測明天是否下雨的模型在78%的時間里是正確的。
如果想要解決此次樣本不平衡,以緩解其帶來的影響,可以參考云朵君先前文章機器學習中樣本不平衡,怎么辦?而這里,我們暫不做任何處理,但愿他對結果影響不大。
樣劃分訓練集和測試集
數據預處理的最后一步是將數據分割為訓練集和測試集。這一步大家應該并不陌生,可以直接使用train_test_split()。
數據類型轉換
為了符合 PyTorch 所需求的數據類型。使用 python標準庫將數據加載到numpy數組里。然后將這個數組轉化成將全部數據轉換為張量(torch.Tensor)。
注意:Torch張量和NumPy數組將共享它們的底層內存位置,因此當一個改變時,另外也會改變。
X_train.head()PyTorch中也是非常方便,直接通過from_numpy直接轉換。
X_train?=?torch.from_numpy(X_train.to_numpy()).float() y_train?=?torch.squeeze(torch.from_numpy(y_train.to_numpy()).float())X_test?=?torch.from_numpy(X_test.to_numpy()).float() y_test?=?torch.squeeze(torch.from_numpy(y_test.to_numpy()).float())print(X_train.shape,?y_train.shape) print(X_test.shape,?y_test.shape)torch.Size([99751, 4]) torch.Size([99751]) torch.Size([24938, 4]) torch.Size([24938])到目前為止,所有數據準備工作已經結束。
構建神經網絡
接下來我們將使用PyTorch建立一個簡單的神經網絡(NN),嘗試預測明天是否會下雨。本次構建的神經網絡結構分為三個層,輸入層、輸出層和隱藏層。
輸入層:?我們的輸入包含四列數據:"Rainfall, Humidity3pm, RainToday, Pressure9am"(降雨量,濕度下午3點,今天下雨,壓力上午9點)。將為此創建一個適當的輸入層。
輸出層:?輸出將是一個介于 0 和 1 之間的數字,代表模型認為明天下雨的可能性。預測將由網絡的輸出層提供給我們。
隱藏層:?將在輸入層和輸出層之間添加兩個隱藏層。這些層的參數(神經元)將決定最終輸出。所有層都將是全連接的,即全連接層。
一個神經網絡的典型訓練過程如下:
定義包含一些可學習參數(或者叫權重)的神經網絡
在輸入數據集上迭代
通過網絡處理輸入
計算loss(輸出和正確答案的距離)
將梯度反向傳播給網絡的參數
更新網絡的權重,一般使用一個簡單的規則:weight = weight - learning_rate * gradient
可以使用torch.nn包來構建神經網絡。即使用 PyTorch 構建神經網絡的一種簡單方法是創建一個繼承自?torch.nn.Module?的類。
這里將nn.Module子類化(它本身是一個類并且能夠跟蹤狀態)。在這種情況下,我們要創建一個類,該類包含前進步驟的權重,偏差和方法。nn.Module具有許多我們將要使用的屬性和方法(例如.parameters()和.zero_grad())。
class?Net(nn.Module):def?__init__(self,?n_features):super(Net,?self).__init__()self.fc1?=?nn.Linear(n_features,?5)self.fc2?=?nn.Linear(5,?3)self.fc3?=?nn.Linear(3,?1)def?forward(self,?x):x?=?F.relu(self.fc1(x))x?=?F.relu(self.fc2(x))return?torch.sigmoid(self.fc3(x))我們只需要定義 forward 函數,backward函數會在使用autograd時自動定義,backward函數用來計算導數。我們可以在 forward 函數中使用任何針對張量的操作和計算。
可視化神經元
這里的可視化神經元主要基于https://github.com/Prodicode/ann-visualizer,完整神經網絡可視化獲取方式:公眾號「機器學習研習院」消息框回復 【神經網絡可視化】獲取。
我們首先在構造函數中創建模型的層。forward()方法是奇跡發生的地方。它接受輸入??并允許它流過每一層。
有一個相應的由PyTorch定義到向后傳遞backward()方法,它允許模型從當前發生的誤差中學習,并修正模型參數。
激活函數
細心的讀者可能會注意到構建的神經網絡中調用?F.relu?和?torch.sigmoid?。這些是激活函數,那我們為什么需要這些?
神經網絡的一個很酷的特性是它們可以近似非線性函數。事實上,已經證明它們可以逼近任何函數[3]。
不過,如果想通過堆疊線性層來逼近非線性函數,此時就需要激活函數。激活函數可以讓神經網絡擺脫線性世界并學習更多。通常將其應用于某個層的輸出。
ReLU
從最廣泛使用的激活函數之一的 ReLU 定義開始:
該激活函數簡單易行,其結果就是輸入值與零比較,得到的最大值。
從可視化結果看
ax?=?plt.gca() plt.plot(np.linspace(-1,?1,?5),?F.relu(torch.linspace(-1,?1,?steps=5)).numpy() ) ax.set_ylim([-1.5,?1.5]);Sigmoid
它被定義為
當需要進行二元決策 / 分類(回答yes或no)時,sigmoid?函數是很有用的。sigmoid 以一種超級的方式將輸入值壓縮在 0 和 1 之間。
從可視化結果看
ax?=?plt.gca()plt.plot(np.linspace(-10,?10,?100),?torch.sigmoid(torch.linspace(-10,?10,?steps=100)).numpy() ) ax.set_ylim([-0.5,?1.5]);訓練神經網絡
目前為止,我們已經看到了如何定義網絡,接下來需要找到預測明天是否會下雨的參數。即需要找到該模型應用于此次問題的最佳參數。而要想做到這點,首先需要一些評價指標來告訴我們,該模型目前做得有多好。
接下來需要計算損失,并更新網絡的權重。
損失函數
一個損失函數接受一對(output, target)作為輸入,計算一個值來估計網絡的輸出和目標值相差多少。BCELoss[4]是一個損失函數,其度量兩個向量之間的差。
而在我們的例子中,這兩個向量即是我們的模型的預測和實際值。該損失函數的期望值由 sigmoid 函數輸出。該值越接近 0,模型效果越好。
但是我們如何找到最小化損失函數的參數呢?
優化器
假設我們的神經網絡的每個參數都是一個旋鈕。優化器的工作是為每個旋鈕找到完美的位置,使損失接近0。
實戰中,模型可能包含數百萬甚至數十億個參數。有這么多旋鈕要轉,如果有一個高效的優化器可以快速找到解決方案,那就完美了。
而理想很豐滿,現實很骨感。深度學習中的優化效果只能達到令人滿意的結果。在實踐中,可以提供可接受的準確性的足夠好的參數,就應該心滿意足了。
在使用神經網絡時,PyTorch中提供了許多經過良好調試過的優化器,可能希望使用各種不同的更新規則,如SGD、Nesterov-SGD、Adam、RMSProp等。雖然你可以從這些優化器中選擇,一般情況下,首選的還是Adam[5]。
optimizer?=?optim.Adam(net.parameters(),?lr=0.001)一個模型的可學習參數可以通過net.parameters()。
自然地,優化器需要輸入參數。第二個參數lr?是?learning rate?(學習率),這是要找到的最優參數和到達最優解的速度之間的權衡。而為此找到最優解的方法或過程可能是黑魔法和大量的暴力“實驗”。
在 GPU 上計算
在 GPU 上進行大規模并行計算是現代深度學習的推動因素之一。為此,您將需要配置 NVIDIA GPU。
如果你的設備上裝有GPU,PyTorch 中可以非常輕松地將所有計算傳輸到 GPU。
我們首先檢查 CUDA 設備是否可用。然后,我們將所有訓練和測試數據傳輸到該設備。最后移動模型和損失函數。
張量可以使用.to方法移動到任何設備(device)上。
device?=?torch.device("cuda:0"?if?torch.cuda.is_available()?else?"cpu")X_train?=?X_train.to(device) y_train?=?y_train.to(device)X_test?=?X_test.to(device) y_test?=?y_test.to(device)net?=?net.to(device) criterion?=?criterion.to(device)尋找最優參數
擁有損失函數固然很好,追蹤模型的準確性是一件更容易理解的事情,而一般通過定義準確性來做模型評價。
def?calculate_accuracy(y_true,?y_pred):predicted?=?y_pred.ge(.5).view(-1)return?(y_true?==?predicted).sum().float()?/?len(y_true)我們定義一個預值,將連續概率值轉換為二分類值。即將每個低于 0.5 的值轉換為 0,高于0.5的值設置為 1。最后計算正確值的百分比。
所有的模塊都準備好了,我們可以開始訓練我們的模型了。
def?round_tensor(t,?decimal_places=3):return?round(t.item(),?decimal_places)for?epoch?in?range(1000):????y_pred?=?net(X_train)y_pred?=?torch.squeeze(y_pred)train_loss?=?criterion(y_pred,?y_train)if?epoch?%?100?==?0:train_acc?=?calculate_accuracy(y_train,?y_pred)y_test_pred?=?net(X_test)y_test_pred?=?torch.squeeze(y_test_pred)test_loss?=?criterion(y_test_pred,?y_test)test_acc?=?calculate_accuracy(y_test,?y_test_pred)print(f'''epoch?{epoch}Train?set?-?loss:?{round_tensor(train_loss)},?accuracy:?{round_tensor(train_acc)}Test??set?-?loss:?{round_tensor(test_loss)},?accuracy:?{round_tensor(test_acc)}''')optimizer.zero_grad()??#?清零梯度緩存train_loss.backward()?#?反向傳播誤差optimizer.step()??#?更新參數epoch 0 Train set - loss: 0.94, accuracy: 0.779 Test set - loss: 0.94, accuracy: 0.778epoch 100 Train set - loss: 0.466, accuracy: 0.78 Test set - loss: 0.466, accuracy: 0.779 ... epoch 900 Train set - loss: 0.41, accuracy: 0.833 Test set - loss: 0.408, accuracy: 0.834在訓練期間,我們向模型傳輸數據共計10,000次。每次測量損失時,將誤差傳播到模型中,并要求優化器找到更好的參數。
用?zero_grad()?方法清零所有參數的梯度緩存,然后進行隨機梯度的反向傳播。如果忽略了這一步,梯度將會累積,導致模型不可用。
測試集上的準確率為 83.4% 聽起來挺合理,但可能要讓你失望了,這樣的結果并不是很理想,接下來看看是如何不合理。
但首先我們需要學習如何保存和加載訓練好的模型。
保存模型
訓練一個好的模型可能需要很多時間。可能是幾周、幾個月甚至幾年。如果在訓練過程了忘記保存,或不知道需要保存模型,這將會是非常痛苦的事情。因此這里需要確保我們知道如何保存寶貴的工作。其實保存很容易,但你不能忘記這件事。
MODEL_PATH?=?'model.pth'??#?后綴名為?.pth torch.save(net,?MODEL_PATH)?#?直接使用torch.save()函數即可當然恢復模型也很容易,直接使用?torch.load()?函數即可。
net?=?torch.load(MODEL_PATH)評估
如果知道你的模型會犯什么樣的錯誤不是很好嗎?當然,這一點是非常難做到的。但是你可以通過一定的方法得到一個估計值。而僅使用準確性來評估并不是一個好方法,尤其在樣本不平衡的二分類數據集上。仔細回想一下,我們的數據是一個很不平衡的數據集,其幾乎不包含明天會降雨樣本。
深入研究模型性能的一種方法是評估每個類的精確度和召回率。在我們的例子中,將是結果標簽分別是?no rain?和?rain?。
classes?=?['No?rain',?'Raining']y_pred?=?net(X_test) y_pred?=?y_pred.ge(.5).view(-1).cpu() y_test?=?y_test.cpu()print(classification_report(y_test,?y_pred,?target_names=classes))precision recall f1-score supportNo rain 0.84 0.97 0.90 19413Raining 0.76 0.37 0.50 5525accuracy 0.83 24938macro avg 0.80 0.67 0.70 24938 weighted avg 0.82 0.83 0.81 24938精確度最大值為1,表明該模型只適用于識別相關的樣本。召回率最大值為1,表示模型可以在這個類的數據集中找到所有相關的示例。
可以看到模型在無雨類方面表現良好,因為樣本中無雨類樣本數量較大。不幸的是,我們不能完全相信有雨類的預測,因為樣本不平衡導致模型傾向于無雨類。
可以通過查看一個簡單的混淆矩陣來評估二分類效果。
cm?=?confusion_matrix(y_test,?y_pred) df_cm?=?pd.DataFrame(cm,?index=classes,?columns=classes)hmap?=?sns.heatmap(df_cm,?annot=True,?fmt="d") hmap.yaxis.set_ticklabels(hmap.yaxis.get_ticklabels(),?rotation=0,?ha='right') hmap.xaxis.set_ticklabels(hmap.xaxis.get_ticklabels(),?rotation=30,?ha='right') plt.ylabel('True?label') plt.xlabel('Predicted?label');你可以清楚地看到,當我們的模型預測要下雨時,我們應該抱有懷疑的態度。
模型預測
使用一些假設的例子上測試下模型。
def?will_it_rain(rainfall,?humidity,?rain_today,?pressure):t?=?torch.as_tensor([rainfall,?humidity,?rain_today,?pressure])?\.float()?\.to(device)output?=?net(t)return?output.ge(0.5).item()這個函數將根據模型預測返回一個布爾值。讓我們試試看:
will_it_rain(rainfall=10,?humidity=10,?rain_today=True,?pressure=2) >>>?Truewill_it_rain(rainfall=0,?humidity=1,?rain_today=False,?pressure=100) >>>?False根據一些參數得到了兩種不同的返回值。到這里為止,模型已準備好部署來,但實際情況下,請不要匆忙部署,因為該模型并不是一個最佳的狀態,只是用來掩飾如何使用PyTorch搭建模型!
寫在最后
如果你看到這里,將給你點個贊!因為你現在成功搭建了一個可以預測天氣的神經網絡深度學習模型。雖然此次用PyTorch搭建的深度學習模型是一個入門級別的模型,但其他更加復雜的神經網絡模型的核心步驟與此類似。
說實話,構建性能良好的模型真的很難,但在多次搭建模型過程中,你會不斷學到一些技巧,并能夠不斷進步,這將會幫助你以后做的更好。
參考資料
[1]?
參考原文:?https://curiousily.com/posts/build-your-first-neural-network-with-pytorch/
[2]?Kaggle:?https://www.kaggle.com/jsphyg/weather-dataset-rattle-package
[3]?已經證明它們可以逼近任何函數:?https://en.wikipedia.org/wiki/Universal_approximation_theorem
[4]?BCELoss:?https://pytorch.org/docs/stable/nn.html#bceloss
[5]?Adam:?https://pytorch.org/docs/stable/optim.html#torch.optim.Adam
往期精彩回顧適合初學者入門人工智能的路線及資料下載機器學習及深度學習筆記等資料打印機器學習在線手冊深度學習筆記專輯《統計學習方法》的代碼復現專輯 AI基礎下載黃海廣老師《機器學習課程》視頻課黃海廣老師《機器學習課程》711頁完整版課件本站qq群955171419,加入微信群請掃碼:
總結
以上是生活随笔為你收集整理的【深度学习】保姆级教程,用PyTorch构建第一个神经网络的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何推送和播放RTMP H265流 (R
- 下一篇: Windows平台摄像头或屏幕RTMP推