4. 数据清洗与预处理:缺失值处理、异常值识别、数据标准化与归一化、数据合并(Merge/Join)
各位工程师,欢迎来到第四章。数据清洗,说白了就是给数据“洗澡”。
我见过太多人,拿到数据就急着建模跑分析。结果呢?模型跑出来一塌糊涂。为什么?因为数据本身就有问题。你想想看,垃圾进,垃圾出,这个道理在半导体良率分析里特别明显。
这一章,我把自己踩过的坑、积累的经验,全部分享给你。咱们从四个核心环节入手:缺失值、异常值、标准化、数据合并。
4.1 缺失值处理:别让“空”坑了你
在Fab里,数据缺失太常见了。设备宕机、传感器故障、人为漏录……原因五花八门。我个人习惯,拿到数据第一件事就是看缺失率。
核心原则:缺失率低于5%,直接删除行;缺失率在5%-20%,考虑填充;超过20%,这个特征基本废了,建议直接扔掉。
填充方法,我常用这几种:
- 均值/中位数填充:适合正态分布的数据。比如CD(关键尺寸)测量值,用中位数填充比较稳。
- 前向/后向填充:时间序列数据,比如机台PM后的参数漂移,用前向填充很合理。
- 模型预测填充:用KNN或回归模型预测缺失值。我在处理WAT(晶圆允收测试)数据时用过,效果不错,但计算量大。
我的经验:千万别用0去填充缺失值!有一次,一个同事把缺失的良率数据填了0,结果模型直接崩了。0在良率数据里是极端异常值,会严重扭曲分布。
# Python示例:缺失值处理
import pandas as pd
import numpy as np
# 读取数据
df = pd.read_csv('yield_data.csv')
# 查看缺失率
missing_rate = df.isnull().mean()
print(missing_rate)
# 中位数填充
df['CD_measurement'].fillna(df['CD_measurement'].median(), inplace=True)
# 前向填充(时间序列)
df['temperature'].fillna(method='ffill', inplace=True)
4.2 异常值识别:揪出“害群之马”
异常值,就是数据里的“老鼠屎”。在半导体良率分析中,异常值往往对应着设备异常、工艺偏移或者测量错误。
我曾经遇到过一个案例:一批晶圆的良率突然暴跌,查了三天没找到原因。最后发现,是一个探针卡接触不良,导致测试数据全是异常值。你看,异常值识别有多重要。
我常用的识别方法:
- 3σ原则:适用于正态分布的数据。超出均值±3个标准差的值,视为异常。
- 箱线图法(IQR):不依赖分布假设。Q1-1.5*IQR 到 Q3+1.5*IQR 之外的值,就是异常。
- 领域知识法:这个最靠谱。比如,光刻胶厚度不可能为负,也不可能超过100μm。超出物理极限的,直接标记。
注意:异常值不一定要删除!有时候,异常值本身就是重要信号。比如,某个机台的温度突然飙升,这可能是故障前兆。我建议先标记,再根据业务判断是否保留。
# Python示例:箱线图法识别异常值
Q1 = df['yield_rate'].quantile(0.25)
Q3 = df['yield_rate'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df['yield_rate'] < lower_bound) | (df['yield_rate'] > upper_bound)]
print(f"发现 {len(outliers)} 个异常值")
4.3 数据标准化与归一化:让数据“站在同一起跑线”
为什么需要标准化?你想想看,温度是几十度,压力是几百帕,良率是百分之几十。量纲不同,直接放一起分析,结果肯定偏。
我见过有人直接用原始数据跑PCA(主成分分析),结果第一主成分完全被压力这个特征主导了。嗯,这就是没标准化的后果。
两种常用方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score标准化 | (x - μ) / σ | 数据近似正态分布,适合回归、SVM等模型 |
| Min-Max归一化 | (x - min) / (max - min) | 数据有明确边界,适合神经网络、KNN |
我的建议:在半导体良率分析中,我更喜欢用Z-score。因为良率数据经常有离群点,Min-Max会被极端值压缩,而Z-score相对稳健。当然,如果你用深度学习,还是归一化更合适。
# Python示例:标准化与归一化
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score标准化
scaler_std = StandardScaler()
df_scaled = scaler_std.fit_transform(df[['temperature', 'pressure', 'yield_rate']])
# Min-Max归一化
scaler_mm = MinMaxScaler()
df_normalized = scaler_mm.fit_transform(df[['temperature', 'pressure', 'yield_rate']])
4.4 数据合并(Merge/Join):把碎片拼成拼图
半导体数据,从来不是一张表能搞定的。WAT数据、CP测试数据、FT测试数据、设备日志……它们分散在不同的系统里。数据合并,就是把碎片拼成完整的拼图。
我记得有一次做良率根因分析,需要把WAT数据和CP数据合并。两个表的晶圆ID格式不一样,一个带字母前缀,一个纯数字。折腾了半天,最后发现是编码规则变了。嗯,这种坑,我踩过不止一次。
合并的三种方式:
- Inner Join:只保留两个表都有的记录。适合严格匹配的场景。
- Left/Right Join:保留主表所有记录。适合以某个表为主的情况。
- Outer Join:保留所有记录。适合数据完整性要求高的场景。
避坑指南:合并前,一定要检查键的唯一性和数据类型。我曾经因为一个空格没去掉,导致合并后数据量翻了三倍。还有,注意重复列名,合并后记得加后缀区分。
# Python示例:数据合并
# 表1:WAT数据
df_wat = pd.DataFrame({
'wafer_id': ['W001', 'W002', 'W003'],
'Vt': [0.45, 0.48, 0.42]
})
# 表2:CP测试数据
df_cp = pd.DataFrame({
'wafer_id': ['W001', 'W002', 'W004'],
'yield': [92.5, 95.1, 88.3]
})
# Inner Join
df_inner = pd.merge(df_wat, df_cp, on='wafer_id', how='inner')
print(df_inner)
# Left Join
df_left = pd.merge(df_wat, df_cp, on='wafer_id', how='left')
print(df_left)
好了,数据清洗与预处理就讲到这里。这四个环节,每一个都值得你花时间打磨。记住,好的数据清洗,是良率分析成功的一半。下一章,咱们聊聊特征工程,看看怎么从数据里挖出更多有价值的信息。