Aller au contenu principal

深度学习 1 — 线性神经元

:::tip Kaggle 笔记本 本章的完整可执行代码在 Kaggle 上:打开 →

法语和英语版本可在首页查看。 :::

第一次接触神经网络,从最简单的情况开始:线性神经元。这里不要期待深度学习——我们正在构建为后续每一章服务的基础。

为什么要学这一章?

在 CNN 和 Transformer 之前,我们需要理解什么是神经元以及它如何学习。您将看到:

  • 什么是线性神经元及其与线性回归的联系;
  • 手动编写的梯度下降
  • 为什么需要归一化输入
  • PyTorch 入门:张量、nn.Linear、优化器、DataLoader
  • 非线性激活函数的重要性。

线性神经元

它实际上是机器学习第二章的线性回归,换了一种表达:

u=Xw+bu = X w + b

其中 XRn×mX \in \mathbb{R}^{n \times m} 是输入,wRmw \in \mathbb{R}^m 是权重,bb 是偏置,uu 是输出。

学习意味着找到最小化损失函数的合适 wwbb。对于回归:

E(w,b)=1ni=1n(yiui)2E(w, b) = \frac{1}{n}\sum_{i=1}^n (y_i - u_i)^2

这是已经遇到过的 MSE

梯度下降

当解析解不存在或代价太高时,我们沿使 EE 下降的方向逐步调整参数:

wwηEw,bbηEbw \leftarrow w - \eta \, \frac{\partial E}{\partial w}, \quad b \leftarrow b - \eta \, \frac{\partial E}{\partial b}

其中 η\eta学习率

对于线性回归,计算给出:

Ew=1nXT(uy),Eb=1ni=1n(uiyi)\frac{\partial E}{\partial w} = \frac{1}{n} X^T (u - y), \quad \frac{\partial E}{\partial b} = \frac{1}{n} \sum_{i=1}^n (u_i - y_i)

(uy)(u - y) 项是预测误差。梯度通过 XTX^T 将这个误差传播到 ww

三个变体

  • 批量(Batch):每步在整个数据集上计算梯度。在大数据集上慢。
  • SGD随机):一个样本。快但有噪声。
  • 小批量(Minibatch):一个子集(32, 64, ...)。实践中使用的折中。

为什么要归一化?

不归一化时,大尺度的变量会主导梯度下降的动态,并强制使用极小的学习率。收敛变得很慢。

对于线性回归,损失关于 ww曲率xi2\sum x_i^2 成正比。如果 xx 有大值,曲率就大,η\eta 必须小。

解决方案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 层不会创建更强大的模型——代数上,两个线性变换组合成一个线性变换:

u=(XW1+b1)W2+b2=X(W1W2)+(b1W2+b2)u = (X W_1 + b_1) W_2 + b_2 = X (W_1 W_2) + (b_1 W_2 + b_2)

为了走得更远,必须在层之间插入非线性

三种经典激活

  • Sigmoidσ(z)=11+ez\sigma(z) = \dfrac{1}{1 + e^{-z}},输出在 (0,1)(0, 1)z|z| 大时饱和。
  • Tanhtanh(z)\tanh(z),输出在 (1,1)(-1, 1),以 0 为中心。
  • ReLUmax(0,z)\max(0, z)z>0z > 0 时不饱和。现代深度学习的默认激活
model = nn.Sequential(
nn.Linear(m, 16),
nn.ReLU(), # 非线性
nn.Linear(16, 1),
)

深度只有在有非线性时才有意义。


Kaggle 上的完整笔记本(可分叉)→