You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scikit-learn神经网络中整合反应器质量平衡约束?

问题描述

我用Python的Scikit-learn构建反应器操作的神经网络模型,模型包含4个输入(X1、X2、X3、X4)和3个输出(Y1、Y2、Y3)。要求预测输出必须满足质量平衡方程:Y1 + Y2 + Y3 - 进料质量流量 = 0。想知道能不能把这个约束整合进神经网络?具体怎么实现?另外我已经清理掉所有不满足质量平衡的样本,现有代码如下:

train_x = ...
train_y = ...
test_x = ...
test_y = ...

processed_data = data[["Y1.PV", "Y2.PV", "Y3.PV", "X1.PV", "X2.PV", "X3.PV", "X4.PV"]]
train, test = train_test_split(processed_data, test_size=.25, random_state=np.random.RandomState(1))

train_x = train.drop(["Y1.PV", "Y2.PV", "Y3.PV"], axis=1)
train_y = train.drop(["Y1.PV", "Y2.PV", "Y3.PV"], axis=1)

test_x = test.drop(["Y1.PV", "Y2.PV", "Y3.PV"], axis=1)
test_y = test.drop(["Y1.PV", "Y2.PV", "Y3.PV"], axis=1)

### TUNED MODEL

best_iter = 2000
best_hidden_layer = 200

neural_net = MLPRegressor(solver='lbfgs', hidden_layer_sizes=best_hidden_layer, random_state=1, max_iter=best_iter)
scaler = StandardScaler()
model = make_pipeline(scaler, neural_net)
model.fit(train_x, train_y)

### END SOLUTION

predict_y_train = model.predict(train_x)
predict_y = model.predict(test_x)

fig, axs = plt.subplots(ncols=2, figsize=(10,4))
axs[0].scatter(train_y, predict_y_train)
axs[0].set_title("Train data")
axs[1].scatter(test_y, predict_y)
axs[1].set_title("Test data")

print("MSE train:", mean_squared_error(train_y, predict_y_train))
print("MSE test:", mean_squared_error(test_y, predict_y))
解决方案

当然可以把质量平衡约束整合进神经网络,主要有两种实用思路:

1. 输出层结构改造(严格满足约束)

核心逻辑是利用Y1 + Y2 + Y3 = F(F为进料质量流量)的关系,将模型输出从3个缩减为2个,第三个输出通过约束直接计算:

  • 训练阶段:把Y1和Y2作为模型的目标输出,Y3由F - Y1 - Y2推导得到
  • 预测阶段:模型先输出Y1_pred和Y2_pred,再通过约束公式计算出Y3_pred

这种方法能100%满足质量平衡,无需修改损失函数,适配Scikit-learn的MLPRegressor,具体实现步骤:

  1. 修正现有代码的错误:train_y和test_y不应删除Y列,需保留Y1.PV、Y2.PV作为训练目标(同时要保留进料流量F的数据,比如新增列F.PV)
  2. 调整模型输出维度:MLPRegressor会自动适配train_y的列数,无需额外设置
  3. 预测后处理:计算第三个输出,确保满足约束

示例调整代码片段:

# 假设进料质量流量列名为F.PV,先提取数据
train_F = train["F.PV"].values
test_F = test["F.PV"].values

# 修正训练目标,只保留Y1和Y2
train_y = train[["Y1.PV", "Y2.PV"]]
test_y = test[["Y1.PV", "Y2.PV"]]

# 训练模型(输出维度自动适配为2)
model.fit(train_x, train_y)

# 预测并计算Y3,生成完整的3个输出
predict_y_train = model.predict(train_x)
Y1_train_pred = predict_y_train[:, 0]
Y2_train_pred = predict_y_train[:, 1]
Y3_train_pred = train_F - Y1_train_pred - Y2_train_pred
predict_y_train_full = np.column_stack([Y1_train_pred, Y2_train_pred, Y3_train_pred])

# 测试集同理处理
predict_y = model.predict(test_x)
Y1_pred = predict_y[:, 0]
Y2_pred = predict_y[:, 1]
Y3_pred = test_F - Y1_pred - Y2_pred
predict_y_full = np.column_stack([Y1_pred, Y2_pred, Y3_pred])

2. 自定义损失函数(软约束)

如果希望模型在训练过程中尽可能逼近约束(而非强制严格满足),可以在原有MSE损失基础上添加约束惩罚项:
损失函数 = MSE(Y_true, Y_pred) + λ * (Y1_pred + Y2_pred + Y3_pred - F)²
其中λ是惩罚系数,值越大约束权重越高。

但Scikit-learn的MLPRegressor不支持直接自定义损失函数,需要改用Keras/TensorFlow实现神经网络,示例思路:

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

# 自定义带约束的损失函数
def constrained_loss(F):
    def loss(y_true, y_pred):
        mse = tf.keras.losses.MSE(y_true, y_pred)
        # 计算质量平衡误差的平方作为惩罚项
        balance_error = tf.square(tf.reduce_sum(y_pred, axis=1) - F)
        return mse + 0.1 * balance_error  # λ=0.1,可根据需求调整
    return loss

# 构建神经网络
model = Sequential([
    Dense(200, activation='relu', input_shape=(4,)),
    Dense(3)  # 输出3个变量
])

# 编译模型时传入自定义损失(F为训练集的进料流量)
model.compile(optimizer='adam', loss=constrained_loss(train_F))

# 训练模型
model.fit(train_x, train_y, epochs=2000, batch_size=32)

额外注意事项

  • 你现有代码存在明显错误:train_y和test_y执行了drop(["Y1.PV", "Y2.PV", "Y3.PV"]),导致训练目标为空,必须修正为保留这三列作为输出目标
  • 如果进料流量F不是当前输入特征之一,需要将其加入train_x和test_x,确保模型能获取该关键信息

内容的提问来源于stack exchange,提问作者Filip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 11:01:28