拓冰建站拓冰建站
首页 / 资讯中心 / 正文

LDA主题模型与情感分析实战:电商评论文本挖掘全流程

简介一份基于LDA主题模型对电商产品评论进行情感分析的Python完整项目面向高校学生与Python初学者适用于期末大作业、课程设计或毕业设计参考。项目包含情感分析源码、文档说明与多种数据文件代码附有详细注释新手也能快速理解并部署运行整体功能完善且操作简便。资源包共1380个文件压缩包约53.97MB核心包括py源码、csv评论数据集、txt说明文档、ipynb分析笔记及html可视化页面等覆盖数据采集、预处理、LDA主题建模、情感判定与结果展示的完整流程。目前已有344人学习下载属于实践性较强的高分项目。读者可从中获得可直接运行的代码框架、电商评论数据样本、建模与分析思路以及配套的文档说明便于在此基础上扩展或复用是快速完成相关课程设计的实用参考。1. 电商评论不想一篇篇翻LDA 主题模型先聚类、再逐类做情感打分手上有几千条电商评论老板要你一周内给出“用户到底在抱怨什么、哪些问题最伤口碑”的结论。逐条看肯定不现实直接扔给大模型又怕上下文太长、费用太高。这份源码给的是经典稳妥的组合用 LDA 主题模型把评论按“聊的话题”自动分成若干簇再对每个簇做情感打分最后得到“物流慢负面 0.32 包装破损负面 0.21 客服态度正面 0.78”这样可汇报的结果。适合三类人刚接触主题建模的学生、做用户洞察的运营数据分析师、以及想在公司内部快速搭一套评论分析 demo 的 Python 工程师。跟着这篇文章过一遍你能弄明白数据从清洗到分词的每个环节也能避开我实际跑的时候踩过的坑。2. 整体链路与源码结构先想清楚三件事再动手2.1 为什么是 LDA 情感分析而不是直接打标签很多人拿到评论文本的第一反应是搞个情感分析模型直接打分。但你冷静想一下情感分析只能告诉你“好评还是差评”不能告诉你“这条评论到底在说哪个话题”。同样是负面情感一句说物流一句说质量处理方式完全不同。LDA 在这里的作用是“自动聚类”。它不需要你提前标注评论属于哪个话题而是通过词共现规律把评论分成若干主题——比如“送货时效”“产品功能”“包装外观”“客服响应”。每一类里有一个主题-词分布解释性强跑完就能看到这类评论的高频词。情感分析则是在每个主题簇内部再打分属于“先归类、再定调”的分析框架。这套组合在电商场景里的优势很实际第一不需要人工标注样本冷启动快第二主题是数据自己长出来的不会被你预设的维度带偏第三报告好写——“负面情感最集中的主题是物流提及占比 37%”这句话背后有具体数据支撑而不是拍脑袋。2.2 源码包结构与运行流程我拿到这份源码后第一件事就是看目录结构。典型的电商评论分析项目一般长这样ecommerce_comment_analysis/ ├── data/ │ ├── raw_comments.csv # 原始评论数据 │ ├── stopwords.txt # 停用词表 │ └── user_dict.txt # jieba 自定义词典 ├── src/ │ ├── data_preprocess.py # 数据清洗与分词 │ ├── lda_model.py # LDA 主题模型训练 │ ├── sentiment_analysis.py # 情感打分模块 │ └── report_generator.py # 结果整理与导出 ├── output/ │ ├── theme_distribution.csv │ ├── sentiment_by_theme.csv │ └── lda_visualization.html └── requirements.txt建议按这个顺序跑先执行data_preprocess.py把原始评论变成分词后的列表再跑lda_model.py训练主题模型并输出每个主题的关键词接着sentiment_analysis.py对评论打分最后report_generator.py把主题和情感交叉汇总成表格。这套流程的顺序是有讲究的。分词结果的干净程度直接决定 LDA 出不出得来主题主题数选得准不准又决定后面情感分析的结论粒度。所以不要急着跳过前面的步骤只跑最后那个“看起来有用”的模块。2.3 环境配置Python 版本与依赖安装评论区常说“环境装到怀疑人生”这份源码依赖倒不算多核心就是 gensim、jieba、snownlp、pandas。我自己用的是 Python 3.9各依赖版本如下可以直接抄pip install gensim4.3.2 pip install jieba0.42.1 pip install snownlp0.12.3 pip install pandas2.0.3 pip install pyLDAvis3.4.1关于版本有一个要提前说明gensim 4.x 和 3.x 的 API 差异比较大尤其是pyLDAvis.gensim的导入方式和prepare函数的参数要求。如果你用的是 gensim 4.3.2下面这段导入是正确的import pyLDAvis import pyLDAvis.gensim # 注意不是 gensim_models pyLDAvis.enable_notebook()如果是 gensim 3.8.3则要写成pyLDAvis.gensim_models这个区别是网上很多报错的根源后面第 6 章我会单独讲。目录结构先看到这里接下来进入数据准备环节。3. 数据清洗与分词处理把评论变成 LDA 能吃的数字3.1 评论数据清洗规则去掉哪些、保留哪些电商评论文本比新闻语料脏得多。我打开raw_comments.csv之后看到的问题包括重复字符“好好好好好”、表情符号、URL、订单号混在正文里、同一用户多次提交相同内容。如果这些不清理它们会成为 LDA 里的“高频噪音词”。清洗逻辑最重要的是定规则不要一把梭。我采用的清洗顺序是import pandas as pd import re df pd.read_csv(data/raw_comments.csv) df df.drop_duplicates(subsetcontent) # 去掉完全重复的评论 def clean_text(text): text str(text) text re.sub(rhttps?://\S, , text) # 去掉 URL text re.sub(r\d{6,}, , text) # 去掉订单号之类长数字 text re.sub(r[【】\[\]{}()], , text) # 去掉括号 text re.sub(r[^\u4e00-\u9fa5a-zA-Z], , text) # 只保留中文和英文 text re.sub(r(.)\1{2,}, r\1, text) # 压缩连续重复字符“好好好” - “好” return text.strip() df[clean_content] df[content].apply(clean_text)逐行解释一下drop_duplicates去重是防止同一条评论反复出现拉高某些词的权重URL 和长数字对主题推断没有任何价值而且会害得分词器把整段连成一串正则里把非中英文的全部替换删除这一步比较激进但电商评论里表情符号本来就多留着只会增加噪音。这里有个取舍要跟你说清楚如果评论里有“发热严重”这句话所有字都被保留但如果商品型号是“iPhone15ProMax”数字加字母连在一起对中文分词器很不友好。所以我会把纯英文和长数字直接删掉只保留中文关键词。代价是少数中英混合的词会损失信息在电商分析里这个损失可以接受。3.2 jieba 分词自定义词典准备好再切清洗之后的文本是连贯的中文字符串LDA 需要的是“词的列表”所以要用 jieba 做分词。很多入门项目在这一步直接用默认词典出来的结果里“不”“了”“还是”这类词占比极高主题模型根本分不出有效话题。正确的做法是先看数据、再决定要不要加自定义词典。比如你做的是美妆电商评论里高频出现“眼线笔”“遮瑕膏”“保湿霜”这些词在 jieba 默认词典里可能被切成“眼线/笔”“遮瑕/膏”语义就碎了。解决办法是把它们维护进data/user_dict.txt每行一个词眼线笔 遮瑕膏 保湿霜 红血丝 敏感肌然后分词时加载这个词典import jieba jieba.load_userdict(data/user_dict.txt) def segment(text): words list(jieba.cut(text)) return words df[tokenized] df[clean_content].apply(segment)load_userdict的作用是让 jieba 分词时优先按你指定的词切割避免通用词典把电商商品词拆散。参数上还可以控制jieba.cut的cut_all值默认是 False也就是精确模式一般保持默认就好全模式只适合搜索引擎场景不适合做主题分析。分词完成后建议先看一眼抽样结果。如果发现“真的真的非常好用”被切成“真的/真的/非常/好用”这没问题但如果出现“不/知道/怎么/说”说明评论里情绪化的口语表达很多后面停用词表要专门补充。3.3 停用词表与词典构建BOW 和 TF-IDF 的区别分词之后紧接着是去停用词。停用词表有两个来源网上公开的哈工大停用词表加上你自己按这份数据补的口语词。电商场景里“东西”“感觉”“觉得”“买回来”“用了”这类词几乎是每一条都出现但它们对区分主题毫无帮助。我是这么合并的先用公开停用词表再把分词结果里词频排前 100、但和目标主题无关的词手动补进去。这个过程通常要跑两轮——第一轮用现有停用词表跑 LDA看哪个主题全是废话就回来看哪个词没被过滤掉。过滤代码stopwords set() for line in open(data/stopwords.txt, r, encodingutf-8): stopwords.add(line.strip()) # 常见电商口语停用词按需追加 extra_stopwords {东西, 感觉, 觉得, 还是, 真的, 非常, 反正, 然后} stopwords.update(extra_stopwords) def remove_stopwords(tokens): return [w for w in tokens if w not in stopwords and len(w) 1] df[filtered_tokens] df[tokenized].apply(remove_stopwords)这一步里的len(w) 1是把单字词直接扔掉因为“好”“差”“快”这类单字词在 LDA 里容易跨主题乱窜对可解释性伤害很大。如果你后面发现主题里某几个单字词反复出现多半是这个过滤条件没加。3.4 构建 gensim 字典与语料库分词和过滤结束后要构建 gensim 需要的两种数据格式Dictionary和LdaModel直接喂的语料库。这里涉及一个选择——用 BOW词袋还是 TF-IDF 来做 LDA 的输入。最常见的做法是用 BOW因为 LDA 本身就是基于词共现的伯努利/多项式分布模型BOW 最贴合它的假设。TF-IDF 一般用于文档相似度计算做 LDA 输入时会把高频词压得太低反而不利于发现主题。但也有例外如果你的评论里有明显的“爆款词”比如某次促销的主推款名称几千条评论都在提用 TF-IDF 能抑制这个词对主题划分的干扰。实践里我建议默认跑一遍 BOW如果主题结果被某个词绑架了再换 TF-IDF 对比。from gensim import corpora, models all_tokens df[filtered_tokens].tolist() dictionary corpora.Dictionary(all_tokens) dictionary.filter_extremes(no_below5, no_above0.5) # 过滤过稀疏和过高频的词 corpus_bow [dictionary.doc2bow(tokens) for tokens in all_tokens] # 如果想试试 TF-IDF 过滤爆款词可以这么做 tfidf models.TfidfModel(corpus_bow) corpus_tfidf tfidf[corpus_bow]关键在于filter_extremes的两个参数no_below5表示词至少在 5 条评论里出现过才保留把只出现在一两条评论里的生僻词滤掉能减少主题数虚高no_above0.5表示词在超过 50% 的评论里出现就丢弃防止“东西”“感觉”这种全局词压过主题特征词。初次跑no_below可以从 2 到 10 之间扫一遍看主题关键词变化。4. LDA 建模与情感打分主题数怎么定、分数怎么算4.1 LdaModel 核心参数num_topics、passes、alpha 怎么设数据准备好之后进入 LDA 训练。gensim 的LdaModel有四个参数最影响结果num_topics、passes、alpha、random_state。我直接把常用的初始配置贴出来from gensim.models import LdaModel lda_model LdaModel( corpuscorpus_bow, id2worddictionary, num_topics8, passes20, alphaauto, etaauto, random_state42, chunksize2000, iterations400 ) topics lda_model.print_topics(num_words15) for topic in topics: print(topic)逐个说参数num_topics8是初始值电商评论数据集我一般从 6 试到 15具体怎么选在 4.2 节讲passes20代表模型对整个语料库遍历 20 遍太少模型没收敛太多训练时间成倍涨20 到 50 是常规区间alphaauto让模型自己学习文档-主题分布的先验比手动设 0.1 之类的固定值更稳random_state42固定随机种子这是确保每个同学跑出来的结果一致的关键。不设这个参数你会发现每次跑出来的主题关键词略有不同不利于调试。chunksize2000是这批评论一次喂给模型的文案数量数据量小几千条以内可以不管它。iterations400是每轮采样的迭代次数保持默认或 300-500 之间都行。一个重要提醒打印出来的主题关键词如果像“东西/感觉/觉得/真的/然后”这种不是你参数设错了而是前面停用词没过滤干净。往回改停用词表比在这里调参数更本质。4.2 用困惑度曲线定主题数别拍脑袋主题数到底定多少这个问题没有绝对正确答案但有一个标准参考量——困惑度perplexity。困惑度的原理可以简单理解成“模型对这个语料有多意外”值越低说明模型越能解释数据。但它也不是越低越好主题数太多时困惑度还会持续下降模型却开始把同一话题拆成多个无聊的子主题。我之前跑过的典型曲线主题数从 2 加到 20困惑度一路下降在 8 到 10 之间降幅明显收窄。所以我一般选“拐点”附近的值也就是再增加主题数收益变小的那个位置。代码写法import numpy as np perplexity_scores {} for k in range(4, 16): model LdaModel( corpuscorpus_bow, id2worddictionary, num_topicsk, passes20, alphaauto, random_state42 ) perplexity_scores[k] model.log_perplexity(corpus_bow) for k, v in sorted(perplexity_scores.items()): print(fnum_topics{k}: log_perplexity{v:.2f})这段代码会帮你跑一批不同主题数的模型然后你把log_perplexity的变化趋势打出来看。注意这里打印的是 log 困惑度值越高代表困惑度越低所以你会看到它随主题数增加而上升随后放缓。选那个“上升开始放缓”的点即可。另一个辅助指标是主题之间的区分度。我会在每个候选主题数下跑两遍比较相同num_topics下主题关键词是否稳定。如果两遍跑出来的主题关键词差很多说明这个主题数不适合当前数据换个值再试。这个技巧比只看困惑度更实操。4.3 SnowNLP 情感打分短文本的边界主题出了之后接下来对每条评论做情感打分。这里用 SnowNLP因为它在中文电商短文本上表现尚可而且安装简单、开箱即用不需要自己训练模型。from snownlp import SnowNLP def sentiment_score(text): try: s SnowNLP(text) return s.sentiments # 0~1越接近1越正面 except Exception: return 0.5 df[sentiment] df[clean_content].apply(sentiment_score) print(df[sentiment].describe())sentiments属性输出的是一个 0 到 1 之间的小数0.5 是中性分界线。跑完之后建议先看一眼分布——如果大部分分数集中在 0.4 到 0.6 之间说明 SnowNLP 对这份数据的区分度不够。这里有实测经验要分享SnowNLP 的底层模型是基于购物语料训练的对“质量好”“物流快”“还会回购”这类正向表达识别得比较准但对讽刺、反话、网络梗基本无效。比如“真是太好了等了一个星期才发货”SnowNLP 很可能打出高分。所以在正式使用前我会先跑 100 条人工抽样把明显的误判率记下来。误判率超过 20% 的数据集建议换用基于大模型 API 的情感分类或者自己标注几百条做微调。4.4 主题×情感交叉分析负面主题不用翻评论就能看见评分只是中间结果最后的输出要做交叉分析。每个主题簇里的评论有正面、负面之分把“主题分布”和“情感分布”合并起来看才真正支持业务决策。from gensim.utils import simple_preprocess # 给每条评论分配最可能的主题 def get_dominant_topic(text): bow_vec dictionary.doc2bow(simple_preprocess(text)) topic_dist lda_model.get_document_topics(bow_vec) topic_dist sorted(topic_dist, keylambda x: x[1], reverseTrue) return topic_dist[0][0] if topic_dist else -1 df[dominant_topic] df[clean_content].apply(get_dominant_topic) # 主题 情感交叉表 cross_table df.groupby([dominant_topic, sentiment_label]).size().unstack(fill_value0) print(cross_table)执行到这里你会看到类似这样的结果主题 0 共 1120 条其中负面 680 条、正面 300 条主题 3 共 450 条负面仅 60 条。再去print_topics(0)看主题 0 的关键词是“配送”“包装”“快递”结论一下就出来了——配送环节是负面话题的重灾区。这一步里get_document_topics算的是“这条评论属于每个主题的概率”取最大值对应的主题编号。注意simple_preprocess(text)只是简单切词如果你发现主题分配不稳定可以改成直接用已经分词好的filtered_tokens转 BOW结果会更一致。5. 避坑LDA 命中这些坑结果基本没法用5.1 主题全是高频废话词现象跑出来的主题关键词全是“东西”“感觉”“觉得”“真的”这类词每个主题看起来都一样。原因停用词没过滤干净。公开停用词表覆盖通用场景但电商评论里的口语化表达“用了”“买回来”“推荐”需要自己补充。解决在extra_stopwords里逐个累加。最实用的一招是把 LDA 结果中词频前 50 的词拉出来人工标一遍标出来就停用。一般经过两轮补词主题关键词就会变成有业务含义的词。5.2 gensim 版本导致 pyLDAvis 导入报错现象在执行import pyLDAvis.gensim时直接 ModuleNotFoundError或者执行prepare时报错说找不到gensim_models模块。原因gensim 4.x 把模型模块位置改了。早期教程大多基于 gensim 3.x写法是pyLDAvis.gensim_models 在 4.x 下失效。解决先确认版本pip show gensim4.3.2然后按pyLDAvis.gensim导入。如果你发现自己的代码是gensim_models但用的是 gensim 4.x直接改成gensim就行。5.3 评论长度太短导致主题归属不稳定现象很多电商评论只有两三个字“不错”“真棒”TF-IDF 转换后语料向量大部分是零向量LDA 给它们分配的主题几乎随机。原因LDA 依赖词共现来区分主题词太少信息量不够。解决情感分析和主题分析拆开处理。短评论少于 10 个字符只做情感打分不参与主题归类主题分析只保留 15 个词以上的评论。这样主图结论来自长文本短文本的情感单独统计不会相互污染。5.4 一个主题同时包含“物流”和“客服”两个话题现象主题 2 的关键词里既有“快递”“配送”又有“态度”“客服”业务上明显是两个话题被卷在一起。原因两个话题经常在一条评论里同时出现“快递慢客服还差”LDA 的假设是每条评论只有一个主话题这种复合评论会让模型产生混合主题。解决两条路。第一条是加大num_topics让模型有更多“槽位”把混合话题拆开第二条是在数据预处理里做细粒度切句把一条长评论按句号拆成多条短评论让每条只保留一个核心话题。我用后者的效果更直接。5.5 SnowNLP 对负面评价打出高分现象人工标注是负面的SnowNLP 给出 0.8 以上的正面分。原因SnowNLP 的训练语料以商品购物评论为主对“反讽”“质疑”“催单”这类隐含情绪表达不敏感。解决高频出现的行业词比如“掉色”“起球”“开裂”“划痕”可以直接作为负面词典在代码里做硬规则覆盖——只要命中词典中的词情感分数强制降到 0.3 以下。这是最便宜、最快的修正手段比重新训练模型适合绝大多数场景。6. 进阶pyLDAvis 可视化验证主题质量顺手看模型有没有跑偏LDA 跑完不能只靠打印关键词判断好坏我会用 pyLDAvis 做交互式验证。它能把主题之间的相似度画成二维图每个圆圈代表一个主题圆圈越大代表该主题的评论占比越高两个圆圈重叠越多说明两个主题越相似。这个图在汇报时也是很好的材料直接导出 HTML 给业务同事看就行。import pyLDAvis import pyLDAvis.gensim vis_data pyLDAvis.gensim.prepare(lda_model, corpus_bow, dictionary) pyLDAvis.save_html(vis_data, output/lda_visualization.html) print(Saved to output/lda_visualization.html)打开 HTML 之后我的判断标准就三条第一主题圆圈之间是否交叠严重。如果三个圆圈大面积重叠说明主题数设多了或者语料本身差异度不够我会回到 4.2 节把num_topics调低重跑。第二右侧的“词频”和“主题相关性”两个维度下灰色条占比是否过高。灰色条代表该词在所有文档中的总词频红色条代表该词在当前主题中的比重红条太短说明这个词在这个主题里没有代表性。第三圆圈的大小是否极度不均衡。如果某个圆圈膨胀得特别大通常意味着停用词没加干净或者某条促销活动影响了整体词频分布。这份源码的价值在于把一套可复用的评论分析链路完整串起来了而不是只给你一个孤零零的模型文件。从清洗、分词到 LDA、情感打分再到交叉分析和可视化每个环节都有对应代码和输出你把数据替换成自己的商品评论就能直接跑。我刚接手这份源码的时候第一轮跑出来的主题全是“东西”“感觉”后来补了两轮停用词、把num_topics从 8 调到 10 才稳定下来。从那以后我每次拿到新的评论数据都会强制先跑一遍词频统计和 100 条人工抽检再进建模流程——这一步能帮你少走至少半天弯路。希望这次拆解能让你在复现的路上走得比我顺。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门