从 CNN 视觉感知到 Transformer 全局认知
Published in:2025-12-11 |
Words: 1.6k | Reading time: 6min | reading:

【硬核手撸】PyTorch 建模实战:从 CNN 视觉感知到 Transformer 全局认知

摘要:在 AI 工程师的日常工作中,"调包"只是基础,理解并能手写模型架构才是进阶。本文将基于 PyTorch 框架,深入拆解深度学习领域的两大基石:CNN (卷积神经网络)Transformer。我们将结合变电站巡检的具体业务场景,手把手演示如何搭建这两种架构,并解析其背后的设计哲学。


一、 PyTorch:AI 工程师的“乐高积木”

在 JD 中,公司明确要求 “熟悉 PyTorch 等主流深度学习框架”
PyTorch 之所以流行,是因为它采用了面向对象 (OOP) 的设计思想。每一个神经网络层(Layer)都是一个 nn.Module,我们可以像搭乐高积木一样,将不同的层组合起来。

在变电站场景中:

  • CNN 是机器的**“眼睛”**:用于提取图像特征(锈蚀、读数)。
  • Transformer 是机器的**“大脑”**:用于处理序列数据(日志分析)或融合多模态信息。

二、 构建 CNN:机器视觉的基石

1. 原理图解:它是怎么“看”东西的?

CNN (Convolutional Neural Network) 的核心在于**“局部感知”“权值共享”**。

1
2
3
4
5
6
7
graph LR
Input[输入图片: 仪表盘] --> Conv1[卷积层1: 提取边缘]
Conv1 --> Pool1[池化层1: 缩小尺寸]
Pool1 --> Conv2[卷积层2: 提取形状]
Conv2 --> Pool2[池化层2: 提取特征]
Pool2 --> FC[全连接层: 分类/回归]
FC --> Output[输出: 读数/状态]

2. PyTorch 代码实现 (工业级写法)

我们不再写玩具代码,而是模拟一个VGG风格的特征提取器,用于变电站设备的锈蚀检测。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import torch
import torch.nn as nn
import torch.nn.functional as F

class IndustrialCNN(nn.Module):
def __init__(self, num_classes=2):
super(IndustrialCNN, self).__init__()

# === 特征提取层 (Feature Extractor) ===
# Block 1: 基础边缘特征
self.layer1 = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=1),
nn.BatchNorm2d(32), # 归一化:防止梯度消失,加速收敛
nn.ReLU(), # 激活函数:引入非线性
nn.MaxPool2d(kernel_size=2, stride=2) # 降采样:减少计算量
)

# Block 2: 高级纹理特征
self.layer2 = nn.Sequential(
nn.Conv2d(32, 64, 3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(2, 2)
)

# Block 3: 抽象语义特征
self.layer3 = nn.Sequential(
nn.Conv2d(64, 128, 3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(),
nn.MaxPool2d(2, 2)
)

# === 分类头 (Classifier Head) ===
# 假设输入图片是 224x224, 经过3次池化(除以8) -> 28x28
self.flatten = nn.Flatten()
self.fc = nn.Sequential(
nn.Linear(128 * 28 * 28, 512),
nn.ReLU(),
nn.Dropout(0.5), # 防止过拟合
nn.Linear(512, num_classes)
)

def forward(self, x):
# 数据流向:Input -> Layer1 -> Layer2 -> Layer3 -> Flatten -> FC -> Output
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.flatten(x)
logits = self.fc(x)
return logits

# 测试模型结构
if __name__ == "__main__":
model = IndustrialCNN()
# 模拟一张 batch=1, rgb=3, 224x224 的图片
dummy_input = torch.randn(1, 3, 224, 224)
output = model(dummy_input)
print(f"CNN Output Shape: {output.shape}") # 预期: [1, 2]

3. 面试加分点解析

  • BatchNorm 的使用:面试官如果问“训练不收敛怎么办”,你可以结合代码说:“我会在卷积层后加入 BatchNorm,它能让数据分布更稳定。”
  • Dropout 的使用:如果问“过拟合怎么办”,你可以指着代码说:“我在全连接层加了 Dropout,随机丢弃一部分神经元,增加鲁棒性。”

三、 构建 Transformer:全局注意力的魔法

1. 原理图解:Self-Attention 是什么?

不同于 CNN 只能看局部,Transformer 通过 Self-Attention (自注意力机制) 可以一次性看到全局。
比如在处理巡检日志:“设备A温度正常,但设备B震动异常”。Transformer 能捕捉到“设备B”和“震动异常”之间的长距离关联。

1
2
3
4
5
6
7
graph TD
Input[输入序列] --> Embed[Embedding + 位置编码]
Embed --> Attn[Multi-Head Self-Attention]
Attn --> Norm1[Add & Norm]
Norm1 --> FFN[Feed Forward Network]
FFN --> Norm2[Add & Norm]
Norm2 --> Output[输出特征]

2. PyTorch 代码实现 (手写 Encoder Block)

虽然 PyTorch 提供了 nn.Transformer,但为了展示深度,我们手写一个 Transformer Encoder Block。这在处理时间序列预测(如预测电压波动)时非常有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class TransformerBlock(nn.Module):
def __init__(self, embed_dim, num_heads, ff_dim, dropout=0.1):
super(TransformerBlock, self).__init__()

# 1. 多头自注意力机制 (核心)
# batch_first=True 让输入变成 [Batch, Seq_Len, Feature]
self.attention = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True)

# 2. 前馈神经网络 (FFN)
self.ffn = nn.Sequential(
nn.Linear(embed_dim, ff_dim),
nn.ReLU(),
nn.Linear(ff_dim, embed_dim)
)

# 3. 层归一化 (LayerNorm) 与 残差连接
self.layernorm1 = nn.LayerNorm(embed_dim)
self.layernorm2 = nn.LayerNorm(embed_dim)
self.dropout = nn.Dropout(dropout)

def forward(self, x):
# --- 子层 1: Attention ---
# 这里的 key, value, query 都是 x (Self-Attention)
attn_output, _ = self.attention(x, x, x)

# 残差连接 (Add) + 归一化 (Norm)
# x + dropout(attn_output)
out1 = self.layernorm1(x + self.dropout(attn_output))

# --- 子层 2: FFN ---
ffn_output = self.ffn(out1)

# 残差连接 + 归一化
out2 = self.layernorm2(out1 + self.dropout(ffn_output))

return out2

# 模拟:变电站电力时序数据分类
class PowerSeriesTransformer(nn.Module):
def __init__(self):
super().__init__()
self.embedding = nn.Linear(10, 64) # 假设有10个传感器指标,映射到64维
self.encoder = TransformerBlock(embed_dim=64, num_heads=4, ff_dim=128)
self.fc = nn.Linear(64, 2) # 二分类:正常/故障

def forward(self, x):
x = self.embedding(x)
x = self.encoder(x)
# 取序列最后一个时间点的特征做分类 (Global Pooling 也可以)
x = x[:, -1, :]
return self.fc(x)

if __name__ == "__main__":
# 模拟数据: Batch=32, 时间步长=50, 传感器特征=10
dummy_series = torch.randn(32, 50, 10)
model = PowerSeriesTransformer()
output = model(dummy_series)
print(f"Transformer Output Shape: {output.shape}") # 预期: [32, 2]

3. 面试加分点解析

  • 残差连接 (Residual Connection):代码中的 x + attn_output。一定要解释:这解决了深层网络梯度消失的问题,让网络可以堆叠得很深。
  • LayerNorm vs BatchNorm:CNN 用 BatchNorm(针对 Batch 做归一化),Transformer 用 LayerNorm(针对单样本内部做归一化)。这是因为序列长度可能不一致,LayerNorm 更稳定。

四、 总结:何时用 CNN?何时用 Transformer?

场景 推荐架构 理由
仪表读数识别 CNN (ResNet/YOLO) 视觉特征具有局部性(指针、刻度),CNN 的平移不变性非常适合处理图像。
设备锈蚀检测 CNN 锈蚀是纹理特征,CNN 擅长提取纹理。
电力日志分析 Transformer 日志是文本序列,上下文关联强。
多模态融合 Transformer 如果要同时结合“监控画面”和“电流传感器数据”来判断故障,Transformer 强大的 Attention 机制能融合不同模态的信息。

掌握了这两套代码模板,你不仅能应对面试中的“手撕网络结构”,更能在实际工作中快速搭建起解决问题的 Baseline 模型。

Prev:
Python装饰器
Next:
从 PyTorch 训练到 C++ 边缘部署全流程解析