神经网络疲劳寿命预测子程序的封装与应用

发布时间:2026/9/14 4:21:39
神经网络疲劳寿命预测子程序的封装与应用 简介一套基于Python实现的神经网络工具包面向希望快速上手模型训练与预测的开发者也兼顾疲劳分析场景下的数据建模需求。项目围绕“网络定义—数据加载—训练—预测”主线组织代码包含NeuralNetwork.py核心模块、MNIST手写数字识别、曲线拟合、逻辑回归三组可运行Demo以及mnist_data.py数据加载与tools.py辅助工具。既能作为入门教程也可参照其网络结构和训练流程将“疲劳子程序”中的载荷历史转化为输入特征进行剩余寿命预测。压缩包共32个文件、15.96MB以22个txt权重/日志、6个py脚本和4个idx格式MNIST数据文件为主目录清晰便于按模型目录和演示脚本分别阅读。txt文件记录了各层权重与偏置可配合演示脚本复盘完整训练过程idx格式数据直接来自MNIST标准数据集下载后即可运行。目前已有183人学习。压缩包内附训练好的权重文件可直观对比不同层参数变化适合初学者理解反向传播与模型保存格式也适合工程人员借用这套流程构建自己的疲劳预测模型。1. 疲劳预测为什么要把神经网络算法包成子程序很多工程师手里都有一份“NeuralNetwork-master”的仓库里面跑通了 MNIST、房价预测或者某个示例数据但当现场设备正在做疲劳寿命预测时这些代码就完全不够用。疲劳预测不是分类问题也不是普通回归样本量小、真实寿命跨几个数量级、失效模式复杂传统 S-N 曲线和 Miner 线性累积损伤在变幅载荷下误差很常见。标题里的“神经网络NNA”我按 Neural Network Algorithm 理解它落到工程上的做法就是训练一个能输入载荷和结构参数、输出寿命的回归模型并把预测逻辑封装成可反复调用的“疲劳子程序”。这篇文章把这条路径讲透从训练样本构造、模型参数选择到子程序接口和验证方法你完全可以照着搭一套用于疲劳故障诊断和寿命预测的最小系统。2. 疲劳训练样本怎么构造特征、标签与数据完整性2.1 疲劳预测的输入特征要覆盖“载荷-结构-环境”三类变量疲劳寿命不是单一应力能决定的。做数据驱动模型时我一般会把特征分成三类这比先跑一堆相关性分析更符合力学逻辑。特征类别特征名物理含义单位载荷特征应力幅值循环中的应力变化幅度MPa载荷特征平均应力循环中的应力基线MPa载荷特征应力比最小应力与最大应力之比无量纲载荷特征加载频率单位时间循环次数Hz结构特征缺口系数 Kt应力集中程度无量纲结构特征表面加工系数表面粗糙度对疲劳强度的影响无量纲环境特征工作温度高温会加速损伤累积摄氏度环境特征腐蚀介质等级腐蚀环境下疲劳极限下降等级值不要一开始就把几十个特征全扔进网络。疲劳机理里最重要的变量是应力幅值和平均应力Goodman 修正曲线就是基于这两个量描述疲劳极限的所以模型至少要有它们。Kt 和表面加工系数决定局部应力温度与腐蚀介质决定环境修正。加特征前先画散点图看寿命是否随特征单调变化或分段变化。相关系数低不代表没有用因为寿命与应力之间是对数关系线性相关系数天然不高。2.2 寿命标签取 log10不然神经网络学不动疲劳寿命的典型范围是从 10³ 到 10⁷ 循环跨了四个数量级。如果直接把“循环次数”作为回归目标损失函数会被高寿命样本主导模型训练时会只顾着把 10⁶ 以上的样本预测准而 10⁴ 以下的低寿命样本几乎学不到。常见做法是先把标签取对数import numpy as np df[log_life] np.log10(df[life_cycles])这样原始寿命 1000 次变成 3.01000 万次变成 7.0数值范围收窄到可以放心用 MSE 训练的程度。预测完成后反变换即可pred_life 10 ** pred_log_life这里有一个疲劳数据特有的坑试验中很多样本在达到预设循环次数后仍未断裂这种样本叫 runout它的标签不是“真实寿命”而是“截止寿命”。直接把它当成精确寿命喂给模型会让模型认为材料在这个应力水平下只能活到截止次数。我一般会先剔除 runout或者单独建一个分类器判断“是否失效”再对失效样本做回归。2.3 清洗和剔除异常样本的实用脚本疲劳试验数据的脏主要体现在单位不统一、手填数据里混入字符串、寿命列出现 0 或负数。这些样本不能参与训练写一个最小的清洗流程import pandas as pd df pd.read_csv(fatigue_data.csv) print(df.info()) print(df.describe()) df df.dropna(subset[stress_amplitude, mean_stress, life_cycles]) df df[df[life_cycles] 0] df df[df[stress_amplitude] 0] df df[df[stress_amplitude] df[ultimate_strength]]这段代码的作用是先看每一列的非空数量和类型然后只删除三个核心字段缺失的行避免把其他可用特征一起删掉再剔除寿命小于等于 0 的记录最后剔除应力幅值超过材料极限强度的记录这类数据通常是人工录入单位错误或试件异常。2.4 先归一化再做数据集拆分防止数据泄漏神经网络对输入量纲很敏感应力幅值可能是 300应力比只有 0.1归一化是必须的。但很多人在归一化上犯过一个隐蔽错误先对整个数据集做 fit_transform再切训练集和验证集。这样验证集的均值和标准差已经进入了缩放参数模型效果看起来很好实际上测试时完全不是那回事。from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler feature_cols [stress_amplitude, mean_stress, stress_ratio, kt, temp] X df[feature_cols].values y df[log_life].values # 先拆分再对训练集 fit验证集只 transform X_train, X_val, y_train, y_val train_test_split( X, y, test_size0.2, random_state42 ) scaler MinMaxScaler() X_train_scaled scaler.fit_transform(X_train) X_val_scaled scaler.transform(X_val)注意验证集使用的是训练集的缩放参数。如果是变幅载荷谱数据不想随机打乱因为疲劳损伤有顺序效应前面一段载荷会影响后面的剩余寿命。这时候把数据集按时间或试验批次切分前 80% 做训练后 20% 做验证才能反映真实场景。提示疲劳数据集里 runout 样本是截尾数据不要和正常寿命混在一起回归把它先剔掉等主模型建好后再单独处理。3. 用 Python 搭建神经网络疲劳寿命预测模型3.1 疲劳样本是时间序列时选 LSTM 还是 MLP很多疲劳数据不是一条静态记录而是一整段载荷谱。比如风力发电机叶片在不同风速下承受随机载荷轴承在变转速下振动信号不断变化。这时候特征不是一个数而是一段时间窗口。网络选型可以按这张表来定数据形态推荐网络选择理由每行为一条静态参数记录MLP参数简单样本量小也能收敛载荷按时间顺序排列的窗口数据LSTM能捕捉载荷顺序对损伤累积的影响多通道振动信号或载荷谱1D CNN / LSTM局部特征提取后再做时序建模静态特征加少量时序统计量MLP 滑动窗口成本最低工程上最容易解释如果你看到“lstm 时间序列预测 python”这个话题别急着把疲劳数据也直接套进去。疲劳寿命预测和时间序列预测不一样时间序列预测是逐点外推下一时刻的值疲劳预测是给一整段输入序列输出一个剩余寿命标量。只有在载荷谱存在明显顺序效应时LSTM 才明显优于 MLP。如果样本本来就是按试验条件汇总后的静态表强行上 LSTM 反而过拟合。3.2 最小可用的神经网络疲劳预测代码下面这个网络结构是我用来跑疲劳寿命预测的起点输入层接两个全连接层和一个 dropout输出层不加激活函数因为回归任务需要输出任意实数。import pandas as pd import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping model Sequential() model.add(Dense(64, activationrelu, input_dimX_train_scaled.shape[1])) model.add(Dropout(0.2)) model.add(Dense(32, activationrelu)) model.add(Dense(1)) model.compile(optimizeradam, lossmse, metrics[mae]) model.summary() history model.fit( X_train_scaled, y_train, validation_data(X_val_scaled, y_val), epochs200, batch_size32, callbacks[EarlyStopping(patience10, restore_best_weightsTrue)], verbose1 )结构上第一层 64 个神经元relu 能缓解梯度消失比 tanh 在这种中规模特征上收敛快Dropout 0.2 表示每次迭代随机冻结 20% 的神经元用来防止小样本过拟合。输出层只有 1 个神经元没有激活函数对应预测的 logN。优化器直接选 adamepochs 设到 200 是因为靠 EarlyStopping 在验证集 loss 连续 10 轮不再下降时自动停下并恢复最佳权重。3.3 损失函数怎么选MSE、MAE 还是 Huber代码里 loss 用的是 mse它对离群点非常敏感。如果残差里偶尔出现一个预测值偏离两个数量级的样本MSE 会把梯度拉向这个样本导致整体精度下降。疲劳数据的噪声本来就大所以我更常用 Huberfrom tensorflow.keras.optimizers import Adam model.compile( optimizerAdam(learning_rate0.001), losshuber, metrics[mae] )Huber 在残差小于阈值时使用平方误差大于阈值时改为线性误差对离群点没那么敏感。实际使用时可以先看验证集的 mae如果 mae 与 rmse 的差距很大说明存在离群预测换成 Huber 通常有用。评估阶段我一般计算三个指标from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score y_pred_log model.predict(X_val_scaled).flatten() mae mean_absolute_error(y_val, y_pred_log) rmse np.sqrt(mean_squared_error(y_val, y_pred_log)) r2 r2_score(y_val, y_pred_log) print(fMAE(log): {mae:.4f}) print(fRMSE(log): {rmse:.4f}) print(fR2: {r2:.4f})MAE 是 0.3意味着预测的 logN 平均偏差 10^0.3约 2 倍寿命误差R2 达到 0.85 以上说明模型解释了大部分方差。只报告 R2 不够疲劳工程里绝对误差更关键。4. 把神经网络封装成疲劳预测子程序4.1 子程序的接口设计输入矩阵输出寿命训练脚本不能直接进工程系统。生产环境的结构强度分析程序、设备健康管理系统通常只需要一个函数传入当前工况参数返回预测寿命。把模型、归一化参数和特征名绑在一个类里是最常见的“疲劳子程序”形态。import numpy as np import tensorflow as tf from tensorflow import keras class FatiguePredictor: def __init__(self, model_path, scaler, feature_names): self.model keras.models.load_model(model_path) self.scaler scaler self.feature_names feature_names def predict_life(self, X): X np.asarray(X, dtypefloat) if X.ndim 1: X X.reshape(1, -1) X_scaled self.scaler.transform(X) log_life self.model.predict(X_scaled, batch_size64, verbose0).flatten() return np.power(10, log_life) feature_cols [stress_amplitude, mean_stress, stress_ratio, kt, temp] predictor FatiguePredictor(fatigue_model.h5, scaler, feature_cols) sample np.array([250, 30, 0.1, 1.6, 80]) print(predictor.predict_life(sample))接口里要注意两个细节一是scaler必须是训练时已经完成 fit 的对象不能对单条样本重新 fit否则缩放范围会变成一个点二是特征顺序必须和训练时完全一致这里用feature_names记录顺序新工况接入时按这个列表组装向量。类比裸函数的好处是模型和缩放器作为内部状态被绑定调用方不需要知道任何预处理细节。4.2 批量预测与结果落盘实际使用中很少一次只预测一条。给一批新工况文件批量预测通常是这个流程df_new pd.read_csv(new_conditions.csv) # 按训练时的特征顺序取值 X_new df_new[feature_cols].values life_pred predictor.predict_life(X_new) df_new[pred_life_cycles] life_pred df_new.to_csv(prediction_result.csv, indexFalse)批量预测最大的坑是内存和速度。如果新工况有十几万行Keras 默认逐批计算不需要手动切分但predict会返回一个很大的 array这时候建议把落盘分成几批写。另外predict默认 verbose0 关闭进度条批量运行时避免日志刷屏。4.3 模型更新用新试验数据做增量训练疲劳寿命模型不会一劳永逸新的试验数据出来后最常见做法是用小学习率继续训练旧模型而不是完全重新开始。完全重新训练成本高而且旧样本可能已经归档难以找回。new_log_life np.log10(new_df[life_cycles].values) X_new_scaled scaler.transform(new_df[feature_cols].values) # 用小学习率在旧权重基础上继续学习 model.compile( optimizerAdam(learning_rate1e-4), losshuber, metrics[mae] ) model.fit( X_new_scaled, new_log_life, epochs20, callbacks[EarlyStopping(patience3, restore_best_weightsTrue)] )学习率从原来的 0.001 降到 0.0001是防止新数据把旧知识冲掉。更稳妥的做法是同时保留一部分旧样本作为验证集一旦增量训练导致旧样本精度下降就提前停止。5. 疲劳预测模型的验证与调优从指标好看到现场能用5.1 残差分布先看偏差再看随机性只看 R2 和 MAE 是不够的。验证集上预测值减去真实值得到残差画一张残差图比任何汇总指标都直观。import matplotlib.pyplot as plt residual y_val - y_pred_log plt.figure(figsize(8, 5)) plt.scatter(y_pred_log, residual, alpha0.6) plt.axhline(0, colorred, linestyle--) plt.xlabel(Predicted logN) plt.ylabel(Residual) plt.title(Residual Plot) plt.show()如果残差点在纵轴 0 线附近随机散布说明模型没有系统性偏差。如果出现喇叭形比如预测值越大残差越分散说明高寿命区间噪声大可以考虑对输出做更强的对数压缩或者改用 Huber。如果残差有一侧整体偏高说明某些关键特征缺失需要回到第 2 章检查特征集合。5.2 过拟合的三个信号与应对疲劳数据集通常只有几百条样本过拟合几乎必然要面对。三个信号分别是训练 loss 持续下降但验证 loss 回升验证 R2 明显低于训练 R2预测结果中位值与真实中位值出现显著偏移。应对方法里最有效的是 EarlyStopping 和 Dropout这两个已经写进第 3 章的代码里。此外可以给 Dense 层加 L2 正则化把核权重限制小一些让模型不依赖单个特征。5.3 滚动预测的坑不要把未来信息带进特征这里是最容易被忽视的一环。疲劳预测里如果做窗口滑动构造特征比如取前 30 秒载荷统计量来预测剩余寿命窗口边界一旦越过“当前时刻”就会把未来信息带进模型。验证集上表现很好的模型到现场后一落千丈大多是这个原因。校验方法是把验证集按时间顺序划分强制验证集时间晚于训练集再重新训练一次如果精度大幅下降说明原模型吃到了未来信息。处理方式是把窗口结构彻底改为因果形式特征只使用当前时刻之前的数据。现场部署时我还会做一个交叉验证把网络预测寿命和 Miner 线性累积损伤结果对比两者经常相差一个数量级这时优先怀疑数据划分而不是急着调网络结构。本文还有配套的精品资源点击获取