资讯详情

GAN-1D生成故障轴承振动信号:小样本诊断的补数据路径

📅 2026/11/2 2:22:13 | 华诺云谱 👁 阅读
GAN-1D生成故障轴承振动信号:小样本诊断的补数据路径
简介这份 Python 项目围绕一维故障轴承振动信号的生成任务展开基于 WGAN 构建 GAN-1D 模型面向具备一定深度学习基础、从事机械故障诊断或信号数据增强的开发者与研究人员。它可用于在真实故障样本不足时扩充振动数据并通过训练脚本中的学习率、迭代轮数、采样间隔及 9 类信号选择等参数观察生成效果与模型收敛过程。压缩包共 19 个文件、约 22.03MB包含训练与测试用的 Python 脚本、MAT 格式振动数据、TensorFlow 模型检查点、生成样例图片以及说明文档便于直接复现实验并对照生成图像判断质量。已有 3387 人学习下载说明该方向具有较高关注度。读者可据此理解 WGAN 在一维轴承信号上的建模流程掌握数据准备、模型训练、采样可视化与结果评估的基本思路也可将脚本作为故障诊断数据扩充的起点按自己的数据与实验条件调整参数继续研究。1. GAN-1D 生成故障轴承振动信号几十条实测样本撑不起诊断模型时的一条补数据路径GAN-1D 说的是用一维卷积搭一套 WGAN直接生成故障轴承振动信号而不是生成图片。它出现的场景通常很朴素某台设备上只采到几十段带标签的故障冲击训练集小到诊断模型刚起步就过拟合于是想用生成模型把样本量撑起来。这条路能走通的前提是把振动信号的物理形态摸清——采样率、切片长度、幅值区间、故障特征频率一个没对齐生成出来的东西在时域上看着像包络谱上却是一片平。下面按实际跑通的顺序讲信号长什么样、为什么选 WGAN、代码怎么落、踩坑在哪、参数边界怎么定。2. 一维振动信号与二维图像差在哪切片形状、归一化区间和 WGAN 选型的三笔账把图像 GAN 的代码改成处理振动信号改完之后直接训练十有八九会在频域评估这一关翻车。时域损失降下去了波形看着也有冲击节奏但把生成样本丢进包络谱一看该出峰的地方没有峰。问题多半不在生成器结构上而在“你把这段信号当成什么”这件事上振动信号是一维时序它的可解释性全在频域里采样率、切片长度、归一化区间这三笔账必须在写模型之前算清楚。2.1 从 1024 点到 2048 点一段轴承振动样本该切多长轴承振动通常用加速度计采采样率取 12 kHz 或 25.6 kHz。12 kHz 覆盖到 6 kHz 带宽够把 2 到 5 kHz 这段结构共振带包进来——故障冲击激起的正是这段共振包络解调要靠它当载波。采样率定低了共振带被折叠进来包络谱上就会多出一堆来路不明的峰那时候你连真假都分不清更别说判断生成质量。切片长度不是随手定的数它直接决定频率分辨率Δf fs / N。12 kHz 采样、1024 点一段分辨率 11.7 Hz2048 点是 5.86 Hz4096 点是 2.93 Hz。外圈故障特征频率在 1500 r/min 下大致落在 80 到 160 Hz内圈略高两侧还挂着转频边带。分辨率 11.7 Hz 时特征频率和它旁边 ±转频 的边带会糊在一起包络谱上看到的是一坨抬起来的宽带包不是一根能指着说“这就是外圈特征频率”的尖峰。我一般从 2048 点起步4096 点更保险代价是同样长度的原始记录切出来的样本数减半。样本本来就只有几十条的时候这个代价不小所以要权衡类别样本数低于 100 条用 2048高于 300 条可以上 4096 换更好的频率分辨率。重叠率也影响成品质量。我固定 50% 重叠2048 点一段、步长 1024。不重叠的话相邻样本首尾相接边界处的冲击被劈成两半等于人为制造了一批“半截冲击”的坏样本生成器会把这些断头冲击当成正常形态学进去。重叠能缓解但不能彻底解决更干净的做法是先按整段记录粗切、再在段内滑窗。2.2 为什么用 WGAN 而不是原始 GAN判别器丢掉 sigmoid 之后发生了什么原始 GAN 的判别器输出过一个 sigmoid损失等价于优化 JS 散度。问题出在真实分布和生成分布的支撑集几乎不重叠——2048 维空间里几十上百条样本撑不起任何有体积的流形判别器几轮就能把真假分到接近 100% 置信JS 散度饱和成常数生成器拿到的梯度趋近于零。表现出来就是判别器损失很快掉到 0 附近生成器损失纹丝不动采样出来还是初始噪声。WGAN 换成 Wasserstein-1 距离判别器不再叫判别器叫 critic最后一层去掉 sigmoid直接输出一个实数打分。这个距离在两个分布不重叠时依然有定义、依然给得出有意义的梯度对小样本、单类别的轴承信号特别关键。代价是要给 critic 加约束让它满足 1-Lipschitz 连续。约束有两种实现。原始 WGAN 用权重裁剪把 critic 的每个参数夹到 [-0.01, 0.01]实现简单但容易把容量裁没critic 学成一个很钝的函数生成质量上不去。WGAN-GP 改成梯度惩罚在真假样本的连线上随机插值要求 critic 在插值点上的梯度范数接近 1超出的部分按平方罚。它对批内每个样本单独算梯度范数不依赖跨样本统计所以 critic 里不能放 BatchNorm——放了梯度惩罚基本失效。选 WGAN-GP 还有个实际好处critic 的损失近似反映两个分布之间的 Wasserstein 距离它下降通常意味着生成在变好。虽然这个相关性也没那么可靠但比原始 GAN 里两个都看不懂的损失强。我一般同时看真实样本打分和生成样本打分的均值差这个差值比损失曲线本身直观。2.3 生成器与判别器的通道账本一维卷积堆叠怎么排GAN-1D 的骨架说到底就是把图像 GAN 里的 Conv2d 换成 Conv1d、上采样换成 ConvTranspose1d但有两处不能照搬。第一处是长度账要算准一维转置卷积的输出长度是 L_out (L_in - 1) × stride - 2 × padding kernel_size配 kernel8、stride4、padding2 时正好放 4 倍三次就放 64 倍。32 点起步三次之后是 2048 点这是整个结构能对上的前提。第二处是归一化层。生成器里可以用 BatchNorm1d批大小 64 时统计量够稳critic 里不能用换 LayerNorm 或者干脆不加。有些实现用 InstanceNorm1d效果也可以它对每个样本每个通道单独归一化不跨样本不破坏梯度惩罚的前提。latent 维度一般取 64取 100 也行主要影响生成多样性和训练前期收敛速度对最终包络谱的形状影响没那么大。生成器先用一个 Linear 把 64 维映射到 base×4×32 再 reshapebase 取 64 时是 256 通道 × 32 点然后 256→128→64→1通道逐层减、长度逐层翻。判别器完全镜像1→64→128→256长度 2048→512→128→32最后 Flatten 接一个 Linear 到标量。通道数下限大约 32再低生成信号的细节撑不住包络谱上既没有毛刺也没有峰上限受显存约束2048 点、base128 时单卡 batch 64 已经吃紧。核宽 8 是折中值核太小4感受野不够冲击的包络形状学不出来核太大16 以上参数涨得快几十条样本下更容易过拟合到训练集那一小撮形态上。3. 把 GAN-1D 跑通的最小闭环切片归一化、模型搭骨架、梯度惩罚训练一套能跑通的实现其实不到两百行但每一段都有坑。下面按数据→模型→训练的次序给三段代码每段后面说清楚参数怎么改、改错了会怎样。代码假设单卡环境输入是已经按整段记录整理好的一维数组长度随意切窗在函数里做。3.1 切片、去均值与分位数归一化三行预处理决定后面能不能收敛import numpy as np def slice_signal(sig, win2048, step1024, clip_pct99.0): 把一条长振动记录切成定长样本返回 (N, win) 的 float32 数组。 sig np.asarray(sig, dtypenp.float32) starts range(0, len(sig) - win 1, step) seg np.stack([sig[i:i win] for i in starts]) # 1) 去均值消掉传感器直流偏置否则 critic 靠这一个维度就能分真假 seg seg - seg.mean(axis1, keepdimsTrue) # 2) 分位数缩放用 99 分位而不是最大值抗削波野点 scale np.percentile(np.abs(seg), clip_pct, axis1, keepdimsTrue) 1e-8 seg seg / scale # 3) 裁到 [-1, 1]和生成器最后一层 tanh 的值域严格对齐 return np.clip(seg, -1.0, 1.0)win决定频率分辨率12 kHz 下 2048 对应 5.86 Hzstep控制重叠率取win // 2就是 50% 重叠clip_pct是抗野点的关键取 100 就等于用最大值缩放一段里只要有一个削波尖峰整段幅值被压扁峭度信息全丢。分位数取 99 到 99.5 之间比较稳取太低95会把真实的冲击幅值一起削掉。注意这里丢掉了每段的scale实际训练里要把它存下来。反归一化的时候x x_norm * scale不存回来生成样本就只有形状没有量纲拿去和真实信号做幅值对比会得出完全错误的结论。3.2 生成器与判别器的 PyTorch 实现转置卷积从 32 点长到 2048 点import torch import torch.nn as nn class Generator1D(nn.Module): latent z - 2048 点一维振动信号输出值域 [-1, 1] def __init__(self, z_dim64, base64, out_len2048): super().__init__() assert out_len 32 * 64, 长度账对不上改 base 或改上采样次数 self.base base self.fc nn.Linear(z_dim, base * 4 * 32) self.net nn.Sequential( nn.ConvTranspose1d(base * 4, base * 2, 8, 4, 2), # 32 - 128 nn.BatchNorm1d(base * 2), nn.ReLU(True), nn.ConvTranspose1d(base * 2, base, 8, 4, 2), # 128 - 512 nn.BatchNorm1d(base), nn.ReLU(True), nn.ConvTranspose1d(base, 1, 8, 4, 2), # 512 - 2048 nn.Tanh(), ) def forward(self, z): h self.fc(z).view(z.size(0), self.base * 4, 32) return self.net(h) class Critic1D(nn.Module): 输出标量打分不带 sigmoid内部不出现 BatchNorm def __init__(self, base64): super().__init__() self.net nn.Sequential( nn.Conv1d(1, base, 8, 4, 2), # 2048 - 512 nn.LeakyReLU(0.2, True), nn.Conv1d(base, base * 2, 8, 4, 2), # 512 - 128 nn.LeakyReLU(0.2, True), nn.Conv1d(base * 2, base * 4, 8, 4, 2), # 128 - 32 nn.LeakyReLU(0.2, True), nn.Flatten(), nn.Linear(base * 4 * 32, 1), ) def forward(self, x): return self.net(x).squeeze(-1)生成器第一层ConvTranspose1d的 kernel/stride/padding 三件套决定了放大倍数改成 kernel4、stride2、padding1 就是每次放 2 倍需要多堆一层才能到 2048参数量和显存都不一样。base是最该调的旋钮32 起步看能不能收敛能收敛再往 64、128 加。critic 里的LeakyReLU(0.2)斜率别随手改成 0.01太小的话负半轴几乎没梯度梯度惩罚会变得很不稳。3.3 梯度惩罚和训练循环n_critic 与 λ 怎么写进代码import torch def gradient_penalty(critic, real, fake, devicecuda): b real.size(0) eps torch.rand(b, 1, 1, devicedevice) # 每个样本一个插值系数 x_hat (eps * real (1 - eps) * fake).requires_grad_(True) d_hat critic(x_hat) grad torch.autograd.grad( outputsd_hat, inputsx_hat, grad_outputstorch.ones_like(d_hat), create_graphTrue, # 惩罚项本身要参与反向传播漏了就白算 retain_graphTrue, only_inputsTrue, )[0] grad grad.view(b, -1) return ((grad.norm(2, dim1) - 1.0) ** 2).mean() # 逐样本求范数不取 batch 平均 def train_step(G, D, opt_g, opt_d, real, z_dim64, n_critic5, lam10.0, devicecuda): bs real.size(0) for _ in range(n_critic): z torch.randn(bs, z_dim, devicedevice) fake G(z).detach() loss_d D(fake).mean() - D(real).mean() lam * gradient_penalty(D, real, fake, device) opt_d.zero_grad(set_to_noneTrue) loss_d.backward() opt_d.step() z torch.randn(bs, z_dim, devicedevice) loss_g -D(G(z)).mean() # 生成器只跟 critic 的打分走 opt_g.zero_grad(set_to_noneTrue) loss_g.backward() opt_g.step() return loss_d.item(), loss_g.item()n_critic5意思是每更新一次生成器critic 先连算 5 步目的是让 critic 尽量贴近当前分布下的最优解再给生成器一个靠谱的梯度方向。lam10是梯度惩罚的经典起点。优化器用 Adam学习率 1e-4betas 取 (0.5, 0.9)——beta1 用默认的 0.9 在 WGAN 里往往会拖慢收敛动量太大让 critic 在插值点上抖。fake G(z).detach()这一句容易被漏掉。不 detach 的话critic 那几步的反向传播会把梯度一路传回生成器等于用 critic 的损失偷偷训了生成器两个优化目标打架训练曲线会变得完全没法读。3.4 训练过程盯什么三个必须打的日志指标只打印 loss_d 和 loss_g 是不够的这两个数在 WGAN 里没有绝对量级只能看趋势。我固定打三样东西指标计算方式健康区间异常时的第一反应Wasserstein 估计D(real).mean() - D(fake).mean()前期快速下降后缓慢收敛一路上升说明 critic 过强或 λ 太小插值点梯度范数惩罚项里 norm 的均值0.8 ~ 1.2远大于 1 说明约束没生效查 create_graph生成样本峭度每批生成样本峭度的中位数4 ~ 8故障冲击接近 3 说明生成成了高斯噪声峭度这一项特别管用。健康轴承的振动峭度在 3 附近带冲击的故障信号峭度能到 4 以上甚至十几。生成样本峭度长期贴着 3基本可以判定模型没学到冲击成分继续训下去也是浪费时间不如回头查归一化和切片。4. 训练不稳时的排查清单五类翻车现场与对应处置WGAN 的调试体验有点像玄学同一个脚本换一批数据就崩。但崩的方式是有规律的下面五类是我反复遇到的按“现象—原因—解决”整理遇到问题时从上往下对一遍多数能定位到具体那几行。4.1 生成信号像白噪声归一化区间和 tanh 输出没对齐现象训练几百步后采样波形是一条均匀的毛刺带幅值铺满 [-1, 1]峭度接近 3包络谱全平。原因预处理用了 min-max 缩放到 [0, 1]而生成器最后一层是 tanh 输出 [-1, 1]。两边差一个偏置critic 学到的第一个特征就是“整体直流偏了多少”——真实样本永远偏 0.5生成样本永远偏 0靠这一个维度就能把真假分开剩下的形状信息完全没必要学。生成器于是退化成输出接近常数的噪声。解决训练前把数据统一到 [-1, 1]去均值加分位数缩放两步都不能省。改完之后重新初始化模型再训别在已经塌掉的权重上接着跑学不到东西。4.2 critic 损失一路俯冲、生成器梯度爆掉现象loss_d 前 100 步正常之后线性下坠到 -30、-50采样出来的幅值炸到饱和峭度飙到几十。原因梯度惩罚里的torch.autograd.grad忘了create_graphTrue惩罚项变成一个常数没有梯度等于完全没加约束critic 退化成可以任意陡的函数另一种情况是梯度范数按整个 batch 求了一个标量再平方惩罚强度被平均稀释批内少数几个陡点没被压住。解决create_graphTrue和retain_graphTrue都写上grad.view(b, -1).norm(2, dim1)逐样本求范数再平方取均值。改完先把 λ 按 10 跑观察梯度范数是否收敛到 1 附近。4.3 包络谱里找不到故障特征频率现象时域看着挺像冲击节奏也对得上但包络谱上目标特征频率处没有峰或者峰位偏了几赫兹。原因切片太短分辨率不够1024 点在 12 kHz 下只有 11.7 Hz特征频率和转频边带糊成一团也可能是生成器容量太小2 到 5 kHz 的共振带学不出来包络解调失去载波解出来自然是平的。解决切片拉长到 2048 或 4096base从 32 提到 64训练步数加到两万步以上。评估时真假样本必须走同一条包络谱流程同样的 Hilbert 变换、同样的窗函数、同样的点数否则差异可能来自评估代码而不是模型。4.4 多类故障混训后串味现象把外圈、内圈、滚动体三类混在一个生成器里训生成样本的包络谱上同时冒出好几个类别的特征频率哪一类都不像。原因latent 是连续无标签的模型学到的是各类分布的“平均”也就是模式平均mode averaging。加上样本本来就少不同类别的冲击周期差异被抹平。解决按类别分开训各自的 GAN-1D一个类别一套权重最省事也最稳。真要做条件生成就把类别 one-hot 拼进 latentcritic 的输入也带上条件标签让它对每类分别打分——这样条件下的 Wasserstein 距离才是你真正要优化的东西。4.5 第几千步突然 NaN现象loss 正常走几千步某个 step 之后突然变成 NaN之后所有权重全废重启也复现不了同一位置。原因原始记录里有传感器削波造成的野点99 分位缩放压不住或者梯度惩罚里norm除到了接近 0 的值也可能是开了混合精度autograd.grad在 fp16 下数值不稳。解决训练前对原始记录做一次幅值筛查超过 5 倍标准差且持续若干点的段直接丢掉norm后面加 1e-12梯度惩罚这条路径全程 float32不要套 autocast。5. 参数边界在哪里n_critic、λ、latent 维度和通道数的取值区间参数表网上一搜一大把但大多是从图像 GAN 直接搬过来的放到 2048 点的一维振动信号上未必成立。下面这几个区间是在一维信号上试出来的经验边界给的是“能跑”的范围而不是“最优”具体到某批数据还是要自己扫一遍。5.1 n_critic 从 5 到 10多算几步 critic 到底换来什么n_critic控制每个生成器 step 之前 critic 更新几次。取 1 就是标准 GAN 的交替训练取 5 是 WGAN 论文的推荐值取 10 以上在小样本上反而有害。原因在于 critic 越接近最优给的梯度方向越准但代价是每一步的墙钟时间线性增长。一维信号样本量小几百到几千条critic 很容易在 3 到 5 步内就收敛到当前分布的最优附近再往上加只是在重复计算。我一般在样本数少于 500 条时用 5多于 2000 条时用 3——样本多的时候每一步的批内信息量大critic 单步就能学到足够多。判断n_critic设得对不对看 Wasserstein 估计的曲线设得太小曲线锯齿明显、上下跳设得合适曲线是平滑下降的设得太大前期下降很慢因为生成器半天才更新一次。5.2 λ 与批大小的联动10 不是万能数梯度惩罚系数 λ 控制 Lipschitz 约束的强度。WGAN-GP 原文取 10这个值在图像任务上很稳在一维振动信号上通常也没问题但它和批大小是有联动的。批大小越小插值点的采样越稀疏梯度范数的估计方差越大这时候 λ 用 10 会让训练变得很抖。批本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑