文本预处理基础:清洗、标准化、分词前处理
很多人一上来就直接搞分词、跑模型,结果模型学出来的东西乱七八糟。我早期也踩过这个坑——数据还没洗干净就喂给模型,输出全是噪音。说白了,文本预处理就是给机器“喂饭”前的洗菜切菜环节。你想想看,菜都没洗干净的厨师能做出好菜吗?
为什么文本预处理如此关键
原始文本里全是“杂质”:HTML标签、特殊符号、大小写混乱、不统一的全角半角、拼写错误……机器可不像我们人眼一样能自动过滤这些。举个例子,“I'm fine” 和 “i am fine” 在语义上一样,但如果不做大小写统一和缩写展开,模型就会把它们当成两个不同的,白白浪费资源。
核心原则:预处理的目标是保留语义信息,去掉非信息噪音。千万不要过度清洗把有用的结构也删掉了。
文本清洗:去除噪音
清洗是第一道关卡。常见的噪音包括:
- HTML/XML标签:网页抓取来的文本常带
<p>、<br>等,需要正则或专用库(如BeautifulSoup)剥离。 - 特殊字符和Emoji:比如 @、#,或者大量重复的标点
!!!。我个人习惯保留句号、问号等基本标点,但去掉连续重复的符号。 - URL和邮箱:通常替换为特殊标记如
[URL],或者直接移除。 - 非ASCII字符:如果是英文任务,把全角字符转成半角;中文任务则保留中文标点。
小技巧:清洗的正则表达式尽量写严谨,别把语言本身的缩写或标点(如 can't 里的单引号)也删了。我曾经因为一刀切规则把 "it's" 清洗成了 "its",语义完全变了。
文本标准化:统一格式
标准化就是把各种“变体”拉回到一个统一的表现形式。常见操作:
| 操作 | 说明 | 示例 |
|---|---|---|
| 大小写统一 | 英文全部转小写(大多数情况) | “Hello” → “hello” |
| 缩写展开 | “don’t” → “do not” | 需注意上下文,避免过度展开 |
| 数字处理 | 数字替换为 <NUM> 或保留原样 | “123” → “<NUM>” |
| 词形还原/词干提取 | “running” → “run” (词干) 或 “run” (词形还原) | 词干更激进,词形还原保留合法词 |
| 全半角转换 | 全角字母和数字转为半角 | “A” → “A” |
这里要注意顺序。我习惯先做大小写统一,再做缩写展开,因为某些缩写依赖小写识别。
分词前处理:为分词铺路
把文本喂给分词器之前,还有几步不能少:
- 去除多余空格和换行符:连续的空白符压缩成一个空格。
- 句子分割:把长文本拆成句子,很多分词器对跨句的分词效果不好。
- 编码统一:确保所有字符都是UTF-8,避免出现奇奇怪怪的乱码。
- 语种识别(可选):如果数据集混有多种语言,需要先分语种处理。
你可能觉得“压缩空格”这种小动作无所谓?错。我曾经处理一批英文新闻,原文里到处都是连续空格,结果分词器把空白本身也当成了,产生了一堆空字符串。清掉之后,词表大小直接降了15%。
实践:一个完整的预处理流水线
下面是我惯用的Python流水线片段,配合 re 和 nltk:
import re
from nltk.tokenize import word_tokenize
def clean_text(text):
# 去除HTML标签
text = re.sub(r'<.*?>', '', text)
# 去除URL
text = re.sub(r'https?://\S+', ' [URL] ', text)
# 统一小写
text = text.lower()
# 展开常见缩写(简化版)
text = re.sub(r"don't", "do not", text)
text = re.sub(r"can't", "cannot", text)
# 压缩空格
text = re.sub(r'\s+', ' ', text).strip()
return text
raw = "I can't believe <b>that</b> cost $100!!"
cleaned = clean_text(raw)
tokens = word_tokenize(cleaned)
print(tokens) # ['i', 'cannot', 'believe', 'that', 'cost', '$', '100', '!', '!']
你看,这个例子保留了符号 $ 和连续感叹号——如果你的任务不需要情感分析,或许要去掉后者。所以预处理一定要根据你的下游任务灵活调整。
常见陷阱与避坑指南
⚠️ 踩坑预警:
- 过度去除标点:比如对于情感分析,感叹号
!是重要特征,去掉会降低准确率。 - 词干提取滥用:“better” 的词干是 “better”,但词形还原可能给出 “good”,两者差异很大。
- 大小写如果用于命名实体识别,就不能直接全部小写,因为 “Apple” 公司名和 “apple” 水果需要区分。
- 忘记处理编码错误:我曾经遇到过
\xe2\x80\x99这种弯单引号,直接扔给分词器会报 UnicodeDecodeError。
嗯,预处理看起来琐碎,但做扎实了,能省掉后续模型调参时的无数烦恼。记住:垃圾进,垃圾出。你花在清洗上的每一分钟,都会在模型性能上得到回报。