深度学习 1 — 线性神经元
:::tip Kaggle 笔记本 本章的完整可执行代码在 Kaggle 上:打开 →
法语和英语版本可在首页查看。 :::
第一次接触神经网络,从最简单的情况开始:线性神经元。这里不要期待深度学习——我们正在构建为后续每一章服务的基础。
为什么要学这一章?
在 CNN 和 Transformer 之前,我们需要理解什么是神经元以及它如何学习。您将看到:
- 什么是线性神经元及其与线性回归的联系;
- 手动编写的梯度下降;
- 为什么需要归一化输入;
- PyTorch 入门:张量、
nn.Linear、优化器、DataLoader; - 非线性激活函数的重要性。
线性神经元
它实际上是机器学习第二章的线性回归,换了一种表达:
其中 是输入, 是权重, 是偏置, 是输出。
学习意味着找到最小化损失函数的合适 和 。对于回归:
这是已经遇到过的 MSE。
梯度下降
当解析解不存在或代价太高时,我们沿使 下降的方向逐步调整参数:
其中 是学习率。
对于线性回归,计算给出:
项是预测误差。梯度通过 将这个误差传播到 。
三个变体
- 批量(Batch):每步在整个数据集上计算梯度。在大数据集上慢。
- SGD(随机):一个样本。快但有噪声。
- 小批量(Minibatch):一个子集(32, 64, ...)。实践中使用的折中。
为什么要归一化?
不归一化时,大尺度的变量会主导梯度下降的动态,并强制使用极小的学习率。收敛变得很慢。
对于线性回归,损失关于 的曲率与 成正比。如果 有大值,曲率就大, 必须小。
解决方案:StandardScaler 将所有变量带到均值 0 和标准差 1。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
:::warning fit_transform vs transform
仅在训练集上拟合(fit_transform),然后应用到测试集(transform)。否则:数据泄漏。
:::
PyTorch:autograd 和优化器
PyTorch 自动化了我们以前手动做的两件事:
- 梯度计算(
autograd); - 参数更新(优化器:SGD、Adam 等)。
PyTorch 张量相当于 NumPy 数组,但增加了存储梯度和在 GPU 上运行的能力。
import torch
import torch.nn as nn
# NumPy → 张量转换
X_train_t = torch.tensor(X_train, dtype=torch.float32)
y_train_t = torch.tensor(y_train, dtype=torch.float32).view(-1, 1)
.view(-1, 1) 将 y 调整为 (n, 1) 以匹配 nn.Linear(m, 1) 层的输出。
PyTorch 中的线性神经元
model = nn.Linear(m, 1)
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
标准训练循环
for _ in range(epochs):
optimizer.zero_grad() # 1. 将梯度归零
y_hat = model(X_train_t) # 2. 前向
loss = criterion(y_hat, y_train_t)
loss.backward() # 3. autograd 计算所有梯度
optimizer.step() # 4. 更新参数
记住这 4 步——它们会在所有后续章节中重现。
DataLoader:自动小批量
为了不手动选择小批量:
from torch.utils.data import TensorDataset, DataLoader
train_dataset = TensorDataset(X_train_t, y_train_t)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
for _ in range(epochs):
for Xb, yb in train_loader:
optimizer.zero_grad()
y_hat = model(Xb)
loss = criterion(y_hat, yb)
loss.backward()
optimizer.step()
shuffle=True 在每个 epoch 重新混洗数据——训练时几乎总是需要的。
堆叠和非线性
堆叠两个 nn.Linear 层不会创建更强大的模型——代数上,两个线性变换组合成一个线性变换:
为了走得更远,必须在层之间插入非线性。
三种经典激活
- Sigmoid:,输出在 。 大时饱和。
- Tanh:,输出在 ,以 0 为中心。
- ReLU:。 时不饱和。现代深度学习的默认激活。
model = nn.Sequential(
nn.Linear(m, 16),
nn.ReLU(), # 非线性
nn.Linear(16, 1),
)
深度只有在有非线性时才有意义。