-推荐关注-
Bag-of-Words 将文本看作由单词构成的无序集合,通过统计单词在文本中出现的频次来表示文本。因此,Bag-of-Words主要用于文本分类、情感分析、信息检索等自然语言处理任务中
1. 什么是词袋模型(Bag of Words) 2. 实现 Bag of Words 的步骤 2.1 文本预处理 2.2 构建词汇表 2.3 向量化文本(词袋表示) 2.4 通过文本的向量表示,使用余弦相似度分析句子相似度 3. 统计词频,通过评论提升餐厅满意度
-- 领取学习资料大礼包,见文末
在自然语言处理领域,如何将人类语言转化为机器能够理解和处理的结构化数据,是解决文本问题的关键。
Bag of Words(简称 BoW)是一种经典的文本表示方法,凭借其简单性和易实现性,在文本分析中被广泛应用。从情感分析到文档分类,BoW 都扮演着重要角色
1. 什么是词袋模型(Bag of Words)
Bag of Words 的核心思想是将文本看作一个“词袋”,不关注单词的顺序,仅统计每个单词在文本中出现的频率。它将一段文本转化为一个固定长度的向量,其中每个元素代表词汇表中一个单词的出现次数。虽然简单,但 BoW 能有效地将自然语言处理任务转化为标准的机器学习问题。
BoW 的特点是只关注词汇的频率,因此它特别适合于情感分析、文本分类、文档聚类等任务。然而,BoW 也有一些缺点,比如忽略了单词之间的顺序和上下文语义信息,这会导致对语义敏感的任务(如句子生成)性能不足。尽管如此,其简单高效的特性使其成为入门自然语言处理的理想方法。
2. 实现 Bag of Words 的步骤
2.1 文本预处理
文本预处理是实现 BoW 的第一步。原始文本通常包含噪声,例如标点符号、停用词和大小写的混乱。清理文本能够减少噪声,提高模型的性能。对于中文,还需要进行分词,因为中文没有天然的单词分隔符。
以下是 Python 实现中文分词和停用词去除的代码:
"""
scikit-learn
"""
import jieba
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
# 客户评论
reviews = [
"食物非常美味,配送速度也很快!",
"配送太慢了,食物已经凉了。",
"食物很棒,服务也不错,就是价格稍贵。",
"从未见过这么差的服务,食物送得又慢又难吃!",
"这家餐厅的食物真是太美味了,值得推荐!",
"虽然配送慢,但食物的味道让我感到惊喜。",
"食物美味可口,配送速度也有待提高。",
"我觉得这道菜非常美味,值得再点一次。",
"配送速度慢得让人失望,但食物的味道弥补了这一点。",
"这道菜的味道真是美味无比,令人回味无穷。",
"虽然等了很久,但食物的美味让我觉得值得。",
"每次来这家店,食物的美味总是让我惊喜。",
"配送的速度实在太慢,影响了我的用餐体验。"
]
# 分词预处理
# 停用词列表
stop_words = ["了", "的", "也", "从未", "这么", "但", "就是"]
# 分词与去停用词
def preprocess_text(corpus):
processed_corpus = []
for text in corpus:
words = jieba.lcut(text) # 使用 jieba 分词
words = [word for word in words if word notin stop_words] # 去停用词
processed_corpus.append(" ".join(words)) # 拼接成字符串
return processed_corpus
preprocessed_reviews = preprocess_text(reviews)
print("分词后的评论:")
print(preprocessed_reviews)
运行这段代码后,我们会得到预处理后的文本,如下所示:
分词后的评论:
['食物 非常 美味 , 配送 速度 很快 !', '配送 太慢 , 食物 已经 凉 。', '食物 很棒 , 服务 不错 , 价格 稍贵 。', '从未见过 差 服务 , 食物 送得 又 慢 又 难吃 !', '这家 餐厅 食物 真是太 美味 , 值得 推荐 !', '虽然 配送 慢 , 食物 味道 让 我 感到 惊喜 。', '食物 美味可口 , 配送 速度 有待 提高 。', '我 觉得 这 道菜 非常 美味 , 值得 再点 一次 。', '配送 速度慢 得 让 人 失望 , 食物 味道 弥补 这 一点 。', '这 道菜 味道 真是 美味 无比 , 令人 回味无穷 。', '虽然 等 很 久 , 食物 美味 让 我 觉得 值得 。', '每次 来 这家 店 , 食物 美味 总是 让 我 惊喜 。', '配送 速度 实在 太慢 , 影响 我 用餐 体验 。']
2.2 构建词汇表
接下来需要构建一个词汇表,包含所有预处理后的评论中出现的唯一单词。词汇表是向量化的基础,每个单词会被分配一个固定的索引。
以下是构建词汇表的代码:
from sklearn.feature_extraction.text import CountVectorizer
# 构建 BoW 模型
vectorizer = CountVectorizer()
bow_matrix = vectorizer.fit_transform(preprocessed_reviews)
# 输出词汇表
print("\n词汇表:")
print(vectorizer.get_feature_names_out())
结果中,vectorizer.get_feature_names_out()
会输出词汇表:
词汇表:
['一次''一点''不错''从未见过''令人''价格''体验''值得''再点''味道''回味无穷''太慢''失望''实在'
'已经''弥补''影响''很快''很棒''总是''惊喜''感到''推荐''提高''无比''有待''服务''每次'
'用餐''真是''真是太''稍贵''美味''美味可口''虽然''觉得''这家''送得''速度''速度慢''道菜''配送'
'难吃''非常''食物''餐厅']
2.3 向量化文本(词袋表示)
利用构建好的词汇表,我们可以将每条评论转化为一个稀疏向量。每个向量的长度等于词汇表的大小,每个元素代表该词汇在文本中出现的次数。
以下是生成 BoW 矩阵的代码:
# 输出 BoW 矩阵
print("\nBoW 矩阵:")
print(bow_matrix.toarray())
运行结果会生成一个稀疏矩阵,例如:
BoW 矩阵:
[[000000000000000001000000000000001000
0010010110]
[000000000001001000000000000000000000
0000010010]
[001001000000000000100000001000010000
0000000010]
[000100000000000000000000001000000000
0100001010]
[000000010000000000000010000000101000
1000000011]
[000000000100000000001100000000000010
0000010010]
[000000000000000000000001010000000100
0010010010]
[100000011000000000000000000000001001
0000100100]
[010000000100100100000000000000000000
0001010010]
[000010000110000000000000100001001000
0000100000]
[000000010000000000000000000000001011
0000000010]
[000000000000000000011000000100001000
1000000010]
[000000100001010010000000000010000000
0010010000]]
每一行是一个评论的 BoW 向量,每一列对应词汇表中的一个单词,元素值为该单词的出现次数。
2.4 通过文本的向量表示,使用余弦相似度分析句子相似度
余弦相似度是衡量两个向量之间夹角余弦值的指标,可以用于比较文本的内容相似程度
假设两个向量为 A 和 B,其余弦相似度定义为:
是两个向量的点积。
和 分别是向量 A 和 B 的模(即欧几里得范数)。
余弦相似度的值在 [−1,1] 之间:
1 表示两个向量方向完全一致(文本非常相似)。 0 表示向量正交(无相似性)。 -1 表示两个向量方向完全相反(完全不相似)。
from sklearn.metrics.pairwise import cosine_similarity
# 计算余弦相似度
cos_sim_matrix = cosine_similarity(bow_matrix)
# 热力图的绘制
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体
plt.rcParams['axes.unicode_minus'] = False# 解决负号显示问题
plt.figure(figsize=(12, 10))
sns.heatmap(cos_sim_matrix, annot=True, cmap='coolwarm',
xticklabels=[reviews[i] for i in range(len(reviews))],
yticklabels=[reviews[i] for i in range(len(reviews))], fmt=".2f",
cbar_kws={"shrink": .8})
plt.title("评论之间的余弦相似度热力图", fontsize=16)
plt.xlabel("评论", fontsize=14)
plt.ylabel("评论", fontsize=14)
# 调整标签的字体大小和旋转角度
plt.xticks(rotation=45, ha='right', fontsize=10)
plt.yticks(rotation=0, fontsize=10)
plt.tight_layout()
plt.show()
矩阵图中每个单元格表示两个句子之间的余弦相似度,颜色越深,句子在语义上越相似。
例如,“食物非常美味,配送速度也很快”和“食物美味可口,配送速度也有待提高”交叉处的单元格颜色相对较深,说明它们具有较高的余弦相似度0.50,这意味着它们在语义上较为相似。
通过余弦相似度矩阵,可以进行以下分析:
找出最相似的评论对,例如通过矩阵中非对角线的最大值。 为新评论寻找最相近的现有评论,作为推荐系统的一部分。 根据相似度对评论进行聚类或分类。
3. 统计词频,通过评论提升餐厅满意度
这些评论混合了正面和负面的反馈,统计矩阵中词频最高的词,可以分析客户关注的核心问题
# 计算词频
word_counts = np.asarray(bow_matrix.sum(axis=0)).flatten()
word_freq = dict(zip(vectorizer.get_feature_names_out(), word_counts))
# 按词频排序并输出前10个词
sorted_word_freq = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)[:10]
print("\n词频最高的前10个词:")
for word, freq in sorted_word_freq:
print(f"{word}: {freq}")
通过对生成的 BoW 矩阵计算词频,进行分析,我们可以发现一些有趣的现象。
例如,词汇“美味”、“值得”和“惊喜”在正面评论中出现频率较高,而“太慢”在负面评论中更常见。这些词的分布可以帮助我们快速定位客户满意和不满的关键点。正面评论中的高频词可能表明用户对食物质量满意,而负面评论中的词则表明配送慢是主要问题。
词频最高的前10个词:
食物: 10
美味: 6
配送: 6
值得: 3
味道: 3
速度: 3
太慢: 2
惊喜: 2
服务: 2
虽然: 2
词袋模型是早期的一种模型,相对简单,存在两个主要问题:
第一,它使用高维稀疏向量来表示文本,每个单词对应词汇表中的一个维度。这导致模型更适用于高维空间,而且计算效率低。
第二,词袋模型在表示单词时忽略了它们在文本中的上下文信息
Bag-of-Words 将文本看作由单词构成的无序集合,通过统计单词在文本中出现的频次来表示文本。因此,Bag-of-Words主要用于文本分类、情感分析、信息检索等自然语言处理任务中,在需要捕捉词序信息的任务中表现较差,如机器翻译和命名实体识别。
有需要的,在公众号「AI取经路」发消息「学习资料」即可获取。
--END--
点亮“赞”和“在看”,“分享”好友一起看