酒店评论情感分析Python方案:从数据清洗到模型评估的完整指南

📅 发布时间:2026/10/1 10:36:30
酒店评论情感分析Python方案:从数据清洗到模型评估的完整指南
简介这是一套面向高校学生与Python初学者的酒店评论情感分析完整项目资料适用于期末大作业、课程设计或NLP入门实战。项目以中文酒店评论文本为对象结合情感词典与打分算法实现情感倾向判定难度适中适合具备Python基础、希望快速完成一份高质量作业的读者。压缩包共23个文件约4.36MB包含2个py源码文件、14个txt词典与停用词表、2个rar语料压缩包以及docx说明文档、pptx演示文稿、md说明和jpg效果图覆盖代码、数据、文档与展示全流程。目前已有157人学习关注。资料中提供了完整可运行的情感打分脚本与入口程序配套哈工大、四川大学等多份停用词表及正向、负向、程度副词、否定词等情感词典还附有酒店评论语料与词云图方便读者直接复现实验、理解词典匹配与分值计算逻辑并据此撰写实验报告与答辩材料。1. 酒店评论情感分析一份能跑通、能写进期末大作业的 Python 方案期末大作业最怕两件事一是选题听着唬人真动手发现数据拿不到二是代码跑起来了报告里却讲不清为什么这么做。酒店评论情感分析恰好卡在中间——数据好找、任务直观、技术栈全是 Python 生态里的熟面孔但真要做出「高分」的样子光调个SnowNLP打印一句「正面」是远远不够的。我带过几届学生的课程设计也自己做过电商评论的落地项目血泪经验是决定分数的从来不是模型多花哨而是数据清洗、标签定义、评估口径这三件事讲没讲透。这篇笔记就按「一份能交差的完整方案」来拆从环境配置、数据获取、中文分词到模型训练、可视化、报告撰写每一步都给可抄的代码和参数说明。适合正在找 Python 课程设计案例源码、想拿情感分析当大作业、或者刚入门 NLP 想找个完整项目练手的人。读完你至少能得到一个能复现的基线系统以及一套知道坑在哪的排错思路。2. 数据从哪来酒店评论的获取、清洗与标签定义2.1 三条数据来源路线先选对再动手做酒店评论情感分析第一步不是写模型是搞数据。我一般把来源分成三档按「省事程度」排序路线具体做法数据量级适合场景公开数据集电商/酒店评论语料如 ChnSentiCorp 类中文情感语料几千到几万条只想跑通流程、赶时间爬虫采集requests BeautifulSoup 抓公开评论页取决于页数想体现「数据获取」工作量模拟/半真实手动整理 模板扩充几百条数据源受限时的兜底期末大作业我通常建议走「公开数据集打底 少量爬虫补充」的组合公开语料保证模型能训起来爬虫部分写进报告体现工程能力。注意爬虫只抓公开可见的评论文本遵守目标站点的 robots 协议控制请求频率别把人家服务器打挂——这不是技术问题是基本素养。2.2 用 pandas 做第一轮清洗去重、去噪、去空拿到原始评论后别急着分词。中文评论里混着表情符号、URL、连续空格、纯数字、广告刷屏这些不处理后面分词和向量化全是噪声。下面这段是我常用的清洗模板import pandas as pd import re def clean_text(text): if not isinstance(text, str): return # 去掉 URL text re.sub(rhttp[s]?://\S, , text) # 去掉 某人 和 #话题# text re.sub(r[\w\u4e00-\u9fa5], , text) text re.sub(r#.*?#, , text) # 只保留中文、英文、数字和基本标点 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9。、], , text) # 压缩连续空白 text re.sub(r\s, , text).strip() return text df pd.read_csv(hotel_comments.csv) df[clean] df[content].apply(clean_text) # 去掉清洗后过短或为空的 df df[df[clean].str.len() 5] # 按清洗后文本去重避免刷屏评论主导训练 df df.drop_duplicates(subset[clean]) print(df.shape)逻辑说明clean_text先干掉 URL 和社交符号再用白名单正则保留有效字符最后压缩空白。drop_duplicates那一步很关键——酒店评论里「好评」「不错」这种短句重复率极高不去重会让模型偏向高频词。参数上str.len() 5这个阈值可以按数据分布调如果语料普遍很短降到 3 也行但别低于 2否则「好」「差」这种单词会污染标签。2.3 标签怎么定二分类、三分类还是星级映射这是最容易被忽略、却最影响结论的一步。常见做法有三种二分类正面 / 负面。适合入门标签干净但丢掉中性评论。三分类正面 / 中性 / 负面。更贴近真实但中性样本边界模糊标注一致性差。星级映射4-5 星记正面1-2 星记负面3 星丢弃或归中性。我一般推荐期末作业用「星级映射 二分类」规则明确、可复现、报告里好解释。如果原始数据只有文本没有星级那就得靠人工标注或弱监督比如用情感词典先打一版再抽样校对。这里有个坑别用模型预测结果当标签再训模型那是自我循环评估数字会虚高得离谱答辩时一问就露馅。# 假设原始数据有 star 字段 def star_to_label(star): if star 4: return 1 # 正面 elif star 2: return 0 # 负面 else: return -1 # 中性先标记后丢弃 df[label] df[star].apply(star_to_label) df df[df[label] ! -1] print(df[label].value_counts())跑完记得看一眼类别分布。如果正面是负面的五倍以上后面训练必须做类别平衡处理否则模型全预测正面也能有 80% 准确率这种「高分」在报告里是减分项。3. 中文分词与特征工程jieba、停用词和 TF-IDF 怎么配3.1 jieba 分词的三种模式和该选哪个中文不像英文有天然空格分词质量直接决定特征质量。jieba 提供三种模式精确模式cut最常用适合文本分析。全模式cut_allTrue把所有可能成词的都切出来冗余大。搜索引擎模式cut_for_search在精确模式基础上对长词再切适合召回。情感分析我一般用精确模式因为评论里的情感词「干净」「吵」「贴心」大多是短词精确模式足够。但有个细节酒店领域有大量专有表达比如「前台小姐姐」「隔音效果」默认词典可能切错。这时候加载自定义词典import jieba jieba.load_userdict(hotel_dict.txt) # 每行一个词可带词频和词性 def tokenize(text): return [w for w in jieba.cut(text) if w.strip()] df[tokens] df[clean].apply(tokenize) print(df[tokens].head())hotel_dict.txt里放你从数据里观察到的领域词比如「行政酒廊」「大床房」「退房速度」。这一步在报告里能体现「领域适配」是加分点。3.2 停用词表通用表 领域表两层过滤停用词不处理「的」「了」「是」这些高频无意义词会占据 TF-IDF 权重稀释真正的情感信号。做法是准备两份表def load_stopwords(*paths): words set() for p in paths: with open(p, encodingutf-8) as f: for line in f: words.add(line.strip()) return words stopwords load_stopwords(stopwords_common.txt, stopwords_hotel.txt) def remove_stopwords(tokens): return [w for w in tokens if w not in stopwords and len(w) 1] df[tokens] df[tokens].apply(remove_stopwords)通用停用词表网上很多领域表要自己加。酒店评论里「酒店」「房间」这类词出现频率极高但区分度低可以酌情加入领域停用词——但注意「房间」本身中性可「房间小」是负面所以别把可能参与情感组合的词一刀切掉最好保留后在特征阶段用 n-gram 补回来。3.3 TF-IDF 参数怎么调max_features 和 ngram_range特征向量化用TfidfVectorizer几个关键参数直接决定效果from sklearn.feature_extraction.text import TfidfVectorizer vectorizer TfidfVectorizer( tokenizerlambda x: x, # 传入已分好词的列表 preprocessorlambda x: x, token_patternNone, max_features5000, # 保留权重最高的 5000 个词 ngram_range(1, 2), # 同时考虑单字词和双词组合 min_df3, # 至少出现在 3 篇文档里才保留 max_df0.9 # 出现在 90% 以上文档的词丢弃 ) X vectorizer.fit_transform(df[tokens]) print(X.shape)参数说明max_features5000是经验值数据量小可以降到 2000大了容易过拟合ngram_range(1,2)能捕捉「不 干净」这种否定组合对情感分析很重要min_df和max_df是双向过滤去掉太罕见和太普遍的词。注意tokenizer这里传的是恒等函数因为我们已经分好词了直接让 vectorizer 把列表当 token 序列处理避免它再切一次。4. 模型训练与评估从朴素贝叶斯到 Logistic 回归的取舍4.1 先跑一个朴素贝叶斯基线别一上来就上深度学习很多人一提到情感分析就想上 BERT但期末大作业的数据量往往只有几千条深度学习不仅训不动还容易过拟合报告里也讲不清。我的建议是先用朴素贝叶斯或 Logistic 回归跑出基线再决定要不要升级。这两个模型训练快、可解释、参数少答辩时能说清楚每个决策。from sklearn.model_selection import train_test_split from sklearn.naive_bayes import MultinomialNB from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report, confusion_matrix X_train, X_test, y_train, y_test train_test_split( X, df[label], test_size0.2, random_state42, stratifydf[label] ) nb MultinomialNB(alpha1.0) nb.fit(X_train, y_train) pred_nb nb.predict(X_test) print(朴素贝叶斯) print(classification_report(y_test, pred_nb)) lr LogisticRegression(max_iter1000, C1.0) lr.fit(X_train, y_train) pred_lr lr.predict(X_test) print(Logistic 回归) print(classification_report(y_test, pred_lr))stratifydf[label]保证训练测试集类别比例一致这个参数不加类别不平衡时评估会失真。alpha1.0是拉普拉斯平滑防止某个词没在训练集出现导致概率为 0C1.0是正则强度越小正则越强过拟合时往下调。4.2 评估不能只看准确率精确率、召回率、F1 怎么读酒店评论场景里负面评论的召回率往往比准确率更重要——漏掉一条差评对业务来说比误判一条好评严重。所以报告里要重点看 negative 类的 recall 和 F1from sklearn.metrics import f1_score print(NB F1:, f1_score(y_test, pred_nb, pos_label0)) print(LR F1:, f1_score(y_test, pred_lr, pos_label0))pos_label0表示我们把「负面」当作关注类。如果两个模型准确率差不多但 LR 在负面类上 F1 更高那就选 LR。这种「按业务目标选模型」的论述是高分报告和普通报告的分水岭。4.3 混淆矩阵告诉你错在哪import matplotlib.pyplot as plt import seaborn as sns cm confusion_matrix(y_test, pred_lr) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabels[负面, 正面], yticklabels[负面, 正面]) plt.xlabel(预测) plt.ylabel(真实) plt.title(Logistic 回归混淆矩阵) plt.savefig(confusion_matrix.png, dpi150)看混淆矩阵时重点看两个数真实负面被预测成正面的数量漏判差评以及真实正面被预测成负面的数量误伤好评。如果前者明显多说明模型对负面特征学得不够可以回头检查负面样本是不是太少或者否定词处理有没有问题。5. 避坑与排查情感分析作业里最容易翻车的五件事5.1 现象模型准确率 95%但预测新评论全是一个类别原因类别极度不平衡且评估用了未分层的随机划分测试集里多数类占绝对优势。解决先看value_counts()如果比例超过 3:1用class_weightbalanced或对少数类过采样SMOTE 对文本不太适用优先调权重。lr LogisticRegression(max_iter1000, class_weightbalanced)5.2 现象分词后全是单字TF-IDF 矩阵稀疏得吓人原因自定义词典没加载或者停用词表把双字词误删了。解决打印df[tokens].head(20)肉眼检查确认 jieba 加载词典的路径正确停用词过滤时别用len(w) 1一刀切掉所有双字词——这个条件本意是去单字但会误伤「不错」这类双字情感词改成保留情感词典里的词。5.3 现象训练集 F1 0.98测试集 F1 0.65原因典型过拟合。max_features设太大、ngram_range到 (1,3)、模型太复杂都会导致。解决降max_features到 2000ngram_range回 (1,2)Logistic 的C调到 0.1再观察测试集变化。5.4 现象否定句情感判反「不干净」被当成正面原因TF-IDF 单字词模型无法表达否定组合。解决ngram_range至少设到 (1,2)让「不 干净」成为一个特征如果还不行在预处理阶段做否定词前缀合并把「不」和后面一个词拼起来。5.5 现象报告里写「准确率 92%」答辩被问「基线是多少」答不上来原因没有对比基准。解决永远先跑一个「全预测多数类」的 dummy 基线from sklearn.dummy import DummyClassifier dummy DummyClassifier(strategymost_frequent) dummy.fit(X_train, y_train) print(Dummy 基线:, dummy.score(X_test, y_test))如果模型只比 dummy 高几个点说明特征或数据有问题别急着写进报告。6. 让作业更像「作品」可视化、报告结构与一个提分技巧到这一步模型能跑了但离「高分」还差一层——把结果讲成一个有说服力的故事。我一般会加三块内容词云看高频情感词、模型系数看哪些词最影响判断、以及一段结论性的业务解读。词云用wordcloud库注意中文字体要指定否则全是方块from wordcloud import WordCloud pos_text .join([ .join(t) for t in df[df[label] 1][tokens]]) wc WordCloud(font_pathSimHei.ttf, width800, height400, background_colorwhite).generate(pos_text) wc.to_file(positive_wordcloud.png)font_path指向系统里的中文字体文件Windows 一般在C:/Windows/Fonts/simhei.ttfMac 用PingFang.ttc。这一步翻车率极高十个人里有三个卡在字体上。看模型系数更直接import numpy as np feature_names vectorizer.get_feature_names_out() coefs lr.coef_[0] top_pos np.argsort(coefs)[-15:] top_neg np.argsort(coefs)[:15] print(正面关键词:, [feature_names[i] for i in top_pos]) print(负面关键词:, [feature_names[i] for i in top_neg])把这些词写进报告比单纯贴准确率有说服力得多。比如你发现「隔音」「异味」是强负面词「贴心」「干净」是强正面词就能顺势给酒店提运营建议——这才是情感分析作业该有的落点。报告结构我建议按「问题定义 → 数据来源与清洗 → 方法与参数 → 实验结果 → 错误分析 → 改进方向」六段写每段配一张图或一张表。错误分析那段尤其重要挑三五个典型错例说清楚为什么错、怎么改这比堆砌模型对比表更能体现你真的动手了。最后一个提分技巧留一个可复现的入口。把清洗、分词、训练、评估封装成一个main.py别人 clone 下来改个数据路径就能跑。期末作业的评分老师未必会跑你的代码但一个结构清晰的工程目录本身就是印象分。我自己做项目时养成的习惯是任何一次实验都记下随机种子、参数和对应的评估数字不然过两天自己都复现不出来——这个习惯比任何模型都值钱。希望帮到你。本文还有配套的精品资源点击获取