HelloAI
L1 第 10 篇 🐣 难度 🕒 14 分钟

PyTorch 基础:张量、自动求导、你的第一个神经网络

把 L1 全部的数学和 Python 武器组合起来——这是你的"AI 工程师"入门仪式。

阿莱
2026/6/23

L1 学到这里,你已经懂:

  • 矩阵和梯度(L1-02, L1-03)
  • 损失函数(L1-04, L1-05)
  • 反向传播(L1-06)
  • Python + NumPy + Pandas(L1-07/08/09)

该跑你的第一个神经网络了。

我们用 PyTorch——目前学术界和工业界最主流的深度学习框架,2026 年生态已经远超 TensorFlow。

第一站:安装

PyTorch 已经预装在 Google Colab——直接用就行。

本地装:

pip install torch

GPU 版本:如果你有 NVIDIA 显卡,访问 pytorch.org 选对应 CUDA 版本。但本篇所有代码 CPU 就够跑。

第二站:张量(Tensor)

Tensor 是 PyTorch 的核心数据结构——本质上就是带 GPU 加速 + 自动求导的 NumPy 数组

import torch

# 创建 tensor(和 NumPy 几乎一样)
a = torch.tensor([1, 2, 3, 4])
print(a)              # tensor([1, 2, 3, 4])
print(a.shape)        # torch.Size([4])
print(a.dtype)        # torch.int64

# 浮点(神经网络常用)
b = torch.tensor([1.0, 2.0, 3.0])
print(b.dtype)        # torch.float32

# 从 NumPy 来
import numpy as np
arr = np.array([1, 2, 3])
t = torch.from_numpy(arr)

# 反过来
t.numpy()

# 常用创建
torch.zeros(3, 4)
torch.ones(2, 3)
torch.randn(2, 3)         # 标准正态
torch.arange(10)
torch.linspace(0, 1, 5)

第三站:张量运算

和 NumPy 99% 一样:

a = torch.tensor([[1.0, 2.0],
                  [3.0, 4.0]])
b = torch.tensor([[5.0, 6.0],
                  [7.0, 8.0]])

# 算术
a + b
a * b      # 元素相乘
a @ b      # 矩阵乘法

# 形状操作
a.shape          # torch.Size([2, 2])
a.reshape(4, 1)
a.T              # 转置

# 聚合
a.sum()
a.mean(dim=0)    # 注意是 dim 不是 axis
a.argmax(dim=1)

# 索引
a[0, 1]
a[:, 0]
a[a > 2]         # 布尔索引

关键差异:PyTorch 用 dim 不是 axis

第四站:GPU 加速

# 检查 GPU
torch.cuda.is_available()    # True/False

# 把 tensor 搬到 GPU
device = 'cuda' if torch.cuda.is_available() else 'cpu'
a = a.to(device)
b = b.to(device)

# 之后所有运算都在 GPU 上
c = a @ b   # GPU 运算

# 搬回 CPU 查看
c.cpu().numpy()

为什么 GPU 快:矩阵运算可以并行——一次算几千个元素。CPU 一次算 1 个,GPU 一次几千个。神经网络几乎全是矩阵运算,所以训练必须 GPU

第五站:自动求导(autograd)

这是 PyTorch 比 NumPy 强的地方——自动算导数。

# 让 PyTorch 追踪一个 tensor
x = torch.tensor(2.0, requires_grad=True)

# 做一些运算
y = x ** 2 + 3 * x + 1

# 一行算梯度
y.backward()

# 查看
print(x.grad)   # tensor(7.) — dy/dx = 2x + 3,x=2 时为 7

回想 L1-06 我们手算的 2 层网络反向传播——PyTorch 直接帮你做了。

一个完整的反向传播例子

# 参数(要训练的)
w = torch.tensor(3.0, requires_grad=True)
b = torch.tensor(0.0, requires_grad=True)

# 数据
x = torch.tensor(2.0)
target = torch.tensor(5.0)

# 前向
y = w * x + b
loss = (y - target) ** 2

# 反向(一行)
loss.backward()

# 查看每个参数的梯度
print(f"dw = {w.grad}")    # ≈ 4
print(f"db = {b.grad}")    # ≈ 2

这取代了 L1-06 我们手算的几页推导

第六站:第一个神经网络

让我们用 PyTorch 实现 L1-06 那个 2 层网络:

import torch
import torch.nn as nn

# 定义网络
class TinyNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(1, 8)    # 输入 1 维,输出 8 维
        self.fc2 = nn.Linear(8, 1)    # 输入 8 维,输出 1 维

    def forward(self, x):
        x = self.fc1(x)
        x = torch.relu(x)
        x = self.fc2(x)
        return x

model = TinyNet()
print(model)
# TinyNet(
#   (fc1): Linear(in_features=1, out_features=8, bias=True)
#   (fc2): Linear(in_features=8, out_features=1, bias=True)
# )

# 看一下有多少参数
total_params = sum(p.numel() for p in model.parameters())
print(f"参数总数: {total_params}")  # 25

第七站:完整训练循环

让我们用这个网络拟合 y=x2y = x^2

import torch
import torch.nn as nn
import torch.optim as optim

# 1. 数据
X = torch.linspace(-5, 5, 100).reshape(-1, 1)
y = X ** 2 + torch.randn_like(X) * 0.1   # 加点噪声

# 2. 模型
model = nn.Sequential(
    nn.Linear(1, 32),
    nn.ReLU(),
    nn.Linear(32, 32),
    nn.ReLU(),
    nn.Linear(32, 1)
)

# 3. 损失函数和优化器
loss_fn = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# 4. 训练循环
for epoch in range(500):
    # 前向
    y_pred = model(X)
    loss = loss_fn(y_pred, y)

    # 反向 + 更新(这三行是 PyTorch 训练的"咒语")
    optimizer.zero_grad()    # 清零上一轮梯度
    loss.backward()           # 反向传播
    optimizer.step()          # 更新参数

    if epoch % 100 == 0:
        print(f"epoch {epoch:3d} | loss {loss.item():.4f}")

# 5. 看预测
with torch.no_grad():           # 推理时不需要梯度
    test_x = torch.tensor([[3.0]])
    test_y = model(test_x)
    print(f"\nf(3) ≈ {test_y.item():.2f} (真实 9.0)")

跑一下,你会看到 loss 一路下降,最终预测 f(3)9f(3) ≈ 9

💡 PyTorch 训练的"咒语"
optimizer.zero_grad()
loss.backward()
optimizer.step()

这三行你会在每个 PyTorch 训练代码里都看到。它就是:

  1. 清掉上轮残留的梯度
  2. 算这轮的梯度
  3. 用梯度更新参数

第八站:批训练 + DataLoader

实际训练几百万样本,不能一次全塞进去——分批:

from torch.utils.data import DataLoader, TensorDataset

# 包装数据
dataset = TensorDataset(X, y)
loader = DataLoader(dataset, batch_size=16, shuffle=True)

# 训练
for epoch in range(20):
    for batch_X, batch_y in loader:
        # 每个 batch 单独做前向 + 反向
        y_pred = model(batch_X)
        loss = loss_fn(y_pred, batch_y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

Mini-batch SGD 是工业训练的标配——比一次全量训练快,比单个样本训练稳。

第九站:模型存取

# 保存
torch.save(model.state_dict(), 'model.pth')

# 加载(必须先定义同样结构的模型)
model = nn.Sequential(...)   # 同样结构
model.load_state_dict(torch.load('model.pth'))
model.eval()                  # 切到推理模式

state_dict 只存参数(不存网络结构)——这是最佳实践。

第十站:常见错误

错误 1:忘了 zero_grad

# ❌ 不清零,梯度会累积
loss.backward()
optimizer.step()

# ✅
optimizer.zero_grad()
loss.backward()
optimizer.step()

错误 2:训练 vs 推理模式

model.train()      # 训练模式(dropout 开)
model.eval()       # 推理模式(dropout 关)

# 推理时还要这样:
with torch.no_grad():
    pred = model(x)   # 不计算梯度,省一半显存

错误 3:shape 不匹配

PyTorch 报错最多的就是 shape——养成”先 print shape” 的习惯。

print(x.shape, y.shape)   # debug 神器

一个完整的”端到端”项目

把 L1 全部学的东西串起来——预测房价:

import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.preprocessing import StandardScaler
from torch.utils.data import DataLoader, TensorDataset

# 1. 加载数据(用 Pandas)
df = pd.read_csv('housing.csv')
X = df[['rooms', 'age', 'distance']].values
y = df['price'].values

# 2. 标准化
scaler = StandardScaler()
X = scaler.fit_transform(X)

# 3. 转 tensor
X = torch.FloatTensor(X)
y = torch.FloatTensor(y).reshape(-1, 1)

# 4. 数据加载器
dataset = TensorDataset(X, y)
loader = DataLoader(dataset, batch_size=32, shuffle=True)

# 5. 模型
model = nn.Sequential(
    nn.Linear(3, 64),
    nn.ReLU(),
    nn.Linear(64, 64),
    nn.ReLU(),
    nn.Linear(64, 1)
)

# 6. 训练
optimizer = optim.Adam(model.parameters(), lr=0.001)
loss_fn = nn.MSELoss()

for epoch in range(50):
    total_loss = 0
    for bx, by in loader:
        pred = model(bx)
        loss = loss_fn(pred, by)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        total_loss += loss.item()

    print(f"Epoch {epoch:02d} | Loss: {total_loss/len(loader):.4f}")

这就是一个真实的 ML 项目结构——L1 学完,你已经能写出来了。

🔬 L1 路径毕业

读完 L1-10,你已经掌握了:

  • 完整 ML 数学(线代 / 微积分 / 概率 / 信息论 / 反向传播)
  • Python + NumPy + Pandas
  • PyTorch 基础 + 训练神经网络

你现在已经能跟 ML 工程师对话了

下一步:

  • L2 经典机器学习(决策树、SVM、聚类等)
  • L3 深度学习核心(CNN、Transformer 等)
  • L4 LLM 与生成式(最热的方向)

按兴趣选。

📬

读到这里说明你认真在学 🎯

订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。

💬

讨论区

· 用 GitHub 账号登录评论
⚠️ Giscus 评论未配置 —— 在 src/components/Comments.astro 顶部填入 仓库 ID 和分类 ID(见组件注释里的配置步骤)。