第 7 篇:工艺参数优化——基于 AI4I 2020 的回归与贝叶斯优化实战

本文为「从零到落地:机器学习分析数据实战系列」第 7 篇,完整系列持续更新中。


前言

前两篇解决了「发现故障」和「定位原因」的问题,这篇要解决最实际的问题:怎么调参数才能让设备运行得最好?

回顾一下前三篇的逻辑链:

  • 第 4 篇:数据工程——把原始数据加工成模型能用的特征
  • 第 5 篇:故障预警——模型能提前发现设备要出故障
  • 第 6 篇:根因分析——知道是哪种故障、为什么会出故障

但是,工厂的最终目标不是「发现问题」,而是解决问题。操作员最想知道的是:「转速调多少、扭矩控制在什么范围、多久换一次刀具,才能让故障率最低、产出最高?」

传统做法靠「老师傅经验」——干了 20 年的老技工凭感觉调参。本篇用数据驱动的方式替代经验:先用回归模型建立「参数→质量」的映射关系,再用贝叶斯优化自动搜索最优参数组合

本篇学完你将掌握

  • 用梯度提升回归建立「工艺参数 → 设备健康度」的预测模型
  • 残差分析判断模型是否可靠
  • 贝叶斯优化的原理(为什么比网格搜索更聪明)
  • Optuna 的参数搜索与多目标优化(帕累托最优)
  • 设备物理约束与安全边界处理

一、环境与数据准备

1.1 安装额外依赖

1
2
3
4
5
6
7
conda activate ml-data

# 贝叶斯优化框架
pip install optuna==4.0.*

# plotly 用于 Optuna 的交互式可视化
pip install plotly==5.24.*

1.2 数据加载与聚焦

复用第 5 篇的 load_and_prepare 函数,但这次我们聚焦可控工艺参数——只有操作员能调整的参数才有优化价值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')

def load_and_prepare(filepath='ai4i2020.csv'):
"""加载 AI4I 2020 数据集并完成特征工程(同第 5 篇)。"""
df = pd.read_csv(filepath)

df['Temperature_diff'] = df['Process temperature [K]'] - df['Air temperature [K]']
df['Power_approx'] = df['Torque [Nm]'] * df['Rotational speed [rpm]']
df['Speed_torque_ratio'] = df['Rotational speed [rpm]'] / (df['Torque [Nm]'] + 1e-6)

bins = [0, 60, 150, 300]
labels = ['early', 'mid', 'late']
df['Wear_stage'] = pd.cut(df['Tool wear [min]'], bins=bins, labels=labels)
df['Wear_stage_code'] = df['Wear_stage'].map({'early': 0, 'mid': 1, 'late': 2})
df = pd.get_dummies(df, columns=['Type'], drop_first=False)

return df

df = load_and_prepare()

# 可控工艺参数(操作员能调的)
controllable_params = ['Rotational speed [rpm]', 'Torque [Nm]', 'Tool wear [min]']

# 不可控参数(环境因素,操作员改不了)
uncontrollable_params = ['Air temperature [K]', 'Process temperature [K]']

print("可控参数统计:")
print(df[controllable_params].describe().round(2))

📌 为什么要区分可控和不可控? 优化的目的是给操作员推荐参数,如果推荐「把气温降到 295K」——但车间气温是环境决定的,操作员控制不了——这个推荐就没有意义。所以我们只优化可控参数。


二、业务理解:工艺优化的本质

这不是一个简单的「求最大值」问题

很多人以为工艺优化就是「找到一个参数组合,让某个指标最好」。实际上它是一个多目标、有约束的优化问题

  1. 多目标:工厂不只关心故障率,还要兼顾产出效率。转速越高产出越快,但故障风险也越高——这两个目标是矛盾的
  2. 有约束:设备有物理极限(转速不能超过 3000 rpm),还有安全边界(不能在极限参数附近运行)
  3. 参数间耦合:扭矩和转速在物理上是关联的(功率 = 扭矩 × 角速度),不能独立调整

所以工艺优化的本质是:在设备约束范围内,找到一组参数组合,让多个目标达到「最合理的平衡」。这个平衡点叫做「帕累托最优」——后面会详细解释。


三、回归建模——建立参数-质量映射

3.1 目标变量定义:用「健康度」作为质量代理指标

我们没有一个直接的「产品质量」指标,但第 6 篇已经用二分类模型的故障概率构造了「健康度」指标(health_score = 1 - 故障概率)。

为什么用健康度? 因为:

  • 健康度高 → 故障概率低 → 良率高、设备稳定
  • 它是一个 0-1 之间的连续数值,适合做回归目标
  • 操作员的目标很明确:让健康度尽可能高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from sklearn.model_selection import train_test_split
from xgboost import XGBClassifier
from imblearn.over_sampling import SMOTE

FEATURES = ['Torque [Nm]', 'Speed_torque_ratio',
'Rotational speed [rpm]', 'Tool wear [min]',
'Wear_stage_code', 'Temperature_diff',
'Air temperature [K]', 'Power_approx']

# 先用二分类模型计算健康度(同第 6 篇)
X = df[FEATURES]
y_binary = df['Machine failure']

X_train, X_test, y_train, y_test = train_test_split(
X, y_binary, test_size=0.3, random_state=42, stratify=y_binary
)

smote = SMOTE(random_state=42)
X_train_sm, y_train_sm = smote.fit_resample(X_train, y_train)

xgb_cls = XGBClassifier(n_estimators=200, max_depth=4, random_state=42,
eval_metric='logloss')
xgb_cls.fit(X_train_sm, y_train_sm)

# 健康度 = 1 - 故障概率
df['health_score'] = 1 - xgb_cls.predict_proba(df[FEATURES])[:, 1]
print(f"健康度范围: {df['health_score'].min():.3f} ~ {df['health_score'].max():.3f}")
print(f"健康度均值: {df['health_score'].mean():.3f}")
1
2
健康度范围: 0.124 ~ 0.998
健康度均值: 0.954

3.2 梯度提升回归建模

现在我们要建立一个回归模型:输入工艺参数,输出健康度预测值。这样操作员就可以问模型:「如果我把转速调到 1600 rpm、扭矩控制在 35 Nm,健康度会是多少?」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error

# 用可控参数 + 磨损阶段作为输入(因为这些是操作员能控制的)
regress_features = controllable_params + ['Wear_stage_code']

X_reg = df[regress_features]
y_reg = df['health_score']

X_r_train, X_r_test, y_r_train, y_r_test = train_test_split(
X_reg, y_reg, test_size=0.3, random_state=42
)

# 梯度提升回归
gbr = GradientBoostingRegressor(
n_estimators=200,
max_depth=4,
learning_rate=0.1,
random_state=42
)
gbr.fit(X_r_train, y_r_train)

y_r_pred = gbr.predict(X_r_test)

# 评估指标
rmse = np.sqrt(mean_squared_error(y_r_test, y_r_pred))
mae = mean_absolute_error(y_r_test, y_r_pred)
r2 = r2_score(y_r_test, y_r_pred)

print(f"回归模型评估:")
print(f" RMSE: {rmse:.4f} (预测误差的标准差)")
print(f" MAE: {mae:.4f} (平均绝对误差)")
print(f" R²: {r2:.4f} (拟合优度,1.0=完美)")
1
2
3
4
回归模型评估:
RMSE: 0.0312 (预测误差的标准差)
MAE: 0.0198 (平均绝对误差)
R²: 0.8234 (拟合优度,1.0=完美)

指标解读

指标 含义
RMSE 0.031 预测的健康度和真实值平均差 0.031(健康度范围 0-1,所以误差约 3%)
MAE 0.020 平均绝对误差更小,说明大部分预测都很准,偶尔有大偏差
0.823 模型解释了 82.3% 的健康度变化——相当不错

3.3 残差分析:模型有没有系统性偏差?

R² 高不代表模型就完美。残差分析能发现模型的系统性弱点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
residuals = y_r_test - y_r_pred

fig, axes = plt.subplots(1, 3, figsize=(16, 5))

# 1. 残差直方图(应该接近正态分布)
axes[0].hist(residuals, bins=30, color='#3498db', alpha=0.7, edgecolor='white')
axes[0].axvline(x=0, color='red', linestyle='--')
axes[0].set_title('残差分布(应接近正态)')
axes[0].set_xlabel('残差(预测值 - 真实值)')

# 2. 残差 vs 预测值(应该随机散布,不应有趋势)
axes[1].scatter(y_r_pred, residuals, alpha=0.3, color='#3498db', s=10)
axes[1].axhline(y=0, color='red', linestyle='--')
axes[1].set_title('残差 vs 预测值(应随机散布)')
axes[1].set_xlabel('预测健康度')
axes[1].set_ylabel('残差')

# 3. Q-Q 图(残差点应沿对角线排列)
from scipy import stats
stats.probplot(residuals, dist="norm", plot=axes[2])
axes[2].set_title('Q-Q 图(点应沿对角线)')

plt.tight_layout()
plt.savefig('residual_analysis.png', dpi=150, bbox_inches='tight')
plt.show()

残差分析解读

  • 残差直方图:如果大部分残差集中在 0 附近、两边对称,说明模型没有系统性偏差
  • 残差 vs 预测值:如果点的分布像一个「均匀的云」,说明模型在各个预测值上都很稳定。如果出现「喇叭形」(高预测值处残差更大),说明模型在高值区不够准确
  • Q-Q 图:点偏离对角线越远,说明残差越不像正态分布,模型的误差分布可能不均匀

📌 实用结论:如果残差分析显示模型在健康度 < 0.7 的区域误差较大,那说明模型对「低健康度(高风险)」场景的预测不够准——这恰好是预警最需要的区域。后续可以用加权训练或专门针对低健康度样本做增强。


四、贝叶斯优化——Optuna 参数寻优

4.1 什么是贝叶斯优化?

假设你想知道「转速和扭矩的最佳组合是什么」,最笨的方法是网格搜索——把转速从 1200 到 2800 每隔 100 取一个值,扭矩从 5 到 70 每隔 5 取一个值,所有组合都试一遍。问题是:如果参数空间大(5 个参数各取 10 个值 = 10^5 = 10 万种组合),根本试不完。

贝叶斯优化的思路完全不同

  1. 先随机试几组参数,记录每组参数的「效果」(健康度)
  2. 用这些已知结果建立一个「代理模型」(高斯过程或随机森林),预测其他参数组合可能的效果
  3. 根据代理模型,选择最可能效果好(或最不确定)的参数组合去试
  4. 把新结果加入已知数据,更新代理模型
  5. 重复 3-4,用尽可能少的试验次数逼近最优解

你可以理解为:网格搜索是「地毯式搜索每个角落」,贝叶斯优化是「有经验的探险家——根据已知线索推断最可能有宝藏的方向,优先探索那里」。

4.2 Optuna 基础用法

Optuna 是日本 Preferred Networks 开发的贝叶斯优化框架,特点是 API 极其简洁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import optuna
from sklearn.model_selection import cross_val_score

# 定义目标函数:告诉 Optuna「你要优化什么」
def objective(trial):
"""
trial 对象用来「建议」参数值。
Optuna 会根据之前的试验结果,智能地选择下一组参数。
"""
# 定义参数空间(Optuna 在这里面搜索)
speed = trial.suggest_int('speed_rpm', 1200, 2800)
torque = trial.suggest_float('torque_nm', 5.0, 70.0)
wear = trial.suggest_int('wear_min', 0, 250)

# 用回归模型预测这组参数下的健康度
sample = pd.DataFrame([[torque, speed, wear]],
columns=['Torque [Nm]', 'Rotational speed [rpm]', 'Tool wear [min]'])
sample['Wear_stage_code'] = pd.cut(
sample['Tool wear [min]'], bins=[0, 60, 150, 300],
labels=[0, 1, 2]
).astype(int)

predicted_health = gbr.predict(sample[regress_features])[0]

# Optuna 默认最大化目标值,所以直接返回健康度
return predicted_health

# 创建 study 并运行优化
optuna.logging.set_verbosity(optuna.logging.WARNING) # 减少日志输出

study = optuna.create_study(direction='maximize') # 目标:最大化健康度
study.optimize(objective, n_trials=200) # 试 200 次

print(f"最优健康度: {study.best_value:.4f}")
print(f"最优参数: {study.best_params}")
1
2
最优健康度: 0.9921
最优参数: {'speed_rpm': 1532, 'torque_nm': 18.3, 'wear_min': 12}

4.3 优化结果分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 最优参数详情
best = study.best_params
print("最优工艺参数推荐:")
print(f" 转速: {best['speed_rpm']} rpm")
print(f" 扭矩: {best['torque_nm']:.1f} Nm")
print(f" 刀具磨损时间: {best['wear_min']} min")
print(f" 预测健康度: {study.best_value:.4f}")

# 优化历史(收敛曲线)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 左图:每次试验的健康度
trials_values = [t.value for t in study.trials]
axes[0].plot(trials_values, alpha=0.3, color='#3498db', label='单次试验')
axes[0].plot(np.maximum.accumulate(trials_values), color='#e74c3c',
linewidth=2, label='历史最优')
axes[0].set_title('Optuna 优化收敛曲线')
axes[0].set_xlabel('试验次数')
axes[0].set_ylabel('健康度')
axes[0].legend()

# 右图:参数重要性(Optuna 内置分析)
optuna.visualization.matplotlib.plot_param_importances(study, ax=axes[1])
axes[1].set_title('参数重要性(Optuna 自动分析)')

plt.tight_layout()
plt.savefig('optuna_optimization.png', dpi=150, bbox_inches='tight')
plt.show()

结果解读

  • 收敛曲线:红色线是「到目前为止的最优值」。可以看到在前 50 次试验后基本收敛,后面只是在微调。这说明贝叶斯优化确实很高效——不需要试完所有组合就能找到近似最优解
  • 参数重要性:Optuna 自动分析哪个参数对结果影响最大。预期 torque_nmwear_min 排名靠前

4.4 参数关系可视化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 等高线图:转速和扭矩的交互效应
# 固定 wear_min = 最优值,画 speed-torque 的健康度等高线
best_wear = study.best_params['wear_min']

speed_range = np.linspace(1200, 2800, 50)
torque_range = np.linspace(5, 70, 50)
S, T = np.meshgrid(speed_range, torque_range)

# 为每个网格点预测健康度
health_grid = np.zeros_like(S)
for i in range(50):
for j in range(50):
sample = pd.DataFrame([{
'Torque [Nm]': T[i, j],
'Rotational speed [rpm]': S[i, j],
'Tool wear [min]': best_wear,
'Wear_stage_code': 0 if best_wear < 60 else (1 if best_wear < 150 else 2)
}])
health_grid[i, j] = gbr.predict(sample[regress_features])[0]

plt.figure(figsize=(10, 7))
contour = plt.contourf(S, T, health_grid, levels=20, cmap='RdYlGn')
plt.colorbar(contour, label='预测健康度')
plt.scatter(best['speed_rpm'], best['torque_nm'],
color='white', s=200, marker='*', edgecolors='black',
linewidths=1.5, label='Optuna 最优点')
plt.title(f'转速-扭矩健康度等高线(磨损时间固定为 {best_wear} min)')
plt.xlabel('转速 (rpm)')
plt.ylabel('扭矩 (Nm)')
plt.legend()
plt.tight_layout()
plt.savefig('param_contour.png', dpi=150, bbox_inches='tight')
plt.show()

怎么看等高线图

  • 绿色区域:健康度高(安全运行区)
  • 红色区域:健康度低(高风险区)
  • 白色星号:Optuna 找到的最优点
  • 等高线密集的地方:健康度变化剧烈,参数微调影响很大
  • 等高线稀疏的地方:健康度变化平缓,参数有较大容差

五、多目标优化

5.1 为什么需要多目标?

前面只优化了一个目标(健康度),但实际工厂里至少要考虑两个目标:

目标 方向 含义
健康度 越高越好 设备稳定、故障少
产出效率 越高越好 加工速度快、产量高

这两个目标通常是矛盾的:转速越高产出越快,但设备负荷增大、健康度下降。如果只优化健康度,最优解可能是「停机不运行」——健康度 100%,但产出为零,没有任何意义。

5.2 帕累托最优:不完美但最合理的平衡

帕累托最优的通俗解释:

想象你在买房,既要面积大又要价格便宜。不存在一套房子「面积最大且价格最低」。但有些房子是「在同价位里面积最大的」或「在同面积里最便宜的」——这些房子组成一条「帕累托前沿」。前沿上的每套房子,你都不能在不牺牲一个目标的前提下改善另一个目标。

工艺参数也一样:帕累托前沿上的每组参数,你都不能在不降低健康度的前提下提高产出,也不能在不降低产出的前提下提高健康度。决策者(工厂主管)根据自己的优先级从前沿上选择。

5.3 Optuna 多目标优化代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def multi_objective(trial):
"""多目标优化:同时优化健康度和产出效率。"""
speed = trial.suggest_int('speed_rpm', 1200, 2800)
torque = trial.suggest_float('torque_nm', 5.0, 70.0)
wear = trial.suggest_int('wear_min', 0, 250)

# 预测健康度(同前)
sample = pd.DataFrame([[torque, speed, wear]],
columns=['Torque [Nm]', 'Rotational speed [rpm]', 'Tool wear [min]'])
sample['Wear_stage_code'] = pd.cut(
sample['Tool wear [min]'], bins=[0, 60, 150, 300],
labels=[0, 1, 2]
).astype(int)

health = gbr.predict(sample[regress_features])[0]

# 产出效率(近似):功率 = 扭矩 × 转速 × 常数
# 功率越高 → 加工速度越快 → 产出越高
power = torque * speed / 1000 # 归一化到合理范围

return health, power # 返回两个目标值

# 多目标优化
study_multi = optuna.create_study(
directions=['maximize', 'maximize'] # 两个目标都最大化
)
study_multi.optimize(multi_objective, n_trials=300)

print(f"帕累托前沿解的数量: {len(study_multi.best_trials)}")
print(f"\n前 5 个帕累托最优解:")
for i, trial in enumerate(study_multi.best_trials[:5]):
print(f" 解 {i+1}: 健康度={trial.values[0]:.4f}, "
f"产出={trial.values[1]:.2f}, "
f"参数={trial.params}")
1
2
3
4
5
6
7
8
帕累托前沿解的数量: 23

前 5 个帕累托最优解:
解 1: 健康度=0.9912, 产出=28.34, 参数={'speed_rpm': 1520, 'torque_nm': 18.6, 'wear_min': 5}
解 2: 健康度=0.9876, 产出=35.21, 参数={'speed_rpm': 1680, 'torque_nm': 20.9, 'wear_min': 15}
解 3: 健康度=0.9801, 产出=42.87, 参数={'speed_rpm': 1850, 'torque_nm': 23.2, 'wear_min': 30}
解 4: 健康度=0.9654, 产出=55.12, 参数={'speed_rpm': 2100, 'torque_nm': 26.2, 'wear_min': 45}
解 5: 健康度=0.9234, 产出=72.56, 参数={'speed_rpm': 2500, 'torque_nm': 29.0, 'wear_min': 80}

5.4 帕累托前沿可视化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 帕累托前沿图
fig, ax = plt.subplots(figsize=(10, 6))

# 所有试验结果
all_health = [t.values[0] for t in study_multi.trials]
all_power = [t.values[1] for t in study_multi.trials]
ax.scatter(all_health, all_power, alpha=0.2, color='#95a5a6',
s=15, label='所有试验')

# 帕累托前沿
pareto_health = [t.values[0] for t in study_multi.best_trials]
pareto_power = [t.values[1] for t in study_multi.best_trials]

# 按健康度排序后画线
sorted_pairs = sorted(zip(pareto_health, pareto_power))
ph, pp = zip(*sorted_pairs)
ax.plot(ph, pp, color='#e74c3c', linewidth=2, marker='o', markersize=6,
label='帕累托前沿')

# 标注几个关键解
ax.annotate('保守策略\n(高健康度)', xy=(ph[0], pp[0]),
fontsize=9, color='#e74c3c')
ax.annotate('激进策略\n(高产出)', xy=(ph[-1], pp[-1]),
fontsize=9, color='#e74c3c')

ax.set_xlabel('健康度')
ax.set_ylabel('产出效率')
ax.set_title('多目标优化:帕累托前沿')
ax.legend()
plt.tight_layout()
plt.savefig('pareto_front.png', dpi=150, bbox_inches='tight')
plt.show()

怎么选择帕累托前沿上的解?

策略偏好 推荐解 适用场景
保守策略 健康度 > 0.99 高精密加工、设备昂贵、不允许停机
平衡策略 健康度 0.95-0.98 大多数场景,兼顾稳定与效率
激进策略 健康度 0.92-0.95 赶工期、设备便宜、可接受一定故障率

💡 核心思想:帕累托前沿不是告诉你「哪个最好」,而是展示「所有合理的选择」,让决策者根据业务优先级来拍板。


六、约束处理与验证

6.1 设备物理约束

Optuna 找到的最优参数不一定都能用——设备有物理极限,有些参数组合在物理上不可行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 定义设备约束条件
CONSTRAINTS = {
'speed_rpm': {'min': 1200, 'max': 2800, 'unit': 'rpm'},
'torque_nm': {'min': 5.0, 'max': 70.0, 'unit': 'Nm'},
'wear_min': {'min': 0, 'max': 253, 'unit': 'min'},
}

# 额外约束:功率不能超过设备额定功率
MAX_POWER = 2500 # 设备额定功率(近似值)

def check_constraints(params):
"""检查参数组合是否满足约束。"""
issues = []

# 物理极限检查
for param, limits in CONSTRAINTS.items():
val = params[param]
if val < limits['min'] or val > limits['max']:
issues.append(f"{param} = {val} 超出范围 [{limits['min']}, {limits['max']}]")

# 功率约束
power = params['torque_nm'] * params['speed_rpm']
if power > MAX_POWER:
issues.append(f"功率 = {power:.0f} 超过额定值 {MAX_POWER}")

return issues

# 检查 Optuna 的最优解
for i, trial in enumerate(study_multi.best_trials[:5]):
issues = check_constraints(trial.params)
status = "✅ 可行" if not issues else f"❌ 违规: {'; '.join(issues)}"
print(f"解 {i+1}: {status}")

6.2 安全边界设定

除了物理极限,还应该设定安全边距——不让设备在极限参数附近运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
SAFETY_MARGIN = 0.1  # 10% 安全边距

def apply_safety_margin(params):
"""给参数加上安全边距,避免在极限附近运行。"""
safe_params = {}
for param, val in params.items():
if param in CONSTRAINTS:
limits = CONSTRAINTS[param]
param_range = limits['max'] - limits['min']
margin = param_range * SAFETY_MARGIN

safe_val = np.clip(val,
limits['min'] + margin,
limits['max'] - margin)
safe_params[param] = safe_val
else:
safe_params[param] = val
return safe_params

# 对最优解应用安全边距
best_params = study_multi.best_trials[0].params
safe_params = apply_safety_margin(best_params)

print("最优参数 vs 安全参数:")
for k in best_params:
diff = safe_params[k] - best_params[k]
marker = " (调整)" if abs(diff) > 0.01 else " (不变)"
print(f" {k}: {best_params[k]:.1f}{safe_params[k]:.1f}{marker}")

6.3 推荐参数验证策略

优化结果不能直接上产线——应该按以下步骤验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def validate_recommendations(params_list, model, features):
"""
验证推荐参数的预测可信度。

对每组推荐参数,输出预测值 + 置信区间(用多次 bootstrap 估计)。
"""
print("=" * 50)
print(" 推荐参数验证报告")
print("=" * 50)

for i, params in enumerate(params_list):
sample = pd.DataFrame([{
'Torque [Nm]': params['torque_nm'],
'Rotational speed [rpm]': params['speed_rpm'],
'Tool wear [min]': params['wear_min'],
'Wear_stage_code': 0 if params['wear_min'] < 60 else (1 if params['wear_min'] < 150 else 2)
}])

pred = model.predict(sample[features])[0]

# 简单置信区间估计(基于模型在相似样本上的残差标准差)
ci_low = max(0, pred - 2 * 0.031) # 用回归模型的 RMSE 近似
ci_high = min(1, pred + 2 * 0.031)

print(f"\n推荐方案 {i+1}:")
print(f" 参数: 转速={params['speed_rpm']}rpm, "
f"扭矩={params['torque_nm']:.1f}Nm, "
f"磨损={params['wear_min']}min")
print(f" 预测健康度: {pred:.4f}")
print(f" 95% 置信区间: [{ci_low:.4f}, {ci_high:.4f}]")

# 验证建议
if ci_low > 0.95:
print(f" 建议: ✅ 置信度高,可直接采用")
elif ci_low > 0.90:
print(f" 建议: ⚠️ 建议小批量试运行 1-2 小时后再全面推广")
else:
print(f" 建议: ❌ 置信度不足,建议重新评估或补充数据")

print("\n" + "=" * 50)

# 验证前 3 个帕累托解
validate_recommendations(
[t.params for t in study_multi.best_trials[:3]],
gbr, regress_features
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
==================================================
推荐参数验证报告
==================================================

推荐方案 1:
参数: 转速=1520rpm, 扭矩=18.6Nm, 磨损=5min
预测健康度: 0.9912
95% 置信区间: [0.9292, 1.0000]
建议: ⚠️ 建议小批量试运行 1-2 小时后再全面推广

推荐方案 2:
参数: 转速=1680rpm, 扭矩=20.9Nm, 磨损=15min
预测健康度: 0.9876
95% 置信区间: [0.9256, 1.0000]
建议: ⚠️ 建议小批量试运行 1-2 小时后再全面推广

推荐方案 3:
参数: 转速=1850rpm, 扭矩=23.2Nm, 磨损=30min
预测健康度: 0.9801
95% 置信区间: [0.9181, 1.0000]
建议: ⚠️ 建议小批量试运行 1-2 小时后再全面推广

==================================================

📌 落地经验:模型推荐的参数只是起点,不是终点。实际落地必须经过「推荐 → 小批量试产 → 收集真实效果 → 微调」的闭环。模型越准,试产周期越短,但不能跳过。


总结与回顾

要点 总结
回归建模 梯度提升回归 R²=0.82,能用工艺参数预测健康度
残差分析 检查模型有没有系统性偏差,是上线前的必做步骤
贝叶斯优化 比网格搜索更高效——200 次试验就逼近最优解
单目标最优 最优参数:转速 1532rpm + 扭矩 18.3Nm + 新刀具 → 健康度 0.99
多目标帕累托 23 个帕累托解,从保守到激进,供决策者选择
约束与验证 物理约束 + 安全边距 + 小批量试产 = 落地闭环
核心教训 模型推荐的是「起点」不是「终点」,必须经过试产验证

下篇预告

第 8 篇:运行趋势研判 —— 前三篇(故障预警、良率分析、工艺优化)都是在「当下」做判断。下篇我们要进入时间维度——基于 NASA C-MAPSS 涡扇发动机退化数据集,预测设备未来的运行趋势和剩余使用寿命(RUL),从线性回归基准线讲到 ARIMA 统计方法,再到 XGBoost 机器学习和 LSTM 深度学习。


本文为「从零到落地:机器学习分析数据实战系列」第 7 篇,完整系列持续更新中。