神经网络疲劳寿命预测子程序的封装与应用
2026/9/14 4:21:20 网站建设 项目流程

简介:一套基于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.0,1000 万次变成 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_size=0.2, random_state=42 ) 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, activation="relu", input_dim=X_train_scaled.shape[1])) model.add(Dropout(0.2)) model.add(Dense(32, activation="relu")) model.add(Dense(1)) model.compile(optimizer="adam", loss="mse", metrics=["mae"]) model.summary() history = model.fit( X_train_scaled, y_train, validation_data=(X_val_scaled, y_val), epochs=200, batch_size=32, callbacks=[EarlyStopping(patience=10, restore_best_weights=True)], verbose=1 )

结构上第一层 64 个神经元,relu 能缓解梯度消失,比 tanh 在这种中规模特征上收敛快;Dropout 0.2 表示每次迭代随机冻结 20% 的神经元,用来防止小样本过拟合。输出层只有 1 个神经元,没有激活函数,对应预测的 logN。优化器直接选 adam,epochs 设到 200 是因为靠 EarlyStopping 在验证集 loss 连续 10 轮不再下降时自动停下,并恢复最佳权重。

3.3 损失函数怎么选:MSE、MAE 还是 Huber

代码里 loss 用的是 mse,它对离群点非常敏感。如果残差里偶尔出现一个预测值偏离两个数量级的样本,MSE 会把梯度拉向这个样本,导致整体精度下降。疲劳数据的噪声本来就大,所以我更常用 Huber:

from tensorflow.keras.optimizers import Adam model.compile( optimizer=Adam(learning_rate=0.001), loss="huber", 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(f"MAE(log): {mae:.4f}") print(f"RMSE(log): {rmse:.4f}") print(f"R2: {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, dtype=float) if X.ndim == 1: X = X.reshape(1, -1) X_scaled = self.scaler.transform(X) log_life = self.model.predict(X_scaled, batch_size=64, verbose=0).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", index=False)

批量预测最大的坑是内存和速度。如果新工况有十几万行,Keras 默认逐批计算,不需要手动切分;但predict会返回一个很大的 array,这时候建议把落盘分成几批写。另外,predict默认 verbose=0 关闭进度条,批量运行时避免日志刷屏。

4.3 模型更新:用新试验数据做增量训练

疲劳寿命模型不会一劳永逸,新的试验数据出来后,最常见做法是用小学习率继续训练旧模型,而不是完全重新开始。完全重新训练成本高,而且旧样本可能已经归档难以找回。

new_log_life = np.log10(new_df["life_cycles"].values) X_new_scaled = scaler.transform(new_df[feature_cols].values) # 用小学习率在旧权重基础上继续学习 model.compile( optimizer=Adam(learning_rate=1e-4), loss="huber", metrics=["mae"] ) model.fit( X_new_scaled, new_log_life, epochs=20, callbacks=[EarlyStopping(patience=3, restore_best_weights=True)] )

学习率从原来的 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, alpha=0.6) plt.axhline(0, color="red", 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 线性累积损伤结果对比,两者经常相差一个数量级,这时优先怀疑数据划分,而不是急着调网络结构。

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

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

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

立即咨询