引入
完成一項機器學習任務時的步驟:
- 數據准備(可以導入、也可以通過爬蟲爬取)。
- 數據預處理(數據格式的統一、必要的數據轉換),並划分訓練集和測試集。
- 選擇模型,並設定損失函數和優化函數以及對應的超參數。
- 用模型擬合訓練集數據,在驗證集/測試集上計算模型表現。
- 利用可視化,對訓練結果進行評價。
深度學習和機器學習的差異:
- 代碼實現上,深度學習樣本量大;batch訓練策略需要在訓練時每次讀取固定數量的樣本。
- 模型訓練上,深度神經網絡層數較多,有一些用於實現特定功能的層(如卷積層、池化層、批正則化層、LSTM層等),需要進行定制化。
- 訓練時,深度學習需要“放入”GPU進行訓練,將損失函數反向傳播回網絡最前面的層,同時使用優化器調整網絡參數。后續計算一些指標還需要把數據“放回”CPU。
深度學習任務步驟:
1.基本配置
導入必須的包
import os
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import torch.optim as optimizer
設置超參數
batch_size = 16 # batch中的樣本數
lr = 1e-4 # 初始學習率
max_epochs = 100 # 訓練次數
配置GPU
# 方案一:使用x,這種情況如果使用GPU不需要設置
os.environ['CUDA_VISIBLE_DEVICES'] = '1' # Only device 1 will be seen
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1' # Devices 0 and 1 will be visible
# 方案二:使用“device”,后續對要使用GPU的變量用.to(device)即可
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
2.數據讀入
構建Dataset
1.直接使用PyTorch倉庫中准備好的數據
FashionMNIST 是一個替代 MNIST 手寫數字 的圖像數據集
training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor()
)
root
:訓練/測試數據的存儲路徑train
:強調是訓練集還是測試集download=True
:如果根路徑的數據不可用,就從互聯網下載數據transform
andtarget_transform
:指定特征和標簽的轉換
2.自定義dataset類進行數據的讀取以及初始化
__init__
: 用於向類中傳入外部參數,同時定義樣本集__getitem__
: 用於逐個讀取樣本集合中的元素,可以進行一定的變換,並將返回訓練/驗證所需的數據__len__
: 用於返回數據集的樣本數
使用DataLoader按批次讀入數據
DataLoader使用iterative的方式不斷讀入批次數據
train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=4, shuffle=True, drop_last=True)
- batch_size:樣本是按“批”讀入的,batch_size就是每次讀入的樣本數
- num_workers:有多少個進程用於讀取數據
- shuffle:是否將讀入的數據打亂
- drop_last:對於樣本最后一部分沒有達到批次數的樣本,不再參與訓練
3.模型構建
神經網絡的構造
PyTorch中神經網絡構造一般是基於 Module 類的模型來完成的,它讓模型構造更加靈活。
Module 類是 nn 模塊里提供的一個模型構造類,是所有神經網絡模塊的基類,我們可以繼承它來定義我們想要的模型。
下面為繼承 Module 類構造多層感知機的示例。這里定義的 MLP 類重載了 Module 類的 init 函數和 forward 函數。它們分別用於創建模型參數和定義前向計算(正向傳播)。
import torch
from torch import nn
class MLP(nn.Module):
# 聲明帶有模型參數的層,這里聲明了兩個全連接層
def __init__(self, **kwargs):
# 調用MLP父類Block的構造函數來進行必要的初始化。這樣在構造實例例時還可以指定其他函數
super(MLP, self).__init__(**kwargs)
self.hidden = nn.Linear(784, 256)
self.act = nn.ReLU()
self.output = nn.Linear(256,10)
# 定義模型的前向計算,即如何根據輸入x計算返回所需要的模型輸出
def forward(self, x):
o = self.act(self.hidden(x))
return self.output(o)
神經網絡中常見的層
深度學習的一個魅力在於神經網絡中各式各樣的層,例如全連接層、卷積層、池化層與循環層等等。雖然PyTorch提供了大量常用的層,但有時候我們依然希望自定義層。
下面是使用 Module 來自定義層,從而可以被反復調用的示例:
-
不含模型參數的層
import torch from torch import nn class MyLayer(nn.Module): def __init__(self, **kwargs): super(MyLayer, self).__init__(**kwargs) def forward(self, x): return x - x.mean()
-
含模型參數的層
Parameter 類其實是 Tensor 的子類,如果一 個 Tensor 是 Parameter,那么它會自動被添加到模型的參數列表里。所以在自定義含模型參數的層時,我們應該將參數定義成 Parameter,除了直接定義成 Parameter 類外,還可以使用ParameterList 和 ParameterDict 分別定義參數的列表和字典。
class MyListDense(nn.Module): def __init__(self): super(MyListDense, self).__init__() self.params = nn.ParameterList([nn.Parameter(torch.randn(4, 4)) for i in range(3)]) self.params.append(nn.Parameter(torch.randn(4, 1))) def forward(self, x): for i in range(len(self.params)): x = torch.mm(x, self.params[i]) return x net = MyListDense() print(net)
-
二維卷積層
二維卷積層將輸入和卷積核做互相關運算,並加上一個標量偏差來得到輸出。卷積層的模型參數包括了卷積核和標量偏差。在訓練模型的時候,通常我們先對卷積核隨機初始化,然后不不斷迭代卷積核和偏差。
-
池化層
池化層每次對輸入數據的一個固定形狀窗口(又稱池化窗口)中的元素計算輸出。不同於卷積層里計算輸入和核的互相關性,池化層直接計算池化窗口內元素的最大值或者平均值。
4.損失函數
損失函數 | 功能 | 代碼 |
---|---|---|
二分類交叉熵損失函數 | 計算二分類任務時的交叉熵(Cross Entropy)函數。 | torch.nn.BCELoss() |
交叉熵損失函數 | 計算交叉熵的函數。 | torch.nn.CrossEntropyLoss() |
L1損失函數 | 計算輸出y 和真實標簽target 之間的差值的絕對值。 |
torch.nn.L1Loss() |
MSE損失函數 | 計算輸出y 和真實標簽target 之差的平方。 |
torch.nn.MSELoss() |
平滑L1 (Smooth L1)損失函數 | L1的平滑輸出,其功能是減輕離群點帶來的影響。 | torch.nn.SmoothL1Loss() |
目標泊松分布的負對數似然損失函數 | 泊松分布的負對數似然損失函數。 | torch.nn.PoissonNLLLoss() |
KL散度 | 計算相對熵。用於連續分布的距離度量,並且對離散采用的連續輸出空間分布進行回歸通常很有用。 | torch.nn.KLDivLoss() |
MarginRankingLoss | 計算兩個向量之間的相似度,用於排序任務。該方法計算兩組數據之間的差異。 | torch.nn.MarginRankingLoss() |
多標簽邊界損失函數 | 對於多標簽分類問題計算損失函數。 | torch.nn.MultiLabelMarginLoss() |
二分類損失函數 | 計算二分類的 logistic 損失。 | torch.nn.SoftMarginLoss() |
多分類的折頁損失 | 計算多分類的折頁損失。 | torch.nn.MultiMarginLoss() |
三元組損失 | 計算三元組損失。 | torch.nn.TripletMarginLoss() |
HingEmbeddingLoss | 對輸出的embedding結果做Hing損失計算。 | torch.nn.HingeEmbeddingLoss() |
余弦相似度 | 對於兩個向量做余弦相似度損失計算。 | torch.nn.CosineEmbeddingLoss() |
CTC損失函數 | 用於解決時序類數據的分類。計算連續時間序列和目標序列之間的損失。 | torch.nn.CTCLoss() |
5.優化器
優化器就是根據網絡反向傳播的梯度信息來更新網絡的參數,以起到降低loss函數計算值,使得模型輸出更加接近真實標簽。
Pytorch提供的優化器庫torch.optim:
-
torch.optim.ASGD
-
torch.optim.Adadelta
-
torch.optim.Adagrad
-
torch.optim.Adam
-
torch.optim.AdamW
-
torch.optim.Adamax
-
torch.optim.LBFGS
-
torch.optim.RMSprop
-
torch.optim.Rprop
-
torch.optim.SGD
-
torch.optim.SparseAdam
優化器的選擇是需要根據模型進行改變的,不存在絕對的好壞之分,需要多進行一些測試。
6.訓練與評估
在PyTorch中設置模型狀態:
- 訓練狀態,模型的參數支持反向傳播的修改;
- 驗證/測試狀態,則不能修改模型參數。
model.train() # 訓練狀態
model.eval() # 驗證/測試狀態
用for循環讀取DataLoader中的全部數據。
for data, label in train_loader:
之后將數據放到GPU上用於后續計算,此處以.cuda()為例
data, label = data.cuda(), label.cuda()
開始用當前批次數據做訓練時,應當先將優化器的梯度置零:
optimizer.zero_grad()
之后將data送入模型中訓練:
output = model(data)
根據預先定義的criterion計算損失函數:
loss = criterion(output, label)
將loss反向傳播回網絡:
loss.backward()
使用優化器更新模型參數:
optimizer.step()
這樣一個訓練過程就完成了,后續還可以計算模型准確率等指標。
驗證/測試的流程基本與訓練過程一致,不同點在於:
- 需要預先設置torch.no_grad,以及將model調至eval模式
- 不需要將優化器的梯度置零
- 不需要將loss反向回傳到網絡
- 不需要更新模型參數
7.可視化
某些任務在訓練完成后,需要對一些必要的內容進行可視化,比如分類的ROC曲線,卷積網絡中的卷積核,以及訓練/驗證過程的損失函數曲線等。
參考鏈接:
- Datawhale深入淺出PyTorch第三章:https://github.com/datawhalechina/thorough-pytorch/tree/main/%E7%AC%AC%E4%B8%89%E7%AB%A0%20PyTorch%E7%9A%84%E4%B8%BB%E8%A6%81%E7%BB%84%E6%88%90%E6%A8%A1%E5%9D%97
- PyTorch官方文檔:https://pytorch.org/tutorials/beginner/basics/data_tutorial.html#:~:text=PyTorch%20provides%20two%20data%20primitives%3A%20torch.utils.data.DataLoader%20and%20torch.utils.data.Dataset,Dataset%20to%20enable%20easy%20access%20to%20the%20samples.
- Dataset類的使用:https://www.jianshu.com/p/4818a1a4b5bd