前言

        在电商、本地生活、内容平台等场景中,每天都会产生海量的用户评价文本。人工逐条区分好评 / 差评、挖掘用户痛点效率极低,而通过 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)实现更精准的情感分析,适配更复杂的语义场景

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐