数据预处理基础:数据清洗、标准化与时间对齐
各位同学,今天我们来聊聊数据预处理。说实话,我在多源情绪融合项目里踩过的坑,十有八九都出在这一步。数据没洗干净,后面模型再花哨也是白搭。咱们直接进入正题。
一、数据清洗——先把脏东西筛掉
多源情绪数据,说白了就是一堆乱七八糟的信号凑在一起。有文本、有语音、有生理信号,每个来源都可能带点「杂质」。我个人的习惯是:拿到数据第一件事,先做缺失值和异常值的排查。
1. 缺失值处理
缺失值很常见。比如用户做情绪实验时,眼动追踪仪突然断连了,那几秒的数据就是空的。怎么办?
- 直接删除:如果缺失比例小于5%,且是随机缺失,我一般直接删掉。省事。
- 均值/中位数填充:对于连续型生理信号(如心率、皮电),用该特征的整体均值填充。但要注意——如果数据本身波动大,均值填充会抹掉细节。
- 插值法:时间序列数据,我推荐线性插值或样条插值。比如脑电信号中间丢了一帧,前后帧取平均,比硬填一个固定值靠谱得多。
- 模型预测填充:如果缺失值很重要(比如关键情绪标签),我会用KNN或随机森林去预测缺失值。不过代价是计算量大,慎用。
避坑指南:我曾经在一个语音情绪数据集里,用均值填充了所有缺失的基频(F0)值。结果模型训练出来,对中性情绪的识别率奇高,但对愤怒和悲伤几乎失效。后来才发现——基频的缺失往往发生在情绪波动大的片段,均值填充直接把情绪差异给抹平了。所以,缺失机制一定要先搞清楚。
2. 异常值处理
异常值比缺失值更隐蔽。你想想看,一个用户的心率突然飙到200,是真实情绪激动还是传感器松了?
- 3σ原则:对于正态分布的数据,超过均值±3倍标准差的值,视为异常。简单粗暴,但有效。
- IQR(四分位距)法:更稳健。Q1-1.5*IQR 到 Q3+1.5*IQR 之外的值,标记为异常。我偏好这个方法,因为它不受极端值影响。
- 领域知识判断:比如心率正常范围是40-200 bpm,超出这个范围的直接剔除。别问我为什么上限是200——我在实验室见过一个被试者跑完楼梯后测情绪,心率198,那是真激动。
小技巧:处理异常值时,别急着删。先可视化——画个箱线图或散点图,看看异常值是不是集中在某个情绪类别里。如果是,那可能是那个情绪本身的特征,不是噪声。
二、数据标准化与归一化——让不同尺度的数据能对话
多源情绪数据有个头疼的问题:尺度不统一。心率是几十到一百多,皮电是微西门子(μS)级别,语音能量是分贝(dB)级别。如果不做标准化,模型会天然偏向数值大的特征。我见过有人直接把原始数据扔进神经网络,结果损失函数死活不收敛——嗯,就是这个原因。
1. 归一化(Min-Max Scaling)
把数据缩放到[0,1]区间。公式很简单:(x - min) / (max - min)。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
normalized_data = scaler.fit_transform(raw_data)
适用场景:数据分布比较均匀,没有极端异常值。比如面部表情编码(AU单元)的强度值,用归一化就很合适。
注意:如果新数据来了,它的值可能超出原来的min-max范围。所以实际部署时,一定要保存训练集的min和max,用它们去变换新数据。
2. 标准化(Z-score)
把数据变成均值为0、标准差为1的分布。公式:(x - μ) / σ。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
standardized_data = scaler.fit_transform(raw_data)
适用场景:数据近似正态分布,或者后续要用到距离度量(如KNN、SVM)。我个人在融合脑电和眼动数据时,几乎都用标准化——因为脑电信号的幅值本身就有个体差异,标准化后能消除这种差异。
3. 什么时候用哪个?
| 方法 | 适用场景 | 我的经验 |
|---|---|---|
| Min-Max归一化 | 数据有明确边界(如0-1的评分) | 文本情感极性得分,我常用这个 |
| Z-score标准化 | 数据有异常值或分布未知 | 生理信号(心率、皮电)首选 |
| Robust缩放 | 数据含大量异常值 | 语音特征(如MFCC)偶尔用 |
三、时间序列对齐技术——让不同频率的信号同步
这是多源融合里最让我头疼的一步。文本数据是离散的(一句话一个时间戳),语音是连续的(16kHz采样),生理信号可能是低频的(比如皮电只有4Hz)。怎么让它们对齐?
1. 硬对齐:重采样
把所有信号统一到同一个采样率。比如语音是100Hz,生理信号是50Hz,那就把语音降采样到50Hz,或者把生理信号升采样到100Hz。
import pandas as pd
# 假设生理信号是低频的,升采样到语音的采样率
physio_resampled = physio_data.resample('10ms').interpolate(method='linear')
我建议:降采样比升采样更安全。升采样会引入虚假信息,降采样只是丢失细节。如果精度允许,尽量往低频对齐。
2. 软对齐:时间窗口法
不要求每个时间点严格对齐,而是用滑动窗口提取特征。比如每2秒一个窗口,窗口内计算语音的MFCC均值和生理信号的均值。这样不同源的数据在窗口级别就对齐了。
实战经验:我在做一个多模态情绪识别项目时,语音和脑电的采样率差了100倍。硬对齐后数据量爆炸,模型训练慢得要死。后来改用2秒滑动窗口,步长0.5秒,每个窗口提取统计特征(均值、方差、最大值)。效果反而更好——因为窗口特征比原始点特征更鲁棒。
3. 事件触发对齐
有些场景下,数据是事件驱动的。比如用户点击按钮时,同时记录语音和面部视频。这时候可以用事件时间戳作为锚点,把不同源的数据片段对齐。
# 假设事件时间戳列表
event_timestamps = [1.2, 3.5, 7.8]
# 对每个事件,截取前后1秒的数据片段
for ts in event_timestamps:
audio_segment = audio_data[ts-1:ts+1]
video_segment = video_data[ts-1:ts+1]
小提示:事件触发对齐的关键是时间戳的精度。我遇到过系统时钟不同步的问题——语音采集用电脑时间,生理信号采集用蓝牙设备时间,两者差了200ms。后来统一用NTP服务器同步,才解决。
知识体系总览
下面这张图,是我自己梳理的数据预处理核心逻辑。你可以把它当成一个检查清单:拿到多源数据后,按这个流程走一遍,基本不会漏掉关键步骤。
这张图里,我特意把「常见问题」列在了左侧。为什么?因为预处理不是流水线,而是一个迭代过程。你做完标准化发现异常值没处理好,就得回头重新清洗。我做过最夸张的一个项目,光预处理就迭代了7轮——嗯,最后模型效果确实好,但过程确实磨人。
好了,数据预处理的基础就这些。记住一句话:预处理花的时间,会在模型训练和部署阶段加倍还给你。别偷懒,每一步都做扎实了。
公众号:蓝海资料掘金营,微信deep3321