ARTICLE DETAIL

资讯详情

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

基于深度学习LSTM的京东商城评论情感分析实践

基于深度学习LSTM的京东商城评论情感分析实践 简介基于深度学习LSTM的情感分析完整项目面向计算机专业毕设或课程作业场景涵盖京东商城用户评论的爬取、预处理、模型训练、评估与可视化全流程。资源共39个文件压缩包约164.28MB主要包括8个Python脚本、1个训练好的.model模型、6组TensorFlow权重及索引文件、7张训练过程与结果图表、2个文本文件及readme说明等代码结构清晰便于对照学习。模型部分采用LSTM门控机制处理序列文本可有效捕捉评论中的长期情感依赖配套的JDSpider模块演示了Scrapy爬虫构建方式StopwordsCN等文件辅助完成分词和停用词过滤适合作为NLP入门与深度学习的综合实践参考。已有174人学习下载适合需要快速上手情感分析任务、参考项目架构或复现实验的学生与开发者。1. 基于深度学习 LSTM 的京东商城情感分析这份毕设资源包能直接跑通什么基于深度学习的 LSTM 情感分析放在京东商城真实评论数据上是毕业设计和课程作业里出现频率最高的选题之一。这份资源把完整链路一次性打包Scrapy 写的京东评论爬虫 JDSpider、中文停用词表 StopwordsCN.txt、训练好的模型文件 jd_comments_181_7_model.model以及一键训练的 run.py。你不用从零造数据拿到就能复现一条从爬评论到出情感极性的完整流程适合赶毕设、需要快速跑通并做改进的学生也适合想在一周内上手深度学习文本分类的初学者。2. 从 JDSpider 爬虫到语料清洗评论数据是怎么变成 LSTM 训练集的做情感分析的人都知道模型决定上限数据决定下限。这份资源里最容易被忽略的其实是 JDSpider 这个爬虫工程它负责把京东商城的真实评论抓下来才谈得上后面的 LSTM 训练。JDSpider 是标准 Scrapy 工程释放后能看到 scrapy.cfg、JDSpider 包和 README.md其中 scrapy.cfg 声明工程配置真正的爬虫代码在 JDSpider/JDSpider/spiders 目录下。先把这个环节拆透后面训练才不会因为数据质量翻车。2.1 JDSpider 工程结构Scrapy 抓取京东评论的接口与字段京东商城的商品评论不是服务端渲染的 HTML而是通过异步接口加载的。直接在商品页里用 requests 拿到的源码里根本没有评论文本需要模拟浏览器去请求评论的 ajax 接口。最常见的是 club.jd.com 下的 productPageComments.action参数 productId 对应当前商品 IDpage 从 0 开始翻页sortType 控制排序方式。下面这段是这类爬虫最典型的实现。# JDSpider/JDSpider/spiders/jd_comment.py # 抓取京东商品评论的 Scrapy 爬虫主体 import scrapy import json class JdCommentSpider(scrapy.Spider): name jd_comment # 商品 ID 从商品页 URL 里取比如 https://item.jd.com/100012043978.html product_id 100012043978 # 京东评论 ajax 接口page 从 0 开始pageSize 每页条数 api (https://club.jd.com/comment/productPageComments.action? productId{pid}score0sortType5page{page}pageSize10) def start_requests(self): for page in range(0, 50): yield scrapy.Request( self.api.format(pidself.product_id, pagepage), callbackself.parse_comment, headers{Referer: fhttps://item.jd.com/{self.product_id}.html} ) def parse_comment(self, response): data json.loads(response.text) for comment in data.get(comments, []): yield { content: comment.get(content, ).strip(), score: comment.get(score, 0), time: comment.get(creationTime, ), }这段代码里的 score0 表示取全量评价京东会把好评、中评、差评按打分 1 到 5 分档返回sortType5 是按时间排序方便抓最新评论。headers 里的 Referer 必须带京东会校验来源页面不带 Referer 的请求大概率返回空 JSON 或 403。yield 出去的字典会走 items.py 和 pipelines.py落盘成 csv 或 json这部分资源包里已经配好改一下商品 ID 就能跑。字段层面至少要把 content、score、creationTime 抓下来。score 是 1 到 5 的整数打分情感分析的标签可以直接从打分映射省掉手动标注的成本映射规则见下表。字段来源用途contentcomment.content模型输入语料scorecomment.score映射训练标签creationTimecomment.creationTime过滤过期或异常评论抓取量上我一般建议爬 30005000 条好中差评各留一档避免后面训练出来的模型把一切评论都判成好评这个坑在避坑章会专门展开。2.2 评论文本清洗与 StopwordsCN.txt 停用词表Scrapy 落盘后的原始评论噪音比想象中大。京东评论里高频出现 [图片] [视频] [追加] 这类占位标记还有 URL、emoji、重复标点和转义引号。这些噪音不清理会被 jieba 切成一堆无意义 token直接污染词表和 Embedding 训练。# data_clean.py 京东评论清洗的三段式正则 import re def clean_comment(text: str) - str: # 先去掉 [图片] [视频] [追加] 占位标记 text re.sub(r\[(图片|视频|追加)\], , text) # 再去 URL 和 emoji text re.sub(rhttps?://\S|www\.\S, , text) text re.sub(r[\U0001F300-\U0001F9FF], , text) # 合并连续标点保留句号作为语义边界 text re.sub(r[。]{2,}, 。, text) return text.strip()正则顺序不能反先删占位符再做 URL 去噪否则 URL 里的方括号会干扰占位符匹配。emoji 的 Unicode 区间在 Python 3 里可以直接用 \U0001F300-\U0001F9FF 匹配不用额外装库。# load_stopwords.py 加载中文停用词表 def load_stopwords(pathStopwordsCN.txt): with open(path, r, encodingutf-8) as f: return {line.strip() for line in f if line.strip()} stopwords load_stopwords() # 分词后用集合过滤tokens [w for w in jieba.cut(text) if w not in stopwords]StopwordsCN.txt 每行一个停用词用集合读进来天然去重。这里有个血泪教训停用词表绝对不能包含否定词。把不没别加进去不推荐会被切掉不只剩推荐模型直接判成正向整条语料的情感极性就反了。常见做法是只过滤的了啊吧这类无实义语气词以及东西感觉这种在评论里高频出现但对极性判断没帮助的词。清洗完的语料统一存成 utf-8 的 csv一列 content一列 label数据侧就绪可以进 run.py 训练模型了。3. LSTM 模型结构与训练参数run.py 里每个关键配置为什么这么设SentimentAnalysis 目录下的 run.py 是整个资源的执行入口做的事情可以拆成四步读语料、转序列、建模型、训练保存。很多人拿到手直接 python run.py 跑一次就完事但答辩时老师一定会问每个参数为什么这么设。这一章把参数从头到尾捋一遍顺便把 LSTM 为什么适合这个场景讲清楚。3.1 分词、词表与序列填充中文评论变成 LSTM 输入张量LSTM 不能直接吃中文文本。常见做法是先用 jieba 把句子切成词再用 Tokenizer 统计词频建词表把词映射成整数 id最后 pad_sequences 把不等长序列统一成固定长度。# tokenize.py 文本到序列的完整转换 import jieba from keras.preprocessing.text import Tokenizer from keras.preprocessing.sequence import pad_sequences MAX_LEN 100 # 每条评论统一截断/填充到 100 个词 VOCAB_SIZE 5000 # 词表保留出现频率前 5000 的词 def build_tokenizer(corpus): tokenizer Tokenizer(num_wordsVOCAB_SIZE, filters) # jieba 分词后用空格拼接交给 tokenizer 统计词频 tokens [ .join(jieba.cut(text)) for text in corpus] tokenizer.fit_on_texts(tokens) return tokenizer, tokens def texts_to_sequences(tokenizer, tokens): seqs tokenizer.texts_to_sequences(tokens) return pad_sequences(seqs, maxlenMAX_LEN, paddingpost, truncatingpost)MAX_LEN 取 100 是因为京东评论平均长度在 5080 个词100 能覆盖九成以上样本取太短会截掉转折句的尾部语义取太长则浪费显存。paddingpost 表示在句尾补 0LSTM 按顺序读完整句后最后一个时间步的隐藏状态才包含完整句意truncatingpost 表示超长部分从尾部截断。Tokenizer 的 filters 要关掉默认标点过滤因为中文分词后标点已经被 jieba 处理过再过滤反而破坏一致性。3.2 LSTM 门控机制为什么短文本情感分析选 LSTMLSTM 是 RNN 的变体核心是三套门控遗忘门决定上一时刻的细胞状态丢弃多少输入门决定当前候选信息写入多少输出门决定当前隐藏状态对外输出多少。这套门控让 LSTM 能以线性方式在细胞状态里传递远距离信息避开普通 RNN 梯度消失的问题。放到具体句子上看物流慢得离谱但客服处理很快总体还是满意的。慢和满意隔着十几个词CNN 的 n-gram 窗口设 3 或 5 都抓不住这种跨句转折普通 RNN 传到句尾也早就把开头忘了LSTM 的细胞状态像一条传送带门控只做有选择的加减让慢的负面信息保留到句尾再和满意结合判断整句极性。商品评论里这种转折句占比不低所以 LSTM 在短文本情感分析里一直是性价比很高的选择。3.3 run.py 的模型结构与训练超参模型结构在 run.py 里对应一段 Sequential典型配置如下。# run.py 中的模型构建代码 from keras.models import Sequential from keras.layers import Embedding, LSTM, Dense, Dropout model Sequential([ Embedding(VOCAB_SIZE, 128, input_lengthMAX_LEN), # 词向量 128 维 LSTM(128, dropout0.2, recurrent_dropout0.2), # 单层 LSTM 128 单元 Dense(64, activationrelu), Dropout(0.3), Dense(3, activationsoftmax) # 负向/中性/正向 三分类 ]) model.compile(optimizeradam, losscategorical_crossentropy, metrics[accuracy]) # 训练超参的常见起点 # epochs30, batch_size128, validation_split0.2 # 保存model.save(jd_comments_181_7_model.model)Embedding 层把每个词 id 映射成 128 维向量向量本身也是训练参数LSTM 单元数 128 在短文本上足够再往上就容易过拟合。dropout 和 recurrent_dropout 都设 0.2前者丢输入、后者丢循环连接主要防过拟合。loss 用 categorical_crossentropy 对应 softmax 三分类如果标签是 0/1/2 整数而不是 one-hot可以改用 sparse_categorical_crossentropy两者数学上等价。超参常用起点调参方向epochs30加 EarlyStopping 后通常 1020 轮收敛batch_size128显存不足降到 32learning_rateadam 默认 0.001不收敛降到 0.0001validation_split0.2数据量小于 3000 时提高到 0.3模型文件名 jd_comments_181_7_model.model 里的 181 和 7从命名习惯看大概率是第 181 轮或某个批次版本号具体含义以训练日志为准不影响加载使用。建议在 run.py 里补一个 EarlyStopping(monitorval_loss, patience3)避免最后几个 epoch 白跑导致过拟合这是答辩时最轻松的加分点。4. 加载 jd_comments_181_7_model.model 做预测单条推理、批量预测与阈值调优训练好的模型以 .model 后缀存放在 SentimentAnalysis 目录下是 Keras 保存的完整模型文件包含网络结构和权重load_model 直接就能用。预测环节最大的坑是预处理流程必须和训练时完全一致尤其是 tokenizer下面从加载到阈值调优逐步说。4.1 加载模型与单条评论预测跑预测前先确认两件事模型文件路径对得上tokenizer 是训练时保存下来的那个。分词、转 id、padding 任何一个环节和训练不一致预测结果都会失真。# predict.py 单条评论预测 from keras.models import load_model from keras.preprocessing.text import tokenizer_from_json from keras.preprocessing.sequence import pad_sequences import jieba # 训练时保存的 tokenizer预测时必须加载同一个 with open(tokenizer.json, r, encodingutf-8) as f: tokenizer tokenizer_from_json(f.read()) model load_model(jd_comments_181_7_model.model) MAX_LEN 100 def predict(text: str): tokens .join(jieba.cut(text)) seq tokenizer.texts_to_sequences([tokens]) vec pad_sequences(seq, maxlenMAX_LEN, paddingpost) proba model.predict(vec)[0] label [负向, 中性, 正向][proba.argmax()] return label, proba # 示例 print(predict(物流很快包装也严实下次还会回购))predict 返回形状为 [1, 3] 的概率数组三个维度分别对应负向、中性、正向和为 1argmax 取最大概率下标作为类别。如果训练时没导出 tokenizer.json预测时新建一个 Tokenizer 重新 fit那新评论里的词几乎全是 OOV输入序列全是 0模型输出自然变成单一固定类别。这是预测环节最典型的翻车点避坑章会再展开。实际使用中很少一条一条预测更多是给整个 csv 批量打标。批量预测时把整个批次同时喂给模型比循环调用 predict 快一个数量级# predict_batch.py 批量预测 def predict_batch(texts): tokens [ .join(jieba.cut(t)) for t in texts] seqs tokenizer.texts_to_sequences(tokens) vecs pad_sequences(seqs, maxlenMAX_LEN, paddingpost) proba model.predict(vecs) labels [[负向, 中性, 正向][p.argmax()] for p in proba] return labels, proba批量预测的优势在于 GPU 可以同时处理几十上百条样本吞吐量远高于单条循环不过要注意内存里一次性放太多序列也会撑爆显存1000 条一批是稳妥的分块大小。4.2 三分类概率与阈值调优直接 argmax 有个隐患三个概率都接近 0.34 时模型其实什么都没判出来只是被迫选了一类。毕业设计演示或实际业务里遇到这种样本硬给标签还不如返回不确定交给规则引擎或人工兜底。# threshold.py 阈值兜底 def predict_with_threshold(text: str, threshold: float 0.6): label, proba predict(text) if proba.max() threshold: return 不确定, proba return label, probathreshold 设得越高模型越倾向输出不确定留下的预测准确率越高但覆盖率越低。阈值 0.5 时几乎不拒判0.7 时可能三分之一的样本被判为不确定具体取值看你的验收标准。阈值准确率倾向覆盖率适用场景0.50低高全量自动打标0.60中中默认推荐0.70高低只取高置信样本还要注意中性类在真实京东评论里占比很低训练时中性样本太少模型会倾向永远不输出中性。常见做法是在清洗环节把 score 为 3 的评论复制两到三份做上采样让三个类别的数量差距控制在 3 倍以内否则准确率虚高但三分类根本没学全。提示调阈值不要拍脑袋拿 50 条人工标注好的样本把阈值从 0.5 到 0.8 扫一遍选准确率和覆盖率平衡点再定。概率输出还有一个加分用法把预测概率画成三根柱状图放在毕设系统界面里比只显示一个标签更能体现深度学习模型的判断过程很多高分答辩都靠这种细节撑场面。5. 避坑与常见问题排查复现这个情感分析项目最容易翻车的五个点资源包能跑通不代表换台机器、换批数据还能跑通。下面五条是我在类似毕设和作业里实际踩过的坑按现象、原因、解决三步写遇到问题直接对号入座。5.1 读评论文件报 UnicodeDecodeError现象打开爬虫导出的 csv 或 StopwordsCN.txt 时抛 UnicodeDecodeError报错位置停在 open() 那一行。原因京东页面原始编码是 GBK 系如果爬虫落盘时没显式指定 utf-8文件实际是 GBK 或混合编码Python 3 默认以 utf-8 读就直接崩。解决读文件时按 utf-8、gbk、gb18030 依次兜底写文件时统一 utf-8。# encode_fallback.py 读取时编码兜底 def safe_open_read(path): for enc in (utf-8, gbk, gb18030): try: return open(path, encodingenc).read() except UnicodeDecodeError: continue raise ValueError(f无法解码: {path})同样的逻辑要写进 Scrapy 的 pipelines.py落盘时用 utf-8 编码写入不要偷懒用默认编码否则换台 Windows 机器数据就花掉。Windows 下默认编码是 gbkLinux 下是 utf-8同一份代码两边跑出来的文件编码都可能不一样。5.2 训练时 OOM显存直接爆掉现象run.py 跑到 model.fit 时报 ResourceExhaustedError日志末尾是 OOM 或 CUDA out of memory。原因batch_size 大、序列又全部 padding 到 MAX_LEN、Embedding 维度高三件事叠加把显存撑爆。解决先降 batch_size128 降到 32 通常立刻缓解MAX_LEN 从 100 降到 80Embedding 维度 128 降到 64。如果还爆直接在 CPU 上跑 2000 条的小数据集 demo训练也就十几分钟本科毕设完全够用没必要硬上大模型。5.3 新评论预测结果永远是同一个类别现象模型加载成功但换任何一句话预测输出都是同一个标签概率分布几乎不变。原因预测时新建了 tokenizer 而不是加载训练时的词表输入句子的词全部 OOV序列全是 0LSTM 对全 0 输入只会输出一个固定分布。这也是答辩现场最容易翻车的点演示时预测全输出同一类基本就是词表没对齐。解决训练完立刻把 tokenizer 落盘预测前加载回来。# 训练后保存 tokenizer tokenizer_json tokenizer.to_json() with open(tokenizer.json, w, encodingutf-8) as f: f.write(tokenizer_json) # 预测前加载 tokenizer from keras.preprocessing.text import tokenizer_from_json with open(tokenizer.json, r, encodingutf-8) as f: tokenizer tokenizer_from_json(f.read())保存模型的同时保存 tokenizer.json这两个文件要像亲兄弟一样成对出现少一个预测就废。怀疑词表问题时打印 tokenizer.word_index 的前十个词就能确认如果加载进来的是空词表那一定是加载逻辑写错了。5.4 爬虫爬到一半返回空 JSON 或 403现象JDSpider 跑 2030 页之后接口返回 {comments:[],maxPage:0}或者直接 403 Forbidden。原因京东对评论接口有频控短时间请求过多触发限流缺 Referer 或 Cookie 也会被拒。解决在 settings.py 里设 DOWNLOAD_DELAY 3再配合随机的 0.52 秒 jitter请求头带齐 Referer 和 Cookie毕设爬 3000 条左右就停手别贪多。被限流后等 1015 分钟再继续硬刷只会把 IP 封得更久。5.5 load_model 报 Unknown layer 或找不到对象现象换一台机器执行 load_model(jd_comments_181_7_model.model)报 ValueError 或 Unknown layer: xxx。原因模型是用特定 Keras/TensorFlow 版本保存的新环境版本不一致H5 里的结构反序列化失败。解决先 pip freeze 对比已装环境把 keras 和 tensorflow 版本对齐到训练环境比如 keras 2.x 配 tensorflow 2.x。如果只是做预测演示临时建一个虚拟环境降级到保存时的版本跑完再删不用动主环境。加载前先看一眼文件头H5 格式和 TensorFlow SavedModel 目录的加载方式不一样别用错 API。6. 进阶验证与部署模型导出 ONNX 做 C 推理并用自测样本集验收模型能预测只是及格线答辩被追问一句能不能部署就会露怯。把 Keras 模型导出 ONNX再用 onnxruntime 的 C API 加载推理是这条路里投入产出比最高的一步。# export_onnx.py 用 tf2onnx 把 Keras 模型转成 ONNX import tf2onnx import tensorflow as tf model tf.keras.models.load_model(jd_comments_181_7_model.model) onnx_model, _ tf2onnx.convert.from_keras(model, opset13) with open(sentiment_lstm.onnx, wb) as f: f.write(onnx_model.SerializeToString())C 侧用 onnxruntime 的 C API 加载 sentiment_lstm.onnx输入 int64 [1, 100]输出 float [1, 3]。最容易出问题的不是 ONNX 本身而是分词一致性Python 端 jieba 分完词转 idC 端必须用同一个 tokenizer 词表做映射。我一般把 tokenizer.json 和词表一起拷过去在 C 里做表查询而不是重新分词两边输出才能对得上。验收环节固定一份自测样本集比任何指标都管用。我常用下面这些类型做回归验证样本类型示例期望明确好评质量很好下次还买正向明确差评收到就坏了差评负向转折句物流慢但东西不错正向否定句没有想象中好用负向中评一般般吧中性短评还行中性验收标准定为 50 条样本模型判断与人工标注一致率不低于 80%其中明确好评和明确差评必须全对。从那以后我每次改词表、改 MAX_LEN、动模型结构都强制把同一份 50 条自测集从头跑一遍记录概率分布和一致率确认没有隐性回归。这种笨办法比任何玄学调参都管用希望帮到你。本文还有配套的精品资源点击获取
返回列表