本教程通过自包含的例子介绍 PyTorch 的基本概念。
要查看格式更加优美的图文并茂的教程,请移步:http://studyai.com/pytorch-1.4/beginner/pytorch_with_examples.html
PyTorch的核心是提供了两个主要特性:
n维Tensor,类似于numpy,但可以在GPU上运行。
建立和训练神经网络的自动微分
我们将使用一个完全连接的relu网络作为我们的运行示例。 该网络将有一个单一的隐藏层,并将用梯度下降训练, 为了适应随机数据,通过最小化网络输出和真正的输出的欧氏距离 来更新网络模型参数。
Note
你可以单独浏览和下载这个示例,在 这个页面的最后 。
张量
热身: numpy
在介绍PyTorch之前,我们首先使用numpy实现网络。
Numpy提供了一个n维数组对象,以及许多用于操作这些数组的函数. Numpy是一个用于科学计算的通用框架;它对计算图、深度学习或梯度一无所知。 但是,我们可以很容易地使用numpy来拟合两层网络中的随机数据, 方法是使用numpy操作手动实现前后向通过网络:
-- coding: utf-8 --
import numpy as np
N is batch size; D_in is input dimension;
H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10
产生随机输入和输出数据
x = np.random.randn(N, D_in)
y = np.random.randn(N, D_out)
随机初始化权重
w1 = np.random.randn(D_in, H)
w2 = np.random.randn(H, D_out)
learning_rate = 1e-6
for t in range(500):
# 前向传递: 计算 predicted y
h = x.dot(w1)
h_relu = np.maximum(h, 0)
y_pred = h_relu.dot(w2)
# 计算和输出损失
loss = np.square(y_pred - y).sum()
print(t, loss)
# 反向传播(Backprop) 去计算 w1 和 w2 相对于loss的梯度
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = h_relu.T.dot(grad_y_pred)
grad_h_relu = grad_y_pred.dot(w2.T)
grad_h = grad_h_relu.copy()
grad_h[h < 0] = 0
grad_w1 = x.T.dot(grad_h)
# 更新权重
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
PyTorch: Tensors
Numpy是一个很好的框架,但它不能使用GPU加速其数值计算。对于现代的深层神经网络, GPU通常提供50倍或更高的速度,因此不幸的是,numpy不足以满足现代深度学习的需要。
这里我们介绍最基础的PyTorch概念:张量(Tensor) 。PyTorch张量在概念上与numpy数组相同: 一个Tensor是一个n维数组,PyTorch提供了许多在这些张量上操作的函数。 在幕后,张量可以跟踪计算图和梯度,但它们也是科学计算的通用工具。
与Numpy不同的是,PyTorch张量可以利用GPU加速它们的数值计算。 要在GPU上运行PyTorch张量,只需将其转换为新的数据类型即可。
在这里,我们使用PyTorch张量对随机数据进行两层网络拟合。 与上面的numpy示例一样,我们需要手动实现通过网络向前和向后传递的内容:
-- coding: utf-8 --
import torch
dtype = torch.float
device = torch.device("cpu")
device = torch.device("cuda:0") # 去掉这行注释就可以在GPU上运行
N is batch size; D_in is input dimension;
H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10
产生随机输入和输出数据
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
随机初始化权重
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)
learning_rate = 1e-6
for t in range(500):
# 前向传递: 计算 predicted y
h = x.mm(w1)
h_relu = h.clamp(min=0)
y_pred = h_relu.mm(w2)
# 计算并输出损失
loss = (y_pred - y).pow(2).sum().item()
print(t, loss)
# 反向传播(Backprop) 去计算 w1 和 w2 相对于loss的梯度
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = h_relu.t().mm(grad_y_pred)
grad_h_relu = grad_y_pred.mm(w2.t())
grad_h = grad_h_relu.clone()
grad_h[h < 0] = 0
grad_w1 = x.t().mm(grad_h)
# 使用梯度下降法更新权重
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
自动梯度
PyTorch: Tensors 和 autograd
在上面的例子中,我们必须手动实现我们的神经网络的前向、后向传播过程。 对于一个小的两层网络来说,手动实现反向传递并不是什么大问题, 但是对于大型复杂网络来说,它很快就会变得令人害怕的(hairy)。
值得庆幸的是,我们可以使用自动微分 (automatic differentiation) 来自动计算神经网络中的反向传播。 PyTorch中的 autograd 包提供了这个功能。使用autograd时, 网络的前向传播过将定义一个计算图(computational graph);图中的节点将是张量, 边将是从输入张量产生输出张量的函数。 然后,通过这个图进行反向传播,您可以轻松地计算梯度。
这听起来很复杂,在实践中很容易使用。每个张量表示计算图中的一个节点。 如果 x 是具有 x.requires_grad=True 状态的张量, 则 x.grad 是另一个张量,它持有 x 相对于某个标量值的梯度。
在这里,我们使用PyTorch张量和自动梯度来实现我们的两层网络; 现在我们不再需要手动实现通过网络的反向传递:
-- coding: utf-8 --
import torch
dtype = torch.float
device = torch.device("cpu")
device = torch.device("cuda:0") #去掉这行注释就可以在GPU上运行
N is batch size; D_in is input dimension;
H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10
创建随机张量以持有输入和输出.
设置 requires_grad=False 表明 我们在反向传递阶段
不需要计算相对于这些张量的梯度
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
创建随机张量用来存放模型的可学习参数: weights
设置 requires_grad=True 表明 我们在反向传递阶段
需要计算相对于这些张量的梯度
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)
learning_rate = 1e-6
for t in range(500):
# 前向传递: 计算预测出的 y 使用Tensors相关的运算/操作;
# 这个地方与上一节中使用Tensor的同样的操作计算前向传递是一样的;
# 但是我们不需要保留计算过程的中间值的引用,
# 因为我们并没有去手动实现反向传递。
y_pred = x.mm(w1).clamp(min=0).mm(w2)
# 使用Tensors的操作 计算损失并输出
# 现在损失是一个 shape 为 (1,) 的张量
# loss.item() 可以获得张量loss中持有的数字
loss = (y_pred - y).pow(2).sum()
print(t, loss.item())
# 使用 autograd 去计算反向传递。 这个调用将会计算
# loss相对于所有状态为 requires_grad=True 的张量的梯度。
# 调用完毕以后, w1.grad 和 w2.grad 将会是两个张量,分别持有
# 损失相对于 w1 和 w2 的梯度。
loss.backward()
# 使用梯度下降法手动更新权重。并将代码分装在 torch.no_grad() 中。
# 因为 权重张量的状态为 requires_grad=True, 但是我们不希望在
# autograd 中去跟踪历史.
# 另一种可选的方法是 直接操作 weight.data 和 weight.grad.data 。
# 回想到 tensor.data 给出一个与其共享存储空间的张量,但是不会跟踪历史。
# 你也可以使用 torch.optim.SGD 来达到此目的。
with torch.no_grad():
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
# 更新完权重以后,手动将所有的梯度清零
w1.grad.zero_()
w2.grad.zero_()
PyTorch: 定义一个新的 autograd 函数
在这种情况下,每个原始的 自动梯度算子(autograd operator) 实际上是两个作用于张量的函数。 forward 函数从输入张量计算输出张量。backward 函数接收输出张量相对于某个标量值的梯度, 并计算输入张量相对于该标量值的梯度。
在PyTorch中,我们可以通过定义 torch.autograd.Function 的子类来 轻松地定义我们自己的自动梯度算子 并 实现 forward 和 backward 函数。 然后,我们可以使用新的自动梯度算子,方法是构造一个类实例并像函数一样调用它, 传递包含输入数据的张量。
在这个例子中,我们定义了自定义的自动梯度函数来执行relu非线性,并使用它来实现我们的两层网络:
-- coding: utf-8 --
import torch
class MyReLU(torch.autograd.Function):
"""
我们可以通过定义 torch.autograd.Function 的子类
并实现forward和backward函数来轻松地定义我们自己的
autograd Functions 。
"""
@staticmethod
def forward(ctx, input):
"""
在前向传递中,我们接收一个包含输入的Tensor并返回
一个包含输出的Tensor。 ctx 是一个上下文对象,
可以用于为反向计算存储信息。
可以使用 ctx.save_for_backward 方法缓存任意对象,以便在向后传递中使用。
"""
ctx.save_for_backward(input)
return input.clamp(min=-2)
@staticmethod
def backward(ctx, grad_output):
"""
在反向传递中,我们接收到一个包含了损失相对于输出的梯度的张量,
并且我们需要计算损失相对于输入的梯度。
"""
input, = ctx.saved_tensors
grad_input = grad_output.clone()
grad_input[input < -2] = 0
return grad_input
dtype = torch.float
device = torch.device("cpu")
device = torch.device("cuda:0") # Uncomment this to run on GPU
N is batch size; D_in is input dimension;
H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10
Create random Tensors to hold input and outputs.
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
Create random Tensors for weights.
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)
learning_rate = 1e-6
for t in range(500):
# 要应用我们自定义的函数, 可以使用 Function.apply 方法.
# 我们给它起个别名 'relu'.
relu = MyReLU.apply
# 前向传递: 使用operations计算预测的 y ; 我们
# 使用自定义的 autograd operation 计算 ReLU 。
y_pred = relu(x.mm(w1)).mm(w2)
# 计算并输出损失
loss = (y_pred - y).pow(2).sum()
print(t, loss.item())
# 使用 autograd 去计算 backward pass.
loss.backward()
# 使用梯度下降法更新权重
with torch.no_grad():
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
# 更新完权重以后,手动清零所有的梯度缓存
w1.grad.zero_()
w2.grad.zero_()
TensorFlow: 静态计算图
PyTorch Autograd看起来很像TensorFlow:在这两个框架中,我们定义了一个计算图, 并使用自动微分来计算梯度。两者最大的区别是TensorFlow的计算图是 静态的(static) , PyTorch使用 动态(dynamic) 计算图。
在TensorFlow中,我们定义一次计算图,然后一次又一次地执行相同的图, 可能会将不同的输入数据输入到图中。 在PyTorch中,每一次前向传播过程都定义一个新的计算图。
静态图很好,因为您可以预先优化它;例如,一个框架可能决定融合一些图节点操作以提高效率, 或者想出一种在多个GPU或多台机器上分配图上计算节点的策略。如果您一次又一次地重用相同的图, 那么这个潜在的代价高昂的预先优化可以被摊还,因为相同的图会一次又一次地重复运行。
静态图和动态图不同的一个方面是控制流(control flow)。对于某些模型,我们可能希望对每个数据点执行不同的计算; 例如,对于每个数据点,可能会对不同的时间步骤展开递归网络;这种展开可以作为一个循环来实现。 对于静态图,循环构造需要是图的一部分;因此,TensorFlow提供了诸如 tf.scan 之类的操作符, 用于将循环嵌入到图中。对于动态图,情况更简单:因为我们为每个示例动态构建图, 所以我们可以使用正常的命令式流控制来执行对每个输入不同的计算。
为了与上面的PyTorch Autograd的示例作对比,这里我们使用TensorFlow来拟合一个简单的两层网络:
-- coding: utf-8 --
import tensorflow as tf
import numpy as np
First we set up the computational graph:
N is batch size; D_in is input dimension;
H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10
Create placeholders for the input and target data; these will be filled
with real data when we execute the graph.
x = tf.placeholder(tf.float32, shape=(None, D_in))
y = tf.placeholder(tf.float32, shape=(None, D_out))
Create Variables for the weights and initialize them with random data.
A TensorFlow Variable persists its value across executions of the graph.
w1 = tf.Variable(tf.random_normal((D_in, H)))
w2 = tf.Variable(tf.random_normal((H, D_out)))
Forward pass: Compute the predicted y using operations on TensorFlow Tensors.
Note that this code does not actually perform any numeric operations; it
merely sets up the computational graph that we will later execute.
h = tf.matmul(x, w1)
h_relu = tf.maximum(h, tf.zeros(1))
y_pred = tf.matmul(h_relu, w2)
Compute loss using operations on TensorFlow Tensors
loss = tf.reduce_sum((y - y_pred) ** 2.0)
Compute gradient of the loss with respect to w1 and w2.
grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])
Update the weights using gradient descent. To actually update the weights
we need to evaluate new_w1 and new_w2 when executing the graph. Note that
in TensorFlow the the act of updating the value of the weights is part of
the computational graph; in PyTorch this happens outside the computational
graph.
learning_rate = 1e-6
new_w1 = w1.assign(w1 - learning_rate * grad_w1)
new_w2 = w2.assign(w2 - learning_rate * grad_w2)
Now we have built our computational graph, so we enter a TensorFlow session to
actually execute the graph.
with tf.Session() as sess:
# Run the graph once to initialize the Variables w1 and w2.
sess.run(tf.global_variables_initializer())
# Create numpy arrays holding the actual data for the inputs x and targets
# y
x_value = np.random.randn(N, D_in)
y_value = np.random.randn(N, D_out)
for _ in range(500):
# Execute the graph many times. Each time it executes we want to bind
# x_value to x and y_value to y, specified with the feed_dict argument.
# Each time we execute the graph we want to compute the values for loss,
# new_w1, and new_w2; the values of these Tensors are returned as numpy
# arrays.
loss_value, _, _ = sess.run([loss, new_w1, new_w2],
feed_dict={x: x_value, y: y_value})
print(loss_value)
nn 模块
PyTorch: nn
计算图和自动梯度是定义复杂算子和自动获取导数的一个非常强大的paradigm; 然而,对于大型神经网络来说,raw autograd 可能有点过于低级。
在建立神经网络时,我们经常会考虑将计算组织成 层(layers) ,其中有些具有可学习的参数(learnable parameters), 在学习过程中会进行优化。
在TensorFlow中, Keras, TensorFlow-Slim, 和 TFLearn 等包提供了比原始计算图更高层次的抽象,这对于构建神经网络非常有用。
在PyTorch中,nn 包也有同样的用途。nn 包定义了一组 模块(Modules) ,它们大致相当于神经网络的层。 模块接收输入张量并计算输出张量,同时也可以保持内部状态,例如包含可学习参数的张量。 nn 包还定义了一组有用的损失函数,这些函数是训练神经网络时常用的。
在本例中,我们使用nn包来实现我们的两层网络:
-- coding: utf-8 --
import torch
N is batch size; D_in is input dimension;
H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10
Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
使用 nn package 来把我们的模型定义为layers构成的序列。nn.Sequential
是一个包含了其他Modules的Module, 并把它们应用在序列中产生输出。
每个Linear Module使用线性函数从输入计算输出,并且持有内部张量用于存储它的权重和偏置。
model = torch.nn.Sequential(
torch.nn.Linear(D_in, H),
torch.nn.ReLU(),
torch.nn.Linear(H, D_out),
)
nn package 也包含了各种广泛使用的损失函数;
在这里,我们使用 Mean Squared Error (MSE) 作为我们的损失函数。
loss_fn = torch.nn.MSELoss(reduction='sum')
learning_rate = 1e-4
for t in range(500):
# 前向传递: 把 x 传入 model 计算预测输出 y 。因为 Module objects 重载了
# call 这个魔法函数,所以你可以像调用函数一样调用 model 。
# 当你这么做的时候,你要把输入数据的Tensor传递到Module里面,并产生输出数据的Tensor.
y_pred = model(x)
# 计算并输出 loss. 我们把包含预测值的张量 y_pred 和真实值的张量 y 都传入损失函数,
# 损失函数返回一个包含损失的张量。
loss = loss_fn(y_pred, y)
print(t, loss.item())
# 在运行反向传播之前先将模型内部的梯度缓存都清零
model.zero_grad()
# 反向传递: 计算损失相对模型中所有可学习参数的梯度
# 在内部, 每个 Module 的参数被存储在状态为
# requires_grad=True 的 Tensors 中, 所以调用backward()后,
# 将会计算模型中所有可学习参数的梯度。
loss.backward()
# 使用梯度下降算法更新权重. 每个参数是一个Tensor, 因此
# 我们可以像之前一样通过 param.grad 来获取梯度
with torch.no_grad():
for param in model.parameters():
param -= learning_rate * param.grad
PyTorch: optim
到目前为止,我们已经通过手动修改包含可学习参数的张量来更新模型的权重(使用 torch.no_grad() 或 .data ,以避免在autograd中跟踪历史记录)。对于像随机梯度下降这样的简单优化算法来说, 这并不是一个巨大的负担,但在实践中,我们经常使用更复杂的优化器, 如AdaGrad、RMSProp、Adam 等来训练神经网络。
PyTorch中的 optim package 抽象了优化算法的思想,并提供了常用优化算法的实现。
在这个例子中,我们将像前面一样使用 nn 包来定义我们的模型,但是我们将 使用 optim package提供的 Adam 算法来优化模型:
-- coding: utf-8 --
import torch
N is batch size; D_in is input dimension;
H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10
创建持有输入和输出的随机张量
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
使用 nn package 来 定义模型和损失函数
model = torch.nn.Sequential(
torch.nn.Linear(D_in, H),
torch.nn.ReLU(),
torch.nn.Linear(H, D_out),
)
loss_fn = torch.nn.MSELoss(reduction='sum')
使用 optim package 来定义一个优化器(Optimizer),用于为我们更新模型的权重。
这里我们使用 Adam; optim package 包含很多其他的优化算法。
Adam 构造函数的第一个参数告诉优化器哪些Tensors需要被更新。
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(500):
# 前向传递: 通过把x传入模型来计算 预测值 y。
y_pred = model(x)
# 计算并输出 loss.
loss = loss_fn(y_pred, y)
print(t, loss.item())
# 在向后传递之前,使用优化器对象把它将要更新的变量(模型的可学习参数)的所有梯度变为零。
# 这是因为默认情况下,不管啥时候调用.backward(),梯度都会累积到缓存(i.e. 不是重新写入)。
# 请查看 torch.autograd.backward 的文档获得更多信息。
optimizer.zero_grad()
# 向后传递: 计算损失相对于模型参数的梯度
loss.backward()
# 调用 Optimizer 的 step 函数对参数进行一步更新
optimizer.step()
PyTorch: 自定义 nn 模块
有时,您可能希望指定比现有模块序列更复杂的模型;在这些情况下, 您可以通过定义 nn.Module 的子类和定义一个 forward 来定义您自己的模块, 它接收输入张量并使用其他模块或对张量的其他自动梯度算子生成输出张量。
在本例中,我们将我们的两层网络实现为自定义模块子类:
-- coding: utf-8 --
import torch
class TwoLayerNet(torch.nn.Module):
def init(self, D_in, H, D_out):
"""
在构造函数中,我们实例化了两个nn.Linear模块,
并将它们赋值为成员变量。
"""
super(TwoLayerNet, self).init()
self.linear1 = torch.nn.Linear(D_in, H)
self.linear2 = torch.nn.Linear(H, D_out)
def forward(self, x):
"""
在前馈函数中,我们接受一个输入数据的 Tensor,
并且我们必须返回输出数据的Tensor。在这里
我们可以使用造函数中已经定义好的Modules和
其他任意的Tensors上的算子来完成前馈函数的任务逻辑。
"""
h_relu = self.linear1(x).clamp(min=0)
y_pred = self.linear2(h_relu)
return y_pred
N is batch size; D_in is input dimension;
H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10
创建持有输入和输出的随机张量
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
通过实例化上面定义的类,来创建模型
model = TwoLayerNet(D_in, H, D_out)
构建我们的损失函数和优化器。在SGD的构造器中调用 model.parameters()
将会包含来自两个nn.Linear modules的可学习参数;
这两个 nn.Linear modules 是我们自定义的模型的类成员。
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
for t in range(500):
# 前向过程: 把 x 传递给model, 计算 predicted y
y_pred = model(x)
# 计算并输出loss
loss = criterion(y_pred, y)
print(t, loss.item())
# 把梯度置零, 执行后向传递, 以及 更新权重
optimizer.zero_grad()
loss.backward()
optimizer.step()
PyTorch: 控制流 + 权重共享
作为动态图和权重共享的一个例子,我们实现了一个非常奇怪的模型:一个完全连接的ReLU网络, 它在每个前向通路上选择一个介于1到4之间的随机数,并使用那许多隐藏层, 重复使用相同的权重多次计算最内部的隐藏层。
对于这个模型,我们可以使用普通的Python流控制来实现循环,我们可以通过在定义前向传递时 多次重用相同的模块来实现最内部层之间的权重共享。
我们可以很容易地将这个模型实现为一个模块子类:
-- coding: utf-8 --
import random
import torch
class DynamicNet(torch.nn.Module):
def init(self, D_in, H, D_out):
"""
在构造函数中,我们创建3个 nn.Linear 的实例,它们将被用于前向传递中。
"""
super(DynamicNet, self).init()
self.input_linear = torch.nn.Linear(D_in, H)
self.middle_linear = torch.nn.Linear(H, H)
self.output_linear = torch.nn.Linear(H, D_out)
def forward(self, x):
"""
在模型的前向传递中, 我们随机的选择 0, 1, 2, 或 3 中的一个数字,
然后我们就重复使用middle_linear Module那么多次 作为计算隐藏层的表示。
因为每一次前向传递都会构建一个动态的计算图,我们在定义模型的前向计算过程时
可以使用普通的Python控制流操作比如 for-loops 或 条件表达式。
在这里,我们还看到,在定义计算图时,多次重用同一个模块是完全安全的。
这是对Lua Torch的一个很大的改进,在那里每个模块只能使用一次。
"""
h_relu = self.input_linear(x).clamp(min=0)
for _ in range(random.randint(0, 3)):
h_relu = self.middle_linear(h_relu).clamp(min=0)
y_pred = self.output_linear(h_relu)
return y_pred
N is batch size; D_in is input dimension;
H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10
创建持有输入和输出的随机张量
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
通过实例化上面定义的类,来创建模型
model = DynamicNet(D_in, H, D_out)
构建我们的损失函数和优化器。使用 普通的SGD 来训练这个奇怪的模型是很难的,
所以我们使用了带有动量项的SGD来优化模型。
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
for t in range(500):
# 前向过程: 把 x 传递给model, 计算 predicted y
y_pred = model(x)
# 计算并输出loss
loss = criterion(y_pred, y)
print(t, loss.item())
# 把梯度置零, 执行后向传递, 以及 更新权重
optimizer.zero_grad()
loss.backward()
optimizer.step()
示例
您可以在这里单独浏览和下载上面的每个示例代码。