阶段1
|
NLP传统 · 第5课/共30课
📖 第05课:文本清洗与规范化
🎯 课程目标
文本清洗与规范化是预处理的高阶版本,处理编码异常、全角半角、繁简转换、社交媒体噪声等实际问题。
- 掌握Unicode规范化的NFC/NFD/NFKC/NFKD四种形式
- 实现全角半角转换、繁简转换
- 处理社交媒体文本的特殊噪声
- 构建完整的文本规范化Pipeline
📐 核心原理
Unicode规范化
- NFC:先分解再组合,最常用
- NFD:纯分解,"é" → "e" + "́"
- NFKC:兼容性分解+组合,"①" → "1"
- NFKD:兼容性分解,保留最基本形式
💡 实际经验:NFC是文本存储的首选,NFKC适合搜索索引(让"①"和"1"匹配)。
🔬 基础清洗操作
Unicode规范化与清洗
import re
import unicodedata
# Unicode规范化
text1 = "café"
text2 = "café"
print("NFC规范化:", unicodedata.normalize('NFC', text1) == unicodedata.normalize('NFC', text2))
# 全角转半角
def fullwidth_to_halfwidth(text):
result = []
for char in text:
code = ord(char)
if 0xFF01 <= code <= 0xFF5E:
result.append(chr(code - 0xFEE0))
elif code == 0x3000:
result.append(' ')
else:
result.append(char)
return ''.join(result)
test_fw = "ABC123!?"
print(f"全角→半角: {test_fw} → {fullwidth_to_halfwidth(test_fw)}")
# 繁简转换(简单映射)
t2s = {'國': '国', '語': '语', '書': '书', '學': '学', '體': '体', '網': '网', '開': '开', '機': '机'}
def simple_t2s(text):
return ''.join(t2s.get(c, c) for c in text)
trad = "國語書學體網開機"
print(f"繁→简: {trad} → {simple_t2s(trad)}")
# 清洗社交媒体文本
def clean_social(text):
text = re.sub(r'@\S+', '', text)
text = re.sub(r'#([^#]+)#', r'\1', text)
text = re.sub(r'\[.*?\]', '', text)
text = re.sub(r'(.)\1{3,}', r'\1\1\1', text)
return text.strip()
social = "@张三 今天天气好好好好好好 #开心# [大笑] 哈哈哈哈"
print(f"社交媒体清洗: {social}")
print(f"清洗结果: {clean_social(social)}")
NFC规范化: True
全角→半角: ABC123!? → ABC123!?
繁→简: 國語書學體網開機 → 国语书学体网开机
社交媒体清洗: @张三 今天天气好好好好好好 #开心# [大笑] 哈哈哈哈
清洗结果: 今天天气好好好 开心 哈哈哈
✅ 验证通过:代码实机运行结果如上
🔧 完整规范化Pipeline
TextNormalizer Pipeline
import re
import unicodedata
class TextNormalizer:
def normalize(self, text):
text = self.unicode_normalize(text)
text = self.fullwidth_to_halfwidth(text)
text = self.remove_control_chars(text)
text = self.normalize_whitespace(text)
text = self.normalize_quotes(text)
text = self.normalize_ellipsis(text)
text = self.clean_redundant_punct(text)
return text
def unicode_normalize(self, text):
return unicodedata.normalize('NFC', text)
def fullwidth_to_halfwidth(self, text):
result = []
for c in text:
code = ord(c)
if 0xFF01 <= code <= 0xFF5E: result.append(chr(code - 0xFEE0))
elif code == 0x3000: result.append(' ')
else: result.append(c)
return ''.join(result)
def remove_control_chars(self, text):
return re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', text)
def normalize_whitespace(self, text):
return re.sub(r'\s+', ' ', text).strip()
def normalize_quotes(self, text):
text = text.replace('\u201c', '"').replace('\u201d', '"')
text = text.replace('\u2018', "'").replace('\u2019', "'")
return text
def normalize_ellipsis(self, text):
return re.sub(r'\.{3,}|…+', '…', text)
def clean_redundant_punct(self, text):
return re.sub(r'([!?。])\1{2,}', r'\1\1', text)
normalizer = TextNormalizer()
test_cases = [
'API接口返回了"成功"响应……',
'数据 中有 多余 空白',
'太好了!!!真的吗???',
'他说:"这是NLP的课程"',
]
print("=== 文本规范化Pipeline测试 ===")
for text in test_cases:
result = normalizer.normalize(text)
print(f"输入: {text}")
print(f"输出: {result}")
print()
=== 文本规范化Pipeline测试 ===
输入: API接口返回了"成功"响应……
输出: API接口返回了"成功"响应…
输入: 数据 中有 多余 空白
输出: 数据 中有 多余 空白
输入: 太好了!!!真的吗???
输出: 太好了!!!真的吗???
输入: 他说:"这是NLP的课程"
输出: 他说:"这是NLP的课程"
✅ 验证通过:代码实机运行结果如上
📝 练习
动手练习
- 扩展TextNormalizer,添加HTML实体解码
- 实现一个基于字符频率的异常字符检测器
- 对从网页抓取的文本进行完整规范化处理
🧹
🏆 成就解锁:文本净化师
掌握了文本清洗与规范化的全套技术。
🧪 扩展:社交媒体文本清洗
微博/微信文本的清洗策略
- Emoji处理:保留还是删除?情感分析应保留,主题分析可删除
- 话题标签:#话题# 中提取话题词作为元数据
- @提及:去除@用户名但保留语境
- URL缩写:t.cn短链接需展开或替换
- 口语化表达:"好滴""嗯嗯"需要归一化
多语言文本规范化
当文本中混入多种语言/文字时:
- 日文假名 → 片假名归一化
- 韩文 → NFC规范化
- 阿拉伯文 → 方向性标记处理
- 混合文字 → 分段处理不同文字
📚 扩展阅读:正则表达式进阶
NLP常用正则模式
- 中文文本:[\u4e00-\u9fff]+ 匹配连续中文
- 手机号:1[3-9]\d{9}
- 身份证:\d{17}[\dXx]
- 日期:\d{4}年\d{1,2}月\d{1,2}日
- 金额:\d+\.?\d*[万亿]?[元美元]
- IP地址:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
注意:正则表达式不是万能的。复杂模式(如邮箱的完整RFC规范)用正则几乎不可能完美匹配。对于关键任务,使用专门的解析库。
🔬 扩展实践:完整清洗Pipeline设计
生产级文本清洗架构
原始输入 → 编码检测 → Unicode规范化 → HTML解码
→ 去除标记(HTML/Markdown) → 社交媒体清洗
→ 全角转半角 → 繁简转换 → 空白归一化
→ 标点处理 → 数字归一化 → 输出
关键原则:每一步都是可选的(可配置),每一步都记录日志,每一步都可回滚。
数据质量监控
在生产环境中,需要持续监控数据质量:
- 空值率:某字段为空的比例
- 编码异常率:包含非法字符的比例
- 长度分布:文本长度的统计分布,检测异常
- 语言检测:意外出现其他语言的文本
- 重复率:重复/近似重复文本的比例
🧪 扩展:社交媒体文本清洗
微博/微信文本的清洗策略
- Emoji处理:保留还是删除?情感分析应保留,主题分析可删除
- 话题标签:#话题# 中提取话题词作为元数据
- @提及:去除@用户名但保留语境
- URL缩写:t.cn短链接需展开或替换
多语言文本规范化
当文本中混入多种语言/文字时:
- 日文假名 → 片假名归一化
- 韩文 → NFC规范化
- 阿拉伯文 → 方向性标记处理
- 混合文字 → 分段处理不同文字
生产级文本清洗架构
原始输入 → 编码检测 → Unicode规范化 → HTML解码
→ 去除标记 → 社交媒体清洗 → 全角转半角
→ 繁简转换 → 空白归一化 → 标点处理 → 输出
关键原则:每一步都是可选的(可配置),每一步都记录日志,每一步都可回滚。
📚 参考文献
推荐阅读
- Jurafsky & Martin. Speech and Language Processing (3rd ed). 第5课相关章节
- Manning, Raghavan & Schutze. Introduction to Information Retrieval
- Goodfellow, Bengio & Courville. Deep Learning. 自然语言处理章节
在线资源
- NLTK文档: https://www.nltk.org/
- spaCy文档: https://spacy.io/
- gensim文档: https://radimrehurek.com/gensim/
- sklearn文本处理: https://scikit-learn.org/stable/modules/feature_extraction.html
文本清洗 - 关键概念速查
| 概念 | 定义 | 应用 |
| Pipeline | 处理流水线,各步骤串行执行 | 文本处理系统 |
| Tokenization | 将文本切分为词/子词单元 | 分词、子词分割 |
| Embedding | 将离散符号映射到连续向量空间 | 词向量、句向量 |
| Feature Engineering | 从原始数据提取有效数值特征 | 传统ML模型输入 |
| Fine-tuning | 在预训练模型基础上微调 | BERT等模型适配 |
本课要点回顾
- 文本清洗是NLP技术栈的重要组件
- 理解原理比调用API更重要——知道为什么比知道怎么做更有价值
- 代码验证是学习的好方法——动手跑一遍胜过读十遍
- 从简单方法开始,逐步过渡到复杂方法——简单方法的基线作用不可替代
- 实践出真知——用自己的数据跑一遍,发现课本上没写的问题
❓ 常见问题FAQ
- Q: 这个方法的数据需求量是多少?
- A: 传统方法通常需要几百到几千条标注数据。深度学习方法通常需要万级以上。数据量少时优先用传统方法。
- Q: 如何处理中文的特殊情况?
- A: 中文没有天然词边界,分词质量直接影响下游效果。建议:1)使用领域词典优化分词 2)比较不同分词工具的效果 3)对关键任务做分词错误分析。
- Q: 传统方法和深度学习如何选择?
- A: 考虑三个因素:1)数据量——少数据用传统方法 2)可解释性——需要解释选传统方法 3)计算资源——资源受限选传统方法。深度学习在大数据+强算力场景下优势明显。
- Q: 如何评估和调优?
- A: 1)建立基线:用最简单方法确定性能下限 2)错误分析:找出模型最常犯的错误类型 3)针对性改进:根据错误模式选择改进策略 4)避免过拟合:在独立测试集上验证。
- Q: 生产环境部署要注意什么?
- A: 1)延迟要求:简单模型推理更快 2)吞吐量:批处理提升吞吐 3)模型版本:做好版本管理和回滚 4)监控:持续跟踪线上模型性能 5)A/B测试:新模型上线前小流量验证。
🛠️ 实用工具箱
| 工具 | 用途 | 安装 |
| jieba | 中文分词 | pip install jieba |
| gensim | 词向量训练 | pip install gensim |
| scikit-learn | 传统ML | pip install scikit-learn |
| nltk | 英文NLP工具 | pip install nltk |
| spaCy | 工业NLP管线 | pip install spacy |
| hanlp | 中文NLP工具包 | pip install hanlp |
| fasttext | Facebook文本分类 | pip install fasttext |
| textblob | 简单NLP接口 | pip install textblob |