通過實例學習 PyTorch


通過范例學習 PyTorch

本博文通過幾個獨立的例子介紹了 PyTorch 的基礎概念。

其核心,PyTorch 提供了兩個主要的特征:

  • 一個 n-維張量(n-dimensional Tensor),類似 NumPy 但是可以運行在 GPU 設備上
  • 構建和訓練神經網絡,可自動求微分

我們將使用三階多項式去擬合 y=sin(x) 的問題作為我們的例子。神經網絡將會有 4 個參數,並且將使用梯度下降通過最小化(minimizing)網絡輸出和真實輸出的歐氏距離(Euclidean distance)去擬合隨機數據。

Tensors

熱身:NumPy

在介紹 PyTorch 之前,我們首先使用 NumPy 實現一個神經網絡。

NumPy 提供了一種 n-維數組(n-dimensional)數組對象,和許多操縱這些數組的函數。NumPy 對於科學計算是一個充滿活力的框架;它不需要了解任何關於計算圖(computation graphs)、深度學習或梯度。但是,我們可以輕松地使用 NumPy 操作手動實現網絡的前向傳播和反向傳播,擬合一個三階多項式到 sine 函數。

import numpy as np
import math

# 創建隨機輸入和輸出的數據
x = np.linspace(-math.pi, math.pi, 2000)
y = np.sin(x)

# 隨機初始化權重(weight)
a = np.random.randn()
b = np.random.randn()
c = np.random.randn()
d = np.random.randn()

learning_rate = 1e-6
for t in range(2000):
    # 前向傳播:計算預測的 y
    # y = a + bx + cx^2 + dx^3
    y_pred = a + b * c + c * x ** 2 + d * x ** 3
    
    # 計算打印損失值(loss)
    loss = np.square(y_pred - y).sum()
    if t % 100 == 99:
        print(t, loss)
    
    # 反向傳播計算 a, b, c, d 關於 loss 的梯度
    grad_y_pred = 2.0 * (y_pred - y)
    grad_a = grad_y_pred.sum()
    grad_b = (grad_y_pred * x).sum()
    grad_c = (grad_y_pred * x ** 2).sum()
    grad_d = (grad_y_pred * x ** 3).sum()
    
    # 更新權重參數(weight)
    a -= learning_rate * grad_a
    b -= learning_rate * grad_b
    c -= learning_rate * grad_c
    d -= learning_rate * grad_d
    
print(f'Result: y = {a} + {b} x + {c} x^2 + {d} x^3')

PyTorch: Tensors

盡管 NumPy 是一個非常棒的框架,但是它不可以利用 GPU 去加速數值計算。對於現代深度神經網絡,GPU 通過提供了 50倍或以上 的加速,不幸地是 NumPy 對於深度學習是還不夠的。

這里我們介紹了 PyTorch 最基礎的概念:Tensor 。一個 PyTorch Tensor 從概念上與 NumPy 數組是完全相同的:Tensor 是一個 n 維數組(n-dimensional array),並且 PyTorch 提供了許多處理這些 Tensor 的函數。幕后,Tensor 記錄了一個計算圖(computation graph)和梯度,並且這些對於科學計算也是一個非常實用並充滿活力的工具。

不像 NumPy,PyTorch Tensor 可以利用 GPU 加速數值計算。為了將 PyTorch Tensor 運行在 GPU 上,你只需要簡單地指定正確的設備。

這里我們使用 PyTorch Tensor 去擬合一個三階多項式到 sine 函數。就像上面 NumPy 的例子,我們需要手動實現網絡的前向傳播和反向傳播。

import torch
import math

dtype = torch.float
device = torch.device("cpu")
# 下面這條注釋,可以使用 GPU
# device = torch.device("cuda:0")

# 創建隨機輸入輸出
x = torch.linspace(-math.pi, math.pi, 2000, device=device, dtype=dtype)
y = torch.sin(x)

# 隨機初始化權重(weight)
a = torch.randn((), device=device, dtype=dtype)
b = torch.randn((), device=device, dtype=dtype)
c = torch.randn((), device=device, dtype=dtype)
d = torch.randn((), device=device, dtype=dtype)

learnign_rate = 1e-6
for t in range(2000):
    # 前向傳播:計算預測的 y
    y_pred = a + b * c + c * x ** 2 + d * x ** 3
    
    # 計算和輸出損失值(loss)
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)
        
    # 反向傳播計算 a, b, c, d 關於損失值(loss)的梯度
    grad_y_pred = 2.0 * (y_pred - y)
    grad_a = grad_y_pred.sum()
    grad_b = (grad_y_pred * x).sum()
    grad_c = (grad_y_pred * x ** 2).sum()
    grad_d = (grad_y_pred * x ** 3).sum()
    
    # 使用梯度下降更新權重(weight)
    a -= learnign_rate * grad_a
    b -= learnign_rate * grad_b
    c -= learnign_rate * grad_c
    d -= learnign_rate * grad_d
    
print(f'Result: y = {a.item()} + {b.item()} x + {c.item()} x^2 + {d.item()} x^3')

自動求導

PyTorch: Tensor and autograd

在上面的例子中,我們必須手動實現神經網絡的前向傳播和反向傳播。

對於一個小的兩層網絡手動實現反向傳播並沒有什么大不了的,但是對於更大更復雜的網絡是一件非常可怕的事情。

幸虧的是,我們可以使用 自動微分(automatic differentiation 自動化神經網絡的反向傳播的計算。在 PyTorch 的 autograd 包正好提供了這個功能。當使用 autograd 時,神經網絡的前向傳播將定義一個 計算圖(Computational graph ,圖中的結點(nodes)將會是一個 Tensor,每條邊(edges)將會是從一個輸入 Tensor 產生輸出 Tensor 的函數。反向傳播通過計算圖讓我們輕松地計算梯度。

這聽起來有些復雜,但是實際上是相當簡單的。每一個 Tensor 代表了計算圖中的結點。如果 x 是一個 Tensor,它就有 x.requires_grad=True 然后 x.grad 就是另一個張量,其持有 x 關於某個標量值的梯度。

這里我們使用 PyTorch Tensor 和 autograd 實現使用一個三階多項式去擬合 sine 曲線的例子;現在,我們不再需要手動地實現網絡的反向傳播。

import torch
import math

dtype = torch.float
device = torch.device("cpu")
# 下面這條注釋,可以使用 GPU
# device = torch.device("cuda:0")

# 創建 Tensor
# 默認情況下,requires_grad=False 表示我們不需要計算關於這些 Tensor 的梯度
x = torch.linspace(-math.pi, math.pi, 2000, device=device, dtype=dtype)
y = torch.sin(x)

# 創建隨機權重(weight) Tensor
# 對於一個三階多項式,我們需要 4 個參數
# 設置 requires_grad=True 表明我們在反向傳播的時候想要計算關於這些 Tensor 的梯度
a = torch.randn((), device=device, dtype=dtype, requires_grad=True)
b = torch.randn((), device=device, dtype=dtype, requires_grad=True)
c = torch.randn((), device=device, dtype=dtype, requires_grad=True)
d = torch.randn((), device=device, dtype=dtype, requires_grad=True)

learnign_rate = 1e-6
for t in range(2000):
    # 使用 Tensor 上的運算前向傳播計算預測的 y
    y_pred = a + b * c + c * x ** 2 + d * x ** 3
    
    # 使用 Tensor 上的操作計算並打印損失值(loss)
    # 現在,loss 是一個 Tensor,shape 是 (1,)
    # loss.item() 得到 loss 持有的標量值
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())
        
    # 使用 autograd 計算反向傳播。下面這個調用將會計算 loss 關於所有 requires_grad=True 的 Tensor 的梯度。
    # 在此之后,調用 a.grad, b.grad, c.grad, d.grad 將得到 a, b, c, d 關於 loss 的梯度
    loss.backward()
    
    # 使用梯度下降手動更新權重。使用 torch.no_grad() 包起來。
    # 因為這些權重(weight)都有 requires_grad=True 但是在 autograd 中,我們不需要跟蹤這些操作。
    with torch.no_grad():
        a -= learning_rate * a.grad
        b -= learning_rate * b.grad
        c -= learning_rate * c.grad
        d -= learning_rate * d.grad
        
        # 更新完參數之后,需要手動地將這些梯度清零
        a.grad = None
        b.grad = None
        c.grad = None
        d.grad = None

print(f'Result: y = {a.item()} + {b.item()} x + {c.item()} x^2 + {d.item()} x^3')

PyTorchL 定義一個新的 autograd 函數

在底層,每一個原始自動求導(autograd)運算符實際上是兩個操作在 Tensor 上的函數。前向傳播(forward 函數計算出從輸入 Tensor 到輸出 Tensor。反向傳播(backward 函數收到輸出 Tensor 關於某個標量值的梯度,並且計算輸入 Tensor 關於那些相同標量值的梯度。

在 PyTorch 中我們可以輕松地定義我們自己的自動求導運算符,一個 torch.autograd.Function 的子類並且實現 forwardbackward 函數。然后我們可以使用我們新定義的自動求導運算符,構建一個類實例(instance)然后像函數樣調用它,傳入輸入數據的 Tensor。

在這個例子中,我們定義我們的模型為 \(y=a+b P_3(c+dx)\) 而不是 \(y=a+bx+cx^2+dx^3\),其中 \(P_3(x)=\frac{1}{2}\left(5x^3-3x\right)\) 是一個 3 次(degreeLegendre 多項式。對於計算 \(P_3\) 的前向傳播和反向傳播,我們寫下自定義的自動求導函數,並用它實現我們的模型。

import torch
import math

class LegendrePolynomial3(torch.autograd.Function):
    """
    我們可以通過繼承 torch.autograd.Function 實現我們自定義自動求導函數,
    並實現操作在 Tensor 上的前向傳播和反向傳播。
    """
    
    @staticmethod
    def forward(ctx, input):
        """
        在前向傳播中,我們接受一個包含輸入的 Tensor 並返回包含輸出的 Tensor。
        ctx 是一個上下文(context)對象,用來存放反向傳播計算時用到的信息。
        你可以使用 ctx.save_for_backward 方法緩存任意對象以供反向傳播使用。
        """
        ctx.save_for_backward(input)
        return 0.5 * (5 * input ** 3 - 3 * input)
    
    @staticmethod
    def backward(ctx, grad_output):
        """
        在反向傳播中,我們接收一個張量,其持有 loss 關於輸出的梯度,
        並且我們需要計算 loss 關於輸入的梯度。
        """
        input, = ctx.saved_tensors
        return grad_output * 1.5 * (5 * input ** 2 - 1)
    
dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0")  # 取消注釋可以使用 GPU

# 創建輸入輸出 Tensor。
# 默認情況下,requires_grad=False 表明我們在反向傳播時不需要計算這些 Tensor 的梯度。
x = torch.linspace(-math.pi, math.pi, 2000, device=device, dtype=dtype)
y = torch.sin(x)

# 創建隨機權重(weight)Tensor。在這個例子,我們需要 4 個權重:
# y = a + b * P3(c + d * x) 為保證收斂,這些權重需要被初始化成離正確結果不太遠。
# 設置 requires_grad=True 表明我們在反向傳播期間想要計算關於這些 Tensor 的梯度。
a = torch.full((), 0.0, device=device, dtype=dtype, requires_grad=True)
b = torch.full((), -1.0, device=device, dtype=dtype, requires_grad=True)
c = torch.full((), 0.0, device=device, dtype=dtype, requires_grad=True)
d = torch.full((), 0.3, device=device, dtype=dtype, requires_grad=True)

learning_rate = 5e-6
for t in range(2000):
    # 為了應用(apply)我們的函數,我們使用 Function.apply
    # 並將這個函數取個別名 P3
    P3 = LegendrePolynomial3.apply
    
    # 前向傳播:使用操作(operations)計算預測的 y。
    # 我們使用我們自定義的 autograd 操作計算 P3。
    y_pred = a + b * P3(c + d * x)
    
    # 計算並輸出損失值(loss)
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())
        
    # 使用 autograd 計算反向傳播。
    loss.backward()
    
    # 使用梯度下降更新權重
    with torch.no_grad():
        a -= learning_rate * a.grad
        b -= learning_rate * b.grad
        c -= learning_rate * c.grad
        d -= learning_rate * d.grad
        
        # 更新完權重后,手動清零梯度
        a.grad = None
        b.grad = None
        c.grad = None
        d.grad = None
        
print(f'Result: y = {a.item()} + {b.item()} * P3({c.item()} + {d.item()} x)')

nn module

PyTorch: nn

對於定義一個復雜的運算符和自動微分,計算圖和 autograd 是一個非常強大的范例(paradigm),但是對於一個很大的神經網絡來說,原生的(raw)autograd 就有一點低級(low-level)了。

當構建神經網絡的時候,我們經常考慮將這些計算安排整理到一個 層(layers 中,在學習期間,一些 可學習參數(learnable parameters 將會被優化。

在 TensorFlow 中,像 Keras, TensorFlow-SlimTFLearn 包在原生計算圖之上提供了更高階的抽象,這非常有益於構建神經網絡。

在 PyTorch 中,nn 包服務於相同的目的。nn 包定義了一套 Modules,大致上等價於神經網絡中的層(layers)。一個 Module 接受輸入 Tensor 並計算輸出 Tensor,也許也會持有內部的狀態(state),比如包含可學習的參數的(learnable parameters)Tensor。當訓練神經網絡時,nn 包也定了一套損失函數(loss function)。

在這個例子中,我們使用 nn 包實現我們的多項式模型。

import torch
import math

# 創建輸入輸出 Tensor
x = torch.linspace(-math.pi, math.pi, 2000)
y = torch.sin(x)

# 在這個例子中,輸出 y 是一個(x,x^2,x^3)的線性函數,所以
# 我們可以考慮它是一個線性(linear layer)神經網絡層。
# 讓我們准備 Tensor(x,x^2,x^3)
p = torch.tensor([1, 2, 3])
xx = x.unsqueeze(-1).pow(p)

# 在上面的代碼中,x.unsqueeze(-1) 有着 (2000,1)的形狀(shape),並且 p 有着 (3,)的形狀
# 對於這個例子,廣播語義(broadcasting semantics),得到一個 (2000,3)的 Tensor。

# 使用 nn 包定義我們一系列的層的模型。nn.Sequential 是一個 Module,其包含其它 Modules
# 並按順序應用它們產生輸出。線性 Modules 從輸入使用一個線性函數計算輸出
# 並在內部持有模型的 weight 和 bias 的 Tensor。Flatten layer 展開線性層的輸出到
# 一個匹配 y 的形狀(shape)的一維(1D)的 Tensor。
model = torch.nn.Sequential(
    torch.nn.Linear(3, 1),
    torch.nn.Flatten(0, 1)
)

# nn 包同樣包含流行的損失函數(loss function)的定義;在這個例子中,
# 我們將使用均方誤差(Mean Square Error——MSE)作為我們的損失函數。
loss_fn = torch.nn.MSELoss(reduction='sum')

learning_rate = 1e-6
for t in range(2000):
    # 前向傳播:通過傳入 x 到 model 計算預測的 y。Module 對象重寫了
    # __call__ 函數,所我們可以就像調用函數一樣調用他們。當你這么做的時候
    # 傳入輸入 Tensor 到 Module 然后它計算產生輸出的 Tensor。
    y_pred = model(xx)
    
    # 計算並打印損失值(loss)。我們傳入 y 的預測值和真實值的 Tensor,
    # 之后 loss function 返回損失值(loss)的 Tensor。
    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())
        
    # 運行反向傳播之前清零一下梯度
    model.zero_grad()
    # 反向傳播:計算 loss 關於所有 model 的可學習參數的梯度。從底層上來說,
    # 每一個 requires_grad=True 的 Module 的參數(parameters)都被存儲在一個 Tensor 中,
    # 所以下面這個調用將計算 model 中所有可學習參數的的梯度。
    loss.backward()
    
    # 使用梯度下降更新權重。每一個參數都是一個 Tensor,
    # 所以我們就像之前一樣得到它的梯度。
    with torch.no_grad():
        for param in model.parameters():
            param -= learning_rate * param.grad
    
# 你也可以就像得到列表(list)的第一個元素一樣,得到 model 的第一層
linear_layer = model[0]

# 對於 linear layer,它的參數被存儲為 weight 和 bias。
print(f'Result: y = {linear_layer.bias.item()} + {linear_layer.weight[:, 0].item()} x + {linear_layer.weight[:, 1].item()} x^2 + {linear_layer.weight[:, 2].item()} x^3')

PyTorch: optim

到目前為止,我們通過 torch.no_grad() 手動更改持有可學習參數的 Tensor 來更新了我們模型的權重。這對於一些簡單的優化算法,比如隨機梯度下降(stochastic gradient descent),並不是一個太大的負擔,但是實際上,我們經常使用更復雜的優化器(Optimizer),比如 AdaGradRMSpropAdam等,訓練神經網絡。

PyTorch 里的 optim 包抽象了一個優化算法的思想,並且提供了常用的優化算法的實現。

在這個例子,我們依舊使用 nn 包定義我們的模型,但是我們將使用 optim 包提供的 RMSprop 算法優化模型。

import torch
import math


# 創建輸入輸出的 Tensor。
x = torch.linspace(-math.pi, math.pi, 2000)
y = torch.sin(x)

# 准備輸入的 Tensor(x,x^2,x^3)。
p = torch.tensor([1, 2, 3])
xx = x.unsqueeze(-1).pow(p)

# 使用 nn 包定義我們的模型和損失函數。
model = torch.nn.Sequential(
    torch.nn.Linear(3, 1),
    torch.nn.Flatten(0, 1)
)
loss_fn = torch.nn.MSELoss(reduction='sum')

# 使用 optim 包定義一個將會為我們更新模型的權重(weight)的優化器(Optimizer)。
# 這里我們將使用 RMSprop,optim 包包含了許多其它優化算法。
# RMSprop 構造器的第一個參數是告訴優化器哪些 Tensor 應該被更新。
learning_rate = 1e-3
optimizer = torch.optim.RMSprop(model.parameters(), lr=learning_rate)
for t in range(2000):
    # 前向傳播:傳入 x 到 model 計算預測的 y
    y_pred = model(xx)
    
    # 計算並打印損失值(loss)
    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())
    
    # 在反向傳播之前,需要使用優化器(optimizer)對象清零所有將被更新的變量的梯度
    # (模型的可學習參數(learnable weight))。這是因為在默認情況下,不論何時 .backward() 被調用時,
    # 梯度會被累積在緩沖區(換言之,不會被覆蓋)。可以通過 torch.autograd.backward 的官方文檔查看更多細節。
    optimizer.zero_grad()
    
    # 反向傳播:計算 loss 關於模型參數的梯度。
    loss.backward()
    
    # 調用優化器上的 step 函數,更新它的參數。
    optimizer.step()
    
linear_layer = model[0]
print(f'Result: y = {linear_layer.bias.item()} + {linear_layer.weight[:, 0].item()} x + {linear_layer.weight[:, 1].item()} x^2 + {linear_layer.weight[:, 2].item()} x^3')

PyTorch:定制 nn Modules

某些時候,你想要指定的模型比 Modules 存在的順序(sequence)模型還要復雜,在這種情況下,你可以通過繼承 nn.Module 的子類並且定義一個 forward 函數,這個函數使用其它 Modules 或者其它 autograd 操作符,接收輸入 Tensor 並計算輸出 Tensor。

在這個例子中,我們實現我們的三階多項式作為定制的 Module 的子類。

import torch
import math

class Polynomial3(torch.nn.Module):
    def __init__(self):
        """
        在這個構造器,我們實例化四個參數並賦它們為成員 parameters。
        """
        super().__init__()
        self.a = torch.nn.Parameter(torch.randn(()))
        self.b = torch.nn.Parameter(torch.randn(()))
        self.c = torch.nn.Parameter(torch.randn(()))
        self.d = torch.nn.Parameter(torch.randn(()))
        
    def forward(self, x):
        """
        在前向傳播函數,我們接收一個輸入數據的 Tensor,並且我們必須返回輸出數據的 Tensor。
        我們可以使用定義在構造器的 Modules 以及任意的操作在 Tensor 上的運算符。
        """
        return self.a + self.b * x + self.c * x ** 2 + self.d * x ** 3
    
    def string(self):
        """
        就像 Python 中的任意一個類一樣,你也可以隨便定義任何的方法(method)在 PyTorch Modules中。
        """
        return f'y = {self.a.item()} + {self.b.item()} x + {self.c.item()} x^2 + {self.d.item()} x^3'
    
# 創建輸入輸出的 Tensor。
x = torch.linspace(-math.pi, math.pi, 2000)
y = torch.sin(x)

# 實例化上面定義的類,構造我們的模型。
model = Polynomial3()

# 構造我們的損失函數(loss function)和一個優化器(Optimizer)。在 SGD 構造器里
# 調用 model.parameters(),構造器將包含 nn.Linear Module 的可學習的參數(learnable parameters)
# 其是模型(model)的成員變量。
criterion = torch.nn.MSELoss(reduce='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-6)
for t in range(2000):
    # 前向傳播:傳入 x 到模型計算預測的 y
    y_pred = model(x)
    
    # 計算並打印損失值(loss)
    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())
        
    # 清零梯度,執行反向傳播,更新參數。
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
print(f'Result: {model.string()}')

PyTorch:控制流 + 權重(參數)共享

作為一個動態圖和權重共享的例子,我們實現一個非常強大的模型:一個 3-5 階的多項式,在前向傳播時選擇一個 3-5 之間的隨機數,並且使用許多階,多次重復使用相同的權重計算第四階和第五階。

對於這個模型,我們可以使用典型的 Python 控制流實現循環,並且當定義前向傳播時,我們可以簡單地復用相同的參數多次實現權重共享。

我們可以繼承 Module 類輕松地實現這個模型。

import random
import torch
import math

class DynamicNet(torch.nn.Module):
    def __init__(self):
        """
        在這個構造器中,我們實例化五個參數並且將它們賦值給成員變量。
        """
        super().__init__()
        self.a = torch.nn.Parameter(torch.randn(()))
        self.b = torch.nn.Parameter(torch.randn(()))
        self.c = torch.nn.Parameter(torch.randn(()))
        self.d = torch.nn.Parameter(torch.randn(()))
        self.e = torch.nn.Parameter(torch.randn(()))
        
    def forward(self, x):
        """
        對於模型的前向傳播,我們隨機選擇 4 或 5 並復用參數 e 計算這些階的貢獻(contribution)。
        因為每一次前向傳播都構建了一個動態的計算圖,當定義模型的前向傳播時,
        我們可以使用常規的 Python 控制流操作符,像循環或條件語句。
        這里我們也看到了當定義一個計算圖時重復使用相同的參數多次是相當安全。
        """
        y = self.a + self.b * x + self.c * x ** 2 + self.d * x ** 3
        for exp in range(4, random.randint(4, 6)):
            y = y + self.e * x ** exp
        return y
    def string(self):
        """
        就像 Python 中的任意一個類一樣,你也可以隨便定義任何的方法(method)在 PyTorch Modules中。
        """
        return f'y = {self.a.item()} + {self.b.item()} x + {self.c.item()} x^2 + {self.d.item()} x^3 + {self.e.item()} x^4 ? + {self.e.item()} x^5 ?'
    
# 創建持有輸入輸出的 Tensor。
x = torch.linspace(-math.pi, math.pi, 2000)
y = torch.sin(x)

# 通過實例化上面定義的類,構造我們的模型。
model = DynamicNet()

# 構造我們的損失函數(loss fcuntion)和一個優化器(Optimizer)。使用毫無特色的
# 隨機梯度下降(stochastic gradient descent)訓練這個強大的模型是艱難的,
# 所以我們使用動量(momentum)。
criterion = torch.nn.MSELoss(reduce='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-8, momentum=0.9)
for t in range(30000):
    # 前向傳播:傳入 x 到 model 計算預測的 y。
    y_pred = model(x)
    
    # 計算並打印損失值(loss)
    loss = criterion(y_pred, y)
    if t % 2000 == 1999:
        print(t, loss.item())
        
    # 清零梯度、執行反向傳播、更新參數
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

print(f'Result: {model.string()}')


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM