PyTorch 最基础的神经网络模板代码**,**分为四个步骤:
1. 准备数据集(Prepare Dataset)
本实例是最基本的一元线性回归,需要导入的数据较少。
python
import torch
x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[2.0], [4.0], [6.0]])
- ***[[1.0], [2.0], [3.0]] :***二维列表(矩阵),外层列表表示样本集,内层每个列表表示单个样本的特征。
- torch.Tensor() : PyTorch 的张量(Tensor)构造函数,将 Python 列表转换为 PyTorch 核心数据结构Tensor(张量)(默认创建
float32类型张量)。
2. 设计模型(Design model)
在PyTorch中使用类来设计模型,模型类必须继承*torch.nn.Module。*定义线性回归模型类:
python
class LinearModel(torch.nn.Module):
def __init__(self):
super().__init__()
self.linear = torch.nn.Linear(1, 1)
def forward(self, x):
y_pred = self.linear(x)
return y_pred
model = LinearModel()
- class LinearModel(torch.nn.Module): 定义自定义类LinearModel ,torch.nn.Module 表示此类继承PyTorch的基类nn.Module(子类*
LinearModel* 会完全继承父类*nn.Module*的所有属性和方法) - ***def init(self):***类的构造方法,创建类的实例时自动执行,在构造方法中初始化模型的层 及参数,是定义模型结构的固定位置。
- super().init():super().init() 作用是执行父类
nn.Module的init() ,如果不写这行,父类*nn.Module* 的初始化逻辑不会执行,模型无法管理参数、无法构建计算图,直接报错(如果子类中没有*init()* 方法,继承父类时会自动调用父类的*init()* ,如果子类中已经有了*init()* 方法,则父类的*init()* 不会自动调用)。
除了super().init(), 也可以使用super() 来调用父类的其他方法。
事实上在这种单继承场景下,直接写Module.init() 效果和super().init ()一样,之所以使用super().init() ,第一个好处是当父类名称修改时我们只需要修改class类定义中的父类而不需要将内部调用父类方法的父类名称都修改;第二个好处是在多继承时,可以避免手动调用父类方法导致的逻辑混乱。 - self.linear = torch.nn.Linear(1, 1): 定义类LinearModel 的第一个属性linear ,本质是实例化 PyTorch 内置的
Linear类,并将这个实例赋值给self.linear(类的属性),同时自动初始化了weight和bias两个可训练参数,可通过linear.weight 及linear.bias 访问。self.linear是模型的核心计算层,后续前向传播直接调用它即可。
Linear类同样继承自nn.Module,其底层已经实现了线性计算和参数初始化逻辑。并且也存在方法forward ,可通过*self.linear(x)*直接调用。(关于Linear类的底层逻辑在文章下方有补充说明) - def forward(self, x):
forward是*nn.Module* 的固定方法名,必须叫forward ,用于定义前向传播的计算逻辑。当我们调用*model(x)* 时,PyTorch 底层会自动调用forward 方法,无需手动写*model.forward(x)*。 - y_pred = self.linear(x): 执行线性层的前向计算,得到模型的预测值*
y_pred* ,同时自动构建计算图(为后续反向传播求导做准备)。
这里的y_pred 仍为Tensor(张量),因为线性层的底层*forward*方法返回的是 Tensor - model = LinearModel(): 实例化自定义类*
LinearModel* ,创建模型对象model。此时模型完成初始化,线性层的和
被随机初始化,所有可训练参数被*
nn.Module* 注册,可通过*model.parameters()*查看。
关于线性模型的具体解释可以参考文章PyTorch_conda-CSDN博客中《Linear Layers(线性层)》一节。
3. 构造损失函数(Construct Loss)
python
criterion = torch.nn.MSELoss(reduction='sum')
- 直接调用 PyTorch 内置的损失函数。
- 实例化 PyTorch 的
MSELoss类,赋值给criterion(准则 / 损失函数)。 MSELoss是均方误差损失,对应线性回归的损失公式:
参数说明: 在旧版本中使用size_average参数来决定是否求平均值,若size_average=False,即criterion = torch.nn.MSELoss(size_average=False),表示不对损失求平均值(即省略公式的
)。
新版本中size_average即将被移除,改为reduction。若reduction='sum' ,则计算所有样本损失的总和;若reduction='mean' ,则计算所有样本损失的平均值;若reduction='none',则保留每个样本的损失值(张量),最终得到的 loss 是一个和样本数量对应的张量。
4. 构造优化器(Construct Optimizer)
python
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
- ***
SGD:***随机梯度下降优化器,最基础的优化算法。 - ***
model.parameters():***获取模型所有可训练参数(线性层的w和b),告诉优化器要更新哪些参数。 - ***
lr=0.01:***学习率,控制参数更新的步长,步长太大不收敛,太小收敛慢。
5. 训练循环(Training Cycle)
循环迭代 1000 次,每一轮都完成**"前向传播算损失→反向传播算梯度→优化器更新参数"** ,让模型的w和b不断逼近真实值。
python
for epoch in range(1000):
y_pred = model(x_data) # 前向传播(计算预测值)
loss = criterion(y_pred, y_data) # 计算损失值
print(epoch, loss.item())
optimizer.zero_grad() # 梯度清零
loss.backward() # 反向传播计算梯度
optimizer.step() # 更新参数
- y_pred = model(x_data): 底层自动调用的
forward方法,执行线性计算,得到所有样本的预测值,同时构建动态计算图,记录所有计算过程,为反向传播求导做准备。
- loss = criterion(y_pred, y_data): 调用损失函数实例
criterion,传入预测值、真实值,计算得到当前轮次的损失值loss。
torch.nn.MSELoss类同样继承自Module,criterion(y_pred, y_data)本质是调用 MSELoss 类的*forward* 方法,此方法返回的loss为Tensor(张量),只有 Tensor 能携带计算图和梯度函数,让 PyTorch 知道 "loss 由 y_pred 计算而来,y_pred 由 weight/bias 计算而来",从而反向推导参数的梯度,因此后续可以使用loss.backward() 方法。
(关于MSELoss类的底层逻辑在文章下方有补充说明) - ***optimizer.zero_grad():***梯度清零,这行必须写,否则梯度会自动累加,上一轮的梯度如果不清零,会和本轮梯度叠加,导致参数更新错误。
- loss.backward(): 调用张量
loss的backward()方法,自动微分引擎会根据前向传播的计算图,自动计算所有可训练参数的梯度,并将梯度存储在参数的.grad属性中; - optimizer.step(): 优化器读取参数的梯度
.grad,根据 SGD 算法自动更新权重和偏置。
补充1:Linear类底层逻辑,更深刻理解***self.linear = torch.nn.Linear(1, 1)***这一步的linear都有哪些属性与方法
python# 模拟PyTorch的nn.Linear底层 class Linear(nn.Module): def __init__(self, in_features, out_features, bias=True): super().__init__() self.in_features = in_features # 输入维度 self.out_features = out_features # 输出维度 # 初始化权重(可训练参数) self.weight = nn.Parameter(torch.randn(out_features, in_features)) if bias: # 初始化偏置(可训练参数) self.bias = nn.Parameter(torch.randn(out_features)) else: self.bias = None def forward(self, x): # 线性计算核心:y = x * weight^T + bias output = x @ self.weight.t() # @是矩阵乘法,.t()是转置 if self.bias is not None: output += self.bias return output补充2:MSELoss类的底层逻辑,更深刻理解criterion = nn.MSELoss(size_average=False) 这一步的criterion都有哪些属性与方法
python# 模拟MSELoss的底层 class MSELoss(nn.Module): def __init__(self, size_average=False): super().__init__() self.size_average = size_average def forward(self, input, target): # 计算均方误差:(预测值-真实值)^2 的和/均值 diff = input - target square_diff = diff **2 if self.size_average: loss = square_diff.mean() # 均值 else: loss = square_diff.sum() # 求和(对应你的代码) return loss
完整实例
python
import torch
x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[2.0], [4.0], [6.0]])
class LinearModel(torch.nn.Module):
def __init__(self):
super(LinearModel, self).__init__()
self.linear = torch.nn.Linear(1, 1)
def forward(self, x):
y_pred = self.linear(x)
return y_pred
model = LinearModel()
criterion = torch.nn.MSELoss(size_average=False)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
for epoch in range(1000):
y_pred = model(x_data)
loss = criterion(y_pred, y_data)
print(epoch, loss.item())
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())
# 推理预测
x_test = torch.Tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)
item():PyTorch 标量张量(只有一个元素的 Tensor) 特有的方法。将标量 Tensor 转换成Python 原生的数值类型(int/float),同时剥离 Tensor 携带的计算图、梯度、设备(CPU/GPU)等所有额外信息,只保留纯数值。