第四节:数据采集与预处理——从车间到模型的“第一公里”
各位同行,今天咱们聊聊数据。做烧结收缩率预测,说白了就是“用历史数据预测未来”。但车间里的数据,可不是实验室里那么干净。我见过太多人,模型建得花里胡哨,结果一跑就崩——问题全出在数据上。
这一节,我把自己踩过的坑、总结的经验,全盘托出。你想想看,如果数据不准,再牛的算法也是白搭。
4.1 历史生产数据收集——别嫌麻烦,这是地基
数据收集,听起来简单吧?不就是把生产记录导出来嘛。但我在项目里遇到过,有的工厂连“烧结温度”都记不全,更别说“升温速率”这种细节了。
我个人习惯,数据收集要覆盖这三大类:
- 原材料数据:粉末牌号、粒度分布、松装密度、化学成分(比如碳含量、氧含量)。
- 工艺参数:压制压力、保压时间、烧结温度曲线(升温段、保温段、降温段)、气氛类型(氢气、氮气、真空)。
- 成品检测数据:烧结后的尺寸(长、宽、高、内径)、密度、硬度、金相组织。
嗯,这里要注意:数据的时间跨度最好超过3个月,覆盖不同季节、不同批次。为什么?因为车间温湿度会影响粉末流动性,进而影响收缩率。我吃过这个亏,夏天和冬天的数据混在一起,模型直接“懵了”。
核心原则:数据量不是越多越好,而是“全”且“准”。至少500条以上,每条记录都要有完整的工艺链。
4.2 数据清洗——把“垃圾”筛出去
数据拿到手,第一件事不是建模,而是“洗菜”。车间数据里,什么妖魔鬼怪都有。
4.2.1 异常值处理——别让一个坏点带偏整个模型
我曾经遇到一批数据,收缩率突然从1.2%跳到了3.5%。查了半天,原来是操作工把“烧结温度”写成了“炉膛温度”——差了200度!
我常用的方法:
- 3σ原则:超出均值±3个标准差的数据,标记为异常。适用于正态分布的数据。
- 箱线图法:用IQR(四分位距)判断。低于Q1-1.5*IQR或高于Q3+1.5*IQR,视为异常。
- 业务规则:比如“烧结温度低于1000℃”或“收缩率超过5%”,直接人工复核。
举个例子,我处理过一批铁基粉末的数据:
import pandas as pd
import numpy as np
# 假设df是数据框,'shrinkage'是收缩率列
Q1 = df['shrinkage'].quantile(0.25)
Q3 = df['shrinkage'].quantile(0.75)
IQR = Q3 - Q1
# 定义异常值边界
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 标记异常值
df['is_outlier'] = (df['shrinkage'] < lower_bound) | (df['shrinkage'] > upper_bound)
# 查看异常值
print(df[df['is_outlier']])
注意:别一棍子打死所有异常值。有些“异常”其实是新工艺试产的数据,反而有价值。我建议先标记,再人工确认。
4.2.2 缺失值填充——别让“空”变成“错”
车间记录本上,经常有空白。比如“保压时间”没填,“密度”没测。怎么办?
我的处理策略:
- 删除法:如果缺失率超过30%,直接删掉该特征或该样本。别心疼,留着也是祸害。
- 均值/中位数填充:适用于数值型特征,比如“烧结温度”缺失,用同批次均值补上。
- 前向/后向填充:适用于时间序列数据。比如“炉温”每隔1秒记录一次,中间缺失了,用前一个值补。
- 模型预测填充:用其他特征(如压制压力、粉末粒度)预测缺失值。这个方法最准,但计算量大。
我记得有一次,一个客户的数据里“氧含量”缺失了40%。我直接用“粉末牌号+存储时间”做了个线性回归模型来预测,效果还不错。
4.3 特征工程——从原始数据里“挖金子”
数据洗干净了,但原始特征还不够。比如“烧结温度曲线”是一个序列,你不能直接把1000个温度点扔进模型。得提炼出有物理意义的特征。
4.3.1 温度曲线特征——把“形状”变成数字
温度曲线,说白了就是“升温-保温-降温”三段。我习惯提取这些特征:
- 升温速率:从室温到保温温度的斜率。单位℃/min。速率太快,粉末内部气体来不及排出,容易鼓包。
- 保温时间:在最高温度停留的时长。单位min。时间越长,收缩越充分,但晶粒也会长大。
- 降温速率:从保温温度到出炉温度的斜率。单位℃/min。冷却太快,容易产生内应力。
- 最高温度:曲线峰值。这个不用多说,直接影响液相烧结的程度。
- 曲线下面积:积分后的总热量。相当于“热输入总量”。
举个例子,我处理过一组316L不锈钢粉末的数据:
# 假设temp_curve是温度曲线列表,time是时间列表
import numpy as np
# 计算升温速率(前30%的数据)
heating_segment = temp_curve[:int(len(temp_curve)*0.3)]
heating_rate = (heating_segment[-1] - heating_segment[0]) / (time[int(len(time)*0.3)] - time[0])
# 计算曲线下面积(梯形积分)
area = np.trapz(temp_curve, time)
print(f"升温速率: {heating_rate:.2f} ℃/min")
print(f"曲线下面积: {area:.2f} ℃·min")
4.3.2 密度特征——从“压坯密度”到“烧结密度”
密度是收缩率的“晴雨表”。我一般提取这几个:
- 压坯密度:压制后的密度。单位g/cm³。压坯密度越高,烧结收缩率越小(因为已经比较密实了)。
- 烧结密度:烧结后的密度。这个直接测量。
- 相对密度:烧结密度/理论密度。理论密度可以从粉末成分查表得到。
- 密度增量:烧结密度 - 压坯密度。这个值越大,说明收缩越剧烈。
小技巧:如果压坯密度数据缺失,可以用“压制压力+粉末粒度”来估算。我做过一个简单模型,R²能达到0.85以上。
4.3.3 成分特征——别小看那0.1%的差异
粉末成分,哪怕差0.1%,收缩率都可能天差地别。我重点关注:
- 主要元素含量:比如Fe、Cu、Ni、C。这些是骨架元素。
- 微量元素:比如P、S、O。P能促进液相烧结,但多了会脆;O多了会氧化。
- 粒度分布:D10、D50、D90。细粉多,收缩率大;粗粉多,收缩率小。
- 形状因子:球形粉 vs 不规则粉。球形粉流动性好,但收缩率相对稳定。
我曾经遇到一个案例:两批粉末,成分几乎一样,但收缩率差了0.3%。查了半天,发现是“碳含量”差了0.05%。别看这0.05%,在烧结过程中,碳会与氧反应生成CO气体,影响致密化。
4.4 知识体系总览——一张图看懂数据预处理
说了这么多,我画了张图,把整个流程串起来。你一看就明白:
4.5 避坑指南——我踩过的那些坑
最后,分享几个实战教训:
- 别信“全自动采集”:有一次,客户说数据是系统自动采集的,绝对准。结果我一查,传感器坏了3天,数据全是“25℃”。嗯,手动复核永远不能省。
- 小心“批次效应”:不同批次的粉末,即使配方一样,收缩率也可能有差异。我建议把“批次号”也作为一个特征,或者做分层采样。
- 特征不是越多越好:我曾经一口气提取了50个特征,结果模型过拟合得一塌糊涂。后来用PCA降维,只留了10个主成分,反而更准。
- 数据标准化:温度是1000℃级别,密度是7g/cm³级别,不标准化的话,模型会“偏袒”数值大的特征。我习惯用Z-score标准化。
一句话总结:数据预处理花的时间,至少占整个项目的60%。别急着建模,先把数据伺候好了。数据干净了,模型自然就听话了。
公众号:蓝海资料掘金营,微信deep3321