
简介本资源是一套基于深度学习LSTM算法的电商评论情感分析Python项目源码面向计算机、人工智能、通信工程等专业的在校学生及需要项目实战练习的学习者可用于毕业设计、课程设计、期末大作业或项目初期立项演示。项目包含京东商城评论爬取与情感分析两大模块爬虫部分基于Scrapy框架实现分析部分提供已训练好的LSTM模型文件与词向量可直接运行验证效果。压缩包共39个文件以py源码、png运行截图、TensorFlow模型文件meta、index、data等及txt停用词表为主整体约164MB目录结构清晰便于按模块学习。目前已有236人学习下载。代码均经过测试运行成功答辩评审平均分达96分读者可据此掌握从数据采集、文本预处理到模型训练与情感预测的完整流程也可在现有代码基础上修改扩展实现其他文本分类或舆情分析功能。1. 京东评论情感分析这套 LSTM 源码到底能跑出什么结果打开京东某个商品的评论区几千条评价里真正带信息量的可能不到三成剩下全是「好评」「不错」「还行」。人工翻一遍不现实用关键词匹配又容易把「不是很好用」判成正面。这套基于深度学习 LSTM 算法实现的电商评论情感分析 Python 源码解决的正是这个问题它把京东评论爬取、中文分词、LSTM 模型训练和情感预测串成了一条完整链路下载后能直接跑通从数据采集到情感判定的全流程。资源里包含两个可独立运行的模块——JDSpider 负责从京东商城抓取评论数据SentimentAnalysis 负责训练和推理。模型目录里已经放了训练好的 checkpoint包括 pretrained_lstm.ckpt 和 jd_comments_181_7_model.model 两组权重文件意味着你不需要从零训练就能先看到预测效果。适合正在做毕设的学生、需要项目实战练习的学习者以及想理解 LSTM 在中文短文本分类上怎么落地的人。下面按「先跑通、再改、再避坑」的顺序拆一遍。2. 环境搭建与 JDSpider 评论采集从 scrapy.cfg 到落库2.1 依赖安装与目录结构确认拿到压缩包解压后先别急着运行。整个项目的目录分成两块JDSpider 是 Scrapy 爬虫工程SentimentAnalysis 是 TensorFlow 情感分析工程。两者共享一份数据文件所以路径关系要先理清楚。JDSpider 目录下有标准的 Scrapy 结构scrapy.cfg 是工程入口配置spiders 文件夹放爬虫逻辑items.py 定义数据结构pipelines.py 负责数据清洗和存储middlewares.py 处理请求中间件。SentimentAnalysis 目录下有 run.py 作为训练/预测入口StopwordsCN.txt 是中文停用词表models 文件夹存放 checkpoint 和 meta 文件。环境方面这套代码基于 Python 3 和 TensorFlow 1.x 编写。常见做法是建一个独立虚拟环境避免和系统里的 TensorFlow 2.x 冲突python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install scrapy tensorflow1.15.0 jieba numpy pandas这里 tensorflow 锁 1.15.0 是关键。代码里用的是 tf.nn.rnn_cell 和 tf.train.Saver 这类 1.x API直接装 2.x 会在 import 阶段就报 AttributeError。如果你机器上已经有 2.x不要试图改代码兼容先隔离环境更省事。2.2 配置爬虫目标与运行采集JDSpider 的爬虫入口在 spiders 目录下。运行前需要确认两件事目标商品 ID 和请求头设置。京东评论接口对 User-Agent 和 Referer 有校验middlewares.py 里通常已经写了随机 UA 的逻辑但 Referer 需要你根据实际商品页补上。# spiders/jd_comment.py 关键参数示意 class JdCommentSpider(scrapy.Spider): name jd_comment allowed_domains [jd.com] def start_requests(self): # product_id 换成你要爬的商品 product_id 100012043978 # 评论接口分页page 从 0 到 最大页 for page in range(0, 50): url fhttps://club.jd.com/comment/productPageComments.action?productId{product_id}score0sortType5page{page}pageSize10 yield scrapy.Request(url, callbackself.parse, headers{ Referer: fhttps://item.jd.com/{product_id}.html }) def parse(self, response): data response.json() for comment in data.get(comments, []): yield { content: comment[content], score: comment[score], time: comment[creationTime] }这段代码的逻辑是构造京东评论 JSON 接口的 URL按页请求解析返回的 comments 数组提取评论内容、评分和时间。productId 换成你想分析的商品即可pageSize 固定 10page 范围根据实际评论总数调整。score 字段是 1 到 5 的整数评分后续可以拿来做情感标签的弱监督来源——评分大于 3 视为正面小于 3 视为负面。运行命令cd JDSpider scrapy crawl jd_comment -o comments.csv输出直接落成 CSV省去自己写 pipeline 的功夫。如果跑出来是空文件先检查 Referer 是否和 productId 匹配再确认商品是否有公开评论。2.3 数据落库与格式对齐pipelines.py 里默认可能只做了去重和简单清洗。实际用的时候评论内容里会混入大量表情符号、HTML 标签和重复刷屏文本。我一般会在 pipeline 里加一步正则清洗import re class CleanPipeline: def process_item(self, item, spider): # 去掉 HTML 标签和多余空白 item[content] re.sub(r[^], , item[content]) item[content] re.sub(r\s, , item[content]).strip() # 过滤长度小于 4 的短评 if len(item[content]) 4: raise DropItem(too short) return item清洗后的 CSV 就是 SentimentAnalysis 的输入。注意列名要和 run.py 里读取的字段对齐否则会在数据加载阶段报 KeyError。这一步不复杂但容易翻车建议先拿 100 条数据跑通再放大。3. LSTM 模型加载与情感预测run.py 里的参数怎么调3.1 理解模型结构与 checkpoint 对应关系SentimentAnalysis 目录下的 models 文件夹里有两组权重。pretrained_lstm.ckpt-10000 和 pretrained_lstm.ckpt-20000 是预训练模型的中间 checkpointjd_comments_181_7_model.model.lstm_model-10000 到 40000 是针对京东评论数据微调后的模型。每组 checkpoint 包含三个文件.data-00000-of-00001 存权重数值.index 存变量索引.meta 存计算图结构。加载模型时TensorFlow 1.x 的流程是先重建计算图再用 Saver 恢复权重import tensorflow as tf # 重建 LSTM 计算图参数需与训练时一致 embedding_size 128 hidden_size 128 num_classes 2 sequence_length 100 input_x tf.placeholder(tf.int32, [None, sequence_length], nameinput_x) embedding tf.Variable(tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0)) embedding_input tf.nn.embedding_lookup(embedding, input_x) cell tf.nn.rnn_cell.LSTMCell(hidden_size) outputs, states tf.nn.dynamic_rnn(cell, embedding_input, dtypetf.float32) logits tf.layers.dense(states.h, num_classes) saver tf.train.Saver() with tf.Session() as sess: saver.restore(sess, models/jd_comments_181_7_model.model.lstm_model-40000) # 后续做预测关键参数说明embedding_size 和 hidden_size 必须和训练时一致否则 restore 会报 shape mismatch。sequence_length 是截断长度京东评论一般较短100 够用。num_classes 为 2 表示二分类正面/负面。vocab_size 取决于词表大小代码里通常从数据中统计生成。3.2 中文分词与词表映射LSTM 的输入是整数序列所以评论文本要先经过分词和词表映射。项目里用 jieba 做分词StopwordsCN.txt 过滤停用词import jieba def load_stopwords(pathStopwordsCN.txt): with open(path, r, encodingutf-8) as f: return set(line.strip() for line in f) stopwords load_stopwords() def text_to_ids(text, word2id, max_len100): words [w for w in jieba.lcut(text) if w not in stopwords] ids [word2id.get(w, word2id[UNK]) for w in words] # 截断或补齐到固定长度 if len(ids) max_len: return ids[:max_len] return ids [word2id[PAD]] * (max_len - len(ids))这里有两个容易忽略的点一是UNK和PAD必须在词表里有对应 ID否则映射会失败二是截断策略用前截断还是后截断会影响短文本情感判断京东评论的关键信息通常在前半段所以取前 100 个词更合理。3.3 批量预测与结果输出单条预测跑通后改成批量处理更实用def batch_predict(texts, sess, input_x, logits, word2id): results [] for i in range(0, len(texts), 64): batch texts[i:i64] batch_ids [text_to_ids(t, word2id) for t in batch] preds sess.run(tf.argmax(logits, 1), feed_dict{input_x: batch_ids}) results.extend(preds.tolist()) return resultsbatch size 设 64 是内存和速度的折中。如果显存不够就降到 32 或 16。输出结果是 0 或 1对应负面和正面。实际用的时候建议把原始评论和预测结果拼在一起输出 CSV方便人工抽检。提示如果你的数据里评分字段可用建议先用评分做一轮弱标注验证看模型预测和评分的一致性。一致性低于 70% 说明模型和你的数据分布不匹配需要重新微调。4. 训练自己的模型从 checkpoint 微调到全量训练4.1 微调已有 checkpoint如果只是想适配自己的商品评论不需要从零训练。加载 jd_comments_181_7_model 的权重换掉最后的分类层用少量标注数据微调即可# 加载已有权重排除最后的分类层 variables_to_restore tf.contrib.framework.get_variables_to_restore( exclude[dense_1, output_layer] ) saver tf.train.Saver(variables_to_restore) saver.restore(sess, models/jd_comments_181_7_model.model.lstm_model-40000) # 重新初始化分类层用新数据训练 optimizer tf.train.AdamOptimizer(learning_rate1e-4) train_op optimizer.minimize(loss)学习率设 1e-4 而不是默认的 1e-3是因为微调阶段权重已经接近最优大步长容易破坏已有特征。微调数据量建议至少 500 条正负样本比例接近 1:1。4.2 从零训练的参数配置如果预训练模型和你的领域差距太大比如从数码产品评论换到食品评论从零训练更稳妥。run.py 里通常有完整的训练入口核心参数如下参数建议值说明embedding_size128词向量维度太小欠拟合太大过拟合hidden_size128LSTM 隐藏层维度和 embedding 保持一致num_layers1单层 LSTM 足够两层容易过拟合learning_rate1e-3从零训练用默认值batch_size64根据显存调整num_epochs10观察验证集 loss 不再下降就停dropout0.5加在 embedding 和 LSTM 输出后训练过程中重点看验证集 loss 和准确率。如果训练 loss 下降但验证 loss 上升说明过拟合加 dropout 或减少 hidden_size。如果两个都不降检查词表是否覆盖了足够的领域词汇。4.3 模型保存与版本管理TensorFlow 1.x 的 Saver 默认保存最近 5 个 checkpoint通过 max_to_keep 控制saver tf.train.Saver(max_to_keep3) # 每 1000 步保存一次 if step % 1000 0: saver.save(sess, models/my_model, global_stepstep)保存出来的文件命名规则是my_model-{step}.data-00000-of-00001、.index、.meta。恢复时只需要指定前缀和步数比如models/my_model-5000。建议在文件名里带上日期或数据版本避免多个实验的 checkpoint 混在一起分不清。5. 避坑与排查跑这套源码最容易翻车的五个地方5.1 TensorFlow 版本不匹配导致 import 失败现象运行 run.py 时报AttributeError: module tensorflow has no attribute nn.rnn_cell或tf.placeholder不存在。原因代码基于 TensorFlow 1.x API 编写环境里装的是 2.x。2.x 移除了大量 1.x 接口。解决建独立虚拟环境装 tensorflow1.15.0。如果必须用 2.x可以用tf.compat.v1命名空间包一层但改动量大不如隔离环境。5.2 checkpoint 恢复时报 shape mismatch现象saver.restore时抛出ValueError: Shapes (128, 2) and (256, 2) are incompatible。原因重建计算图时的 hidden_size 或 num_classes 和训练时不一致。常见于改了参数但忘了同步改 restore 的图。解决打开 checkpoint 目录下的 checkpoint 文件里面记录了每层的变量名和 shape。对照着改代码里的参数确保 embedding_size、hidden_size、num_classes 完全一致。5.3 京东评论爬取返回空数据现象scrapy crawl 跑完CSV 里只有表头没有数据。原因京东评论接口对 Referer 和 Cookie 有校验或者商品 ID 对应的评论接口路径变了。解决先用浏览器打开商品页F12 抓一次评论请求对比 URL 和请求头。重点检查 Referer 是否带上了商品 ID以及 pageSize 是否被限制。如果接口返回{code:0,comments:[]}说明该商品没有公开评论或需要登录。5.4 分词后大量 UNK 导致预测全为同一类现象模型对任何输入都输出 0 或都输出 1。原因词表里没有覆盖输入文本的词汇所有词都映射成了UNK模型相当于在猜。解决检查词表生成逻辑确认训练数据的词汇被正确统计。如果换领域使用需要用新领域的语料重新生成词表并微调 embedding 层。5.5 训练 loss 不下降或震荡现象训练几个 epoch 后 loss 维持在 0.69 左右二分类的随机水平。原因学习率过大、数据标签错误、或者输入序列全被 padding 填充。解决先把学习率降到 1e-4 试一轮。然后抽 20 条数据打印出来确认 text_to_ids 的输出不是全零。最后检查标签列是否和评论内容对齐CSV 读取时列错位是常见问题。6. 把预测结果用起来批量推理与置信度过滤的一个技巧模型跑通之后真正影响使用体验的不是准确率本身而是怎么处理低置信度的预测。LSTM 最后输出的是 logits经过 softmax 后得到概率分布。如果正面概率是 0.51、负面是 0.49这条预测实际上没有参考价值。我一般会在推理阶段加一个置信度阈值def predict_with_confidence(texts, sess, input_x, logits, word2id, threshold0.7): results [] for i in range(0, len(texts), 64): batch texts[i:i64] batch_ids [text_to_ids(t, word2id) for t in batch] probs sess.run(tf.nn.softmax(logits), feed_dict{input_x: batch_ids}) for prob in probs: max_prob max(prob) label int(prob[1] prob[0]) if max_prob threshold: results.append((label, max_prob, low_confidence)) else: results.append((label, max_prob, ok)) return resultsthreshold 设 0.7 是个经验值。低于这个值的预测单独标记出来人工复核或者直接丢弃。这样最终输出的情感分布比全量硬分类更可信。如果业务上不能丢数据可以把低置信度的单独存一个文件后续用规则兜底。另一个实用技巧是滑动窗口预测。京东评论里有些长评前半段夸、后半段骂整条截断到 100 个词会丢失后半段信息。我一般会把超过 100 词的评论切成多个窗口分别预测再按窗口取多数票def sliding_predict(text, sess, input_x, logits, word2id, window100, stride50): words [w for w in jieba.lcut(text) if w not in stopwords] if len(words) window: return predict_with_confidence([text], sess, input_x, logits, word2id)[0] votes [] for start in range(0, len(words) - window 1, stride): segment .join(words[start:startwindow]) label, prob, status predict_with_confidence([segment], sess, input_x, logits, word2id)[0] votes.append(label) final_label max(set(votes), keyvotes.count) return final_label, len(votes), slidingstride 设 50 是窗口的一半保证相邻窗口有重叠避免在边界处切断关键短语。这个方法在长评论上的效果比单次截断稳定得多代价是推理时间线性增加。如果评论平均长度在 200 词以内用两次窗口就够了。从那以后我每次拿到一个新的情感分析模型都会先用滑动窗口跑一遍长文本再对比单次截断的结果。差异超过 10% 就说明截断策略需要调整不能直接上批量。希望帮到你。本文还有配套的精品资源点击获取