模块一:数学与编程基础(地基)

没有这些,深度学习就是空中楼阁。

  • 线性代数:标量、向量、矩阵、张量(Tensor,深度学习中最基本的数据结构)、矩阵乘法(前向传播的核心)、特征值分解、奇异值分解(SVD)。

  • 微积分与优化:导数、偏导数、链式法则(反向传播的理论基石)、梯度、雅可比矩阵。

  • 概率与信息论:条件概率、贝叶斯定理、期望方差、最大似然估计(MLE,很多损失函数的源头)、交叉熵、KL散度(衡量分布差异)。

  • 编程工具:Python(绝对主流),以及 NumPy(科学计算)、Pandas(数据处理)、Matplotlib(可视化)。深度学习框架目前主流是 PyTorch(研究首选)和 TensorFlow(工业界常见)。

模块二:神经网络基础(核心组件)

这是深度学习最基本的“积木”。

  • 神经元(感知机):加权求和 + 激活函数(引入非线性)。

  • 激活函数:Sigmoid(早期常用,易梯度消失)、Tanh、ReLU(当前最常用)及其变种(Leaky ReLU, ELU)、Softmax(用于多分类输出)。

  • 网络结构:输入层、隐藏层、输出层。层数越多,网络越“深”。

  • 前向传播:数据从输入到输出的计算过程。

  • 损失函数:衡量预测值与真实值的差距。分类常用交叉熵,回归常用均方误差(MSE)。

  • 反向传播(BP):深度学习的灵魂。利用链式法则,从输出层向输入层逐层计算梯度,用以更新权重。

  • 优化算法:

    • SGD(随机梯度下降) 及其变种:Momentum(动量)、AdaGrad、RMSProp、Adam(目前最常用的自适应优化器)。

    • 学习率衰减策略。

模块三:解决过拟合与训练技巧(工程艺术)

模型不是越大越好,关键要“能训练出来”且“泛化能力强”。

  • 正则化:

    • L1/L2 正则化(权重衰减)。

    • Dropout(训练时随机丢弃神经元,相当于集成学习)。

    • Batch Normalization(批归一化,解决内部协变量偏移,加速收敛)。

  • 数据增强:对图像进行旋转、裁剪、翻转、色彩抖动,以增加数据多样性。

  • 权重初始化:Xavier初始化、He初始化(配合ReLU),防止梯度消失或爆炸。

  • 早停法:监控验证集损失,在过拟合前停止训练。

模块四:经典网络架构(三大流派)

这是深度学习发展史上的里程碑,也是你必须要懂的模型范式。

  • 1. 全连接网络(FCN):最简单的多层感知机,常用于表格数据,但不适合处理高维图像。

  • 2. 卷积神经网络(CNN,处理空间数据):

    • 核心操作:卷积(提取特征)、池化(降维)、填充。

    • 经典模型:LeNet-5(开山之作)、AlexNet(深度学习爆发点)、VGG(证明层数越深越好)、ResNet(残差网络,解决了极深网络退化问题,至今仍广泛使用)、Inception(多尺度卷积)。

  • 3. 循环神经网络(RNN,处理序列数据):

    • 核心概念:共享权重、隐藏状态(记忆)。

    • 变种:LSTM(长短期记忆,解决长距离依赖)、GRU(参数更少的简化版)。

    • 应用:自然语言处理、时间序列预测。

    • 注意:现在序列任务已大量被 Transformer 取代。

模块五:进阶前沿架构(现代主流)

当前深度学习的顶梁柱。

  • Transformer(完全基于注意力机制):

    • 核心:自注意力(Self-Attention)、多头注意力、位置编码、前馈网络。

    • 打破了 RNN 必须串行计算的限制,具备并行性和全局感受野。

    • BERT(双向编码器,擅长理解任务)、GPT(单向解码器,擅长生成任务)。

  • 生成对抗网络(GAN):生成器 + 判别器相互博弈,用于生成逼真的图像、视频。

  • 扩散模型(Diffusion):当前图像生成的新SOTA,如 Stable Diffusion,通过逐步去噪生成图像。

  • 图神经网络(GNN):处理非欧几里得数据(如社交网络、分子结构)。

模块六:应用领域与细分方向

学完理论,要落地到场景。

  • 计算机视觉:图像分类、目标检测(YOLO,Faster R-CNN)、图像分割(U-Net)、人脸识别。

  • 自然语言处理:机器翻译、文本摘要、情感分析、对话系统(ChatGPT)。

  • 多模态:CLIP(文本-图像匹配)、文生图、文生视频(Sora)。

  • 强化学习:智能体与环境交互(如 AlphaGo,自动驾驶决策)。

第一部分:核心理念的文字描述

在跑代码前,先在大脑里建立这个数据流水线的概念:

  1. 数据流:原始图片(像素矩阵) -> 转化为张量(Tensor) -> 归一化(加速收敛) -> 打包成Batch(批次)喂给GPU。

  2. 前向传播:Batch数据进入网络,经过卷积池化提取特征,最后通过全连接层输出一个[Batch_Size, 10]的分数矩阵(Logits)。

  3. 损失计算:将Logits和真实标签传入CrossEntropyLoss(交叉熵损失),算出这次预测差了多少。

  4. 反向传播(核心):调用loss.backward(),PyTorch自动计算所有参数的梯度(链式法则)。

  5. 参数更新:优化器(如Adam)根据梯度调整权重,让损失变小。

  6. 迭代:重复1-5步成千上万次。

第二部分:详细代码实现(PyTorch)

请确保你的环境已安装 torch, torchvision, matplotlib。如果没有GPU,代码会自动切换到CPU。

步骤 1:导入库与设备配置

python

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np

# 【核心】检测是否有GPU,没有就用CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"正在使用设备: {device}")
步骤 2:数据预处理与加载(数据增强)

文字解释:原始图片是0-255的整数,需要转为0-1的浮点数(ToTensor),然后标准化(Normalize)让数据分布均值为0,方差为1,这样梯度下降更平稳。

python

# 定义针对训练集和测试集的不同变换
# 训练集加入随机水平翻转和旋转,用来增加数据多样性(防止过拟合)
transform_train = transforms.Compose([
    transforms.RandomHorizontalFlip(),  # 随机左右翻转
    transforms.RandomCrop(32, padding=4), # 随机裁剪
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) # CIFAR10的均值标准差
])

transform_test = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])

# 下载数据集(第一次运行会自动下载)
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train)
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)

# 创建数据加载器(DataLoader):将数据分成小批次(Batch),打乱顺序
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2)
testloader = torch.utils.data.DataLoader(testset, batch_size=100, shuffle=False, num_workers=2)

# CIFAR-10 的类别名称
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
步骤 3:搭建卷积神经网络(CNN)—— 这里我们实现一个类似经典 VGG 的简化版

文字解释:我们用两层卷积提取边缘纹理,用池化压缩尺寸,最后用三层全连接层做分类。注意在卷积层之间加入BatchNorm(批归一化),它能防止梯度消失,让学习率可以设得更大。

python

class SimpleCNN(nn.Module):
    def __init__(self, num_classes=10):
        super(SimpleCNN, self).__init__()
        # 特征提取部分 (卷积层)
        self.features = nn.Sequential(
            # 输入 3通道RGB,输出 64个特征图,卷积核3x3
            nn.Conv2d(3, 64, kernel_size=3, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 64, kernel_size=3, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 尺寸从 32x32 -> 16x16

            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),
            nn.Conv2d(128, 128, kernel_size=3, padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 16x16 -> 8x8
        )
        
        # 分类器部分 (全连接层)
        self.classifier = nn.Sequential(
            nn.Dropout(0.3),  # 随机丢弃30%的神经元,防止过拟合
            nn.Linear(128 * 8 * 8, 256),
            nn.ReLU(inplace=True),
            nn.Dropout(0.3),
            nn.Linear(256, 128),
            nn.ReLU(inplace=True),
            nn.Linear(128, num_classes)
        )

    def forward(self, x):
        x = self.features(x)
        # 将卷积输出的三维特征图 [B, C, H, W] 展平为一维 [B, C*H*W] 才能输入全连接层
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

# 实例化模型,并放到GPU(如果可用)
net = SimpleCNN().to(device)
print(net) # 打印网络结构
步骤 4:定义损失函数和优化器

python

# 交叉熵损失:非常适合多分类任务(内部自动做了Softmax)
criterion = nn.CrossEntropyLoss()

# Adam优化器:结合了动量与自适应学习率,比纯SGD更鲁棒
# 学习率 lr=0.001 是个经典起始值,weight_decay是L2正则化系数
optimizer = optim.Adam(net.parameters(), lr=0.001, weight_decay=5e-4)

# 【进阶技巧】学习率调度器:每训练10个epoch,学习率乘以0.1,让模型在Loss plateau时继续下降
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)
步骤 5:编写训练函数(核心循环)

文字解释:这就是深度学习的“心脏跳动”过程。net.train() 告诉模型开启训练模式(Dropout生效),optimizer.zero_grad() 必须手动清零梯度(因为PyTorch默认累积梯度),然后 loss.backward() 计算梯度,最后 optimizer.step() 更新参数。

python

def train(epochs=20):
    print("开始训练...")
    for epoch in range(epochs):
        running_loss = 0.0
        correct = 0
        total = 0
        
        # 开启训练模式(对于Dropout和BatchNorm很重要)
        net.train()
        
        for i, data in enumerate(trainloader, 0):
            # 获取输入数据并送到设备
            inputs, labels = data[0].to(device), data[1].to(device)
            
            # 1. 清零梯度(必须做,否则梯度会累加)
            optimizer.zero_grad()
            
            # 2. 前向传播:输入图片,得到预测输出
            outputs = net(inputs)
            
            # 3. 计算损失
            loss = criterion(outputs, labels)
            
            # 4. 反向传播:计算这一批次的梯度(核心中的核心)
            loss.backward()
            
            # 5. 优化器更新参数
            optimizer.step()
            
            # 统计训练准确率(用于观察)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
            running_loss += loss.item()
            
            # 每100个批次打印一次当前状态
            if i % 100 == 99:    
                print(f'Epoch {epoch+1}, Batch {i+1}, Loss: {running_loss / 100:.3f}, Acc: {100 * correct / total:.2f}%')
                running_loss = 0.0
                correct = 0
                total = 0
                
        # 每个epoch结束后,调整学习率
        scheduler.step()
        
        # 每个epoch结束后跑一次测试集看看效果
        test_accuracy = test()
        print(f'Epoch {epoch+1} 结束,测试集准确率: {test_accuracy:.2f}%')
        
    print("训练完成!")
步骤 6:编写测试函数(评估模型)

文字解释:net.eval() 告诉模型进入评估模式(Dropout关闭,BatchNorm使用全局统计量)。注意 with torch.no_grad(): 这个上下文管理器,它强制不计算梯度,能大幅节省内存和计算时间,因为推理时我们不需要反向传播。

python

def test():
    net.eval()  # 切换到评估模式
    correct = 0
    total = 0
    with torch.no_grad():  # 禁用梯度计算
        for data in testloader:
            images, labels = data[0].to(device), data[1].to(device)
            outputs = net(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    
    accuracy = 100 * correct / total
    return accuracy

# 先跑一次测试看看随机初始化时的准确率(大概10%左右)
print(f"初始测试准确率: {test():.2f}%")

第三部分:启动训练与结果可视化

python

# 运行训练(笔记本上大概跑10-15分钟,准确率能达到75%左右)
train(epochs=20)

# 训练完毕后,保存模型权重(以后可以直接加载,不用重训)
PATH = './cifar10_cnn.pth'
torch.save(net.state_dict(), PATH)
print(f"模型已保存至 {PATH}")

# 最终测试
final_acc = test()
print(f"最终测试集准确率: {final_acc:.2f}%")

第四部分:代码中的“陷阱”与深度思考

我刚给出的代码虽然能跑通,但如果你只照抄,就不算真正理解。这里有几个核心痛点:

  1. 为什么要做 optimizer.zero_grad()?

    • 因为PyTorch的反向传播是梯度累加机制。如果你不清零,这一次的梯度会加到上一次之上。这在某些情况下(比如显存不够,想加大Batch Size)有用,但常规训练必须清零。

  2. net.train() 和 net.eval() 到底做了什么?

    • 在训练时,Dropout 会随机屏蔽神经元,BatchNorm 会使用当前Batch的均值和方差。

    • 在测试时,Dropout 必须关闭(否则预测不稳定),BatchNorm 必须使用训练时记录下来的全局滑动平均值。忘记切换 eval 模式,是导致训练集准确率高但测试集一塌糊涂的头号元凶。

  3. 为什么 CrossEntropyLoss 不需要显式加 Softmax?

    • 因为 nn.CrossEntropyLoss 内部集成了 LogSoftmax 和 NLLLoss。如果你在网络最后一层加了Softmax,再用这个Loss,相当于算了两遍Softmax,会导致概率分布被扭曲,虽然训练可能勉强进行,但收敛极慢且结果错误。

  4. 为什么 CIFAR-10 不用全连接层(MLP)而用 CNN?

    • 一张 32x32 的彩色图有 3072 个像素,如果用全连接层,第一层参数就是 3072 * 3072 ≈ 940万,极易过拟合且计算极慢。CNN 利用权值共享和局部感受野,把参数量降到了几千个,这是深度学习成功的基石。

更多推荐