恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

红楼梦人物关系图谱与问答系统:Python知识图谱构建实战

  • 首页
  • 资讯中心
  • /
  • 红楼梦人物关系图谱与问答系统:Python知识图谱构建实战

相关资讯

用 Microsoft Agent Framework 构建 SubAgent(Multi-Agent):把 Codex auth.json 改到 TaoToken 的实操大纲 2026/10/12 22:05:24
深度强化学习实现动态避障路径规划:PPO与双目标奖励设计实战 2026/10/12 22:05:24
基于Python的PDF知识图谱构建与检索源码实战 2026/10/12 22:05:24

最新资讯

AI大模型如何重塑软件开发流程:用TaoToken统一Key打通Cline MCP与Windsurf BYOK
WizardLM-2 开源即删库复盘:MT-Bench 评测超越 GPT-4,7B 追平 Qwen1.5-32B 的实测路径
CTF逆向入门实战:从easy-100题掌握二进制分析与调试技巧
国产CAD主机厂落地难题拆解:从部署故障到跨软件协同的快速响应
BEM叶素动量理论工程实现:稳定求解诱导因子a与a‘
同为 8B 梯队:Qwen3.8 与上一代 Qwen3-8B 到底差在哪

今日推荐

Debian新手入门:从部署到日常操作的完整指南
MongoDB复制集扩缩容实战:从rs.add到选主事故复盘
条形码目标检测数据集实战:从YOLOv8训练到部署

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

红楼梦人物关系图谱与问答系统:Python知识图谱构建实战

发布时间:2026/10/12 22:05:24
红楼梦人物关系图谱与问答系统:Python知识图谱构建实战 简介这是一份面向计算机相关专业毕业设计、课程设计与项目实战练习者的完整源码包以Python结合知识图谱技术实现红楼梦人物关系可视化与智能问答适合正在准备毕设或需要大作业参考的学生直接上手。资源包含可运行的全部代码与配套文档说明项目经导师指导并获评审99分认可代码结构完整、注释清晰零基础也能按文档逐步跑通。压缩包共约240个文件、5.71MB其中184张jpg图片用于人物头像与界面素材8个py文件承载知识图谱构建、关系抽取与问答逻辑另有css、js、html等前端页面文件及json、txt配置数据整体目录按功能模块划分便于检索与二次开发。目前已有133人学习下载。读者可获得一套从数据建模、图谱存储到可视化展示与问答交互的完整实现方案并借助文档快速理解各模块职责与运行流程为答辩与后续扩展提供扎实基础。1. 红楼梦人物关系可视化与问答系统从文本到图谱的工程落地《红楼梦》人物关系错综复杂四大家族、主仆、姻亲、诗社往来交织在一起用传统表格或思维导图根本理不清。这个毕业设计题目的核心是用 Python 把书里的人物和关系抽出来构建一张知识图谱再在上面做两件事一是可视化展示人物关系网络二是支持自然语言问答。它解决的是“人物关系查询靠翻书、靠记忆”的低效问题适合中文、计算机相关专业做毕设的同学也适合想入门知识图谱构建的 Python 开发者。整条链路涉及文本预处理、实体关系抽取、图数据库存储、前端可视化和问答接口麻雀虽小五脏俱全。下面按实际落地顺序拆开讲每一步都给可复现的命令和参数。2. 知识图谱构建从红楼梦文本到三元组2.1 为什么选 Neo4j 而不是 NetworkX 做存储很多同学第一反应是用 NetworkX 画图但 NetworkX 是内存图计算库不适合做持久化存储和复杂查询。知识图谱问答需要频繁做多跳关系查询比如“贾宝玉的父亲的妻子是谁”这种查询用 Cypher 在 Neo4j 里一行搞定用 NetworkX 得手写遍历逻辑。Neo4j 的社区版免费支持 Cypher 查询语言Python 通过 py2neo 或官方 neo4j 驱动连接是毕设级别最稳妥的选择。安装 Neo4j 桌面版后默认 Bolt 端口 7687HTTP 端口 7474。启动后浏览器打开http://localhost:7474初始用户名和密码都是neo4j首次登录会强制改密码。Python 端安装驱动pip install neo4j py2neo连接测试from neo4j import GraphDatabase # 替换成你设置的密码 driver GraphDatabase.driver(bolt://localhost:7687, auth(neo4j, 你的密码)) def test_connection(tx): result tx.run(RETURN connected AS status) return result.single()[status] with driver.session() as session: print(session.execute_read(test_connection))逻辑说明GraphDatabase.driver建立连接池session.execute_read执行只读查询。参数auth是元组第一个是用户名第二个是密码。如果报ServiceUnavailable先确认 Neo4j 服务已启动再检查防火墙是否放行 7687 端口。2.2 人物实体抽取规则匹配加词典兜底红楼梦人物有几百个全用 NER 模型标注成本太高。毕设场景下最稳的做法是“人物词典 规则匹配”。先从公开的红楼梦人物列表整理一份 CSV包含姓名、别名、身份。比如“贾宝玉”别名“宝玉”“宝二爷”“林黛玉”别名“黛玉”“颦儿”。然后遍历章节文本用别名做匹配。import csv import re # 加载人物词典 def load_characters(path): characters {} with open(path, r, encodingutf-8) as f: reader csv.DictReader(f) for row in reader: name row[name] aliases row[aliases].split(|) if row[aliases] else [] characters[name] aliases return characters # 构建正则模式长别名优先匹配避免宝玉匹配到贾宝玉时截断 def build_pattern(characters): alias_map {} for name, aliases in characters.items(): for alias in aliases [name]: alias_map[alias] name # 按长度降序排列保证贾宝玉先于宝玉匹配 sorted_aliases sorted(alias_map.keys(), keylen, reverseTrue) pattern re.compile(|.join(re.escape(a) for a in sorted_aliases)) return pattern, alias_map # 对每段文本抽取人物 def extract_characters(text, pattern, alias_map): found set() for match in pattern.finditer(text): found.add(alias_map[match.group()]) return list(found)逻辑说明build_pattern把别名按长度降序拼成正则确保“贾宝玉”不会被“宝玉”抢先匹配。extract_characters返回标准名列表。参数方面人物词典 CSV 至少要有name和aliases两列别名用竖线分隔。如果召回率不够可以加入“老太太”“凤姐”这类口语称呼。2.3 关系抽取共现窗口加规则模板关系抽取比实体抽取更难。毕设级别不需要上深度学习用“共现 规则”就能覆盖大部分主仆、亲属关系。具体做法以段落为窗口同一段里出现的人物两两建立CO_OCCUR关系再用规则模板匹配“A是B的X”这类句式抽取出具体的FATHER、MOTHER、SPOUSE等关系。# 关系规则模板 RELATION_PATTERNS [ (re.compile(r(.{2,4})是(.{2,4})的(父亲|父亲大人|爹)), FATHER), (re.compile(r(.{2,4})是(.{2,4})的(母亲|娘|妈)), MOTHER), (re.compile(r(.{2,4})是(.{2,4})的(妻子|夫人|老婆)), SPOUSE), (re.compile(r(.{2,4})是(.{2,4})的(儿子|女儿)), CHILD), ] def extract_relations(text, characters, alias_map): relations [] # 规则匹配 for pattern, rel_type in RELATION_PATTERNS: for match in pattern.finditer(text): a, b match.group(1), match.group(2) a_std alias_map.get(a, a) b_std alias_map.get(b, b) if a_std in characters and b_std in characters: relations.append((a_std, rel_type, b_std)) # 共现关系 found extract_characters(text, build_pattern(characters)[0], alias_map) for i in range(len(found)): for j in range(i 1, len(found)): relations.append((found[i], CO_OCCUR, found[j])) return relations逻辑说明规则模板用捕获组提取主语、宾语和关系词再通过alias_map归一化到标准名。共现关系作为补充保证图谱连通性。参数上共现窗口建议按段落切分不要按整章否则关系太稀疏。如果发现关系数量爆炸可以限制共现只保留出现次数大于 2 的人物对。2.4 批量写入 Neo4j用 MERGE 避免重复节点抽取完三元组后写入 Neo4j 要用MERGE而不是CREATE否则同一个人物会创建多个节点。批量写入用UNWIND提高效率。def write_relations(driver, relations): query UNWIND $rels AS rel MERGE (a:Person {name: rel.source}) MERGE (b:Person {name: rel.target}) MERGE (a)-[r:RELATION {type: rel.type}]-(b) with driver.session() as session: session.run(query, rels[ {source: s, type: t, target: o} for s, t, o in relations ]) # 调用 relations extract_relations(text, characters, alias_map) write_relations(driver, relations)逻辑说明UNWIND把列表展开成多行MERGE保证节点和关系不存在时才创建。参数$rels是参数化查询避免 Cypher 注入。如果关系类型很多可以把RELATION改成动态类型但 Neo4j 不支持动态关系类型所以用属性type区分。查询时用WHERE r.type FATHER过滤。3. 人物关系可视化用 Pyecharts 做可交互网络图3.1 为什么选 Pyecharts 而不是 MatplotlibMatplotlib 画网络图节点一多就糊成一团而且没有交互。Pyecharts 基于 ECharts支持拖拽、缩放、点击高亮导出 HTML 后可以直接嵌到论文或答辩 PPT 里。安装pip install pyecharts从 Neo4j 查数据from pyecharts import options as opts from pyecharts.charts import Graph def get_graph_data(driver): query MATCH (a:Person)-[r:RELATION]-(b:Person) RETURN a.name AS source, b.name AS target, r.type AS rel_type LIMIT 500 with driver.session() as session: result session.run(query) nodes set() links [] for record in result: nodes.add(record[source]) nodes.add(record[target]) links.append({ source: record[source], target: record[target], label: {show: True, formatter: record[rel_type]} }) return list(nodes), links def render_graph(nodes, links, outputhongloumeng.html): node_data [{name: n, symbolSize: 20} for n in nodes] graph ( Graph() .add( , node_data, links, repulsion8000, layoutforce, edge_labelopts.LabelOpts(is_showFalse), linestyle_optsopts.LineStyleOpts(width1, curve0.2), ) .set_global_opts( title_optsopts.TitleOpts(title红楼梦人物关系图谱), legend_optsopts.LegendOpts(is_showFalse), ) ) graph.render(output)逻辑说明repulsion控制节点斥力值越大节点越分散建议 5000 到 10000 之间调。layoutforce是力引导布局适合人物关系图。curve0.2让边稍微弯曲避免重叠。如果节点超过 200 个建议先按关系类型过滤只展示FATHER、MOTHER、SPOUSE等核心关系否则浏览器会卡。3.2 节点分类着色按家族或身份分组红楼梦里贾、史、王、薛四大家族用不同颜色区分能一眼看出阵营。在节点数据里加category字段Pyecharts 会自动按类别着色。# 人物到家族的映射可以从词典 CSV 里读 FAMILY_MAP { 贾宝玉: 贾家, 贾母: 贾家, 贾政: 贾家, 林黛玉: 林家, 薛宝钗: 薛家, 王熙凤: 王家, # ... 补充完整 } def build_categories(nodes): categories [{name: 贾家}, {name: 林家}, {name: 薛家}, {name: 王家}, {name: 其他}] node_data [] for n in nodes: family FAMILY_MAP.get(n, 其他) node_data.append({name: n, symbolSize: 20, category: family}) return node_data, categories # 在 add 里传入 categories 参数 graph.add(, node_data, links, categoriescategories, ...)逻辑说明category字段对应categories列表里的namePyecharts 自动分配颜色。参数上symbolSize可以根据人物出现频次动态调整频次高的节点大一些视觉上更突出核心人物。4. 问答系统用模板匹配加 Cypher 查询实现自然语言问答4.1 问答架构意图识别加槽位填充毕设级别的问答不需要上大模型用“意图识别 槽位填充 Cypher 模板”就能覆盖大部分问题。意图分三类查关系“贾宝玉的父亲是谁”、查属性“林黛玉的别名是什么”、查路径“贾宝玉和林黛玉什么关系”。槽位就是人物名和关系类型。import re # 意图模板 INTENT_PATTERNS [ (re.compile(r(.{2,4})的(父亲|母亲|妻子|儿子|女儿)是谁), query_relation), (re.compile(r(.{2,4})和(.{2,4})什么关系), query_path), (re.compile(r(.{2,4})的别名), query_alias), ] def parse_question(question, alias_map): for pattern, intent in INTENT_PATTERNS: match pattern.search(question) if match: slots [alias_map.get(g, g) for g in match.groups() if g] return intent, slots return None, []逻辑说明parse_question返回意图和槽位列表。参数上正则里的.{2,4}限制人物名长度避免匹配到整句话。如果问题里出现别名通过alias_map归一化。4.2 Cypher 查询模板把意图翻译成图查询CYPHER_TEMPLATES { query_relation: MATCH (a:Person {name: $name})-[:RELATION {type: $rel_type}]-(b:Person) RETURN b.name AS answer , query_path: MATCH path shortestPath((a:Person {name: $name1})-[*..4]-(b:Person {name: $name2})) RETURN [n IN nodes(path) | n.name] AS path_nodes , query_alias: MATCH (a:Person {name: $name}) RETURN a.aliases AS aliases , } def answer_question(driver, question, alias_map): intent, slots parse_question(question, alias_map) if not intent: return 抱歉我暂时无法理解这个问题。 if intent query_relation: name, rel_word slots[0], slots[1] rel_type {父亲: FATHER, 母亲: MOTHER, 妻子: SPOUSE, 儿子: CHILD, 女儿: CHILD}.get(rel_word) if not rel_type: return 不支持的关系类型。 with driver.session() as session: result session.run(CYPHER_TEMPLATES[intent], namename, rel_typerel_type) answers [r[answer] for r in result] return 、.join(answers) if answers else 没有查到相关关系。 elif intent query_path: name1, name2 slots[0], slots[1] with driver.session() as session: result session.run(CYPHER_TEMPLATES[intent], name1name1, name2name2) record result.single() if record: return - .join(record[path_nodes]) return 没有找到关系路径。 elif intent query_alias: name slots[0] with driver.session() as session: result session.run(CYPHER_TEMPLATES[intent], namename) record result.single() if record and record[aliases]: return 、.join(record[aliases]) return 没有别名记录。逻辑说明query_relation用关系类型过滤query_path用shortestPath找最短路径限制 4 跳以内避免全图扫描。参数上$name、$rel_type是参数化查询防止注入。如果路径查询返回空可能是图谱里两个人物之间没有连通路径需要检查共现关系是否写入。4.3 用 Flask 包一个问答接口from flask import Flask, request, jsonify app Flask(__name__) app.route(/qa, methods[POST]) def qa(): data request.get_json() question data.get(question, ) answer answer_question(driver, question, alias_map) return jsonify({question: question, answer: answer}) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)逻辑说明/qa接收 POST 请求JSON 体里带question字段返回answer。参数上debugTrue只在开发时用部署时关掉。前端可以用简单的 HTML 页面发 fetch 请求也可以直接 curl 测试curl -X POST http://localhost:5000/qa -H Content-Type: application/json -d {question:贾宝玉的父亲是谁}5. 避坑与排查红楼梦知识图谱落地时最容易翻车的 5 个点5.1 人物别名匹配错乱导致关系张冠李戴现象查询“宝玉的父亲”返回“贾政”但查询“贾宝玉的父亲”返回空。原因别名映射表里“宝玉”映射到了“贾宝玉”但正则匹配时“贾宝玉”被“宝玉”截断导致标准名不一致。解决在build_pattern里按别名长度降序排列确保长别名优先匹配。同时在alias_map里把“宝玉”和“贾宝玉”都映射到同一个标准名。5.2 Neo4j 写入速度慢几千条关系跑十几分钟现象批量写入时每条关系单独开 session速度极慢。原因每次session.run都是一次网络往返。解决用UNWIND批量写入一次传几百条关系。另外给Person.name建唯一约束能加速MERGECREATE CONSTRAINT person_name IF NOT EXISTS FOR (p:Person) REQUIRE p.name IS UNIQUE;5.3 Pyecharts 渲染出来一片空白现象HTML 打开后只有标题没有节点。原因links里的source和target必须是节点列表里存在的name如果节点数据里没有对应名称边就画不出来。解决检查nodes和links是否一致确保每个边的两端都在节点列表里。另外repulsion太小会导致节点重叠调大到 8000 以上。5.4 问答系统只能回答固定句式现象用户问“贾宝玉他爹是谁”系统无法识别。原因意图模板只覆盖了“父亲”这个标准词没有覆盖“爹”“父亲大人”等口语变体。解决在parse_question之前加一层同义词替换把“爹”“父亲大人”“父”都替换成“父亲”再走模板匹配。同义词表可以放在配置文件里方便扩展。5.5 路径查询返回空但两个人明明有关系现象查询“贾宝玉和林黛玉什么关系”返回空。原因图谱里只有CO_OCCUR关系没有直接的关系类型而shortestPath默认走所有关系类型但如果两个人不在同一个连通分量里就查不到。解决先确认两个人之间是否有共现关系如果没有检查文本预处理时是否漏掉了同时出现这两个人的段落。另外shortestPath的跳数限制可以放宽到 6 跳但不要太大否则查询变慢。6. 进阶技巧用 PageRank 找红楼梦的核心人物图谱建好后除了问答和可视化还能做一件很有意思的事用 PageRank 算法找出红楼梦里的核心人物。Neo4j 的 Graph Data Science 库自带 PageRank社区版可以用 Cypher 手写简化版或者用 NetworkX 算完再写回 Neo4j。import networkx as nx from neo4j import GraphDatabase def build_networkx_graph(driver): G nx.DiGraph() query MATCH (a:Person)-[r:RELATION]-(b:Person) RETURN a.name AS s, b.name AS t, r.type AS type with driver.session() as session: for record in session.run(query): G.add_edge(record[s], record[t], typerecord[type]) return G def rank_characters(G, top_n10): # 只保留核心关系去掉共现边避免噪声 core_edges [(u, v) for u, v, d in G.edges(dataTrue) if d[type] ! CO_OCCUR] H G.edge_subgraph(core_edges).copy() pr nx.pagerank(H, alpha0.85) return sorted(pr.items(), keylambda x: x[1], reverseTrue)[:top_n] G build_networkx_graph(driver) top rank_characters(G) for name, score in top: print(f{name}: {score:.4f})逻辑说明alpha0.85是阻尼系数经典取值。去掉CO_OCCUR边是因为共现关系噪声大会把丫鬟和主子拉平。跑出来的结果里贾宝玉、贾母、王熙凤通常排前三和红学研究的结论基本一致。这个排名可以写回 Neo4j给节点加pagerank属性可视化时用节点大小表示。参数上top_n建议取 10 到 15太多就失去区分度。如果发现某个丫鬟排名异常高检查是不是共现边没过滤干净。另外PageRank 对有向图和无向图结果不同红楼梦关系图建议用有向图因为“父亲”和“儿子”方向不同权重也不一样。我自己做这个图谱时最大的教训是别一上来就追求全自动抽取。先用词典和规则把核心的几十个人物和关系跑通可视化出来能看问答能答对几个典型问题再逐步加人物、加关系类型。一上来就上深度学习 NER标注数据不够调参调到怀疑人生最后图谱还没建起来。先把最小可用版本跑通再迭代这是最稳的路。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号