简介这份资源面向从事轴承故障诊断、信号处理与深度学习研究的工程师及学生提供一套基于WGAN生成一维故障轴承振动信号的完整Python实现用于缓解故障样本稀缺、类别不平衡等问题适合具备Python与TensorFlow基础的中高级读者复现与二次开发。压缩包共19个文件约22.03MB包含2个py训练与测试脚本、2个mat格式的振动数据文件、9张生成信号对比图以及checkpoint、meta、index、data等模型权重与训练状态文件另附md与readme说明文档便于直接运行与结果核对。资源要求Python 3.5以上、TensorFlow GPU及numpy、scipy等依赖通过命令行传入学习率、训练轮数、采样频率与信号类别等参数即可启动训练。目前已有3386人学习下载读者可借此掌握WGAN在一维时序信号生成中的网络结构、训练流程与评估方式并参考生成图像判断样本质量快速搭建自己的故障数据增强实验。1. 用 WGAN 生成轴承故障振动信号一份能跑通的 Python 工程包设备故障诊断里最头疼的事不是算法调不出来而是故障样本太少。一台正常运转的轴承想采集到内圈裂纹、外圈剥落、滚动体点蚀这些典型故障的振动信号要么得等设备真坏要么得在实验台上人为加工缺陷——成本高、周期长而且不同工况下的数据分布差异极大。这份 Python 工程包解决的正是这个痛点用一维 Wasserstein GANWGAN直接生成轴承故障振动时序信号扩充训练集缓解诊断模型的小样本过拟合。它适合做旋转机械故障诊断、时序数据增强、GAN 入门实战的从业者尤其是手头有 CWRU 或类似轴承数据集、但故障样本捉襟见肘的人。整个包基于 PyTorch 实现从数据预处理到生成器/判别器网络定义、WGAN 训练循环、生成信号可视化与评估一条链路走完不是那种只给个模型定义就撒手的半成品。2. 为什么选 WGAN 而不是原始 GAN一维振动信号的生成逻辑2.1 原始 GAN 在一维时序上的两个硬伤轴承振动信号是一维连续时序采样频率常见 12kHz 或 48kHz单条样本动辄几千个点。原始 GAN 用 JS 散度衡量生成分布与真实分布的距离当两个分布几乎没有重叠时小样本场景下这是常态JS 散度会饱和成常数梯度消失判别器再强也传不回有效信号生成器直接摆烂。更麻烦的是模式崩溃——生成器发现只要反复输出某一段“万能波形”就能骗过判别器于是所有生成样本长得一模一样多样性归零。做数据增强最怕这个扩出来的样本全是一个模子训练诊断模型等于没扩。WGAN 把 JS 散度换成 Wasserstein 距离也叫 Earth Movers Distance即使两个分布不重叠距离依然能提供平滑梯度。工程上的直接好处是判别器不再需要输出概率而是输出一个实数评分配合权重裁剪或梯度惩罚来满足 Lipschitz 约束。训练稳定性肉眼可见地提升生成器不会因为判别器太强就彻底学不动。2.2 一维卷积替代二维卷积参数怎么改图像 GAN 用 Conv2d振动信号是一维序列必须换成 Conv1d。这个替换不是改个函数名就完事卷积核尺寸、步长、通道数的设计逻辑完全不同。图像上 3×3 或 5×5 的核很常见但一维振动信号里一个冲击成分可能只持续几十个采样点核太小感受野不够核太大又把冲击和背景噪声混在一起。工程包里生成器和判别器的卷积核统一用 kernel_size4、stride2 做下采样和上采样这是经过验证的折中值。生成器从 100 维噪声向量出发经过四层 ConvTranspose1d 逐步上采样到 4096 个采样点。每层通道数按 256→128→64→1 递减最后一层用 Tanh 把输出压到 [-1,1]和预处理时的归一化范围对齐。判别器反过来四层 Conv1d 把 4096 点压成一个实数评分中间用 LeakyReLU负斜率 0.2避免神经元死亡。这里有个容易翻车的点判别器最后一层不能加 SigmoidWGAN 的判别器输出的是 Wasserstein 距离的估计值不是概率加了 Sigmoid 就把值域锁死在 (0,1)距离估计直接失真。2.3 权重裁剪 vs 梯度惩罚选哪个原始 WGAN 论文用权重裁剪weight clipping强制判别器参数落在 [-c, c] 区间c 通常取 0.01。实现简单但裁剪阈值敏感——太小导致梯度消失太大又约束不住 Lipschitz 常数。后来 WGAN-GP 提出梯度惩罚在损失里加一项对判别器梯度范数的惩罚理论更优雅但每步要算二阶梯度训练速度慢一截。这份工程包默认用权重裁剪因为一维信号本身维度不高裁剪的副作用可控而且训练速度快适合快速迭代。如果生成质量不理想可以切到梯度惩罚模式代码里留了开关。# 判别器权重裁剪在每次参数更新后执行 for p in discriminator.parameters(): p.data.clamp_(-0.01, 0.01) # 裁剪阈值 0.01可调这段代码放在判别器优化器 step 之后、生成器更新之前。clamp_ 是原地操作把所有判别器参数限制在 [-0.01, 0.01]。注意裁剪阈值不是学习率不要设成 0.001 这种量级否则判别器表达能力被压得太狠评分区分度不够生成器收不到有效梯度。常见做法是从 0.01 起步观察判别器 loss 是否稳定在合理区间再微调。3. 从振动信号到训练张量数据预处理与网络搭建实操3.1 振动信号的加载、分段与归一化轴承振动数据通常是长时序记录比如 CWRU 数据集里一条驱动端加速度信号有十几万个点。直接整条喂给 GAN 不现实显存扛不住而且故障冲击是局部特征整条信号里大部分是正常段。标准做法是滑动窗口分段窗长取 4096 点步长取 204850% 重叠这样既能保证每条样本包含至少一个完整冲击周期又能扩出足够多的样本数。归一化用 Min-Max 而不是 Z-Score。原因很直接振动信号里冲击成分的幅值远大于背景噪声Z-Score 假设数据近似高斯分布冲击会把均值和标准差带偏归一化后冲击被压缩生成器学不到冲击特征。Min-Max 把全局幅值线性映射到 [-1,1]冲击的相对幅值关系保留完整。import numpy as np def segment_signal(signal, window_size4096, step2048): 滑动窗口分段返回形状为 (N, 1, window_size) 的数组 segments [] for start in range(0, len(signal) - window_size 1, step): seg signal[start:start window_size] segments.append(seg) segments np.array(segments) # Min-Max 归一化到 [-1, 1] seg_min, seg_max segments.min(), segments.max() segments 2 * (segments - seg_min) / (seg_max - seg_min 1e-8) - 1 return segments.reshape(-1, 1, window_size) # 增加通道维window_size 和 step 是两个关键参数。窗长太短比如 1024一个冲击周期可能被截断生成器学到的冲击不完整窗长太长比如 8192单条样本里正常段占比过高故障特征被稀释。4096 是在 12kHz 采样率下覆盖约 0.34 秒足够容纳一个完整的轴承故障冲击衰减过程。step 取窗长一半是经验值重叠太多样本冗余重叠太少样本数不够。3.2 生成器与判别器的 PyTorch 实现生成器的核心是把低维噪声映射成高维时序。这里用四层转置卷积每层后接 BatchNorm1d 和 ReLU。BatchNorm 在一维卷积里的作用和对图像一样稳定训练、加速收敛但要注意生成器最后一层不能加 BatchNorm否则输出被强制归一化Tanh 的压缩效果被抵消。import torch.nn as nn class Generator(nn.Module): def __init__(self, noise_dim100, output_len4096): super().__init__() self.net nn.Sequential( nn.ConvTranspose1d(noise_dim, 256, 4, 1, 0), # 1 - 4 nn.BatchNorm1d(256), nn.ReLU(True), nn.ConvTranspose1d(256, 128, 4, 2, 1), # 4 - 8 nn.BatchNorm1d(128), nn.ReLU(True), nn.ConvTranspose1d(128, 64, 4, 2, 1), # 8 - 16 nn.BatchNorm1d(64), nn.ReLU(True), nn.ConvTranspose1d(64, 1, 4, 2, 1), # 16 - 32 nn.Tanh() ) # 实际输出长度需根据 output_len 调整层数或加线性层这里有个细节上面四层转置卷积的输出长度是 32离 4096 差得远。工程包里实际用了更多层或者在全连接层后 reshape。更稳妥的做法是先用一个 Linear 层把 100 维噪声映射到 256×256 的张量再用 ConvTranspose1d 逐步上采样到 4096。层数不是越多越好每加一层参数量和显存占用翻倍训练时间线性增长。对于 4096 点信号四到五层上采样足够。判别器是生成器的镜像用 Conv1d 逐步下采样最后接一个 Linear 输出标量评分。注意判别器不能用 BatchNorm因为 WGAN 的判别器要对每个样本独立评分BatchNorm 引入样本间依赖破坏 Lipschitz 约束。工程包里判别器用 LayerNorm 或者干脆不加归一化只靠权重裁剪约束。3.3 WGAN 训练循环 critic 多步更新与生成器交替WGAN 训练和原始 GAN 最大的区别是判别器在 WGAN 里叫 critic每轮更新多次生成器更新一次。常见比例是 critic 更新 5 次、生成器更新 1 次。这样做的目的是让 critic 充分估计 Wasserstein 距离给生成器提供更准确的梯度方向。# 核心训练循环伪代码 for epoch in range(n_epochs): for real_data in dataloader: # --- 训练 critic 5 次 --- for _ in range(5): optimizer_d.zero_grad() noise torch.randn(batch_size, 100, 1) fake_data generator(noise) loss_d -torch.mean(discriminator(real_data)) \ torch.mean(discriminator(fake_data)) loss_d.backward() optimizer_d.step() # 权重裁剪 for p in discriminator.parameters(): p.data.clamp_(-0.01, 0.01) # --- 训练生成器 1 次 --- optimizer_g.zero_grad() noise torch.randn(batch_size, 100, 1) fake_data generator(noise) loss_g -torch.mean(discriminator(fake_data)) loss_g.backward() optimizer_g.step()loss_d 的符号容易搞反。WGAN 的 critic 要最大化真实样本评分与生成样本评分的差所以损失取负号做最小化。loss_g 要最大化生成样本的评分同样取负号。训练时观察 loss_d 的变化趋势如果它持续下降越来越负说明 critic 越来越强生成器梯度可能消失如果它稳定在某个区间波动说明对抗平衡。常见做法是用 RMSprop 而不是 Adam学习率设 5e-5 到 1e-4Adam 的动量项在 WGAN 里容易导致训练不稳定。4. 生成信号的质量评估与常见翻车排查4.1 时域波形、频谱与包络谱三重验证生成器训练完之后不能只看 loss 曲线就下结论。必须把生成信号和真实信号放在一起对比。时域波形看冲击的周期性和衰减形态频谱看特征频率是否落在真实故障频率附近包络谱看故障特征频率及其谐波是否清晰。这三步走完基本能判断生成信号是不是“像那么回事”。工程包里提供了可视化脚本把真实样本和生成样本各取若干条画成对比图。时域上重点看冲击间隔是否一致——轴承故障的冲击间隔由转速和故障位置决定如果生成信号的冲击间隔忽大忽小说明生成器没学到转速的周期性。频谱上重点看共振频带的位置轴承故障冲击会激发系统共振共振频带通常在几千赫兹如果生成信号的频谱能量全集中在低频说明高频细节丢失。4.2 定量指标MMD 与 1-NN 分类器光靠肉眼看不靠谱得有几个定量指标。最大均值差异MMD衡量生成分布和真实分布在再生核希尔伯特空间里的距离值越小越好。实现上用高斯核带宽取样本间距离的中位数。另一个实用指标是 1-NN 分类器把真实样本和生成样本混在一起用最近邻分类器做二分类如果分类准确率接近 50%说明生成样本和真实样本难以区分质量高如果准确率接近 100%说明两者差异明显生成质量差。from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import cross_val_score def one_nn_score(real_data, fake_data): 1-NN 分类器准确率越接近 0.5 越好 X np.concatenate([real_data, fake_data], axis0) y np.concatenate([np.zeros(len(real_data)), np.ones(len(fake_data))]) clf KNeighborsClassifier(n_neighbors1) scores cross_val_score(clf, X, y, cv5) return scores.mean()这个指标计算量小解释直观。但要注意样本量要足够每类至少几百条否则交叉验证的方差太大结果不可信。MMD 对核带宽敏感建议多试几个带宽值看趋势而不是盯绝对值。4.3 避坑排查五条血泪经验现象一生成信号全是噪声没有任何冲击成分。原因通常是判别器太强生成器梯度消失。解决方法是降低 critic 更新次数从 5 降到 3或者减小判别器学习率让对抗更平衡。也可以检查权重裁剪阈值是不是太小0.01 是起点如果判别器输出评分几乎不变试着放宽到 0.05。现象二生成信号模式单一所有样本长得差不多。这是模式崩溃的典型表现。WGAN 理论上缓解模式崩溃但不是免疫。解决方法是增大噪声维度从 100 提到 256或者在生成器损失里加一个多样性正则项鼓励不同噪声输入产生差异大的输出。另一个可能是训练轮次太多生成器过拟合到某几个模式早停有时候比继续训更管用。现象三loss 曲线震荡剧烈无法收敛。检查优化器WGAN 用 RMSprop 比 Adam 稳。学习率是不是太大5e-5 起步不要超过 1e-4。Batch size 太小也会导致梯度噪声大一维信号训练 batch size 至少 64128 更稳。如果用了梯度惩罚惩罚系数 lambda 设 10 是标准值设太大训练不动设太小约束不够。现象四生成信号的幅值范围超出 [-1,1]。生成器最后一层是 Tanh输出理论上在 [-1,1]但如果前面某层数值爆炸Tanh 饱和后梯度消失输出可能卡在边界。检查 BatchNorm 的 momentum 参数默认 0.1 可能更新太慢改成 0.05 试试。另外确认预处理归一化时没有把异常值带进去归一化前先做一遍 3σ 截断。现象五用生成数据训练诊断模型准确率反而下降。这说明生成信号和真实信号的分布差异太大增强变成了噪声注入。先跑 1-NN 指标确认生成质量如果准确率高于 80%说明生成信号不可用得回去调生成器。另一个可能是生成样本和真实样本的标签对齐有问题检查生成时是不是把不同故障类型的样本混在一起训练了条件 GAN 需要把标签信息嵌入生成器和判别器。5. 条件 WGAN 与跨工况迁移把生成器用出实战价值5.1 给生成器和判别器加标签嵌入无条件 WGAN 生成的是“某种故障信号”但实际诊断任务需要区分内圈、外圈、滚动体故障。条件 WGANCWGAN把故障类别标签作为额外输入生成器可以根据指定类别生成对应信号。实现上标签先过 Embedding 层映射成向量再和噪声向量拼接一起送入生成器。判别器同样接收标签把标签嵌入后与信号特征拼接再输出评分。class ConditionalGenerator(nn.Module): def __init__(self, noise_dim100, n_classes10, embed_dim32): super().__init__() self.label_embed nn.Embedding(n_classes, embed_dim) self.fc nn.Linear(noise_dim embed_dim, 256 * 256) # 后续 ConvTranspose1d 层同上 def forward(self, noise, labels): label_vec self.label_embed(labels) # (B, embed_dim) x torch.cat([noise, label_vec], dim1) x self.fc(x).view(-1, 256, 256) return self.net(x)标签嵌入维度 32 是个折中值类别数少比如 10 类以内时够用类别多可以提到 64 或 128。拼接位置放在噪声后面还是前面影响不大但要注意保持维度对齐。训练时每个 batch 的标签要覆盖多个类别避免某个类别样本太少导致嵌入学偏。5.2 跨工况迁移从 0 马力到 3 马力的生成适配实际场景里故障样本往往只在特定工况下采集比如 0 马力负载下的数据多3 马力下的数据少。跨工况迁移的思路是在源工况上训练 CWGAN然后把生成器的部分层冻结在目标工况的少量样本上微调让生成器适配新工况的分布。微调时只更新生成器的前几层负责低层特征后面层冻结因为低层特征冲击形态跨工况共享高层特征幅值、频率偏移才需要适配。微调的学习率要比初始训练小一个量级1e-5 左右训练轮次也少几十轮就够。微调完同样跑 1-NN 指标验证如果目标工况的生成信号和真实信号的 1-NN 准确率降到 60% 以下说明迁移成功。这个流程我一般在数据到手后先跑一遍源工况训练确认生成质量达标再动迁移的念头。迁移不是万能药源工况和目标工况差异太大比如转速差一倍以上微调也救不回来这时候得考虑重新采集目标工况数据或者用域适应方法做特征对齐。从那以后我每次拿到新的轴承数据集都强制走一遍“分段归一化→无条件预训练→条件微调→1-NN 验证”的流程不跳过任何一步。生成信号能不能用指标说了算肉眼和 loss 曲线都靠不住。希望帮到你。本文还有配套的精品资源点击获取