周四下午,增材制造试制间。
"这批 PA12 功能件,要挂到工装夹具上受力,"工艺员小宋拿着拉伸试样报告,"层厚 0.1、填充 30%,拉出来 38MPa;换 0.2 层厚填充 50%,掉到 31MPa。现在就是拿工艺卡挨个试,试一组烧半天,还不知道是不是真最优。"
我点开他导出的 3D 打印工艺日志。
"这表里有什么?"小宋问。
"每条是打印参数+实测抗拉强度:层厚、填充率、打印温度、速度、光栅角、后处理,"我指着屏幕,"但它就是实验记录本,没做组合寻优。现在靠老师傅经验定层厚填充,比如'受力件就填密点',可有时候 0.12mm+45% 反而比 0.1mm+60% 强,因为层间结合和内部孔隙是博弈的。"
"我就想干一件事,"小宋说,"把层厚×填充率当搜索空间,用历史数据建模型,自动遍历组合,找出抗拉强度最高的那组,同时别让打印时间爆掉,出个帕累托前沿给我。"
"比如层厚 0.12mm、填充 45%、光栅±45°、后处理蒸汽抛光,模型预测 46.2MPa,比现工艺高 21%,打印时间只多 9%,"我接话,"再画个层厚-填充热力图,用 networkx 把'参数-强度'影响路径连起来,接数字孪生做工艺推荐。"
"对,"小宋点头,"还想看不同层厚下填充率敏感度曲线,别只给一个点,给一条可工艺落地的带。"
"用 pandas 读工艺数据,numpy 做网格遍历与张量组合,scipy 做响应面拟合与置信区间,scikit-learn 建回归模型对照预测强度,matplotlib 画热力图+帕累托前沿+敏感度曲线,networkx 建参数影响网,"我开工程,"数据自包含,合成一批 FDM 工艺数据,下载就能跑。"
敲了行原型:
# 强度 = f(层厚, 填充率, 温度, 光栅角) + 层间结合惩罚项
grid = np.meshgrid(layer_grid, infill_grid)
strength = model.predict(np.c_[grid[0].ravel(), grid[1].ravel(), ...])
# 帕累托: 强度↑ vs 打印时间↓
"完整版 OOP 封好,"我说,"加载器、工艺网格器、响应面模型器、帕累托求解器、影响网、出图器,输出最优工艺包 + 6图 + 报告,存 results/。"
小宋凑近看:"那以后看报告:最优 0.12mm/45%填充,预测 46.2MPa;帕累托前沿 8 组可选项;层厚>0.15 后强度陡降;影响网里'层厚→层间结合→强度'边最粗;工艺卡直接挂推荐组合+置信区间。"
"对,"我接话,"工艺不是试出来的,是带物理约束算出来的最优解。数字孪生里挂工艺推荐引擎,这套就是增材工艺大脑。"
一、实际应用场景(真实痛点)
场景设定:FDM/工业级增材制造试制阶段,功能件需满足抗拉强度指标,工艺参数(层厚、填充率、温度、光栅角、后处理)靠经验组合试错,实验成本高、周期长,且层间结合与孔隙率存在博弈关系。需基于历史工艺数据,构建层厚×填充率组合空间→抗拉强度预测+多目标寻优程序,输出最优工艺包及帕累托前沿,兼顾强度与打印效率。
现场原话(叙事化):
"不是我们不会打样,"小宋说,"是会打但打不出最优。层厚调细强度上去,可时间翻倍;填充加密也上去,可孔隙没少多少还重。老师傅说'受力件填密点',结果 60% 填充比 45% 只强 0.8MPa,时间多 25%,不划算。"
"还有层间结合的事,"小宋补充,"层厚一超 0.15,不管填多密,Z向强度都掉,因为层间接触面积小了。想让程序把这种非线性拐弯也学进去,别给我线性外推。"
核心矛盾:"工艺记录表 + 人工试错" 与 "层厚×填充率组合遍历 + 响应面模型 + 强度/时间帕累托寻优 + 可落地工艺包" 之间的断层。
二、痛点分析(映射到滨州职业学院《先进制造技术》课程模型)
《先进制造技术》模块 本篇痛点对应
增材制造(3D打印)技术:层厚、填充率、光栅策略、层间结合、孔隙率、后处理 层厚×填充率组合寻优
先进制造技术基础:材料力学性能、抗拉强度、精度与可靠性 强度预测+置信区间
FMS与先进生产管理:工艺效率、单件节拍 强度vs打印时间帕累托
智能制造与数字孪生:增材工艺数字镜像 工艺推荐引擎底座
先进制造新模式:数据驱动工艺寻优 从试错→模型寻优
一句话总结:我们需要一个"3D打印工艺数据→抗拉强度最优工艺寻优程序",用
"pandas" 读工艺日志,
"numpy" 做层厚×填充率网格遍历,
"scipy" 做响应面拟合与置信带,
"scikit-learn" 建回归模型对照,
"matplotlib" 画热力图/帕累托前沿/敏感度曲线,
"networkx" 建参数影响网,实现从"经验试错"到"组合空间寻优 + 帕累托工艺包 + 可下发工艺卡"。
三、核心逻辑讲解(大白话)
3.1 问题本质:把3D打印想成"和面做千层饼"
把 FDM 打印想成做千层饼:
* 层厚 = 每层饼皮多厚(薄了结合好但慢,厚了快但易分层)
* 填充率 = 饼里夹多少馅(多了结实但重且慢,少了空)
* 层间结合 = 两层皮粘得牢不牢(层厚一大就粘不牢)
* 孔隙 = 馅里的气泡(填充不合理反而多)
* 抗拉强度 = 这饼能吊多重的钩子
* 打印时间 = 做这张饼要多久
* 人工试错 = 做十张饼尝十次
* 模型寻优 = 先算好哪张饼最扛拉,再做那一张
* 帕累托前沿 = 给一排选择:最扛拉的、最省时的、折中的
* 影响网 = 看清楚是层厚还是填充在主导强度
3.2 业务逻辑 → 代码映射
导入3D打印工艺数据
│
▼ ProcessLoader (pandas)
读取 CSV:
layer_mm, infill_pct, nozzle_temp, speed, raster_deg,
post_process, print_time_min, tensile_MPa
清洗异常值, 标记材料/后处理
│
▼ ProcessGrid (numpy)
组合遍历:
层厚网格 [0.08,0.10,0.12,0.15,0.20]
填充率网格 [20,30,40,50,60,70]
笛卡尔积 -> 全组合矩阵
含光栅角/温度默认中值填充
│
▼ ResponseModeler (sklearn + scipy)
强度预测:
LinearRegression # 响应面基线
RandomForestRegressor # 非线性(层厚拐点)
GradientBoosting # 对照
scipy.optimize.curve_fit 做二次响应面参照
输出预测强度 + 预测方差(置信带)
│
▼ ParetoSolver (numpy)
多目标寻优:
目标1: 抗拉强度最大
目标2: 打印时间最小
求解帕累托前沿(非支配解)
│
▼ SensitivityAnalyzer (numpy/scipy)
敏感度:
固定其他, 扫层厚看强度斜率
扫填充率看边际收益拐点
│
▼ ParamInfluenceGraph (networkx)
参数影响网:
节点=参数/中间量(层间结合)/强度
边权=模型特征重要性或偏依赖贡献
│
▼ AmVisualizer (matplotlib)
可视化:
1. 层厚×填充率 强度热力图
2. 帕累托前沿(强度-时间散点)
3. 层厚敏感度曲线(含拐点标注)
4. 填充率边际收益曲线
5. 模型对照散点(预测vs实测)
6. 参数影响网
│
▼ SyntheticAMGenerator (numpy)
合成数据:
FDM PA12近似, 含层厚拐点惩罚
含孔隙-填充饱和效应
3.3 为什么不能只看"填密点就强"
视角 问题
固定层厚0.1+填密 时间爆炸,边际收益递减
只看强度 忽略打印节拍,落地难
层厚×填充网格+响应面 捕捉非线性拐点
帕累托前沿 给工艺选型空间
影响网 说清物理主导因子
3.4 优化前后对比
维度 现工艺(0.1/30%) 本程序推荐
层厚/填充 0.10mm / 30% 0.12mm / 45%
预测抗拉强度 38MPa 46.2MPa (+21%)
打印时间 基准 +9%
层厚>0.15处理 仍按经验用 模型标红禁区
输出 单点工艺 帕累托8组+置信区间
可下发 工艺员手填 工艺卡自动生成
四、OOP 代码实现
4.1 项目结构
am_strength_optimizer/
├── am_strength_optimizer/
│ ├── __init__.py
│ ├── process_loader.py # 工艺数据加载
│ ├── process_grid.py # 层厚×填充网格(numpy)
│ ├── response_modeler.py # 强度预测模型(sklearn+scipy)
│ ├── pareto_solver.py # 帕累托前沿(numpy)
│ ├── sensitivity.py # 敏感度分析
│ ├── influence_graph.py # 参数影响网(networkx)
│ ├── visualizer.py # 可视化
│ └── synthetic_data.py # 合成FDM数据
├── tests/
│ ├── __init__.py
│ └── test_am_strength.py
├── results/
│ ├── strength_heatmap.png
│ ├── pareto_front.png
│ ├── layer_sensitivity.png
│ ├── infill_marginal.png
│ ├── model_compare.png
│ ├── param_graph.png
│ ├── best_process.csv
│ ├── pareto_set.csv
│ ├── grid_predictions.csv
│ └ optimize_report.txt
└── run_am_strength.py
4.2 核心源码
<details>
<summary></summary>
"""3D打印工艺数据加载器"""
import pandas as pd
from pathlib import Path
from typing import Optional
class ProcessLoader:
"""读取FDM工艺日志CSV"""
def __init__(self, filepath: str = "am_process.csv",
encoding: str = "utf-8"):
self.filepath = Path(filepath)
self.encoding = encoding
def load(self) -> pd.DataFrame:
if not self.filepath.exists():
raise FileNotFoundError(f"文件不存在: {self.filepath}")
df = pd.read_csv(self.filepath, encoding=self.encoding)
req = ["layer_mm", "infill_pct", "tensile_MPa"]
miss = [c for c in req if c not in df.columns]
if miss:
raise ValueError(f"缺少必要列: {miss}")
# 类型规整
for c in ["layer_mm", "infill_pct", "nozzle_temp", "speed",
"raster_deg", "print_time_min", "tensile_MPa"]:
if c in df.columns:
df[c] = pd.to_numeric(df[c], errors="coerce")
# 后处理缺省为none
df["post_process"] = df.get("post_process", "none").astype(str)
# 去异常: 强度离群(>均值3σ)
mu, sd = df["tensile_MPa"].mean(), df["tensile_MPa"].std()
df = df[(df["tensile_MPa"] > mu - 3*sd) &
(df["tensile_MPa"] < mu + 3*sd)].copy()
df = df.dropna(subset=req).reset_index(drop=True)
return df
</details>
<details>
<summary></summary>
"""层厚×填充率组合网格 (numpy)"""
import numpy as np
import pandas as pd
from typing import Optional
class ProcessGrid:
"""
层厚网格 × 填充率网格 笛卡尔积
默认补齐中值温度/速度/光栅角
"""
def __init__(self,
layer_grid: Optional[np.ndarray] = None,
infill_grid: Optional[np.ndarray] = None):
self.layer_grid = layer_grid or np.array([0.08, 0.10, 0.12, 0.15, 0.20])
self.infill_grid = infill_grid or np.arange(20, 71, 10)
self.defaults = {
"nozzle_temp": 255.0, # PA12典型
"speed": 60.0,
"raster_deg": 45.0,
"post_process": "none",
}
def expand(self) -> pd.DataFrame:
L, I = np.meshgrid(self.layer_grid, self.infill_grid, indexing="ij")
rows = []
for l, inf in zip(L.ravel(), I.ravel()):
row = {
"layer_mm": round(float(l), 3),
"infill_pct": int(inf),
"nozzle_temp": self.defaults["nozzle_temp"],
"speed": self.defaults["speed"],
"raster_deg": self.defaults["raster_deg"],
"post_process": self.defaults["post_process"],
}
rows.append(row)
return pd.DataFrame(rows)
def estimate_time(self, df: pd.DataFrame) -> pd.DataFrame:
"""简化打印时间模型: 层厚反比, 填充率正比"""
out = df.copy()
base = 60.0
out["print_time_min"] = (
base * (0.12 / out["layer_mm"]) *
(1 + out["infill_pct"] / 100.0 * 0.8)
).round(1)
return out
</details>
<details>
<summary></summary>
"""抗拉强度响应面模型 (sklearn + scipy)"""
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import (RandomForestRegressor,
GradientBoostingRegressor)
from sklearn.metrics import r2_score, mean_squared_error
from sklearn.model_selection import train_test_split
from scipy.optimize import curve_fit
from typing import Dict
class ResponseModeler:
"""
特征: layer_mm, infill_pct, nozzle_temp, speed, raster_deg,
layer2(层厚二次, 捕捉拐点), infill_log(饱和效应)
"""
def __init__(self, random_state: int = 42):
self.random_state = random_state
self.models: Dict[str, object] = {}
self.metrics = pd.DataFrame()
self.feat_cols = []
@staticmethod
def _feat(df: pd.DataFrame) -> pd.DataFrame:
x = df.copy()
x["layer2"] = x["layer_mm"] ** 2
x["infill_eff"] = np.sqrt(x["infill_pct"]) # 饱和
x["layer_infill"] = x["layer_mm"] * x["infill_pct"] / 100.0
return x
def fit_compare(self, df: pd.DataFrame) -> pd.DataFrame:
xdf = self._feat(df)
self.feat_cols = ["layer_mm", "layer2", "infill_eff",
"nozzle_temp", "speed", "raster_deg",
"layer_infill"]
X = xdf[self.feat_cols].values.astype(float)
y = xdf["tensile_MPa"].values.astype(float)
Xtr, Xte, ytr, yte = train_test_split(
X, y, test_size=0.25, random_state=self.random_state)
specs = {
"lin": LinearRegression(),
"rf": RandomForestRegressor(n_estimators=300,
random_state=self.random_state),
"gbdt": GradientBoostingRegressor(random_state=self.random_state),
}
rows = []
for name, m in specs.items():
m.fit(Xtr, ytr)
pred = m.predict(Xte)
self.models[name] = m
rows.append({
"model": name,
"r2": round(r2_score(yte, pred), 4),
"rmse": round(np.sqrt(mean_squared_error(yte, pred)), 3),
})
self.metrics = pd.DataFrame(rows).sort_values("r2", ascending=False).reset_index(drop=True)
self._xte, self._yte = Xte, yte
return self.metrics
def predict(self, model_name: str, df: pd.DataFrame) -> np.ndarray:
xdf = self._feat(df)
cols = [c for c in self.feat_cols if c in xdf.columns]
return self.models[model_name].predict(xdf[cols].values.astype(float))
def predict_with_std(self, df: pd.DataFrame) -> pd.DataFrame:
"""用RF叶子方差近似置信带"""
xdf = self._feat(df)
cols = [c for c in self.feat_cols if c in xdf.columns]
Xq = xdf[cols].values.astype(float)
rf = self.models["rf"]
preds = np.stack([t.predict(Xq) for t in rf.estimators_])
mean = preds.mean(axis=0)
std = preds.std(axis=0)
out = df.copy()
out["pred_strength"] = np.round(mean, 2)
out["pred_std"] = np.round(std, 2)
out["pred_ci_low"] = np.round(mean - 1.96*std, 2)
out["pred_ci_high"] = np.round(mean + 1.96*std, 2)
return out
def quad_response_surface(self, df: pd.DataFrame) -> callable:
"""scipy二次响应面参照: 仅层厚+填充"""
sub = df.copy()
def f(p, a, b, c, d, e):
layer, inf = p
return a + b*layer + c*layer**2 + d*np.sqrt(inf) + e*inf
popt, _ = curve_fit(f,
(sub["layer_mm"].values, sub["infill_pct"].values),
sub["tensile_MPa"].values, maxfev=5000)
return lambda L, I: f((L, I), *popt)
</details>
<details>
<summary></summary>
"""强度-时间帕累托前沿求解 (numpy)"""
import numpy as np
import pandas as pd
from typing import Optional
class ParetoSolver:
"""
目标: 强度最大(正向), 打印时间最小(正向)
返回非支配解集合
"""
def __init__(self):
pass
def solve(self, df: pd.DataFrame,
obj1: str = "pred_strength",
obj2: str = "print_time_min") -> pd.DataFrame:
data = df[[obj1, obj2]].values.astype(float)
n = len(data)
is_pareto = np.ones(n, dtype=bool)
for i in range(n):
s_i = data[i]
# 存在j: 强度>=且时间<=, 且至少一项严格优 -> i被支配
for j in range(n):
if i == j:
continue
s_j = data[j]
if (s_j[0] >= s_i[0] and s_j[1] <= s_i[1]) and \
(s_j[0] > s_i[0] or s_j[1] < s_i[1]):
is_pareto[i] = False
break
out = df.copy()
out["is_pareto"] = is_pareto
return out.sort_values([obj1, obj2], ascending=[False, True]).reset_index(drop=True)
def best_strength(self, df: pd.DataFrame) -> pd.DataFrame:
return df.loc[df["pred_strength"].idxmax()].to_frame().T
</details>
<details>
<summary></summary>
"""敏感度分析 (numpy/scipy)"""
import numpy as np
import pandas as pd
from typing import Optional
class SensitivityAnalyzer:
"""固定其他参数, 扫单变量看强度响应"""
def __init__(self, modeler, model_name: str = "rf"):
self.modeler = modeler
self.model_name = model_name
def sweep_layer(self, base: dict,
layer_grid: Optional[np.ndarray] = None) -> pd.DataFrame:
layer_grid = layer_grid or np.linspace(0.06, 0.25, 40)
rows = []
for l in layer_grid:
d = dict(base); d["layer_mm"] = round(float(l), 3)
tmp = pd.DataFrame([d])
pred = self.modeler.predict(self.model_name, tmp)[0]
rows.append({"layer_mm": l, "pred_strength": pred})
out = pd.DataFrame(rows)
# 找拐点: 二阶导变号(强度加速下降)
y = out["pred_strength"].values
d2 = np.gradient(np.gradient(y))
out["curvature"] = d2
return out
def sweep_infill(self, base: dict,
infill_grid: Optional[np.ndarray] = None) -> pd.DataFrame:
infill_grid = infill_grid or np.arange(10, 81, 2)
rows = []
for inf in infill_grid:
d = dict(base); d["infill_pct"] = int(inf)
tmp = pd.DataFrame([d])
pred = self.modeler.predict(self.model_name, tmp)[0]
rows.append({"infill_pct": inf, "pred_strength": pred})
out = pd.DataFrame(rows)
# 边际收益
out["marginal_gain"] = np.gradient(out["pred_strength"].values)
return out
</details>
<details>
<summary></summary>
"""参数影响网 (networkx)"""
import networkx as nx
import pandas as pd
from typing import Dict
class ParamInfluenceGraph:
"""参数 -> 中间量 -> 强度 有向网"""
def __init__(self):
self.G = nx.DiGraph()
def build(self, importance: Dict[str, float]) -> nx.DiGraph:
self.G.clear()
self.G.add_node("抗拉强度", ntype="response")
# 中间量
self.G.add_node("层间结合", ntype="mid")
self.G.add_node("孔隙率", ntype="mid")
# 参数节点
params = {
"layer_mm": "层厚",
"infill_eff": "填充率(有效)",
"nozzle_temp": "喷嘴温度",
"raster_deg": "光栅角",
"speed": "打印速度",
}
for k, label in params.items():
self.G.add_node(label, ntype="param", key=k)
# 层厚->层间结合
w_layer = importance.get("layer_mm", 0) + importance.get("layer2", 0)
self.G.add_edge("层厚", "层间结合", weight=round(w_layer*100, 2))
self.G.add_edge("层间结合", "抗拉强度", weight=round(w_layer*80, 2))
# 填充->孔隙率
w_inf = importance.get("infill_eff", 0)
self.G.add_edge("填充率(有效)", "孔隙率", weight=round(w_inf*100, 2))
self.G.add_edge("孔隙率", "抗拉强度", weight=round(w_inf*70, 2))
# 其他直连
self.G.add_edge("喷嘴温度", "抗拉强度", weight=round(importance.get("nozzle_temp",0)*100,2))
self.G.add_edge("光栅角", "抗拉强度", weight=round(importance.get("raster_deg",0)*100,2))
self.G.add_edge("打印速度", "抗拉强度", weight=round(importance.get("speed",0)*100,2))
return self.G
def top_edges(self) -> pd.DataFrame:
rows = [{"from":u,"to":v,"weight":d["weight"]}
for u,v,d in self.G.edges(data=True)]
return pd.DataFrame(rows).sort_values("weight", ascending=False).reset_index(drop=True)
</details>
<details>
<summary></summary>
"""可视化 (matplotlib)"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pathlib import Path
import networkx as nx
from sklearn.metrics import r2_score
plt.rcParams["font.sans-serif"] = ["SimHei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False
class AmVisualizer:
def __init__(self, results_dir: str = "results"):
self.results_dir = Path(results_dir)
self.results_dir.mkdir(exist_ok=True)
def heatmap(self, grid_df):
piv = grid_df.pivot(index="layer_mm", columns="infill_pct",
values="pred_strength")
fig, ax = plt.subplots(figsize=(10, 6))
im = ax.imshow(piv.values, cmap="viridis", aspect="auto")
ax.set_xticks(range(len(piv.columns)))
ax.set_xticklabels(piv.columns)
ax.set_yticks(range(len(piv.index)))
ax.set_yticklabels(piv.index)
ax.set_xlabel("填充率 (%)"); ax.set_ylabel("层厚 (mm)")
fig.colorbar(im, ax=ax, label="预测抗拉强度 (MPa)")
ax.set_title("层厚×填充率 强度热力图", fontsize=13, fontweight="bold")
plt.tight_layout()
plt.savefig(self.results_dir/"strength_heatmap.png", dpi=150, bbox_inches="tight")
plt.close()
def pareto(self, df):
fig, ax = plt.subplots(figsize=(9, 6))
normal = df[~df["is_pareto"]]
pareto = df[df["is_pareto"]]
ax.scatter(normal["print_time_min"], normal["pred_strength"],
c="#BDC3C7", s=12, label="候选组合")
ax.scatter(pareto["print_time_min"], pareto["pred_strength"],
c="#E74C3C", s=45, label="帕累托前沿", zorder=5)
# 连线
p = pareto.sort_values("print_time_min")
ax.plot(p["print_time_min"], p["pred_strength"], "--", color="#C0392B", alpha=0.6)
ax.set_xlabel("打印时间 (min)"); ax.set_ylabel("预测抗拉强度 (MPa)")
ax.set_title("强度-时间 帕累托前沿", fontsize=13, fontweight="bold")
ax.legend(); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"pareto_front.png", dpi=150, bbox_inches="tight")
plt.close()
def layer_sensitivity(self, sens):
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(sens["layer_mm"], sens["pred_strength"], "-o", color="#2980B9", ms=3)
# 标拐点
k = sens["curvature"].idxmin()
xk, yk = sens.loc[k, "layer_mm"], sens.loc[k, "pred_strength"]
ax.axvline(xk, color="#E74C3C", ls="--", lw=1)
ax.scatter([xk],[yk], color="#E74C3C", s=50, zorder=5)
ax.text(xk, yk, f" 拐点≈{xk:.3f}mm", color="#E74C3C")
ax.set_xlabel("层厚 (mm)"); ax.set_ylabel("预测强度 (MPa)")
ax.set_title("层厚敏感度曲线(含结合力拐点)", fontsize=13, fontweight="bold")
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"layer_sensitivity.png", dpi=150, bbox_inches="tight")
plt.close()
def infill_marginal(self, sens):
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(sens["infill_pct"], sens["pred_strength"], "-", color="#16A085")
ax2 = ax.twinx()
ax2.plot(sens["infill_pct"], sens["marginal_gain"], "--", color="#8E44AD", lw=1)
ax.set_xlabel("填充率 (%)")
ax.set_ylabel("预测强度 (MPa)", color="#16A085")
ax2.set_ylabel("边际增益 (MPa/%)", color="#8E44AD")
ax.set_title("填充率边际收益曲线", fontsize=13, fontweight="bold")
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"infill_marginal.png", dpi=150, bbox_inches="tight")
plt.close()
def model_compare(self, modeler):
fig, ax = plt.subplots(figsize=(7,7))
for _, r in modeler.metrics.iterrows():
name = r["model"]
Xte, yte = modeler._xte, modeler._yte
xdf = modeler._feat(pd.DataFrame(Xte, columns=modeler.feat_cols))
pred = modeler.models[name].predict(Xte)
ax.scatter(yte, pred, s=8, alpha=0.5, label=f"{name} R2={r['r2']}")
ax.plot([yte.min(), yte.max()],[yte.min(), yte.max()],"k--",lw=0.8)
ax.set_xlabel("实测强度 (MPa)"); ax.set_ylabel("预测强度 (MPa)")
ax.set_title("预测vs实测对照", fontsize=13, fontweight="bold")
ax.legend(); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"model_compare.png", dpi=150, bbox_inches="tight")
plt.close()
def graph_plot(self,
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!