4. 数据预处理与清洗:从原始垃圾到可用资产
数据预处理,说白了就是「洗菜」。你从地里拔出来的萝卜带着泥,直接下锅肯定不行。非结构化数据更是如此——文本里藏着乱码,图片分辨率参差不齐,音频里全是环境噪音。我做了这么多年数据治理,见过太多「模型不收敛」的案例,最后查下来,80% 的问题都出在预处理阶段。
这一章,我们就把四种主流非结构化数据的预处理流程拆开揉碎。你跟着走一遍,以后拿到原始数据就不会慌了。
4.1 文本清洗:正则与去噪
文本是非结构化数据里最常见的形式。但原始文本有多脏?我举个例子:爬虫抓下来的网页,里面可能夹杂着 HTML 标签、JavaScript 代码、特殊符号、全半角混用、重复空格……这些噪声不清理,分词和向量化都会出问题。
4.1.1 正则表达式:文本清洗的瑞士军刀
正则表达式(Regex)是文本清洗的必备技能。我个人习惯把常用模式存成一个工具库,随取随用。下面这个 Python 函数,覆盖了 90% 的日常清洗场景:
import re
def clean_text(text):
# 1. 去除 HTML 标签
text = re.sub(r'<[^>]+>', '', text)
# 2. 去除 URL
text = re.sub(r'http\S+|www\S+', '', text)
# 3. 去除特殊符号(保留中文、英文、数字、基本标点)
text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s\.\,\!\?\-\:\;]', '', text)
# 4. 合并多余空格和换行
text = re.sub(r'\s+', ' ', text).strip()
# 5. 全角转半角(数字和字母)
text = text.translate(str.maketrans(
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
))
return text
4.1.2 常见噪声类型与处理策略
| 噪声类型 | 示例 | 处理方式 |
|---|---|---|
| HTML/XML 标签 | <div class="content"> |
正则 <[^>]+> 替换为空 |
| URL 和邮箱 | https://example.com |
正则 http\S+ 或 \S+@\S+\.\S+ |
| 特殊符号 | ★◆※→ |
白名单保留中文、英文、数字、基本标点 |
| 重复字符 | 好开心啊啊啊啊啊 |
正则 (.)\1{2,} 替换为 \1\1 |
| 全半角混用 | Hello,世界 |
全角字母数字转半角,全角标点转半角 |
re.sub(r'[^\w\s]', '', text) 去除了所有标点。结果「好!不错」变成了「好不错」,语义完全变了。中文标点(。,!?)一定要保留,除非你做的是关键词匹配。
4.2 图像预处理:缩放与归一化
图像数据预处理的核心目标只有一个:让模型看到「标准」的输入。你想想看,如果训练集里图片大小不一、亮度各异、角度歪斜,模型学到的特征就会混乱。
4.2.1 图像缩放与裁剪
大多数深度学习模型要求固定输入尺寸,比如 224×224 或 256×256。直接拉伸会变形,直接裁剪可能丢失关键信息。我常用的策略是「先等比例缩放,再中心裁剪」:
from PIL import Image
def resize_and_crop(image_path, target_size=224):
img = Image.open(image_path)
# 等比例缩放,使短边等于 target_size
w, h = img.size
if w < h:
new_w = target_size
new_h = int(h * target_size / w)
else:
new_h = target_size
new_w = int(w * target_size / h)
img = img.resize((new_w, new_h), Image.LANCZOS)
# 中心裁剪
left = (new_w - target_size) // 2
top = (new_h - target_size) // 2
img = img.crop((left, top, left + target_size, top + target_size))
return img
4.2.2 像素值归一化
归一化不是可选项,是必选项。原始像素值范围是 0-255,直接喂给神经网络会导致梯度爆炸或收敛缓慢。标准做法是缩放到 [0,1] 或标准化到均值为 0、方差为 1:
import numpy as np
def normalize_image(img_array):
# 方法一:缩放到 [0, 1]
img_normalized = img_array / 255.0
# 方法二:标准化(使用 ImageNet 统计值)
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
img_standardized = (img_normalized - mean) / std
return img_standardized
关键点:训练集和测试集必须使用完全相同的归一化参数。我见过有人对训练集用 [0,1] 缩放,对测试集用标准化,结果模型评估指标直接崩了。
4.3 音频/视频预处理
音频和视频的预处理比文本和图像更复杂,因为涉及时间维度。我刚开始接触时也踩了不少坑,这里把核心流程梳理清楚。
4.3.1 音频预处理流程
音频数据通常以波形(WAV)或压缩格式(MP3)存储。预处理的核心步骤包括:
- 重采样:统一采样率,常见的是 16kHz 或 22.05kHz
- 声道合并:立体声转单声道,减少数据量
- 静音切除:去除首尾静音段,减少无效计算
- 预加重:提升高频信号,补偿语音信号的高频衰减
- 分帧加窗:将长音频切分成短帧(通常 25ms),帧移 10ms
import librosa
def preprocess_audio(file_path, target_sr=16000):
# 加载音频,自动重采样
audio, sr = librosa.load(file_path, sr=target_sr, mono=True)
# 去除首尾静音
audio, _ = librosa.effects.trim(audio, top_db=20)
# 预加重
pre_emphasis = 0.97
audio = np.append(audio[0], audio[1:] - pre_emphasis * audio[:-1])
return audio, sr
4.3.2 视频预处理要点
视频本质上是「图像序列 + 音频流」。预处理时,我习惯把视频拆成两个独立管道:
| 管道 | 处理内容 | 常用工具 |
|---|---|---|
| 视觉管道 | 抽帧(固定帧率)、缩放、归一化、数据增强 | OpenCV, FFmpeg |
| 音频管道 | 提取音频流、重采样、降噪、分帧 | librosa, FFmpeg |
4.4 数据质量评估
预处理做完了,怎么知道效果好不好?不能光靠「看着还行」。数据质量评估需要量化指标。我总结了一套「四维评估法」:
4.4.1 完整性
检查缺失值比例。文本字段是否有空字符串?图片是否损坏无法打开?音频文件是否静音?
4.4.2 一致性
检查格式是否统一。所有图片是否都是 RGB 三通道?所有音频采样率是否一致?时间戳格式是否相同?
4.4.3 准确性
抽样人工验证。文本清洗后是否保留了关键信息?图片裁剪后主体是否完整?音频降噪后是否引入了失真?
4.4.4 时效性
检查数据时间戳是否在合理范围内。我遇到过一批新闻数据,时间戳全是 1970-01-01,显然是解析错误。
os.path.getsize() 检查文件大小是否大于 0。
4.5 本章知识体系总览
下面这张图把四种非结构化数据的预处理流程和评估维度串起来了。你可以把它当作操作手册,做项目时对照着来:
预处理不是一次性工作。你跑完一轮评估,发现完整性不达标,就得回去补清洗逻辑;发现一致性有问题,就得调整格式转换代码。这个循环可能要跑好几轮,但每一轮都会让数据质量上一个台阶。
嗯,这一章的内容就到这里。记住一句话:预处理花的时间,会在模型训练阶段十倍地还给你。