资讯动态

Seq2seq-LSTM-Attention中文聊天机器人毕业设计

发布时间:2026/9/16 11:00:41 来源:尧图企业网站定制
简介本资源是一套完整的本科毕业设计项目实现面向人工智能方向初学者与NLP实践者聚焦于融合情绪识别能力的智能聊天机器人开发。项目基于Seq2seq框架集成LSTM编码器-解码器与Attention机制在TensorFlow 2.0Keras环境下完成模型训练并通过VueHTMLAjax构建交互式网页端支持实时对话与用户抑郁倾向初步判别。资源包共45个文件涵盖11个核心Python脚本含数据预处理、模型训练、推理服务、4个Jupyter Notebook含数据加载、非Attention/Attention双版本推理对比、6个pkl/npy模型参数与词表文件、4个HTML前端页面及2个h5模型权重整体体积66.81MB结构清晰、模块解耦度高。目前已有191人学习下载读者可直接复现从文本清洗、Seq2seq建模、情绪分类训练到前后端联调的全流程获取带标签的中文对话数据集qingyun.tsv、预训练词向量Word2Vec_java.pkl、Attention增强推理代码及部署用Flask服务脚本server.py具备强教学参考与工程迁移价值。1. 这不是玩具级聊天机器人一个带情绪感知能力的 Seq2seq-LSTM-Attention 毕业设计系统你打开网页输入“最近睡不好总觉得心慌”机器人不仅回复“听起来你压力很大要不要试试深呼吸”还会在后台悄悄跑完一个二分类模型把“抑郁倾向0.83”写进日志——这不是演示视频里的特效而是这个毕业设计项目真实可复现的能力。它用标准 Seq2seq 架构打底LSTM 作为编码器/解码器核心再叠加自定义 Attention 机制提升长句对齐质量情绪检测模块不依赖外部 API而是基于带标签的中文对话数据qingyun.tsv训练独立分类器与聊天主干模型解耦但协同调用。整个系统跑在 TensorFlow 2.0 Keras 上前端用 Vue Ajax 实现无刷新交互后端 server.py 仅暴露/chat和/detect-emotion两个轻量接口。适合本科毕设、课程设计或想深入理解 NLP 多任务协同落地的同学你既能看清 LSTM 遗忘门如何处理上下文衰减也能动手改chatbot2_inference_Attention.ipynb里的注意力权重可视化逻辑更关键的是——所有代码、预训练权重lstm_java_total.h5、词表word_to_index.pkl、甚至中文字体simkai.ttf全打包在 zip 里无需从零爬语料、训模型、搭环境。提示项目默认情绪检测目标是“抑郁倾向”但标签体系可替换。qingyun.tsv中每行格式为文本\t标签如“我好累啊\t1”其中 1 表示抑郁倾向阳性0 为阴性。这不是临床诊断工具而是教学级情绪分类范例。2. 从数据到模型Seq2seq-LSTM-Attention 的完整构建链路2.1 文本预处理中文分词、序列对齐与 padding 的三重约束中文文本不能直接喂给 LSTM必须先解决三个问题如何切词、如何统一长度、如何保留语义边界。该项目采用langconv.pyzh_wiki.py做简繁转换归一化再用jieba虽未显式 import但dataset.py中cut_words()函数隐含调用进行分词。关键在于get_data.ipynb中的pad_sequences逻辑from tensorflow.keras.preprocessing.sequence import pad_sequences # 假设已加载 word_to_index.pkl 得到 word2idx 字典 def load_and_pad_data(file_path, max_len20): questions, answers [], [] with open(file_path, r, encodingutf-8) as f: for line in f: if \t not in line: continue q, a line.strip().split(\t) q_seq [word2idx.get(w, word2idx[UNK]) for w in jieba.cut(q)] a_seq [word2idx.get(w, word2idx[UNK]) for w in jieba.cut(a)] # 注意decoder 输入需加 START输出需加 END q_padded pad_sequences([q_seq], maxlenmax_len, paddingpost, valueword2idx[PAD])[0] a_in [word2idx[START]] a_seq a_out a_seq [word2idx[END]] a_padded_in pad_sequences([a_in], maxlenmax_len1, paddingpost, valueword2idx[PAD])[0] a_padded_out pad_sequences([a_out], maxlenmax_len1, paddingpost, valueword2idx[PAD])[0] questions.append(q_padded) answers.append((a_padded_in, a_padded_out)) return np.array(questions), np.array(answers)这段代码强制要求encoder 输入长度固定为 20decoder 输入/输出长度固定为 21因加了START/END。pad_sequences的paddingpost确保填充在末尾避免干扰 LSTM 时间步的语义重心valueword2idx[PAD]使用专用 PAD token 而非 0防止与词汇表中真实词 ID 冲突。若你遇到ValueError: Input arrays should have the same number of samples大概率是questions和answers维度不匹配——检查jieba.cut()是否返回空列表或max_len是否小于最长句词数。2.2 模型架构LSTM 编码器 Attention 解码器的 PyTorch 式实现TensorFlow 2.0 版项目实际使用 Keras Functional API 构建双路 Seq2seq但chatbot2_inference_Attention.ipynb揭示了 Attention 的核心计算逻辑。它并非 Keras 内置Attention层而是手动实现 Luong-style attention加性 attentionimport tensorflow as tf from tensorflow.keras.layers import Dense, LSTM, Input, Embedding, Concatenate, TimeDistributed # Encoder encoder_inputs Input(shape(None,)) encoder_embedding Embedding(vocab_size, embedding_dim)(encoder_inputs) encoder_lstm LSTM(units256, return_stateTrue, return_sequencesTrue) encoder_outputs, state_h, state_c encoder_lstm(encoder_embedding) encoder_states [state_h, state_c] # Decoder with Attention decoder_inputs Input(shape(None,)) decoder_embedding Embedding(vocab_size, embedding_dim)(decoder_inputs) decoder_lstm LSTM(units256, return_sequencesTrue, return_stateTrue) decoder_outputs, _, _ decoder_lstm(decoder_embedding, initial_stateencoder_states) # Attention mechanism: compute context vector per decoder timestep # Step 1: Expand decoder outputs to match encoder_outputs shape for dot product decoder_expanded tf.expand_dims(decoder_outputs, 2) # (batch, dec_len, 1, units) encoder_expanded tf.expand_dims(encoder_outputs, 1) # (batch, 1, enc_len, units) # Step 2: Dot product - alignment scores attention_scores tf.reduce_sum(decoder_expanded * encoder_expanded, axis-1) # (batch, dec_len, enc_len) # Step 3: Softmax over encoder timesteps attention_weights tf.nn.softmax(attention_scores, axis-1) # (batch, dec_len, enc_len) # Step 4: Weighted sum of encoder outputs context_vector tf.matmul(attention_weights, encoder_outputs) # (batch, dec_len, units) # Step 5: Concatenate context with decoder output decoder_combined Concatenate(axis-1)([decoder_outputs, context_vector]) # Final output layer output TimeDistributed(Dense(vocab_size, activationsoftmax))(decoder_combined)这段代码的关键参数说明units256LSTM 隐藏层维度直接影响模型容量和显存占用。项目中.h5模型文件对应此配置return_sequencesTrue确保 encoder 输出每个时间步的隐藏状态供 Attention 计算tf.expand_dims(..., 2)和tf.expand_dims(..., 1)是实现 batch-wise 矩阵乘法的 trick避免 for 循环attention_weights形状为(batch, dec_len, enc_len)可直接用plt.imshow()可视化对齐热力图。注意lstm_java_total.h5是训练好的完整模型包含 encoder、decoder 和 attention 权重。若你修改units或embedding_dim必须重新训练否则model.load_weights()会报ValueError: Layer weight shape错误。2.3 情绪检测模块独立于聊天主干的二分类子模型情绪检测不是 Seq2seq 的副产品而是单独训练的 CNNLSTM 混合模型见train.py中build_emotion_model()。它接收原始用户输入文本经同样分词、索引、padding 后输入一个轻量级网络def build_emotion_model(vocab_size, embedding_dim, max_len): inputs Input(shape(max_len,)) x Embedding(vocab_size, embedding_dim)(inputs) x Conv1D(64, 5, activationrelu)(x) # 提取局部 n-gram 特征 x MaxPooling1D(5)(x) x LSTM(128, dropout0.3, recurrent_dropout0.3)(x) # 捕捉长程情绪线索 x Dense(64, activationrelu)(x) outputs Dense(1, activationsigmoid)(x) # 二分类抑郁倾向概率 return Model(inputs, outputs) emotion_model build_emotion_model(vocab_size5000, embedding_dim100, max_len50) emotion_model.compile(optimizeradam, lossbinary_crossentropy, metrics[accuracy])该模型与聊天机器人解耦server.py中detect_emotion()函数先调用此模型预测再将结果注入响应 JSON。qingyun.tsv是其唯一训练数据源共 12,478 行标注样本。若你希望检测“焦虑”而非“抑郁”只需替换qingyun.tsv中的标签列并将Dense(1)改为Dense(2, activationsoftmax)同时调整损失函数为categorical_crossentropy。3. 从前端到部署Vue Ajax Flask 的轻量级服务闭环3.1 前端交互逻辑Vue 实例如何驱动实时聊天与情绪反馈chat.html中的 Vue 实例并非简单绑定 input而是通过axios封装了两层异步调用!-- chat.html 片段 -- div idapp div v-formsg in messages :keymsg.id classmessage span classrole{{ msg.role }}/span span classcontent{{ msg.text }}/span !-- 情绪状态仅显示给用户不发送给后端 -- span v-ifmsg.role user classemotion-tag {{ msg.emotion ? 情绪${msg.emotion} : }} /span /div input v-modelinputText keyup.entersendChat placeholder输入消息... / /div script new Vue({ el: #app, data: { messages: [], inputText: , // 注意emotionResult 存储上一条用户消息的情绪检测结果 emotionResult: null }, methods: { async sendChat() { if (!this.inputText.trim()) return; // 步骤1添加用户消息暂无情绪标签 this.messages.push({ role: user, text: this.inputText, id: Date.now() }); const userMsg this.inputText; this.inputText ; try { // 步骤2并发调用聊天接口和情绪检测接口 const [chatRes, emotionRes] await Promise.all([ axios.post(/chat, { message: userMsg }), axios.post(/detect-emotion, { text: userMsg }) ]); // 步骤3更新用户消息的情绪标签 const userMsgIndex this.messages.length - 1; this.messages[userMsgIndex].emotion emotionRes.data.score 0.5 ? 抑郁倾向高 : 情绪平稳; // 步骤4添加机器人回复 this.messages.push({ role: bot, text: chatRes.data.response, id: Date.now() 1 }); } catch (err) { console.error(请求失败:, err); this.messages.push({ role: bot, text: 抱歉服务暂时不可用, id: Date.now() 1 }); } } } }); /script这里的关键设计点Promise.all([...])实现聊天与情绪检测的并行请求避免串行等待导致响应延迟emotionResult不作为聊天上下文输入仅用于前端展示符合隐私设计原则v-ifmsg.role user确保情绪标签只出现在用户消息旁不污染 bot 回复。3.2 后端服务Flask 如何安全暴露两个独立接口server.py是极简 Flask 应用核心在于模型加载时机和线程安全from flask import Flask, request, jsonify import tensorflow as tf import numpy as np from chatbot import ChatBot # 加载 Seq2seq 模型 from infer import EmotionDetector # 加载情绪检测模型 app Flask(__name__) # 全局单例避免每次请求都 reload 模型 chat_bot ChatBot(model_pathmodels/lstm_java_total.h5, word2idx_pathword_to_index.pkl, idx2word_pathindex_to_word.pkl) emotion_detector EmotionDetector(model_pathmodels/emotion_model.h5, word2idx_pathword_to_index.pkl) app.route(/chat, methods[POST]) def chat(): data request.get_json() user_input data.get(message, ) if not user_input: return jsonify({response: 请输入内容}) # 调用 Seq2seq 模型生成回复 response chat_bot.predict(user_input) return jsonify({response: response}) app.route(/detect-emotion, methods[POST]) def detect_emotion(): data request.get_json() text data.get(text, ) if not text: return jsonify({score: 0.0, label: invalid}) # 调用情绪检测模型 score emotion_detector.predict(text) label 抑郁倾向高 if score 0.5 else 情绪平稳 return jsonify({score: float(score), label: label}) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse) # 生产环境务必关闭 debug参数说明与部署注意debugFalse开发时可开但部署必须关闭否则暴露调试信息host0.0.0.0允许外部访问但需配合防火墙策略ChatBot和EmotionDetector类在初始化时完成tf.keras.models.load_model()后续请求复用同一实例避免重复加载耗时若你遇到OSError: Unable to open file检查models/目录下lstm_java_total.h5和emotion_model.h5是否存在且路径正确。3.3 静态资源与字体为什么 simkai.ttf 是必需的static/font/simkai.ttf被templates/chat.html中 CSS 显式引用font-face { font-family: SimKai; src: url(/static/font/simkai.ttf) format(truetype); } body { font-family: SimKai, Microsoft YaHei, sans-serif; }原因在于qingyun.tsv和用户输入均为简体中文而部分 Linux 服务器默认字体如 DejaVu Sans对中文支持不佳导致网页中出现方框乱码。simkai.ttf华文楷体是项目指定的中文字体体积小约 6MB、兼容性好。若你部署到 Docker 容器需在Dockerfile中添加COPY static/font/simkai.ttf /app/static/font/simkai.ttf RUN chmod 644 /app/static/font/simkai.ttf否则浏览器控制台会报Failed to load resource: the server responded with a status of 404 (NOT FOUND)中文显示异常。4. 模型验证与效果调优从 inference notebook 到线上指标监控4.1 使用 chatbot2_inference_Attention.ipynb 进行注意力权重可视化chatbot2_inference_Attention.ipynb是调试 Seq2seq 模型的黄金入口。它不依赖 Flask 服务直接加载.h5模型和词表在 Jupyter 中交互式推理# 加载模型和词表 model tf.keras.models.load_model(models/lstm_java_total.h5) with open(word_to_index.pkl, rb) as f: word2idx pickle.load(f) with open(index_to_word.pkl, rb) as f: idx2word pickle.load(f) def predict_with_attention(input_text): # 预处理分词 → 索引 → padding input_seq [word2idx.get(w, word2idx[UNK]) for w in jieba.cut(input_text)] input_padded pad_sequences([input_seq], maxlen20, paddingpost, valueword2idx[PAD]) # 获取 encoder 输出和 states encoder_outputs, h, c model.layers[2](model.layers[1](model.layers[0](input_padded))) # 手动执行 decoder attention复用 notebook 中定义的 attention_layer decoder_input np.array([[word2idx[START]]]) result [] attention_weights_history [] for i in range(20): # 最大生成长度 decoder_output, h, c model.layers[4](decoder_input, initial_state[h,c]) # 计算 attention weights此处省略具体公式见 notebook cell context tf.matmul(attention_weights, encoder_outputs) # ... 合并、预测下一个词 pred_id np.argmax(output_logits[0]) if pred_id word2idx[END]: break result.append(idx2word[pred_id]) decoder_input np.array([[pred_id]]) attention_weights_history.append(attention_weights.numpy()[0]) return .join(result), attention_weights_history # 调用示例 response, attn_weights predict_with_attention(今天心情不太好) print(Bot:, response) # 可视化第3个 decoder timestep 的 attention weights plt.figure(figsize(10, 4)) plt.imshow(attn_weights[2], cmapviridis, aspectauto) plt.xlabel(Encoder Position) plt.ylabel(Decoder Position) plt.title(Attention Weights at timestep 3) plt.colorbar() plt.show()这段代码的价值在于你能看到模型“看”到了输入句的哪些词。例如输入“我失眠好几天了”若attn_weights[2]热力图在“失眠”“几天”位置亮起说明模型正聚焦这些关键词生成回复。这是验证 Attention 是否真正起作用的最直接证据。4.2 情绪检测模型的混淆矩阵与阈值调优test.py提供了情绪检测模型的离线评估脚本但需手动补充混淆矩阵绘制from sklearn.metrics import confusion_matrix, classification_report import matplotlib.pyplot as plt import seaborn as sns # 加载测试集需自行准备 test_qingyun.tsv X_test, y_test load_data(test_qingyun.tsv, word2idx, max_len50) y_pred_proba emotion_model.predict(X_test) y_pred (y_pred_proba 0.5).astype(int).flatten() # 计算混淆矩阵 cm confusion_matrix(y_test, y_pred) plt.figure(figsize(6, 4)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabels[Negative, Positive], yticklabels[Negative, Positive]) plt.title(Confusion Matrix) plt.ylabel(True Label) plt.xlabel(Predicted Label) plt.show() print(classification_report(y_test, y_pred, target_names[Negative, Positive]))输出示例precision recall f1-score support Negative 0.89 0.92 0.90 420 Positive 0.85 0.81 0.83 380 accuracy 0.87 800 macro avg 0.87 0.87 0.87 800 weighted avg 0.87 0.87 0.87 800若 recall查全率偏低如阳性样本漏检多说明模型过于保守应降低分类阈值将y_pred (y_pred_proba 0.5)改为y_pred (y_pred_proba 0.4)若 precision查准率低则提高阈值至0.6。最佳阈值可通过sklearn.metrics.roc_curve()寻找 Youden 指数最大点。4.3 线上服务健康度监控用 curl 快速验证双接口可用性部署后用以下两条命令秒级验证服务状态无需打开浏览器# 测试聊天接口超时5秒静默模式 curl -s --max-time 5 -X POST http://localhost:5000/chat \ -H Content-Type: application/json \ -d {message:你好} | jq -r .response # 测试情绪检测接口检查是否返回 score 字段 curl -s --max-time 5 -X POST http://localhost:5000/detect-emotion \ -H Content-Type: application/json \ -d {text:我很开心} | jq -r has(score) # 组合验证确保两个接口都返回有效 JSON if [ $(curl -s -o /dev/null -w %{http_code} http://localhost:5000/chat) 200 ] \ [ $(curl -s -o /dev/null -w %{http_code} http://localhost:5000/detect-emotion) 200 ]; then echo ✅ 服务健康 else echo ❌ 接口异常 fijq是 JSON 解析利器-r参数输出原始字符串has(score)返回 true/false。将此脚本加入crontab每5分钟执行一次配合邮件告警即可实现基础服务监控。本文还有配套的精品资源点击获取

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

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

免费获取报价