TensorFlow中MLP结合数据与物理损失训练的目标分离问题求助
问题分析与修正方案
核心错误集中在混合损失函数的张量操作逻辑、指标计算的维度匹配两个方面,以下是针对性修正方案:
修正后的完整代码
import tensorflow as tf import pandas as pd import numpy as np from sklearn.preprocessing import StandardScaler from tensorflow.keras.models import Sequential from tensorflow.keras import regularizers from tensorflow.keras.layers import Dense from tensorflow.keras.optimizers import SGD from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score import joblib # 加载数据 train_csv_path = "/home/qiki1832/PycharmProjects/datasets_TABULA/GroundTruth_train20250219.csv" val_csv_path = "/home/qiki1832/PycharmProjects/datasets_TABULA/GroundTruth_val20250218.csv" # 读取并清洗数据 df_train = pd.read_csv(train_csv_path, delimiter=",").dropna() df_val = pd.read_csv(val_csv_path, delimiter=",").dropna() # 特征与目标列定义 features = ['T_se', 'T_si', 'q_si', 'd'] target_true = ['R'] # 真实回归标签 target_phys = ['R_phys,i'] # 物理模型预测值 # 提取特征与双目标数据 X_train = df_train[features].values y_train_true = df_train[target_true].values y_train_phys = df_train[target_phys].values X_val = df_val[features].values y_val_true = df_val[target_true].values y_val_phys = df_val[target_phys].values # 归一化输入特征 scaler_X = StandardScaler() X_train_scaled = scaler_X.fit_transform(X_train) X_val_scaled = scaler_X.transform(X_val) # 归一化真实标签 scaler_y = StandardScaler() y_train_true_scaled = scaler_y.fit_transform(y_train_true) y_val_true_scaled = scaler_y.transform(y_val_true) # 归一化物理模型预测值(独立scaler) scaler_phys = StandardScaler() y_train_phys_scaled = scaler_phys.fit_transform(y_train_phys) y_val_phys_scaled = scaler_phys.transform(y_val_phys) # 保存scaler用于后续推理 joblib.dump(scaler_X, "/home/qiki1832/PycharmProjects/PINN/PhysicsRegularisation/MLP_01/transient_scaler_X.pkl") joblib.dump(scaler_y, "/home/qiki1832/PycharmProjects/PINN/PhysicsRegularisation/MLP_01/transient_scaler_y.pkl") joblib.dump(scaler_phys, "/home/qiki1832/PycharmProjects/PINN/PhysicsRegularisation/MLP_01/transient_scaler_phys.pkl") # 合并双目标为单一张量(列维度拼接) y_train_tensor = tf.convert_to_tensor(np.hstack([y_train_true_scaled, y_train_phys_scaled]), dtype=tf.float32) y_val_tensor = tf.convert_to_tensor(np.hstack([y_val_true_scaled, y_val_phys_scaled]), dtype=tf.float32) X_train_tensor = tf.convert_to_tensor(X_train_scaled, dtype=tf.float32) X_val_tensor = tf.convert_to_tensor(X_val_scaled, dtype=tf.float32) print('训练数据形状:', X_train_tensor.shape, y_train_tensor.shape) print('验证数据形状:', X_val_tensor.shape, y_val_tensor.shape) # 定义MLP模型 def create_model(): model = Sequential([ Dense(4, activation="relu", input_shape=(X_train_tensor.shape[1],), kernel_regularizer=regularizers.l2(1e-3)), Dense(1, activation="linear") # 输出为预测的R值 ]) return model model = create_model() optimizer = SGD(learning_rate=0.001, momentum=0.8) # 修正后的混合损失函数(全TensorFlow原生操作) def hybrid_loss(y_true, y_pred): # 从合并的目标张量中分离真实标签与物理模型值 R_true = y_true[:, 0:1] R_phys_scaled = y_true[:, 1:2] # 已完成归一化,直接使用 # 计算真实数据损失 L_data = tf.reduce_mean(tf.square(y_pred - R_true)) # 计算物理约束损失 L_physics = tf.reduce_mean(tf.square(y_pred - R_phys_scaled)) # 加权组合总损失 physics_weight = 0.05 data_weight = 0.95 return data_weight * L_data + physics_weight * L_physics # 自定义模型保存回调 class CustomModelCheckpoint(tf.keras.callbacks.Callback): def __init__(self, filepath, save_freq=1): self.filepath = filepath self.save_freq = save_freq def on_epoch_end(self, epoch, logs=None): if (epoch + 1) % self.save_freq == 0: print(f"\n保存第 {epoch + 1} 轮模型...") self.model.save(self.filepath.format(epoch=epoch + 1)) checkpoint_callback = CustomModelCheckpoint("/home/qiki1832/PycharmProjects/PINN/PhysicsRegularisation/MLP_01/model_epoch_{epoch}.h5", save_freq=1) # 修正后的指标记录回调(仅用真实标签计算指标) class MetricsLogger(tf.keras.callbacks.Callback): def __init__(self, X_train, y_train_true, X_val, y_val_true, filepath): self.X_train = X_train self.y_train_true = y_train_true # 仅传入真实标签张量 self.X_val = X_val self.y_val_true = y_val_true self.filepath = filepath self.metrics_data = [] def on_epoch_end(self, epoch, logs=None): y_train_pred = self.model.predict(self.X_train, verbose=0) y_val_pred = self.model.predict(self.X_val, verbose=0) # 反归一化到真实尺度,计算更具实际意义的指标 y_train_pred_unscaled = scaler_y.inverse_transform(y_train_pred) y_train_true_unscaled = scaler_y.inverse_transform(self.y_train_true) y_val_pred_unscaled = scaler_y.inverse_transform(y_val_pred) y_val_true_unscaled = scaler_y.inverse_transform(self.y_val_true) # 训练集指标计算 train_mae = mean_absolute_error(y_train_true_unscaled, y_train_pred_unscaled) train_mse = mean_squared_error(y_train_true_unscaled, y_train_pred_unscaled) train_rmse = np.sqrt(train_mse) train_r2 = r2_score(y_train_true_unscaled, y_train_pred_unscaled) # 验证集指标计算 val_mae = mean_absolute_error(y_val_true_unscaled, y_val_pred_unscaled) val_mse = mean_squared_error(y_val_true_unscaled, y_val_pred_unscaled) val_rmse = np.sqrt(val_mse) val_r2 = r2_score(y_val_true_unscaled, y_val_pred_unscaled) # 获取当前学习率 current_lr = float(tf.keras.backend.get_value(self.model.optimizer.lr)) # 记录指标 self.metrics_data.append([ epoch + 1, current_lr, train_mae, train_mse, train_rmse, train_r2, val_mae, val_mse, val_rmse, val_r2 ]) # 保存到CSV文件 metrics_df = pd.DataFrame(self.metrics_data, columns=[ "Epoch", "Learning Rate", "Train MAE", "Train MSE", "Train RMSE", "Train R²", "Val MAE", "Val MSE", "Val RMSE", "Val R²" ]) metrics_df.to_csv(self.filepath, index=False) metrics_csv_path = "/home/qiki1832/PycharmProjects/PINN/PhysicsRegularisation/MLP_01/training_epoch_metrics.csv" metrics_logger = MetricsLogger(X_train_tensor, y_train_true_scaled, X_val_tensor, y_val_true_scaled, metrics_csv_path) # 编译并训练模型 model.compile(optimizer=optimizer, loss=hybrid_loss, metrics=['mae']) num_epochs = 50 batch_size = 256 model.fit( X_train_tensor, y_train_tensor, validation_data=(X_val_tensor, y_val_tensor), epochs=num_epochs, batch_size=batch_size, verbose=1, callbacks=[checkpoint_callback, metrics_logger] )
关键修正点说明
混合损失函数优化
- 移除了张量转numpy再重复归一化的冗余操作,直接使用预处理阶段完成归一化的物理模型值
- 全部采用TensorFlow原生操作,保证计算图兼容性,避免Eager/Graph模式切换报错
指标计算逻辑修正
- MetricsLogger初始化时仅传入真实标签张量,解决模型1维输出与2维目标张量的维度不匹配问题
- 增加反归一化步骤,计算真实尺度下的回归指标,结果更具业务参考价值
数据处理逻辑清晰化
- 拆分真实标签与物理模型预测值的变量命名,避免混淆
- 明确双目标张量的拼接逻辑,确保训练时输入输出维度严格匹配
内容的提问来源于stack exchange,提问作者Alexander Benz
相关产品推荐
相关产品推荐

