跳转到内容

1:基础的线性回归

机器学习Banner

💙 从一条直线开始理解机器学习 💙

Section titled “💙 从一条直线开始理解机器学习 💙”

~(∠・ω< )⌒☆ ciallo~

在上一部分,你已经迈出了机器学习的第一步,熟悉了Python、NumPy和PyTorch的基本操作。现在,让我们正式进入机器学习最基础也最核心的模型——线性回归

线性回归是机器学习中最简单的模型之一,但它蕴含的思想却贯穿整个深度学习领域:定义模型 → 定义损失函数 → 梯度下降优化参数。如果把神经网络比作一座摩天大楼,那么线性回归就是这栋大楼最底层的砖石。理解透彻线性回归,你就掌握了通往复杂模型的钥匙。

在本节中,你将:

  1. 理解梯度、反向传播和梯度下降等核心概念
  2. 动手实现线性回归的各个组件
  3. 使用PyTorch完成一个完整的线性回归任务

在你动手写代码之前,请先回答以下问题。这些概念是整个深度学习的基石,花时间理解它们,后面的路会顺畅很多。

在微积分中,梯度是一个向量,它由多元函数对所有变量的偏导数组成。请结合你的理解回答:

  • 梯度的数学定义是什么?(可结合二元函数 f(x,y)f(x, y) 进行说明)
  • 梯度的几何意义是什么?(提示:梯度方向与函数值变化的关系)
  • 梯度在机器学习中扮演什么角色?
  • 请简述梯度下降算法的基本思想。
  • 梯度下降中的”学习率”是什么?它过大或过小会带来什么问题?
  • 常见的梯度下降变体有哪些?(至少列举两种,如批量梯度下降、随机梯度下降、小批量梯度下降等)
  • 反向传播算法解决了什么问题?
  • 请简要描述反向传播的计算过程(可以结合计算图进行说明)。
  • 反向传播与链式求导法则之间有什么关系?
  • 损失函数的作用是什么?
  • 请列举至少三种常见的损失函数,并说明它们通常用于什么类型的任务。(提示:回顾均方误差、交叉熵损失等)
  • 对于线性回归任务,通常使用什么损失函数?为什么?

对于线性回归模型 y=wx+by = w x + b(简单的一元情况),其中 ww 为权重,bb 为偏置:

  • 请写出均方误差损失函数 LL 的表达式(假设有 nn 个样本)。
  • 请推导出损失函数 LL 对参数 wwbb 的梯度(即 Lw\frac{\partial L}{\partial w}Lb\frac{\partial L}{\partial b})。
  • 请写出参数更新的公式。

在这一部分,你需要根据提示补全代码,实现一个简单的线性回归模型。所有的代码框架已经给出,你只需要在标记了 ______ 的地方填入正确的代码。

请仔细阅读每个代码块的注释,理解每一行代码的作用。

import torch
import numpy as np
import matplotlib.pyplot as plt

我们首先创建一组简单的模拟数据,用于训练线性回归模型。

# 设置随机种子,保证结果可复现
torch.manual_seed(42)
# 生成100个样本点,真实关系为 y = 2x + 1 + 噪声
def generate_data(n_samples=100):
# 生成 x,范围在 [0, 10] 之间
x = torch.linspace(0, 10, n_samples).reshape(-1, 1)
# 真实的权重和偏置
w_true = 2.0
b_true = 1.0
# 生成 y,加上高斯噪声
noise = torch.randn(n_samples, 1) * 0.5
y = w_true * x + b_true + noise
return x, y
x_data, y_data = generate_data()
print(f"x 的形状: {x_data.shape}")
print(f"y 的形状: {y_data.shape}")
print(f"前5个样本点:\n x: {x_data[:5].flatten()}\n y: {y_data[:5].flatten()}")
# TODO: 初始化参数 w 和 b
# 提示:使用 torch.tensor 创建张量
# 初始化权重 w,形状为 (1, 1),初始值为 0
w = ______
# 初始化偏置 b,形状为 (1,),初始值为 0
b = ______
print(f"初始 w: {w.item():.4f}, 初始 b: {b.item():.4f}")
# TODO: 定义线性回归模型的前向计算
def linear_model(x, w, b):
"""
线性回归模型: y = w * x + b
参数:
x: 输入数据,形状为 (n_samples, 1)
w: 权重,形状为 (1, 1)
b: 偏置,形状为 (1,)
返回:
预测值,形状为 (n_samples, 1)
"""
return ______
# TODO: 定义均方误差损失函数
def mse_loss(y_pred, y_true):
"""
均方误差损失函数: L = (1/n) * Σ(y_pred - y_true)²
参数:
y_pred: 预测值,形状为 (n_samples, 1)
y_true: 真实值,形状为 (n_samples, 1)
返回:
损失值,一个标量
"""
return ______

2.6 定义梯度计算函数(手动推导 + 代码实现)

Section titled “2.6 定义梯度计算函数(手动推导 + 代码实现)”

这是最关键的一步!请根据你在 Part 1 中推导出的梯度公式,实现梯度计算。

# TODO: 手动计算损失函数对 w 和 b 的梯度
def compute_gradients(x, y_true, y_pred):
"""
计算损失函数对 w 和 b 的梯度。
注意:这里我们不使用 PyTorch 的自动求导,而是手动实现梯度计算。
提示:
- 均方误差 L = (1/n) * Σ(y_pred - y_true)²
- ∂L/∂w = (2/n) * Σ[(y_pred - y_true) * x]
- ∂L/∂b = (2/n) * Σ[(y_pred - y_true)]
参数:
x: 输入数据,形状为 (n_samples, 1)
y_true: 真实值,形状为 (n_samples, 1)
y_pred: 预测值,形状为 (n_samples, 1)
返回:
grad_w: 损失函数对 w 的梯度
grad_b: 损失函数对 b 的梯度
"""
n = x.shape[0] # 样本数量
# 计算预测值与真实值的差值
diff = ______
# 计算梯度(注意形状匹配)
grad_w = ______
grad_b = ______
return grad_w, grad_b
# TODO: 实现梯度下降的参数更新
def update_parameters(w, b, grad_w, grad_b, learning_rate):
"""
使用梯度下降更新参数
w_new = w - lr * grad_w
b_new = b - lr * grad_b
参数:
w: 当前权重
b: 当前偏置
grad_w: w 的梯度
grad_b: b 的梯度
learning_rate: 学习率
返回:
更新后的 w 和 b
"""
w_new = ______
b_new = ______
return w_new, b_new
# TODO: 补全训练循环
def train(x, y, w, b, learning_rate, num_epochs):
"""
训练线性回归模型
参数:
x: 训练数据
y: 目标值
w: 初始权重
b: 初始偏置
learning_rate: 学习率
num_epochs: 训练轮数
返回:
训练过程中的损失记录, 训练好的 w 和 b
"""
losses = []
for epoch in range(num_epochs):
# 1. 前向传播:计算预测值
y_pred = ______
# 2. 计算损失
loss = ______
losses.append(loss.item())
# 3. 计算梯度
grad_w, grad_b = ______
# 4. 更新参数
w, b = ______
# 5. 每100轮打印一次损失
if (epoch + 1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.6f}, w: {w.item():.4f}, b: {b.item():.4f}')
return losses, w, b
# 设置超参数
learning_rate = 0.01
num_epochs = 1000
# 开始训练
losses, w_trained, b_trained = train(x_data, y_data, w, b, learning_rate, num_epochs)
print("\n===== 训练完成 =====")
print(f"真实的 w: 2.0, 学习到的 w: {w_trained.item():.4f}")
print(f"真实的 b: 1.0, 学习到的 b: {b_trained.item():.4f}")
# 绘制损失下降曲线
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.grid(True)
# 绘制拟合结果
plt.subplot(1, 2, 2)
plt.scatter(x_data.numpy(), y_data.numpy(), alpha=0.6, label='Training data')
plt.plot(x_data.numpy(), (w_trained * x_data + b_trained).numpy(),
color='red', linewidth=2, label='Fitted line')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Linear Regression Fit')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.savefig('./images/linear_regression_manual_result.png', dpi=150)
plt.show()

Part 3: 实战——用 PyTorch 拟合二元函数

Section titled “Part 3: 实战——用 PyTorch 拟合二元函数”

恭喜你完成了手动实现的线性回归!现在,让我们使用 PyTorch 的自动求导机制和优化器来完成一个更复杂的任务:拟合一个二元函数 z=f(x1,x2)z=f(x_1,x_2)

给定一个二元函数:
z=3x1+2x2+1+ϵz=3x_1+2x_2+1+ϵ
其中 ϵ 是均值为0、标准差为0.3的高斯噪声。
你需要:

  1. 生成训练数据集
  2. 使用 PyTorch 构建线性回归模型(使用 nn.Module)
  3. 使用均方误差损失函数
  4. 训练模型
  5. 使用 matplotlib 绘制训练损失曲线,并可视化模型拟合效果
  6. 将结果保存为图片
  1. 线性回归的局限性:如果真实的数据关系是 z=x12+x22+1z=x_1^2+x_2^2+1,线性回归能很好地拟合吗?如果不能,你觉得可以用什么方法解决?
  2. 梯度下降的收敛性:观察你的损失曲线,它是单调递减的吗?如果不是,可能的原因是什么?

与第0部分保持一致,我们鼓励你聪明地使用AI,但请确保:

  1. 所有代码必须自己理解,并添加足够的注释说明你的理解
  2. 简答题请用自己的语言回答,不要直接复制粘贴
  3. 在代码注释或文档中标注你在哪些地方使用了AI的帮助
  • 代码文件

    将 Part 2 和 Part 3 的代码整合到一个 .ipynb 文件中,命名格式:ml-1-姓名-学号.ipynb

  • 文档报告

    将 Part 1 的简答题答案以及 Part 3 的思考题(如果做的话)写入 .md 文件,命名格式:ml-1-姓名-学号.md

  • 邮箱: gimmerml401@163.com

  • 主题: 1-姓名-学号

出题人:出题人头像  Taffy

QQ:2078306188

邮箱:2078306188@qq.com