简介:这是一份面向插层熔喷非织造材料性能研究的代码包,整合了基于回归模型的完整分析流程,涵盖数据预处理、回归建模、结果可视化与检验报告,适合材料、纺织、计算机等专业本科生或研究生用于课程设计、期末大作业及毕业设计。包内共36个文件,以matlab脚本(.m)、Python脚本(.py)、Excel数据表格(.xlsx)、CSV数据、可视化图表(png)为主,附PDF格式的详细报告与说明文档,整体约2.02MB,轻量易下载。目前已有80人学习,代码采用参数化编程,注释明细,且兼容matlab2014/2019a/2021a,方便修改参数直接运行。资源提供了插层前后原始数据及标准化、正态检验等处理脚本,并针对过滤阻力差、过滤效率差、孔隙率差、厚度差、压缩回弹性率差等指标进行了分布拟合与盒须图展示,便于直观理解熔喷材料性能变化规律。同时包含完整的回归建模代码和中文实验报告,读者可结合自身算法做扩展,快速实现从数据清洗到论文出图的全流程复现,实用价值较高。
1. 拿到这个 zip,先想清楚回归模型在插层熔喷材料性能预测里解决什么问题
插层熔喷非织造材料的性能预测,本质上是一个“材料配方—工艺参数—宏观性能”的映射问题。实验人员更换插层剂种类、调整含量和热风工艺参数,做出一批样品后测量过滤效率、透气率和拉伸强力;样本量不大,但变量之间存在明显的交互效应。用 python 编写回归模型,就是为了在有限的实验数据上找到可泛化的映射关系,让后续少做几组实验也能预判新配方的性能走向。拿到这类项目的代码压缩包,第一步不是急着搭环境,而是确认压缩包内是否包含数据读取、特征工程、回归模型对比、调参与评估、解释与导出这几段能连跑的脚本。不同阶段换一个人接手,缺了任何一段都要返工。这篇文章按这条流水线的四个环节拆解,每个环节都给出可以直接运行的 python 代码,以及参数怎么设、失败时先看哪里。
2. 从实验记录到 DataFrame:插层熔喷数据的清洗与特征工程
解压 zip 之后先别急着 import sklearn。材料实验数据的原始形态通常是 Excel 多 sheet,或者命名随意的 CSV,列名可能是“样品编号”“插层剂含量(%)”这种带中文和括号的形式。很多刚接触 python 数据分析的同事第一次就是栽在 Excel 读取上,因为实验室表格里几乎必然存在合并单元格、空行和重复批次。第一步是把它们读进一个列名统一的 DataFrame,我一般会单独写一个load_data.py,保证原始文件只读,清洗逻辑沉淀成脚本,后续数据更新后可以一键重跑。
2.1 多 sheet 实验表格的读取与合并
材料团队习惯把不同批次的实验结果放在同一个 Excel 文件的不同 sheet 里,少的三五张,多的十几张,列名还不完全一致。pandas 的read_excel在sheet_name=None时会把所有 sheet 读成字典,循环处理并合并,这是这类数据最常见的入口写法:
import glob import pandas as pd def load_experiment_data(path_pattern: str) -> pd.DataFrame: frames = [] for file_path in glob.glob(path_pattern): # sheet_name=None 返回 {sheet名: DataFrame} 的字典 sheets = pd.read_excel(file_path, sheet_name=None) for sheet_name, df in sheets.items(): df = df.copy() # 统一列名:去掉首尾空格,替换空格和中文括号,转小写 df.columns = [ col.strip().lower().replace(' ', '_') .replace('(', '(').replace(')', ')') for col in df.columns ] df['source_sheet'] = f"{file_path}::{sheet_name}" frames.append(df) df_all = pd.concat(frames, ignore_index=True) df_all = df_all.drop_duplicates() print(f"merged {df_all.shape[0]} rows from {len(frames)} sheets") return df_all df = load_experiment_data("data/*.xlsx")glob 的*.xlsx能匹配同一个 data 目录下多个文件,年底追加一批新实验数据时不需要修改代码。sheet_name=None是 read_excel 的固定用法,返回值为字典,key 是 sheet 名,value 是 DataFrame。列名统一成小写下划线风格后,后面写X['intercalant_content']不会因为中英文混输而出错。source_sheet列用来追溯每行数据的来源,排查异常批次时按它筛选非常高效。
合并之后立即检查两件事:各列缺失值比例,以及 sample_id 是否有重复。材料实验里同一个样品测两次取平均是合理的,但如果两个 sheet 中出现完全相同的行,多半是录入时整行复制,drop_duplicates()直接去除即可。缺失值超过 30% 的特征建议直接丢弃,而不是填充,因为实验数据量本来就不大,填出来的值会带明显的人为偏差。
2.2 插层剂种类与含量的编码:名义变量和连续变量分开处理
合并后的 DataFrame 里,intercalant_type是名义变量,常见取值可能只有蒙脱土、纳米 SiO₂、TiO₂ 三种,不能直接编码成 0/1/2,否则模型会认为 TiO₂ 是蒙脱土的 2 倍。应当用 OneHotEncoder 展开成多个 0/1 列。其余连续变量如intercalant_content(含量百分比)、screw_speed(螺杆转速)、air_temp(热风温度)量纲差异大,树模型无所谓,线性模型会被大数值变量带走,所以需要 StandardScaler 标准化。用 ColumnTransformer 可以把两类处理放到同一个预处理对象里:
from sklearn.compose import ColumnTransformer from sklearn.preprocessing import OneHotEncoder, StandardScaler categorical_cols = ['intercalant_type'] numeric_cols = ['intercalant_content', 'screw_speed', 'air_temp', 'die_temp'] prep = ColumnTransformer([ ('cat', OneHotEncoder(handle_unknown='ignore'), categorical_cols), ('num', StandardScaler(), numeric_cols) ], remainder='drop')handle_unknown='ignore'解决一个实际场景:模型训练时没出现过的插层剂种类,在后续预测时不会抛异常,而是让所有哑变量保持 0。remainder='drop'明确丢弃source_sheet、sample_id这类不该进入模型的信息。需要特别注意的是,prep 的 fit 只能放在训练集上,transform 再作用到测试集,不能先在全量数据上标准化再划分训练测试集;这个细节放到第 4 章交叉验证处还会强调,是回归模型在小样本材料数据上最容易出问题的数据泄漏点。
2.3 用相关性矩阵初筛特征,避免强共线变量干扰回归模型
插层熔喷工艺的变量并不独立:热风温度直接影响纤维直径,而纤维直径又关系到过滤效率和透气率,特征之间的相关系数经常超过 0.9。对 Ridge 这类线性回归来说,强共线会让系数在不同参数组合下来回跳动,得到和材料常识相悖的回归方向。先把数值特征的相关矩阵画出来,顺便打印每个特征与目标变量的相关系数:
import matplotlib.pyplot as plt import seaborn as sns target = 'filtration_efficiency' corr = df_all[numeric_cols + [target]].corr() print(corr[target].sort_values(ascending=False)) plt.figure(figsize=(8, 6)) sns.heatmap(corr, annot=True, fmt='.2f', cmap='coolwarm', center=0) plt.title('feature correlation matrix') plt.tight_layout() plt.savefig('corr_heatmap.png', dpi=150)corr() 返回完整相关矩阵,按[target]取值就能得到每个特征与过滤效率的相关系数,降序打印方便判断哪些特征需要重点解释。热力图保存为 png,既便于贴进实验报告,也能在多人协作时快速沟通特征情况。发现两个特征相关系数绝对值超过 0.95 时,我不会自动删除,而是回到实验记录确认它们是不是同一件事的两种测量口径;优先保留业务上更关心、测量误差更小的那个。
注意:如果目标是预测过滤效率,纤维直径这类需要实际测试才能获得的中间性能,即使与目标相关性很高也应谨慎作为特征。否则在新配方上做预测时,必须先做出样品测出纤维直径,回归模型就失去了提前预测的意义。
3. 基线与主力:从线性回归到随机森林、XGBoost 回归模型的选型逻辑
特征工程完成后,模型选择我习惯分三步走:先用 Ridge 打基线,再上随机森林回归模型验证非线性空间,最后用 XGBoost 回归模型在限制过拟合的前提下看有没有进一步提升。材料实验数据点不多,模型间的差距不会像大数据集那么悬殊,因此每一步都必须有明确的判断标准,而不是把算法全部跑一遍挑个最高分。
3.1 先做统一特征切分,再用 Ridge 回归建立指标基线
为了不让模型对比变成“各写各的预处理”,先统一把训练测试集划分好,并让 prep 只拟合训练集,得到编码后的特征矩阵:
from sklearn.model_selection import train_test_split feature_cols = categorical_cols + numeric_cols X = df_all[feature_cols] y = df_all[target].astype(float) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) X_train_enc = prep.fit_transform(X_train) X_test_enc = prep.transform(X_test) print(f"train shape: {X_train_enc.shape}, test shape: {X_test_enc.shape}")prep 的 fit_transform 只发生在 X_train 上,X_test 走的是 transform,这样测试集不会把自己的均值和方差写进标准化参数。之后所有模型都基于X_train_enc训练,保证了对比公平。Ridge 回归是这套流程里最便宜的基线,它用 L2 正则收缩系数,适合特征列数接近样本量的情况:
from sklearn.linear_model import Ridge from sklearn.model_selection import cross_val_score ridge = Ridge(alpha=1.0) scores = cross_val_score(ridge, X_train_enc, y_train, cv=5, scoring='r2') print(f"Ridge 5-fold CV R2: {scores.mean():.3f} (+/- {scores.std():.3f})")cross_val_score默认用 R² 作为打分函数,返回值是每一折的分数,打印均值和标准差可以直观看到稳定性。如果 R² 为负,表示比“直接猜均值”还要差,先不要怀疑模型,回头查目标值是否方差过小、样本行数是否太少、或者编码后特征里混入了异常值。Ridge 的 alpha 默认 1.0 可以接受,不必第一轮就调,它存在的意义是给非线性模型一个参照系。
3.2 随机森林回归模型:小样本材料数据的默认选择
Ridge 假设性能与特征基本线性,但插层改性的关系往往带交互效应:同样 2% 的插层剂含量,在低转速下效果明显,高转速下几乎不起作用。线性模型要抓住这种关系需要手工构造交互项,而随机森林按特征阈值逐层切分,天然能处理这种非线性。材料数据样本少、异常点多,随机森林对异常值不敏感,参数也比较稳,所以是默认选择:
from sklearn.ensemble import RandomForestRegressor rf_model = RandomForestRegressor( n_estimators=300, min_samples_leaf=2, max_depth=6, random_state=42, n_jobs=-1 ) rf_scores = cross_val_score(rf_model, X_train_enc, y_train, cv=5, scoring='r2') print(f"RandomForest 5-fold CV R2: {rf_scores.mean():.3f} (+/- {rf_scores.std():.3f})")min_samples_leaf=2是防过拟合最关键的一项:它强制每个叶子节点至少保留 2 个样本,避免树把单条实验记录的偶然波动背下来。max_depth=6在特征数十几到二十几的材料项目里已经足够,超过 8 的深度几乎只在训练集上好看。n_estimators调到 300 是为了让集成结果稳定,它在 200 到 500 之间对精度影响很小,但取值太小会明显增加预测方差;random_state固定之后,网格搜索里不同参数组合才有可比性。
如果随机森林的交叉验证 R² 明显高于 Ridge,说明非线性关系真实存在,值得继续调参;如果两者差不多,那么后续宁可把精力放在 SHAP 解释上,也不要盲目追求更复杂的模型。
3.3 XGBoost 回归模型的关键参数与过拟合控制
XGBoost 在小样本上要特别小心。默认learning_rate=0.3、max_depth=6,训练集 R² 能达到 0.99,交叉验证却经常在 0.6 以下。我习惯先固定一组保守参数,再在交叉验证内部配合早停控制迭代轮数:
from xgboost import XGBRegressor from sklearn.model_selection import KFold import numpy as np kf = KFold(n_splits=5, shuffle=True, random_state=42) xgb_cv_scores = [] for train_idx, val_idx in kf.split(X_train_enc): model = XGBRegressor( n_estimators=500, learning_rate=0.05, max_depth=3, min_child_weight=3, subsample=0.8, colsample_bytree=0.8, random_state=42, verbosity=0 ) model.fit( X_train_enc[train_idx], y_train.iloc[train_idx], eval_set=[(X_train_enc[val_idx], y_train.iloc[val_idx])], verbose=False ) xgb_cv_scores.append(model.score(X_train_enc[val_idx], y_train.iloc[val_idx])) print(f"XGBoost 5-fold CV R2: {np.mean(xgb_cv_scores):.3f} (+/- {np.std(xgb_cv_scores):.3f})")每折循环里重新实例化 model,避免同一个对象在多次 fit 之间把前一次的早停状态带进来。learning_rate=0.05配合最大 500 棵树,等于用更慢的学习速度换取更平滑的下降,这是小样本上最有效的防过拟合手段之一。max_depth=3限制每棵树只能做三次分裂,min_child_weight=3要求叶子节点样本权重和至少达到 3,这两项把单棵树的容量压住。subsample和colsample_bytree都取 0.8,分别控制行采样和列采样,增加每棵树的随机性,让集成结果更稳健。如果验证集的分数在 100 棵以后持续下降,说明学习率还可以再调低;如果一开始就在波动,则说明数据量撑不起太复杂的集成,退回随机森林更合适。
交叉验证跑完后,用同样参数在全量训练集上拟合一份最终模型,后面评估和导出都能复用:
best_xgb_model = XGBRegressor( n_estimators=500, learning_rate=0.05, max_depth=3, min_child_weight=3, subsample=0.8, colsample_bytree=0.8, random_state=42, verbosity=0 ) best_xgb_model.fit(X_train_enc, y_train, verbose=False)4. 网格搜索与交叉验证:让回归模型在材料小数据上不飘
材料实验数据通常只有 30 到 200 条,这个量级下偶然因素会被放大:同一份数据换一个random_state,交叉验证分数可能从 0.85 掉到 0.70。调参的目标不是找到“全局最优参数”,而是找到在数据扰动下仍然稳定的参数区间。下面按交叉验证选择、网格搜索范围、指标解读三个方面来收口。
4.1 样本少于 30 时改用留一法,数据多时用 5 折交叉验证
5 折交叉验证在小样本上有个明显问题:总样本 25 个时每折验证集只有 5 个样本,验证分数的标准差极大,某一折出现一个离群实验点,分数就会被带偏。样本量低于 30 时,我一般直接切换到留一法,每次用一个样本做验证,虽然拟合次数多,但样本量小,计算时间反而可以接受。代码里按数据量自动切换:
from sklearn.model_selection import LeaveOneOut cv = LeaveOneOut() if len(X_train_enc) < 30 else 5 loo_scores = cross_val_score(rf_model, X_train_enc, y_train, cv=cv, scoring='r2') print(f"CV R2: {loo_scores.mean():.3f} (+/- {loo_scores.std():.3f})")注意 LeaveOneOut 的每次验证只用一条样本,预测结果对这条样本的测量误差非常敏感,所以它更适合用来判断“模型是不是在某几个样品上系统性失效”,而不是直接和 5 折分数比大小。数据量在 30 到 200 之间时,5 折配合shuffle=True就够用,折数再多也不会带来明显收益。所有用到随机采样的模型必须固定random_state,否则每次跑出来的指标都不同,材料团队根本无法判断调参是否真的有效。
4.2 GridSearchCV 调参:三个参数就够用
随机森林需要网格搜索的参数其实只有三个:max_depth、min_samples_leaf和max_features。n_estimators不产生过拟合,只影响计算时间,直接固定。max_depth的范围取 [4, 6, 8],在特征数不超过 20 的材料数据里,8 已经接近上限;超过 8 基本是记住噪声。min_samples_leaf取 [2, 3, 5],值越大叶子越粗,对噪声的容忍度越高。max_features取 [0.6, 0.8, 1.0],表示每棵树随机看到的特征比例,它决定了树之间的差异度:
from sklearn.model_selection import GridSearchCV param_grid = { 'max_depth': [4, 6, 8], 'min_samples_leaf': [2, 3, 5], 'max_features': [0.6, 0.8, 1.0] } grid = GridSearchCV( RandomForestRegressor(n_estimators=300, random_state=42, n_jobs=-1), param_grid, cv=5, scoring='r2', refit=True ) grid.fit(X_train_enc, y_train) print(f"best params: {grid.best_params_}") print(f"best CV R2: {grid.best_score_:.3f}")GridSearchCV 内部会对每组参数组合做 5 折交叉验证,cv 的折数与前面保持一致,避免“调参用 5 折、评估用留一法”这种指标口径混乱。refit=True表示搜索完成后用全部训练数据重新训练一份最佳模型,这份模型可以直接拿去做测试集评估和导出。如果best_params_落在搜索区间的边界,比如max_depth=8是三个选项里的最大值,说明边界外可能还有更好的参数,下一轮搜索要把范围外扩;如果落在中间,说明当前范围覆盖了稳定区间。
4.3 用 R²、MAE、RMSE 三个指标判断回归模型是否可用
网格搜索内部用 R² 做排序没问题,但对外报告时只看 R² 不够。材料工程师更关心“预测误差到底是多少个百分点”,MAE 直接给出平均绝对误差,RMSE 放大极端偏差,配合使用才不会漏掉个别配方的严重失效。在测试集上统一评估三个模型:
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error def evaluate_model(name, model, X_test_enc, y_test): pred = model.predict(X_test_enc) return { 'model': name, 'R2': round(r2_score(y_test, pred), 3), 'MAE': round(mean_absolute_error(y_test, pred), 3), 'RMSE': round(np.sqrt(mean_squared_error(y_test, pred)), 3) } results = [ evaluate_model('Ridge', ridge, X_test_enc, y_test), evaluate_model('RandomForest', grid.best_estimator_, X_test_enc, y_test), evaluate_model('XGBoost', best_xgb_model, X_test_enc, y_test) ] print(pd.DataFrame(results))输出的对比表大致长这样:
| 模型 | R² | MAE | RMSE |
|---|---|---|---|
| Ridge | 0.711 | 2.134 | 2.887 |
| RandomForest | 0.843 | 1.672 | 2.109 |
| XGBoost | 0.829 | 1.735 | 2.254 |
如果三个模型的 RMSE 都稳定在 2 个百分点左右,说明数据里存在约 2% 的不可约噪声,可能是测量仪器本身的误差,也可能是温湿度这类没记录的环境变量;这种情况下继续调参的收益有限,更值得做的是补充实验数据。如果树模型明显低于线性模型,说明非线性效应真实存在,调参方向就对了。另外,测试集只占 20% 时样本数可能不到 10 条,单次划分的指标偶然性很大;更可靠的做法是多次用不同 random_state 划分并记录指标均值,而不是只报一次最好看的数。
5. 从预测到解释:用 SHAP 找出关键工艺变量,把模型导出成可调用脚本
回归模型跑通只是第一步,材料团队拿到模型后通常要回答两件事:哪个工艺变量对过滤效率影响最大,以及换一组新配方参数时如何快速得到预测值。分别对应 SHAP 分析和模型导出。
5.1 SHAP 值定位影响过滤效率的关键工艺变量
SHAP 值对树模型给出的是加性解释,能告诉我们某一样品的过滤效率为什么高于或低于平均。把 GridSearchCV 得到的最佳随机森林套进一个完整 Pipeline,再做 SHAP 分析,这样特征列名不会乱:
import shap from sklearn.pipeline import Pipeline import pandas as pd final_pipeline = Pipeline([ ('prep', prep), ('rf', grid.best_estimator_) ]) X_test_enc_shap = final_pipeline.named_steps['prep'].transform(X_test) feature_names = final_pipeline.named_steps['prep'].get_feature_names_out() X_test_df = pd.DataFrame(X_test_enc_shap, columns=feature_names) explainer = shap.TreeExplainer(final_pipeline.named_steps['rf']) shap_values = explainer.shap_values(X_test_df) shap.summary_plot(shap_values, X_test_df)summary_plot里每个点代表一个样本,颜色代表特征值高低,点分布在 x=0 左侧表示该特征把预测值压低,右侧表示推高。材料数据里最常见的结论是:插层剂含量对过滤效率不是单调关系,含量低时提升、超过某个阈值反而下降,这在 SHAP 图上会表现为正负两侧都有分布。特征名会带num__、cat__前缀,这是 ColumnTransformer 的命名规则,看多了就习惯了。
5.2 用 joblib 导出完整流水线,预测脚本直接调用
不要只导出随机森林模型,一定要把 prep 和模型包在一个 Pipeline 里整体导出。这样预测脚本不需要关心 OneHot 编码列名和标准化参数,减少很多手动对齐的工作:
import joblib joblib.dump(final_pipeline, 'meltblown_perf_model.joblib')预测脚本里按原始列名传参数即可,键名必须与训练时feature_cols完全一致,包括大小写和下划线:
import joblib import pandas as pd model = joblib.load('meltblown_perf_model.joblib') def predict_performance(config: dict) -> float: df = pd.DataFrame([config]) pred = model.predict(df) print(f"predicted filtration efficiency: {pred[0]:.2f}%") return pred[0] predict_performance({ 'intercalant_type': 'nanoclay', 'intercalant_content': 2.5, 'screw_speed': 800, 'air_temp': 210, 'die_temp': 220, })5.3 zip 解压、环境与复现的几个实际注意点
下载的 zip 如果解压时报error read zip archive一类的错误,优先判断压缩包是否下载完整,Windows 下换 7-Zip 或者 WinRAR 重新解压,Linux 下先跑unzip -t检查完整性,大部分情况都是传输过程丢字节,而不是代码本身的问题。脚本文件名如果是中文,解压后建议改成 ASCII 命名再运行,避免不同系统间的编码差异。运行环境方面,新建一个虚拟环境再安装 pandas、scikit-learn、xgboost、shap 这几个依赖,配合 vscode 里配置好的 python 环境和代码补全,跑通整套脚本基本不会超过半小时。最后把训练时用的feature_cols、目标列名、数据量写进一个 README 文件,材料团队三个月后再看就不会出现“这个列是哪来的”的问题。
本文还有配套的精品资源,点击获取