Python大数据实战(六):中文文本分类从词云到深度学习——NLP全流程实战指南

Python大数据实战(六):中文文本分类从词云到深度学习------NLP全流程实战指南


文章目录


前言

"如何让机器读懂中文?"

这是自然语言处理(NLP)领域最核心的问题之一。与英文不同,中文没有天然的空格分隔,词语之间紧密相连,加上一词多义、上下文依赖等特性,使得中文文本处理比英文更具挑战性。

想象一个场景:你是一家新闻平台的数据工程师,每天有上万篇文章涌入后台。老板要求你自动将这些文章分类------科技、体育、娱乐、财经......人工分类显然不现实。这时候,文本自动分类就成了刚需。

本文将从最基础的词云可视化 讲起,逐步深入到TF-IDF关键词提取词袋模型LDA主题建模 ,再到朴素贝叶斯 机器学习分类,最后用CNN和LSTM深度学习模型完成文本分类任务。全文超过15000字,配合大量可运行代码,带你走通中文NLP的完整技术栈。


一、项目全景概览

1.1 项目目标

构建一个完整的中文文本分类系统,实现从原始文本到分类结果的端到端流程:

  • 输入:中文文本语料(新闻、评论、文章等)
  • 输出:每条文本的类别标签(如科技/体育/娱乐等)
  • 技术栈:jieba分词 → TF-IDF/词袋特征 → 朴素贝叶斯/CNN/LSTM分类

1.2 前置知识要求

知识领域 具体要求 重要程度
Python基础 熟悉基本语法、列表/字典操作 ⭐⭐⭐⭐⭐
NLP基础概念 了解分词、停用词、词向量 ⭐⭐⭐⭐
机器学习 了解分类算法基本原理 ⭐⭐⭐
深度学习 了解CNN/LSTM基本结构 ⭐⭐⭐
数学基础 概率论、线性代数基础 ⭐⭐

1.3 技术路线架构图

#mermaid-svg-JpPUIXG8kDHKbx6R{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JpPUIXG8kDHKbx6R .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JpPUIXG8kDHKbx6R .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JpPUIXG8kDHKbx6R .error-icon{fill:#552222;}#mermaid-svg-JpPUIXG8kDHKbx6R .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JpPUIXG8kDHKbx6R .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JpPUIXG8kDHKbx6R .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JpPUIXG8kDHKbx6R .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JpPUIXG8kDHKbx6R .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JpPUIXG8kDHKbx6R .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JpPUIXG8kDHKbx6R .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JpPUIXG8kDHKbx6R .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JpPUIXG8kDHKbx6R .marker.cross{stroke:#333333;}#mermaid-svg-JpPUIXG8kDHKbx6R svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JpPUIXG8kDHKbx6R p{margin:0;}#mermaid-svg-JpPUIXG8kDHKbx6R .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JpPUIXG8kDHKbx6R .cluster-label text{fill:#333;}#mermaid-svg-JpPUIXG8kDHKbx6R .cluster-label span{color:#333;}#mermaid-svg-JpPUIXG8kDHKbx6R .cluster-label span p{background-color:transparent;}#mermaid-svg-JpPUIXG8kDHKbx6R .label text,#mermaid-svg-JpPUIXG8kDHKbx6R span{fill:#333;color:#333;}#mermaid-svg-JpPUIXG8kDHKbx6R .node rect,#mermaid-svg-JpPUIXG8kDHKbx6R .node circle,#mermaid-svg-JpPUIXG8kDHKbx6R .node ellipse,#mermaid-svg-JpPUIXG8kDHKbx6R .node polygon,#mermaid-svg-JpPUIXG8kDHKbx6R .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JpPUIXG8kDHKbx6R .rough-node .label text,#mermaid-svg-JpPUIXG8kDHKbx6R .node .label text,#mermaid-svg-JpPUIXG8kDHKbx6R .image-shape .label,#mermaid-svg-JpPUIXG8kDHKbx6R .icon-shape .label{text-anchor:middle;}#mermaid-svg-JpPUIXG8kDHKbx6R .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JpPUIXG8kDHKbx6R .rough-node .label,#mermaid-svg-JpPUIXG8kDHKbx6R .node .label,#mermaid-svg-JpPUIXG8kDHKbx6R .image-shape .label,#mermaid-svg-JpPUIXG8kDHKbx6R .icon-shape .label{text-align:center;}#mermaid-svg-JpPUIXG8kDHKbx6R .node.clickable{cursor:pointer;}#mermaid-svg-JpPUIXG8kDHKbx6R .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JpPUIXG8kDHKbx6R .arrowheadPath{fill:#333333;}#mermaid-svg-JpPUIXG8kDHKbx6R .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JpPUIXG8kDHKbx6R .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JpPUIXG8kDHKbx6R .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JpPUIXG8kDHKbx6R .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JpPUIXG8kDHKbx6R .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JpPUIXG8kDHKbx6R .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JpPUIXG8kDHKbx6R .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JpPUIXG8kDHKbx6R .cluster text{fill:#333;}#mermaid-svg-JpPUIXG8kDHKbx6R .cluster span{color:#333;}#mermaid-svg-JpPUIXG8kDHKbx6R div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JpPUIXG8kDHKbx6R .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JpPUIXG8kDHKbx6R rect.text{fill:none;stroke-width:0;}#mermaid-svg-JpPUIXG8kDHKbx6R .icon-shape,#mermaid-svg-JpPUIXG8kDHKbx6R .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JpPUIXG8kDHKbx6R .icon-shape p,#mermaid-svg-JpPUIXG8kDHKbx6R .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JpPUIXG8kDHKbx6R .icon-shape rect,#mermaid-svg-JpPUIXG8kDHKbx6R .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JpPUIXG8kDHKbx6R .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JpPUIXG8kDHKbx6R .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JpPUIXG8kDHKbx6R :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始中文文本
数据预处理
jieba分词
去除停用词
去除特殊字符
文本特征工程
词云可视化
TF-IDF特征提取
TextRank关键词
词袋模型 BOW
LDA主题建模
传统机器学习分类
朴素贝叶斯 MultinomialNB
CountVectorizer + 分类器
深度学习分类
CNN文本分类
LSTM/GRU文本分类
模型评估与对比
最优模型部署

1.4 中文NLP vs 英文NLP

对比维度 中文NLP 英文NLP
分词难度 ,词语间无空格分隔 低,天然空格分隔
分词工具 jieba、HanLP、pkuseg NLTK、spaCy
歧义处理 一词多义、边界模糊 相对清晰
停用词库 需专门的中文停用词表 NLTK内置
词向量 需大规模中文语料训练 预训练资源丰富
语法结构 灵活,缺乏形态变化 规则性强

💡 关键洞察:中文NLP的"第一关"就是分词。分词的准确性直接影响后续所有任务的效果。"南京市长江大桥"可以分词为"南京市/长江大桥"或"南京/市长/江大桥"------同样的字序列,不同的分词结果意味着完全不同的语义理解。


二、词云可视化:让文本"看得见"

2.1 什么是词云

词云(Word Cloud) 是对文本中出现频率较高的"关键词"予以视觉上的突出展示,形成"关键词云层"或"关键词渲染"。通过词云,浏览者只需一眼扫过就能领略文本的主旨------出现频率越高的词,在词云中显示得越大。

词云是文本分析的"第一眼"工具,它能快速回答:

  • 这篇文章主要在讲什么?
  • 哪些关键词被反复提及?
  • 不同文章的主题差异在哪里?

2.2 中文分词:jieba入门

在生成词云之前,必须先对中文文本进行分词。jieba是Python中最优秀的中文分词第三方库。

安装

bash 复制代码
# 安装jieba分词库
pip install jieba

基础分词示例

python 复制代码
import jieba

# 待分词的文本
text = "我爱北京天安门,天安门上太阳升"

# 精确模式分词(最常用)
words = jieba.lcut(text)
print("精确模式:", words)
# 输出: ['我', '爱', '北京', '天安门', ',', '天安门', '上', '太阳', '升']

# 全模式分词(把所有可能的词都分出来)
words_full = jieba.lcut(text, cut_all=True)
print("全模式:", words_full)
# 输出: ['我', '爱', '北京', '天安门', '天安', '门上', '太阳', '升']

# 搜索引擎模式(在精确模式基础上再切分长词)
words_search = jieba.lcut_for_search(text)
print("搜索引擎模式:", words_search)
# 输出: ['我', '爱', '北京', '天安', '天安门', '天安门', '上', '太阳', '升']

处理分词结果

分词完成后,通常需要进行以下清洗步骤:

python 复制代码
import jieba
import re

def clean_text(raw_text):
    """
    文本清洗函数:分词 + 去除特殊字符 + 去除停用词
    
    参数:
        raw_text: 原始文本字符串
    
    返回:
        清洗后的词语列表
    """
    # 1. 去除特殊字符和标点符号
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9]', ' ', raw_text)
    
    # 2. jieba分词
    words = jieba.lcut(text)
    
    # 3. 加载停用词表(需自行准备中文停用词文件)
    stopwords = set()
    with open('stopwords.txt', 'r', encoding='utf-8') as f:
        for line in f:
            stopwords.add(line.strip())
    
    # 4. 过滤停用词和单字词
    cleaned = [w for w in words if w not in stopwords and len(w) > 1]
    
    return cleaned

# 使用示例
text = "今天天气真好,我们一起去公园散步吧!"
result = clean_text(text)
print(result)
# 输出: ['今天', '天气', '真好', '我们', '一起', '公园', '散步']

2.3 生成词云

安装依赖

bash 复制代码
# 安装词云生成库
pip install wordcloud

# 安装图片处理库(用于自定义背景图)
pip install imageio
pip install matplotlib

基础词云生成

python 复制代码
import jieba
from wordcloud import WordCloud
import matplotlib.pyplot as plt

# 示例文本
text = """
自然语言处理是人工智能领域的重要分支。自然语言处理技术
包括分词、词性标注、命名实体识别、句法分析、语义理解等。
近年来,深度学习技术在自然语言处理领域取得了巨大成功,
BERT、GPT等预训练模型大幅提升了各项NLP任务的性能。
"""

# 分词
words = jieba.lcut(text)
word_str = ' '.join(words)  # wordcloud需要空格分隔的字符串

# 生成词云
wc = WordCloud(
    font_path='simhei.ttf',      # 中文字体路径(必须指定!)
    width=800,                    # 画布宽度
    height=600,                   # 画布高度
    background_color='white',     # 背景颜色
    max_words=100,                # 最多显示词数
    max_font_size=120,            # 最大字号
    random_state=42               # 随机种子,保证可复现
).generate(word_str)

# 显示词云
plt.figure(figsize=(12, 8))
plt.imshow(wc, interpolation='bilinear')
plt.axis('off')
plt.title('中文文本词云', fontsize=20)
plt.show()

# 保存词云图片
wc.to_file('wordcloud_output.png')

自定义背景图词云

python 复制代码
import imageio
import numpy as np
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt

# 读取背景图片(建议使用黑白轮廓图)
mask_image = imageio.imread('background.png')

# 生成带背景图的词云
wc = WordCloud(
    font_path='simhei.ttf',
    mask=mask_image,              # 设置背景蒙版
    background_color='white',
    max_words=200,
    max_font_size=100,
    random_state=42
).generate(word_str)

# 使用背景图的颜色方案
image_colors = ImageColorGenerator(mask_image)

plt.figure(figsize=(12, 8))
plt.imshow(wc.recolor(color_func=image_colors), interpolation='bilinear')
plt.axis('off')
plt.show()

⚠️ 常见错误 :生成中文词云时如果不指定 font_path 参数,中文会显示为方框(□□□)。务必指定一个支持中文的字体文件路径。


三、文本特征工程:从文字到数字

3.1 TF-IDF关键词提取

算法原理

TF-IDF(Term Frequency-Inverse Document Frequency) 是信息检索和文本挖掘中最经典的加权技术。

其核心思想非常直观:如果一个词在一篇文章中出现频率高(TF高),但在其他文章中很少出现(IDF高),那么这个词就具有很好的类别区分能力。

指标 全称 计算公式 含义
TF Term Frequency 词在文档中出现次数 / 文档总词数 词在单文档中的频率
IDF Inverse Document Frequency log(总文档数 / 包含该词的文档数) 词的普遍重要性度量
TF-IDF TF × IDF TF × IDF 综合权重

jieba实现TF-IDF

python 复制代码
import jieba.analyse

# 待提取关键词的文本
text = """
自然语言处理是人工智能和语言学领域的分支学科。
它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。
自然语言处理包括分词、词性标注、命名实体识别、句法分析、语义理解、
机器翻译、文本分类、情感分析等多个子任务。
"""

# 基于TF-IDF提取关键词
keywords_tfidf = jieba.analyse.extract_tags(
    text,
    topK=10,           # 返回权重最大的10个关键词
    withWeight=True,   # 一并返回权重值
    allowPOS=()        # 不限制词性,空元组表示全部
)

print("TF-IDF关键词提取结果:")
for word, weight in keywords_tfidf:
    print(f"  {word}: {weight:.4f}")

# 输出示例:
# TF-IDF关键词提取结果:
#   自然语言: 0.8523
#   处理: 0.6234
#   分词: 0.4512
#   分析: 0.3891
#   ...

参数说明

参数 类型 默认值 说明
sentence str - 待提取的文本
topK int 20 返回关键词数量
withWeight bool False 是否返回权重值
allowPOS tuple () 允许的词性列表,空表示不筛选

3.2 TextRank关键词提取

算法原理

TextRank 是一种基于图的排序算法,灵感来源于Google的PageRank。其基本思想是:

  1. 将文本中的词看作图中的节点
  2. 以固定窗口大小(默认5)内词的共现关系作为边
  3. 构建无向有权图
  4. 迭代计算每个节点的PageRank值
  5. 排名最高的词即为关键词

#mermaid-svg-kGx91VAqYqfANYKO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kGx91VAqYqfANYKO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kGx91VAqYqfANYKO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kGx91VAqYqfANYKO .error-icon{fill:#552222;}#mermaid-svg-kGx91VAqYqfANYKO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kGx91VAqYqfANYKO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kGx91VAqYqfANYKO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kGx91VAqYqfANYKO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kGx91VAqYqfANYKO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kGx91VAqYqfANYKO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kGx91VAqYqfANYKO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kGx91VAqYqfANYKO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kGx91VAqYqfANYKO .marker.cross{stroke:#333333;}#mermaid-svg-kGx91VAqYqfANYKO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kGx91VAqYqfANYKO p{margin:0;}#mermaid-svg-kGx91VAqYqfANYKO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kGx91VAqYqfANYKO .cluster-label text{fill:#333;}#mermaid-svg-kGx91VAqYqfANYKO .cluster-label span{color:#333;}#mermaid-svg-kGx91VAqYqfANYKO .cluster-label span p{background-color:transparent;}#mermaid-svg-kGx91VAqYqfANYKO .label text,#mermaid-svg-kGx91VAqYqfANYKO span{fill:#333;color:#333;}#mermaid-svg-kGx91VAqYqfANYKO .node rect,#mermaid-svg-kGx91VAqYqfANYKO .node circle,#mermaid-svg-kGx91VAqYqfANYKO .node ellipse,#mermaid-svg-kGx91VAqYqfANYKO .node polygon,#mermaid-svg-kGx91VAqYqfANYKO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kGx91VAqYqfANYKO .rough-node .label text,#mermaid-svg-kGx91VAqYqfANYKO .node .label text,#mermaid-svg-kGx91VAqYqfANYKO .image-shape .label,#mermaid-svg-kGx91VAqYqfANYKO .icon-shape .label{text-anchor:middle;}#mermaid-svg-kGx91VAqYqfANYKO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kGx91VAqYqfANYKO .rough-node .label,#mermaid-svg-kGx91VAqYqfANYKO .node .label,#mermaid-svg-kGx91VAqYqfANYKO .image-shape .label,#mermaid-svg-kGx91VAqYqfANYKO .icon-shape .label{text-align:center;}#mermaid-svg-kGx91VAqYqfANYKO .node.clickable{cursor:pointer;}#mermaid-svg-kGx91VAqYqfANYKO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kGx91VAqYqfANYKO .arrowheadPath{fill:#333333;}#mermaid-svg-kGx91VAqYqfANYKO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kGx91VAqYqfANYKO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kGx91VAqYqfANYKO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kGx91VAqYqfANYKO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kGx91VAqYqfANYKO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kGx91VAqYqfANYKO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kGx91VAqYqfANYKO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kGx91VAqYqfANYKO .cluster text{fill:#333;}#mermaid-svg-kGx91VAqYqfANYKO .cluster span{color:#333;}#mermaid-svg-kGx91VAqYqfANYKO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kGx91VAqYqfANYKO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kGx91VAqYqfANYKO rect.text{fill:none;stroke-width:0;}#mermaid-svg-kGx91VAqYqfANYKO .icon-shape,#mermaid-svg-kGx91VAqYqfANYKO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kGx91VAqYqfANYKO .icon-shape p,#mermaid-svg-kGx91VAqYqfANYKO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kGx91VAqYqfANYKO .icon-shape rect,#mermaid-svg-kGx91VAqYqfANYKO .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kGx91VAqYqfANYKO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kGx91VAqYqfANYKO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kGx91VAqYqfANYKO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始文本
分词
滑动窗口构建共现图
计算PageRank值
排序输出关键词

jieba实现TextRank

python 复制代码
import jieba.analyse

# 基于TextRank提取关键词
keywords_textrank = jieba.analyse.textrank(
    text,
    topK=10,
    withWeight=True,
    allowPOS=('ns', 'n', 'vn', 'v')  # 仅保留名词、动名词、动词
)

print("TextRank关键词提取结果:")
for word, weight in keywords_textrank:
    print(f"  {word}: {weight:.4f}")

TF-IDF vs TextRank 对比

对比维度 TF-IDF TextRank
原理 词频 × 逆文档频率 基于图的PageRank排序
是否需要语料库 需要(计算IDF) 不需要(单文档即可)
计算速度 较慢(需构建图)
适用场景 有大量文档的语料库 单篇文档分析
结果稳定性 依赖语料库质量 依赖窗口大小设置

💡 选择建议:如果你有大规模语料库,优先使用TF-IDF;如果只有单篇文档,TextRank是更好的选择。

3.3 词袋模型(Bag of Words)

核心思想

词袋模型(BOW) 是文本向量化最基础的方法。它将所有词语装进一个"袋子"里,不考虑词法和语序,每个词语都是独立的。

直观示例

假设有以下两个句子:

  • 句子A:Jane wants to go to Shenzhen.
  • 句子B:Bob wants to go to Shanghai.

构建词袋词典:

复制代码
[Jane, wants, to, go, Shenzhen, Bob, Shanghai]

将句子转换为词频向量:

句子 Jane wants to go Shenzhen Bob Shanghai
句子A 1 1 2 1 1 0 0
句子B 0 1 2 1 0 1 1

CountVectorizer实现

python 复制代码
from sklearn.feature_extraction.text import CountVectorizer

# 示例语料
corpus = [
    "我 喜欢 Python 编程",
    "Python 是 一门 优秀 的 编程 语言",
    "我 每天 都 用 Python 写 代码"
]

# 创建CountVectorizer
cv = CountVectorizer()

# 拟合并转换文本
bow_matrix = cv.fit_transform(corpus)

# 查看特征词
print("特征词列表:", cv.get_feature_names_out())
# 输出: ['一门', '代码', '优秀', '喜欢', '我', '是', '每天', '用', '的', '编程', '语言', '都', 'Python']

# 查看词频矩阵
print("词袋矩阵:\n", bow_matrix.toarray())
# 输出:
# [[0 0 0 1 1 0 0 0 0 1 0 0 1]
#  [1 0 1 0 0 1 0 0 1 1 1 0 1]
#  [0 1 0 0 1 0 1 1 0 0 0 1 1]]

# 查看每篇文档的词频统计
import pandas as pd
df_bow = pd.DataFrame(bow_matrix.toarray(), columns=cv.get_feature_names_out())
print(df_bow)

词袋模型的优缺点

优点 缺点
简单直观,易于理解 丢失语序信息
计算效率高 向量稀疏,维度高
可作为大多数分类器的输入 无法捕捉语义相似性
适合短文本分类 忽略词语间的关联

四、LDA主题建模:发现文本的隐藏主题

4.1 LDA简介

LDA(Latent Dirichlet Allocation,隐狄利克雷分布) 是主题模型中最重要的算法之一。它的核心思想是:

一篇文章包含多个主题,每个主题对应一组词语的概率分布。

LDA可以自动从文档集合中发现"隐藏的主题",常用于:

  • 新闻主题聚类
  • 用户评论分析
  • 学术文献主题挖掘
  • 社交媒体趋势分析

4.2 LDA建模流程

#mermaid-svg-Vimbe5YxexWTQXE9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Vimbe5YxexWTQXE9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Vimbe5YxexWTQXE9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Vimbe5YxexWTQXE9 .error-icon{fill:#552222;}#mermaid-svg-Vimbe5YxexWTQXE9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Vimbe5YxexWTQXE9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Vimbe5YxexWTQXE9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Vimbe5YxexWTQXE9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Vimbe5YxexWTQXE9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Vimbe5YxexWTQXE9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Vimbe5YxexWTQXE9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Vimbe5YxexWTQXE9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Vimbe5YxexWTQXE9 .marker.cross{stroke:#333333;}#mermaid-svg-Vimbe5YxexWTQXE9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Vimbe5YxexWTQXE9 p{margin:0;}#mermaid-svg-Vimbe5YxexWTQXE9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Vimbe5YxexWTQXE9 .cluster-label text{fill:#333;}#mermaid-svg-Vimbe5YxexWTQXE9 .cluster-label span{color:#333;}#mermaid-svg-Vimbe5YxexWTQXE9 .cluster-label span p{background-color:transparent;}#mermaid-svg-Vimbe5YxexWTQXE9 .label text,#mermaid-svg-Vimbe5YxexWTQXE9 span{fill:#333;color:#333;}#mermaid-svg-Vimbe5YxexWTQXE9 .node rect,#mermaid-svg-Vimbe5YxexWTQXE9 .node circle,#mermaid-svg-Vimbe5YxexWTQXE9 .node ellipse,#mermaid-svg-Vimbe5YxexWTQXE9 .node polygon,#mermaid-svg-Vimbe5YxexWTQXE9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Vimbe5YxexWTQXE9 .rough-node .label text,#mermaid-svg-Vimbe5YxexWTQXE9 .node .label text,#mermaid-svg-Vimbe5YxexWTQXE9 .image-shape .label,#mermaid-svg-Vimbe5YxexWTQXE9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Vimbe5YxexWTQXE9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Vimbe5YxexWTQXE9 .rough-node .label,#mermaid-svg-Vimbe5YxexWTQXE9 .node .label,#mermaid-svg-Vimbe5YxexWTQXE9 .image-shape .label,#mermaid-svg-Vimbe5YxexWTQXE9 .icon-shape .label{text-align:center;}#mermaid-svg-Vimbe5YxexWTQXE9 .node.clickable{cursor:pointer;}#mermaid-svg-Vimbe5YxexWTQXE9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Vimbe5YxexWTQXE9 .arrowheadPath{fill:#333333;}#mermaid-svg-Vimbe5YxexWTQXE9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Vimbe5YxexWTQXE9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Vimbe5YxexWTQXE9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Vimbe5YxexWTQXE9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Vimbe5YxexWTQXE9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Vimbe5YxexWTQXE9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Vimbe5YxexWTQXE9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Vimbe5YxexWTQXE9 .cluster text{fill:#333;}#mermaid-svg-Vimbe5YxexWTQXE9 .cluster span{color:#333;}#mermaid-svg-Vimbe5YxexWTQXE9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Vimbe5YxexWTQXE9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Vimbe5YxexWTQXE9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Vimbe5YxexWTQXE9 .icon-shape,#mermaid-svg-Vimbe5YxexWTQXE9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Vimbe5YxexWTQXE9 .icon-shape p,#mermaid-svg-Vimbe5YxexWTQXE9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Vimbe5YxexWTQXE9 .icon-shape rect,#mermaid-svg-Vimbe5YxexWTQXE9 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Vimbe5YxexWTQXE9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Vimbe5YxexWTQXE9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Vimbe5YxexWTQXE9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始文本语料
分词处理
构建词典 Dictionary
文档转词袋 Corpus
LDA模型训练
主题分析
查看主题-词分布
查看文档-主题分布
主题可视化

4.3 gensim实现LDA

数据准备

LDA建模要求输入数据为特定格式:一个包含句子的list,list中每个元素是一句话分词后的词list。

python 复制代码
# 数据格式要求
sentences = [
    ['第一', '条', '新闻', '在', '这里'],
    ['第二', '条', '新闻', '在', '这里'],
    ['这', '是', '在', '做', '什么'],
    # ...更多句子
]

完整建模代码

python 复制代码
from gensim import corpora, models
import gensim

# ============ 步骤1: 准备数据 ============
# sentences: 文本内容每一行分词后的列表
# 例如: [['今天','天气','不错'], ['明天','可能','下雨'], ...]

# ============ 步骤2: 构建词典 ============
dictionary = corpora.Dictionary(sentences)

# 过滤极端词(可选)
# dictionary.filter_extremes(no_below=5, no_above=0.5)
# no_below: 至少在5个文档中出现
# no_above: 在超过50%的文档中出现则过滤

# ============ 步骤3: 文档转词袋向量 ============
corpus = [dictionary.doc2bow(sentence) for sentence in sentences]

# doc2bow返回格式: [(词ID, 词频), ...]
# 例如: [(0, 1), (1, 2), (3, 1)] 表示词0出现1次,词1出现2次,词3出现1次

# ============ 步骤4: 训练LDA模型 ============
lda_model = gensim.models.ldamodel.LdaModel(
    corpus=corpus,
    id2word=dictionary,
    num_topics=10,        # 主题数量
    passes=10,            # 迭代次数
    alpha='auto',         # 文档-主题先验
    eta='auto',           # 主题-词先验
    random_state=42
)

# ============ 步骤5: 查看主题 ============
print("发现的主题:")
for idx, topic in lda_model.print_topics(num_topics=10, num_words=8):
    print(f"主题 {idx}: {topic}")

# 输出示例:
# 主题 0: 0.023*"科技" + 0.018*"人工智能" + 0.015*"数据" + ...
# 主题 1: 0.025*"体育" + 0.020*"比赛" + 0.017*"球员" + ...

# ============ 步骤6: 对新文档推断主题分布 ============
new_doc = ['深度', '学习', '在', '自然语言', '处理', '中', '的', '应用']
new_bow = dictionary.doc2bow(new_doc)
topic_dist = lda_model.get_document_topics(new_bow)
print("新文档的主题分布:", topic_dist)
# 输出: [(0, 0.65), (3, 0.20), (7, 0.10), ...]

模型参数详解

参数 说明 建议值
num_topics 主题数量 根据业务场景设定,通常10-100
passes 遍历语料的次数 10-50,越多越精确但越慢
alpha 文档-主题分布的Dirichlet先验 'auto' 自动学习
eta 主题-词分布的Dirichlet先验 'auto' 自动学习
chunksize 每次处理的文档数 2000(大数据集)

LDA主题可视化

python 复制代码
import pyLDAvis.gensim_models
import pyLDAvis

# 准备可视化数据
vis_data = pyLDAvis.gensim_models.prepare(lda_model, corpus, dictionary)

# 生成HTML可视化报告
pyLDAvis.save_html(vis_data, 'lda_visualization.html')

# 或在Jupyter中直接显示
# pyLDAvis.display(vis_data)

⚠️ 常见错误:LDA建模前务必做好文本预处理------分词、去停用词、去低频词。如果语料中存在大量噪声词(如"的"、"了"、"是"),会导致主题质量严重下降。


五、机器学习文本分类:朴素贝叶斯

5.1 朴素贝叶斯算法原理

朴素贝叶斯(Naïve Bayes) 是基于贝叶斯定理与特征条件独立假设的分类方法。

核心公式

P ( C k ∣ X ) = P ( X ∣ C k ) ⋅ P ( C k ) P ( X ) P(C_k | X) = \frac{P(X | C_k) \cdot P(C_k)}{P(X)} P(Ck∣X)=P(X)P(X∣Ck)⋅P(Ck)

其中:

  • P ( C k ∣ X ) P(C_k | X) P(Ck∣X):给定特征X,样本属于类别 C k C_k Ck的概率(后验概率)
  • P ( X ∣ C k ) P(X | C_k) P(X∣Ck):在类别 C k C_k Ck下观测到特征X的概率(似然)
  • P ( C k ) P(C_k) P(Ck):类别 C k C_k Ck的先验概率
  • P ( X ) P(X) P(X):特征X的边缘概率

"朴素"的含义

"朴素" 指的是假设样本的各特征之间相互独立。虽然这个假设在现实中几乎不成立,但朴素贝叶斯在实际应用中效果出奇地好,尤其在文本分类任务中。

5.2 文本分类实战

完整流程

python 复制代码
import jieba
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score

# ============ 步骤1: 准备数据 ============
# 假设我们有标注好的数据
texts = [
    "中国队在世界乒乓球锦标赛中夺得冠军",
    "NBA总决赛湖人队击败凯尔特人夺冠",
    "苹果发布新一代iPhone手机",
    "华为推出最新款折叠屏手机",
    "股市今日大幅上涨,沪指突破3500点",
    "央行宣布降准释放流动性",
    # ... 更多标注数据
]

labels = [
    "体育", "体育",
    "科技", "科技",
    "财经", "财经",
    # ... 对应标签
]

# ============ 步骤2: 中文分词 ============
def chinese_tokenizer(text):
    """中文分词函数"""
    words = jieba.lcut(text)
    # 过滤单字词
    return [w for w in words if len(w) > 1]

# 对所有文本进行分词并用空格连接
texts_cut = [' '.join(chinese_tokenizer(text)) for text in texts]

# ============ 步骤3: 划分训练集和测试集 ============
X_train, X_test, y_train, y_test = train_test_split(
    texts_cut, labels, test_size=0.2, random_state=42, stratify=labels
)

print(f"训练集大小: {len(X_train)}")
print(f"测试集大小: {len(X_test)}")

# ============ 步骤4: 特征提取(词袋模型) ============
cv = CountVectorizer(max_features=5000)  # 限制最大特征数
X_train_bow = cv.fit_transform(X_train)
X_test_bow = cv.transform(X_test)  # 注意:测试集用transform,不用fit

print(f"特征维度: {X_train_bow.shape[1]}")

# ============ 步骤5: 训练朴素贝叶斯分类器 ============
classifier = MultinomialNB(alpha=1.0)  # alpha是拉普拉斯平滑参数
classifier.fit(X_train_bow, y_train)

# ============ 步骤6: 模型评估 ============
y_pred = classifier.predict(X_test_bow)

print("准确率:", accuracy_score(y_test, y_pred))
print("\n分类报告:")
print(classification_report(y_test, y_pred))

模型评估指标解读

指标 含义 计算公式
Precision(精确率) 预测为正的样本中真正为正的比例 TP / (TP + FP)
Recall(召回率) 真正为正的样本中被预测出来的比例 TP / (TP + FN)
F1-Score 精确率和召回率的调和平均 2 × P × R / (P + R)
Accuracy(准确率) 所有预测正确的比例 (TP + TN) / Total

5.3 常见问题与解决方案

问题1:分词不准确导致分类效果差

错误案例:

python 复制代码
# ❌ 错误做法:直接使用默认分词,未添加自定义词典
text = "南京市长江大桥"
words = jieba.lcut(text)
print(words)  # ['南京市', '长江大桥'] 或 ['南京', '市长', '江大桥']

解决方案:

python 复制代码
# ✅ 正确做法:添加自定义词典
jieba.add_word("南京市长江大桥")  # 添加专有名词
jieba.suggest_freq(('南京', '市'), tune=True)  # 调整词频

text = "南京市长江大桥"
words = jieba.lcut(text)
print(words)  # ['南京市长江大桥']

问题2:停用词未正确过滤

错误案例:

python 复制代码
# ❌ 错误做法:未过滤停用词,导致特征中包含大量无意义词
cv = CountVectorizer()
X_bow = cv.fit_transform(texts_cut)
# 特征中包含"的"、"了"、"是"等高频无意义词

解决方案:

python 复制代码
# ✅ 正确做法:在CountVectorizer中指定停用词
stop_words = ['的', '了', '是', '在', '和', '就', '都', '而', '及',
              '与', '着', '或', '一个', '没有', '我们', '你们', '他们']

cv = CountVectorizer(stop_words=stop_words, max_features=5000)
X_bow = cv.fit_transform(texts_cut)

问题3:类别不平衡导致模型偏向多数类

错误案例:

python 复制代码
# ❌ 错误做法:直接训练,不处理类别不平衡
# 假设科技类1000条,体育类100条
classifier = MultinomialNB()
classifier.fit(X_train, y_train)
# 模型可能把所有样本都预测为"科技",准确率看似90%但实际无用

解决方案:

python 复制代码
# ✅ 正确做法:使用class_weight或采样策略
from sklearn.utils import class_weight
import numpy as np

# 方案A:设置类别权重
class_weights = class_weight.compute_class_weight(
    'balanced',
    classes=np.unique(y_train),
    y=y_train
)
weight_dict = dict(zip(np.unique(y_train), class_weights))

classifier = MultinomialNB()
classifier.fit(X_train, y_train)
# 注意:MultinomialNB不直接支持class_weight
# 可使用sample_weight参数

# 方案B:过采样/欠采样
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_train_bow, y_train)

六、深度学习文本分类:CNN与LSTM

6.1 CNN文本分类

为什么CNN能做文本分类?

卷积神经网络(CNN)虽然最初为图像设计,但同样适用于文本分类。在NLP任务中:

  • 输入:不再是图像像素,而是以矩阵表示的句子(每行是一个词的词向量)
  • 卷积核:滑过矩阵的一"行"(即单词),而不是图像的"块"
  • 池化:提取最重要的特征,降低维度

#mermaid-svg-60Bh7REg8gtdQCXj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-60Bh7REg8gtdQCXj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-60Bh7REg8gtdQCXj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-60Bh7REg8gtdQCXj .error-icon{fill:#552222;}#mermaid-svg-60Bh7REg8gtdQCXj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-60Bh7REg8gtdQCXj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-60Bh7REg8gtdQCXj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-60Bh7REg8gtdQCXj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-60Bh7REg8gtdQCXj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-60Bh7REg8gtdQCXj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-60Bh7REg8gtdQCXj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-60Bh7REg8gtdQCXj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-60Bh7REg8gtdQCXj .marker.cross{stroke:#333333;}#mermaid-svg-60Bh7REg8gtdQCXj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-60Bh7REg8gtdQCXj p{margin:0;}#mermaid-svg-60Bh7REg8gtdQCXj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-60Bh7REg8gtdQCXj .cluster-label text{fill:#333;}#mermaid-svg-60Bh7REg8gtdQCXj .cluster-label span{color:#333;}#mermaid-svg-60Bh7REg8gtdQCXj .cluster-label span p{background-color:transparent;}#mermaid-svg-60Bh7REg8gtdQCXj .label text,#mermaid-svg-60Bh7REg8gtdQCXj span{fill:#333;color:#333;}#mermaid-svg-60Bh7REg8gtdQCXj .node rect,#mermaid-svg-60Bh7REg8gtdQCXj .node circle,#mermaid-svg-60Bh7REg8gtdQCXj .node ellipse,#mermaid-svg-60Bh7REg8gtdQCXj .node polygon,#mermaid-svg-60Bh7REg8gtdQCXj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-60Bh7REg8gtdQCXj .rough-node .label text,#mermaid-svg-60Bh7REg8gtdQCXj .node .label text,#mermaid-svg-60Bh7REg8gtdQCXj .image-shape .label,#mermaid-svg-60Bh7REg8gtdQCXj .icon-shape .label{text-anchor:middle;}#mermaid-svg-60Bh7REg8gtdQCXj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-60Bh7REg8gtdQCXj .rough-node .label,#mermaid-svg-60Bh7REg8gtdQCXj .node .label,#mermaid-svg-60Bh7REg8gtdQCXj .image-shape .label,#mermaid-svg-60Bh7REg8gtdQCXj .icon-shape .label{text-align:center;}#mermaid-svg-60Bh7REg8gtdQCXj .node.clickable{cursor:pointer;}#mermaid-svg-60Bh7REg8gtdQCXj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-60Bh7REg8gtdQCXj .arrowheadPath{fill:#333333;}#mermaid-svg-60Bh7REg8gtdQCXj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-60Bh7REg8gtdQCXj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-60Bh7REg8gtdQCXj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-60Bh7REg8gtdQCXj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-60Bh7REg8gtdQCXj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-60Bh7REg8gtdQCXj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-60Bh7REg8gtdQCXj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-60Bh7REg8gtdQCXj .cluster text{fill:#333;}#mermaid-svg-60Bh7REg8gtdQCXj .cluster span{color:#333;}#mermaid-svg-60Bh7REg8gtdQCXj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-60Bh7REg8gtdQCXj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-60Bh7REg8gtdQCXj rect.text{fill:none;stroke-width:0;}#mermaid-svg-60Bh7REg8gtdQCXj .icon-shape,#mermaid-svg-60Bh7REg8gtdQCXj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-60Bh7REg8gtdQCXj .icon-shape p,#mermaid-svg-60Bh7REg8gtdQCXj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-60Bh7REg8gtdQCXj .icon-shape rect,#mermaid-svg-60Bh7REg8gtdQCXj .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-60Bh7REg8gtdQCXj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-60Bh7REg8gtdQCXj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-60Bh7REg8gtdQCXj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 文本输入
词嵌入层
卷积层 多个卷积核
池化层 MaxPooling
全连接层
Softmax分类

CNN文本分类实现

python 复制代码
import torch
import torch.nn as nn
import torch.nn.functional as F

class TextCNN(nn.Module):
    """
    文本卷积神经网络
    
    参数:
        vocab_size: 词汇表大小
        embed_dim: 词向量维度
        num_classes: 分类类别数
        filter_sizes: 卷积核尺寸列表,如 [2, 3, 4]
        num_filters: 每种尺寸卷积核的数量
    """
    def __init__(self, vocab_size, embed_dim=128, num_classes=10,
                 filter_sizes=[2, 3, 4], num_filters=100, dropout=0.5):
        super(TextCNN, self).__init__()
        
        # 词嵌入层
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        
        # 多个不同尺寸的卷积层
        self.convs = nn.ModuleList([
            nn.Conv2d(
                in_channels=1,           # 输入通道数(单通道)
                out_channels=num_filters, # 卷积核数量
                kernel_size=(fs, embed_dim)  # 卷积核尺寸:(窗口大小, 词向量维度)
            )
            for fs in filter_sizes
        ])
        
        # Dropout层(防止过拟合)
        self.dropout = nn.Dropout(dropout)
        
        # 全连接分类层
        self.fc = nn.Linear(len(filter_sizes) * num_filters, num_classes)
    
    def forward(self, x):
        """
        前向传播
        
        参数:
            x: 输入张量,形状 (batch_size, seq_len)
        
        返回:
            分类logits,形状 (batch_size, num_classes)
        """
        # 词嵌入: (batch, seq_len) -> (batch, seq_len, embed_dim)
        x = self.embedding(x)
        
        # 增加通道维度: (batch, 1, seq_len, embed_dim)
        x = x.unsqueeze(1)
        
        # 卷积 + ReLU激活 + 池化
        pooled = []
        for conv in self.convs:
            # 卷积: (batch, num_filters, seq_len - fs + 1, 1)
            c = F.relu(conv(x))
            # 最大池化: (batch, num_filters, 1, 1)
            c = F.max_pool2d(c, (c.size(2), 1))
            pooled.append(c)
        
        # 拼接所有卷积核的输出: (batch, total_filters)
        x = torch.cat(pooled, dim=1)
        x = x.view(x.size(0), -1)
        
        # Dropout + 全连接分类
        x = self.dropout(x)
        x = self.fc(x)
        
        return x

# ============ 使用示例 ============
# 假设词汇表大小为10000,分类数为5
model = TextCNN(vocab_size=10000, num_classes=5)
print(model)

# 模拟输入: batch_size=32, 序列长度=50
sample_input = torch.randint(0, 10000, (32, 50))
output = model(sample_input)
print(f"输出形状: {output.shape}")  # (32, 5)

CNN文本分类的优缺点

优点 缺点
训练速度快,可并行计算 卷积和池化会丢失局部字词排列关系
能捕捉局部n-gram特征 不适合序列标注和NER任务
参数相对较少 对长距离依赖捕捉能力弱
适合短文本分类 窗口大小需要手动调参

6.2 LSTM/GRU文本分类

为什么需要LSTM?

CNN擅长捕捉局部特征 ,但文本是序列数据,词语之间的长距离依赖关系同样重要。例如:

"我昨天去了那家餐厅 ,虽然排队等了很久,但菜品的味道确实让值得推荐。"

要理解"它"指代"餐厅",模型需要记住前面出现的"餐厅"------这就是长距离依赖

LSTM(Long Short-Term Memory) 通过精巧的门控机制(遗忘门、输入门、输出门),能够有效地捕捉这种长距离依赖关系。

LSTM文本分类实现

python 复制代码
import torch
import torch.nn as nn

class TextLSTM(nn.Module):
    """
    LSTM文本分类模型
    
    参数:
        vocab_size: 词汇表大小
        embed_dim: 词向量维度
        hidden_dim: LSTM隐藏层维度
        num_classes: 分类类别数
        num_layers: LSTM层数
        bidirectional: 是否使用双向LSTM
    """
    def __init__(self, vocab_size, embed_dim=128, hidden_dim=256,
                 num_classes=10, num_layers=2, bidirectional=True, dropout=0.5):
        super(TextLSTM, self).__init__()
        
        # 词嵌入层
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        
        # LSTM层
        self.lstm = nn.LSTM(
            input_size=embed_dim,
            hidden_size=hidden_dim,
            num_layers=num_layers,
            bidirectional=bidirectional,
            batch_first=True,       # 输入格式: (batch, seq, feature)
            dropout=dropout if num_layers > 1 else 0
        )
        
        # 计算LSTM输出维度
        lstm_output_dim = hidden_dim * 2 if bidirectional else hidden_dim
        
        # 全连接分类层
        self.fc = nn.Sequential(
            nn.Dropout(dropout),
            nn.Linear(lstm_output_dim, hidden_dim),
            nn.ReLU(),
            nn.Dropout(dropout),
            nn.Linear(hidden_dim, num_classes)
        )
    
    def forward(self, x):
        """
        前向传播
        
        参数:
            x: 输入张量,形状 (batch_size, seq_len)
        
        返回:
            分类logits,形状 (batch_size, num_classes)
        """
        # 词嵌入: (batch, seq_len) -> (batch, seq_len, embed_dim)
        x = self.embedding(x)
        
        # LSTM: output形状 (batch, seq_len, lstm_output_dim)
        lstm_out, (hidden, cell) = self.lstm(x)
        
        # 取最后一个时间步的输出(或双向LSTM的拼接)
        # 也可以使用hidden state
        x = lstm_out[:, -1, :]  # (batch, lstm_output_dim)
        
        # 全连接分类
        x = self.fc(x)
        
        return x

# ============ 使用示例 ============
model = TextLSTM(vocab_size=10000, num_classes=5, bidirectional=True)
print(model)

sample_input = torch.randint(0, 10000, (32, 50))
output = model(sample_input)
print(f"输出形状: {output.shape}")  # (32, 5)

GRU:LSTM的轻量级替代

GRU(Gated Recurrent Unit) 是LSTM的简化版本,将遗忘门和输入门合并为"更新门",参数更少、训练更快:

python 复制代码
class TextGRU(nn.Module):
    """GRU文本分类模型"""
    def __init__(self, vocab_size, embed_dim=128, hidden_dim=256,
                 num_classes=10, num_layers=2, bidirectional=True, dropout=0.5):
        super(TextGRU, self).__init__()
        
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        
        # 将LSTM替换为GRU
        self.gru = nn.GRU(
            input_size=embed_dim,
            hidden_size=hidden_dim,
            num_layers=num_layers,
            bidirectional=bidirectional,
            batch_first=True,
            dropout=dropout if num_layers > 1 else 0
        )
        
        gru_output_dim = hidden_dim * 2 if bidirectional else hidden_dim
        
        self.fc = nn.Sequential(
            nn.Dropout(dropout),
            nn.Linear(gru_output_dim, hidden_dim),
            nn.ReLU(),
            nn.Dropout(dropout),
            nn.Linear(hidden_dim, num_classes)
        )
    
    def forward(self, x):
        x = self.embedding(x)
        gru_out, hidden = self.gru(x)
        x = gru_out[:, -1, :]
        x = self.fc(x)
        return x

6.3 模型对比总结

模型 参数量 训练速度 长文本效果 短文本效果 适用场景
朴素贝叶斯 ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐ 基线模型、快速验证
CNN ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ 短文本分类、情感分析
LSTM ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐ 长文本、序列标注
GRU ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ LSTM的轻量替代

七、完整项目实战:中文新闻分类

7.1 项目流程

#mermaid-svg-Q4En5ScEQtyLvxZb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Q4En5ScEQtyLvxZb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Q4En5ScEQtyLvxZb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Q4En5ScEQtyLvxZb .error-icon{fill:#552222;}#mermaid-svg-Q4En5ScEQtyLvxZb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q4En5ScEQtyLvxZb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Q4En5ScEQtyLvxZb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q4En5ScEQtyLvxZb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q4En5ScEQtyLvxZb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Q4En5ScEQtyLvxZb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q4En5ScEQtyLvxZb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q4En5ScEQtyLvxZb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q4En5ScEQtyLvxZb .marker.cross{stroke:#333333;}#mermaid-svg-Q4En5ScEQtyLvxZb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q4En5ScEQtyLvxZb p{margin:0;}#mermaid-svg-Q4En5ScEQtyLvxZb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q4En5ScEQtyLvxZb .cluster-label text{fill:#333;}#mermaid-svg-Q4En5ScEQtyLvxZb .cluster-label span{color:#333;}#mermaid-svg-Q4En5ScEQtyLvxZb .cluster-label span p{background-color:transparent;}#mermaid-svg-Q4En5ScEQtyLvxZb .label text,#mermaid-svg-Q4En5ScEQtyLvxZb span{fill:#333;color:#333;}#mermaid-svg-Q4En5ScEQtyLvxZb .node rect,#mermaid-svg-Q4En5ScEQtyLvxZb .node circle,#mermaid-svg-Q4En5ScEQtyLvxZb .node ellipse,#mermaid-svg-Q4En5ScEQtyLvxZb .node polygon,#mermaid-svg-Q4En5ScEQtyLvxZb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q4En5ScEQtyLvxZb .rough-node .label text,#mermaid-svg-Q4En5ScEQtyLvxZb .node .label text,#mermaid-svg-Q4En5ScEQtyLvxZb .image-shape .label,#mermaid-svg-Q4En5ScEQtyLvxZb .icon-shape .label{text-anchor:middle;}#mermaid-svg-Q4En5ScEQtyLvxZb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Q4En5ScEQtyLvxZb .rough-node .label,#mermaid-svg-Q4En5ScEQtyLvxZb .node .label,#mermaid-svg-Q4En5ScEQtyLvxZb .image-shape .label,#mermaid-svg-Q4En5ScEQtyLvxZb .icon-shape .label{text-align:center;}#mermaid-svg-Q4En5ScEQtyLvxZb .node.clickable{cursor:pointer;}#mermaid-svg-Q4En5ScEQtyLvxZb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Q4En5ScEQtyLvxZb .arrowheadPath{fill:#333333;}#mermaid-svg-Q4En5ScEQtyLvxZb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q4En5ScEQtyLvxZb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q4En5ScEQtyLvxZb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q4En5ScEQtyLvxZb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Q4En5ScEQtyLvxZb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q4En5ScEQtyLvxZb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Q4En5ScEQtyLvxZb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q4En5ScEQtyLvxZb .cluster text{fill:#333;}#mermaid-svg-Q4En5ScEQtyLvxZb .cluster span{color:#333;}#mermaid-svg-Q4En5ScEQtyLvxZb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Q4En5ScEQtyLvxZb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Q4En5ScEQtyLvxZb rect.text{fill:none;stroke-width:0;}#mermaid-svg-Q4En5ScEQtyLvxZb .icon-shape,#mermaid-svg-Q4En5ScEQtyLvxZb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q4En5ScEQtyLvxZb .icon-shape p,#mermaid-svg-Q4En5ScEQtyLvxZb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Q4En5ScEQtyLvxZb .icon-shape rect,#mermaid-svg-Q4En5ScEQtyLvxZb .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q4En5ScEQtyLvxZb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Q4En5ScEQtyLvxZb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Q4En5ScEQtyLvxZb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 加载新闻数据集
数据探索与可视化
文本预处理
jieba分词
去除停用词
文本清洗
特征工程
词袋模型/TF-IDF
词向量 Word2Vec
模型训练
朴素贝叶斯基线
CNN模型
LSTM模型
模型评估与对比
选择最优模型

7.2 完整代码框架

python 复制代码
"""
中文新闻文本分类完整流程
从数据加载到模型评估的端到端实现
"""

import jieba
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns

# ============ 1. 数据加载 ============
def load_data(file_path):
    """
    加载新闻数据集
    
    参数:
        file_path: 数据文件路径
    
    返回:
        texts: 文本列表
        labels: 标签列表
    """
    df = pd.read_csv(file_path)
    texts = df['content'].tolist()
    labels = df['category'].tolist()
    return texts, labels

# ============ 2. 文本预处理 ============
def preprocess_text(text):
    """
    文本预处理:分词 + 清洗
    
    参数:
        text: 原始文本
    
    返回:
        处理后的词语列表
    """
    # 去除特殊字符
    import re
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z]', ' ', text)
    
    # jieba分词
    words = jieba.lcut(text)
    
    # 过滤单字词
    words = [w for w in words if len(w) > 1]
    
    return ' '.join(words)

# ============ 3. 特征提取 ============
def extract_features(train_texts, test_texts, max_features=5000):
    """
    使用TF-IDF提取文本特征
    
    参数:
        train_texts: 训练文本列表
        test_texts: 测试文本列表
        max_features: 最大特征数
    
    返回:
        X_train, X_test: 特征矩阵
        vectorizer: 向量化器(用于后续预测)
    """
    vectorizer = TfidfVectorizer(
        max_features=max_features,
        ngram_range=(1, 2),      # 使用unigram和bigram
        sublinear_tf=True         # 使用1+log(tf)
    )
    
    X_train = vectorizer.fit_transform(train_texts)
    X_test = vectorizer.transform(test_texts)
    
    return X_train, X_test, vectorizer

# ============ 4. 模型训练与评估 ============
def train_and_evaluate(X_train, X_test, y_train, y_test):
    """
    训练模型并评估
    
    参数:
        X_train, X_test: 特征矩阵
        y_train, y_test: 标签
    
    返回:
        model: 训练好的模型
        report: 分类报告
    """
    # 训练朴素贝叶斯
    model = MultinomialNB(alpha=0.5)
    model.fit(X_train, y_train)
    
    # 预测
    y_pred = model.predict(X_test)
    
    # 评估
    print("=" * 50)
    print("模型评估结果")
    print("=" * 50)
    print(classification_report(y_test, y_pred))
    
    # 混淆矩阵可视化
    cm = confusion_matrix(y_test, y_pred)
    plt.figure(figsize=(10, 8))
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
    plt.title('混淆矩阵')
    plt.xlabel('预测标签')
    plt.ylabel('真实标签')
    plt.show()
    
    return model

# ============ 5. 预测新文本 ============
def predict_new_text(model, vectorizer, text):
    """
    对新文本进行分类预测
    
    参数:
        model: 训练好的模型
        vectorizer: TF-IDF向量化器
        text: 待分类的原始文本
    
    返回:
        预测的类别
    """
    # 预处理
    processed = preprocess_text(text)
    
    # 向量化
    features = vectorizer.transform([processed])
    
    # 预测
    prediction = model.predict(features)[0]
    proba = model.predict_proba(features)[0]
    
    print(f"预测类别: {prediction}")
    print(f"各类别概率: {dict(zip(model.classes_, proba))}")
    
    return prediction

# ============ 主流程 ============
if __name__ == '__main__':
    # 加载数据
    texts, labels = load_data('news_data.csv')
    
    # 预处理
    processed_texts = [preprocess_text(t) for t in texts]
    
    # 划分数据集
    X_train, X_test, y_train, y_test = train_test_split(
        processed_texts, labels, test_size=0.2, random_state=42
    )
    
    # 特征提取
    X_train_vec, X_test_vec, vectorizer = extract_features(X_train, X_test)
    
    # 训练评估
    model = train_and_evaluate(X_train_vec, X_test_vec, y_train, y_test)
    
    # 测试新文本
    new_text = "中国航天员成功完成太空行走任务"
    predict_new_text(model, vectorizer, new_text)

八、总结与展望

8.1 核心要点回顾

本文从零开始,系统讲解了中文文本分类的完整技术栈:

章节 核心技术 关键要点
词云可视化 jieba + wordcloud 分词是中文NLP的基础,字体配置是关键
TF-IDF 词频 × 逆文档频率 需要语料库支撑,适合大规模文本
TextRank 基于图的PageRank 单文档即可,不需要语料库
词袋模型 CountVectorizer 简单高效,但丢失语序信息
LDA主题建模 gensim LdaModel 自动发现隐藏主题,需做好预处理
朴素贝叶斯 MultinomialNB 经典基线模型,文本分类首选
CNN文本分类 TextCNN 捕捉局部n-gram特征,训练快
LSTM/GRU TextLSTM/TextGRU 捕捉长距离依赖,适合长文本

8.2 技术选型建议

#mermaid-svg-3RiT6ukBzaZLTLtH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3RiT6ukBzaZLTLtH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3RiT6ukBzaZLTLtH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3RiT6ukBzaZLTLtH .error-icon{fill:#552222;}#mermaid-svg-3RiT6ukBzaZLTLtH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3RiT6ukBzaZLTLtH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3RiT6ukBzaZLTLtH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3RiT6ukBzaZLTLtH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3RiT6ukBzaZLTLtH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3RiT6ukBzaZLTLtH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3RiT6ukBzaZLTLtH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3RiT6ukBzaZLTLtH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3RiT6ukBzaZLTLtH .marker.cross{stroke:#333333;}#mermaid-svg-3RiT6ukBzaZLTLtH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3RiT6ukBzaZLTLtH p{margin:0;}#mermaid-svg-3RiT6ukBzaZLTLtH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3RiT6ukBzaZLTLtH .cluster-label text{fill:#333;}#mermaid-svg-3RiT6ukBzaZLTLtH .cluster-label span{color:#333;}#mermaid-svg-3RiT6ukBzaZLTLtH .cluster-label span p{background-color:transparent;}#mermaid-svg-3RiT6ukBzaZLTLtH .label text,#mermaid-svg-3RiT6ukBzaZLTLtH span{fill:#333;color:#333;}#mermaid-svg-3RiT6ukBzaZLTLtH .node rect,#mermaid-svg-3RiT6ukBzaZLTLtH .node circle,#mermaid-svg-3RiT6ukBzaZLTLtH .node ellipse,#mermaid-svg-3RiT6ukBzaZLTLtH .node polygon,#mermaid-svg-3RiT6ukBzaZLTLtH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3RiT6ukBzaZLTLtH .rough-node .label text,#mermaid-svg-3RiT6ukBzaZLTLtH .node .label text,#mermaid-svg-3RiT6ukBzaZLTLtH .image-shape .label,#mermaid-svg-3RiT6ukBzaZLTLtH .icon-shape .label{text-anchor:middle;}#mermaid-svg-3RiT6ukBzaZLTLtH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3RiT6ukBzaZLTLtH .rough-node .label,#mermaid-svg-3RiT6ukBzaZLTLtH .node .label,#mermaid-svg-3RiT6ukBzaZLTLtH .image-shape .label,#mermaid-svg-3RiT6ukBzaZLTLtH .icon-shape .label{text-align:center;}#mermaid-svg-3RiT6ukBzaZLTLtH .node.clickable{cursor:pointer;}#mermaid-svg-3RiT6ukBzaZLTLtH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3RiT6ukBzaZLTLtH .arrowheadPath{fill:#333333;}#mermaid-svg-3RiT6ukBzaZLTLtH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3RiT6ukBzaZLTLtH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3RiT6ukBzaZLTLtH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3RiT6ukBzaZLTLtH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3RiT6ukBzaZLTLtH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3RiT6ukBzaZLTLtH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3RiT6ukBzaZLTLtH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3RiT6ukBzaZLTLtH .cluster text{fill:#333;}#mermaid-svg-3RiT6ukBzaZLTLtH .cluster span{color:#333;}#mermaid-svg-3RiT6ukBzaZLTLtH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3RiT6ukBzaZLTLtH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3RiT6ukBzaZLTLtH rect.text{fill:none;stroke-width:0;}#mermaid-svg-3RiT6ukBzaZLTLtH .icon-shape,#mermaid-svg-3RiT6ukBzaZLTLtH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3RiT6ukBzaZLTLtH .icon-shape p,#mermaid-svg-3RiT6ukBzaZLTLtH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3RiT6ukBzaZLTLtH .icon-shape rect,#mermaid-svg-3RiT6ukBzaZLTLtH .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3RiT6ukBzaZLTLtH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3RiT6ukBzaZLTLtH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3RiT6ukBzaZLTLtH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 小规模 <1万条
中等规模 1-10万条
大规模 >10万条
短文本
长文本
有GPU
无GPU
数据规模?
朴素贝叶斯 + TF-IDF
文本长度?
深度学习 CNN/LSTM
CNN + Word2Vec
LSTM/GRU + 预训练词向量
GPU资源?
LSTM/Transformer
FastText / CNN

8.3 进阶方向

  1. 预训练模型:BERT、RoBERTa、ERNIE等预训练模型在中文NLP上表现优异
  2. 多标签分类:一篇新闻可能同时属于多个类别
  3. 少样本学习:在标注数据稀缺的场景下提升分类效果
  4. 在线学习:模型随新数据不断更新,适应数据分布变化

参考链接

  1. jieba分词官方文档 - 中文分词工具
  2. gensim LDA文档 - 主题建模
  3. scikit-learn文本特征提取 - CountVectorizer/TfidfVectorizer
  4. TextRank论文: Bringing Order into Texts - 关键词提取算法
  5. PyTorch文本分类教程 - 深度学习文本分类

🐾 下一篇预告:Python大数据实战(七)------旅游景点票价预测。我们将使用回归分析和机器学习模型,预测全国各大旅游景点的门票价格,帮你规划最具性价比的旅行路线!敬请期待。

📝 本文为"16个Python大数据项目"系列第6篇,完整项目列表请关注专栏更新。