Skip to content

章节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 n

1.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]短文本、标签/集合比较

小结

  1. jieba 分词提供三种模式:精确(默认)、全模式(高召回)、搜索引擎(兼顾)。
  2. 词性标注NER 为后续任务提供词法层面的结构化信息。
  3. 预处理流水线包括去噪、标准化、分词、去停用词四个核心步骤。
  4. TF‑IDFTextRank 是两种互补的关键词提取方法。
  5. 余弦相似度考虑词频分布,Jaccard 只考虑集合是否出现,各有所长。

练习

  1. 用 jieba 精确模式对"南京市长江大桥"进行分词,观察结果并尝试添加自定义词典纠偏。
  2. 编写一个函数,从一篇新闻中提取所有人名和地名。
  3. 收集10条微博文本,构建预处理流水线,然后用 TF‑IDF 提取每条文本的前3个关键词。
  4. 对两篇相似新闻分别计算余弦相似度和 Jaccard 相似度,比较结果差异并分析原因。
  5. 挑战题: 用 TextRank 算法(不借助 jieba.analyse)手动实现关键词提取,核心步骤包括构建共现图、随机游走迭代至收敛。

Python 学习资料