第一个模型:30 分钟跑通全流程
目标:亲手走完「数据 → 模型 → 损失 → 优化 → 评估」这一圈。
这一页的意义不在结果(线性回归太简单了),而在于建立地图:之后学的所有模型,都是这五步的变体。
全流程只有五步
1
准备数据(x, y) 成对样本
2
定义模型y = wx + b
3
定义损失MSE 衡量差距
4
优化参数沿梯度反方向走
5
评估在新数据上看误差
任务:用学习时长预测考试分数
| 学习时长(h) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| 分数 | 42 | 55 | 58 | 71 | 78 | 83 | 92 | 96 |
(理想情况接近「分数 ≈ 40 + 7 × 时长」,我们让模型自己找出来。)
版本一:NumPy 手写(推荐先跑这个)
不用框架,看清每一步在干什么。
python
import numpy as np
# ① 数据
x = np.array([1, 2, 3, 4, 5, 6, 7, 8], dtype=float)
y = np.array([42, 55, 58, 71, 78, 83, 92, 96], dtype=float)
# ② 模型:y_hat = w * x + b
w, b = 0.0, 0.0
# ③ 超参数
lr = 0.01 # 学习率
epochs = 2000 # 训练轮数
n = len(x)
for epoch in range(epochs):
# 前向:预测
y_hat = w * x + b
# 损失:均方误差
loss = np.mean((y_hat - y) ** 2)
# 反向:对 w 和 b 求偏导(手推出来的,见下)
dw = 2 * np.mean((y_hat - y) * x)
db = 2 * np.mean(y_hat - y)
# ④ 优化:沿梯度反方向走一小步
w -= lr * dw
b -= lr * db
if epoch % 400 == 0:
print(f"epoch={epoch:4d} loss={loss:8.3f} w={w:.3f} b={b:.3f}")
print(f"\n最终: y ≈ {w:.2f}x + {b:.2f}")
print(f"测试:学 10 小时 → 预测 {w*10+b:.1f} 分")你应该看到:loss 从上千降到个位数,w 收敛到约 7.3,b 收敛到约 35。
那两行偏导是怎么来的?
损失 $L = \frac{1}{n}\sum(\hat y - y)^2$,而 $\hat y = wx + b$。对 $w$ 求导得 $\frac{2}{n}\sum(\hat y-y)x$,对 $b$ 求导得 $\frac{2}{n}\sum(\hat y-y)$。
看不懂没关系 —— PyTorch 会自动帮你算。但你要知道「这里确实是在求导」,不是魔法。数学细节见 L1 数学。
版本二:PyTorch 自动求导
同样的任务,让框架算梯度。
python
import torch
import torch.nn as nn
x = torch.tensor([[1.], [2.], [3.], [4.], [5.], [6.], [7.], [8.]])
y = torch.tensor([[42.], [55.], [58.], [71.], [78.], [83.], [92.], [96.]])
# ② 模型
model = nn.Linear(in_features=1, out_features=1)
# ③ 损失 + ④ 优化器
loss_fn = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
for epoch in range(2000):
pred = model(x) # 前向
loss = loss_fn(pred, y) # 算损失
optimizer.zero_grad() # 清空上轮梯度(漏了会累加!)
loss.backward() # 反向传播,自动求导
optimizer.step() # 更新参数
if epoch % 400 == 0:
w = model.weight.item()
b = model.bias.item()
print(f"epoch={epoch:4d} loss={loss.item():8.3f} w={w:.3f} b={b:.3f}")三个必踩的坑
- 忘了
zero_grad()→ 梯度在上一轮基础上累加,模型乱飞 - 忘了
loss.backward()→ 梯度是空的,参数永远不变 loss忘了.item()→ 把整个计算图存下来,内存爆掉
版本三:scikit-learn 三行版
实际业务里,这类简单任务直接调库:
python
import numpy as np
from sklearn.linear_model import LinearRegression
X = np.array([1, 2, 3, 4, 5, 6, 7, 8]).reshape(-1, 1)
y = np.array([42, 55, 58, 71, 78, 83, 92, 96])
model = LinearRegression().fit(X, y)
print(f"y ≈ {model.coef_[0]:.2f}x + {model.intercept_:.2f}")
print(f"R² = {model.score(X, y):.3f}")三行搞定,但你应该先跑版本一,否则永远不知道 fit 里发生了什么。
三个版本对照
| 手写 NumPy | PyTorch | scikit-learn | |
|---|---|---|---|
| 需要理解的 | 梯度怎么算、怎么更新 | 计算图、反向传播 | 几乎没有 |
| 代码量 | ~20 行 | ~12 行 | 3 行 |
| 适用 | 学习原理 | 深度模型 | 传统 ML 业务 |
| 换模型成本 | 全部重写 | 改一行 | 改一行 |
加一步:看拟合效果
模型好不好,画出来最直观。
python
import matplotlib.pyplot as plt
plt.figure(figsize=(6, 4))
plt.scatter(x, y, label="真实数据")
plt.plot(x, w * x + b, color="crimson", label=f"拟合: y={w:.1f}x+{b:.1f}")
plt.xlabel("学习时长 (h)")
plt.ylabel("分数")
plt.legend()
plt.title("线性回归拟合结果")
plt.grid(alpha=0.3)
plt.show()看到点紧贴直线,就说明模型学到了规律。「画图检查」是 AI 工程里最便宜、最有效的验证手段,从今天开始养成。
从这一步能延伸出什么
这个 30 行的例子,是所有模型的骨架:
| 你现在做的 | 换成什么就成了… |
|---|---|
w*x+b | 换成多层神经网络 → 深度学习 |
| MSE 损失 | 换成交叉熵 → 分类任务 |
| 全量数据算梯度 | 每次取 32 条 → 小批量 SGD |
| 8 条样本 | 换成 1 万亿 token → 大模型预训练 |
| 手算梯度 | 换成 loss.backward() → 自动微分 |
框架在变,骨架从没变过。
通过标准
下一步
L0 结束,进入 L1 数学与数据基础 —— 开始补「为什么」。