当前位置: 首页 > news >正文

词袋模型 (BOW) 解析及代码实战

词袋模型 (BOW) 解析及代码实战

引言:文本表示的核心挑战

在自然语言处理(NLP)领域,文本数据的数学化表示是算法理解语义的基础。词袋模型(Bag of Words, BoW) 作为文本表示领域的里程碑方法,通过将文本转化为数值向量,成功解决了早期NLP任务的特征工程难题。尽管存在忽略词序的局限性,词袋模型在文本分类、情感分析等场景中仍展现出惊人的实用性。


词袋模型深度解析

模型定义与数学表达

词袋模型将文本抽象为无序词集合(“bag”),通过向量化表示实现文本数字化。给定文档集D包含m个文档,经处理后得到n维词汇表V,每个文档可表示为:

Document i = [ w i 1 , w i 2 , . . . , w i n ] \text{Document}_i = [w_{i1}, w_{i2}, ..., w_{in}] Documenti=[wi1,wi2,...,win]

其中 w i j w_{ij} wij 表示词汇表第j个词在文档i中的出现频次或存在性(0/1)。这种表示方式使文本数据可直接输入机器学习模型。

完整处理流程

  1. 文本预处理(核心步骤常被忽视)

    • 分词处理(中文需专门分词器)
    • 去除停用词(的、是、了等无实义词)
    • 词干提取(英文场景常见)
  2. 词汇表构建

    • 收集所有文档的唯一词项
    • 建立词项到索引的映射
  3. 向量化表示

    • 频次统计(TF表示)
    • 二值化表示(存在性判断)
    • TF-IDF加权(进阶改进)

模型特性分析

核心优势

  • 复杂度可控(时间复杂度O(n))
  • 可解释性强(特征对应具体词语)
  • 基线模型基准(任何NLP系统都应对比BoW效果)

关键局限

  • 语义信息丢失("猫追狗"与"狗追猫"无法区分)
  • 维度灾难(词汇表可达 1 0 5 10^5 105量级)
  • 同义忽略("电脑"与"计算机"视为不同特征)

中文词袋模型完整实现

实验环境搭建

!pip install jieba matplotlib scikit-learn  # 中文分词与可视化支持

定制化中文语料库

corpus = [
    "我特别特别喜欢看电影",          # 文档1:电影偏好表达
    "这部电影真的是很好看的电影",    # 文档2:影片评价
    "今天天气真好是难得的好天气",    # 文档3:天气描述
    "我今天去看了一部电影",         # 文档4:观影记录
    "电影院的电影都很好看"          # 文档5:影院评价
]

中文分词实战

import jieba

# 精准模式分词(相比全模式更准确)
corpus_tokenized = [list(jieba.cut(sentence, cut_all=False)) 
                   for sentence in corpus]

print("分词结果:")
for i, tokens in enumerate(corpus_tokenized):
    print(f"文档{i+1}: {'/'.join(tokens)}")

输出示例

分词结果:
文档1: 我/特别/特别/喜欢/看/电影
文档2: 这部/电影/真的/是/很/好看/的/电影
文档3: 今天天气/真好/是/难得/的/好/天气
文档4: 我/今天/去/看/了/一部/电影
文档5: 电影院/的/电影/都/很/好看

词汇表构建优化

from collections import defaultdict

# 自动排序词汇表
word_index = defaultdict(lambda: len(word_index))
for tokens in corpus_tokenized:
    for word in tokens:
        word_index[word]  # 自动分配递增索引

# 转换为标准字典
vocab = dict(word_index)
print("有序词汇表:", vocab)

输出示例

有序词汇表: {'我': 0, '特别': 1, '喜欢': 2, '看': 3, '电影': 4, '这部': 5, '真的': 6, '是': 7, '很': 8, '好看': 9, '的': 10, '今天天气': 11, '真好': 12, '难得': 13, '好': 14, '天气': 15, '今天': 16, '去': 17, '了': 18, '一部': 19, '电影院': 20, '都': 21}

词频向量生成

import numpy as np

# 初始化矩阵 (文档数 × 词汇量)
bow_matrix = np.zeros((len(corpus), len(vocab)), dtype=np.int16)

for doc_idx, tokens in enumerate(corpus_tokenized):
    for word in tokens:
        bow_matrix[doc_idx, vocab[word]] += 1

print("词频矩阵:")
print(bow_matrix)

输出结果

词频矩阵:
[[1 2 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 2 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 1 1 1 1 1 1 0 0 0 0 0 0]
 [1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0]
 [0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1]]

输出解析

文档1向量: [1,2,1,1,1,0,...]  # "特别"出现2次
文档2向量: [0,0,0,2,0,...]    # "电影"出现2次

相似度计算与可视化

余弦相似度数学原理

余弦相似度衡量向量空间中的方向相似性:

similarity = cos ⁡ ( θ ) = A ⋅ B ∥ A ∥ ∥ B ∥ \text{similarity} = \cos(\theta) = \frac{A \cdot B}{\|A\| \|B\|} similarity=cos(θ)=A∥∥BAB

  • 值域范围:[-1,1],文本分析中通常为[0,1]
  • 对绝对频次不敏感,关注分布模式

代码实现

def enhanced_cosine_sim(vec_a, vec_b):
    """添加零向量保护机制的余弦相似度"""
    dot = np.dot(vec_a, vec_b)
    norm_a = np.linalg.norm(vec_a)
    norm_b = np.linalg.norm(vec_b)
    
    # 处理零向量特殊情况
    if norm_a == 0 or norm_b == 0:
        return 0.0  # 定义零向量与任何向量相似度为0
    
    return dot / (norm_a * norm_b)

# 构建相似度矩阵
sim_matrix = np.array([
    [enhanced_cosine_sim(bow_matrix[i], bow_matrix[j]) 
     for j in range(len(corpus))]
    for i in range(len(corpus))
])

热力图可视化

import matplotlib.pyplot as plt
import seaborn as sns

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体作为默认字体
plt.rcParams['axes.unicode_minus'] = False     # 解决负号 '-' 显示为方块的问题

plt.figure(figsize=(10,8))
sns.heatmap(
    sim_matrix,
    annot=True,
    xticklabels=[f"Doc{i+1}" for i in range(5)],
    yticklabels=[f"Doc{i+1}" for i in range(5)],
    cmap="Blues",
    vmin=0,
    vmax=1,
)
plt.title("文档间余弦相似度热力图", fontsize=14)
plt.show()

在这里插入图片描述

结果分析

  • 文档2与文档5相似度高
  • 文档3与其他文档相似度低
  • 文档4与文档1、2存在一定相似性

工业级实现:Scikit-learn实战

中文CountVectorizer适配

from sklearn.feature_extraction.text import CountVectorizer

# 示例文本数据
documents = [
    "This is the first document.",
    "This document is the second document.",
    "And this is the third one.",
    "Is this the first document?"
]

# 使用CountVectorizer构建词袋模型
vectorizer = CountVectorizer()

# 对文本进行拟合和转换
X = vectorizer.fit_transform(documents)

# 获取词袋模型的词汇表
vocab = vectorizer.get_feature_names_out()

# 将稀疏矩阵转换为密集矩阵
dense_matrix = X.toarray()

# 显示词袋模型的结果
print("词汇表:", vocab)
print("词袋向量:")
print(dense_matrix)

解析 dense_matrix = X.toarray()
在上述代码示例中,X 是通过 CountVectorizer 对文本数据进行拟合和转换后得到的结果。X 实际上是一个稀疏矩阵(scipy.sparse matrix),它用来存储词袋模型(Bag of Words model)的计数结果。稀疏矩阵是一种专门用于存储大多数元素为零的矩阵的数据结构,这样可以有效地节省内存空间和提高计算效率。

  • 稀疏矩阵 vs 密集矩阵:在自然语言处理(NLP)和其他应用中,当我们使用诸如词袋模型、TF-IDF等方法将文本转化为数值向量时,通常会得到一个包含大量零值的矩阵。这是因为每篇文档只包含词汇表中的少部分单词。为了高效地存储和处理这种数据,我们使用稀疏矩阵。然而,有时我们需要将这个稀疏矩阵转换成密集矩阵(dense matrix),即普通二维数组,其中所有元素都以显式形式存储,包括零值。
  • toarray() 方法X.toarray() 就是用来完成稀疏矩阵到密集矩阵转换的方法。它将稀疏矩阵 X 转换成一个标准的 NumPy 数组。这意味着,即使矩阵中有很多零值,它们也会被显式地表示出来,从而形成一个完整的二维数组。

需要注意的是,如果文档数量很大或者词汇表非常丰富,toarray() 可能会导致大量的内存消耗,因为它将所有的零也显式存储起来。在这种情况下,可能更合适直接使用稀疏矩阵的格式进行后续操作,除非确实需要密集矩阵的形式。


模型演进与拓展应用

词袋模型的现代变体

模型变种核心改进典型应用场景
TF-IDF词频-逆文档频率加权信息检索
n-gram模型保留局部词序(2-3词组合)短文本分类
哈希技巧降维处理大规模文本处理
加权词袋融入词性、位置等信息法律文本分析

参考文献

【AIGC篇】文本表示方法比较:词袋模型 vs. 词向量 - 知了知了__ - 稀土掘金
NLP_Bag-Of-Words(词袋模型) - you_are_my_sunshine* - CSDN

相关文章:

  • 华为支付-商户基础支付场景准备
  • MongoDB 入门操作指南
  • 有哪些滤波,原理是什么,分别在什么时候用
  • 模糊数学模型:基础概念
  • DeepSeek 助力 Vue 开发:打造丝滑的卡片(Card)
  • 基于SpringBoot+uniapp的在线办公小程序+LW示例参考
  • 2025 docker可视化管理面板DPanel的安装
  • 如何使用CSS画一个三角形,原理是什么?
  • HarmonyOS:使用List实现分组列表(包含粘性标题)
  • 算法18(力扣136)只出现一次的数字
  • Huggingface加载阅读理解任务数据集至本地
  • 深度学习项目--基于RNN的阿尔茨海默病诊断研究(pytorch实现)
  • Node.js技术原理分析系列——Node.js调试能力分析
  • pycharm ai插件
  • 【人工智能】如何选择合适的大语言模型,是能否提高工作效率的关键!!!
  • 【学术投稿-第四届智能电网和绿色能源国际学术会议(ICSGGE 2025)】CSS基本选择器详解:掌握基础,轻松布局网页
  • ML.NET库学习006:成人人口普查数据分析与分类预测
  • AI 编程私有化部署,在使用 cline 时,可能无法避免私隐的泄漏问题
  • Kotlin 优雅的接口实现
  • 如何通过MDM高效管理企业的Android平板?
  • 手游传奇开服网站/品牌软文案例
  • 一级a做爰全过程片视频网站/seo工资多少
  • 免费数据库网站空间/新闻稿发布
  • 网站备案需要ftp吗/怎么宣传网站
  • 福州做网站的公司多少钱/互联网舆情监测系统
  • 广西企业网站有哪些/h5网站制作平台