Django+DeepSeek大模型+知识图谱的古诗词情感分析系统实战拆解
去年这时候我还在选题系统里反复横跳看到“古诗词情感分析”“知识图谱”“推荐系统”这些词堆在一起我第一反应是这又是哪个老师挂出来的“满汉全席”型题目——看着唬人其实根本不知道从哪下手。后来真把这个系统从零到一做完、拿到答辩老师点头认可我才发现这类题目的价值不在某个单一技术点上而在于它把Django开发、DeepSeek大模型接入、知识图谱构建、情感分析、推荐算法和可视化展示全部串成了一条完整链路。做的时候确实累但做完以后你对“一个真实的人工智能Web应用到底长什么样”会有一个非常踏实的认知。这篇文章我就以一个把整套流程走完的过来人身份把这套系统的拆解思路、核心模块实现、踩坑记录和答辩准备全部分享出来。适合正在做类似计算机毕业设计、或者想用知识图谱和大模型做文本分析应用的同学参考。不是教科书式讲解全是实际操作里摸出来的经验。1. 开题阶段的题目拆解这套系统到底在解决什么问题1.1 “四合一”题目的真实工作量评估很多同学看到“DjangoDeepSeek大模型知识图谱古诗词情感分析”这个长标题第一反应就是“工作量爆炸”。其实只要把它拆开看你会发现它本质上是四个独立的模块数据处理与存储、情感分析服务、图谱关系检索、可视化展示。再加上一个推荐系统作为亮点收尾整体架构就清晰了。我当时列了一张需求拆解表方便答辩时展示模块核心功能技术实现工作量占比古诗词管理导入、检索、详情展示Django ORM MySQL15%情感分析对诗词进行情感极性识别与标签输出DeepSeek大模型 情感词典兜底25%知识图谱诗人、诗作、意象、情感的实体关系网络Neo4j Cypher25%推荐系统基于情感偏好与相似诗词的推荐ItemCF 图谱增强20%数据可视化情感分布、朝代趋势、图谱关系展示ECharts Ajax15%为什么我把推荐和可视化单独拎出来而不是并进情感分析因为答辩时老师一定会问“你的推荐到底推荐了什么、依据是什么”如果你只说“根据情感相似推荐”那太单薄了。必须有明确的算法路径和图谱路径做支撑这部分本身就是独立的创新点。1.2 为什么选Django而不是Spring Boot选题定下来之后第一个技术选型问题是后端框架。我周围也有人用Spring Boot做毕设但古诗词这个场景我最终还是选了Django理由很实际ORM对中文数据友好古诗词数据大多是CSV或JSON格式用Django的ORM写导入脚本非常快model定义完直接跑migration不用手写SQL建表。Admin后台免费送答辩演示时我直接展示Django Admin后台的诗词管理界面评委能看到“这是个完整系统”而不是花架子。模板渲染DRF二合一页面部分用模板渲染省事图谱和推荐的数据接口用DRFDjango REST Framework暴露JSON前后端分离不分离都能应对。1.3 DeepSeek大模型在毕设里的定位我个人强烈建议不要试图从头训练一个古诗词情感分析模型那是研究型硕士的工作量。毕设的正确做法是用现有的开源大模型做推理和抽取你只需要把模型接进来、设计好提示词、处理好输出再结合少量传统方法做兜底。DeepSeek在这个项目里的定位是对诗词文本做情感分类与情感极性判断输出如“悲愁”“豪放”“闲适”等标签从文本中抽取意象实体月、花、酒、舟等用于知识图谱构建为推荐系统生成语义向量表示或结构化标签辅助相似计算。这样一来大模型不是配角而是整个系统的“语义引擎”。你做的是工程化整合这是毕设最合理的分工。2. Django工程搭建与DeepSeek模型接入的落地方案2.1 工程初始化和app划分我建议用Django 4.x现在5.x也稳定了但4.x的资料更多踩坑好搜。项目名我起了一个中性的名称避免用拼音缩写直接用poetry_analysis。整个工程按业务边界拆成5个apppoetry_analysis/ ├── manage.py ├── config/ # 项目配置 ├── poems/ # 诗词数据模型与管理 ├── analysis/ # 情感分析服务 ├── graph/ # 知识图谱数据构建与查询 ├── recommend/ # 推荐算法服务 └── visualization/ # 可视化API与页面app拆得清楚后面写代码时心情会好很多。尤其是答辩时老师看项目结构一眼就能看出你是否理解“高内聚低耦合”。我自己之前做个小系统图省事全塞一个app里后来加功能差点把自己绕晕这次学乖了。2.2 本地加载DeepSeek模型的两种方案对比大模型接入有两种路线调用API和本地部署。我两种都试过给你一个参考对比方案优点缺点适用场景调用官方API零部署成本、推理快、效果稳定演示时若无网会尴尬、有调用费用、传数据有隐私顾虑开发调试阶段本地部署量化模型完全离线、免费、演示不怕断网显存占用高、推理较慢、需要下载模型文件答辩演示、最终交付我当时最终选了本地部署原因很简单答辩教室的网络状况不可控我不想赌它一定通畅。模型选的是DeepSeek开源系列的蒸馏版本7B规模用4-bit量化之后显存占用大概6-8GB一张普通显卡能扛住。加载时间确实有点长但答辩前预热好就行后面我会专门讲演示预案。如果用的是Ollama跑本地模型我当时的模型调用封装长这样用兼容OpenAI格式的HTTP接口方便替换# analysis/services/llm_client.py import httpx import json from django.conf import settings class LLMClient: 本地DeepSeek模型客户端封装 def __init__(self): self.base_url settings.LLM_BASE_URL # 例如 http://127.0.0.1:11434/v1 self.api_key settings.LLM_API_KEY # Ollama/VLLM不校验填任意值 self.model settings.LLM_MODEL_NAME def chat(self, messages, temperature0.1, max_tokens512): payload { model: self.model, messages: messages, temperature: temperature, max_tokens: max_tokens, stream: False } resp httpx.post( f{self.base_url}/chat/completions, jsonpayload, headers{Authorization: fBearer {self.api_key}}, timeout120 ) resp.raise_for_status() content resp.json()[choices][0][message][content] return content注意temperature要设低我调到0.1目的是让情感分析这种任务尽量稳定不要每次输出不一样。2.3 三步解决“模型回复不稳定”大模型直接返回文本时你没法保证它每次输出都是规范的JSON。我做了一套保障流程提示词里强制要求JSON输出并给示例代码里用正则抽取JSON块就算模型多说了废话也能截取出来用json.loads解析失败时自动重试一次还失败就落到传统情感词典方案。这套兜底逻辑极其重要。我见过太多人写完prompt就以为万事大吉结果上线一跑十条里有三条输出乱七八糟整个系统就卡住了。后来我专门加了一个parse_json_response()函数来处理import re import json def parse_json_response(text: str) - dict: 从模型回复中健壮地抽取JSON # 去掉可能的markdown代码块标记 cleaned text.strip().replace(json, ).replace(, ) match re.search(r\{.*\}, cleaned, re.S) if not match: raise ValueError(fNo JSON found in: {text[:100]}) return json.loads(match.group())加了这个之后分析模块的容错率一下就上来了。3. 古诗词知识图谱构建实体抽取、关系建模与Neo4j落地3.1 实体和关系设计不是所有东西都往图里塞知识图谱最大的坑在于“什么都想建模成节点”最后图里一团乱麻。古诗词领域我最终只保留了五类实体、三类关系实体类型诗人如李白、杜甫诗作如《静夜思》词牌/曲牌如《水调歌头》意象如月、酒、花、舟、孤雁情感标签如悲愁、豪放、闲适、思乡关系类型(诗人) - [创作] - (诗作)(诗作) - [使用意象] - (意象)(诗作) - [表达情感] - (情感标签)为什么把情感标签作为节点而不是属性因为答辩要展示可视化图谱如果你把情感放成属性值图谱里显示不出来“哪些诗是悲愁的”这种聚合关系。做成节点之后Cypher查询一条语句就能把“表达思乡情感的诗作及使用意象”全部拉出来效果很直观。导入Neo4j时我用的Python驱动核心代码大致是from neo4j import GraphDatabase class KnowledgeGraphBuilder: def __init__(self, uri, user, password): self.driver GraphDatabase.driver(uri, auth(user, password)) def create_poem(self, poem_data): cypher MERGE (p:Poem {id: $id}) SET p.title $title, p.content $content, p.dynasty $dynasty WITH p MATCH (a:Author {name: $author}) MERGE (a)-[:CREATED]-(p) RETURN p with self.driver.session() as session: session.run(cypher, **poem_data) def link_emotion(self, poem_id, emotions: list): cypher MATCH (p:Poem {id: $poem_id}) UNWIND $emotions AS e MERGE (em:Emotion {name: e}) MERGE (p)-[:EXPRESSES]-(em) with self.driver.session() as session: session.run(cypher, poem_idpoem_id, emotionsemotions)用MERGE而不是CREATE是为了防止重复导入时产生重复节点。3.2 用DeepSeek批量抽取意象和情感标签知识图谱的数据构建是整个项目最耗时间的环节。我整理了一个公开的古诗词数据集大约3.8万首但全量让大模型跑一遍太久了。我的处理策略是分层抽样 全量兜底对教材大纲内的高频诗词约800首用DeepSeek做细粒度抽取包括意象、情感、创作背景关键词对剩余诗词用情感词典快速标注粗粒度情感等推荐模块需要时再按需补抽取。细粒度抽取的提示词模板我调了好几版最终稳定版是这样的prompt 你是一位古典文学研究专家。请分析下面这首古诗词的情感类型和意象使用。 【诗词内容】 {poem_content} 【要求】 1. 从以下情感类型中选择最匹配的1-2个喜悦, 悲愁, 豪放, 闲适, 思乡, 离别, 愤懑, 孤寂 2. 列出3-5个关键意象必须是名词性词语如月酒落花孤舟 3. 用JSON格式输出输出结构为 {{emotions: [悲愁, 孤寂], images: [月, 酒]}} 只输出JSON不要有任何解释。 .format(poem_contentpoem.content)这里有个经验意象抽取如果不限定数量模型有时给十几个有时给一个后面图谱会很难看。限成3-5个图谱的连边数量可控可视化也更清晰。3.3 图谱查询接口与前端联动图谱组件后端我只暴露了一个API返回诗作关联的子图def get_subgraph(poem_id: str): cypher MATCH (p:Poem {id: $poem_id}) OPTIONAL MATCH (p)-[r]-(n) RETURN p, r, n # 转换成ECharts graph格式返回 nodes 和 edges 两个字段前端用ECharts的graph类型渲染力导向图节点大小按关联度取值颜色按实体类型区分。展示效果就是整张大网里点一首诗周围牵连出诗人、意象和情感节点。这个图我放在系统首页正中打开系统的第一眼就有视觉冲击力。4. 情感分析与推荐模块从“调API”到“可解释推荐”4.1 情感分析的三种路线对比我为什么没选微调模型接大模型之前我先认真评估过三条技术路线。这个对比直接在论文的“方案选型”章节里原样写了技术方案优点缺点适合场景情感词典如大连理工大学情感词汇本体库无需算力、速度快、可解释不识别古诗语境例如“落日”是中性词但古诗里常带悲愁兜底方案微调BERT/ERNIE类预训练模型效果较好、推理快需要标注数据、训练时间长、答辩不好讲清楚有GPU且愿意炼丹的同学大模型直接推理零标注成本、效果最好、天然适配古诗语境推理慢、显存占用高、需要设计prompt我最终选的方案我选大模型直接推理核心原因是毕设时间有限没有标注数据可用来微调。另外DeepSeek对古诗词的语境理解能力明显强于传统情感词典——比如“却道天凉好个秋”词典方案根本分不清这是愤懑和无奈大模型能准确识别出“愁苦中带自嘲”。4.2 情感分析的稳定输出设计我在analysis模块里写了一个SentimentAnalyzer类核心流程是先走大模型失败走词典兜底。下面是大模型情感分析的核心代码class SentimentAnalyzer: EMOTIONS [喜悦, 悲愁, 豪放, 闲适, 思乡, 离别, 愤懑, 孤寂] def analyze(self, poem_content: str): messages [ {role: system, content: 你是一位擅长古典诗词情感分析的专家只输出JSON。}, {role: user, content: self._build_prompt(poem_content)} ] try: raw self.llm.chat(messages, temperature0.1) data parse_json_response(raw) emotions [e for e in data.get(emotions, []) if e in self.EMOTIONS] return emotions or self._dictionary_fallback(poem_content) except Exception: return self._dictionary_fallback(poem_content)这里_dictionary_fallback用情感词汇本体库统计词频虽然效果差点但至少不会让接口报错。整个系统对外展示时“永远有数据可显示”这种工程上的稳健性答辩老师是很吃这一套的。4.3 推荐系统不能只靠“按情感过滤”糊弄过去推荐模块是区分“做了”和“做透了”的分水岭。如果你答辩时说“推荐就是查同情感类型的诗”老师大概率会追问“那跟普通分类检索有什么区别”。所以我做了两层推荐逻辑第一层基于情感标签的ItemCF协同过滤每首诗有1-2个情感标签计算诗与诗之间的标签余弦相似度用户收藏/浏览某首诗后推荐情感标签相似的诗。第二层知识图谱路径增强在Neo4j里查询两首诗的图谱路径比如都使用了“月”这个意象或者出自同一个词牌把路径匹配强度作为协同过滤相似度的叠加分。最终推荐分数公式我在论文里写得很清楚Score(A, B) α * 情感标签相似度 β * 意象共现相似度 γ * 朝代/词牌相似度其中α0.5β0.3γ0.2。这组权重怎么来的我拿用户问卷做了小范围测试让20个人对推荐结果打分然后网格搜索调参最后定到这组数值。协同过滤部分的Python实现核心是这个相似度计算def compute_similarity(poem_a, poem_b): tags_a set(poem_a.emotion_tags) tags_b set(poem_b.emotion_tags) if not tags_a or not tags_b: return 0.0 # 杰卡德相似度 jaccard len(tags_a tags_b) / len(tags_a | tags_b) images_a set(poem_a.image_tags) images_b set(poem_b.image_tags) if images_a and images_b: image_jaccard len(images_a images_b) / len(images_a | images_b) else: image_jaccard 0.0 dynasty_score 1.0 if poem_a.dynasty poem_b.dynasty else 0.0 return 0.5 * jaccard 0.3 * image_jaccard 0.2 * dynasty_score这个代码量不大但胜在逻辑完整、可解释性强。答辩时老师问“推荐依据是什么”我直接指着这个公式说“综合考虑情感、意象、朝代三个维度权重是实验调出来的”基本就没再追问了。4.4 冷启动问题的处理新用户没有任何行为数据推荐系统推什么这是答辩高频追问。我的方案是注册时让用户选“此刻想读什么心情的诗”选项清静闲适、慷慨豪迈、忧愁烦闷、思乡怀人作为初始偏好。然后直接把该情感标签下的高分诗推给用户。这个设计不仅解决了冷启动还顺便给答辩加了一个“用户交互设计”的亮点。5. 可视化层怎么把“大数据”的感觉做出来5.1 数据聚合接口设计可视化如果只是前端画几个死图那谁都会做。关键在于“图表能反映数据特点”。我做了四个核心图表每个都有明确的分析价值图表类型展示内容分析价值南丁格尔玫瑰图全库诗词情感类型分布一眼看出古诗词整体情感偏向悲愁孤寂堆叠柱状图唐宋两代各情感类型数量对比反映朝代审美差异词云图高频意象统计展示“月”“酒”“花”在古诗词中的统治地位力导向图诗人-诗作-意象-情感关系网络知识图谱核心展示Django这边我写了一个视图返回聚合好的JSON数据比如情感分布的核心逻辑def emotion_distribution(request): # 从Redis缓存读取避免每次刷新都跑大模型 cache_key viz:emotion_distribution result cache.get(cache_key) if result is None: rows EmotionTag.objects.values(name).annotate(countmodels.Count(poem)) result {item[name]: item[count] for item in rows} cache.set(cache_key, result, timeout60 * 30) return JsonResponse(result)加了Redis缓存是实战里踩坑之后补的——没有缓存时前端页面每刷新一次后端就要重新聚合几千条数据数据库顶不住。加了缓存之后页面响应从秒级降到毫秒级体感完全不一样。5.2 情感趋势折线图把数据分析深度往上抬一个台阶很多毕设的可视化就是“查出来多少显示多少”没有时间维度。我额外做了一个“宋代词人情感倾向变化趋势”的折线图按时间段统计词的创作数量和各情感类型占比。这个图做好了之后答辩老师直接问了一个额外问题“你能从这个图上看出宋代词风从婉约转为豪放的发展脉络吗”这就说明你已经把可视化从“展示”做成了“分析”。数据源不是现成的需要按年份字段重新聚合。这一步用Django ORM的TruncYear配合注释字段几行就查出来了。5.3 ECharts接入Django模板的细节ECharts的图形配置很长塞在Django模板里会非常乱。我建议把图形初始化逻辑抽到独立的JS文件后端模板只负责定义一个div容器和拿到数据的接口地址。代码大致长这样div idemotionChart stylewidth: 100%; height: 420px;/div script src{% static js/charts/emotionChart.js %}/script script // emotionChart.js 内部通过 fetch(/api/emotion-distribution/) 拉数并渲染 /script这里有个小坑用{% static %}引JS文件时JS内部不能用Django模板变量所以所有数据都通过Ajax拿不要在JS里直接嵌数据。保持前后端数据通道单一后面调试会轻松很多。6. 部署、论文文档LW与答辩演示的实操经验6.1 环境依赖与模型量化的“血泪”本地部署大模型最容易翻车的就是环境依赖。PyTorch版本、CUDA版本、transformers版本三者一旦不匹配模型根本加载不起来。我在这个坑里耗了两天。最后我的稳定组合是Python 3.10 PyTorch 2.1 CUDA 11.8 对应版本的transformers大家直接照抄这个配置可以少走很多弯路。配好环境后4-bit量化加载的核心代码大致是from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig quant_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypetorch.float16, bnb_4bit_use_double_quantTrue ) model AutoModelForCausalLM.from_pretrained( /path/to/deepseek-local-model, quantization_configquant_config, device_mapauto )模型加载完之后第一次调用推理会特别慢可能需要几秒到十几秒所以答辩之前一定要“预热”——在系统里跑几条分析让模型权重加载到显存里。我差点在正式演示时翻车后来把预热做成系统启动时的自动任务才彻底解决。6.2 LW文档写作策略扬长避短毕设的大论文怎么写我总结了六个字“结果先行推导在后”。每一章开头先放这个模块“做出了什么”再补技术原理。例如知识图谱章节我先放整张图谱可视化的截图和统计指标实体数量、关系数量再讲实体抽取的prompt设计和Cypher建模。还有一个策略很管用在论文里加一张对比实验表证明你做过方案选型的分析情感分析方案准确率抽样100首人工标注推理耗时情感词典方案52%5ms微调BERT方案74%200msDeepSeek大模型推理方案86%3.2s注意这里“准确率”一定要自己抽样标注别编造数据答辩老师如果真的抽查会很难看。抽样100首自己找两个同学标注取一致的部分作为标准这个工作量是值得花的。6.3 演示时的“断网应急预案”最后这点是真的保命技巧。答辩现场大概率没有外网或者网络慢得跟拨号一样。我准备了三层预案所有静态资源全部本地化ECharts的JS文件、字体、图片全部下载到本地静态目录绝不要用CDN链接。模型本地部署绝不依赖API正如前面说的整个系统完全离线可运行。准备一份“演示数据缓存”在Redis里提前跑好所有核心分析结果。万一现场模型加载失败或推理超时我有一键“走缓存数据”的模式页面照样有数可显。答辩当天我全程开着飞行模式演示的页面流畅、图表照常加载、推荐结果秒出。评委老师看完第一句话就是“这系统做得挺完整。”对毕设来说“完整、稳定、能跑”有时候比技术天花乱坠更重要。回看整个项目我最深的体会是这题目看似堆了好几个热点技术但实际上足够“重剑无锋”。只要把每一步的技术选型想清楚、给每个模块都留出容错兜底、在答辩前解决掉所有“演示时可能出丑”的隐性风险整套做下来不仅顺利过关还能在过程中把Django工程能力、大模型应用能力和知识图谱的建模思维一次补齐。如果你也正在做类似的题目别被标题吓住拆开一步一步走稳得很。