快捷方式

簡介 || 張量 || Autograd || 構建模型 || TensorBoard 支援 || 訓練模型 || 理解模型

使用 PyTorch 構建模型

建立日期:2021 年 11 月 30 日 | 最後更新:2024 年 10 月 15 日 | 最後驗證:2024 年 11 月 5 日

請觀看下方影片或在 YouTube 上觀看。

torch.nn.Moduletorch.nn.Parameter

在本影片中,我們將討論 PyTorch 為構建深度學習網路提供的一些工具。

Parameter 外,我們在本影片中討論的所有類都是 torch.nn.Module 的子類。這是 PyTorch 的基類,旨在封裝 PyTorch 模型及其元件的特定行為。

torch.nn.Module 的一個重要行為是註冊引數。如果特定的 Module 子類具有學習權重,這些權重將表示為 torch.nn.Parameter 的例項。Parameter 類是 torch.Tensor 的子類,其特殊行為是當它們被賦值為 Module 的屬性時,它們會被新增到該模組的引數列表中。可以透過 Module 類上的 parameters() 方法訪問這些引數。

作為一個簡單的例子,這裡有一個包含兩個線性層和一個啟用函式的非常簡單的模型。我們將建立一個例項並讓它報告其引數

import torch

class TinyModel(torch.nn.Module):

    def __init__(self):
        super(TinyModel, self).__init__()

        self.linear1 = torch.nn.Linear(100, 200)
        self.activation = torch.nn.ReLU()
        self.linear2 = torch.nn.Linear(200, 10)
        self.softmax = torch.nn.Softmax()

    def forward(self, x):
        x = self.linear1(x)
        x = self.activation(x)
        x = self.linear2(x)
        x = self.softmax(x)
        return x

tinymodel = TinyModel()

print('The model:')
print(tinymodel)

print('\n\nJust one layer:')
print(tinymodel.linear2)

print('\n\nModel params:')
for param in tinymodel.parameters():
    print(param)

print('\n\nLayer params:')
for param in tinymodel.linear2.parameters():
    print(param)
The model:
TinyModel(
  (linear1): Linear(in_features=100, out_features=200, bias=True)
  (activation): ReLU()
  (linear2): Linear(in_features=200, out_features=10, bias=True)
  (softmax): Softmax(dim=None)
)


Just one layer:
Linear(in_features=200, out_features=10, bias=True)


Model params:
Parameter containing:
tensor([[ 0.0765,  0.0830, -0.0234,  ..., -0.0337, -0.0355, -0.0968],
        [-0.0573,  0.0250, -0.0132,  ..., -0.0060,  0.0240,  0.0280],
        [-0.0908, -0.0369,  0.0842,  ..., -0.0078, -0.0333, -0.0324],
        ...,
        [-0.0273, -0.0162, -0.0878,  ...,  0.0451,  0.0297, -0.0722],
        [ 0.0833, -0.0874, -0.0020,  ..., -0.0215,  0.0356,  0.0405],
        [-0.0637,  0.0190, -0.0571,  ..., -0.0874,  0.0176,  0.0712]],
       requires_grad=True)
Parameter containing:
tensor([ 0.0304, -0.0758, -0.0549, -0.0893, -0.0809, -0.0804, -0.0079, -0.0413,
        -0.0968,  0.0888,  0.0239, -0.0659, -0.0560, -0.0060,  0.0660, -0.0319,
        -0.0370,  0.0633, -0.0143, -0.0360,  0.0670, -0.0804,  0.0265, -0.0870,
         0.0039, -0.0174, -0.0680, -0.0531,  0.0643,  0.0794,  0.0209,  0.0419,
         0.0562, -0.0173, -0.0055,  0.0813,  0.0613, -0.0379,  0.0228,  0.0304,
        -0.0354,  0.0609, -0.0398,  0.0410,  0.0564, -0.0101, -0.0790, -0.0824,
        -0.0126,  0.0557,  0.0900,  0.0597,  0.0062, -0.0108,  0.0112, -0.0358,
        -0.0203,  0.0566, -0.0816, -0.0633, -0.0266, -0.0624, -0.0746,  0.0492,
         0.0450,  0.0530, -0.0706,  0.0308,  0.0533,  0.0202, -0.0469, -0.0448,
         0.0548,  0.0331,  0.0257, -0.0764, -0.0892,  0.0783,  0.0062,  0.0844,
        -0.0959, -0.0468, -0.0926,  0.0925,  0.0147,  0.0391,  0.0765,  0.0059,
         0.0216, -0.0724,  0.0108,  0.0701, -0.0147, -0.0693, -0.0517,  0.0029,
         0.0661,  0.0086, -0.0574,  0.0084, -0.0324,  0.0056,  0.0626, -0.0833,
        -0.0271, -0.0526,  0.0842, -0.0840, -0.0234, -0.0898, -0.0710, -0.0399,
         0.0183, -0.0883, -0.0102, -0.0545,  0.0706, -0.0646, -0.0841, -0.0095,
        -0.0823, -0.0385,  0.0327, -0.0810, -0.0404,  0.0570,  0.0740,  0.0829,
         0.0845,  0.0817, -0.0239, -0.0444, -0.0221,  0.0216,  0.0103, -0.0631,
         0.0831, -0.0273,  0.0756,  0.0022,  0.0407,  0.0072,  0.0374, -0.0608,
         0.0424, -0.0585,  0.0505, -0.0455,  0.0268, -0.0950, -0.0642,  0.0843,
         0.0760, -0.0889, -0.0617, -0.0916,  0.0102, -0.0269, -0.0011,  0.0318,
         0.0278, -0.0160,  0.0159, -0.0817,  0.0768, -0.0876, -0.0524, -0.0332,
        -0.0583,  0.0053,  0.0503, -0.0342, -0.0319, -0.0562,  0.0376, -0.0696,
         0.0735,  0.0222, -0.0775, -0.0072,  0.0294,  0.0994, -0.0355, -0.0809,
        -0.0539,  0.0245,  0.0670,  0.0032,  0.0891, -0.0694, -0.0994,  0.0126,
         0.0629,  0.0936,  0.0058, -0.0073,  0.0498,  0.0616, -0.0912, -0.0490],
       requires_grad=True)
Parameter containing:
tensor([[ 0.0504, -0.0203, -0.0573,  ...,  0.0253,  0.0642, -0.0088],
        [-0.0078, -0.0608, -0.0626,  ..., -0.0350, -0.0028, -0.0634],
        [-0.0317, -0.0202, -0.0593,  ..., -0.0280,  0.0571, -0.0114],
        ...,
        [ 0.0582, -0.0471, -0.0236,  ...,  0.0273,  0.0673,  0.0555],
        [ 0.0258, -0.0706,  0.0315,  ..., -0.0663, -0.0133,  0.0078],
        [-0.0062,  0.0544, -0.0280,  ..., -0.0303, -0.0326, -0.0462]],
       requires_grad=True)
Parameter containing:
tensor([ 0.0385, -0.0116,  0.0703,  0.0407, -0.0346, -0.0178,  0.0308, -0.0502,
         0.0616,  0.0114], requires_grad=True)


Layer params:
Parameter containing:
tensor([[ 0.0504, -0.0203, -0.0573,  ...,  0.0253,  0.0642, -0.0088],
        [-0.0078, -0.0608, -0.0626,  ..., -0.0350, -0.0028, -0.0634],
        [-0.0317, -0.0202, -0.0593,  ..., -0.0280,  0.0571, -0.0114],
        ...,
        [ 0.0582, -0.0471, -0.0236,  ...,  0.0273,  0.0673,  0.0555],
        [ 0.0258, -0.0706,  0.0315,  ..., -0.0663, -0.0133,  0.0078],
        [-0.0062,  0.0544, -0.0280,  ..., -0.0303, -0.0326, -0.0462]],
       requires_grad=True)
Parameter containing:
tensor([ 0.0385, -0.0116,  0.0703,  0.0407, -0.0346, -0.0178,  0.0308, -0.0502,
         0.0616,  0.0114], requires_grad=True)

這展示了 PyTorch 模型的基本結構:有一個定義模型層和其他元件的 __init__() 方法,以及一個進行計算的 forward() 方法。請注意,我們可以列印模型或其任何子模組來了解其結構。

常見層型別

線性層

神經網路中最基本的層型別是 線性全連線 層。在這種層中,每個輸入都會在一定程度上影響該層的每個輸出,影響程度由層的權重指定。如果模型有 m 個輸入和 n 個輸出,權重將是一個 m x n 的矩陣。例如

lin = torch.nn.Linear(3, 2)
x = torch.rand(1, 3)
print('Input:')
print(x)

print('\n\nWeight and Bias parameters:')
for param in lin.parameters():
    print(param)

y = lin(x)
print('\n\nOutput:')
print(y)
Input:
tensor([[0.8790, 0.9774, 0.2547]])


Weight and Bias parameters:
Parameter containing:
tensor([[ 0.1656,  0.4969, -0.4972],
        [-0.2035, -0.2579, -0.3780]], requires_grad=True)
Parameter containing:
tensor([0.3768, 0.3781], requires_grad=True)


Output:
tensor([[ 0.8814, -0.1492]], grad_fn=<AddmmBackward0>)

如果你將 x 與線性層的權重進行矩陣乘法,並加上偏置,你會發現得到輸出向量 y

另一個需要注意的重要特性是:當我們使用 lin.weight 檢查層的權重時,它報告自己是 Parameter(它是 torch.Tensor 的子類),並告訴我們它正在使用 autograd 跟蹤梯度。這是 Parameter 的預設行為,與 Tensor 不同。

線性層廣泛應用於深度學習模型。最常見的使用場景之一是分類器模型,通常會在末尾有一個或多個線性層,其中最後一層將有 n 個輸出,n 是分類器處理的類別數量。

卷積層

卷積 層用於處理具有高度空間相關性的資料。它們在計算機視覺中非常常用,用於檢測特徵的緊密分組,然後將這些分組組合成更高級別的特徵。它們也出現在其他上下文中,例如在自然語言處理應用中,詞的即時上下文(即序列中附近的詞)會影響句子的含義。

我們在之前的影片中看到了 LeNet5 中的卷積層是如何工作的

import torch.functional as F


class LeNet(torch.nn.Module):

    def __init__(self):
        super(LeNet, self).__init__()
        # 1 input image channel (black & white), 6 output channels, 5x5 square convolution
        # kernel
        self.conv1 = torch.nn.Conv2d(1, 6, 5)
        self.conv2 = torch.nn.Conv2d(6, 16, 3)
        # an affine operation: y = Wx + b
        self.fc1 = torch.nn.Linear(16 * 6 * 6, 120)  # 6*6 from image dimension
        self.fc2 = torch.nn.Linear(120, 84)
        self.fc3 = torch.nn.Linear(84, 10)

    def forward(self, x):
        # Max pooling over a (2, 2) window
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # If the size is a square you can only specify a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:]  # all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

讓我們分解這個模型卷積層中發生的事情。從 conv1 開始

  • LeNet5 旨在接收 1x32x32 的黑白影像。卷積層建構函式的第一個引數是輸入通道數。 在這裡,它是 1。如果我們構建這個模型來處理 3 個顏色通道,它將是 3。

  • 卷積層就像一個掃描影像的視窗,尋找它能識別的模式。這些模式被稱為 特徵, 卷積層的一個引數是我們希望它學習的特徵數量。這是建構函式的第二個引數,即輸出特徵的數量。 在這裡,我們要求我們的層學習 6 個特徵。

  • 就在上面,我把卷積層比作一個視窗——但視窗有多大?第三個引數是視窗或核大小。 在這裡,“5”表示我們選擇了一個 5x5 的核。(如果你想要一個高寬不同的核,你可以為這個引數指定一個元組——例如,(3, 5) 來獲得一個 3x5 的卷積核。)

卷積層的輸出是 啟用圖 ——它是輸入張量中特徵存在情況的空間表示。conv1 將為我們提供一個 6x28x28 的輸出張量;6 是特徵數量,28 是我們圖的高度和寬度。(28 是因為當在 32 畫素的行上掃描 5 畫素的視窗時,只有 28 個有效位置。)

然後我們將卷積的輸出透過一個 ReLU 啟用函式(稍後會詳細介紹啟用函式),再透過一個最大池化層。最大池化層將啟用圖中彼此靠近的特徵組合在一起。它透過減小張量來實現這一點,將輸出中每 2x2 的單元格組合併為一個單元格,並將進入其中的 4 個單元格的最大值賦給這個單元格。這為我們提供了一個較低解析度的啟用圖版本,維度為 6x14x14。

我們的下一個卷積層 conv2 期望有 6 個輸入通道(對應於第一層尋找的 6 個特徵),有 16 個輸出通道,以及一個 3x3 的核。它輸出一個 16x12x12 的啟用圖,該圖再次被最大池化層縮小到 16x6x6。在將此輸出傳遞給線性層之前,它被重塑為 16 * 6 * 6 = 576 元素的向量,供下一層使用。

有用於處理一維、二維和三維張量的卷積層。卷積層建構函式還有更多可選引數,包括輸入中的步長(例如,只掃描每第二個或每第三個位置)、填充(以便你可以掃描到輸入的邊緣)等等。請參閱文件以獲取更多資訊。

迴圈層

迴圈神經網路(或 RNN)用於處理序列資料——從科學儀器的時序測量到自然語言句子再到 DNA 核苷酸,無所不包。RNN 透過維護一個 隱藏狀態 來實現這一點,隱藏狀態充當對它在序列中迄今為止所見內容的記憶。

RNN 層——或其變體 LSTM(長短期記憶)和 GRU(門控迴圈單元)——的內部結構相當複雜,超出了本影片的範圍,但我們將透過一個基於 LSTM 的詞性標註器(一種分類器,告訴你一個詞是名詞、動詞等等)向你展示它的實際應用。

class LSTMTagger(torch.nn.Module):

    def __init__(self, embedding_dim, hidden_dim, vocab_size, tagset_size):
        super(LSTMTagger, self).__init__()
        self.hidden_dim = hidden_dim

        self.word_embeddings = torch.nn.Embedding(vocab_size, embedding_dim)

        # The LSTM takes word embeddings as inputs, and outputs hidden states
        # with dimensionality hidden_dim.
        self.lstm = torch.nn.LSTM(embedding_dim, hidden_dim)

        # The linear layer that maps from hidden state space to tag space
        self.hidden2tag = torch.nn.Linear(hidden_dim, tagset_size)

    def forward(self, sentence):
        embeds = self.word_embeddings(sentence)
        lstm_out, _ = self.lstm(embeds.view(len(sentence), 1, -1))
        tag_space = self.hidden2tag(lstm_out.view(len(sentence), -1))
        tag_scores = F.log_softmax(tag_space, dim=1)
        return tag_scores

建構函式有四個引數

  • vocab_size 是輸入詞彙表中的單詞數量。每個單詞在 vocab_size 維空間中是一個 one-hot 向量(或單位向量)。

  • tagset_size 是輸出集合中的標籤數量。

  • embedding_dim 是詞彙表 嵌入 空間的大小。嵌入將詞彙表對映到低維空間,在該空間中,含義相似的詞彼此靠近。

  • hidden_dim 是 LSTM 記憶的大小。

輸入將是句子,單詞表示為 one-hot 向量的索引。然後嵌入層將這些向量對映到 embedding_dim 維空間。LSTM 接收這個嵌入序列並對其進行迭代,生成長度為 hidden_dim 的輸出向量。最後的線性層充當分類器;對最後一層的輸出應用 log_softmax() 會將輸出轉換為一組歸一化的估計機率,表示給定單詞對映到給定標籤的機率。

如果你想看到這個網路的實際應用,請檢視 pytorch.org 上的序列模型和 LSTM 網路教程。

Transformer

Transformer 是多用途網路,憑藉 BERT 等模型在自然語言處理領域取得了最先進的成果。Transformer 架構的討論超出了本影片的範圍,但 PyTorch 有一個 Transformer 類,允許你定義 Transformer 模型的整體引數——注意力頭的數量、編碼器和解碼器層的數量、dropout 和啟用函式等。(甚至可以使用這一個類構建 BERT 模型,只需提供正確的引數!)torch.nn.Transformer 類還包含封裝各個元件(TransformerEncoderTransformerDecoder)和子元件(TransformerEncoderLayerTransformerDecoderLayer)的類。有關詳細資訊,請查閱 Transformer 類的文件

其他層和函式

資料操作層

還有其他層型別在模型中執行重要功能,但不參與學習過程本身。

最大池化(及其孿生兄弟最小池化)透過組合單元格來減小張量,並將輸入單元格的最大值賦給輸出單元格(我們已經看到了這一點)。例如

tensor([[[0.5036, 0.6285, 0.3460, 0.7817, 0.9876, 0.0074],
         [0.3969, 0.7950, 0.1449, 0.4110, 0.8216, 0.6235],
         [0.2347, 0.3741, 0.4997, 0.9737, 0.1741, 0.4616],
         [0.3962, 0.9970, 0.8778, 0.4292, 0.2772, 0.9926],
         [0.4406, 0.3624, 0.8960, 0.6484, 0.5544, 0.9501],
         [0.2489, 0.8971, 0.7499, 0.1803, 0.9571, 0.6733]]])
tensor([[[0.7950, 0.9876],
         [0.9970, 0.9926]]])

如果你仔細觀察上面的值,你會發現最大池化輸出中的每個值都是 6x6 輸入中每個象限的最大值。

歸一化層 在將一層輸出饋送到另一層之前,對其進行重新居中和歸一化。對中間張量進行居中和縮放具有許多有益效果,例如讓你在不出現梯度爆炸/消失的情況下使用更高的學習率。

tensor([[[ 7.7375, 23.5649,  6.8452, 16.3517],
         [19.5792, 20.3254,  6.1930, 23.7576],
         [23.7554, 20.8565, 18.4241,  8.5742],
         [22.5100, 15.6154, 13.5698, 11.8411]]])
tensor(16.2188)
tensor([[[-0.8614,  1.4543, -0.9919,  0.3990],
         [ 0.3160,  0.4274, -1.6834,  0.9400],
         [ 1.0256,  0.5176,  0.0914, -1.6346],
         [ 1.6352, -0.0663, -0.5711, -0.9978]]],
       grad_fn=<NativeBatchNormBackward0>)
tensor(3.3528e-08, grad_fn=<MeanBackward0>)

執行上面的單元格,我們已經為輸入張量添加了一個較大的縮放因子和偏移量;你應該會看到輸入張量的 mean() 在 15 附近。透過歸一化層執行後,你會看到值變小了,並且聚集在零附近——事實上,均值應該非常小(> 1e-8)。

這是有益的,因為許多啟用函式(下面討論)在接近 0 的地方具有最強的梯度,但有時對於使它們遠離零的輸入會遭受梯度消失或爆炸的問題。將資料保持在最陡峭梯度區域附近,往往意味著更快、更好的學習和更高的可行學習率。

Dropout 層 是一種鼓勵模型中 稀疏表示 的工具——也就是說,推動它使用更少的資料進行推斷。

Dropout 層透過在 訓練期間 隨機設定輸入張量的部分來工作——Dropout 層在推理時總是關閉的。這迫使模型針對這個掩蔽或縮減的資料集進行學習。例如

tensor([[[0.0000, 0.0000, 0.0000, 0.2878],
         [0.0000, 0.6824, 0.0000, 0.5920],
         [0.0000, 0.0000, 1.3319, 0.5738],
         [0.5676, 0.8335, 0.9647, 0.2928]]])
tensor([[[0.0000, 0.0000, 0.2098, 0.0000],
         [0.0000, 0.6824, 0.0000, 0.0000],
         [0.0000, 0.0000, 0.0000, 0.5738],
         [0.0000, 0.8335, 0.0000, 0.2928]]])

上面,你可以看到 dropout 對示例張量的影響。你可以使用可選的 p 引數設定單個權重 dropout 的機率;如果不設定,預設為 0.5。

啟用函式

啟用函式使深度學習成為可能。神經網路實際上是一個程式——具有許多引數——它 模擬數學函式。如果所有我們做的就是重複地用層權重乘以張量,我們只能模擬 線性函式; 此外,擁有許多層就沒有意義了,因為整個網路可以簡化為單次矩陣乘法。在層之間插入 非線性 啟用函式是深度學習模型能夠模擬任何函式,而不僅僅是線性函式的原因。

torch.nn.Module 包含了封裝所有主要啟用函式(包括 ReLU 及其許多變體、Tanh、Hardtanh、sigmoid 等)的物件。它還包括其他函式,例如 Softmax,這些函式在模型的輸出階段最有用。

損失函式

損失函式告訴我們模型的預測與正確答案相差多遠。PyTorch 包含多種損失函式,包括常見的 MSE(均方誤差 = L2 範數)、交叉熵損失和負對數似然損失(對分類器有用)等。

指令碼總執行時間: ( 0 分鐘 0.021 秒)

由 Sphinx-Gallery 生成的畫廊

文件

訪問 PyTorch 的全面開發者文件

檢視文件

教程

獲取面向初學者和高階開發者的深度教程

檢視教程

資源

查詢開發資源並獲得問題解答

檢視資源