--- name: stats_ml description: 统计建模与机器学习(sklearn / statsmodels / PyMC 三库合一,场景导航选库)。✅ 触发:配方-性能建模、DoE 响应面、强度预测、特征重要性、假设检验、置信区间、贝叶斯小样本估计、聚类异常实验检测。⛔ 不触发:用户只问描述性统计(均值方差),pandas 一行搞定;深度学习场景(走单独的 torch / lightning skill,本仓库未集成)。 --- # stats_ml 服务建材院典型场景:**掺合料配比 → 性能(强度 / 流动度 / 凝结时间)** 的建模与推断。三库分工: | 你要做 | 用 | 一句话理由 | |---|---|---| | 要 p-value、置信区间、假设检验 | **statsmodels** | OLS / ANOVA / 假设检验是 statsmodels 主场,sklearn 不给 p-value | | 要预测精度高,可解释性次要 | **sklearn** | RandomForest / GBDT / XGBoost 是性能预测的现代基线 | | 样本 < 30 且要不确定度估计 | **PyMC** | 贝叶斯给出参数后验分布,小样本下比频率派的置信区间更稳 | | 要可解释的线性关系 | statsmodels OLS / sklearn LinearRegression | 都可以,统计推断走 statsmodels,纯预测走 sklearn | | 要 DoE 响应面拟合 | statsmodels(二次回归 + ANOVA) | 主流做法,系数显著性能直接读 | | 要聚类找异常配方 | sklearn (KMeans / DBSCAN) | 标准工具 | | 要降维可视化(配方空间) | sklearn (PCA / t-SNE) | 标准工具 | ## 何时用 / 何时不用 ✅ 用: - 用户给一张配方-性能表(N 行配方 + 性能列),要建模 / 预测 / 推断 - 要回答"哪个因素影响最大"(特征重要性 / 系数显著性) - 要回答"这个新配方预测强度多少 + 置信区间多大" - 要找异常实验点 / 配方聚类 ⛔ 不用: - 用户只要看均值方差直方图 → pandas + matplotlib 直接搞,杀鸡不用牛刀 - 用户给的"数据"只有 3-5 个点 → 任何 ML / 统计都不可靠,告诉用户先补数据 - 深度学习需求(CNN / Transformer)→ 不在本 skill 范围 ## 准备 ```python # 按需 import,不要全 import import numpy as np import pandas as pd # sklearn from sklearn.model_selection import train_test_split, KFold, StratifiedKFold, GridSearchCV from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.metrics import r2_score, mean_squared_error from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor # statsmodels import statsmodels.api as sm import statsmodels.formula.api as smf # PyMC(重,按需 import) # import pymc as pm # import arviz as az ``` ## 典型工作流 ### A. 配方-性能回归(sklearn,要预测精度) ```python # 数据:每行一个配方,X 是组分比例,y 是 28d 抗压强度 X = df[["cement", "fly_ash", "slag", "water", "sand", "stone"]].values y = df["strength_28d"].values # Pipeline 把预处理和模型绑死 —— 反模式见末尾 pipe = Pipeline([ ("scaler", StandardScaler()), # 线性 / 距离类模型必须;树模型可省 ("model", RandomForestRegressor(n_estimators=300, random_state=42)), ]) # 数据小(< 200)用 5-fold CV,不要单次 train/test from sklearn.model_selection import cross_val_score scores = cross_val_score(pipe, X, y, cv=5, scoring="r2") print(f"R2 mean={scores.mean():.3f} std={scores.std():.3f}") # 训完看特征重要性 pipe.fit(X, y) imp = pipe.named_steps["model"].feature_importances_ for name, val in sorted(zip(df.columns, imp), key=lambda t: -t[1]): print(f"{name}: {val:.3f}") ``` ### B. 显著性分析(statsmodels,要 p-value) ```python import statsmodels.formula.api as smf # formula API 让模型 spec 看起来跟 R 一样 model = smf.ols( formula="strength_28d ~ cement + fly_ash + slag + water + water:cement", # 含交互 data=df, ).fit() print(model.summary()) # 系数、std err、t、p>|t|、95% CI 全在这 # ANOVA 比较两个嵌套模型 m1 = smf.ols("strength_28d ~ cement + fly_ash", data=df).fit() m2 = smf.ols("strength_28d ~ cement + fly_ash + slag", data=df).fit() from statsmodels.stats.anova import anova_lm print(anova_lm(m1, m2)) # slag 加进去显著吗 ``` ### C. DoE 响应面(statsmodels 二次回归) ```python # 中心复合设计后,做二阶模型 model = smf.ols( formula="strength ~ x1 + x2 + x3 + I(x1**2) + I(x2**2) + I(x3**2) + x1:x2 + x1:x3 + x2:x3", data=df, ).fit() print(model.summary()) # 看哪些二阶项 / 交互项显著,定优化方向 ``` ### D. 小样本贝叶斯(PyMC,N<30 要不确定度) ```python import pymc as pm import arviz as az with pm.Model() as model: # 系数先验:弱信息正态 intercept = pm.Normal("intercept", mu=0, sigma=10) beta = pm.Normal("beta", mu=0, sigma=5, shape=X.shape[1]) sigma = pm.HalfNormal("sigma", sigma=10) mu = intercept + pm.math.dot(X, beta) y_obs = pm.Normal("y_obs", mu=mu, sigma=sigma, observed=y) trace = pm.sample(2000, tune=1000, chains=4, target_accept=0.95) az.summary(trace) # 后验均值 / std / 94% HDI az.plot_posterior(trace) # 后验分布图 ``` ### E. 配方聚类找异常实验(sklearn KMeans / DBSCAN) ```python from sklearn.cluster import DBSCAN from sklearn.preprocessing import StandardScaler Xs = StandardScaler().fit_transform(X) labels = DBSCAN(eps=0.5, min_samples=5).fit_predict(Xs) # labels == -1 的点 = 离群,可能是录入错或工艺异常,人工核实 ``` ## 反模式 ### sklearn 1. **预处理在 pipeline 外** —— 交叉验证会数据泄漏(test fold 的统计量被 train 看到) 2. **scaler 在 test 上 fit** —— 一律 `fit(train)` / `transform(test)`,Pipeline 自动保证 3. **分类任务忘 stratified split** —— 类别不平衡时 `train_test_split(..., stratify=y)` 4. **不必要地 scale 树模型** —— RF / GBDT / XGBoost 不受单调变换影响 5. **忽略 convergence warning** —— LogisticRegression / SVM 不收敛要加 `max_iter` 或 scale 特征 6. **小数据集还 80/20 切** —— N<200 用 5-fold CV,N<50 用 LOOCV ### statsmodels 7. **直接读系数不看 condition number** —— `summary()` 末尾的 condition number > 30 = 严重共线性,系数解读无效 8. **多重比较忘修正** —— 同时检验多个系数显著性时,用 Bonferroni / FDR(`statsmodels.stats.multitest`) 9. **OLS 残差不检验** —— `model.resid` 不正态 / 异方差时,OLS 推断不可靠,改 robust SE(`fit(cov_type="HC3")`) ### PyMC 10. **不看 R-hat / ESS** —— `az.summary(trace)` 的 `r_hat` > 1.01 或 `ess_bulk` < 400 = 没收敛,加 tune / 调 target_accept,不能用 11. **强先验当数据用** —— 先验定太窄会主导后验,先用弱信息先验试 12. **PyMC 装包巨大** —— 仅小样本需要时才用,大样本(N>500)频率派结果一样可信且快 100 倍 ### 通用 13. **没看数据分布就建模** —— 先 `df.describe()` + `df.hist()` 扫一遍,有 NaN / 量纲极差 / 长尾分布要先处理 14. **R2 > 0.95 就开香槟** —— 大概率是数据泄漏 / 过拟合 / 训练集等于测试集,先查 15. **预测新配方不报置信区间** —— 工程决策不能只给点估计,sklearn 用 `RandomForestRegressor` 的 tree-level prediction std,statsmodels / PyMC 直接给 CI / HDI 16. **数据点不到 10 还硬上 ML** —— 告诉用户先做 DoE 扩样本,再建模 ## 依赖 `requirements.txt` 加: ``` scikit-learn>=1.4.0 statsmodels>=0.14.0 pymc>=5.10.0 # 重,装包带 pytensor,可选 arviz>=0.17.0 # PyMC 后验诊断 ``` 装:`.venv/Scripts/pip install scikit-learn statsmodels` PyMC 单独装:`.venv/Scripts/pip install pymc arviz`(确认要做贝叶斯再装,首次装包 5 分钟起)