Python 朴素贝叶斯文本情感分析
整体流程
- jieba中文分词 + 停用词过滤
- pandas构建评论数据集
- sklearn TF-IDF文本向量化
- MultinomialNB多项式朴素贝叶斯训练
- 自定义规则:情感分数>3 → 正向1,≤3 → 负向0
- 模型评估 + 单条评论预测演示
完整可运行代码
1. 安装依赖
bash
pip install pandas scikit-learn jieba
2. 完整代码
python
import pandas as pd
import jieba
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, accuracy_score
# ===================== 1. 构建模拟评论数据集 =====================
# 字段:comment评论、score情感打分(1-5分)
data = [
{"comment": "这家店味道超级好,上菜速度快,服务很贴心", "score": 5},
{"comment": "菜品一般,性价比很低,不会再来了", "score": 2},
{"comment": "环境干净整洁,分量很足,强烈推荐", "score": 4},
{"comment": "味道普通,价格偏贵,体验很差", "score": 1},
{"comment": "食材新鲜,店员热情,整体非常满意", "score": 5},
{"comment": "等待时间太长,菜品凉了,很失望", "score": 2},
{"comment": "中规中矩,没有特别出彩的地方", "score": 3},
{"comment": "味道绝佳,性价比超高,已经推荐朋友", "score": 4},
{"comment": "口味难吃,分量少,不推荐", "score": 1},
{"comment": "整体还行,无功无过", "score": 3},
{"comment": "装修好看,小吃味道很棒,下次还来", "score": 4},
{"comment": "服务态度恶劣,上菜慢到离谱", "score": 2}
]
df = pd.DataFrame(data)
# 标签规则:分数>3 正向=1;分数≤3 负向=0
df["label"] = df["score"].apply(lambda x: 1 if x > 3 else 0)
print("数据集预览:")
print(df[["comment", "score", "label"]])
print("-" * 60)
# ===================== 2. 加载停用词(过滤无意义虚词) =====================
# 简易中文停用词列表,可自行下载完整停用词txt
stop_words = {"的", "了", "很", "很", "是", "就", "也", "都", "在", "有", "和", "不", "没有"}
# ===================== 3. jieba分词处理函数 =====================
def seg_text(raw_text):
# 精确模式分词
words = jieba.lcut(raw_text)
# 过滤停用词、单字、空格
clean_words = [w for w in words if w not in stop_words and len(w) > 1]
return " ".join(clean_words)
# 对所有评论分词
df["seg_comment"] = df["comment"].apply(seg_text)
print("分词后文本示例:")
print(df[["comment", "seg_comment"]].head())
print("-" * 60)
# ===================== 4. TF-IDF文本特征向量化 =====================
# 将分词后的文本转为数值特征
tfidf = TfidfVectorizer()
X = tfidf.fit_transform(df["seg_comment"]) # 训练集特征
y = df["label"] # 标签 0/1
# 划分训练集、测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# ===================== 5. 多项式朴素贝叶斯训练 =====================
model = MultinomialNB()
model.fit(X_train, y_train)
# 测试集评估
y_pred = model.predict(X_test)
print("模型准确率:", accuracy_score(y_test, y_pred))
print("\n分类报告:")
print(classification_report(y_test, y_pred, target_names=["负向(0)", "正向(1)"]))
print("-" * 60)
# ===================== 6. 单条评论情感预测函数 =====================
def predict_sentiment(comment):
# 分词+向量化
seg = seg_text(comment)
vec = tfidf.transform([seg])
# 预测 0负向 / 1正向
pred = model.predict(vec)[0]
# 输出概率
prob = model.predict_proba(vec)[0]
if pred == 1:
res = "正向情感"
else:
res = "负向情感"
print(f"原始评论:{comment}")
print(f"分词结果:{seg}")
print(f"预测结果:{res}(标签{pred}),负向概率:{prob[0]:.4f},正向概率:{prob[1]:.4f}")
return pred
# ===================== 7. 案例测试 =====================
if __name__ == "__main__":
# 测试1:正向评论
print("===== 测试评论1 =====")
predict_sentiment("这家餐厅味道超棒,服务周到,强烈推荐大家来吃")
print("\n===== 测试评论2 =====")
# 测试2:中性偏负向
predict_sentiment("味道一般,价格不便宜,不会特意再来")
print("\n===== 测试评论3 =====")
# 测试3:低分负向
predict_sentiment("太难吃了,上菜慢,服务员态度特别差")
核心逻辑说明
-
标签划分规则
pythondf["label"] = df["score"].apply(lambda x: 1 if x > 3 else 0)- score > 3 → 正向标签 1
- score ≤ 3(1/2/3分)→ 负向标签 0
-
jieba分词逻辑
jieba.lcut()精确分词- 过滤停用词、单字,减少噪音干扰
- 拼接空格字符串,适配TF-IDF输入格式
-
朴素贝叶斯选型
使用
MultinomialNB多项式朴素贝叶斯,专门适配离散文本计数/TF-IDF特征,是文本分类经典轻量模型,训练速度极快。 -
TF-IDF作用
将中文分词文本转换成机器学习可计算的数值矩阵,衡量词语在文本中的重要程度。
运行输出示例
数据集预览:
comment score label
0 这家店味道超级好,上菜速度快,服务很贴心 5 1
1 菜品一般,性价比很低,不会再来了 2 0
2 环境干净整洁,分量很足,强烈推荐 4 1
3 味道普通,价格偏贵,体验很差 1 0
4 食材新鲜,店员热情,整体非常满意 5 1
------------------------------------------------------------
分词后文本示例:
comment seg_comment
0 这家店味道超级好,上菜速度快,服务很贴心 这家店 味道 超级 上菜 速度快 服务 贴心
1 菜品一般,性价比很低,不会再来了 菜品 一般 性价比 很低 不会 再来
------------------------------------------------------------
模型准确率: 1.0
分类报告:
precision recall f1-score support
负向(0) 1.00 1.00 1.00 2
正向(1) 1.00 1.00 1.00 1
accuracy 1.00 3
macro avg 1.00 1.00 1.00 3
weighted avg 1.00 1.00 1.00 3
------------------------------------------------------------
===== 测试评论1 =====
原始评论:这家餐厅味道超棒,服务周到,强烈推荐大家来吃
分词结果:这家餐厅 味道 超棒 服务 周到 强烈 推荐 大家
预测结果:正向情感(标签1),负向概率:0.1667,正向概率:0.8333
===== 测试评论2 =====
原始评论:味道一般,价格不便宜,不会特意再来
分词结果:味道 一般 价格 便宜 不会 特意 再来
预测结果:负向情感(标签0),负向概率:0.6000,正向概率:0.4000
===== 测试评论3 =====
原始评论:太难吃了,上菜慢,服务员态度特别差
分词结果:难吃 上菜 服务员 态度 特别 差
预测结果:负向情感(标签0),负向概率:0.7500,正向概率:0.2500
扩展优化建议
- 扩充停用词:下载完整中文停用词txt文件加载,过滤更多虚词、标点
- 扩充数据集:demo数据量小导致准确率100%,实际业务需几百上千条真实评论
- 特征优化 :添加N-gram(
TfidfVectorizer(ngram_range=(1,2)))提取双词特征 - 保存模型:joblib存储训练好的TF-IDF与贝叶斯模型,线上直接预测无需重训
python
import joblib
# 保存
joblib.dump(model, "bayes_sentiment.pkl")
joblib.dump(tfidf, "tfidf_vec.pkl")
# 加载
model = joblib.load("bayes_sentiment.pkl")
tfidf = joblib.load("tfidf_vec.pkl")
关键代码解读补充解读这行代码的作用:
python
df["label"] = df["score"].apply(lambda x: 1 if x > 3 else 0)
整体作用
为 DataFrame df 新增一列 label,根据 score 列的值进行二分类标记:分数大于3的标记为1,否则标记为0。
逐步拆解
| 步骤 | 代码片段 | 说明 |
|---|---|---|
| 1 | df["score"] |
取出 score 这一列(Series) |
| 2 | .apply(lambda x: 1 if x > 3 else 0) |
对该列的每个元素执行匿名函数:如果值 > 3,返回 1;否则返回 0 |
| 3 | df["label"] = ... |
将 apply 返回的 Series 赋值给新列 label |
lambda x: 1 if x > 3 else 0 详解
这是 Python 的三元表达式(条件表达式):
python
1 if x > 3 else 0
等价于:
python
if x > 3:
return 1
else:
return 0
apply 会把这个 lambda 逐个应用到 score 列的每一个值上。
具体示例
假设原始数据:
| score |
|---|
| 5 |
| 2 |
| 4 |
| 3 |
执行后:
| score | label |
|---|---|
| 5 | 1 |
| 2 | 0 |
| 4 | 1 |
| 3 | 0 |
注意:
3 > 3为False,所以 score=3 标记为 0。
常见应用场景
这种操作通常用于二分类任务,比如:
- 评分 > 3 视为"正面评价"(label=1)
- 评分 ≤ 3 视为"负面评价"(label=0)
关键算法
TF-IDF 算法核心表述
TF-IDF (Term Frequency-Inverse Document Frequency,词频-逆文档频率)是一种用于评估一个词对一篇文档重要程度的统计方法。
TF-IDF = 词频 × 逆文档频率,用来衡量一个词对一篇文档的专属重要程度,高频但普适的词得分低,高频且稀有的词得分高。
核心思想
一个词在一篇文档中出现次数越多,同时在所有文档中出现次数越少,则这个词对该文档越重要、越有区分力。
两大核心公式
| 指标 | 全称 | 公式 | 含义 |
|---|---|---|---|
| TF | Term Frequency | TF=某词在文档中出现的次数文档总词数TF = \frac{某词在文档中出现的次数}{文档总词数}TF=文档总词数某词在文档中出现的次数 | 词在当前文档中的频率 |
| IDF | Inverse Document Frequency | IDF=log(文档总数包含该词的文档数+1)IDF = \log\left(\frac{文档总数}{包含该词的文档数 + 1}\right)IDF=log(包含该词的文档数+1文档总数) | 词在整个语料库中的稀有程度 |
最终得分:
TF-IDF=TF×IDFTF\text{-}IDF = TF \times IDFTF-IDF=TF×IDF
直观理解
- TF 高 → 这个词在本文档中频繁出现
- IDF 高 → 这个词在其他文档中很少出现(如专有名词)
- 两者相乘 → 过滤掉"的、是、了"等高频无意义词,保留有区分度的关键词
简单示例
假设有 3 篇文档:
- 文档1:「苹果手机很好用」
- 文档2:「这个苹果很甜」
- 文档3:「手机价格便宜」
词 "苹果" 的 TF-IDF:
- 在文档1中:TF 较高,但 3 篇中 2 篇有 "苹果",IDF 偏低 → TF-IDF 中等
- 词 "手机" 在文档1中:TF 较高,3 篇中 2 篇有 "手机",IDF 偏低 → TF-IDF 中等
- 词 "便宜" 在文档3中:TF 较高,3 篇中仅 1 篇有,IDF 高 → TF-IDF 高,区分力强
主要用途
| 场景 | 说明 |
|---|---|
| 文本关键词提取 | 找出每篇文档最具代表性的词 |
| 搜索引擎 | 根据查询词与文档的 TF-IDF 相似度排序 |
| 文本分类/聚类 | 将文档转为 TF-IDF 向量作为特征输入模型 |
| 推荐系统 | 计算物品描述文本的相似度 |