ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python机器学习实战:豆瓣电影评论情感分析全流程解析

Python机器学习实战:豆瓣电影评论情感分析全流程解析 简介这是一份基于Python与机器学习技术的豆瓣电影数据分析项目完整资料适合计算机、人工智能、大数据、数学等专业的学生用于课程设计、期末大作业或毕业设计参考也适合想系统掌握爬虫、文本挖掘与可视化流程的学习者。压缩包共16个文件约28.18MB包含Python源码、实验报告PDF、HTML可视化结果、停用词表与电影评论数据集等其中py文件为可运行的主程序txt为数据与辅助配置jpg图片展示了词云和统计图表rar内为原始评论数据整体结构清晰便于对照学习。目前已有351人学习使用。资料经过严格调试下载后可直接运行能帮助读者快速理解从数据采集、清洗、机器学习建模到结果展示的完整链路尤其适合需要参考真实项目代码完成作业或提升实战能力的人群。1. 基于 Python 与机器学习的豆瓣电影数据分析这份源码包想把评论文本变成什么期末作业要求用 python 和机器学习做一次数据分析最容易翻车的不是写代码而是数据拿回来之后卡在中间不知道该干什么。这份源码包正好把链路补完整了从 2018 年豆瓣电影评论的原始文本出发做清洗、分词、停用词过滤再用 TF-IDF 把文本变成特征跑朴素贝叶斯和逻辑回归做情感倾向分类最后输出词云图和实验报告。适合正在做课程设计、期末大作业或者毕设预研的计科、大数据、人工智能专业学生。代码本身是调试过的能直接跑通但换数据、改路径、调参数这些事需要自己动手所以建议有一定 python 基础的人拿它当骨架而不是直接改名交作业。2. 数据预处理先把 2018 豆瓣评论 .rar 解开后的脏文本洗成可建模语料拿到压缩包先不要急着写模型绝大多数翻车都发生在数据读取这一步。资源里是一个 rar 评论集外加 all.txt、high.txt、low.txt 三个文本文件从文件命名看all 是全量短评high/low 大概率是按打分或情感倾向分出来的两组数据后面的二分类标签就可以从这两组里生成。先把 rar 解压到英文目录和作业代码.py 放在同一个文件夹下这是后面所有步骤的前提。不要直接在压缩包里读文件后面改路径会乱到怀疑人生。2.1 从 rar 到 DataFrame编码和分隔符两个坑一起解决解压后第一步是用 pandas 把文本读成 DataFrame。常见做法是先读 all.txt 看它是不是一行一条短评再决定要不要分隔符我一般先试不带 sep 的读法import pandas as pd df pd.read_csv( all.txt, headerNone, names[comment], encodingutf-8, enginepython ) print(df.shape) print(df.head())代码里 headerNone 表示原文件没有表头names 手动把唯一一列命名为 comment。encoding 先试 utf-8如果报 UnicodeDecodeError把这一行换成 encodinggbk 再跑Windows 下从 rar 拖出来的 txt 大概率是 GBK。enginepython 是为了让解析更宽容文件特别大时可以去掉它换回默认 C 引擎速度更快。如果 all.txt 实际是几列结构再加 sep\t 或 sep,用逗号分隔时要先确认短评内容里没有逗号否则字段会被切碎。读完之后立刻打印 shape 和 head这一步能确认两件事数据读进来了多少行以及文本里有没有奇怪的符号。如果 head 里出现 b\xef\xbb\xbf 这种前缀说明文件是带 BOM 的 UTF-8把 encoding 改成 utf-8-sig 就行。这个细节在 Windows 上做作业时特别常见报告里数据量统计不对八成是这里出了问题。2.2 文本清洗三步去 HTML、去重复、去停用词豆瓣短评不是干净的文本里面混着换行、HTML 片段、链接、多余空格还有大量复制粘贴的灌水内容。清洗的顺序有讲究先做粗清洗再去做重否则同一句话的不同变体会绕过去重逻辑。下面这段是资源里清洗思路的常见实现import re def clean_text(raw: str) - str: text str(raw).strip() text re.sub(r[^], , text) # 去掉 HTML 标签 text re.sub(rhttps?://\S, , text) # 去掉链接 text re.sub(r[^\u4e00-\u9fa5A-Za-z0-9], , text) # 标点符号变成空格 text re.sub(r\s, , text) # 连续空白压成一个空格 return text df[comment] df[comment].apply(clean_text) df[comment] df[comment].replace(, pd.NA).dropna() df df.drop_duplicates(subset[comment]).reset_index(dropTrue)这段代码里第三行正则[^\u4e00-\u9fa5A-Za-z0-9]表示把中英文和数字以外的全部字符替换成空格目的是拆开被标点粘连的词语防止「还行,吧」被错误拼成「还行吧」影响分词。去重用 drop_duplicates 是因为豆瓣短评区大量复读重复样本会让后面的训练集虚胖模型学到的全是重复模式而不是情感规律。dropna 之前先把空字符串替换成 pd.NA是为了避免清洗后整条变空的情况污染语料。这里有个容易被忽略的点清洗后建议打印几条看看你会发现「电影」「真的」这类词出现频率极高但它们对情感判断没有贡献。这就是为什么要配一份停用词表资源里的 cn_stopwords.txt 就是干这个的。注意清洗不要做过头比如去掉所有数字会把「1921」「战狼2」这种片名相关词也删掉具体保留什么取决于你的分析目标。2.3 jieba 分词与高频词统计先摸清数据再谈建模中文文本不能像英文那样按空格切词必须分词。常见做法是 jieba 分词配合刚才的停用词表一起过滤。加载停用词表时用 set 而不是 list几万条评论过滤时查找效率差很多import jieba with open(cn_stopwords.txt, encodingutf-8) as f: stopwords {line.strip() for line in f if line.strip()} def cut_words(text: str) - list: words jieba.lcut(text) words [w for w in words if len(w) 1 and w not in stopwords and not w.isdigit()] return words df[cuted] df[comment].apply(cut_words) df[corpus] df[cuted].apply( .join)jieba.lcut 返回的是 list比 cut 更直观。过滤条件里 len(w) 1 表示过滤单字这会顺带把「不」「很」这类有情感倾向的单字也滤掉。如果你要做的是情感分析我建议把这条放宽成 len(w) 1然后在停用词表里手动排除无关单字否则「不推荐」会被拆成「推荐」情感方向直接反转。这个坑在作业报告里不容易被发现但模型准确率会莫名掉几个点。分词完成之后先别急着建模用 Counter 统计一下词频确认数据分布符合预期from collections import Counter all_words [] for words in df[cuted]: all_words.extend(words) counter Counter(all_words) print(counter.most_common(30))打印出来的高频词如果全是「电影」「一部」「还是」这类无信息量词说明停用词表没生效如果出现「好看」「演技」「剧情」「导演」这类评价词说明语料基本可用。这一步花两分钟能避免后面模型训完才发现数据没洗干净。机器学习作业.txt 里大概率也写了这一步的要求对照着看能知道老师期望你做到哪一层。3. 特征工程与机器学习建模TF-IDF 向量化后选朴素贝叶斯还是逻辑回归文本本身不能直接喂给模型需要先转成数值特征。这一步是整个作业里最像「机器学习」的地方也是实验报告里最好写分析的部分。特征做得好不好比模型选什么影响更大。这里先讲清楚为什么用 TF-IDF再给出能直接跑的建模代码。3.1 文本特征化为什么中文短评要转 TF-IDF 而不是直接数词频最直觉的做法是词袋模型把每篇文档变成一个向量每个位置对应一个词在文档里出现的次数。但纯词频有两个问题高频的「电影」「真的」会占据主导即使它们不携带情感信息长文档天然有更高的词频这会让评论长度干扰分类。TF-IDF 对这两个问题做了修正词频部分计算一个词在文档里的占比逆文档频率部分削弱那些在很多文档里都出现的常见词。sklearn 里直接用 TfidfVectorizer参数按下面的配置基本够用from sklearn.feature_extraction.text import TfidfVectorizer vectorizer TfidfVectorizer( max_features5000, ngram_range(1, 2), min_df2, sublinear_tfTrue ) X vectorizer.fit_transform(df[corpus]) print(X.shape)max_features5000 表示只保留词频最高的 5000 个特征中文短评数据集通常几千条这个量级够了再多会引入噪声并拖慢训练。ngram_range(1, 2) 同时保留单个词和相邻两个词的组合像「不好看」这种三字词组合不一定能全覆盖但「不 好看」这种 bigram 能被捕捉到对情感分析帮助很大。min_df2 表示出现次数低于 2 的词直接丢弃过滤掉只在一条评论里出现的生僻词。sublinear_tfTrue 把词频做对数缩放防止某个词在超长评论里刷频率。这里有个前提df[corpus] 必须是分词后用空格 join 好的文本也就是第 2 章里生成的那一列。如果直接把原始中文句子传进去TfidfVectorizer 会把整句话当作一个 token特征矩阵稀疏到没法用。这是新手最容易犯的错模型跑出来准确率奇低却找不到原因。3.2 模型训练与对比朴素贝叶斯、逻辑回归、线性 SVM 三选一中文短文本情感分类是典型的低维度、稀疏特征场景不需要上深度学习。资源里最可能用的是朴素贝叶斯因为它对高维稀疏文本特征表现稳定、训练快报告里也容易解释。我一般会把朴素贝叶斯、逻辑回归、线性 SVM 都跑一遍挑准确率最高的写进报告这样对比表一放报告的说服力立刻不一样from sklearn.model_selection import train_test_split from sklearn.naive_bayes import MultinomialNB from sklearn.linear_model import LogisticRegression from sklearn.svm import LinearSVC from sklearn.metrics import accuracy_score, classification_report y df[label] # high 组标 1low 组标 0 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) models { NB: MultinomialNB(alpha0.5), LR: LogisticRegression(C1.0, max_iter1000), SVM: LinearSVC(C1.0), } for name, model in models.items(): model.fit(X_train, y_train) pred model.predict(X_test) acc accuracy_score(y_test, pred) print(f{name}: {acc:.4f}) print(classification_report(y_test, pred))train_test_split 里 random_state42 是必须的没有它每次跑出来的结果都不一样报告里的数字和代码复现结果对不上答辩时会被问住。stratifyy 保证高低分组在两个集合里的比例一致防止某一类全被切进测试集。MultinomialNB 的 alpha0.5 是拉普拉斯平滑系数默认 1.0短文本场景下调小一点能减少对稀有特征的过度平滑先跑默认值再对比调参结果。LogisticRegression 的 max_iter1000 是因为特征维度高默认 100 次迭代可能没收敛就警告。LinearSVC 适合稀疏高维特征训练速度比 RBF 核的 SVC 快几个量级。实际跑下来多数情况下线性 SVM 或逻辑回归准确率略高于朴素贝叶斯但朴素贝叶斯胜在训练快、可解释性强而且对样本量小的数据集更稳健。报告里建议写清楚三个模型的对比结论别只贴一个最高分。如果你发现三个模型准确率都在 0.5 附近不用怀疑模型回头查数据清洗和标签构造问题一定在前面的环节。3.3 结果落盘与报告素材让实验报告里的表格自洽模型跑完要把预测结果留下来后面画图、做错误分析都要用。用 pandas 导出 CSV 时注意编码Excel 直接打开 GBK 文件中文会乱码utf-8-sig 是最稳的选择import pandas as pd result pd.DataFrame({ comment: df[comment], label_true: y, label_pred: model.predict(X), prob_positive: model.predict_proba(X)[:, 1] if hasattr(model, predict_proba) else None }) result.to_csv(result.csv, indexFalse, encodingutf-8-sig)predict_proba 输出的是属于每个类别的概率取 [:, 1] 得到正类的概率值后续分析模型置信度、找出哪些评论被高置信度地分错这些内容写进报告比只贴准确率丰富得多。注意 LinearSVC 没有 predict_proba所以代码里用 hasattr 做兼容。实验报告.pdf 里的表格和图表本质上就是从这类输出文件里来的保证 CSV 里的数据和报告数字一致比什么都重要。4. 词云与可视化从 cn_stopwords 到一张能放进报告的 ciyun.jpg词云是这份作业里视觉冲击力最强的部分。很多人以为词云就是调一个 WordCloud 库但中文词云有两个特有的坑中文字体、背景图处理。资源里的 low.jpg、high.jpg 大概率是准备好的词云背景图ciyun.jpg 是生成结果。这一章把这套流程完整走一遍。4.1 中文词云必踩的字体坑font_path 不设就全乱码wordcloud 库默认字体不支持中文不指定 font_path 的话生成的图片里所有中文全是方框这个现象几乎人人都会遇到。解决办法是指定一个系统里存在的中文字体文件Windows 上最常用的是 simhei.ttf 或 msyh.ttcimport numpy as np import matplotlib.pyplot as plt from wordcloud import WordCloud from PIL import Image plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] Falseplt.rcParams 这两行是给 matplotlib 用的SimHei 是黑体确保后面所有图里的中文标签不变成方框。axes.unicode_minus 设成 False 是修复坐标轴负号显示成方块的问题。这两行应该写在任何画图代码的前面作为一个全局配置。font_path 参数的值必须是你机器上真实存在的字体文件路径。常见路径是 C:/Windows/Fonts/simhei.ttf如果你用的是 macOS路径换成 /System/Library/Fonts/PingFang.ttc。这个路径因人而异作业代码.py 里如果写死了某个路径在别人机器上跑会报 FileNotFoundError拿到代码第一件事就是确认这个路径。4.2 从分词结果到 ciyun.jpg背景图、停用词和颜色参数怎么配词云生成的输入是空格分隔的长字符串正好用第 2 章里 df[corpus] 这一列拼起来。背景图用 low.jpg 或 high.jpg 控制词云的形状常见做法是用 PIL 读图转成 numpy 数组传给 mask 参数from wordcloud import WordCloud from PIL import Image import numpy as np mask np.array(Image.open(low.jpg)) wc WordCloud( font_pathC:/Windows/Fonts/simhei.ttf, maskmask, background_colorwhite, max_words200, width1200, height800, stopwordsstopwords, colormapRdYlBu, contour_width1, contour_colorsteelblue ) wc.generate( .join(df[corpus].tolist())) wc.to_file(ciyun.jpg)mask 参数控轮廓建议用黑白分明的图片效果最好彩色背景图会被词云按亮度区域计算遮盖细节容易糊。background_colorwhite 是生成白底如果背景图本身有颜色把这项设成 None 可以保留透明背景。max_words200 控制显示 200 个词词太多会挤成一团。colormapRdYlBu 是颜色映射方案控制词的配色适合电影评论这种偏感性的展示场景。contour_width 和 contour_color 是给轮廓加描边可以让形状更清晰。生成之后建议把 width、height 调成 1200 以上再输出否则放进报告印刷会模糊。自己画图时注意了词云里出现「电影」「一部」这种词说明停用词表没有覆盖到位回到第 2 章把 cn_stopwords.txt 补全再重新生成。资源里的 ciyun.jpg 就是这一条命令的产物跑完对比一下形状和词分布应该大致对齐。4.3 报告素材怎么组织实验报告.pdf、README.md 和图片的配合实验报告.pdf 是这套资源里除了代码之外最值钱的部分它告诉你老师想看到的作业长什么样。完整的数据分析报告至少要有四块数据来源与规模说明、数据清洗过程、模型对比结果、可视化展示。词云图放在可视化那一节旁边配一段文字说明高频词反映了什么比如截取 ciyun.jpg 里出现最频繁的评价词说明观众对演技、剧情、特效的关注度这就把图片和领域背景联系起来了。资源里的 README.md 通常写了运行步骤和文件说明建议先读它再动代码。作业代码.html 是代码的网页渲染版用浏览器打开能看到运行结果和输出不用启动 python 就能快速了解每一步在干什么。如果你只是要快速把环境跑通照着 html 里的输出对比自己的结果能省不少排错时间。name.txt、OIP.jpg 这类文件不影响主流程可以忽略。5. 避坑指南豆瓣反爬、中文路径与编码的五个常见问题这套资源在跑的过程中我根据经验整理了几个高频故障。下面按现象到原因到解决的方式记录碰到问题直接对照排查比从头读代码效率高。5.1 反爬与文本读取评论抓一半、GBK 乱码的高频故障现象爬虫跑了一会儿就再抓不到数据返回内容为空或者报 418/403 错误重新执行前几百条能抓到越到后面越吃力。原因豆瓣对高频请求有反爬限制同一个 IP 短时间内请求过多会触发封禁而不是直接拒绝而是返回空数据或验证页。资源里提供的 rar 文件是已抓取结果但如果你要自己扩展数据这个问题早晚会遇到。解决请求头必须带 User-Agent参考浏览器真实值每次请求之间加随机延时用 time.sleep(random.uniform(1, 3))不要把短评接口全部爬完按页采样即可。还有一点把爬到的内容及时落盘别只存在内存里程序中断不至于白跑。现象read_csv 读 txt 报 UnicodeDecodeError错误信息里出现 gbk codec cant decode byte。原因Windows 下 pandas 默认用 GBK 读文件而 txt 里是 UTF-8 编码的中文反过来文件是 GBK 而代码里写 encodingutf-8 也会报错。这份资源里的 all.txt 来源可能是不同工具导出的编码不统一。解决读取时先试 encodingutf-8报错就换 encodinggbk再不行用 encodingutf-8-sig。如果文件里混着不同编码用 open() 的 errorsignore 跳过无法解码的字节但要注意这样会丢失少量数据适合对精度要求不高的词频统计场景。我自己处理这类文件的时候会先写三行探测代码把三种编码都试一遍打印能成功读出的行数而不是靠猜。5.2 可视化与复现中文字体、中文路径、随机种子三个隐蔽问题现象matplotlib 生成的图里中文全部显示成方框英文数字正常或者词云里中文全是乱码方块。原因matplotlib 和 wordcloud 默认字体都不含中文字形。这不是代码逻辑问题是字体配置问题几乎每个用 Windows 跑中文可视化的人都会遇到。解决matplotlib 在画图前加 plt.rcParams[font.sans-serif] [SimHei] 和 plt.rcParams[axes.unicode_minus] Falsewordcloud 在初始化时传 font_path 指向具体字体文件。注意 SimHei 在某些精简版 Linux 系统里不存在这时候要安装中文字体包或者改用系统自带的其他字体路径。写代码时把字体路径提取成变量方便换机器时统一修改。现象代码明明没改同一行执行两次accuracy 不一样报告里的数字复现不出来有时差 1% 到 2%。原因train_test_split 没有固定随机种子每次切分的数据不同模型训练结果自然不同。这个问题在学习阶段最容易被忽略但答辩时老师重新跑一遍代码发现数字对不上会很尴尬。解决所有涉及随机切分的地方都传 random_state42包括 train_test_split、模型里的随机参数。建议在代码开头用 np.random.seed(42) 统一设置虽然不能覆盖 sklearn 的全部随机源但能覆盖大部分场景。报告里的数字必须和代码真实输出一致这是实验报告最基本的要求。现象代码里写了 open(评论数据.txt, encodingutf-8)报错 FileNotFoundError但文件明明存在。原因压缩包解压后的文件名可能是 GBK 编码在当前系统上显示成乱码实际文件名和代码里写的不一致或者路径里有中文字符导致跨平台兼容问题。资源里 high.txt、low.txt 这种英文命名就是为了避免这个问题。解决所有中间文件统一用英文命名放在代码同目录下如果文件名已经乱码用 os.listdir() 打印实际文件名再复制上去。路径里不要出现中文目录Windows 上最保险的做法是把整个项目放在纯英文路径下比如 D:/douban_project/。从那以后我每次拿到带中文文件名的数据包第一件事就是批量改成英文名再动手。6. 换一套电影数据也能跑把这份源码改造成自己的分析管道资源里的代码是针对豆瓣 2018 年评论写的但要换新电影、新平台的数据只需要把数据读取和标签构造两步改一下。常见做法是把整个流程封装成一个函数输入是原始文本 CSV输出是清洗后的语料和模型对比结果def build_pipeline(csv_path, text_colcomment, label_colNone): df pd.read_csv(csv_path, encodingutf-8-sig) df[text_col] df[text_col].apply(clean_text) df[cuted] df[text_col].apply(cut_words) df[corpus] df[cuted].apply( .join) if label_col is not None: vectorizer TfidfVectorizer(max_features5000, ngram_range(1, 2)) X vectorizer.fit_transform(df[corpus]) y df[label_col] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) model MultinomialNB(alpha0.5) model.fit(X_train, y_train) print(accuracy_score(y_test, model.predict(X_test))) return df # 无标注数据时只做清洗和词云 df build_pipeline(new_comments.csv, text_colshort_comment) wordcloud_text .join(df[corpus].tolist())封装之后验证这套代码是否可靠的方法很简单准备 50 条新电影的短评人工标注每条是好评还是差评跑一遍模型预测算一下人工标注和预测结果的一致性。一致率到 0.8 以上说明这套管道可以用低于 0.7回去检查标签是否合理、样本是否均衡。另一个验证点是拿训练好的模型预测几条明显的好评和差评比如「剧情紧凑演技在线」和「节奏拖沓强行煽情」看输出概率分布是否符合直觉。资源里的实验报告.pdf 可以当作模板换成你自己的数据、模型对比表格和词云图结构上完全能复用。如果你用的是其他平台的评论数据字段名不一样把 text_col 参数改成对应的列名就行。数据量大时把清洗结果先存成中间文件后续调参数不用重新清洗。如果想进一步扩展把朴素贝叶斯换成预训练模型做嵌入准确率通常能再提几个点但计算成本会明显上去课程作业阶段没必要。从那以后我每次拿到新的文本数据都会强制先跑一遍清洗脚本再谈建模这条流程已经成了我的肌肉记忆。希望帮到你。本文还有配套的精品资源点击获取
返回列表