1. 项目背景与问题定位
去年实验室新来的师弟找我帮忙复现西储大学轴承故障诊断的迁移学习代码,本以为是个简单的活——毕竟网上公开的案例不少。但真正动手才发现,从数据预处理到模型迁移的每个环节都藏着"暗坑"。这次经历让我意识到,很多看似成熟的技术方案在实际落地时,依然需要根据具体场景做大量适配工作。
西储大学轴承数据集(CWRU)作为故障诊断领域的基准数据,包含驱动端轴承、风扇端轴承在不同负载条件下的振动信号。原始数据采样频率12kHz,包含正常状态和内圈、外圈、滚动体三种故障类型,每种故障又有不同损伤直径(0.007英寸到0.021英寸)。这种多维度的故障特征本应让分类任务变得简单,但实际处理时会遇到几个典型问题:
- 原始振动信号长度不一致(从48000到120000个采样点不等)
- 不同故障类型的样本数量严重不均衡
- 迁移学习时源域(实验室数据)与目标域(现场数据)的分布差异
- 时频域特征提取方法对最终分类效果的影响
2. 数据预处理的关键陷阱
2.1 信号标准化与分段处理
原始振动信号需要先进行标准化(z-score归一化),这个步骤看似基础却容易出错。常见错误是直接对整个数据集计算均值和标准差,这会导致数据泄露(data leakage)。正确做法应该按以下顺序:
python复制def segment_signal(data, window_size=1024, step_size=512):
segments = []
for start in range(0, len(data)-window_size, step_size):
segment = data[start:start+window_size]
segments.append(segment)
return np.array(segments)
# 对每个样本独立标准化后再分段
raw_signals = [...] # 原始振动信号列表
processed = []
for signal in raw_signals:
normalized = (signal - np.mean(signal)) / np.std(signal)
segments = segment_signal(normalized)
processed.extend(segments)
重要提示:窗口大小选择需要匹配轴承的故障特征频率。对于CWRU数据集,1024点的窗口(约0.085秒)能捕获大部分故障冲击特征,同时保持计算效率。
2.2 样本不均衡的解决方案
实验发现原始数据中不同故障类型的样本比例差异可达1:5。直接训练会导致模型偏向多数类。我们测试了三种方案:
| 方法 | 准确率 | F1-score | 实现复杂度 |
|---|---|---|---|
| 类别权重 | 89.2% | 0.87 | 低 |
| SMOTE过采样 | 91.5% | 0.89 | 中 |
| 分段欠采样 | 90.8% | 0.91 | 高 |
最终选择PyTorch的WeightedRandomSampler实现类别加权,因其在保持原始数据分布的同时,只需在DataLoader中添加:
python复制class_counts = torch.bincount(labels)
class_weights = 1. / class_counts
sample_weights = class_weights[labels]
sampler = WeightedRandomSampler(sample_weights, len(sample_weights))
3. 迁移学习架构设计与调优
3.1 基模型选择与改造
使用ResNet18作为基础架构,但需要针对振动信号特点进行改造:
- 输入通道:将原始1D振动信号转为2D时频图(使用连续小波变换CWT)
- 第一层卷积:修改kernel_size=(7,7)→(3,7)以适应时频特征
- 全连接层:替换为适合故障分类任务的输出维度
python复制class CWT_ResNet(nn.Module):
def __init__(self, n_classes=10):
super().__init__()
self.cwt = CWTTransform() # 自定义小波变换层
self.base = resnet18(pretrained=True)
self.base.conv1 = nn.Conv2d(1, 64, kernel_size=(3,7), stride=(1,2), padding=(1,3), bias=False)
self.base.fc = nn.Linear(512, n_classes)
def forward(self, x):
x = self.cwt(x) # [batch, 1, 64, 64]
return self.base(x)
3.2 领域自适应技巧
源域(CWRU实验室数据)和目标域(实际设备数据)的分布差异会导致性能下降。我们采用联合训练策略:
- 冻结ResNet底层参数,只微调最后三层
- 添加MMD(最大均值差异)损失对齐特征分布
- 使用学习率热启动策略(LR warmup)
python复制def mmd_loss(source, target):
delta = source.mean(0) - target.mean(0)
return delta @ delta.T
# 在训练循环中加入
for (x_src, y_src), (x_tgt, _) in zip(src_loader, tgt_loader):
feat_src = model.feature_extractor(x_src)
feat_tgt = model.feature_extractor(x_tgt)
loss = criterion(outputs, y_src) + 0.5 * mmd_loss(feat_src, feat_tgt)
4. 实际踩坑与解决方案实录
4.1 时频分析参数选择
最初直接使用STFT导致分类准确率仅76%,排查发现:
- 窗口长度太短(256点),无法捕捉低频故障特征
- 重叠率不足(50%),时域信息丢失严重
改用Morlet小波变换并优化参数后提升至89%:
python复制class CWTTransform(nn.Module):
def __init__(self, scales=np.arange(10,100,5)):
super().__init__()
self.scales = scales
def forward(self, x):
# x: [batch, 1024]
cwt = []
for signal in x:
coef, _ = pywt.cwt(signal.numpy(), self.scales, 'morl')
cwt.append(coef)
return torch.tensor(np.stack(cwt)).unsqueeze(1) # [batch, 1, scales, time]
经验:对于轴承故障诊断,尺度参数scales应覆盖轴承的特征频率(CWRU案例中10-100的线性间隔效果最佳)
4.2 过拟合问题的应对
即使使用预训练模型,在小样本场景下仍出现过拟合:
- 验证集准确率比训练集高15%(典型过拟合迹象)
- 混淆矩阵显示模型将少数类全部预测为多数类
解决方案组合:
- 添加Dropout层(p=0.5)
- 使用Label Smoothing(ε=0.1)
- 采用MixUp数据增强
python复制def mixup_data(x, y, alpha=0.4):
lam = np.random.beta(alpha, alpha)
batch_size = x.size(0)
index = torch.randperm(batch_size)
mixed_x = lam * x + (1 - lam) * x[index]
return mixed_x, y, y[index], lam
5. 完整实现流程与验证
5.1 端到端训练步骤
-
数据准备阶段
bash复制wget https://csegroups.case.edu/bearingdatacenter/pages/download-data-file unzip -j CWRU.zip "**/DE/*.mat" -d cwru_data -
特征工程流水线
python复制pipeline = Compose([ RandomCrop(1024), Normalize(), CWTTransform(), RandomTimeShift(max_shift=64) ]) -
训练脚本核心逻辑
python复制for epoch in range(100): model.train() for x, y in train_loader: x, y_a, y_b, lam = mixup_data(x, y) outputs = model(x) loss = lam * criterion(outputs, y_a) + (1-lam) * criterion(outputs, y_b) loss.backward() optimizer.step()
5.2 性能验证结果
在测试集(20%未参与训练数据)上的表现:
| 故障类型 | 准确率 | 召回率 | 备注 |
|---|---|---|---|
| 正常 | 95.2% | 96.1% | - |
| 内圈故障 | 89.7% | 88.3% | 小直径损伤易误判 |
| 外圈故障 | 92.1% | 90.5% | 负载变化影响大 |
| 滚动体故障 | 85.4% | 82.7% | 样本量最少 |
关键发现:迁移学习相比从零训练提升约23%的准确率(特别是对样本少的故障类型),但需要仔细处理领域偏移问题。
6. 给初学者的实操建议
-
数据检查清单:
- 确保每个样本的采样率一致
- 验证故障标签与实际信号对应关系
- 检查信号基线是否漂移(需去除直流分量)
-
模型调试技巧:
- 先在小样本(10%)上快速验证流程
- 使用TensorBoard监控特征分布变化
- 对错误样本进行人工复查(常能发现标注问题)
-
效率优化:
- 将时频变换预处理转为ONNX加速
- 使用PyTorch的
pin_memory加速数据加载 - 对连续小波变换实现多进程并行
这个项目让我深刻体会到,即使是经典案例的复现,也需要根据实际数据特点进行调整。特别是在工业场景中,振动信号的采集条件与实验室差异很大,如何设计鲁棒的迁移学习方案才是真正的挑战。后续我们计划加入对抗训练(DANN)来进一步提升模型在跨设备场景下的泛化能力。