ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

化工生产预测实战:从特征工程到LightGBM部署

化工生产预测实战:从特征工程到LightGBM部署 简介面向化工生产预测场景这个资源包适合需要了解数据驱动建模流程的学生、工程师或竞赛参与者用于解决产品指标预测与工艺参数优化问题。压缩包共22个文件、约696KB以csv数据文件为核心包含产品检验与提交结果数据辅以Python脚本、算法说明书pdf、README说明和可视化png图可快速了解数据到预测结果的完整链路目前已有220人学习下载。内容覆盖化工生产中的温度、压力、反应时间等多参数影响分析以及数据清洗、特征构造、模型训练与验证、模型解释等环节。其中csv样本与结果文件便于复现提交算法说明书与README可辅助理解建模思路可视化图片直观展示磷含量、氮含量等指标预测效果xml与iml文件则保留了项目工程配置方便直接打开浏览。整体麻雀虽小五脏俱全可作为数据建模入门或化工竞赛参考。1. 化工生产预测不是堆模型从一份历史检验报告说起许多工厂每天都会产生大量检验报告但报告出来时生产往往已经结束。chemical_products-master这个项目正是瞄准这个痛点利用历史检验报告与过程参数预测下一批产品的磷含量、氮含量、总养分、颗粒大小和水分含量。它不是一套复杂的工业软件而是一个可以本地跑通的预测模型工程包含数据、训练脚本、结果文件和算法说明书。这里会从数据清洗、特征构建、模型选型、结果验证到接口化部署完整拆解这类化工生产预测任务的操作路径适合做过程工业数据分析和智能制造转型的工程师参考。2. 数据清洗与特征工程把检验报告变成可学习的时间序列2.1 原始数据结构和主要字段data目录下的产品检验报告按照日期命名产品检验报告2018-4-1.csv是历史批次的主数据集产品检验报告2018-5-1-sample.csv看起来是预留的评估样本。每一行通常对应一个生产批次或一个取样时间点列会分成两部分一部分是过程变量比如反应温度、反应压力、搅拌速度、pH、原料配比另一部分是质量指标比如磷含量、氮含量、总养分、颗粒大小、水分含量。这类数据最常见的麻烦是粒度不一致。过程参数可能在DCS系统里是分钟级记录而化验室指标每天只出几条甚至一个班次只有一条。如果直接用原始行做训练模型会对时间对齐方式非常敏感。我一般先确定“预测目标发生在什么时刻”然后把该时刻之前一段时间内的过程变量聚合为均值、最大值、最小值和标准差作为这个批次的输入特征。从项目的运行说明和算法说明书来看预处理脚本承担的就是这类工作。实际处理时我还会把日期列统一成datetime类型并检查是否存在同一时间点重复采样。如果有重复保留最后一条或按批次ID去重否则后续滞后特征会出现同一时刻多条记录同时参与训练的情况。2.2 缺失值处理和异常值剔除化工过程数据的缺失通常来自传感器短时离线、设备清洗或人工漏录。直接用均值填充会掩盖前后趋势突变我习惯先按时间排序再用线性插值处理稀疏缺失对整段缺失则用邻近正常值填充。下面这段代码可以看作是快速起步的模板import pandas as pd import numpy as np # 注意有些工厂导出CSV是GBK编码 df pd.read_csv(产品检验报告2018-4-1.csv, encodinggbk, parse_dates[date]) print(df.isna().mean().sort_values(ascendingFalse)) # 时间排序是插值和切分的基础 df df.sort_values(date).reset_index(dropTrue) # 线性插值最多向外填充2个点避免长段伪造趋势 df df.interpolate(methodlinear, limit2, limit_directionboth) df df.ffill().bfill() # 用IQR识别过程量的离群点先置空再插值 def filter_outlier(s): q1, q3 s.quantile(0.25), s.quantile(0.75) iqr q3 - q1 return s.where((s q1 - 1.5 * iqr) (s q3 1.5 * iqr), np.nan) for col in [temperature, pressure, stir_speed]: df[col] filter_outlier(df[col]) df df.interpolate(methodlinear, limit2, limit_directionboth)这段代码里isna().mean()能快速暴露哪些列缺失比例过高如果某列缺失超过40%建议直接放弃而不是强行填充。sort_values和reset_index是必须的因为插值是按行顺序进行的原始文件如果按产线或人工分类排列插值结果会错位。limit参数限制向外插值的次数避免连续几十个值都是插出来的假数据。异常值处理需要特别注意质量指标列的异常值不要随意剔除因为预测目标本身可能就包含真实波动。如果某个批次的磷含量特别高先查生产日志确认是配料错误还是化验误差再决定是否修正。2.3 特征构造滞后变量与移动平均特征化工生产有很强的惯性。当前批次的质量不仅取决于当前过程参数还与前几个批次的运行状态有关比如反应釜壁的残留物、换热器效率下降都会带来缓慢漂移。构造滞后变量和滚动特征就是把这些历史状态显式喂给模型。feature_cols [] # 滞后1-3批次的过程量 for lag in [1, 2, 3]: df[ftemp_lag{lag}] df[temperature].shift(lag) df[fpressure_lag{lag}] df[pressure].shift(lag) feature_cols [ftemp_lag{lag}, fpressure_lag{lag}] # 滚动统计过去3个批次的均值、最大值和标准差 df[temp_ma3] df[temperature].rolling(3).mean() df[temp_max3] df[temperature].rolling(3).max() df[temp_std3] df[temperature].rolling(3).std() feature_cols [temp_ma3, temp_max3, temp_std3] # 前几行会因为shift产生空值建模前丢弃 df_model df.dropna(subsetfeature_cols).reset_index(dropTrue)滞后步数是需要实验的。先用3步只是起点如果自相关图显示影响持续更长可以把滞后步数扩大到5或8。滚动窗口3和5的选择取决于批次持续时间窗口太大会把不同工况的数据混在一起窗口太小又捕捉不到慢变化。这里有一个容易忽略的点rolling默认包含当前行如果目标是预测“当前批次”的结果但当前过程量还没结束就需要把滚动窗口整体前移一步也就是用t-3到t-1的数据构造特征再用它们预测t。提示在做滞后特征时shift之后的前N行会产生NaN。如果直接dropna会损失最早几个批次的样本如果样本量紧张可以考虑用第一个正常值回填但回填比例不宜超过5%。3. 模型选型与训练从线性基线到LightGBM的多目标预测3.1 选型逻辑为什么不是深度学习化工生产预测的数据量通常不大一个工厂一年也就数千条检验记录。在这个尺度下深度学习模型很难发挥优势反而容易过拟合。我一般先跑一个线性回归作为基线再上树模型。线性回归能快速检查特征方向是否合理比如温度升高时磷含量预测是上升还是下降如果方向和工艺常识相反说明特征或数据有问题。如果线性基线的R2已经达到0.85以上说明问题本身不太复杂树模型的提升空间有限。如果R2很低再考虑LightGBM或XGBoost。树模型擅长处理特征交互比如“高温度低压力”组合对反应效率的影响不是简单地相加。同时树模型对缺失值敏感度低可以容忍部分未填充噪声。项目附带的算法说明书通常会记录这类对比结果我在实际项目里也会把基线结果写进运行说明方便后续回溯。这里的核心观点是先用简单模型确认数据和特征的质量再上复杂模型而不是一上来就套Stacking或神经网络。3.2 时间序列交叉验证与LightGBM参数训练集的划分必须尊重时间顺序不能随机打乱。LightGBM的early stopping也需要一个时间上“更晚”的验证集来模拟未来预测。下面以预测磷含量为例import lightgbm as lgb from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_squared_error tscv TimeSeriesSplit(n_splits5) mse_list [] models [] for fold, (train_idx, valid_idx) in enumerate(tscv.split(X)): X_train, X_valid X.iloc[train_idx], X.iloc[valid_idx] y_train, y_valid y.iloc[train_idx], y.iloc[valid_idx] model lgb.LGBMRegressor( n_estimators800, learning_rate0.03, num_leaves31, max_depth6, subsample0.8, colsample_bytree0.8, reg_alpha0.1, reg_lambda0.5, random_state42, verbosity-1 ) model.fit( X_train, y_train, eval_set[(X_valid, y_valid)], callbacks[lgb.early_stopping(100)] ) pred model.predict(X_valid) mse_list.append(mean_squared_error(y_valid, pred)) models.append(model) print(f平均MSE: {np.mean(mse_list):.6f} ± {np.std(mse_list):.6f})TimeSeriesSplit按顺序生成5组训练/验证切分第一折用最早的数据训练最后一折用最近的数据验证。这样评估指标反映的是“用过去预测未来”的真实能力。参数表里的n_estimators和learning_rate要配合使用后者越小模型需要更多树才能收敛但泛化能力通常更好。num_leaves和max_depth控制模型复杂度数据量小的时候建议从31和6往下调比如num_leaves16max_depth5。subsample和colsample_bytree是两类随机采样分别从行和列上减少过拟合。reg_alpha和reg_lambda是L1和L2正则项特征数量多时通过增加这两个值可以让特征权重分布更平滑。early_stopping的100表示验证集分数连续100轮不提升就停止可以节省训练时间但要注意不要把eval_set也用于最终模型测试否则会产生乐观偏差。3.3 多目标预测和结果文件输出项目中要预测的不只是磷含量还有氮含量、总养分、颗粒大小、水分等指标。这些目标之间存在相关性但分开建模更容易维护。MultiOutputRegressor可以帮我们一次性训练多个目标每个目标内部保持独立的LightGBM模型。from sklearn.multioutput import MultiOutputRegressor y_train_multi df_model[target_cols].values y_test_multi df_test[target_cols].values multi_model MultiOutputRegressor( lgb.LGBMRegressor(n_estimators500, learning_rate0.05, num_leaves31, random_state42) ) multi_model.fit(X_train, y_train_multi) y_pred_multi multi_model.predict(X_test) result_df pd.DataFrame(y_pred_multi, columnstarget_cols) result_df.insert(0, batch_id, df_test[batch_id].values) result_df.to_csv(result_0.353305.csv, indexFalse)这个输出的CSV会和项目中的result_0.353305.csv结构类似每行对应一个测试批次每列对应一个质量指标的预测值。文件名里的0.353305我倾向于理解为验证阶段的某个误差指标比如MSE或MAE。后续每次改特征或参数都用同样的评估流程计算这个分数才能判断改动到底是提升还是退步。使用MultiOutputRegressor时如果某个指标的数据缺失较多最好单独剔除缺失样本再训练不要让缺失值污染其他目标的模型。另外如果不同目标的量纲差异很大比如磷含量在15-25之间水分在0.5-2之间模型评估时要分别计算MSE不能直接平均。4. 结果验证与业务解读从误差曲线到工艺调整建议4.1 残差分析别只盯着平均误差模型预测完成后第一步是把预测结果和真实检验报告按照batch_id合并然后按时间画出残差。每个批次的实际检验值虽然晚于预测时刻但用于离线验证是完全正确的。下面代码展示了基本流程result pd.read_csv(result_0.353305.csv) truth pd.read_csv(产品检验报告2018-5-1-sample.csv) merged pd.merge(truth, result, onbatch_id, suffixes(_real, _pred)) merged[residual] merged[phosphorus_content_real] - merged[phosphorus_content_pred] plt.figure(figsize(10, 5)) plt.plot(merged[date], merged[residual], o-, alpha0.7) plt.axhline(0, colorred, linestyle--) plt.axhline(0.05, colorgray, linestyle:) plt.axhline(-0.05, colorgray, linestyle:)如果残差长期在零轴之上说明模型系统性地低估可能训练集和验证集来自不同季节或不同原料供应商。如果残差在正负0.05之间随机波动说明误差在可接受范围内。还可以按班组、按原料批次分箱检查误差是否集中在特定工艺条件下。4.2 异常批次定位与处理把残差绝对值超过2倍标准差的样本挑出来建立一张排查表。实际项目里这些异常点往往是模型迭代的最大价值来源。表格可以记录批次号、预测值、实际值、残差以及初步判断批次号实际磷含量预测磷含量残差可能原因B2018051622.320.81.5温度传感器漂移B2018052318.921.0-2.1前段清洗不彻底B2018060424.122.61.5原料批次更换这些异常点不用急着从训练集删掉。先核对生产日志和DCS历史曲线如果确认是数据记录错误再修正或剔除如果确认是真实工艺波动就要思考为什么模型没有学到这个模式。比如“原料批次更换”这个因素在原始特征里没有被编码加入一个“原料供应商”离散特征往往比调模型参数更有效。4.3 用SHAP解释预测结果化工工程师接受模型的前提是模型能解释。SHAP值可以告诉我们每个特征对某个预测结果贡献了多少。LightGBM是树模型直接用TreeExplainer速度很快import shap explainer shap.TreeExplainer(models[-1]) shap_values explainer.shap_values(X_valid) shap.summary_plot(shap_values, X_valid, feature_namesfeature_cols)summary_plot会按特征重要性排序颜色表示特征值高低。比如“上一批次的磷含量”排在第一位这在业务上完全合理因为原料循环和反应釜残留会导致批次间延续性。如果“搅拌速度”几乎没有贡献不一定是它不重要而是它和“反应温度”高度相关信息已经被温度特征覆盖了。此时可以保留两个特征不影响模型效果但解释时不要单独宣称搅拌速度无关。SHAP的另一个用法是单批次诊断。当某个批次预测偏高时用shap.force_plot查看是哪些特征把预测值推高然后工艺员可以针对性地检查对应传感器。这比单纯给出一个预测值有用得多。5. 把预测模型接进生产接口、重训和验证技巧5.1 用FastAPI封装预测服务离线模型要产生价值需要接进工厂的MES或报表系统。最常见的部署方式是FastAPI输入当前批次的工艺参数返回预测结果。接口层应该做输入范围校验防止传感器异常值直接进模型。from fastapi import FastAPI from pydantic import BaseModel import joblib app FastAPI() model joblib.load(lgb_phosphorus.joblib) class ProcessParams(BaseModel): temperature: float pressure: float stir_speed: float ph: float app.post(/predict) def predict(params: ProcessParams): x [[params.temperature, params.pressure, params.stir_speed, params.ph]] if not (0 params.temperature 200): return {error: temperature out of range} pred model.predict(x)[0] return {phosphorus_content: pred}接口上线后要记录每一次请求的输入和输出便于后续分析预测分布漂移。如果某一天模型预测值整体偏高检查输入分布是不是和训练集不同。5.2 模型重训触发策略生产模型不能一直用旧数据。我会设置两个触发条件每积累50个新批次自动生成候选模型连续7天预测残差均值超过阈值则告警重训。重训后先在历史数据上回放确认指标不落后再切换。不要每次有微小提升就替换模型否则维护成本很高。5.3 最值得养成的验证技巧最后想说一个最容易忽略的细节日期格式和批次ID的统一。很多项目模型本身没问题但上线后对不上号原因是产品检验报告中的日期是2018-4-1而MES导出的是2018-04-01字符串排序完全不一样。建议所有数据在进入建模流程前统一成ISO格式的日期和字符串型批次号并保留一个原始列作为回溯依据。这样看起来是很小的事但带来的上线阻塞往往比模型调参更严重。本文还有配套的精品资源点击获取
返回列表