WGAN生成轴承故障振动信号:从数据不足到样本扩充的实战指南

发布时间:2026/9/29 19:00:57

WGAN生成轴承故障振动信号:从数据不足到样本扩充的实战指南 简介这份资源面向从事轴承故障诊断、信号处理与深度学习研究的工程师及学生提供一套基于WGAN生成一维故障轴承振动信号的完整Python实现用于缓解故障样本稀缺、类别不平衡等问题适合具备一定TensorFlow基础的中高级读者复现与二次开发。压缩包共19个文件约22.03MB包含2个py训练与测试脚本、2个mat数据文件、9张png生成效果图以及checkpoint、meta、index、data等模型权重与训练状态文件另附md与readme说明文档便于直接运行与结果对照。资源要求Python 3.5以上、TensorFlow GPU及numpy、scipy等环境支持通过命令行调整学习率、训练轮数、采样频率并可在9类信号中切换训练数据。目前已有3386人学习下载读者可借此掌握WGAN在一维振动信号生成中的网络结构、训练流程与评估方式快速搭建数据增强实验为故障诊断模型提供扩充样本与排错参考。1. 用WGAN生成故障轴承振动信号从“数据不够”到“样本管够”的落地路径设备故障诊断最头疼的问题从来不是算法不够先进而是故障样本太少。一台正常运转的轴承你可能连续采集几个月都碰不到一次内圈剥落或外圈裂纹但深度学习模型偏偏需要大量故障样本来训练。更麻烦的是不同工况下的振动信号分布差异极大实验室台架上采集的故障数据搬到产线上往往直接翻车。GAN生成对抗网络的出现给了一条新路既然真实故障样本难获取那就让网络自己“造”出以假乱真的振动信号。而WGANWasserstein GAN相比原始GAN在训练稳定性和生成质量上有明显优势尤其适合振动信号这种一维时序数据。这篇内容面向已经了解轴承故障诊断基本流程、想用WGAN扩充故障样本集的工程师从原理选型讲到代码复现再到参数调优和踩坑记录争取让你看完就能在自己的数据集上跑起来。2. 为什么选WGAN而不是原始GAN振动信号生成的选型逻辑2.1 原始GAN在振动信号上的三个硬伤原始GAN用JS散度衡量生成分布和真实分布的差异这个选择在图像生成上勉强能用但搬到一维振动信号上问题就被放大了。第一个硬伤是梯度消失当判别器太强时生成器几乎拿不到有效梯度loss曲线看着在动但生成的信号始终是噪声。第二个硬伤是模式崩溃生成器发现只要反复输出某一段“万能信号”就能骗过判别器于是所有生成样本长得一模一样多样性直接归零。第三个硬伤是训练震荡判别器和生成器的loss此起彼伏你根本不知道什么时候该停。振动信号本身的特点让这些问题更严重。轴承故障信号是典型的一维时序数据采样率高、频带窄、故障特征往往藏在冲击成分的间隔和幅值调制里。原始GAN的判别器很容易通过捕捉局部噪声模式来区分真假导致生成器学不到真正的故障冲击特征。WGAN的核心改动是用Wasserstein距离替代JS散度。Wasserstein距离的好处是即使两个分布没有重叠它也能提供有意义的梯度。具体实现上WGAN做了三件事去掉判别器最后一层的Sigmoid把判别器改叫Critic把loss改成真实样本得分减去生成样本得分每次更新Critic后把权重裁剪到[-c, c]区间。这三步下来训练稳定性提升非常明显。2.2 WGAN-GP的改进与振动信号适配权重裁剪有个副作用它会把Critic的参数强行限制在一个小范围内导致Critic学不到复杂的判别函数。WGAN-GPGradient Penalty用梯度惩罚替代权重裁剪让Critic的梯度范数接近1既保证了Lipschitz约束又不会过度限制模型容量。对于振动信号生成我一般会做以下适配适配项原始WGAN-GP振动信号适配做法输入维度图像用4D张量一维时序用(batch, 1, length)生成器结构转置卷积为主一维转置卷积残差连接Critic结构卷积全连接一维卷积谱归一化梯度惩罚系数105~10之间调训练轮次几万到几十万5000~20000轮足够生成器结构上我习惯用三层一维转置卷积每层后面接BatchNorm和LeakyReLU最后用Tanh把输出压到[-1, 1]。Critic用四层一维卷积每层接LeakyReLU最后全局平均池化后输出一个标量。这个结构在轴承振动信号上表现稳定生成样本的时域波形和包络谱都能和真实故障信号对上。2.3 数据预处理把振动信号切成WGAN能吃的形状原始振动信号是长时序直接喂给WGAN效果很差。常见做法是滑动窗口切分每个窗口包含至少一个完整的故障冲击周期。以轴承故障为例如果外圈故障特征频率是100Hz采样率12kHz那一个冲击周期对应120个采样点窗口长度至少取1024才能覆盖多个周期。import numpy as np from sklearn.preprocessing import MinMaxScaler def segment_signal(signal, window_size1024, step512): 滑动窗口切分振动信号 signal: 一维numpy数组原始振动信号 window_size: 每个样本的采样点数 step: 滑动步长通常取window_size的一半 返回: (n_samples, window_size)的数组 segments [] for start in range(0, len(signal) - window_size 1, step): segment signal[start:start window_size] segments.append(segment) return np.array(segments) def normalize_signal(segments): 归一化到[-1, 1]适配Tanh输出 注意按全局最大最小值归一化不要按每个样本单独归一化 否则会破坏样本间的幅值相对关系 scaler MinMaxScaler(feature_range(-1, 1)) n_samples, window_size segments.shape flat segments.reshape(-1, 1) scaled scaler.fit_transform(flat) return scaled.reshape(n_samples, window_size), scaler # 使用示例 raw_signal np.load(bearing_vibration.npy) # 假设是单通道长信号 segments segment_signal(raw_signal, window_size1024, step512) normalized, scaler normalize_signal(segments) print(f切分后样本数: {normalized.shape[0]}, 每样本长度: {normalized.shape[1]})这里有个容易忽略的点归一化必须按全局做不能每个样本单独归一化。如果每个样本各自缩放到[-1, 1]那幅值信息就丢了而轴承故障的严重程度恰恰体现在冲击幅值上。另外窗口长度建议取1024或2048太短覆盖不了多个冲击周期太长则单样本计算量太大。3. 搭一个能跑的WGAN-GP生成器和Critic的代码实现3.1 生成器网络从噪声到振动波形的映射生成器的任务是把一个低维噪声向量映射成1024点的一维振动信号。我一般用全连接层先把噪声升维到合适长度再堆叠一维转置卷积逐步上采样。import torch import torch.nn as nn class Generator(nn.Module): def __init__(self, noise_dim100, output_length1024): super(Generator, self).__init__() self.noise_dim noise_dim self.output_length output_length # 全连接层噪声 - 初始特征图 self.fc nn.Sequential( nn.Linear(noise_dim, 256 * 8), nn.BatchNorm1d(256 * 8), nn.ReLU(True) ) # 一维转置卷积堆叠 self.conv_blocks nn.Sequential( # 输入: (batch, 256, 8) nn.ConvTranspose1d(256, 128, kernel_size4, stride2, padding1), nn.BatchNorm1d(128), nn.ReLU(True), # 输出: (batch, 128, 16) nn.ConvTranspose1d(128, 64, kernel_size4, stride2, padding1), nn.BatchNorm1d(64), nn.ReLU(True), # 输出: (batch, 64, 32) nn.ConvTranspose1d(64, 32, kernel_size4, stride2, padding1), nn.BatchNorm1d(32), nn.ReLU(True), # 输出: (batch, 32, 64) nn.ConvTranspose1d(32, 16, kernel_size4, stride2, padding1), nn.BatchNorm1d(16), nn.ReLU(True), # 输出: (batch, 16, 128) nn.ConvTranspose1d(16, 1, kernel_size4, stride2, padding1), # 输出: (batch, 1, 256) ) # 自适应池化到目标长度 self.adaptive_pool nn.AdaptiveAvgPool1d(output_length) self.tanh nn.Tanh() def forward(self, z): x self.fc(z) x x.view(x.size(0), 256, 8) x self.conv_blocks(x) x self.adaptive_pool(x) return self.tanh(x)生成器的关键参数是noise_dim和output_length。noise_dim一般取100或128太小生成多样性不足太大训练收敛慢。output_length要和预处理时的窗口长度一致。转置卷积的kernel_size和stride决定了上采样倍数这里用kernel_size4、stride2的组合每层长度翻倍。最后用AdaptiveAvgPool1d强制对齐到目标长度避免手工计算每层尺寸。3.2 Critic网络用一维卷积提取振动特征Critic不用Sigmoid输出是实数分数。结构上我用一维卷积逐层降采样最后全局平均池化。class Critic(nn.Module): def __init__(self, input_length1024): super(Critic, self).__init__() self.conv_blocks nn.Sequential( # 输入: (batch, 1, 1024) nn.Conv1d(1, 32, kernel_size5, stride2, padding2), nn.LeakyReLU(0.2, inplaceTrue), # 输出: (batch, 32, 512) nn.Conv1d(32, 64, kernel_size5, stride2, padding2), nn.LeakyReLU(0.2, inplaceTrue), # 输出: (batch, 64, 256) nn.Conv1d(64, 128, kernel_size5, stride2, padding2), nn.LeakyReLU(0.2, inplaceTrue), # 输出: (batch, 128, 128) nn.Conv1d(128, 256, kernel_size5, stride2, padding2), nn.LeakyReLU(0.2, inplaceTrue), # 输出: (batch, 256, 64) ) self.global_pool nn.AdaptiveAvgPool1d(1) self.fc nn.Linear(256, 1) def forward(self, x): x self.conv_blocks(x) x self.global_pool(x).squeeze(-1) return self.fc(x)Critic里没有BatchNorm这是WGAN-GP的常见做法因为BatchNorm会引入样本间的依赖破坏梯度惩罚的独立性。LeakyReLU的负斜率取0.2比默认的0.01更适合振动信号能让负半轴的梯度更好地回传。3.3 梯度惩罚与训练循环WGAN-GP的核心在梯度惩罚项。每次迭代时在真实样本和生成样本之间随机插值计算Critic对插值样本的梯度范数惩罚其偏离1的程度。def gradient_penalty(critic, real, fake, device, lambda_gp10): 计算WGAN-GP的梯度惩罚项 real: 真实样本 (batch, 1, length) fake: 生成样本 (batch, 1, length) lambda_gp: 惩罚系数常用10 batch_size real.size(0) # 随机插值系数 alpha torch.rand(batch_size, 1, 1, devicedevice) # 插值样本 interpolated alpha * real (1 - alpha) * fake interpolated.requires_grad_(True) # Critic对插值样本的评分 score critic(interpolated) # 计算梯度 grad torch.autograd.grad( outputsscore, inputsinterpolated, grad_outputstorch.ones_like(score), create_graphTrue, retain_graphTrue )[0] # 梯度范数 grad_norm grad.view(batch_size, -1).norm(2, dim1) # 惩罚项梯度范数偏离1的平方 penalty ((grad_norm - 1) ** 2).mean() return lambda_gp * penalty def train_wgan_gp(generator, critic, dataloader, epochs2000, n_critic5, lr1e-4, devicecuda): WGAN-GP训练循环 n_critic: 每更新一次生成器Critic更新n_critic次 g_optimizer torch.optim.Adam(generator.parameters(), lrlr, betas(0.5, 0.9)) c_optimizer torch.optim.Adam(critic.parameters(), lrlr, betas(0.5, 0.9)) generator.to(device) critic.to(device) for epoch in range(epochs): for i, (real_signal,) in enumerate(dataloader): real_signal real_signal.to(device) batch_size real_signal.size(0) # ---- 训练Critic ---- for _ in range(n_critic): z torch.randn(batch_size, 100, devicedevice) fake_signal generator(z).detach() c_real critic(real_signal).mean() c_fake critic(fake_signal).mean() gp gradient_penalty(critic, real_signal, fake_signal, device) c_loss c_fake - c_real gp c_optimizer.zero_grad() c_loss.backward() c_optimizer.step() # ---- 训练生成器 ---- z torch.randn(batch_size, 100, devicedevice) fake_signal generator(z) g_loss -critic(fake_signal).mean() g_optimizer.zero_grad() g_loss.backward() g_optimizer.step() if (epoch 1) % 100 0: print(fEpoch [{epoch1}/{epochs}] fC_loss: {c_loss.item():.4f} fG_loss: {g_loss.item():.4f} fW_distance: {(c_real - c_fake).item():.4f}) return generator, critic训练循环里有几个参数需要关注。n_critic5表示每训练一次生成器Critic训练五次这是WGAN论文推荐的值。学习率用1e-4Adam的betas设为(0.5, 0.9)这是WGAN-GP的标配。W_distance是真实样本得分减去生成样本得分这个值应该随着训练逐渐减小如果它一直震荡或增大说明训练出了问题。4. 生成质量评估与故障诊断验证怎么证明“假数据”真有用4.1 时域和频域的双重评估指标生成样本不能只看loss必须从时域和频域两个维度验证。时域看波形形态和统计特征频域看包络谱里的故障特征频率是否对得上。import numpy as np from scipy.fft import fft, fftfreq from scipy.stats import kurtosis, skew def evaluate_generated_signals(real_signals, fake_signals, fs12000): 评估生成信号质量 real_signals: 真实信号 (n, length) fake_signals: 生成信号 (n, length) fs: 采样率 metrics {} # 时域统计特征对比 for name, signals in [(real, real_signals), (fake, fake_signals)]: metrics[f{name}_mean] np.mean(np.abs(signals)) metrics[f{name}_std] np.std(signals) metrics[f{name}_kurtosis] np.mean(kurtosis(signals, axis1)) metrics[f{name}_skewness] np.mean(skew(signals, axis1)) # 包络谱对比 def envelope_spectrum(signal): # Hilbert变换求包络 from scipy.signal import hilbert analytic hilbert(signal) envelope np.abs(analytic) # 去均值 envelope envelope - np.mean(envelope) # FFT n len(envelope) spectrum np.abs(fft(envelope))[:n//2] * 2 / n freqs fftfreq(n, 1/fs)[:n//2] return freqs, spectrum # 取平均包络谱 real_env np.mean([envelope_spectrum(s)[1] for s in real_signals[:50]], axis0) fake_env np.mean([envelope_spectrum(s)[1] for s in fake_signals[:50]], axis0) freqs envelope_spectrum(real_signals[0])[0] # 找包络谱峰值对应的频率 real_peak_freq freqs[np.argmax(real_env)] fake_peak_freq freqs[np.argmax(fake_env)] metrics[real_peak_freq] real_peak_freq metrics[fake_peak_freq] fake_peak_freq metrics[freq_error] abs(real_peak_freq - fake_peak_freq) return metrics # 使用示例 real_samples normalized[:200] # 取200个真实样本 z torch.randn(200, 100).to(device) with torch.no_grad(): fake_samples generator(z).cpu().numpy().squeeze(1) metrics evaluate_generated_signals(real_samples, fake_samples) for k, v in metrics.items(): print(f{k}: {v:.4f})评估时重点看三个指标kurtosis峭度反映冲击成分的强弱故障轴承信号的峭度通常大于3包络谱峰值频率应该对应故障特征频率生成样本的std不能太小否则说明模式崩溃了。如果生成信号的峭度远低于真实信号说明生成器没学到冲击特征需要调整网络结构或增加训练轮次。4.2 用生成样本扩充训练集做故障分类生成样本的最终目的是提升故障诊断模型的性能。我一般会做对比实验只用真实样本训练分类器 vs 真实样本生成样本训练分类器看测试集准确率的变化。from sklearn.svm import SVC from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, classification_report def fault_classification_experiment(real_data, real_labels, generator, device, n_generate_per_class200): 对比实验真实样本 vs 真实生成样本 real_data: 真实信号 (n, length) real_labels: 标签 (n,) # 提取时域统计特征作为分类器输入 def extract_features(signals): features [] for s in signals: feat [ np.mean(np.abs(s)), np.std(s), kurtosis(s), skew(s), np.max(np.abs(s)), np.sqrt(np.mean(s**2)) ] features.append(feat) return np.array(features) # 只用真实样本 X_real extract_features(real_data) X_train, X_test, y_train, y_test train_test_split( X_real, real_labels, test_size0.3, random_state42, stratifyreal_labels ) clf_real SVC(kernelrbf, C10, gammascale) clf_real.fit(X_train, y_train) acc_real accuracy_score(y_test, clf_real.predict(X_test)) # 真实生成样本 generated_signals [] generated_labels [] for class_id in np.unique(real_labels): z torch.randn(n_generate_per_class, 100).to(device) with torch.no_grad(): fake generator(z).cpu().numpy().squeeze(1) generated_signals.append(fake) generated_labels.extend([class_id] * n_generate_per_class) generated_signals np.vstack(generated_signals) generated_labels np.array(generated_labels) X_gen extract_features(generated_signals) X_combined np.vstack([X_real, X_gen]) y_combined np.concatenate([real_labels, generated_labels]) X_train_c, X_test_c, y_train_c, y_test_c train_test_split( X_combined, y_combined, test_size0.3, random_state42, stratifyy_combined ) clf_combined SVC(kernelrbf, C10, gammascale) clf_combined.fit(X_train_c, y_train_c) acc_combined accuracy_score(y_test_c, clf_combined.predict(X_test_c)) print(f仅真实样本准确率: {acc_real:.4f}) print(f真实生成样本准确率: {acc_combined:.4f}) print(f提升: {(acc_combined - acc_real) * 100:.2f}%) return acc_real, acc_combined这个实验里生成样本的数量要控制好。如果生成样本远多于真实样本分类器会被生成样本主导反而可能降低性能。我一般让生成样本和真实样本数量相当或者生成样本略少一些。另外分类器用SVM就够了不用上深度学习因为我们要验证的是生成样本的质量不是分类器的能力。5. 避坑与排查WGAN生成振动信号时最容易翻车的五个地方5.1 生成信号全是噪声loss看着正常但样本没法用现象训练几千轮后生成的信号波形杂乱无章和噪声没区别但Critic loss和Generator loss都在缓慢下降看不出异常。原因最常见的原因是Critic太强生成器拿不到有效梯度。在WGAN-GP里如果n_critic设得太大比如10以上或者Critic的学习率比生成器高Critic很快就能完美区分真假样本生成器的梯度就消失了。解决把n_critic降到3~5Critic和生成器用相同的学习率。另外检查梯度惩罚系数lambda_gp如果太小比如1Lipschitz约束不够Critic会变得太强如果太大比如100Critic被限制得太死学不到东西。我一般从10开始调观察W_distance的变化如果它很快降到0附近并保持不变说明Critic太强了。5.2 生成样本多样性不足所有样本长得差不多现象生成1000个样本画出来发现波形几乎一样只是幅值有微小差异。计算样本间的欧氏距离发现远小于真实样本间的距离。原因模式崩溃。生成器发现只要输出某一段“万能信号”就能骗过Critic于是放弃了学习完整的故障分布。在振动信号上这个问题特别容易出现在故障特征频率比较单一的情况下。解决首先检查噪声维度noise_dim100是底线如果降到32或16多样性肯定不够。其次可以在生成器里加Dropout层训练时随机丢弃一些神经元强迫生成器不依赖固定模式。另外可以尝试minibatch discrimination让Critic同时看一批样本惩罚过于相似的生成结果。我自己的经验是在生成器的全连接层后加一个Dropout(0.3)模式崩溃的概率会明显降低。5.3 包络谱峰值对不上故障特征频率现象生成信号的时域波形看着还行但包络谱里的峰值频率和真实故障特征频率偏差超过10%。原因生成器没有学到故障冲击的周期特性。一维转置卷积的感受野有限如果故障冲击周期比较长生成器可能只学到了局部波形没学到全局的周期结构。解决增大生成器的感受野。具体做法是增加转置卷积的层数或者用空洞卷积dilated convolution扩大感受野。另一个办法是在生成器里加入自注意力机制让网络能捕捉长距离依赖。我一般会先检查窗口长度是否覆盖了至少3个冲击周期如果窗口太短生成器根本看不到完整的周期结构包络谱肯定对不上。5.4 训练后期W_distance突然崩掉现象训练前1000轮W_distance稳步下降然后突然跳到很大的值生成样本质量急剧恶化。原因梯度惩罚项计算时插值样本的梯度可能爆炸。如果真实样本和生成样本的幅值范围差异很大插值后的样本可能落在极端区域导致梯度范数异常。解决确保真实样本和生成样本都归一化到相同的范围比如[-1, 1]。另外可以在梯度惩罚计算时加一个小的epsilon防止除零。如果崩掉后恢复不了就降低学习率从1e-4降到5e-5重新加载崩掉之前的模型继续训练。我习惯每500轮保存一次模型崩了就从最近的检查点恢复比从头训练省时间。5.5 生成样本用于分类后准确率反而下降现象用真实生成样本训练分类器测试准确率比只用真实样本还低。原因生成样本的质量参差不齐有些样本虽然看着像故障信号但特征分布和真实故障有偏差相当于引入了噪声标签。另外如果生成样本数量远多于真实样本分类器会被生成样本主导。解决不要把所有生成样本都塞进训练集。我一般会先用包络谱峰值频率筛选一遍只保留峰值频率和真实故障特征频率偏差小于5%的生成样本。另外控制生成样本和真实样本的比例在1:1到1:2之间不要超过2:1。如果准确率还是下降说明生成样本质量不够需要回到训练阶段继续调WGAN。6. 从生成到落地把WGAN变成故障诊断流水线里的一个模块6.1 条件WGAN按故障类型定向生成无条件WGAN生成的是混合故障样本你没法控制它生成的是内圈故障还是外圈故障。实际产线上你往往需要针对特定故障类型补充样本。条件WGANConditional WGAN把故障标签作为额外输入生成器和Critic都接收标签信息。class ConditionalGenerator(nn.Module): def __init__(self, noise_dim100, n_classes4, output_length1024): super(ConditionalGenerator, self).__init__() # 标签嵌入 self.label_embedding nn.Embedding(n_classes, 50) # 噪声标签嵌入拼接后输入 self.fc nn.Sequential( nn.Linear(noise_dim 50, 256 * 8), nn.BatchNorm1d(256 * 8), nn.ReLU(True) ) # 后续转置卷积层与无条件生成器相同 self.conv_blocks nn.Sequential( nn.ConvTranspose1d(256, 128, 4, 2, 1), nn.BatchNorm1d(128), nn.ReLU(True), nn.ConvTranspose1d(128, 64, 4, 2, 1), nn.BatchNorm1d(64), nn.ReLU(True), nn.ConvTranspose1d(64, 32, 4, 2, 1), nn.BatchNorm1d(32), nn.ReLU(True), nn.ConvTranspose1d(32, 16, 4, 2, 1), nn.BatchNorm1d(16), nn.ReLU(True), nn.ConvTranspose1d(16, 1, 4, 2, 1), ) self.adaptive_pool nn.AdaptiveAvgPool1d(output_length) self.tanh nn.Tanh() def forward(self, z, labels): label_emb self.label_embedding(labels) x torch.cat([z, label_emb], dim1) x self.fc(x) x x.view(x.size(0), 256, 8) x self.conv_blocks(x) x self.adaptive_pool(x) return self.tanh(x)条件WGAN的训练循环和无条件版本基本一致只是每次前向传播时要同时传入噪声和标签。Critic也要接收标签通常做法是把标签嵌入后和信号特征拼接。条件WGAN的好处是你可以指定生成“内圈故障”或“外圈故障”生成样本的标签是确定的不需要额外标注。6.2 在线生成训练好的生成器怎么嵌入诊断系统训练好的生成器可以导出为TorchScript或ONNX嵌入到诊断系统的推理流程里。当系统检测到某类故障样本不足时实时调用生成器补充样本。# 导出为TorchScript generator.eval() example_noise torch.randn(1, 100) example_label torch.tensor([0]) traced_generator torch.jit.trace(generator, (example_noise, example_label)) traced_generator.save(wgan_generator.pt) # 推理时加载 loaded_generator torch.jit.load(wgan_generator.pt) loaded_generator.eval() def generate_fault_samples(fault_type, n_samples100): 按故障类型生成样本 fault_type: 0-正常, 1-内圈故障, 2-外圈故障, 3-滚动体故障 z torch.randn(n_samples, 100) labels torch.full((n_samples,), fault_type, dtypetorch.long) with torch.no_grad(): samples loaded_generator(z, labels) return samples.numpy().squeeze(1)在线生成时要注意推理速度。生成器本身计算量不大1000个样本在GPU上不到1秒。但如果诊断系统部署在边缘设备上可能需要把生成器量化成FP16或INT8。我一般会在导出前做一次量化感知训练精度损失控制在1%以内。6.3 一个我踩过的坑生成样本的幅值分布偏移最后说一个我实际踩过的坑。训练WGAN时我用MinMaxScaler把信号归一化到[-1, 1]生成器输出也用Tanh压到[-1, 1]。看起来没问题但生成样本的幅值分布明显偏向0附近大冲击成分的幅值比真实样本低。原因是Tanh在接近±1时梯度很小生成器倾向于输出中间值来避免梯度消失。解决办法是在生成器最后一层用线性输出然后在loss里加一个幅值约束项惩罚生成样本和真实样本的幅值分布差异。或者更简单在归一化时不要用MinMaxScaler改用StandardScaler让数据分布接近标准正态Tanh的输出范围刚好匹配。这个改动让生成样本的峭度从2.1提升到了3.8和真实故障信号的峭度基本一致。做故障诊断这行数据永远是最贵的。WGAN给了一条低成本扩充样本的路但这条路不是一键生成的参数要调、结构要改、生成样本要筛。我现在的习惯是每拿到一个新工况的数据先花半天时间把WGAN跑通生成一批样本然后用包络谱筛一遍最后混入训练集做对比实验。这套流程跑下来通常能把小样本故障诊断的准确率提升5到15个百分点。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/29 19:00:57

RAG知识库解析优化:MinerU 4.0四档解析与定位器实践

做RAG项目的人,八成都有过这种体验:检索链路调得再顺,召回率就是卡在某个瓶颈上不去,翻来覆去找原因,最后发现是文档解析这一步把整个知识库的质量拖垮了。PDF这个格式本身是“给打印机看的”,页面坐标里藏…

2026/9/29 19:00:57

RAG、记忆、API与MCP:带鉴权审计的大模型应用落地实战

1. 从"能跑通"到"敢上线":这套应用到底在解决什么问题大模型应用最尴尬的阶段,不是Demo跑不起来,而是Demo跑起来之后没人敢用。我见过太多团队花两周搭出一个能对话、能查知识库的原型,演示时效果惊艳&#x…

2026/9/29 18:55:57

TDA4VM R5F中断实战:VIC与非VIC模式对比与配置陷阱

TDA4VM/VH 这颗芯片,我前后摸了一年多,从硬件参考设计看到 RTOS 底层调度,再一路追到中断控制器。说实话,第一眼看到 R5F 核要同时面对 VIC 和非 VIC 两种中断处理路径时,我是有点懵的——同一个核,两种中断…

2026/9/29 21:16:06

MCP与A2A协议实战:TaoToken统一Key下AI Agent通信配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/29 21:16:06

【Python 学习第 20 天】类型转换

【Python 学习第 20 天】类型转换学习日期:2026-09-28 知识点:类型转换 难度:0.65一、今日知识点 今天学习的是 类型转换。 类型转换是把一种数据类型变成另一种数据类型。Python 中分为隐式转换(自动进行,如 int 和 f…

2026/9/29 21:16:06

ASM入网小助手卸载成功:TaoToken 统一 Key 通道配置与验证实录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/29 21:11:06

不敢让 Codex 直接改代码?我先让它只读分析一个 Node.js 项目

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/28 6:05:15

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 0:04:04

AI Evals实战指南:从零搭建LLM应用评估体系与CI/CD集成

1. 为什么AI Evals值得你花时间搞明白做LLM应用的人,迟早会撞上同一堵墙:模型输出飘忽不定,今天答得好好的,明天换个问法就胡说八道。你改了一版提示词,感觉好像好了点,但到底好了多少?说不清。…

2026/9/29 0:04:04

Java采购管理系统实战:从数据库设计到事务一致性

简介:这是一套面向Java Web初学者与课程设计者的采购管理系统完整源码,采用JSP技术搭建,配合MySQL数据库,用于解决企业采购信息的管理问题,适合作为毕业设计、课程大作业或进销存类项目的参考模板。系统实现了用户登录…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/29 9:46:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/29 6:36:14

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑