图像特征提取:从“看见”到“用好”
目录
- 0. 摘要与你将收获什么
- 1. 特征到底是什么:三重视角
- 2. 特征如何表示:张量的语义与布局
- 3. 从经典到深度:特征提取的两大范式
- 4. 数学骨架:卷积、感受野、不变性/等变性
- 5. 变形(reshape/permute/flatten)会不会改变特征表达?
- 6. 看懂学到的特征:可视化与诊断方法
- 7. 特征如何被使用:分类/检测/分割/检索
- 8. 实战脚手架:一份可直接开跑的 PyTorch 模板
- 9. 性能与数值稳定:dtype、归一化、内存布局
- 10. 踩坑清单与速查表
- 11. FAQ:把最容易误解的点讲透
- 12. 一句话总结
0. 摘要与你将收获什么
一句话:特征是把“像素的排列”变成“可计算、可分辨、可泛化的信息坐标”。
1. 特征到底是什么:三重视角
1.1 物理直觉
- 通道像滤光片:每个通道是一张“热力图”,亮的地方表示“某种模式在这里”。
- 堆叠成谱带:把很多通道叠成一个
H×W×C的“特征立方体”,每层关注一种模式(边缘、纹理、角点、部件等)。 - 层级抽象:浅层看边缘/纹理,中层看部件,高层看语义(“它像猫”)。
1.2 数学表达
设输入图像
x
∈
R
H
×
W
×
C
in
x\in\mathbb{R}^{H\times W\times C_\text{in}}
x∈RH×W×Cin,卷积核(滤波器)
W
∈
R
k
×
k
×
C
in
×
C
out
W\in\mathbb{R}^{k\times k\times C_\text{in}\times C_\text{out}}
W∈Rk×k×Cin×Cout,偏置
b
b
b。深度学习里常用交叉相关形式:
y
h
,
w
,
c
=
∑
i
=
1
k
∑
j
=
1
k
∑
c
′
=
1
C
in
x
h
+
i
−
1
,
,
w
+
j
−
1
,
,
c
′
⋅
W
i
,
j
,
c
′
,
c
+
b
c
y_{h,w,c}=\sum_{i=1}^{k}\sum_{j=1}^{k}\sum_{c'=1}^{C_\text{in}} x_{h+i-1,,w+j-1,,c'}\cdot W_{i,j,c',c}+b_c
yh,w,c=i=1∑kj=1∑kc′=1∑Cinxh+i−1,,w+j−1,,c′⋅Wi,j,c′,c+bc
y
y
y 就是特征图。
1.3 工程实践
- 张量布局:NCHW(批×通道×高×宽)与 NHWC。语义相同、性能不同,需与算子/硬件匹配。
dtype(如float16/32)影响速度与显存;不改变语义,但会影响数值稳定。
2. 特征如何表示:张量的语义与布局
- “形状 = 语义的声明”:
(N,C,H,W)表示“有C个滤片,每个滤片覆盖H×W的空间”。 - 通道 = 语义维,空间 = 拓扑维。改变维度顺序不会改变数值,但会影响后续算子如何解释这些数值。
- 示意(ASCII):
输入图像 (H×W×3)
│ Conv
▼
特征图 (H'×W'×C)
├─ 通道 1:边缘水平
├─ 通道 2:边缘垂直
├─ 通道 3:45° 纹理
└─ ...
3. 从经典到深度:特征提取的两大范式
3.1 经典(手工)特征
- Sobel/Scharr:边缘方向与强度;
- Harris/FAST:角点;
- HOG:方向梯度直方图;
- Gabor:方向性纹理;
优点:可解释性强、轻量;缺点:复杂语义弱、跨域泛化差。
3.2 深度(学习)特征
- 卷积核自学出“有用的滤片”,多层叠加形成层级表征;
- 等变/不变:卷积对平移等变,池化/步幅带来平移近似不变;
- 感受野递增:越深层“看得越广”,语义越全局。
4. 数学骨架:卷积、感受野、不变性/等变性
4.1 感受野公式(递推)
设第
l
l
l 层核大小
k
l
k_l
kl、步幅
s
l
s_l
sl,累计步幅
S
l
=
∏
t
=
1
l
s
t
S_l=\prod_{t=1}^l s_t
Sl=∏t=1lst,则:
R
l
=
R
l
−
1
+
(
k
l
−
1
)
⋅
S
l
−
1
,
R
0
=
1
,
;
S
0
=
1
R_l=R_{l-1} + (k_l-1)\cdot S_{l-1},\quad R_0=1,; S_0=1
Rl=Rl−1+(kl−1)⋅Sl−1,R0=1,;S0=1
4.2 全局平均池化(GAP)
把
H
×
W
×
C
H\times W\times C
H×W×C 变
1
×
1
×
C
1\times 1\times C
1×1×C:
g
c
=
1
H
W
∑
h
,
w
y
h
,
w
,
c
g_c=\frac{1}{HW}\sum_{h,w} y_{h,w,c}
gc=HW1h,w∑yh,w,c
随后用线性层 + Softmax 完成分类:
z
=
W
g
+
b
,
p
k
=
e
z
k
∑
j
e
z
j
z=Wg+b,\quad p_k=\frac{e^{z_k}}{\sum_j e^{z_j}}
z=Wg+b,pk=∑jezjezk
4.3 不变性/等变性
- 等变(equivariance):输入做某变换,输出按相应规则变换(如平移等变)。
- 不变(invariance):输入做小变换,输出基本不变(如经过 GAP 的类别分数)。
- 归纳偏置决定模型对称性感知:卷积偏爱局部与平移;注意力偏爱全局交互。
5. 变形(reshape/permute/flatten)会不会改变特征表达?
结论先行:变形本身不改数值,但会改变“谁跟谁做运算”的邻接关系,从而改变语义解释。
5.1 常见操作的语义
reshape/view:只改形状(可能改变步幅),值不变;permute/transpose:交换维度顺序,值不变;flatten:把多维摊平;concat/stack:拼接,值集合发生变化;unfold/im2col:把局部补丁“展开”为列,配合matmul实现卷积;- PixelShuffle:在“通道↔空间”间搬运信息,常用于超分;
- 插值/池化/卷积:会改变数值,因此改变特征内容。
5.2 安全与危险:一张对照表
| 操作/场景 | 目的 | 语义风险 | 建议 |
|---|---|---|---|
(N,C,H,W)→(N,HW,C) | 把每个像素当 token 做注意力 | 序列邻接≠空间邻接(raster 顺序) | 可行,需位置编码;注意力可建全局关系 |
(N,C,H,W)→(N,C*H*W) | 全局特征输入 MLP/度量 | 丢空间拓扑 | 适合分类/检索,不适合定位 |
permute 切 NCHW↔NHWC | 适配算子/硬件 | 若后续权重按另一布局解释→灾难 | 明确期望布局,I/O 一致 |
flatten(H,W) 后做 1D 卷积 | 想做局部序列建模 | 1D 局部≠2D 邻域 | 慎用;若要局部二维关系,用 Conv/Local Attention |
| 变形后接 BatchNorm/GroupNorm | 归一化统计 | 把“空间维”当“通道维” | 对齐 num_features 到“通道维”;或改用 LayerNorm |
permute 后直接 view | 内存非连续 | 触发错误/隐式 copy | 先 .contiguous() 再 view |
5.3 “为什么变形会改语义但不改数值?”
因为后续算子(卷积/归一化/注意力)会沿特定维度计算邻域或统计量。你改了维度含义,就相当于改了“谁和谁发生关系”。
6. 看懂学到的特征:可视化与诊断方法
- 滤波器可视化:优化输入让某通道激活最大,看到该通道偏好的模式;
- 激活图(Feature Map):将某层输出标准化/上采样,观察“哪里被点亮”;
- Grad-CAM:基于梯度的权重得到类别相关热力图;
- 特征分布:PCA/t-SNE/UMAP 降维看类簇是否可分;
- 余弦相似度:
KaTeX parse error: Expected 'EOF', got '_' at position 11: \text{cos_̲sim}(f_i,f_j)=\…
图示占位:
- 图 1:浅/中/深层通道热力图并排对比(边缘→部件→语义)。
- 图 2:t-SNE 上不同类别的特征点云。
- 图 3:Grad-CAM 覆盖在原图上。
7. 特征如何被使用:分类/检测/分割/检索
7.1 分类
- GAP 得到全局向量,再线性 + Softmax;对“有什么”敏感、对“在哪里”不敏感。
7.2 检测/分割/关键点
- 检测:多尺度特征金字塔(FPN),在不同分辨率上预测框与类别;
- 语义分割:对每个像素位置用 1 × 1 1\times 1 1×1 卷积分类;
- 关键点:为每个关键点学一张热力图,峰值即坐标。
7.3 检索与度量学习
- 取倒数第二层或显式的嵌入层得到向量 f ∈ R d f\in\mathbb{R}^d f∈Rd,做 L2 归一化,使用余弦/欧式距离;
- ArcFace(角度间隔)示例:
logit ∗ y = s ⋅ cos ( θ y + m ) , logit ∗ k ≠ y = s ⋅ cos ( θ k ) \text{logit}*y = s\cdot \cos(\theta_y+m),\quad \text{logit}*{k\ne y} = s\cdot \cos(\theta_k) logit∗y=s⋅cos(θy+m),logit∗k=y=s⋅cos(θk)
提升类间间隔与检索判别力。
8. 实战
说明:以 ResNet50 为例,给出两种特征:(A) 全局向量 与 (B) 空间热力图;附带相似度计算与热力图可视化骨架。
# pip install torch torchvision pillow
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import models, transforms
from PIL import Image
# ========== 1) 骨干与特征头 ==========
class FeatExtractor(nn.Module):
def __init__(self, backbone='resnet50', pretrained=True, return_gap=True):
super().__init__()
m = getattr(models, backbone)(weights='IMAGENET1K_V2' if pretrained else None)
# ResNet: 到 layer4 后接 avgpool 和 fc。我们切到 avgpool 前(特征图)或后(全局特征)。
self.stem = nn.Sequential(
m.conv1, m.bn1, m.relu, m.maxpool,
m.layer1, m.layer2, m.layer3, m.layer4
)
self.gap = nn.AdaptiveAvgPool2d((1,1))
self.return_gap = return_gap # True: 返回全局特征;False: 返回空间特征图
def forward(self, x):
fmap = self.stem(x) # (N, C=2048, H=7, W=7) for 224×224
if self.return_gap:
g = self.gap(fmap).flatten(1) # (N, 2048)
return F.normalize(g, dim=1) # 归一化便于检索与相似度
else:
return fmap
# ========== 2) 预处理 ==========
pre = transforms.Compose([
transforms.Resize(256), transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225])
])
def load_img(path):
return pre(Image.open(path).convert('RGB')).unsqueeze(0)
# ========== 3A) 全局特征 + 相似度 ==========
@torch.no_grad()
def global_cosine(path_a, path_b, device='cuda'):
model = FeatExtractor(return_gap=True).eval().to(device)
xa, xb = load_img(path_a).to(device), load_img(path_b).to(device)
fa, fb = model(xa), model(xb)
cos = (fa @ fb.t()).item()
return cos
# ========== 3B) 空间特征图可视化(单通道示意) ==========
@torch.no_grad()
def single_channel_heatmap(path, ch=0, device='cuda'):
model = FeatExtractor(return_gap=False).eval().to(device)
x = load_img(path).to(device)
fmap = model(x)[0, ch] # (H, W)
fmap = fmap.detach().cpu()
# 归一化到 [0,1]
fm = (fmap - fmap.min()) / (fmap.max() - fmap.min() + 1e-6)
return fm.numpy() # 上层用任意绘图库叠加可视化
# 使用:
# print(global_cosine('a.jpg','b.jpg'))
# heat = single_channel_heatmap('a.jpg', ch=123)
可选:Grad-CAM 骨架(以最后一个卷积层为目标层,略):
- 前向拿到 logits;
- 反向对目标类求梯度;
- 对梯度在空间上做 GAP 得到每个通道的权重;
- 通道加权求和→ReLU→上采样→叠加原图。
9. 性能与数值稳定:dtype、归一化、内存布局
-
AMP(混合精度):
float16/bfloat16提速省显存,但在归一化/损失处可切回float32增强稳定性; -
归一化选择:
- BatchNorm:对“通道维”做批统计,批小不稳;
- GroupNorm/LayerNorm:对小批更稳;
- 实例归一化适合风格与生成任务;
-
内存布局:
permute后若view,先.contiguous(); -
布局匹配:确保
NCHW/NHWC与算子期望一致,防止“把高度当通道”的灾难性错误。
10. 问题
10.1 注意问题
- 变形后语义错位:把空间维当通道做 BatchNorm;
- 1D 卷积替代 2D 邻域:flatten 后做序列局部卷积,破坏二维邻接;
- 布局不一致:模型要 NCHW,却给 NHWC;
- 非连续内存 + view:
permute后没.contiguous(); - 过度下采样:感受野大了,但分辨率过低导致定位困难;
- GAP 用错场景:需要定位却把空间全部平均掉。
11. FAQ
Q1:reshape 会“损伤”特征吗?
A: 不直接损伤数值,但可能让后续算子在错误维度上运算,导致语义扭曲。把维度当“坐标系标签”,你改了标签,后续运算就可能用错坐标系。
Q2:为什么 GAP 后还能分类?空间信息没了呀?
A: GAP 保留了全局统计,对“有什么”很敏感,对“哪里”不敏感——正符合分类需求。
Q3:想兼顾“哪里”和“是什么”?
A: 保留更高分辨率(少用大步幅/大池化),或用 FPN/U-Net 做多尺度融合。
Q4:如何理解
1
×
1
1\times 1
1×1 卷积?
A: 在每个空间位置做通道间线性混合,相当于“通道维的全连接”,还能做维度升降与跨通道交互。
Q5:ViT 的 patchify(分块)是不是危险变形?
A: 这是设计使然:把
(
H
,
W
,
C
)
(H,W,C)
(H,W,C) 切成
(
N
patch
,
P
2
C
)
(N_\text{patch}, P^2 C)
(Npatch,P2C) 的 token,再做全局注意力。虽削弱局部等变性,但获得全局建模能力;可用位置编码/局部注意力辅助空间结构。
12. 一句话总结
特征 = 可计算的语义坐标。变形不改数值,但会改变谁与谁做运算的邻接关系——这决定语义如何被解释。只要维度语义清晰、后续算子与任务对称性匹配,就能看清、表得稳、用得好。
**附:公式
- 交叉相关(Conv2D 常用实现):
y h , w , c = ∑ i = 1 k ∑ j = 1 k ∑ c ′ x h + i − 1 , w + j − 1 , c ′ , W i , j , c ′ , c + b c y_{h,w,c}=\sum_{i=1}^{k}\sum_{j=1}^{k}\sum_{c'} x_{h+i-1, w+j-1, c'}, W_{i,j,c',c}+b_c yh,w,c=i=1∑kj=1∑kc′∑xh+i−1,w+j−1,c′,Wi,j,c′,c+bc - 感受野递推:
R l = R l − 1 + ( k l − 1 ) ⋅ ∏ t = 1 l − 1 s t , R 0 = 1 R_l=R_{l-1}+(k_l-1)\cdot\prod_{t=1}^{l-1}s_t,\quad R_0=1 Rl=Rl−1+(kl−1)⋅t=1∏l−1st,R0=1 - 全局平均池化:
g c = 1 H W ∑ h , w y h , w , c g_c=\frac{1}{HW}\sum_{h,w} y_{h,w,c} gc=HW1h,w∑yh,w,c - Softmax:
p k = e z k ∑ j e z j p_k=\frac{e^{z_k}}{\sum_j e^{z_j}} pk=∑jezjezk - 余弦相似度:
KaTeX parse error: Expected 'EOF', got '_' at position 11: \text{cos_̲sim}(f_i,f_j)=\… - ArcFace 角度间隔:
logit ∗ y = s ⋅ cos ( θ y + m ) , logit ∗ k ≠ y = s ⋅ cos ( θ k ) \text{logit}*y=s\cdot \cos(\theta_y+m),\qquad \text{logit}*{k\ne y}=s\cdot \cos(\theta_k) logit∗y=s⋅cos(θy+m),logit∗k=y=s⋅cos(θk) - Fisher 判别(两类):
J = ∣ μ 1 − μ 2 ∣ 2 2 σ 1 2 + σ 2 2 J=\frac{|\mu_1-\mu_2|_2^2}{\sigma_1^2+\sigma_2^2} J=σ12+σ22∣μ1−μ2∣22
14万+

被折叠的 条评论
为什么被折叠?



