深度学习详解

从神经元到深度神经网络,全面讲解深度学习原理
大量图解和代码,通俗易懂
适合想深入理解深度学习的工程师


目录


第一章 神经网络基础

1.1 从生物神经元到人工神经元

1.1.1 生物神经元是如何工作的

大脑的神奇之处

人脑有约860亿个神经元
每个神经元可以与数千个其他神经元连接
正是这些连接,让我们能够思考、学习、记忆

生物神经元的结构

        树突(接收信号)
          ↓ ↓ ↓
    ┌───────────────┐
    │   细胞体      │ ← 整合信号
    │   (处理)    │
    └───────┬───────┘
            │
        轴突(发送信号)
            │
            ↓
        突触(连接其他神经元)

工作流程

1. 树突接收来自其他神经元的信号
2. 细胞体整合所有信号
3. 如果信号超过阈值神经元"激活"
4. 通过轴突发送信号给其他神经元
5. 突触强度决定了信号的强弱
6. 学习过程就是调整突触强度

1.1.2 人工神经元模型

把生物神经元抽象成数学模型

输入 x₁ ────→ w₁ ──┐
                    │
输入 x₂ ────→ w₂ ──┼──→ 求和 ──→ 激活函数 ──→ 输出 y
                    │
输入 x₃ ────→ w₃ ──┘
                    ↑
                偏置 b

数学表达式

y = f(w₁×x₁ + w₂×x₂ + w₃×x₃ + b)

简化写法:
y = f(Σwᵢxᵢ + b)
或
y = f(W·X + b)

各部分含义

符号 含义 类比
xᵢ 输入信号 树突接收的信号
wᵢ 权重 突触强度
b 偏置 激活阈值
f 激活函数 决定是否激活
y 输出 轴突发送的信号

1.1.3 一个具体的例子

场景:决定要不要出去玩

输入:
x₁ = 天气好不好(0或1)
x₂ = 有没有朋友约(0或1)
x₃ = 有没有作业(0或1)

权重:
w₁ = 0.5(天气重要)
w₂ = 0.8(朋友约很重要)
w₃ = -0.6(有作业就不想出去)

偏置:
b = 0.1(比较喜欢宅)

激活函数:
如果得分 > 0,就出去玩
否则不出去

计算过程

假设:天气好(1),有朋友约(1),没作业(0)

得分 = w₁×x₁ + w₂×x₂ + w₃×x₃ + b
     = 0.5×1 + 0.8×1 + (-0.6)×0 + 0.1
     = 0.5 + 0.8 + 0 + 0.1
     = 1.4

因为 1.4 > 0,所以出去玩!

1.1.4 代码实现

import torch
import torch.nn as nn

# 单个神经元
neuron = nn.Linear(3, 1)  # 3个输入,1个输出

# 输入
x = torch.tensor([[1.0, 1.0, 0.0]])  # 天气好,有朋友约,没作业

# 前向传播
output = neuron(x)
print(f"输出: {output}")

# 加上激活函数
import torch.nn.functional as F
activated = F.relu(output)  # 如果output>0,输出output;否则输出0
print(f"激活后: {activated}")

1.2 感知机:最简单的神经网络

1.2.1 什么是感知机

感知机:最简单的神经网络,只有一个神经元。

1958年由Frank Rosenblatt发明
是深度学习的鼻祖

感知机的局限

只能解决线性可分的问题

什么是线性可分?
- 可以用一条直线分开两类点

经典例子:AND、OR问题可以解决,XOR问题不能解决

XOR问题:
(0,0) → 0    (0,1) → 1
(1,0) → 1    (1,1) → 0

画出来:
    1(×)    0(○)

    0(○)    1(×)

无法用一条直线分开!

1.2.2 感知机代码实现

import numpy as np

class Perceptron:
    def __init__(self, input_size, lr=0.1, epochs=100):
        self.weights = np.zeros(input_size)
        self.bias = 0
        self.lr = lr
        self.epochs = epochs

    def activation(self, x):
        return 1 if x >= 0 else 0

    def predict(self, x):
        z = np.dot(x, self.weights) + self.bias
        return self.activation(z)

    def train(self, X, y):
        for epoch in range(self.epochs):
            for i in range(len(X)):
                pred = self.predict(X[i])
                error = y[i] - pred

                # 更新权重和偏置
                self.weights += self.lr * error * X[i]
                self.bias += self.lr * error

# 测试AND问题
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([0, 0, 0, 1])  # AND

perceptron = Perceptron(input_size=2)
perceptron.train(X, y)

print("AND问题测试:")
for x in X:
    print(f"{x}{perceptron.predict(x)}")

1.3 多层神经网络

1.3.1 为什么需要多层

问题:单层感知机无法解决XOR问题。

解决:用多层网络!

单层网络:          多层网络:
输入层              输入层
  │                   │
输出层              隐藏层
                      │
                    输出层

多层网络如何解决XOR

第一层:把问题变换到另一个空间
第二层:在新空间中线性分开

原始空间:          变换后的空间:
    1    0              1    1

    0    1              0    0

无法分开            可以分开!

1.3.2 多层感知机(MLP)

输入层        隐藏层        输出层
  x₁           h₁           y₁
  x₂    →      h₂    →      y₂
  x₃           h₃           y₃

数学表达

隐藏层:
H = f(W₁·X + b₁)

输出层:
Y = g(W₂·H + b₂)

其中:
- f, g 是激活函数
- W₁, W₂ 是权重矩阵
- b₁, b₂ 是偏置向量

1.3.3 代码实现

import torch
import torch.nn as nn

class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super().__init__()
        self.hidden = nn.Linear(input_size, hidden_size)
        self.output = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        x = torch.relu(self.hidden(x))
        x = self.output(x)
        return x

# 创建网络
model = MLP(input_size=3, hidden_size=4, output_size=2)

# 输入
x = torch.randn(1, 3)

# 前向传播
output = model(x)
print(f"输出: {output}")

1.3.4 深度神经网络

什么是"深度"?

浅层网络:1-2个隐藏层
深层网络:多个隐藏层(几十甚至上百层)

为什么深度网络厉害?
- 每一层学习不同层次的特征
- 浅层:简单特征(边缘、纹理)
- 深层:复杂特征(形状、物体)

1.4 激活函数详解

1.4.1 为什么需要激活函数

关键问题:如果没有激活函数会怎样?

答案:无论网络多深,都等价于一个线性变换!

证明

假设没有激活函数:

第一层:y₁ = W₁x + b₁
第二层:y₂ = W₂y₁ + b₂ = W₂(W₁x + b₁) + b₂

合并:
y₂ = W₂W₁x + W₂b₁ + b₂ = W'x + b'

还是线性变换!

结论:激活函数引入非线性,让网络能学习复杂的模式。

1.4.2 常用激活函数

1. Sigmoid

公式:σ(x) = 1 / (1 + e⁻ˣ)

特点:
- 输出范围:(0, 1)
- S形曲线
- 可以解释为概率

问题:
- 梯度消失:当|x|很大时,梯度接近0
- 输出非零中心

适用场景:
- 二分类输出层
- 需要输出概率
import torch
import matplotlib.pyplot as plt

x = torch.linspace(-10, 10, 100)
y = torch.sigmoid(x)

plt.plot(x, y)
plt.title('Sigmoid')
plt.grid(True)
plt.show()

2. Tanh

公式:tanh(x) = (eˣ - e⁻ˣ) / (eˣ + e⁻ˣ)

特点:
- 输出范围:(-1, 1)
- 零中心化
- S形曲线

问题:
- 仍然存在梯度消失

适用场景:
- RNN隐藏层

3. ReLU(最常用)

公式:ReLU(x) = max(0, x)

特点:
- 计算极其简单
- 正区间梯度恒为1,缓解梯度消失
- 稀疏激活(负区间输出0)

问题:
- Dead ReLU:某些神经元可能永远不会激活

适用场景:
- 大多数隐藏层
x = torch.linspace(-10, 10, 100)
y = torch.relu(x)

plt.plot(x, y)
plt.title('ReLU')
plt.grid(True)
plt.show()

4. Leaky ReLU

公式:LeakyReLU(x) = max(αx, x)

其中α是很小的正数,如0.01

特点:
- 解决Dead ReLU问题
- 负区间也有梯度

5. GELU

公式:GELU(x) = x × Φ(x)

其中Φ(x)是标准正态分布的累积分布函数

特点:
- 平滑的ReLU替代品
- 在Transformer中表现优异

适用场景:
- Transformer隐藏层
- BERT、GPT等模型

1.4.3 如何选择激活函数

位置 推荐激活函数 原因
隐藏层 ReLU 简单高效
隐藏层(有Dead ReLU问题) Leaky ReLU 解决神经元死亡
Transformer隐藏层 GELU 效果更好
二分类输出层 Sigmoid 输出概率
多分类输出层 Softmax 输出概率分布
回归输出层 直接输出数值

第二章 神经网络如何学习

2.1 前向传播

2.1.1 什么是前向传播

定义:数据从输入层开始,一层一层地通过网络,最终到达输出层的过程。

类比:水流过一系列的过滤器,每经过一层,数据就被处理一次。

2.1.2 前向传播的计算过程

假设有一个3层网络:

输入层:X
隐藏层1:H₁ = f(W₁·X + b₁)
隐藏层2:H₂ = f(W₂·H₁ + b₂)
输出层:Y = g(W₃·H₂ + b₃)

其中f、g是激活函数

2.1.3 代码实现

import torch
import torch.nn as nn

class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer1 = nn.Linear(784, 256)
        self.layer2 = nn.Linear(256, 128)
        self.layer3 = nn.Linear(128, 10)

    def forward(self, x):
        # 前向传播
        x = torch.relu(self.layer1(x))
        x = torch.relu(self.layer2(x))
        x = self.layer3(x)
        return x

# 创建网络
model = SimpleNet()

# 输入(假设是MNIST图像,28×28=784像素)
x = torch.randn(1, 784)

# 前向传播
output = model(x)
print(f"输出形状: {output.shape}")  # [1, 10]

2.2 损失函数

2.2.1 什么是损失函数

定义:衡量模型预测与真实值之间差距的函数。

目标:让损失函数尽可能小。

2.2.2 常用损失函数

1. 均方误差(MSE)- 用于回归

公式:MSE = (1/n) × Σ(yᵢ - ŷᵢ)²

特点:
- 对大误差惩罚更重
- 梯度计算简单

适用场景:
- 回归问题
import torch.nn as nn

criterion = nn.MSELoss()

y_true = torch.tensor([1.0, 2.0, 3.0])
y_pred = torch.tensor([1.1, 2.2, 2.8])

loss = criterion(y_pred, y_true)
print(f"MSE损失: {loss.item():.4f}")

2. 交叉熵损失 - 用于分类

公式:CE = -Σyᵢlog(ŷᵢ)

特点:
- 预测越准确,损失越小
- 与softmax配合使用

适用场景:
- 分类问题
# 多分类交叉熵
criterion = nn.CrossEntropyLoss()

y_true = torch.tensor([1])  # 真实类别
y_pred = torch.tensor([[0.1, 0.8, 0.1]])  # 预测概率

loss = criterion(y_pred, y_true)
print(f"交叉熵损失: {loss.item():.4f}")

3. 二分类交叉熵

# 二分类交叉熵
criterion = nn.BCEWithLogitsLoss()

y_true = torch.tensor([1.0, 0.0, 1.0])
y_pred = torch.tensor([0.9, 0.1, 0.8])

loss = criterion(y_pred, y_true)
print(f"二分类交叉熵损失: {loss.item():.4f}")

2.3 反向传播算法

2.3.1 什么是反向传播

定义:一种高效计算梯度的算法,从输出层向输入层逐层计算每个参数的梯度。

核心思想:利用链式法则。

2.3.2 链式法则

如果 y = f(g(x)),那么:
dy/dx = dy/dg × dg/dx

例子:y = (x²)³
dy/dx = 3(x²)² × 2x = 6x⁵

2.3.3 反向传播的计算过程

前向传播:
输入 x → 层1 → 层2 → 层3 → 输出 y → 损失 L

反向传播:
损失 L → ∂L/∂y → ∂L/∂层3 → ∂L/∂层2 → ∂L/∂层1

计算步骤

1. 计算损失对输出的梯度:∂L/y
2. 计算输出对层3输出的梯度:∂y/z₃
3. 计算损失对层3参数的梯度:∂L/W₃ = L/y × y/z₃ × z₃/W₃
4. 继续向后传播...

2.3.4 PyTorch自动计算梯度

import torch

# 定义参数(需要计算梯度)
w = torch.tensor([2.0], requires_grad=True)
b = torch.tensor([1.0], requires_grad=True)

# 前向传播
x = torch.tensor([3.0])
y = w * x + b
loss = (y - 10) ** 2

print(f"预测值: {y.item()}")
print(f"损失: {loss.item()}")

# 反向传播(自动计算梯度)
loss.backward()

print(f"\n梯度:")
print(f"∂L/∂w = {w.grad.item()}")
print(f"∂L/∂b = {b.grad.item()}")

2.4 梯度下降优化器

2.4.1 梯度下降的基本原理

公式

θ = θ - η × ∇L(θ)

其中:
- θ:参数
- η:学习率
- ∇L:梯度

直观理解

想象你在山上,想下山到最低点:
1. 看看周围哪个方向最陡(梯度)
2. 往那个方向走一步(学习率)
3. 重复1-2,直到到达山谷

2.4.2 常用优化器

1. SGD(随机梯度下降)

optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

2. SGD with Momentum

optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

3. Adam(最常用)

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

4. AdamW

optimizer = torch.optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)

2.4.3 完整训练代码

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

# 1. 创建数据
X = torch.randn(1000, 784)
y = torch.randint(0, 10, (1000,))
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

# 2. 创建模型
model = SimpleNet()

# 3. 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 4. 训练循环
for epoch in range(10):
    total_loss = 0
    for batch_x, batch_y in dataloader:
        # 前向传播
        outputs = model(batch_x)
        loss = criterion(outputs, batch_y)

        # 反向传播
        optimizer.zero_grad()  # 清零梯度
        loss.backward()        # 计算梯度
        optimizer.step()       # 更新参数

        total_loss += loss.item()

    print(f"Epoch {epoch+1}, Loss: {total_loss/len(dataloader):.4f}")

第三章 卷积神经网络CNN

3.1 为什么需要CNN

3.1.1 全连接网络处理图像的问题

一张224×224的彩色图像:
- 像素数:224 × 224 × 3 = 150,528
- 如果第一层有1000个神经元
- 参数量:150,528 × 1000 = 1.5亿!

问题:
1. 参数太多,容易过拟合
2. 没有利用图像的空间结构
3. 计算量太大

3.1.2 CNN的优势

1. 局部连接每个神经元只看一小块区域
2. 参数共享同一个卷积核扫描整张图
3. 平移不变性无论物体在哪都能检测到

3.2 卷积操作详解

3.2.1 什么是卷积

通俗理解:用一个小的"窗口"(卷积核)在图像上滑动,提取特征。

图像(5×5):        卷积核(3×3):
1  2  3  4  5       1  0  -1
6  7  8  9  10      1  0  -1
11 12 13 14 15      1  0  -1
16 17 18 19 20
21 22 23 24 25

卷积过程:
卷积核放在图像左上角,对应位置相乘再求和:
1×1 + 2×0 + 3×(-1) + 6×1 + 7×0 + 8×(-1) + 11×1 + 12×0 + 13×(-1)
= 1 - 3 + 6 - 8 + 11 - 13 = -6

然后滑动卷积核,继续计算...

3.2.2 卷积的关键参数

1. 卷积核大小(Kernel Size)

常用:3×3, 5×5, 7×7

越大感受野越大,但参数越多

2. 步长(Stride)

步长=1:每次移动1格
步长=2:每次移动2格(输出尺寸减半)

3. 填充(Padding)

在边缘填充0
保持输出尺寸不变

4. 输出尺寸计算

输出尺寸 = (输入尺寸 + 2×填充 - 卷积核大小) / 步长 + 1

例子:
输入:224×224,卷积核:3×3,步长:1,填充:1
输出 = (224 + 2×1 - 3) / 1 + 1 = 224

3.2.3 代码实现

import torch
import torch.nn as nn

# 卷积层
conv = nn.Conv2d(
    in_channels=3,      # 输入通道数(RGB=3)
    out_channels=64,    # 输出通道数(卷积核数量)
    kernel_size=3,      # 卷积核大小
    stride=1,           # 步长
    padding=1           # 填充
)

# 输入:batch × channels × height × width
x = torch.randn(32, 3, 224, 224)  # 32张RGB图像

# 卷积
output = conv(x)
print(f"输入形状: {x.shape}")
print(f"输出形状: {output.shape}")  # [32, 64, 224, 224]

3.3 池化操作

3.3.1 什么是池化

作用:降低特征图尺寸,减少计算量,增加感受野。

最大池化

取每个窗口内的最大值

输入(4×4):         输出(2×2):
1  2  3  4           2  4
5  6  7  8    →      6  8
9  10 11 12
13 14 15 16

2×2窗口,步长2:
[1,2,5,6] → max=6
[3,4,7,8] → max=8
...

3.3.2 代码实现

# 最大池化
maxpool = nn.MaxPool2d(kernel_size=2, stride=2)

x = torch.randn(1, 64, 224, 224)
output = maxpool(x)
print(f"池化后形状: {output.shape}")  # [1, 64, 112, 112]

# 平均池化
avgpool = nn.AvgPool2d(kernel_size=2, stride=2)

# 全局平均池化
gap = nn.AdaptiveAvgPool2d(1)
output = gap(x)
print(f"全局池化后形状: {output.shape}")  # [1, 64, 1, 1]

3.4 经典CNN架构

3.4.1 LeNet(1998)

输入 → 卷积 → 池化 → 卷积 → 池化 → 全连接 → 输出
32×32  28×28  14×14  10×10   5×5    120      10

意义:开创了CNN时代

3.4.2 AlexNet(2012)

创新:
- ReLU激活函数
- Dropout正则化
- 数据增强
- GPU训练

意义:开启了深度学习时代

3.4.3 VGG(2014)

核心思想:用小卷积核堆叠

两个3×3卷积 = 一个5×5卷积的感受野
但参数更少,非线性更多

3.4.4 ResNet(2015)

核心创新:残差连接

普通网络:y = f(x)
残差网络:y = f(x) + x

为什么有效?
- 梯度可以直接通过恒等映射传播
- 解决了深层网络的退化问题
import torchvision.models as models

# 使用预训练的ResNet
model = models.resnet50(pretrained=True)

# 修改最后一层
num_classes = 10
model.fc = nn.Linear(2048, num_classes)

第四章 循环神经网络RNN

4.1 为什么需要RNN

4.1.1 序列数据的特点

序列数据:文本、语音、时间序列...

特点:
- 长度不固定
- 前后有关联
- 顺序很重要

4.1.2 全连接网络处理序列的问题

问题:
1. 无法处理变长序列
2. 无法利用上下文信息
3. 参数无法共享

4.2 RNN基本原理

4.2.1 RNN的核心思想

用隐藏状态保存历史信息

h_t = f(W_hh × h_{t-1} + W_xh × x_t + b)

其中:
- h_t:当前时刻的隐藏状态
- h_{t-1}:上一时刻的隐藏状态
- x_t:当前时刻的输入

4.2.2 RNN的结构

时间步t的展开:

x₁    x₂    x₃    x₄
│     │     │     │
↓     ↓     ↓     ↓
┌───┐ ┌───┐ ┌───┐ ┌───┐
│RNN│→│RNN│→│RNN│→│RNN│
└───┘ └───┘ └───┘ └───┘
│     │     │     │
y₁    y₂    y₃    y₄

→ 表示隐藏状态的传递

4.2.3 代码实现

import torch
import torch.nn as nn

# PyTorch中的RNN
rnn = nn.RNN(
    input_size=10,    # 输入维度
    hidden_size=20,   # 隐藏状态维度
    num_layers=2,     # 层数
    batch_first=True  # 输入格式:(batch, seq, feature)
)

# 输入
x = torch.randn(32, 5, 10)  # batch=32, seq_len=5, input_size=10
h0 = torch.zeros(2, 32, 20)  # 初始隐藏状态

# 前向传播
output, hn = rnn(x, h0)
print(f"输出形状: {output.shape}")  # [32, 5, 20]
print(f"最终隐藏状态形状: {hn.shape}")  # [2, 32, 20]

4.3 LSTM详解

4.3.1 RNN的问题

梯度消失:在长序列中,梯度会逐渐衰减,导致无法学习长期依赖。

4.3.2 LSTM的解决方案

用门控机制控制信息的流动

LSTM有三个门:
1. 遗忘门:决定丢弃哪些旧信息
2. 输入门:决定写入哪些新信息
3. 输出门:决定输出哪些信息

还有一个"细胞状态":长期记忆的传送带

4.3.3 代码实现

# LSTM
lstm = nn.LSTM(
    input_size=10,
    hidden_size=20,
    num_layers=2,
    batch_first=True
)

x = torch.randn(32, 5, 10)
h0 = torch.zeros(2, 32, 20)
c0 = torch.zeros(2, 32, 20)

output, (hn, cn) = lstm(x, (h0, c0))
print(f"输出形状: {output.shape}")

4.4 GRU详解

GRU = 简化版的LSTM

gru = nn.GRU(
    input_size=10,
    hidden_size=20,
    num_layers=2,
    batch_first=True
)

output, hn = gru(x, h0)

第五章 Transformer架构

5.1 注意力机制

5.1.1 核心思想

让模型知道"关注哪里"

例子:翻译句子

"I love you" → "我爱你"

翻译"爱"时,应该关注"love"
翻译"我"时,应该关注"I"

注意力机制让模型学会这种关注

5.2 自注意力

5.2.1 计算过程

1. 把输入转换为QueryKeyValue
   Q = W_q × X  查询我要找什么
   K = W_k × X  我是什么
   V = W_v × X  我的内容

2. 计算注意力分数
   Score = Q × K^T

3. 缩放和Softmax
   Attention = Softmax(Score / d_k)

4. 加权求和
   Output = Attention × V

5.2.2 代码实现

import torch
import torch.nn as nn
import math

class SelfAttention(nn.Module):
    def __init__(self, embed_dim):
        super().__init__()
        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.scale = math.sqrt(embed_dim)

    def forward(self, x):
        Q = self.query(x)
        K = self.key(x)
        V = self.value(x)

        scores = torch.matmul(Q, K.transpose(-2, -1)) / self.scale
        attention = torch.softmax(scores, dim=-1)
        output = torch.matmul(attention, V)

        return output

5.3 Transformer完整结构

编码器层:
输入 → 多头注意力 → 前馈网络 → 输出

解码器层:
输入 → 掩码多头注意力 → 编码器-解码器注意力 → 前馈网络 → 输出
from transformers import BertModel, BertTokenizer

# 使用预训练的BERT
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')

inputs = tokenizer("Hello world", return_tensors='pt')
outputs = model(**inputs)

第六章 训练技巧

6.1 正则化技术

6.1.1 Dropout

原理:训练时随机"关掉"一些神经元

dropout = nn.Dropout(0.5)  # 丢弃50%

x = torch.randn(1, 100)
y = dropout(x)

6.1.2 批归一化

原理:把每层的输入归一化

bn = nn.BatchNorm1d(100)

x = torch.randn(32, 100)
y = bn(x)

6.2 优化技巧

6.2.1 学习率调度

from torch.optim.lr_scheduler import StepLR, CosineAnnealingLR

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# 阶梯衰减
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)

# 余弦退火
scheduler = CosineAnnealingLR(optimizer, T_max=100)

for epoch in range(100):
    train(...)
    scheduler.step()

6.2.2 梯度裁剪

# 防止梯度爆炸
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

6.3 迁移学习

import torchvision.models as models

# 加载预训练模型
model = models.resnet50(pretrained=True)

# 冻结参数
for param in model.parameters():
    param.requires_grad = False

# 修改最后一层
model.fc = nn.Linear(2048, num_classes)

# 只训练最后一层
optimizer = torch.optim.Adam(model.fc.parameters(), lr=0.001)

总结

深度学习的核心概念

1. 神经网络由神经元组成的网络
2. 激活函数引入非线性
3. 损失函数衡量预测好坏
4. 反向传播计算梯度
5. 优化器更新参数

深度学习的三大架构

架构 适用场景 代表模型
CNN 图像处理 ResNet, VGG
RNN 序列数据 LSTM, GRU
Transformer NLP, 多模态 BERT, GPT

学习建议

1. 理解基本原理
2. 多写代码实践
3. 阅读经典论文
4. 尝试实际项目

恭喜你完成了深度学习的学习!