Appearance
章节1:文本处理基础
学习目标
- 掌握 jieba 分词库的三种模式(精确、全、搜索引擎)及其适用场景
- 理解词性标注与命名实体识别的原理与实现
- 能够搭建完整的文本清洗与预处理流水线
- 掌握 TF‑IDF 与 TextRank 两种关键词提取算法
- 熟练计算文本的余弦相似度与 Jaccard 相似系数
1.1 中文分词技术
1.1.1 为什么中文需要分词
中文句子中词语之间没有天然空格分隔,分词是大多数 NLP 任务的第一步。
1.1.2 jieba 的三种模式
python
import jieba
text = "我在北京大学学习自然语言处理"
# 精确模式(最常用,无冗余)
seg_exact = jieba.lcut(text, cut_all=False)
print("精确模式:", seg_exact)
# 输出: ['我', '在', '北京大学', '学习', '自然语言处理']
# 全模式(输出所有可能的词,有冗余)
seg_all = jieba.lcut(text, cut_all=True)
print("全模式:", seg_all)
# 输出: ['我', '在', '北京', '北京大学', '大学', '学习', '自然', '自然语言处理', '语言', '处理']
# 搜索引擎模式(精确模式基础上再次切分长词)
seg_search = jieba.lcut_for_search(text)
print("搜索引擎模式:", seg_search)
# 输出: ['我', '在', '北京', '大学', '北京大学', '学习', '自然', '语言', '处理', '自然语言处理']模式对比:
| 模式 | 特点 | 常用场景 |
|---|---|---|
| 精确模式 | 不冗余、语义完整 | 文本分析、特征提取 |
| 全模式 | 召回高、有冗余 | 词典构建、召回候选 |
| 搜索引擎模式 | 兼顾精度与召回 | 搜索引擎索引 |
1.1.3 自定义词典
python
jieba.load_userdict("user_dict.txt")
# 每行格式: 词语 词频 词性
# 例: 自然语言处理 5 n1.2 词性标注与命名实体识别
1.2.1 词性标注
python
import jieba.posseg as pseg
words = pseg.lcut("我拿着华为手机在北京天安门拍照")
for word, flag in words:
print(f"{word}/{flag}", end=" ")
# 输出: 我/r 拿/v 着/uz 华为/nz 手机/n 在/p 北京/ns 天安门/ns 拍照/v常见词性标签:n(名词)、v(动词)、a(形容词)、ns(地名)、nr(人名)、nt(机构名)
1.2.2 命名实体识别(NER)
使用 jieba 内置 + HanLP 示例:
python
# jieba 自带人名地名识别
text = "李华毕业于清华大学,现在在北京工作"
words = pseg.lcut(text)
for w, f in words:
if f in ("nr", "ns", "nt"):
print(f"实体: {w} 类型: {f}")
# nr→人名, ns→地名, nt→机构名深入: 工业级 NER 通常使用 BiLSTM+CRF 或 BERT+CRF 模型(见章节4)。
1.3 文本清洗与预处理流水线
1.3.1 标准流水线
原始文本 → 去噪 → 标准化 → 分词 → 去停用词 → 特征化1.3.2 代码实现
python
import re
import jieba
class TextPreprocessor:
def __init__(self, stopwords_path=None):
self.stopwords = set()
if stopwords_path:
with open(stopwords_path, "r", encoding="utf-8") as f:
self.stopwords = {line.strip() for line in f}
# 内置简单停用词
self.stopwords.update({"的", "了", "在", "是", "我", "有", "和", "就",
"不", "人", "都", "一", "一个", "上", "也", "很",
"到", "说", "要", "去", "你", "会", "着", "没有"})
def denoise(self, text: str) -> str:
"""去噪:移除 HTML 标签、URL、特殊符号"""
text = re.sub(r"<[^>]+>", "", text) # HTML
text = re.sub(r"http\S+", "", text) # URL
text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9\s]", "", text) # 保留中文、英文、数字
return text
def normalize(self, text: str) -> str:
"""标准化:统一大小写、全角转半角"""
text = text.lower()
result = []
for ch in text:
code = ord(ch)
if 0xFF01 <= code <= 0xFF5E: # 全角转半角
result.append(chr(code - 0xFEE0))
elif code == 0x3000:
result.append(" ")
else:
result.append(ch)
return "".join(result)
def segment(self, text: str) -> list:
"""分词 + 去停用词"""
words = jieba.lcut(text)
return [w.strip() for w in words if w.strip() and w.strip() not in self.stopwords
and len(w.strip()) > 1] # 过滤单字
def pipeline(self, text: str) -> list:
"""完整预处理流水线"""
text = self.denoise(text)
text = self.normalize(text)
words = self.segment(text)
return words
# 使用示例
pp = TextPreprocessor()
raw = '<p>我在<em>北京</em>学习人工智能!访问 https://example.com 了解更多。</p>'
result = pp.pipeline(raw)
print(result) # ['北京', '学习', '人工智能', '访问', '了解']1.4 关键词提取
1.4.1 TF‑IDF(词频-逆文档频率)
核心思想: 一个词在当前文档中出现次数多(TF高),但在整个语料中出现次数少(IDF高),则它很有代表性。
python
from sklearn.feature_extraction.text import TfidfVectorizer
corpus = [
"我爱北京天安门",
"天安门上太阳升",
"北京欢迎你"
]
vectorizer = TfidfVectorizer(tokenizer=jieba.lcut)
tfidf = vectorizer.fit_transform(corpus)
# 查看每个词的 TF-IDF 值
feature_names = vectorizer.get_feature_names_out()
for i, doc in enumerate(corpus):
print(f"\n文档{i+1} 关键词:")
row = tfidf[i].toarray().flatten()
idxs = row.argsort()[-3:][::-1] # 取前3
for idx in idxs:
print(f" {feature_names[idx]}: {row[idx]:.4f}")1.4.2 TextRank(基于图排序)
借鉴 PageRank 思想,将文档中的词看作节点,共现关系构成边,迭代计算权重。
python
import jieba.analyse
text = "自然语言处理是人工智能的一个重要分支,它研究如何让计算机理解人类语言。"
# TextRank 提取关键词
keywords = jieba.analyse.textrank(text, topK=5, withWeight=True)
for word, weight in keywords:
print(f"{word}: {weight:.4f}")
# 输出示例:
# 自然语言处理: 1.0
# 人工智能: 0.8...
# 计算机: 0.6...
# 人类语言: 0.5...
# 分支: 0.3...
# TF-IDF 提取(使用 jieba 内置)
keywords_tfidf = jieba.analyse.extract_tags(text, topK=5, withWeight=True)TF‑IDF vs TextRank:
| 方法 | 优点 | 缺点 |
|---|---|---|
| TF‑IDF | 简单高效、可解释性强 | 依赖语料库统计信息 |
| TextRank | 无需外部语料、全局信息 | 计算量大、对短文本效果不稳定 |
1.5 文本相似度计算
1.5.1 余弦相似度(Cosine Similarity)
python
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
doc1 = "北京是中国的首都"
doc2 = "北京是中国首都"
vectorizer = TfidfVectorizer(tokenizer=jieba.lcut)
tfidf_matrix = vectorizer.fit_transform([doc1, doc2])
sim = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2])
print(f"余弦相似度: {sim[0][0]:.4f}")
# 输出接近 1.0 表示非常相似1.5.2 Jaccard 相似系数
python
def jaccard_similarity(text1: str, text2: str) -> float:
set1 = set(jieba.lcut(text1))
set2 = set(jieba.lcut(text2))
intersection = set1 & set2
union = set1 | set2
if not union:
return 0.0
return len(intersection) / len(union)
print(f"Jaccard 相似度: {jaccard_similarity(doc1, doc2):.4f}")两种方法对比:
| 指标 | 考虑词频 | 数值范围 | 适用场景 |
|---|---|---|---|
| 余弦相似度 | ✅ 是 | [0, 1] | 长文本、向量空间模型 |
| Jaccard | ❌ 否 | [0, 1] | 短文本、标签/集合比较 |
小结
- jieba 分词提供三种模式:精确(默认)、全模式(高召回)、搜索引擎(兼顾)。
- 词性标注与 NER 为后续任务提供词法层面的结构化信息。
- 预处理流水线包括去噪、标准化、分词、去停用词四个核心步骤。
- TF‑IDF 与 TextRank 是两种互补的关键词提取方法。
- 余弦相似度考虑词频分布,Jaccard 只考虑集合是否出现,各有所长。
练习
- 用 jieba 精确模式对"南京市长江大桥"进行分词,观察结果并尝试添加自定义词典纠偏。
- 编写一个函数,从一篇新闻中提取所有人名和地名。
- 收集10条微博文本,构建预处理流水线,然后用 TF‑IDF 提取每条文本的前3个关键词。
- 对两篇相似新闻分别计算余弦相似度和 Jaccard 相似度,比较结果差异并分析原因。
- 挑战题: 用 TextRank 算法(不借助 jieba.analyse)手动实现关键词提取,核心步骤包括构建共现图、随机游走迭代至收敛。