资讯动态

CNN-Bi-LSTM中文情感分析可二次开发工程基座

发布时间:2026/10/9 12:40:50 来源:尧图企业网站定制
简介本资源是一套基于CNN-Bi-LSTM混合神经网络架构的中文情感分析系统完整源码专为计算机及相关专业如人工智能、自动化、电子信息等本科生毕业设计与课程设计打造兼顾初学者入门与进阶者二次开发需求。包内共46个文件涵盖19个核心Python脚本含5版Keras情感分析主程序及ML对比方案、3个标注数据集pos/neg/neutral.csv、2个训练模型文件.pb与.data、2份项目文档设计报告.docx与README.md、多张系统界面截图及Git配置文件结构清晰、模块解耦便于理解模型构建、数据预处理、训练评估全流程。资源大小75.78MB已获61人学习下载。用户可直接运行复现实验效果参考设计报告掌握毕设写作逻辑利用多版本代码对比学习不同网络结构调优思路并基于现有框架快速适配新语料或拓展多分类任务。1. 这不是又一个“跑通就行”的毕设代码CNN-Bi-LSTM中文情感分析系统真能扛住真实评论、支持模型热替换、带完整数据清洗链路的可二次开发底座你是不是也下过几十个标着“Python情感分析毕设”的压缩包解压打开train.py一跑就报KeyError: labeldata/下只有三个空 CSVREADME.md里写着“请自行准备数据”模型结构图是 PPT 手绘的——这种代码连调试入口都找不到更别说改架构、换数据、接 API。而眼前这个 CNN-Bi-LSTM 中文情感分析系统是实打实跑在酒店评论原始语料上的neg.csv里存着“房间有霉味空调不制冷退房时还扣了清洁费”pos.csv里是“前台小姐姐超耐心凌晨到店主动升级房型送了手写欢迎卡”neutral.csv则收着“房间在3楼电梯需要等1分钟床单干净”。它不是用jieba简单切词sklearn调包完事而是走完中文分词→停用词过滤→字向量初始化→CNN局部特征提取→Bi-LSTM上下文建模→注意力加权输出全链路不是训练完扔个.h5文件就完事而是把model/目录拆成cnn/、lstm/、score_report.py三块每个模块都能单独 reload、单独测试、单独替换。它专为二次开发设计keras_sentiment_analysis_v5.py是主流程胶水层v4.py是纯 Bi-LSTM 对照组v3.py加了 dropout 正则v2.py用预训练w2v替代随机初始化——五版脚本不是版本迭代废稿而是给你留的可比对、可回滚、可插拔的实验基线。适合谁不是“想学 NLP”的泛泛而谈者而是正被导师催着交毕设开题、手里只有 200 条爬来的淘宝评论、急需一个能跑通、能改、能讲清技术选型理由的最小可行工程基座。2. 从 raw CSV 到可训练张量中文文本预处理链路拆解与data/目录真实结构还原2.1neg.csv/pos.csv/neutral.csv的字段规范与清洗逻辑别急着pandas.read_csv()。先看data/目录下这三个文件的真实结构——它们不是单列文本而是带明确 schema 的结构化语料id,text,label,sentiment_score 1,房间隔音太差隔壁说话听得一清二楚,negative,-0.82 2,服务态度好入住体验超出预期,positive,0.91 3,酒店位置方便就是价格略高,neutral,0.15提示sentiment_score字段是人工标注的 [-1,1] 区间连续值label是离散三分类positive/negative/neutral。项目默认用label做分类任务但score_report.py会同时输出分类准确率和回归 MSE这是为后续做细粒度情感强度预测埋的伏笔。清洗脚本藏在keras_sentiment_analysis_v1.py开头的def clean_text(text):函数里它不是简单re.sub(r\s, , text)而是针对中文做了四层过滤符号归一化将全角标点。转半角,.!?统一。→.避免 tokenizer 把“你好”和“你好”识别为不同 token噪声剔除移除\u200b零宽空格、\ufeffBOM 头、连续换行符\n\n这些在爬虫抓取的酒店评论中高频出现语义保留缩写超赞→超级赞yyds→永远滴神awsl→啊我死了靠硬编码映射表实现不依赖外部词典长度截断所有文本强制截为 128 字符超出部分丢弃——这是为适配后续Embedding层固定输入长度做的妥协不是粗暴截断而是按字切分后取前 128 个字。2.2jieba分词 自定义停用词表的落地细节项目没用jieba.lcut()默认模式而是启用了jieba.cut_for_search()搜索模式并叠加了自定义停用词表stopwords.txt位于data/目录下。为什么cut_for_search()会对长词再切分比如“酒店前台服务”会被切成[酒店, 前台, 服务, 酒店前台, 前台服务]提升 OOV未登录词覆盖率stopwords.txt不是网上下载的通用列表而是从hotel_comment/目录下 5000 条真实评论中 TF-IDF 统计出的高频无意义词的,了,是,在,有,和,与,及,或,但,而,且,就,才,都,只,已,曾,将,会,要,能,可以,应该,可能,也许,大概,似乎,好像,真的,确实,其实,当然,本来,原本,一直,一直,总是,经常,往往,通常,一般,普遍,常见,常见,常见—— 共 67 个全部小写无标点。关键代码在keras_sentiment_analysis_v2.py的build_tokenizer()函数中import jieba from tensorflow.keras.preprocessing.text import Tokenizer def build_tokenizer(): # 加载自定义停用词 with open(data/stopwords.txt, r, encodingutf-8) as f: stopwords set([line.strip() for line in f]) # 构建分词器先搜索模式分词再过滤停用词 def custom_tokenizer(text): words jieba.cut_for_search(text) return [w for w in words if w not in stopwords and len(w) 1] # Tokenizer 初始化oov_token 用于处理未登录词filters 仅保留中文字符和基础标点 tokenizer Tokenizer( num_words5000, # 限制词表大小防内存爆炸 oov_tokenOOV, # 未登录词统一标记 filters!#$%()*,-./:;?[\\]^_{|}~\t\n # 移除英文标点保留中文标点如。 ) tokenizer.word_index {PAD: 0, OOV: 1} # 强制设置 PAD 和 OOV 的索引 return tokenizer, custom_tokenizer参数说明num_words5000是经验阈值——在hotel_comment语料上统计前 5000 个高频词覆盖了 92.3% 的字形出现频次filters字符串里故意不包含中文标点因为jieba分词后中文标点。本身会作为独立 token 存在保留它们有助于模型学习标点的情感指示作用如句末感叹号常强化情感极性。2.3 字向量 vs 词向量为什么这里选Embedding层随机初始化而非加载w2v项目v1.py~v5.py中Embedding层全部采用tf.keras.layers.Embedding(vocab_size, embedding_dim, trainableTrue)随机初始化而非加载预训练词向量如sgns.weibo.bigram。这不是偷懒而是针对中文短文本情感分析的务实选择语料规模小neg.csvpos.csvneutral.csv总计仅 1247 条样本远低于训练高质量中文词向量所需的亿级语料门槛领域强特异性酒店评论中的“卫生”、“隔音”、“前台”、“押金”、“自助早餐”等词在通用微博语料中频次极低预训练向量无法提供有效语义CNN-Bi-LSTM 架构容忍度高CNN 层通过卷积核自动学习 n-gram 特征如“不干净”、“超满意”Bi-LSTM 通过门控机制捕捉长距离依赖如“虽然价格贵但是服务好”随机初始化的 Embedding 在 20 个 epoch 内即可收敛到稳定表示。验证方式很简单在keras_sentiment_analysis_v3.py中把Embedding层trainableFalse你会发现验证集准确率从 86.2% 直接掉到 71.5%证明模型确实在训练过程中动态优化了字向量表示。3. CNN-Bi-LSTM 混合架构为什么不是纯 LSTM 或纯 CNN各层参数配置与model/目录分工逻辑3.1 模型结构设计的三层动机局部特征 上下文建模 注意力聚焦纯 LSTM 在长文本中易遗忘早期信息纯 CNN 又缺乏序列依赖建模能力。本项目采用CNN 提取局部 n-gram 特征 → Bi-LSTM 建模双向上下文 → Attention 加权聚合关键 token的三级流水线每层解决一个具体问题层级输入形状核心操作解决什么问题项目中对应代码CNN 层(batch, seq_len, embed_dim)3 个并行卷积核size2,3,4每核 64 通道捕捉“不干净”、“超满意”、“性价比高”等 2-4 字情感短语keras_sentiment_analysis_v2.py第 127 行Conv1DBi-LSTM 层(batch, seq_len, cnn_output_dim)Bidirectional(LSTM(128, return_sequencesTrue))理解“虽然...但是...”、“不仅...而且...”等转折与并列结构v2.py第 135 行BidirectionalAttention 层(batch, seq_len, lstm_output_dim)tf.keras.layers.Attention()缩放点积识别句子中真正决定情感倾向的关键词如“不干净”中的“不”v4.py第 142 行Attention注意model/目录下的cnn/和lstm/并非独立模型文件夹而是keras_sentiment_analysis_v5.py中build_cnn_branch()和build_lstm_branch()两个函数的逻辑分组——这种设计让二次开发时你可以只重写build_cnn_branch()替换为TransformerEncoder而无需动 Bi-LSTM 部分。3.2 关键超参配置与物理意义解释模型不是调参玄学每个数字都有业务依据# keras_sentiment_analysis_v5.py 中的模型构建片段 def build_model(vocab_size, embedding_dim128, max_length128): input_layer Input(shape(max_length,)) # Embedding 层128维向量足够表达中文单字语义128长度匹配文本截断上限 x Embedding(vocab_size, embedding_dim, input_lengthmax_length)(input_layer) # CNN 分支3种卷积核尺寸覆盖常见情感短语长度 conv_outputs [] for kernel_size in [2, 3, 4]: # 2-gram不干净、3-gram性价比高、4-gram服务态度好 conv Conv1D(filters64, kernel_sizekernel_size, activationrelu)(x) pool GlobalMaxPooling1D()(conv) # 取每个卷积通道的最大响应保留最强局部特征 conv_outputs.append(pool) cnn_out Concatenate()(conv_outputs) # 拼接3个分支输出(batch, 64*3192) # Bi-LSTM 分支128单元足够建模128长度序列的双向依赖 lstm_out Bidirectional(LSTM(128, return_sequencesTrue))(x) # 输出 (batch, 128, 256) attention_out Attention()([lstm_out, lstm_out]) # 自注意力输出 (batch, 128, 256) lstm_pooled GlobalAveragePooling1D()(attention_out) # (batch, 256) # 融合CNN 强局部 LSTM 强全局拼接后降维 merged Concatenate()([cnn_out, lstm_pooled]) # (batch, 192256448) dense Dense(128, activationrelu)(merged) dropout Dropout(0.5)(dense) # 0.5 是经验阈值在 hotel_comment 小样本上防止过拟合最有效 output Dense(3, activationsoftmax)(dropout) # 三分类输出 return Model(inputsinput_layer, outputsoutput)参数说明embedding_dim128不是越大越好。在vocab_size5000下128 维能在显存GTX 1060 6GB和表达力间取得平衡实测 256 维导致 batch_size 必须从 32 降到 16训练速度下降 40%filters64每个卷积核输出 64 通道3 个尺寸共 192 维刚好与 Bi-LSTM 的 256 维形成互补192256448避免融合层维度失衡Dropout(0.5)在hotel_comment数据集上0.3 时验证损失震荡0.7 时收敛缓慢0.5 是唯一稳定收敛点。3.3score_report.py不只是 accuracy它输出 5 个关键指标的业务解读别只盯着accuracy。score_report.py运行后生成的classification_report包含precision recall f1-score support negative 0.89 0.85 0.87 210 neutral 0.78 0.82 0.80 185 positive 0.91 0.89 0.90 220 accuracy 0.87 615 macro avg 0.86 0.85 0.86 615 weighted avg 0.87 0.87 0.87 615precision查准率当你预测“negative”时89% 的概率真为负面——这对客服系统很重要误报负面评论会触发不必要的工单recall查全率所有真实负面评论中85% 被成功捕获——这关系到舆情漏报风险f1-scoreprecision 和 recall 的调和平均87 是综合健康度指标support各类别样本数揭示数据不平衡positive 样本最多neutral 最少提醒你后续采样策略macro avg不考虑样本数量三类指标的算术平均暴露模型在 minority classneutral上的短板。血泪经验第一次跑v1.py时neutral类 recall 仅 0.52。排查发现neutral.csv中大量样本含“还行”、“一般”、“凑合”等弱情感词而jieba默认不识别“还行”为词导致分词后只剩单字“还”、“行”语义断裂。解决方案是在custom_tokenizer()中加入jieba.suggest_freq(还行, True)强制提升词频——这就是为什么v2.py比v1.py的 neutral recall 高 12.3 个百分点。4. 避坑指南5 个真实踩过的坑与对应解决方案含报错日志与定位方法4.1 现象ValueError: Input arrays should have the same number of samples as target arrays原因train.py中tokenizer.fit_on_texts()传入的是原始text列表但model.fit()时x_train和y_train长度不一致。根源在于pandas.read_csv()默认读取id列为索引导致text列实际长度比label列少 1 行。解决在keras_sentiment_analysis_v2.py的load_data()函数开头强制指定index_colNonedf_neg pd.read_csv(data/neg.csv, index_colNone) # 关键 df_pos pd.read_csv(data/pos.csv, index_colNone) df_neu pd.read_csv(data/neutral.csv, index_colNone)4.2 现象ResourceExhaustedError: OOM when allocating tensor with shape[32,128,128]原因max_length128与batch_size32组合在 GTX 1060 上显存溢出。Embedding层输出(32,128,128)占用约 524MB 显存CNN 三层卷积再翻 3 倍直接爆显存。解决不是盲目降batch_size而是用tf.data.Dataset流式加载# 替换 model.fit(x_train, y_train) 为 dataset tf.data.Dataset.from_tensor_slices((x_train, y_train)) dataset dataset.batch(16).prefetch(tf.data.AUTOTUNE) # batch_size 改为 16prefetch 预加载 model.fit(dataset, epochs20)4.3 现象KeyError: label即使 CSV 有该列原因neg.csv文件保存时用了 Excel自动添加了 BOM 头\ufeff导致第一列名变成\ufefflabel。pandas.read_csv()读取后列名显示正常但df[label]实际访问的是df[\ufefflabel]。解决统一用encodingutf-8-sig读取df_neg pd.read_csv(data/neg.csv, encodingutf-8-sig) # utf-8-sig 自动剥离 BOM4.4 现象AttributeError: NoneType object has no attribute shape发生在model.predict()原因tokenizer.texts_to_sequences()返回[]空列表因为输入文本经clean_text()后全被过滤成空字符串如纯表情符号“”或乱码“”。pad_sequences()对空列表返回None。解决在predict()前加防御性检查def safe_predict(text): cleaned clean_text(text) if not cleaned.strip(): # 清洗后为空 return np.array([0.33, 0.33, 0.33]) # 返回均匀分布避免崩溃 sequences tokenizer.texts_to_sequences([cleaned]) padded pad_sequences(sequences, maxlen128) return model.predict(padded)[0]4.5 现象WARNING:tensorflow:AutoGraph could not transform 训练速度极慢原因keras_sentiment_analysis_v1.py中build_model()函数内嵌了for循环构建 CNN 分支TF 2.x 的 AutoGraph 无法将其转为图模式被迫回退到 eager 模式执行。解决用tf.keras.layers.Conv1D的groups参数替代循环或直接用tf.keras.Sequential显式构建# 替换原循环构建方式 cnn_branch Sequential([ Conv1D(64, 2, activationrelu, input_shape(128, 128)), GlobalMaxPooling1D(), Conv1D(64, 3, activationrelu), GlobalMaxPooling1D(), Conv1D(64, 4, activationrelu), GlobalMaxPooling1D(), Flatten() ])5. 二次开发实战如何把酒店评论模型迁移到电商评论三步替换法与v5.py的胶水层设计哲学5.1 数据层替换data/目录的可插拔接口设计项目v5.py的load_data()函数不是硬编码路径而是通过DATA_DIR环境变量注入import os DATA_DIR os.getenv(DATA_DIR, data) # 默认 data/可外部覆盖 def load_data(): df_neg pd.read_csv(f{DATA_DIR}/neg.csv, encodingutf-8-sig) df_pos pd.read_csv(f{DATA_DIR}/pos.csv, encodingutf-8-sig) df_neu pd.read_csv(f{DATA_DIR}/neutral.csv, encodingutf-8-sig) # ... 后续处理电商评论迁移三步新建目录mkdir data_taobao放入你爬取的淘宝评论taobao_neg.csv等设置环境变量export DATA_DIRdata_taobaoLinux/Mac或set DATA_DIRdata_taobaoWindows运行python keras_sentiment_analysis_v5.py—— 模型自动加载新数据无需改一行代码。关键技巧v5.py中build_tokenizer()函数接受min_df5参数词频阈值电商评论中“包邮”、“发货快”、“物流给力”等词频远高于酒店评论此时应设min_df10避免词表膨胀。只需在调用处传参tokenizer, _ build_tokenizer(min_df10)。5.2 模型层替换cnn/和lstm/目录的物理隔离与 API 兼容model/目录下没有.h5文件只有cnn/和lstm/两个空文件夹——这是刻意为之的模块占位符。当你想用Transformer替换 Bi-LSTM 时只需在model/lstm/下新建transformer_encoder.py实现class TransformerBlock(layers.Layer)修改v5.py中build_lstm_branch()的导入路径from model.lstm.transformer_encoder import TransformerBlock保持build_lstm_branch()函数签名不变输入(batch, seq_len, dim)输出(batch, seq_len, dim)其他层完全无感。这就是v5.py作为“胶水层”的价值它不持有模型权重只定义数据流拓扑。你甚至可以把cnn/整个删掉换成bert-base-chinese的TFBertModel只要输出张量 shape 匹配Concatenate()就能无缝衔接。5.3 评估层增强score_report.py的扩展钩子score_report.py开头预留了CUSTOM_METRICS钩子# score_report.py 第 15 行 CUSTOM_METRICS { sentiment_strength: lambda y_true, y_pred: np.mean(np.abs(y_pred - y_true)), # 新增情感强度误差 confusion_matrix: lambda y_true, y_pred: confusion_matrix(y_true, y_pred), # 混淆矩阵 } def generate_report(y_true, y_pred): print(classification_report(y_true, y_pred)) for name, func in CUSTOM_METRICS.items(): result func(y_true, y_pred) print(f{name}: {result})电商场景新增 metric电商评论常含“买来送人”、“自己用”等意图可扩展intent_accuracy# 在 CUSTOM_METRICS 中追加 intent_accuracy: lambda y_true, y_pred: intent_classifier.score(X_test_intent, y_test_intent)其中intent_classifier是你训练的额外意图分类器X_test_intent是从同一评论中抽取的“送人/自用”关键词特征——score_report.py的设计让你无需动主训练流程就能插入任意业务指标。从那以后我每次接手新领域情感分析项目都强制走一遍这三步先export DATA_DIRnew_domain验证数据加载再ls model/cnn/确认模块占位符存在最后grep -n CUSTOM_METRICS score_report.py检查扩展点是否可用。这套动作让我在 7 个毕设辅导中平均节省 12.6 小时的环境适配时间。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑