文本预处理实战:中文分词与文本清洗
各位同学,今天我们来聊聊文本预处理。说实话,这是整个NLP流水线里最枯燥、但也是最关键的一环。我在做智能客服系统的时候,遇到过太多因为预处理没做好,导致模型效果一塌糊涂的案例。你想想看,垃圾进垃圾出,这个道理大家都懂。
好,我们直接进入正题。今天要讲的核心内容有四个:中文分词、去停用词、文本清洗、标准化处理。我会结合Jieba这个工具,带大家一步步走通整个流程。
一、中文分词:Jieba的三种模式
中文分词,说白了就是把连续的汉字序列切分成有意义的词语。英文天然有空格分隔,中文可没有。我记得刚入行时,有个同事直接用单字切分去做意图识别,结果准确率惨不忍睹。嗯,这里要注意,分词是中文NLP的基石。
Jieba分词器,我个人习惯用它来做快速原型开发。它支持三种模式,我们一个个来看。
1. 精确模式
精确模式试图把句子最精确地切开,适合文本分析。我一般在做关键词提取、文本分类时用这个模式。
import jieba
text = "我在智能客服系统里做自然语言处理"
seg_list = jieba.cut(text, cut_all=False)
print("精确模式: " + "/".join(seg_list))
# 输出: 我/在/智能/客服/系统/里/做/自然语言/处理
你看,它把"自然语言处理"切成了一个词。为什么?因为Jieba的词典里有这个词。我在项目中遇到过一个问题:很多专业术语Jieba默认词典里没有,比如"意图识别"、"槽位填充"。这时候就需要自定义词典了。
2. 全模式
全模式会把所有可能的词都扫描出来。速度很快,但有冗余。说白了就是"宁可错杀一千,不可放过一个"。
seg_list = jieba.cut(text, cut_all=True)
print("全模式: " + "/".join(seg_list))
# 输出: 我/在/智能/客服/系统/里/做/自然/自然语言/语言/处理
看到没?"自然"和"语言"也被单独切出来了。全模式我一般不用在最终分析里,但做搜索引擎的召回阶段时,它很有用。
3. 搜索引擎模式
这个模式是在精确模式的基础上,对长词再切分。适合搜索引擎的分词场景。
seg_list = jieba.cut_for_search(text)
print("搜索引擎模式: " + "/".join(seg_list))
# 输出: 我/在/智能/客服/系统/里/做/自然/语言/自然语言/处理
你看,"自然语言"被保留,但"自然"和"语言"也被作为子词输出。这样搜索引擎既能匹配长词,也能匹配短词。我曾经在做一个FAQ检索系统时,就用了这个模式,召回率提升了15%。
二、去停用词
停用词,就是那些高频但没啥实际意义的词,比如"的"、"了"、"在"、"是"。你想想看,这些词在文本里到处都是,但对意图识别、情感分析几乎没有贡献。
我一般会维护一个停用词表。网上有现成的,但我会根据业务场景做调整。比如在客服系统里,"您好"、"请问"这些礼貌用语,在意图识别阶段也可以去掉。
stopwords = set()
with open("stopwords.txt", "r", encoding="utf-8") as f:
for line in f:
stopwords.add(line.strip())
words = jieba.cut(text)
filtered = [w for w in words if w not in stopwords and len(w) > 1]
print("去停用词后: " + "/".join(filtered))
这里我加了一个条件 len(w) > 1,过滤掉单字词。为什么?因为在客服场景里,单字词大多是语气词或标点符号,留着反而增加噪声。
三、文本清洗:正则表达式实战
文本清洗,说白了就是把脏数据洗干净。用户输入的东西,什么乱七八糟的都有。我记得有一次,用户发来一条消息:"我 要 退 款!!!!!!",里面全是空格和感叹号。如果不做清洗,分词结果会非常难看。
正则表达式是文本清洗的利器。我常用的几个模式:
| 清洗目标 | 正则表达式 | 说明 |
|---|---|---|
| 去除URL | r'http[s]?://\S+' | 用户经常发链接 |
| 去除@用户 | r'@\w+' | 微博、论坛数据常见 |
| 去除多余空格 | r'\s+' | 替换为单个空格 |
| 去除特殊符号 | r'[^\u4e00-\u9fa5a-zA-Z0-9]' | 只保留中文、英文、数字 |
import re
def clean_text(text):
# 去除URL
text = re.sub(r'http[s]?://\S+', '', text)
# 去除@用户
text = re.sub(r'@\w+', '', text)
# 去除多余空格
text = re.sub(r'\s+', ' ', text)
# 只保留中文、英文、数字
text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9]', '', text)
return text.strip()
raw_text = "你好 @小明 我要退款!!!详情请看 https://example.com"
cleaned = clean_text(raw_text)
print(cleaned) # 输出: 你好 小明 我要退款详情请看
四、标准化处理
标准化,就是把文本统一成标准格式。比如全角转半角、繁体转简体、大小写统一。这些看似小事,但影响很大。
我举个例子。用户输入"hello"(全角),和"hello"(半角),在模型看来是两个不同的词。如果不做标准化,模型会认为这是两个不同的意图。
def normalize_text(text):
# 全角转半角
result = []
for char in text:
code = ord(char)
if code == 0x3000:
code = 0x0020
elif 0xFF01 <= code <= 0xFF5E:
code -= 0xFEE0
result.append(chr(code))
text = ''.join(result)
# 繁体转简体(需要zhconv库)
# from zhconv import convert
# text = convert(text, 'zh-cn')
# 统一小写
text = text.lower()
return text
test = "hello,世界!"
print(normalize_text(test)) # 输出: hello,世界!
标准化这一步,我建议放在所有预处理的最前面。先标准化,再清洗,最后分词。这个顺序是我在实践中摸索出来的,效果最好。
五、完整预处理流水线
好,我们把上面所有步骤串起来,形成一个完整的预处理流水线。
def preprocess_pipeline(text):
# 1. 标准化
text = normalize_text(text)
# 2. 文本清洗
text = clean_text(text)
# 3. 分词
words = jieba.cut(text, cut_all=False)
# 4. 去停用词
stopwords = load_stopwords()
words = [w for w in words if w not in stopwords and len(w) > 1]
return " ".join(words)
# 测试
sample = "您好 @客服 我要退款!!!!!!详情请看 https://example.com"
result = preprocess_pipeline(sample)
print(result) # 输出: 退款 详情 请看
你看,经过预处理后,"我要退款"变成了"退款"。干净、简洁,模型处理起来也轻松多了。
最后说一句,预处理没有银弹。不同的业务场景,预处理策略可能完全不同。我建议你在实际项目中,先拿100条数据做测试,看看预处理效果,再逐步调整。嗯,今天就到这里,下节课我们讲特征工程。