import os
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
import numpy as np, pandas as pd, tensorflow as tf
from sklearn.preprocessing import StandardScaler
from sklearn.utils.class_weight import compute_class_weight
from sklearn.model_selection import train_test_split
from sklearn.metrics import (classification_report, roc_auc_score,
f1_score, recall_score, precision_score)
from tensorflow.keras.layers import (Input, LSTM, Dense, Dropout, BatchNormalization,
Bidirectional, Concatenate, Attention)
from tensorflow.keras.models import Model
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint
from tensorflow.keras.optimizers import Nadam
from bayes_opt import BayesianOptimization
from imblearn.over_sampling import SMOTE
from functools import partial
# --------------------------------------------------
# 1. 读取数据
# --------------------------------------------------
data_path = r'C:\Users\Lenovo\Desktop\时间序列.xlsx'
data = pd.read_excel(data_path)
X = data.iloc[:, :7].values # 前 7 列特征
y_bin = data.iloc[:, 7].values # 第 8 列 0/1 异常标志
y_type_raw = data.iloc[:, 8].values # 第 9 列 1–5 类磨损(异常样本)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# --------------------------------------------------
# 2. 滑窗
# --------------------------------------------------
time_steps = 8
def create_sequences(data_X, data_y, time_steps):
X, y = [], []
for i in range(len(data_X) - time_steps + 1):
X.append(data_X[i:i + time_steps])
y.append(data_y[i + time_steps - 1])
return np.array(X), np.array(y)
X_seq, y_seq_bin = create_sequences(X_scaled, y_bin, time_steps)
# --------------------------------------------------
# 3. 时序划分
# --------------------------------------------------
X_train, X_test, y_train_bin, y_test_bin = train_test_split(
X_seq, y_seq_bin, test_size=0.2, shuffle=False, random_state=42)
# --------------------------------------------------
# 4. SMOTE + 类别权重(仅针对异常检测)
# --------------------------------------------------
X_train_2d = X_train.reshape(X_train.shape[0], -1)
X_train_res, y_train_res = SMOTE(random_state=42).fit_resample(X_train_2d, y_train_bin)
X_train_res = X_train_res.reshape(-1, time_steps, X_train.shape[2])
classes = np.unique(y_train_res)
weights = compute_class_weight('balanced', classes=classes, y=y_train_res)
class_weight = dict(zip(classes, weights))
# --------------------------------------------------
# 5. 自定义 Focal Loss
# --------------------------------------------------
def focal_loss_fixed(y_true, y_pred, gamma=2.0, alpha=0.8):
eps = tf.keras.backend.epsilon()
y_pred = tf.clip_by_value(y_pred, eps, 1 - eps)
p_t = tf.where(tf.equal(y_true, 1), y_pred, 1 - y_pred)
alpha_t = tf.where(tf.equal(y_true, 1), alpha, 1 - alpha)
return -tf.reduce_mean(alpha_t * tf.pow(1 - p_t, gamma) * tf.math.log(p_t))
# --------------------------------------------------
# 6. 异常检测模型
# --------------------------------------------------
def build_bin_model(lstm1, lstm2, lstm3, dropout, lr, gamma, alpha):
inputs = Input(shape=(time_steps, X_train_res.shape[2]))
x = Bidirectional(LSTM(int(lstm1), return_sequences=True))(inputs)
x = BatchNormalization()(x); x = Dropout(dropout)(x)
x = LSTM(int(lstm2), return_sequences=True)(x)
x = BatchNormalization()(x); x = Dropout(dropout)(x)
att = Attention()([x, x])
x = Concatenate()([x, att])
x = LSTM(int(lstm3))(x)
x = BatchNormalization()(x); x = Dropout(dropout)(x)
outputs = Dense(1, activation='sigmoid')(x)
model = Model(inputs, outputs)
model.compile(optimizer=Nadam(learning_rate=lr, clipnorm=1.0),
loss=partial(focal_loss_fixed, gamma=gamma, alpha=alpha),
metrics=[tf.keras.metrics.AUC(name='auc'),
tf.keras.metrics.Recall(name='recall')])
return model
# --------------------------------------------------
# 7. 异常检测贝叶斯优化
# --------------------------------------------------
def bin_cv(lstm1, lstm2, lstm3, dropout, lr, batch_size, gamma, alpha):
lstm1, lstm2, lstm3, batch_size = map(int, (lstm1, lstm2, lstm3, batch_size))
gamma, alpha, dropout, lr = float(gamma), float(alpha), float(dropout), float(lr)
model = build_bin_model(lstm1, lstm2, lstm3, dropout, lr, gamma, alpha)
es = EarlyStopping(monitor='val_recall', mode='max', patience=5, restore_best_weights=True, verbose=0)
X_tr, X_val, y_tr, y_val = train_test_split(
X_train_res, y_train_res, test_size=0.1, random_state=42, stratify=y_train_res)
model.fit(X_tr, y_tr, epochs=30, batch_size=batch_size, verbose=0,
validation_data=(X_val, y_val), class_weight=class_weight, callbacks=[es])
return max(model.history.history['val_recall'])
pbounds_bin = {'lstm1': (64, 256), 'lstm2': (32, 128), 'lstm3': (16, 96),
'dropout': (0.1, 0.5), 'lr': (1e-4, 5e-3), 'batch_size': (16, 64),
'gamma': (1.5, 3.0), 'alpha': (0.7, 0.95)}
bin_opt = BayesianOptimization(f=bin_cv, pbounds=pbounds_bin, random_state=42, verbose=2)
bin_opt.maximize(init_points=6, n_iter=12)
best_bin = bin_opt.max['params']
# --------------------------------------------------
# 8. 重新训练异常检测模型(去掉 batch_size)
# --------------------------------------------------
best_bin_pop = best_bin.copy()
best_bin_pop.pop('batch_size', None)
final_bin_model = build_bin_model(**best_bin_pop)
es_bin = EarlyStopping(monitor='val_recall', mode='max', patience=20, restore_best_weights=True, verbose=1)
lr_bin = ReduceLROnPlateau(monitor='val_recall', mode='max', factor=0.5, patience=5, min_lr=1e-5)
ckpt_bin = ModelCheckpoint('best_bin_model.keras', monitor='val_recall', mode='max', save_best_only=True, verbose=1)
X_tr, X_val, y_tr, y_val = train_test_split(
X_train_res, y_train_res, test_size=0.1, random_state=42, stratify=y_train_res)
final_bin_model.fit(X_tr, y_tr, epochs=120, batch_size=int(best_bin['batch_size']), verbose=2,
validation_data=(X_val, y_val), class_weight=class_weight,
callbacks=[es_bin, lr_bin, ckpt_bin])
# --------------------------------------------------
# 9. 阈值搜索(最大 F1)
# --------------------------------------------------
y_val_prob = final_bin_model.predict(X_val, verbose=0).ravel()
ths = np.arange(0.05, 0.95, 0.02)
best_th, best_f1 = 0.3, 0
for th in ths:
ff = f1_score(y_val, (y_val_prob > th).astype(int))
if ff > best_f1:
best_f1, best_th = ff, th
print(f'最优阈值 = {best_th:.3f}, F1 = {best_f1:.4f}')
# --------------------------------------------------
# 10. 构造异常样本多分类数据集
# --------------------------------------------------
abn_mask = y_train_res == 1
X_train_abn = X_train_res[abn_mask]
y_type_train = y_type_raw[np.where(y_train_res == 1)[0]]
mask = ~pd.isna(y_type_train)
X_train_abn, y_type_train = X_train_abn[mask], y_type_train[mask].astype(int) - 1 # 0-based
num_classes = 5
# --------------------------------------------------
# 11. 多分类模型
# --------------------------------------------------
def build_type_model(lstm1, lstm2, lstm3, dropout, lr):
inputs = Input(shape=(time_steps, X_train_abn.shape[2]))
x = Bidirectional(LSTM(int(lstm1), return_sequences=True))(inputs)
x = BatchNormalization()(x); x = Dropout(dropout)(x)
x = LSTM(int(lstm2), return_sequences=True)(x)
x = BatchNormalization()(x); x = Dropout(dropout)(x)
att = Attention()([x, x])
x = Concatenate()([x, att])
x = LSTM(int(lstm3))(x)
x = BatchNormalization()(x); x = Dropout(dropout)(x)
outputs = Dense(num_classes, activation='softmax')(x)
model = Model(inputs, outputs)
model.compile(optimizer=Nadam(learning_rate=lr, clipnorm=1.0),
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
return model
# --------------------------------------------------
# 12. 多分类贝叶斯优化
# --------------------------------------------------
def type_cv(lstm1, lstm2, lstm3, dropout, lr, batch_size):
lstm1, lstm2, lstm3, batch_size = map(int, (lstm1, lstm2, lstm3, batch_size))
dropout, lr = float(dropout), float(lr)
model = build_type_model(lstm1, lstm2, lstm3, dropout, lr)
es = EarlyStopping(monitor='val_accuracy', mode='max', patience=5, restore_best_weights=True, verbose=0)
X_tr, X_val, y_tr, y_val = train_test_split(
X_train_abn, y_type_train, test_size=0.1, random_state=42, stratify=y_type_train)
model.fit(X_tr, y_tr, epochs=30, batch_size=batch_size, verbose=0,
validation_data=(X_val, y_val), callbacks=[es])
return max(model.history.history['val_accuracy'])
pbounds_type = {'lstm1': (64, 256), 'lstm2': (32, 128), 'lstm3': (16, 96),
'dropout': (0.1, 0.5), 'lr': (1e-4, 5e-3), 'batch_size': (16, 64)}
type_opt = BayesianOptimization(f=type_cv, pbounds=pbounds_type, random_state=42, verbose=2)
type_opt.maximize(init_points=6, n_iter=12)
best_type = type_opt.max['params']
# --------------------------------------------------
# 13. 重新训练多分类模型(去掉 batch_size)
# --------------------------------------------------
best_type_pop = best_type.copy()
best_type_pop.pop('batch_size', None)
final_type_model = build_type_model(**best_type_pop)
es_type = EarlyStopping(monitor='val_accuracy', mode='max', patience=20, restore_best_weights=True, verbose=1)
lr_type = ReduceLROnPlateau(monitor='val_accuracy', mode='max', factor=0.5, patience=5, min_lr=1e-5)
ckpt_type = ModelCheckpoint('best_type_model.keras', monitor='val_accuracy', mode='max', save_best_only=True, verbose=1)
final_type_model.fit(X_train_abn, y_type_train, epochs=120,
batch_size=int(best_type['batch_size']), verbose=2,
validation_split=0.1, callbacks=[es_type, lr_type, ckpt_type])
# --------------------------------------------------
# 14. 级联预测(测试集)
# --------------------------------------------------
y_test_prob = final_bin_model.predict(X_test, verbose=0).ravel()
abn_mask = y_test_prob > best_th
out_df = pd.DataFrame({
'sample_idx': np.arange(len(X_test)),
'异常概率': y_test_prob,
'是否异常': abn_mask.astype(int)
})
if abn_mask.sum() == 0:
print('测试集未检测到异常磨损')
else:
X_test_abn = X_test[abn_mask]
type_pred_prob = final_type_model.predict(X_test_abn, verbose=0)
type_pred = np.argmax(type_pred_prob, axis=1) + 1 # 回到 1–5
out_df.loc[abn_mask, '预测磨损类型'] = type_pred
print('\n异常类型预测分布:', out_df['预测磨损类型'].value_counts().sort_index())
# 保存结果
out_df.to_excel('测试集_级联预测结果.xlsx', index=False)
print('\n结果已保存 → 测试集_级联预测结果.xlsx')C:\Users\Lenovo\PycharmProjects\pythonProject1\.venv\Scripts\python.exe C:\Users\Lenovo\PycharmProjects\pythonProject1\异常磨损.py
| iter | target | lstm1 | lstm2 | lstm3 | dropout | lr | batch_... | gamma | alpha |
-------------------------------------------------------------------------------------------------------------------------
2025-09-23 22:25:54.170990: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: SSE3 SSE4.1 SSE4.2 AVX AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
| 1 | 1.0 | 135.91170 | 123.26857 | 74.559515 | 0.3394633 | 0.0008644 | 23.487736 | 1.5871254 | 0.9165440 |
| 2 | 0.9838709 | 179.41408 | 99.974967 | 17.646759 | 0.4879639 | 0.0041789 | 26.192277 | 1.7727374 | 0.7458511 |
| 3 | 0.9946236 | 122.41451 | 82.376617 | 50.555601 | 0.2164916 | 0.0030980 | 22.695705 | 1.9382169 | 0.7915904 |
| 4 | 1.0 | 151.56543 | 107.37689 | 31.973902 | 0.3056937 | 0.0030028 | 18.229619 | 2.4113172 | 0.7426310 |
| 5 | 1.0 | 76.489905 | 123.09301 | 93.250562 | 0.4233589 | 0.0015926 | 20.688261 | 2.5263495 | 0.8100381 |
| 6 | 1.0 | 87.431341 | 79.536983 | 18.751081 | 0.4637281 | 0.0013680 | 47.801069 | 1.9675666 | 0.8300170 |
| 7 | 0.9838709 | 151.64387 | 108.05156 | 33.210093 | 0.3047760 | 0.0019968 | 17.370767 | 2.2596136 | 0.7493951 |
| 8 | 1.0 | 207.63514 | 52.826583 | 25.072802 | 0.3162133 | 0.0018876 | 61.947854 | 2.7411665 | 0.9293844 |
| 9 | 1.0 | 116.10946 | 57.215411 | 33.765850 | 0.1049776 | 0.0007837 | 63.286934 | 1.8170114 | 0.7413915 |
| 10 | 1.0 | 80.761740 | 61.367136 | 32.208205 | 0.3440713 | 0.0023078 | 36.162482 | 1.6843879 | 0.7710770 |
| 11 | 1.0 | 153.20095 | 66.419383 | 95.684081 | 0.4571187 | 0.0022739 | 57.864851 | 1.8958393 | 0.8710439 |
| 12 | 1.0 | 87.445312 | 121.16384 | 25.812336 | 0.3630193 | 0.0040144 | 31.316988 | 2.3228903 | 0.8363443 |
| 13 | 1.0 | 73.709315 | 119.30836 | 38.733579 | 0.1933675 | 0.0045279 | 29.510451 | 2.1319602 | 0.8940009 |
| 14 | 1.0 | 182.27697 | 45.327479 | 19.691559 | 0.1714069 | 0.0027865 | 25.721406 | 2.1917102 | 0.8625757 |
| 15 | 1.0 | 152.19878 | 42.491115 | 63.709088 | 0.1071735 | 0.0038790 | 40.120381 | 2.7839652 | 0.7519634 |
| 16 | 1.0 | 197.40245 | 43.079261 | 38.372960 | 0.3732381 | 0.0037628 | 57.029413 | 2.7235014 | 0.7404201 |
| 17 | 1.0 | 176.97395 | 61.954725 | 68.060175 | 0.3824341 | 0.0015251 | 36.412878 | 2.7052709 | 0.8370069 |
| 18 | 0.9892473 | 215.98821 | 126.71244 | 36.321956 | 0.1183744 | 0.0018048 | 61.276768 | 2.8230706 | 0.7190355 |
=========================================================================================================================
Epoch 1/120
Traceback (most recent call last):
File "C:\Users\Lenovo\PycharmProjects\pythonProject1\异常磨损.py", line 127, in <module>
final_bin_model.fit(X_tr, y_tr, epochs=120, batch_size=int(best_bin['batch_size']), verbose=2,
File "C:\Users\Lenovo\PycharmProjects\pythonProject1\.venv\Lib\site-packages\keras\src\utils\traceback_utils.py", line 122, in error_handler
raise e.with_traceback(filtered_tb) from None
File "C:\Users\Lenovo\PycharmProjects\pythonProject1\异常磨损.py", line 70, in focal_loss_fixed
return -tf.reduce_mean(alpha_t * tf.pow(1 - p_t, gamma) * tf.math.log(p_t))
~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
TypeError: Input 'y' of 'Mul' Op has type float32 that does not match type float64 of argument 'x'.
进程已结束,退出代码为 1