2. 卷积核权重分布可视化与冗余分析
任务目标
分析一个已训练CNN模型中各卷积层的权重分布,通过直方图可视化识别分布集中(方差小)的层——这类层通常冗余度较高,是后续剪枝的候选对象。本案例使用PyTorch加载预训练的ResNet18,提取全部卷积层权重并绘制直方图。
环境依赖
- Python 3.8+,PyTorch 1.9+,torchvision,matplotlib,numpy
- 无需GPU,CPU即可完成
核心实现
import torch
import torchvision.models as models
import matplotlib.pyplot as plt
import numpy as np
# 1. 加载预训练模型并提取卷积层
model = models.resnet18(pretrained=True)
model.eval()
conv_layers = []
for name, module in model.named_modules():
if isinstance(module, torch.nn.Conv2d):
conv_layers.append((name, module.weight.data.numpy()))
print(f"共找到 {len(conv_layers)} 个卷积层")
# 2. 计算每层权重统计量(均值、标准差、稀疏度)
stats = []
for name, w in conv_layers:
flat = w.flatten()
std = float(np.std(flat))
mean = float(np.mean(flat))
# 稀疏度:|w| < 0.01 的权重占比
sparse_ratio = float(np.mean(np.abs(flat) < 0.01))
stats.append((name, mean, std, sparse_ratio))
# 3. 绘制直方图(每层一个子图)
n = len(conv_layers)
cols = 4
rows = (n + cols - 1) // cols
fig, axes = plt.subplots(rows, cols, figsize=(16, 3 * rows))
axes = axes.flatten()
for i, (name, w) in enumerate(conv_layers):
ax = axes[i]
flat = w.flatten()
ax.hist(flat, bins=60, color='steelblue', alpha=0.7)
ax.set_title(f"{name}\nstd={stats[i][2]:.4f}", fontsize=8)
ax.set_xlim([-0.5, 0.5])
ax.tick_params(labelsize=6)
# 隐藏多余的子图
for j in range(n, len(axes)):
axes[j].axis('off')
plt.tight_layout()
plt.savefig("conv_weight_hist.png", dpi=150)
print("直方图已保存为 conv_weight_hist.png")
# 4. 输出冗余度排序(按标准差升序,即分布最集中的层)
print("\n=== 冗余度排序(标准差最小 = 分布最集中)===")
sorted_stats = sorted(stats, key=lambda x: x[2])
for name, mean, std, sparse in sorted_stats[:5]:
print(f"{name:30s} mean={mean:+.4f} std={std:.4f} 稀疏度={sparse:.2%}")
运行方式
pip install torch torchvision matplotlib numpy
python weight_hist.py
验证方法与判定标准
- 运行成功标志:终端输出卷积层数量(ResNet18为20层),并生成
conv_weight_hist.png文件。 - 可视化判读:观察直方图——若某层直方图呈尖峰状且集中在0附近(标准差小),说明该层大量权重接近0,冗余度高;若分布平坦(标准差大),则信息量相对丰富。
- 量化判据:程序输出的排序列表中,前几层(如
conv1或深层残差块中的3×3卷积)通常std最小,这些层即为剪枝优先考虑的对象。 - 预期输出示例(基于ResNet18预训练权重的确定性统计):
conv1层std约为0.05~0.07,而layer4.1.conv2等深层std约为0.01~0.03,后者冗余度更高。
常见问题
- 首次运行需下载权重:约45MB,需联网;若网络受限,可设置
model = models.resnet18(weights=None)仅观察随机初始化分布(此时各层std接近,无法体现训练后的冗余差异)。 - 直方图横轴范围:代码固定为[-0.5, 0.5],若个别层权重超出此范围,可调整
ax.set_xlim参数。