这类深度学习算法教程最值得先看的不是标题里的“保姆级”或“一口气学透”,而是它到底能不能帮你把每个算法的核心逻辑、适用场景和实际代码跑通。很多教程把 CNN、RNN、Transformer 等算法堆在一起,但初学者最容易卡住的是分不清什么时候该用哪个、怎么调参数、以及怎么从单样本实验扩展到真实数据集。

我建议先按这个顺序拆解学习路径:先搞懂每个算法解决什么问题,再跑通最小代码示例,最后再对比它们的差异和组合方式。下面我会围绕这八大算法,用可验证的代码段、参数解释和常见坑点,带你走一遍实际学习时最需要关注的实操细节。

1. 先明确每个算法到底解决什么问题,再动手写代码

很多人一上来就抄 CNN 的卷积层代码,但没搞明白为什么图像任务适合用卷积,而文本任务常用 RNN 或 Transformer。先花 10 分钟搞清楚每个算法的核心能力边界,能省掉后面 80% 的调参瞎试时间。

1.1 CNN:为什么图像、视频、局部模式检测任务首选卷积网络?

CNN 的核心是 局部感知 参数共享 。它通过卷积核在输入数据上滑动,提取局部特征(比如边缘、纹理),再通过池化层压缩特征维度。这种设计让 CNN 特别适合处理网格状数据(图像、时序信号)。

什么时候该用 CNN?

  • 输入数据有局部相关性:图像相邻像素、音频相邻帧、传感器时序信号。
  • 任务需要平移不变性:无论物体在图像中哪个位置,都能识别出来。
  • 数据维度高但特征可复用:一张 1000x1000 的图,用 3x3 卷积核只需学习一次参数,全连接层则需要 10^6 级参数。

最小代码示例(PyTorch):

import torch.nn as nn

class SimpleCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.conv_layers = nn.Sequential(
            nn.Conv2d(3, 32, kernel_size=3, padding=1),  # 输入通道3, 输出32, 卷积核3x3
            nn.ReLU(),
            nn.MaxPool2d(2),  # 池化层, 高宽减半
            nn.Conv2d(32, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.classifier = nn.Linear(64 * 8 * 8, num_classes)  # 假设输入图像是32x32, 经过两次池化后为8x8

    def forward(self, x):
        x = self.conv_layers(x)
        x = x.view(x.size(0), -1)  # 展平
        x = self.classifier(x)
        return x

关键参数解释:

  • kernel_size=3 :卷积核大小,3x3 是最常用尺寸,平衡感受野和计算量。
  • padding=1 :边缘填充,保证输出尺寸不变(当 stride=1 时)。
  • MaxPool2d(2) :2x2 最大池化,每次压缩为 1/4 大小。

最容易卡住的地方:

  • 输入图像尺寸变化:如果原始图像不是 32x32,需要自己计算经过卷积和池化后的尺寸,否则全连接层会报维度错误。
  • 通道数对齐:第一层卷积的输入通道数必须等于图像通道数(RGB 是 3,灰度是 1)。

1.2 RNN 和 LSTM:处理序列数据的核心差异在哪里?

RNN 的设计是为了处理 序列数据 (文本、语音、时间序列),它通过隐藏状态传递历史信息。但普通 RNN 有梯度消失问题,长序列下会遗忘早期信息。LSTM 通过门控机制(输入门、遗忘门、输出门)控制信息流动,能更好地捕捉长期依赖。

什么时候该用 RNN/LSTM?

  • 数据有时序关系:下一时刻的值依赖前面若干时刻。
  • 输入输出长度可变:比如机器翻译中源语言和目标语言句子长度不同。
  • 需要记忆上下文:如文本生成、语音识别。

最小代码示例(LSTM 文本分类):

class TextLSTM(nn.Module):
    def __init__(self, vocab_size=10000, embed_dim=100, hidden_dim=128, num_layers=2, num_classes=2):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.lstm = nn.LSTM(embed_dim, hidden_dim, num_layers, batch_first=True, dropout=0.2)
        self.classifier = nn.Linear(hidden_dim, num_classes)

    def forward(self, x):
        # x 形状: (batch_size, seq_length)
        x = self.embedding(x)  # 变为 (batch_size, seq_length, embed_dim)
        lstm_out, (hidden, cell) = self.lstm(x)  # lstm_out 包含每个时间步的输出
        # 取最后一个时间步的输出作为特征
        last_output = lstm_out[:, -1, :]
        return self.classifier(last_output)

关键参数解释:

  • batch_first=True :输入张量形状为 (batch_size, seq_length, features),否则默认是 (seq_length, batch_size, features)。
  • dropout=0.2 :LSTM 层间的 dropout,防止过拟合。
  • hidden_dim=128 :隐藏状态维度,影响模型容量和计算量。

常见误区:

  • 误用最后一个隐藏状态:对于分类任务,通常用最后一个时间步的输出( lstm_out[:, -1, :] )而不是最后一个隐藏状态( hidden ),因为 hidden 可能包含整个序列的压缩信息,但实验表明前者往往更直接。
  • 序列长度不一致:如果批量中序列长度不同,需要先用 pack_padded_sequence 处理,否则计算浪费。

1.3 Transformer:为什么它在长序列任务上比 RNN 更高效?

Transformer 的核心是 自注意力机制 ,可以并行计算整个序列的关联度,不受序列长度限制(RNN 必须一步步计算)。但它的计算复杂度是序列长度的平方,所以极长序列下内存可能爆掉。

什么时候该用 Transformer?

  • 序列长且需要全局依赖:如机器翻译、长文档分类。
  • 需要并行加速:训练时 Transformer 比 RNN 快得多。
  • 预训练模型迁移:BERT、GPT 等都是 Transformer 结构,微调成本低。

自注意力代码示例:

import torch.nn.functional as F

class SelfAttention(nn.Module):
    def __init__(self, embed_dim, heads=8):
        super().__init__()
        self.embed_dim = embed_dim
        self.heads = heads
        self.head_dim = embed_dim // heads
        assert self.head_dim * heads == embed_dim, "embed_dim 必须能被 heads 整除"
        
        self.query = nn.Linear(embed_dim, embed_dim)
        self.key = nn.Linear(embed_dim, embed_dim)
        self.value = nn.Linear(embed_dim, embed_dim)
        self.fc_out = nn.Linear(embed_dim, embed_dim)

    def forward(self, x):
        batch_size, seq_len, embed_dim = x.shape
        Q = self.query(x).view(batch_size, seq_len, self.heads, self.head_dim).transpose(1, 2)
        K = self.key(x).view(batch_size, seq_len, self.heads, self.head_dim).transpose(1, 2)
        V = self.value(x).view(batch_size, seq_len, self.heads, self.head_dim).transpose(1, 2)
        
        energy = torch.matmul(Q, K.transpose(-2, -1)) / (self.head_dim ** 0.5)
        attention = F.softmax(energy, dim=-1)
        out = torch.matmul(attention, V).transpose(1, 2).contiguous().view(batch_size, seq_len, embed_dim)
        return self.fc_out(out)

多头注意力关键点:

  • heads=8 :将嵌入维度拆成 8 份,每份独立计算注意力,最后拼接。多头能让模型关注不同方面的信息。
  • 缩放因子 1/sqrt(d_k) :防止点积过大导致 softmax 梯度消失。

Transformer 落地最常踩的坑:

  • 位置编码缺失:Transformer 本身没有位置信息,必须加位置编码(正弦波或可学习的)。
  • 序列长度超出训练时长度:如果推理时输入比训练数据长,位置编码会外推,效果可能下降。

1.4 GAN、DQN、GNN、DBN:这些算法分别适合什么场景?

  • GAN :生成任务。比如生成图像、语音、文本。训练不稳定是最大难点,建议从 DCGAN 开始,再试 WGAN-GP。
  • DQN :强化学习中的值函数逼近。适合离散动作空间(如游戏动作选择)。需要定义状态、动作、奖励函数。
  • GNN :图结构数据。比如社交网络、分子结构、推荐系统。核心是聚合邻居信息。
  • DBN :深度信念网络,早期深度模型,现在多用更简单的 MLP 或 Autoencoder 替代。

选型速查表:

算法 最适合的数据类型 典型任务 新手友好度
CNN 图像、网格数据 分类、检测、分割 ★★★★★
RNN/LSTM 序列数据 文本生成、时序预测 ★★★★☆
Transformer 长序列、文本 翻译、摘要、预训练 ★★★☆☆
GAN 生成任务 图像生成、数据增强 ★★☆☆☆
DQN 离散决策 游戏 AI、控制 ★★☆☆☆
GNN 图结构数据 节点分类、链接预测 ★★★☆☆
DBN 无监督预训练 特征提取 ★☆☆☆☆

2. 环境配置:别在环境上卡半天,先选对 PyTorch 或 TensorFlow

深度学习算法实现高度依赖框架,PyTorch 和 TensorFlow 是两大主流。我更推荐新手用 PyTorch,因为它的动态图更直观,调试容易。但如果你要部署到移动端或需要 TF Serving,那 TensorFlow 更成熟。

2.1 最小化环境配置(PyTorch 为例)

Conda 环境创建(隔离依赖,避免版本冲突):

conda create -n dl-tutorial python=3.8
conda activate dl-tutorial
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
# 如果是 GPU 环境,去官网查对应 CUDA 版本的安装命令

验证安装:

import torch
print(torch.__version__)  # 预期输出 1.x.x
print(torch.cuda.is_available())  # 检查 GPU 是否可用

常见环境问题:

  • CUDA 版本不匹配:PyTorch 官网提供不同 CUDA 版本的安装命令,别直接 pip install torch
  • 虚拟环境没激活:命令前面应该看到 (dl-tutorial) 提示符。
  • 内存不足:GPU 显存不够时,先调小 batch_size 或图像尺寸。

2.2 数据集准备:不要一上来就跑大数据集

先用小数据集验证算法能否跑通,比如 MNIST(手写数字)、CIFAR-10(小图像)、IMDB(情感分析)。这些数据集框架内置,下载快,容易验证。

PyTorch 加载 MNIST 示例:

from torchvision import datasets, transforms

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))  # MNIST 的均值和标准差
])
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('./data', train=False, transform=transform)

train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=1000, shuffle=False)

数据加载器参数:

  • batch_size=64 :一次处理的样本数。GPU 显存小就调小,但别小于 16,否则批次统计不稳定。
  • shuffle=True :训练时打乱数据,防止模型记忆顺序。

3. 训练流程:从单样本调试到全量训练

很多人直接跑完整训练,然后卡在 loss 不下降或准确率 0%。我建议先过一遍单样本调试流程,确认数据流、模型前向传播、损失计算都没问题,再放开全量数据。

3.1 单样本调试流程

model = SimpleCNN()  # 用前面定义的 CNN 模型
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# 取一条数据
sample_data, sample_label = next(iter(train_loader))
sample_data, sample_label = sample_data[0:1], sample_label[0:1]  # 只留一个样本

# 前向传播
output = model(sample_data)
loss = criterion(output, sample_label)

print("输出形状:", output.shape)  # 应该是 (1, 10) for MNIST
print("Loss 值:", loss.item())    # 应该是合理数值,不是 nan 或极大值

单样本验证 checklist:

  • [ ] 输入数据形状符合模型预期(比如 CNN 输入是 [batch, channels, height, width])。
  • [ ] 模型输出形状和类别数一致。
  • [ ] Loss 可计算,不是 nan 或爆炸。
  • [ ] 反向传播能执行: loss.backward() 不报错。
  • [ ] 参数有梯度:检查 model.parameters() 中某个参数的 .grad 不为 None。

3.2 全量训练循环

单样本没问题后,再写训练循环。关键是要加验证集评估,防止过拟合。

def train_model(model, train_loader, test_loader, epochs=10):
    train_losses, test_accuracies = [], []
    for epoch in range(epochs):
        model.train()
        total_loss = 0
        for batch_idx, (data, target) in enumerate(train_loader):
            optimizer.zero_grad()
            output = model(data)
            loss = criterion(output, target)
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
        
        avg_loss = total_loss / len(train_loader)
        train_losses.append(avg_loss)
        
        # 验证集评估
        model.eval()
        correct = 0
        with torch.no_grad():
            for data, target in test_loader:
                output = model(data)
                pred = output.argmax(dim=1)
                correct += pred.eq(target).sum().item()
        
        accuracy = 100. * correct / len(test_loader.dataset)
        test_accuracies.append(accuracy)
        print(f'Epoch {epoch+1}: Loss={avg_loss:.4f}, Accuracy={accuracy:.2f}%')
    
    return train_losses, test_accuracies

训练关键监控点:

  • Loss 下降趋势 :应该逐渐下降,震荡是正常的,但长期不降可能是学习率问题。
  • 训练集 vs 验证集准确率 :如果训练集准确率高但验证集低,是过拟合,需要加 dropout 或数据增强。
  • GPU 显存占用 :用 nvidia-smi 监控,如果接近上限,调小 batch_size。

3.3 超参数调优顺序

不要同时调多个参数。按这个顺序试:

  1. 学习率 :最常见问题源。先从 1e-3 开始,如果 loss 爆炸则调小到 1e-4,如果下降太慢则调到 1e-2。
  2. 批量大小 :影响训练稳定性和速度。一般设 32、64、128,GPU 显存小就用 16。
  3. 模型容量 :如果欠拟合(训练集准确率也低),增加层数或隐藏单元数。
  4. 正则化 :如果过拟合,加 dropout(0.2~0.5)或数据增强。

4. 算法组合与进阶思路:什么时候该混用 CNN、RNN、Transformer?

实际项目很少只用单一算法。比如图像描述生成可能用 CNN 提取图像特征,再用 RNN 或 Transformer 生成文本。关键是要清楚每个模块的作用和接口。

4.1 CNN + RNN 组合示例(图像描述生成)

class CNN2RNN(nn.Module):
    def __init__(self, cnn_feature_dim, rnn_hidden_dim, vocab_size, num_layers=1):
        super().__init__()
        self.cnn = SimpleCNN()  # 需要调整 CNN 输出层,使其输出特征向量
        self.rnn = nn.LSTM(cnn_feature_dim, rnn_hidden_dim, num_layers, batch_first=True)
        self.fc = nn.Linear(rnn_hidden_dim, vocab_size)
    
    def forward(self, image, captions=None, max_length=20):
        # 提取图像特征
        image_features = self.cnn(image)  # 形状: (batch, cnn_feature_dim)
        # 将图像特征作为 RNN 的初始输入或每个时间步的输入
        # 这里简化处理:将图像特征重复 max_length 次作为输入序列
        image_features = image_features.unsqueeze(1).repeat(1, max_length, 1)
        rnn_out, _ = self.rnn(image_features)
        output = self.fc(rnn_out)  # 每个时间步预测一个词
        return output

组合关键点:

  • 特征维度对齐:CNN 输出特征维度必须和 RNN 输入维度一致。
  • 序列处理方式:图像特征可以只作为 RNN 的初始状态,也可以每个时间步都注入。

4.2 Transformer 替代 RNN 的趋势

在大多数序列任务上,Transformer 已经比 RNN 表现更好。但 Transformer 需要更多数据,计算资源要求更高。如果数据量小,RNN 可能更稳妥。

何时选 Transformer:

  • 数据量足够大(数万样本以上)。
  • 序列长(超过 100 个时间步)。
  • 有预训练模型可用(如 BERT、GPT)。

何时仍用 RNN/LSTM:

  • 数据量小,需要更简单的模型防止过拟合。
  • 序列短(10~20 步),RNN 足够且更轻量。
  • 需要在线学习(流式数据),Transformer 的全序列注意力不适合。

4.3 避免算法误用的典型场景

  • 用 CNN 处理文本 :除非把文本当成图像(每个词作为像素),否则不如用 RNN 或 Transformer。
  • 用 RNN 处理图像 :像素级序列太长,RNN 会遗忘早期信息,计算慢。
  • 用 Transformer 处理超长序列 :序列长度超过 1000 时,注意力计算 O(n^2) 会爆内存。
  • 用 GAN 做分类 :GAN 是生成模型,分类还是用 CNN/RNN/Transformer+分类头。

5. 实战排查清单:任务跑不通时按这个顺序查

5.1 数据流问题(最常见)

  1. 输入维度不对 :检查 data.shape 是否匹配模型输入。常见错误:忘记加 batch 维度、通道顺序不对(PyTorch 是 channels-first,TensorFlow 默认 channels-last)。
  2. 数据未归一化 :图像像素值应该是 [0,1] 或 [-1,1],不是 [0,255]。用 transforms.Normalize 处理。
  3. 标签范围错误 :分类标签应该从 0 开始,且小于类别数。比如 10 类问题标签必须是 0~9。

5.2 模型问题

  1. 梯度消失/爆炸 :观察 loss 是否变成 nan 或极大值。解决方案:梯度裁剪 ( torch.nn.utils.clip_grad_norm_ )、更好的权重初始化、调小学习率。
  2. 模型容量不足 :训练集准确率一直很低,增加层数或隐藏单元数。
  3. 过拟合 :训练集准确率高但验证集低,加 dropout、数据增强、早停、权重衰减。

5.3 训练配置问题

  1. 学习率不合适 :loss 震荡太大说明学习率过高,下降太慢说明过低。
  2. 批次大小太大/小 :太小时梯度噪声大,太大时显存不足且可能泛化差。
  3. 未切换 train/eval 模式 :训练时用 model.train() ,评估时用 model.eval() (影响 dropout、BN 等)。

5.4 资源问题

  1. GPU 显存不足 :减小 batch_size、图像尺寸、模型大小,或用梯度累积模拟大 batch。
  2. 内存不足 :数据加载时用 num_workers 多进程,避免大数据一次加载。
  3. 训练太慢 :用混合精度训练 ( torch.cuda.amp )、数据预加载、升级硬件。

最后建议不要追求“一口气学透”,而是每个算法先跑通最小示例,再找 1~2 个真实数据集实战。遇到问题优先查数据流和超参数,大多数情况不是算法本身的问题,而是实现细节没对齐。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐