使用PyTorch实现线性回归

名词解释:张量、标量、向量、矩阵

  • 张量:即前面提到的Tensor,是 PyTorch 存储数据和执行计算的基本单位,张量是一个多维数组,它是标量、向量和矩阵的广义统称。

  • 标量:标量是一个零维张量 (0-D Tensor) ,它只包含一个数值,没有方向或大小之分。

    • torch.tensor(5.0)
  • 向量:向量是一个一维张量 (1-D Tensor),它包含一列有序的数值,只有大小和方向。

    • torch.tensor([1, 2, 3])
  • 矩阵:矩阵是一个二维张量 (2-D Tensor) ,它由行和列组成,是两个向量的排列。

    • torch.tensor([[1, 2], [3, 4]])

[!TIP]

几个 [] 就是几维张量

前置关键代码解释

nn.Linear(in_features, out_features, bias=true)

一共三个主要参数需要填写,Linear源码如下图:

image-20251011191525307

  • in_features:输入特征数,或者说输入的维度,比如说你的输入矩阵是一个3*2矩阵,每一行代表单个样本,也就是说一个样本有两个维度,即列数,那么这里就应该设置in_features=2
  • out_features:输出特征数,同上。如果输出是一个4*3的矩阵,每一行代表单个样本,也就是有3个维度,那么就应该设置out_features=3
  • bias:默认为True,用于是否要在计算过程中加上偏置项b

[!NOTE]

这里有一个很重要的思想转变,即输入输出的值,从"数字"变成“矩阵”

看网课时我一开始一直没理解老师为什么要一直使用矩阵的知识,我默认认为一个线性模型y = w*x + b中的x、y、w、b都只是一个数字。

但是现在要转变观念,X可以是一个m * n的矩阵,Y可以是一个 m * q 的矩阵

那么又引申出一个问题,W应该是怎样的矩阵?

当使用PyTorch进行线性回归时,将会遵循以下乘法规则:

image-20251011193345138

至于为什么要对W进行转置,请往下看:

  • 当你输入nn.Linear(3,2)时,也就意味着你的输入数据集X_Data是形状是 N * 3,输出数据集Y_Data的形状是 N * 2,总共有N个样本

  • 如果采用上图的乘法顺序,W的性质应该刚好是 3 * 2,和你括号中输入的顺序是一致的,但是由于PyTorch内部规定了需要取转置后再乘(如下图),所以为了符合直觉,源代码中特地反转了。

    image-20251011194135976

optimizer = torch.optim.SGD(my_model.parameters(), lr=0.01)

  • my_model.parameters() 的作用是返回一个迭代器 (iterator) ,其中包含了 my_model 实例中所有需要学习和更新的参数 (Parameter) 。
  • 反正是pytorch帮忙找,用迭代找的,后续需要再深入学习。

执行my_model(x_data)会自动执行forward返回预测值

具体代码

import torch
from torch import nn ## nn 是 neural network 神经网络的缩写
import matplotlib.pyplot as plt

#定义数据集,设置为矩阵模式,3*1 的矩阵
x_data = torch.tensor([[1.0], [2.0], [3.0]], dtype=torch.float)
y_data = torch.tensor([[2.0], [4.0], [6.0]], dtype=torch.float)

class LinearRegression(nn.Module):
    def __init__(self):
        # 调用父类的初始化方法
        super(LinearRegression, self).__init__()
        # nn.Linear 自动创建并初始化 w 和 b,反正会自动,具体可以后续详细了解
        self.linear = nn.Linear(1, 1)

    # 前馈
    def forward(self, x):
        # 默认执行 y = xW^T + b.
        return self.linear(x)

    # 反馈,PyTorch会自动帮你求导,除非你自己写的效率比PyTorch还高

# 实例化自己的模型
my_model = LinearRegression()
# 均方误差损失函数
criterion = nn.MSELoss()
# 使用随机梯度下降优化器(即更新权重的算法)
optimizer = torch.optim.SGD(my_model.parameters(), lr=0.01)

# 打印训练前的参数
w_init = my_model.linear.weight.item()
b_init = my_model.linear.bias.item()
print(f"训练前参数: w={w_init:.4f}, b={b_init:.4f}")

# 3. 训练参数
num_epochs = 500
costs_list = []
epochs_list = []

for epoch in range(num_epochs):
    # 每次迭代都需要执行的操作:

    # (A) 梯度清零:使用优化器内置的方法,替代 w.grad.zero_()
    optimizer.zero_grad()

    # (B) 前向传播
    y_pred = my_model(x_data)

    # (C) 计算损失
    loss = criterion(y_pred, y_data)

    # (D) 反向传播:计算梯度
    loss.backward()

    # (E) 更新权重:使用优化器内置的方法,替代 with torch.no_grad(): W -= lr * W.grad
    optimizer.step()

    # 记录损失(使用更新后的权重计算)
    # 如果不在意小精度,也可以直接costs_list.append(loss.item()),也就是使用旧的W
    with torch.no_grad():
        costs_list.append(criterion(my_model(x_data), y_data).item())
    epochs_list.append(epoch)

    if (epoch + 1) % 10 == 0:
        current_w = my_model.linear.weight.item()
        current_b = my_model.linear.bias.item()
        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.6f}, w: {current_w:.4f}, b: {current_b:.4f}')

# 5. 最终预测和结果
print("-" * 40)
final_w = my_model.linear.weight.item()
final_b = my_model.linear.bias.item()

# 预测 x=4
x_test = torch.tensor([[4.0]], dtype=torch.float32)
y_test_pred = my_model(x_test).item()

print(f"训练后参数: w={final_w:.4f}, b={final_b:.4f}")
print(f"训练后预测 x=4, y_pred={y_test_pred:.4f}")

# 6. 绘图
plt.plot(epochs_list, costs_list)
plt.ylabel('MSE Loss')
plt.xlabel('Epoch')
plt.show()

训练结果最好的一次

使用其他优化器

image-20251011202737953

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐