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

Python 实战:基于朴素贝叶斯的中文评价情感分析(好评 / 差评自动识别)| 附完整可运行代码

前言

在电商、本地生活、内容平台等场景中,每天都会产生海量的用户评价文本。人工逐条区分好评 / 差评、挖掘用户痛点效率极低,而通过 NLP + 机器学习技术实现评价情感倾向自动分类,可以帮助商家快速定位产品问题、优化服务体验,也能为平台运营提供数据支撑。

本文将从 0 到 1 完整实现一套中文评价情感分析系统:从原始文本数据预处理、中文分词、停用词过滤,到词向量转换、朴素贝叶斯模型训练,再到解决数据不平衡问题、最终实现用户输入任意评价自动判断好评 / 差评,全程附带代码解析、踩坑记录和真实运行效果,新手也能直接复现。数据集的准备请移步博客文章《开箱即用!商品评价爬虫实战,好评差评数据直接拿》。

一、项目整体效果与技术选型

1. 最终实现效果

  1. 支持本地优质评价 / 差评文本批量训练模型
  2. 自动完成中文文本分词、停用词过滤、词向量转换全流程
  3. 针对评价数据常见的类别不平衡问题,通过 SMOTE 过采样优化模型效果
  4. 提供交互式输入功能:输入任意评价文本,实时输出「优质评价」/「差评」的判断结果
  5. 完整的模型评估报告,精准查看模型在训练集 / 测试集的分类效果

2. 核心技术栈

工具 / 库核心用途
jieba中文文本分词,解决中文无天然分词符的核心问题
pandas文本数据读取、结构化处理与数据集整合
scikit-learn数据集切分、词向量转换、朴素贝叶斯模型构建、分类效果评估
imblearn(SMOTE)过采样处理,解决好评 / 差评样本数量严重不平衡的问题
MultinomialNB多项式朴素贝叶斯模型,专为文本分类这类离散特征场景优化

3. 环境准备

执行以下命令一键安装所有依赖:

pip install pandas jieba scikit-learn imbalanced-learn

二、数据准备与文本预处理

1. 数据源说明

本文使用 3 个核心数据文件,和代码放在同一目录下即可:

  • 优质评价.txt:每行一条用户好评文本,作为正样本
  • 差评.txt:每行一条用户差评文本,作为负样本
  • stopword.txt:中文停用词表,包含「的、了、啊、吧」等无情感意义的语气词、连接词,用于过滤噪声

2. 文本读取与中文分词

中文和英文最大的区别是没有天然的空格分隔词,因此第一步必须用分词工具将句子拆分为独立的词语。这里选用最主流的jieba分词库,使用精确模式lcut完成分词。

import pandas as pd import jieba # 读取评价文件 cp_content = open(r'差评.txt', encoding='utf-8') yz_content = open(r'优质评价.txt', encoding='utf-8') # 对差评文本逐行分词 cp_sum = [] for line in cp_content: result = jieba.lcut(line) # 过滤空行和分词后仅1个词的无意义内容 if len(result) > 1: cp_sum.append(result) # 对优质评价文本逐行分词 yz_sum = [] for line in yz_content: yo = jieba.lcut(line) if len(yo) > 1: yz_sum.append(yo) # 关闭文件,避免资源泄露(新手易踩坑点) cp_content.close() yz_content.close()

3. 停用词过滤

分词后的结果里包含大量无情感意义的停用词,会干扰模型的特征学习,因此需要过滤掉;同时补充过滤长度≤1 的单字,进一步降低噪声。

# 读取停用词表,转为列表格式(关键修正:直接用DataFrame会导致in判断失效) stopwords = pd.read_csv(r'stopword.txt', encoding='utf-8', engine='python') stopwords_list = stopwords['stopword'].values.tolist() # 定义停用词过滤函数 def stop(contests, stopwords): seg_clear = [] for contest in contests: line_clear = [] for word in contest: # 核心过滤规则:停用词 + 长度≤1的无意义词 if word in stopwords or len(word) <= 1: continue line_clear.append(word) seg_clear.append(line_clear) return seg_clear # 对分词结果执行停用词过滤 cp_fc_results_clear = stop(cp_sum, stopwords_list) yz_fc_results_clear = stop(yz_sum, stopwords_list)

4. 标签标注与数据集合并

监督学习需要为样本标注标签,这里我们定义:优质评价(好评):标签为 0,差评:标签为 1,将两类样本合并为完整的训练数据集,方便后续切分和训练。

# 为样本标注标签 cp_train = pd.DataFrame({'pj':cp_fc_results_clear,'lable':1}) yp_train = pd.DataFrame({'pj':yz_fc_results_clear,'lable':0}) # 纵向合并两个数据集,得到完整的标注数据集 pj_train = pd.concat([cp_train,yp_train])

三、数据集切分与词向量转换

1. 训练集 / 测试集切分

将数据集按 8:2 的比例切分为训练集和测试集。训练集:用于模型学习特征和拟合。测试集:用于验证模型的泛化能力,避免过拟合

固定random_state保证每次运行的切分结果一致,方便复现效果。

from sklearn.model_selection import train_test_split x_train, x_test, y_train, y_test = \ train_test_split( pj_train['pj'].values, pj_train['lable'].values, train_size=0.8, random_state=0 )

2. 文本转词向量(核心步骤)

计算机无法直接处理文本字符串,必须将其转为数值型的向量,这一步就是文本向量化。本文使用CountVectorizer词频向量化,核心逻辑是:基于训练集构建高频词库,将每条文本转为「词频向量」,向量的每个维度对应词库中的一个词,值为该词在文本中出现的次数。

# 格式化文本:将分词列表转为空格分隔的字符串,适配CountVectorizer的输入格式 words = [] for line_index in range(len(x_train)): words.append(' '.join(x_train[line_index])) # 导入词向量转换库 from sklearn.feature_extraction.text import CountVectorizer # 初始化词向量转换器 vec = CountVectorizer( max_features=1700, # 只保留词频最高的1700个词作为词库,降低维度避免过拟合 lowercase=False, # 中文无需转为小写,关闭该功能 ngram_range=(1,3) # 同时考虑1-3个词的组合,捕捉「物流慢、质量差」这类短语语义 ) # 核心步骤:fit基于训练集构建词库,transform将文本转为词频向量 vec.fit(words) # 仅用训练集构建词库,避免数据泄露 x_train_vec = vec.transform(words) # 训练集转为词向量

注!!!

新手必看踩坑点:测试集只能用vec.transform(),绝对不能用fit_transform()否则会用测试集重新构建词库,导致数据泄露,模型评估结果失真。

四、解决核心痛点:类别不平衡问题

从我的运行结果可以看到,电商评价数据存在严重的类别不平衡

  • 训练集中,优质评价(标签 0)有 2911 条,差评(标签 1)仅 15 条
  • 测试集中,优质评价 725 条,差评仅 7 条

如果直接用不平衡的数据集训练,模型会严重偏向多数类(好评),哪怕全部预测为好评,准确率也能达到 99%,但完全无法识别差评,失去了业务价值。

这里我们用SMOTE 过采样解决该问题:通过算法合成少数类(差评)的样本,让训练集中两类样本的数量达到平衡,提升模型对差评的识别能力。

# 导入SMOTE过采样器 from imblearn.over_sampling import SMOTE # 初始化SMOTE,固定随机种子保证结果可复现 smote = SMOTE(random_state=42) # 仅对训练集执行过采样,测试集保持原始分布 x_train_smote, y_train_smote = smote.fit_resample(x_train_vec, y_train)

五、朴素贝叶斯模型训练与效果评估

1. 模型训练

文本分类场景中,多项式朴素贝叶斯MultinomialNB是最经典、高效的算法,对离散的词频特征适配性极强,训练速度快,泛化效果好。

# 导入朴素贝叶斯模型 from sklearn.naive_bayes import MultinomialNB # 初始化模型,alpha=1为拉普拉斯平滑,避免出现零概率问题 classifier = MultinomialNB(alpha=1) # 用平衡后的训练集训练模型 classifier.fit(x_train_smote, y_train_smote)

2. 模型效果评估

使用classification_report输出完整的分类报告,重点关注精准率、召回率、F1-score,不要只看整体准确率(不平衡数据中准确率无参考意义)。

from sklearn import metrics # 训练集效果验证 train_pr = classifier.predict(x_train_vec) print("训练集分类报告(原始数据预测):") print(metrics.classification_report(y_train,train_pr)) # 测试集效果验证(核心看泛化能力) test_words = [] for line_index in range(len(x_test)): test_words.append(' '.join(x_test[line_index])) test_pr = classifier.predict(vec.transform(test_words)) print("测试集分类报告:") print(metrics.classification_report(y_test,test_pr))

真实运行结果展示

从运行结果可以看到:

  • 模型整体准确率达到 99%,对优质评价的识别效果极佳
  • 经过 SMOTE 过采样后,模型对差评的召回率达到 71%,解决了不平衡数据下少数类完全无法识别的问题
  • 测试集泛化效果稳定
  • 由于差评数据较少,即使通过过拟合,结果仍然不是很理想。可以通过继续爬取差评数据来增加差评的真实数据。增强差评的训练能力

六、交互式预测:输入评价自动判断好评 / 差评

封装预测函数,实现和训练集完全一致的预处理流程,保证预测结果的准确性,同时增加边界处理,避免无效输入导致报错。

def predict_sentiment(text): # 1. 对输入文本执行jieba分词 seg = jieba.lcut(text) # 2. 执行停用词过滤,和训练集预处理规则保持一致 clear_seg = stop([seg], stopwords_list)[0] # 边界处理:过滤后无有效词汇,返回提示信息 if not clear_seg: return "无法判断(输入文本过短或无有效词汇)" # 3. 转为词向量,使用训练集构建的词库 text_vec = vec.transform([' '.join(clear_seg)]) # 4. 模型预测 pred = classifier.predict(text_vec)[0] # 5. 返回可读性强的结果 return "优质评价(好)" if pred == 0 else "差评(坏)" # 程序入口:用户交互式输入 if __name__ == "__main__": user_input = input("请输入要判断的评价:") result = predict_sentiment(user_input) print(f"判断结果:{result}")

七、新手必看踩坑记录

  1. 停用词判断失效:读取停用词后直接用 DataFrame,没有转为列表,导致word in stopwords判断完全失效,必须用.values.tolist()转为列表格式
  2. 数据泄露问题:测试集使用fit_transform()重新构建词库、在全数据集执行过采样,都会导致模型评估结果虚高,正确做法是仅用训练集 fit / 过采样
  3. 文件资源泄露:用 open 打开文件后没有 close,长时间运行会导致句柄泄露,推荐用with open()上下文管理器自动管理文件
  4. 预处理流程不一致:预测单条文本时,没有执行和训练集完全一致的分词、停用词过滤规则,导致预测结果出错
  5. 只看准确率:不平衡数据中,哪怕全部预测为多数类,准确率也能达到 99%,必须重点关注少数类的 F1-score 和召回率

八、完整可运行代码

import pandas as pd import jieba # 读取评价文件 cp_content = open(r'差评.txt', encoding='utf-8') yz_content = open(r'优质评价.txt', encoding='utf-8') # 对差评分词 cp_sum = [] for line in cp_content: result = jieba.lcut(line) if len(result) > 1: cp_sum.append(result) cp_fc_results = pd.DataFrame({'chaping': cp_sum}) # 对优质评价分词 yz_sum = [] for line in yz_content: yo = jieba.lcut(line) if len(yo) > 1: yz_sum.append(yo) yz_fc_results = pd.DataFrame({'content': yz_sum}) # 导入停用词(关键修正:读取后转为列表) stopwords = pd.read_csv(r'stopword.txt', encoding='utf-8', engine='python') stopwords_list = stopwords['stopword'].values.tolist() # 提取停用词列表 # 定义去除停用词(关键修正:判断词是否在停用词列表中) def stop(contests, stopwords): seg_clear = [] for contest in contests: line_clear = [] for word in contest: # 过滤停用词 + 过滤长度为1的无意义词(可选优化,不影响核心功能) if word in stopwords or len(word) <= 1: continue line_clear.append(word) seg_clear.append(line_clear) return seg_clear # 去除停用词(传入停用词列表) cp_fc_results_clear = stop(cp_sum, stopwords_list) yz_fc_results_clear = stop(yz_sum, stopwords_list) # 关闭文件(原代码遗漏,补充关闭避免资源泄露) cp_content.close() yz_content.close() '''朴素贝叶斯分类''' '''标签分类''' cp_train = pd.DataFrame({'pj':cp_fc_results_clear,'lable':1})#表格数据 yp_train = pd.DataFrame({'pj':yz_fc_results_clear,'lable':0}) pj_train = pd.concat([cp_train,yp_train]) '''数据切分''' from sklearn.model_selection import train_test_split x_train, x_test, y_train, y_test = \ train_test_split(pj_train['pj'].values,pj_train['lable'].values,train_size=0.8,random_state=0) '''将所有词转化为词向量''' words = [] #将转化的词向量为标准格式 for line_index in range(len(x_train)): words.append(' '.join(x_train[line_index])) print(words) #导入词向量转化库 from sklearn.feature_extraction.text import CountVectorizer vec = CountVectorizer(max_features=1700,lowercase=False,ngram_range=(1,3)) #max_features表示提取词频最高的1700个作为词库 #lowercase表示把所有词转化为小写,False不需要 vec.fit(words) #传入训练集的所有词 ##fit_transform()2个功能 1:训练出词库 2:transform根据词库转化为词向量 x_train_vec = vec.transform(words) # 添加过采样处理(SMOTE) from imblearn.over_sampling import SMOTE # 初始化SMOTE过采样器 smote = SMOTE(random_state=42) # 对训练集进行过采样 x_train_smote, y_train_smote = smote.fit_resample(x_train_vec, y_train) """使用贝叶斯""" from sklearn.naive_bayes import MultinomialNB,ComplementNB classifier = MultinomialNB(alpha=1) # 使用过采样后的数据集进行训练 classifier.fit(x_train_smote, y_train_smote) train_pr = classifier.predict(x_train_vec) # 用原始训练集向量预测(对比用) from sklearn import metrics print("训练集分类报告(原始数据预测):") print(metrics.classification_report(y_train,train_pr)) # 测试集数据分析测试 test_words = [] for line_index in range(len(x_test)): test_words.append(' '.join(x_test[line_index])) test_pr = classifier.predict(vec.transform(test_words)) print("测试集分类报告:") print(metrics.classification_report(y_test,test_pr)) '''用户输入预测部分''' def predict_sentiment(text): # 对输入文本分词 seg = jieba.lcut(text) # 去除停用词和长度为1的词 clear_seg = stop([seg], stopwords_list)[0] # 传入列表的列表,取第一个结果 if not clear_seg: # 若处理后无有效词汇 return "无法判断(输入文本过短或无有效词汇)" # 转换为词向量格式 text_vec = vec.transform([' '.join(clear_seg)]) # 预测 pred = classifier.predict(text_vec)[0] return "优质评价(好)" if pred == 0 else "差评(坏)" # 用户交互 if __name__ == "__main__": user_input = input("请输入要判断的评价:") result = predict_sentiment(user_input) print(f"判断结果:{result}")

九、总结与拓展优化方向

本文完整实现了从原始评价文本到自动情感分类的全流程,解决了中文分词、文本向量化、数据不平衡等核心问题,最终实现了用户输入任意评价自动判断好评 / 差评的功能,可直接落地到电商评价分析、客服消息情感识别等场景。

后续可拓展优化的方向:

  1. 数据优化:继续爬取差评,增加真实差评数量。进而增加差评训练能力
  2. 词向量优化:用TfidfVectorizer替代CountVectorizer,给有区分度的词更高权重,进一步提升分类效果
  3. 模型优化:使用专为不平衡文本分类设计的ComplementNB模型,或尝试 SVM、随机森林、XGBoost 等算法
  4. 功能拓展:批量读取 Excel/CSV 中的评价数据,批量完成分类并输出结果文件,生成词云可视化
  5. 效果升级:使用预训练语言模型(如 BERT、RoBERTa)实现更精准的情感分析,适配更复杂的语义场景
http://www.cnnetsun.cn/news/1333365.html

相关文章:

  • 一文详解Diffusion Policy
  • C++11中智能指针:shared_ptr的引用计数是线程安全的吗?
  • 不懂代码,我用AI编程给5岁女儿开发了个流光画板(带你一步一步设计一个属于自己的流光画板)
  • VScode快捷键
  • 小白从零开始勇闯人工智能:LangChain 入门指南(下)
  • AI时代的教育“外包”:中国家长将作业辅导交给机器
  • 2026年课程论文降AI率工具推荐:便宜好用才是硬道理
  • 软件系统安全赛初赛misc题-steganography wp
  • 东方仙盟・神识共创共生,智启万象—架构思路—未来之窗行业应用跨平台架构
  • linux-安装配置jdk mysql redis elasticsearch
  • Python 之程序截图的几种方式(含chromedriver下载链接)
  • android studio项目 gradle-xx-bin.zip下载失败或很慢的解决方法
  • AperiSolve 开源项目教程
  • 企业决策视角下微服务全链路性能瓶颈分析平台对比及实践指南
  • 【C语言】程序环境与预处理
  • SCUT_thesis项目:解决长章名换行后不居中的排版问题
  • PowerPlatformConnectors三大类型深度对比:自定义、认证与独立发布者连接器怎么选?
  • Pleaserun vs 手动编写init脚本:效率提升10倍的秘密
  • dbblog部署教程:Docker容器化部署与服务器配置全流程
  • AniVu BitTorrent下载功能深度测评:速度与稳定性全面测试
  • DC-TTS语音合成效果对比:LJ Speech与KSS数据集实测
  • 提升Haskell开发效率:ghcid高级功能与实用技巧
  • Saasify:让API变现从未如此简单!一站式实现API商业化的终极指南
  • Ikemen-GO开发者指南:用Go语言构建自定义格斗游戏引擎
  • GoMLX未来路线图:即将发布的5大令人期待的功能
  • lidar_camera_calib常见问题排查:从数据准备到结果验证的全流程避坑指南
  • 如何用tinygrad实现高效目标检测:YOLO和RetinaNet完整指南
  • 如何用tinygrad实现高效全局优化:从模拟退火到能量最小化的完整指南
  • 提升深度学习模型性能:tinygrad图像数据增强完全指南
  • 如何利用tinygrad数据流水线实现高效数据加载和预处理:从理论到实践