☰
用WGAN生成轴承故障振动信号:一维GAN数据增强实战
2026/10/10 21:16:08 网站建设 项目流程

简介:基于沃瑟斯坦生成对抗网络的故障轴承振动信号生成项目,面向故障诊断与深度学习交叉领域的开发者与研究者,提供一套可直接落地的完整的一维信号生成对抗网络训练与测试方案。压缩包共十九个文件,涵盖训练与测试脚本、两组轴承振动数据、模型权重文件以及九张示例输出图,整体约二十二兆字节,目录划分清晰,便于按需检索。目前已有三千三百八十六人学习使用。基于Python的代码支持调整学习率、训练轮数与采样周期等超参数,并内置九种不同故障状态的信号数据,方便切换对比。同时提供训练所得的模型检查点与可视化图像,可直接复现生成效果,为轴承样本增强、特征扩展与异常检测实验提供可靠的参考基准。

1. GAN-1D 用 WGAN 生成故障轴承振动信号:先回答值不值得做

做设备故障诊断的同行基本都撞过同一堵墙:现场采集的轴承故障样本少得可怜,内圈裂纹、外圈点蚀、滚动体磨损这些工况,正常数据攒了几十个 G,故障数据可能就几十条。拿这点样本去训 CNN 或 LSTM,指标再好看也是过拟合的幻觉。GAN-1D 这个方向就是把一维卷积生成对抗网络用在振动时序上,配合 WGAN 的 Wasserstein 距离和梯度惩罚,让生成器学会复现故障信号的时域波形和频域特征,再拿生成样本去扩充训练集。这篇笔记会从网络结构、PyTorch 最小实现、参数调节到验证回测完整走一遍,适合正在做故障诊断数据增强、却对 GAN 训练心里没底的工程师。先说结论:这个方案能落地,但它的价值不在“生成波形好看”,而在“补上的故障样本能让诊断模型涨点”——围绕这个目标,下面每一步都按可复现的标准来写。

2. 理解 GAN-1D 与 WGAN:一维时序生成为什么非要用 WGAN 这套路

2.1 一维生成和二维生成的本质差异:卷积核怎么“看”振动信号

图像 GAN 的输入输出是 H×W 的矩阵,卷积核在空间维度上滑动,提取的是局部纹理和全局结构。振动信号是长度 N 的一维序列,比如采样率 12 kHz 下转一圈采集 1024 个点,卷积核只能在时间轴上从左往右扫。这意味着 GAN-1D 的归纳偏置和图像模型完全不同:它不需要关注“猫耳朵在哪”,只需要关注“这个时间片段的幅值包络和相邻片段的统计关系”。

我在实际搭建时发现,判别器对振动信号的敏感点集中在三个地方:冲击成分的幅值(内圈故障每转一圈就敲一下)、高频衰减振荡的持续时间、以及背景噪声的方差。普通 GAN 的判别器输出的是“真假概率”,它很容易找到一个捷径——只盯着峰值高低判断,导致生成器学到“把幅值调大就骗过去了”。WGAN 的判别器(严格说叫 critic)输出的是一个无上界的实数,衡量的是两个分布之间的 Wasserstein 距离,它逼着生成器在整体分布形状上对齐,而不是钻局部特征的空子。

2.2 原始 GAN 训练翻车的三个病根,以及 WGAN 对应的三处改动

原始 GAN 用 JS 散度做优化目标,当生成分布和真实分布的重叠区域很小时,JS 散度是一个常数,梯度直接消失——这就是新手最常见的“判别器 loss 降到底,生成器 loss 不动”现象。WGAN 把目标函数换成 Wasserstein 距离,即使两个分布完全不重叠,梯度依然存在且方向正确,从根上解决了训练初期梯度枯竭的问题。

第二处改动是去掉输出层的 Sigmoid。原始 GAN 判别器输出 (0,1) 区间的概率,WGAN 的 critic 输出一个实数,配合 Lipschitz 约束(通常用梯度惩罚实现),让 critic 的梯度范数被限制在 1 附近。第三处改动是更新节奏:原始 GAN 里判别器和生成器要小心翼翼地保持平衡,WGAN 里 critic 可以多训几步(n_critic 通常取 5),因为 Wasserstein 距离估计得越准,生成器拿到的梯度信号越干净。

这三处改动对应到代码层面就三行:损失函数从 BCE 换成“critic(fake) 均值减 critic(real) 均值”、去掉最后一层 Sigmoid、在损失里加一项梯度惩罚。改动量不大,但训练稳定性提升是质变的。下面进入正题,直接搭一个能跑的最小工程。

3. 用 PyTorch 搭建 WGAN 生成轴承振动信号的最小复现

3.1 数据预处理:把原始振动信号切成固定长度的训练张量

轴承振动数据通常是长时序,以凯斯西储大学(CWRU)数据集为例,一个文件里是几秒钟的连续采样。切段时我一般取 1024 个点作为一个样本,这个长度在 12 kHz 采样率下约 85 ms,能覆盖至少一个转频周期(转频 30 Hz 时周期约 33 ms),又能让故障特征频率在 FFT 后有足够分辨率。切段步长取 512,相邻样本有 50% 重叠,相当于做了数据增强。切完必须做 z-score 标准化,否则生成器要同时学均值和方差,训练负担翻倍。

import numpy as np import torch from torch.utils.data import Dataset, DataLoader def segment_signal(data, segment_len=1024, stride=512): samples = [] for i in range(0, len(data) - segment_len + 1, stride): seg = data[i:i + segment_len].astype(np.float32) # z-score 标准化:消除不同工况下的幅值差异 seg = (seg - seg.mean()) / (seg.std() + 1e-8) samples.append(seg) return np.stack(samples) # shape: (num_segments, 1024) class VibrationDataset(Dataset): def __init__(self, samples): self.samples = torch.from_numpy(samples).unsqueeze(1) # (N, 1, 1024) def __len__(self): return len(self.samples) def __getitem__(self, idx): return self.samples[idx] # 以单个故障类别为例:内圈故障 raw = np.load("inner_race_fault.npy") # 一维原始振动信号 segments = segment_signal(raw) loader = DataLoader(VibrationDataset(segments), batch_size=64, shuffle=True)

这里有个容易被忽略的细节:unsqueeze(1)把每个样本变成 (1, 1024),对应一维卷积的输入通道格式 (batch, channels, length)。DataLoader的shuffle=True不能省,WGAN 对样本顺序比普通 GAN 更敏感,如果同批次全是同一转速段的样本,批评者估计的 Wasserstein 距离会偏差很大。

提示:如果你的原始数据是多个文件(不同负载、不同转速),不要全部混在一起切,每个文件单独切段后再合并。转速不同会导致故障特征频率整体偏移,混合训练会让生成器学成“四不像”。

3.2 生成器与判别器网络定义:一维转置卷积与步长为 2 的下采样

生成器的输入是 100 维高斯噪声,输出是 1024 点的振动序列。网络骨架用转置卷积逐级上采样,每次把长度翻倍:16 → 32 → 64 → 128 → 256 → 512 → 1024,共 6 层。每层后面接 BatchNorm1d 和 ReLU,最后一层用 Tanh 把输出压到 [-1, 1]——因为输入数据标准化后幅值基本落在 ±3 以内,Tanh 和 z-score 是配套的。

import torch.nn as nn class Generator1D(nn.Module): def __init__(self, z_dim=100, output_len=1024): super().__init__() self.init_len = output_len // 64 # 16 self.fc = nn.Linear(z_dim, 128 * self.init_len) self.deconv = nn.Sequential( nn.ConvTranspose1d(128, 64, 4, stride=2, padding=1), nn.BatchNorm1d(64), nn.ReLU(inplace=True), nn.ConvTranspose1d(64, 32, 4, stride=2, padding=1), nn.BatchNorm1d(32), nn.ReLU(inplace=True), nn.ConvTranspose1d(32, 16, 4, stride=2, padding=1), nn.BatchNorm1d(16), nn.ReLU(inplace=True), nn.ConvTranspose1d(16, 8, 4, stride=2, padding=1), nn.BatchNorm1d(8), nn.ReLU(inplace=True), nn.ConvTranspose1d(8, 4, 4, stride=2, padding=1), nn.BatchNorm1d(4), nn.ReLU(inplace=True), nn.ConvTranspose1d(4, 1, 4, stride=2, padding=1), nn.Tanh() ) def forward(self, z): x = self.fc(z).view(z.size(0), 128, self.init_len) return self.deconv(x)

逻辑说明:fc先把 100 维噪声映射到 128×16=2048 维,然后 reshape 成 128 个通道、长度 16 的特征图,后续每层转置卷积把长度翻倍。kernel_size=4、stride=2、padding=1 是转置卷积“翻倍长度”的标准配置,output_len必须是 64 的倍数才能整除到 16,这也是我选 1024 而不是 1000 的直接原因。

判别器(critic)是生成器的镜像,用普通卷积逐层下采样,把长度从 1024 压到 1。注意两点:用 LeakyReLU 而不是 ReLU,避免 Dead Neuron;最后一层不做 Sigmoid,直接输出一个标量。

class Critic1D(nn.Module): def __init__(self): super().__init__() self.conv = nn.Sequential( nn.Conv1d(1, 16, 4, stride=2, padding=1), nn.LeakyReLU(0.2), nn.Conv1d(16, 32, 4, stride=2, padding=1), nn.LeakyReLU(0.2), nn.Conv1d(32, 64, 4, stride=2, padding=1), nn.LeakyReLU(0.2), nn.Conv1d(64, 128, 4, stride=2, padding=1), nn.LeakyReLU(0.2), nn.Conv1d(128, 1, 4, stride=2, padding=1), ) def forward(self, x): # x: (B, 1, 1024) return self.conv(x).mean(dim=(1, 2)) # scalar per sample

mean(dim=(1,2))把最后一层输出的所有值取平均,等效于一个全局池化。这个操作能显著减少参数量,也让 critic 对输入长度的适配更灵活,是 WGAN 实现里的常见做法。

3.3 WGAN-GP 的损失函数与梯度惩罚实现

WGAN 的原始论文用权重裁剪(weight clipping)来约束 Lipschitz 条件,但裁剪值不好调,裁剪狠了梯度消失、裁剪松了约束失效。WGAN-GP 用梯度惩罚替代:在真实样本和生成样本的连线上随机取插值点,要求批评者在该点的梯度范数尽量接近 1。实现时需要对插值点求二阶梯度,涉及torch.autograd.grad,这是新手最容易被绊倒的地方。

def compute_gradient_penalty(critic, real, fake): batch_size = real.size(0) eps = torch.rand(batch_size, 1, 1, device=real.device) # 在 real 和 fake 的连线上随机插值 interpolated = (eps * real + (1 - eps) * fake).requires_grad_(True) output = critic(interpolated) grad = torch.autograd.grad( outputs=output, inputs=interpolated, grad_outputs=torch.ones_like(output), create_graph=True, retain_graph=True )[0] grad = grad.view(batch_size, -1) grad_norm = grad.norm(2, dim=1) return ((grad_norm - 1) ** 2).mean()

逻辑说明:eps从均匀分布 U(0,1) 采样,插值点介于真实样本与生成样本之间;requires_grad_(True)必须显式打开,否则autograd.grad会报“not part of the graph”的错误。create_graph=True是为了让梯度惩罚本身也能被反向传播,否则整个判别器损失在 backward 时会断掉。最后(grad_norm - 1)^2是惩罚项,等价于要求 critic 的梯度范数处处接近 1,超参数 λ(通常取 10)在外面乘。

3.4 训练循环主体:n_critic 与损失方向

WGAN 的训练节奏是“批评者多走几步,生成者走一步”。每个 batch 内,先冻结生成器、更新 critic 5 次,然后冻结 critic、更新生成器 1 次。生成器的损失是-critic(fake)的均值,即它希望 fake 的评分越高越好。

import torch.optim as optim device = torch.device("cuda" if torch.cuda.is_available() else "cpu") generator = Generator1D().to(device) critic = Critic1D().to(device) # WGAN 经验配置:Adam 的 lr 取 1e-4,betas 用 (0.5, 0.9) opt_g = optim.Adam(generator.parameters(), lr=1e-4, betas=(0.5, 0.9)) opt_c = optim.Adam(critic.parameters(), lr=1e-4, betas=(0.5, 0.9)) n_critic = 5 lambda_gp = 10.0 z_dim = 100 for epoch in range(200): for real_batch in loader: real_batch = real_batch.to(device) # 1. 更新 critic:n_critic 次 for _ in range(n_critic): z = torch.randn(real_batch.size(0), z_dim, device=device) fake_batch = generator(z) loss_c = (torch.mean(critic(fake_batch)) - torch.mean(critic(real_batch)) + lambda_gp * compute_gradient_penalty(critic, real_batch, fake_batch)) opt_c.zero_grad() loss_c.backward() opt_c.step() # 2. 更新 generator:1 次 z = torch.randn(real_batch.size(0), z_dim, device=device) fake_batch = generator(z) loss_g = -torch.mean(critic(fake_batch)) opt_g.zero_grad() loss_g.backward() opt_g.step() if epoch % 20 == 0: print(f"Epoch {epoch} | critic loss: {loss_c.item():.3f} | gen loss: {loss_g.item():.3f}") # 保存生成的样本,供后续频域验证 torch.save(fake_batch.detach().cpu(), f"gen_samples_{epoch}.pt")

参数说明:n_critic=5是最常用取值,如果 critic 的 loss 一直剧烈震荡,可以试着提到 10;Adam 的betas用 (0.5, 0.9) 而不是默认的 (0.9, 0.999),因为一阶动量系数 0.9 在 GAN 里容易让梯度历史积累过长,导致训练震荡。z_dim=100是经验值,太小的 z 维度会让生成样本多样性不足,太大又增加生成器的映射难度。

WGAN 的 loss 数值不像普通 GAN 那样有明确的分数含义,它只在相对比较时有意义。如果你看到 critic loss 稳定在某个负数附近、生成器 loss 稳定为正,这是正常的;真正需要警惕的是 loss 绝对值持续上涨或出现 NaN。到这里一个最小可跑的训练流程已经完整了,接下来讲那些调参时才真正决定成败的参数。

4. 影响生成质量的四个必调参数:信号长度、n_critic、λ 与 batch size

4.1 信号长度与 z 维度:时间窗口决定可分辨的故障频率下限

信号长度 1024 不是拍脑袋定的,它直接决定 FFT 的频率分辨率。采样率 fs=12 kHz 时,1024 点的分辨率是 fs/N ≈ 11.7 Hz。轴承内圈故障特征频率通常在 100~500 Hz 之间,11.7 Hz 的分辨率足够区分内圈故障(约 162 Hz)和外圈故障(约 107 Hz)。如果把信号长度缩到 256,分辨率变成 46.9 Hz,两个故障特征频率直接糊在一起,生成器根本学不到区分度。

z 维度影响的是生成样本的多样性空间。我做过一个对比实验:z_dim=10 时,生成样本的主频集中在两三个固定值上,包络谱几乎重合;z_dim=100 时,主频有 ±5 Hz 的合理抖动,更接近真实信号的自然波动。z 维度再往上加收益递减,反而让生成器需要更多 epoch 才能收敛。

4.2 n_critic 与学习率:批评者不能永远强于生成者

n_critic 控制 critic 每轮训练领先生成器的步数。WGAN 的理论要求 critic 先充分收敛,才能给出准确的 Wasserstein 距离估计,但实战中 n_critic 太大会导致生成器梯度信号过早消失——批评者强到完美区分真假,生成器怎么更新都骗不过它,loss 卡死。我的一般做法是:初始设 5,如果生成器 loss 长时间不下降(超过 30 个 epoch 毫无变化),把 n_critic 降到 3 或 2,给生成器更多更新机会。

学习率这里单独说,因为它是全流程最玄学的一个超参。Adam 默认 lr=1e-3 在 WGAN 上几乎必炸,loss 会在前几百个 iteration 里冲到 NaN。WGAN-GP 论文推荐的 lr=1e-4 不是随意的,梯度惩罚项的量级和普通 BCE 损失差一个数量级,lr 太大会让惩罚项主导更新方向,把网络权重推向震荡。降 lr 是最优先尝试的止损手段,比调任何网络结构都管用。

4.3 梯度惩罚系数 λ 与 batch size 的相互作用

λ 控制 Lipschitz 约束的强度。论文里的标准值是 10,但如果你发现生成样本的频谱“过度平滑”(所有频率成分幅值几乎相等,像白噪声),很可能是 λ 偏大,把 critic 约束得太死,梯度信号失去了对不同频率成分的区分度。反过来如果 critic loss 持续上涨、生成样本出现尖锐的单一频率峰,λ 可能偏小。我一般在 5~20 之间网格搜索,这个范围已经覆盖了绝大多数工况。

batch size 的影响隐蔽但致命。WGAN-GP 的梯度惩罚是对每个样本独立计算的,batch size 越小,单个 batch 的惩罚项估计方差越大,训练曲线抖动越明显。我用 32 的 batch 训练时,critic loss 曲线像心电图;换到 64 后明显平滑。如果你的 GPU 显存紧张,优先保 batch size,宁可把网络通道数减半,也别把 batch 降到 32 以下。

4.4 训练轮数与模型保存策略:不要等到最后一轮才留后手

WGAN 不像分类任务那样有明确的收敛点,生成质量随训练轮数先升后降,降的那一段就是过拟合的翻车现场。我的做法是每 20 个 epoch 保存一次生成样本,并顺带计算生成样本和真实样本在频域上的 MMD(最大均值差异),一旦 MMD 开始回升,立即回滚到前一个 checkpoint。这个“后悔药”机制看起来笨,但比任何早停策略都可靠,因为你永远不知道最优轮数在哪里。

训练轮数方面,200 epoch 是起点不是终点。小数据集(几千个片段)大概 100 到 150 轮就能学到像样的包络形状,但要让冲击成分的周期抖动也真实,300 轮以上更稳妥。关键是观察生成样本的包络谱而不是盯着 loss 看,loss 平稳不等于生成质量好。

5. WGAN 生成轴承信号的 5 个高频踩坑与排查清单

5.1 critic loss 不降反升,生成器 loss 直接变 NaN

现象:训练刚开始几十个 iteration,critic loss 从 -5 一路涨到 +50,随后日志里出现 inf 或 NaN,程序报梯度溢出。

原因:最常见的是学习率过大,Adam 默认 lr=1e-3 配上梯度惩罚项后,权重更新步长超出合理范围,梯度在反向传播时数值爆炸。第二个常见原因是数据里混入了 NaN 或幅值异常大的样本(比如传感器掉电瞬间的尖峰),z-score 标准化后标准差接近 0,导致该样本数值异常。

解决:先把 lr 降到 1e-4,这是 WGAN-GP 最稳的起点。如果还炸,用torch.nan_to_num兜底梯度,再检查原始数据里是否有超过 5 倍标准差的离群点,直接剔除。我的排查顺序永远是“数据干净度 → 学习率 → 网络初始化”,别一上来就改结构。

5.2 生成信号几乎全是高频噪声,包络谱没有明显的故障特征峰

现象:生成的波形在时域上是密密麻麻的毛刺,FFT 之后频谱平平的,没有集中在某个频段,包络谱上压根找不到内圈故障特征频率。

原因:生成器把噪声直接映射成了白噪声,说明它没有学到信号的结构信息,只学会了匹配均值方差。这往往是批评者太弱或梯度惩罚过强导致的——批评者对真实样本和生成样本的区分信息没有有效传递回生成器。另一个很现实的原因是训练轮数不够,WGAN 生成器在前 80 轮本来就容易输出噪声,这是正常过程。

解决:先排除轮数问题,把训练延长到 200 轮再看。如果仍是白噪声,检查compute_gradient_penalty的create_graph是否正确,很多复制粘贴的代码在梯度惩罚上少了这一个参数,导致批评者的梯度信号是断开的,生成器拿不到有效反馈。再把 λ 从 10 调到 5,放宽对批评者的约束。

5.3 三种故障类型最终只生成一种(模式崩塌的变种)

现象:同时训练内圈、外圈、滚动体三类故障数据时,生成样本的包络谱全部指向同一类故障,其他类别凭空消失。原始 GAN 里这个现象叫 mode collapse,WGAN 里不常见但会以“critis 太强”的形式出现。

原因:WGAN 缓解但没根治模式崩塌,当某一类故障样本的数量或幅值特征占主导时,critic 估计的 Wasserstein 距离被该类别主导,生成器发现“生成这一类能最大程度降低损失”,于是放弃了其他类别。

解决:先统计三个类别的样本数量和标准化后的 RMS 幅值,保证均衡。训练时每个 batch 强制从三个类别中各取 1/3 样本,而不是全局随机采样。如果幅值差异大,按类别单独做标准化,而不是全部混在一起算均值和方差。这是我在实际项目中遇到的真实翻车现场,最后就是用类别均衡采样解决的。

5.4 生成信号的故障特征频率总比真实值偏 5~8 Hz

现象:真实内圈故障特征频率是 162.5 Hz,生成的信号包络谱峰值在 157 Hz 左右,且每次训练偏移量不同。时域波形肉眼看没问题,一算包络谱就对不上。

原因:频率偏移主要来自两个地方。一是信号长度 1024 导致 FFT 分辨率只有 11.7 Hz,峰值落在哪根谱线上有量化误差;二是生成器在拼接局部片段时,冲击成分的周期抖动引入了额外调制,让包络谱的主峰发生偏移。前者是数学误差,后者是模型误差。

解决:把信号长度加到 2048,分辨率提升到 5.9 Hz,偏移量减半。如果还偏,用重心法读取峰值频率——在峰值附近取 3 个谱线做加权平均,能进一步把误差压到 1 Hz 以内。这是纯后处理技巧,不需要改网络。

5.5 同一份代码两次训练结果完全不一样

现象:相同的超参数、相同的数据集,第一次生成的信号有清晰冲击特征,第二次全是噪声,或者两次的主频峰位置不同。

原因:PyTorch 默认的随机初始化、DataLoader shuffle 的随机顺序、GPU 上的非确定性算法,三者叠加让训练过程成为一个随机过程。WGAN 对初始化状态尤其敏感,不同初始化可能收敛到完全不同的局部最优。

解决:在训练脚本开头固定三处随机种子:torch.manual_seed(42)、np.random.seed(42)、random.seed(42),并在加载 DataLoader 时设置generator=torch.Generator().manual_seed(42)。这是工业落地的血泪经验——没有固定种子,任何超参数对比实验都是玄学,你根本分不清效果差异来自参数还是来自随机性。

6. 验证生成信号:用频域包络谱和诊断模型回测证明它真的能用

生成信号好不好看不算数,能提升诊断模型性能才算数。我会做三件事来验证:包络谱对比看故障特征频率是否对齐,t-SNE 看生成样本与真实样本在特征空间的分布是否重叠,最后用诊断模型回测看加入生成数据后准确率的变化。

包络谱对比是最直观的验证:对生成信号做 Hilbert 变换取包络,再对包络做 FFT。

from scipy.signal import hilbert import numpy as np def envelope_spectrum(signal, fs=12000): analytic = hilbert(signal) env = np.abs(analytic) spec = np.abs(np.fft.rfft(env)) freqs = np.fft.rfftfreq(len(env), 1 / fs) return freqs, spec

把真实信号和生成信号的包络谱叠在一张图上,重点看故障特征频率(如 162.5 Hz)处是否有对齐的峰值。误差在 3 Hz 以内说明生成器学到了正确的冲击节奏,否则回到第 5.4 节排查。

t-SNE 验证更省事:把真实样本和生成样本分别用同一个预训练特征提取器编码,再用sklearn.manifold.TSNE投影到二维平面,观察两组点云是否混在一起。如果生成样本聚成一团离真实样本很远,说明生成器学到的是另一种“看起来像”的分布,不是真正的故障分布。

最后做回测:用真实故障样本训一个简单的一维 CNN 分类器,测试集固定不变;然后在训练集里混入 30% 生成样本重新训练,对比测试集准确率。涨点 3~5 个百分点是正常收益,不涨甚至下降就要怀疑生成样本的质量——这种情况我遇到一次,后来发现是生成样本全是同一类别的模式崩塌,混入训练集反而加剧了类别不平衡。

这三步做完,心里就有底了。最终能支撑你上线跑的,不是生成波形和真实信号有多像,而是加入生成数据之后诊断模型在真实场景的泛化能力有没有实质提升。到现在我仍然会在每次换数据集之后跑一遍这三步验证,留着包络谱图和准确率对比表当过程记录。这套流程踩过的坑实在太多,从最初的白噪声生成器到后来能稳定涨点,每一步都是在碎掉的 loss 曲线里趟出来的。希望帮到你,少走几段弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询