简介这份资源是面向计算机相关专业学生与项目实战学习者的中文情感分析系统完整资料包可作为毕业设计、课程设计、期末大作业或立项演示的参考方案帮助解决酒店评论场景下文本预处理、特征提取与情感倾向判别的实现问题。压缩包共约2000个文件以txt文本为主包含正负样本评论语料与停用词表另有1个Python脚本承担建模与分类任务整体约1.77MB结构清晰便于按模块查阅。目前已有360人学习下载说明该方向具备一定关注度。读者可从中获得可运行的源码、设计文档与配套数据集理解从分词、去停用词到PCA降维与SVM分类的完整流程并据此修改扩展功能用于自身课题或作业提交。1. 酒店评论情感分析从一堆中文差评里挖出可落地的改进信号酒店运营每天面对成百上千条评论靠人工逐条读读到第一百条时脑子已经木了更别提从里面提炼出“隔音差”“空调吵”“前台慢”这些具体问题。基于 Python 的酒店评论中文情感分析系统要解决的就是这件事把非结构化的中文评论自动分成正面、负面再进一步定位负面评论集中在哪些维度。它适合两类人——一类是想拿一套完整源码和数据集练手的中文 NLP 入门者另一类是真的在酒店或 OTA 平台做运营、需要批量处理评论的从业者。这套东西的核心链路并不复杂中文分词、去停用词、特征提取、模型训练、情感判别、结果可视化。但中文评论有它自己的脾气口语化、反讽、缩写、错别字混在一起直接套英文那套流程大概率翻车。下面按“数据怎么准备、模型怎么选、代码怎么写、坑在哪”的顺序把这条链路拆开讲清楚。2. 中文酒店评论的数据集长什么样字段、标注与清洗边界2.1 一份能用的评论数据集该有哪些字段酒店评论数据集通常以 CSV 或 Excel 形式提供核心字段包括评论正文、评分、评论时间、房型、入住类型等。情感分析真正依赖的是评论正文和评分两列。评分是天然的弱标签4 到 5 分视为正面1 到 2 分视为负面3 分中性样本在二分类任务里一般直接丢弃否则模型会在边界上反复摇摆。常见做法是把评分映射成 0/1 标签0 代表负面1 代表正面。拿到数据集后先做一轮字段体检重点看三件事评论正文的空值比例、评分分布是否严重倾斜、有没有重复评论。酒店评论天然偏正面很多数据集正面样本占七成以上直接训练会让模型倾向于把所有评论判成正面。处理办法后面会讲这里先记住这个分布问题。import pandas as pd # 读取评论数据集注意编码中文 CSV 常见 utf-8 或 gbk df pd.read_csv(hotel_reviews.csv, encodingutf-8) # 只保留情感分析需要的列 df df[[content, score]].dropna() # 评分映射为二分类标签1-2 分负面4-5 分正面3 分丢弃 df df[df[score] ! 3] df[label] df[score].apply(lambda x: 0 if x 2 else 1) print(df[label].value_counts()) print(df.head())这段代码做了三件事选列、去空值、按评分生成标签。dropna()不加参数的默认行为是只要该行有任意空值就删如果只想针对评论正文去空要写成dropna(subset[content])。score ! 3这一步是在过滤中性样本如果你的数据集评分是 1 到 10 分制阈值要相应调整比如 1 到 4 分负面、7 到 10 分正面。打印value_counts()是为了确认正负样本比例后面决定要不要做重采样。2.2 中文清洗比英文多出来的那几步中文评论清洗和英文最大的区别在于英文按空格切词就行中文必须先分词。但分词之前还有几件事要做。第一是去除 HTML 标签和特殊符号很多从网页抓下来的评论带着br、nbsp;这类残留。第二是处理全角半角混用中文标点和英文标点混在一起会让后续正则匹配变得麻烦。第三是繁简转换部分平台评论里夹杂繁体字不统一会影响词表覆盖。import re from opencc import OpenCC cc OpenCC(t2s) # 繁体转简体 def clean_text(text): text re.sub(r.*?, , text) # 去 HTML 标签 text re.sub(r[a-zA-Z];, , text) # 去 HTML 实体 text cc.convert(text) # 繁转简 text re.sub(r\s, , text) # 合并空白 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9。、], , text) # 只留中文英文数字和常用标点 return text.strip() df[clean] df[content].apply(clean_text)OpenCC需要单独安装pip install opencc-python-reimplemented即可。正则[^\u4e00-\u9fa5a-zA-Z0-9。、]的含义是保留中文汉字、英文字母、数字和几个常用中文标点其余全部删掉。这一步会误伤一些表情符号和网络用语但酒店评论里表情符号对情感判断的贡献有限删掉反而降低噪声。如果你的数据里表情符号很多且确实携带情感可以单独建一个表情映射表把常见表情转成“正面”“负面”文字再拼回评论。清洗完还要去停用词。中文停用词表网上有很多版本但通用停用词表用在酒店评论上会有一个问题它会把“不”“没”“差”这类否定词也去掉而否定词恰恰是情感分析的关键。我一般会加载通用停用词表后手动把否定词和程度副词从表里剔除保证“不干净”“非常差”这类表达不被破坏。3. 用 jieba 分词加 TF-IDF 跑通第一个情感分类基线3.1 分词、去停用词与词向量构建中文情感分析的第一版基线不需要上深度学习jieba 分词加 TF-IDF 加逻辑回归就能跑出一个可用的结果而且训练快、可解释性强适合先验证数据质量。jieba 支持自定义词典酒店领域有很多专有词比如“大床房”“行政酒廊”“钟点房”默认词典可能切错加一个自定义词典能明显改善分词质量。import jieba import jieba.analyse from sklearn.feature_extraction.text import TfidfVectorizer # 加载自定义词典每行一个词可带词频和词性 jieba.load_userdict(hotel_dict.txt) # 加载停用词表并剔除否定词和程度副词 stopwords set(open(stopwords.txt, encodingutf-8).read().split()) keep_words {不, 没, 很, 太, 非常, 特别, 有点, 比较} stopwords stopwords - keep_words def cut_words(text): words jieba.lcut(text) return .join([w for w in words if w not in stopwords and len(w) 1]) df[cut] df[clean].apply(cut_words) # TF-IDF 向量化最多保留 5000 个特征词 tfidf TfidfVectorizer(max_features5000, ngram_range(1, 2)) X tfidf.fit_transform(df[cut]) y df[label].valueslen(w) 1是为了过滤单字中文单字携带的信息量通常不够但“差”“脏”“吵”这类单字其实很有判别力所以更稳妥的做法是保留一个单字白名单而不是一刀切。ngram_range(1, 2)表示同时考虑单个词和相邻两个词的组合能捕捉“不干净”“隔音差”这类二元表达代价是特征维度上升训练变慢。5000 这个数字不是固定的数据量大可以调到 10000数据量小调到 2000 到 3000 更合适。3.2 逻辑回归基线训练与评估指标怎么看基线模型选逻辑回归原因是它训练快、系数可解释能直接看出哪些词对正面或负面贡献最大。评估不能只看准确率正负样本不均衡时准确率会骗人。要看精确率、召回率和 F1尤其是负面类的召回率——漏掉一条差评的代价通常比误判一条好评大。from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) clf LogisticRegression(max_iter1000, class_weightbalanced) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(classification_report(y_test, y_pred, target_names[负面, 正面]))class_weightbalanced是应对样本不均衡的关键参数它会让模型在训练时自动给少数类更高的权重。stratifyy保证训练集和测试集的正负比例一致避免随机切分导致某一类在测试集里几乎消失。max_iter1000是因为 TF-IDF 特征维度高默认的 100 次迭代可能不收敛会报收敛警告。看报告时重点关注负面类的 recall如果低于 0.7说明模型漏掉了大量差评需要回头检查数据清洗或调整分类阈值。4. 从 TF-IDF 升级到深度学习什么情况下值得换4.1 什么时候 TF-IDF 不够用TF-IDF 加逻辑回归在酒店评论上通常能跑到 85% 到 90% 的准确率但有几类情况它会明显吃力。一是反讽和复杂否定比如“真是‘干净’得让我不敢住”TF-IDF 看到“干净”会判正面但实际是负面。二是长评论里的转折前半段夸后半段骂词袋模型把整条评论的词混在一起无法区分转折关系。三是新词和网络用语TF-IDF 依赖训练集词表没见过的词直接忽略。如果你的数据里这类情况占比不高TF-IDF 完全够用没必要为了深度学习而深度学习。但如果负面评论里大量出现反讽和转折换模型才有意义。4.2 用 TextCNN 或 BERT 中文预训练模型做升级升级路线有两条。轻量路线是 TextCNN用预训练词向量加卷积核提取局部 n-gram 特征训练速度快比 TF-IDF 能更好地捕捉词序信息。重量路线是 BERT 中文预训练模型直接微调下游分类任务效果通常最好但需要 GPU训练时间长。# TextCNN 核心结构示意基于 PyTorch import torch import torch.nn as nn class TextCNN(nn.Module): def __init__(self, vocab_size, embed_dim, num_classes, filter_sizes, num_filters): super().__init__() self.embedding nn.Embedding(vocab_size, embed_dim, padding_idx0) self.convs nn.ModuleList([ nn.Conv2d(1, num_filters, (fs, embed_dim)) for fs in filter_sizes ]) self.dropout nn.Dropout(0.5) self.fc nn.Linear(num_filters * len(filter_sizes), num_classes) def forward(self, x): x self.embedding(x) # [batch, seq_len, embed_dim] x x.unsqueeze(1) # [batch, 1, seq_len, embed_dim] x [torch.relu(conv(x)).squeeze(3) for conv in self.convs] x [torch.max_pool1d(f, f.size(2)).squeeze(2) for f in x] x torch.cat(x, dim1) x self.dropout(x) return self.fc(x)filter_sizes一般取 2、3、4对应二元、三元、四元 n-gram 的局部特征。num_filters每个尺寸取 128 或 256。padding_idx0告诉模型 0 是填充位不参与梯度更新。TextCNN 的输入需要先把文本转成整数序列用 Keras 的 Tokenizer 或 torchtext 都能做词表大小建议控制在 20000 以内超出部分截断或映射为未知词。如果选 BERT 路线用 HuggingFace 的 transformers 库加载bert-base-chinese把分类层换成二分类输出学习率设到 2e-5 到 5e-5训练 3 到 5 个 epoch 通常就能收敛。BERT 的坑在于输入长度限制 512 个 token长评论要截断截断策略建议保留头部和尾部丢掉中间因为酒店评论的结论往往在开头或结尾。5. 避坑与排查中文情感分析里最容易翻车的五个地方5.1 现象模型准确率很高但负面评论全漏了原因正负样本严重不均衡正面占八成以上模型学会了“全猜正面”就能拿到高准确率。解决训练时加class_weightbalanced或者对负面样本做上采样用imblearn的RandomOverSampler把负面样本复制到和正面相当。评估时盯住负面类的 recall不要只看 accuracy。5.2 现象分词把“不干净”切成“不”和“干净”情感判反原因jieba 默认词典没有把否定词和形容词绑定停用词表又把“不”去掉了。解决把否定词从停用词表里剔除同时在自定义词典里加入“不干净”“不推荐”“不隔音”这类高频否定短语让 jieba 把它们当成一个词切出来。5.3 现象训练集效果好测试集一塌糊涂原因数据泄漏。常见于先对全量数据做 TF-IDF 再切分训练测试集导致测试集的词表信息泄漏到训练阶段。解决先切分训练集和测试集只用训练集fit向量化器再用同一个向量化器transform测试集。代码顺序不能反。5.4 现象BERT 微调后效果还不如 TF-IDF原因学习率设太大导致预训练权重被破坏或者训练轮数太多导致过拟合。解决学习率降到 2e-5加 warmup训练 3 个 epoch 就早停。另外检查一下 tokenizer 是不是用的中文预训练模型对应的版本用错 tokenizer 会让输入完全错位。5.5 现象新评论里出现训练集没见过的词模型直接忽略原因TF-IDF 词表固定未知词在向量化时被丢弃。解决TfidfVectorizer里没有直接处理未知词的参数但可以在清洗阶段做同义词归一化把“隔音不好”“隔音差”“不隔音”统一映射成“隔音差”减少词表外表达。或者换用基于字符的 n-gramanalyzerchar对未登录词更鲁棒。6. 把情感分析结果落到运营动作上维度拆解与可视化技巧模型输出 0 或 1 只是第一步运营真正需要的是“负面评论集中在哪些维度”。做法是在情感分类之后对负面评论再做一次关键词提取或主题聚类。jieba 的analyse.extract_tags可以按 TF-IDF 权重提取每条负面评论的关键词把所有负面评论的关键词汇总后做词频统计就能看出“隔音”“空调”“前台”“卫生”哪些维度被提及最多。import jieba.analyse from collections import Counter negative_reviews df[df[label] 0][clean].tolist() all_keywords [] for review in negative_reviews: # 每条评论提取 top5 关键词 keywords jieba.analyse.extract_tags(review, topK5) all_keywords.extend(keywords) counter Counter(all_keywords) for word, freq in counter.most_common(20): print(word, freq)extract_tags默认用 TF-IDF 权重排序topK5表示每条评论取前 5 个关键词。汇总后most_common(20)给出负面评论里最高频的 20 个词。这一步的坑在于高频词里会出现“酒店”“房间”这类无区分度的通用词需要在停用词表里提前把这些领域通用词加进去否则统计结果会被它们占满。我一般会单独维护一个酒店领域停用词表把“酒店”“房间”“入住”“客人”这类词过滤掉只留下真正指向问题的词。可视化方面词云是最直观的但词云不适合精确对比。更好的做法是用横向条形图展示 top20 负面关键词的词频再用饼图展示负面评论的维度占比。如果要做时间趋势把评论时间按周或按月聚合画出负面评论占比的折线图能看出某段时间是不是因为装修、换季空调问题导致差评集中爆发。最后说一个我自己的习惯每次跑完模型不要只看总体指标一定手动抽 20 条预测错误的样本逐条读。中文酒店评论里模型犯错的地方往往高度集中——要么是反讽要么是转折要么是短文本信息不足。读错例比调参更能告诉你下一步该改数据还是改模型。这套源码和数据集值得动手跑一遍跑通之后你对中文情感分析的理解会比看十篇教程都扎实。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?