Python文本转知识图谱实战:抽取实体关系构建Neo4j图谱

发布时间:2026/10/12 23:49:40

Python文本转知识图谱实战:抽取实体关系构建Neo4j图谱 简介面向Python开发者与NLP初学者的文本转知识图谱实战资源围绕预处理、命名实体识别、关系抽取、图构建与可视化等完整流程提供可运行的工程实现。压缩包共63个文件核心包括text_grapher.py、keywords_textrank.py、sentence_parser.py、GraphShow.py等4个Python脚本以及41个JavaScript文件、2个CSS、graph_show.html和LTP数据等整体约605.62MB涵盖从文本解析到知识图谱前端展示的完整链路。已有598人学习适合人工智能、数据分析方向的开发者快速搭建知识图谱原型并参考其代码结构。通过资源可学习实体识别、关键词抽取、句法解析与echarts可视化如何协同工作同时借助配套脚本和工程组织方式降低踩坑成本便于进一步扩展为问答系统或推荐引擎。1. 文本转知识图谱不是黑匣子先搞清你的文本里能抽出什么我接过不少这样的需求手里有几百份商品说明书或合同想快速找出“哪个供应商给哪个项目供货”“某型号属于哪条产品线”。手工整理不是不行但换一批文本又要重来。所谓基于 Python 做文本转化知识图谱本质上就是把非结构化句子拆成“实体-关系-实体”的三元组再把这些三元组装成节点和边。很多人以为一定要上大模型其实用词典、正则加一个轻量级 NER 模型就能覆盖大部分结构化程度尚可的业务文本。这篇文章适合刚接触知识图谱构建、又不想一开始就引入复杂框架的工程师和数据分析师照着本文的思路你可以在自己机器上跑通最小闭环再按需求加厚。2. 定边界再选型实体、关系、属性的三类抽取方案文本转知识图谱的第一步往往不是写代码而是先回答三个问题你的文本里有哪些实体、实体之间的关系用什么谓词表达、哪些信息应该作为实体属性而不是独立节点。这三个问题不解决后面跑多好的模型都会产出“看起来很全、实际没法用”的图。2.1 先做轻量本体建模把实体类型和关系类型写进一张表我开始做这类任务时习惯先花半小时在纸上列一张“本体表”而不是直接调库。比如处理产品说明书我会先定义四类实体产品、部件、组织、地点再定义五类关系part_of部件属于产品、produced_by产品由组织生产、located_in组织位于地点、replace_by产品被替代、related_to通用关联。这个表就是最简的 schema也是后期所有规则的“字典”。不要被“本体建模”这个词吓到。对于中小项目用 Python 里的一个字典就能把 schema 管理起来SCHEMA { entity_types: [产品, 部件, 组织, 地点], relation_types: [part_of, produced_by, located_in, replace_by, related_to], attr_keys: [型号, 发布日期, 价格] }这段代码的作用是定义一个配置文件后续实体识别和关系抽取都会参考这个结构。参数说明entity_types决定实体识别时保留哪些词relation_types决定关系模板的谓词库attr_keys决定从文本里额外抽哪些字段作为节点属性。经验是关系类型最好控制在 58 个一开始就写二十种关系规则和人工校验都会变得不可维护。2.2 实体识别自定义词典加正则必要时再引入模型在中文业务文本里我推荐第一版方案用jieba加自定义词典配合词性过滤。因为大多数领域实体型号、部门名、产品线都是短语通用分词器默认切分不准而自定义词典的投入产出比极高。先看核心代码import re import jieba import jieba.posseg as pseg # 自定义词典每行格式实体名 词频 词性 # 例如北斗导航 1000 nt jieba.load_userdict(domain_words.txt) def find_entities(sent: str): # 去掉多余符号避免分词被空格和标点带偏 cleaned re.sub(r[^\u4e00-\u9fa5A-Za-z0-9], , sent) entities [] for word, flag in pseg.cut(cleaned): if flag in {n, nr, ns, nt, nz}: entities.append((word, flag)) return entities这段代码先清洗文本再用带词性标注的pseg分词最后只保留名词类词性。参数说明domain_words.txt是领域词典词频数字影响分词优先级词性nt表示机构名、ns表示地名、nz表示专名find_entities返回的列表里每个元素是(实体文本, 词性)后面关系抽取直接复用这个结果。我说“第一版用词典”是因为很多业务文本句式相对固定比如专利摘要、产品说明、新闻通稿。如果文本口语化严重或者实体边界模糊再考虑用spacy的预训练中文模型做 NER。安装模型时要注意先pip install spacy再执行python -m spacy download zh_core_web_sm否则代码里spacy.load会直接报错。2.3 关系抽取先写规则模板再用依存句法兜底关系抽取是整个流程里最“玄学”的部分。我通常先用正则模板处理高频句式因为规则最直观也最容易排查。比如“A 是 B 的组成部分”“A 位于 B”每条规则对应一个三元组模式import re RULE_PATTERNS [ # 长模板优先于短模板避免误匹配 (r(?Psubj[\u4e00-\u9fa5]{2,8})是(?Pobj[\u4e00-\u9fa5]{2,12})的(?:组成部分|子类|一部分), lambda m: (m.group(subj), part_of, m.group(obj))), (r(?Psubj[\u4e00-\u9fa5]{2,8})位于(?Pobj[\u4e00-\u9fa5]{2,12}), lambda m: (m.group(subj), located_in, m.group(obj))), ] def extract_by_rule(sent: str): results [] for pattern, builder in RULE_PATTERNS: for matcher in re.finditer(pattern, sent): results.append(builder(matcher)) return results规则模板的好处是可解释抽错了一眼能看出是哪个正则误伤。参数说明{2,8}和{2,12}限制实体长度过短容易匹配到单字过长会把整句吞进去lambda负责把匹配结果拼成(主语, 关系, 宾语)三元组。实际使用时要按模板长度从长到短排序否则“A 是 B 的组成部分”会被短模板“A 是 B”提前截走。规则覆盖不了的句式我会用spacy的依存句法分析来提取“主谓宾”。中文模型需要先下载代码里nlp可以复用import spacy nlp spacy.load(zh_core_web_sm) def extract_by_dep(sent: str): doc nlp(sent) triples [] for token in doc: # 找出主语且它的核心词是动词 if token.dep_ in {nsubj, nsubjpass} and token.head.pos_ VERB: subj token.text verb token.head # 在动词的从属成分里找宾语或表语 for child in verb.children: if child.dep_ in {dobj, attr, xcomp}: triples.append((subj, verb.text, child.text)) return triples这段代码从依存树里抓“主谓宾”三元组。参数说明nsubj是名义主语nsubjpass是被动句主语dobj是直接宾语attr是表语token.head是当前词的核心词。注意预训练模型对中文长句的依存分析不一定稳定所以我的习惯是规则抽出来的结果当作高置信度集合依存句法抽出来的结果至少要人工抽查 20 条再决定是否全量使用。2.4 属性抽取把型号、日期、价格挂在节点上实体通常需要携带属性否则图谱只剩骨架。属性抽取不需要复杂模型正则往往就够了。比如从句子“北斗导航模块2023 年发布价格 1200 元”里提取型号和发布时间import re TIME_PATTERN re.compile(r(19|20)\d{2}\s*年?\s*\d{1,2}\s*月?) def extract_attrs(sent: str): attrs {} hit TIME_PATTERN.search(sent) if hit: attrs[发布时间] hit.group() # 这里可以继续加价格、型号等正则 return attrs属性抽取结果不会生成新节点而是作为节点的属性存进图里。关键建议属性不要抽太多优先保留查询时会用到的字段比如设备型号、时间戳其余信息放回原文避免图谱节点变得臃肿。3. 从 TXT 到可导入 Neo4j 的 Python 脚本一条龙跑通第二章解决了“怎么抽”的问题这一章把散落的函数串成一条可重复执行的流水线。目标很简单输入一篇 TXT输出三份文件——三元组 CSV、GraphML 图文件、给 Neo4j 用的导入脚本。3.1 分句与清洗先给文本“断句”关系抽取的单位是句子不是整篇。如果直接把整段文本扔给规则模板正则的边界很容易失守。所以我习惯先分句再过滤掉太短或太脏的句子def split_sentences(text: str): text re.sub(r\s, , text).strip() parts re.split(r[。\n], text) return [p.strip() for p in parts if len(p.strip()) 4]这段代码把中文句号、感叹号、分号和换行都当成句子边界。参数说明len(p.strip()) 4是过滤阈值太短的句子大概率没有有效三元组如果你处理的文本是英文或混排还要补充英文句号的切分规则我一般会加一个“后面跟空格或大写字母才切分”的条件否则小数点和缩写会被切碎。3.2 批量抽取把实体识别和关系抽取串进流水线分句之后对每个句子先跑规则模板再跑依存句法最后做去重和归一化def build_triples(text: str): triples [] for sent in split_sentences(text): triples.extend(extract_by_rule(sent)) triples.extend(extract_by_dep(sent)) return unique_triples(triples) def unique_triples(triples): seen set() result [] for subj, rel, obj in triples: key (subj.strip(), rel.strip(), obj.strip()) if key not in seen: seen.add(key) result.append(key) return resultbuild_triples是全流程的核心编排函数。参数说明extract_by_rule的规则结果优先保留因为规则通常更准extract_by_dep给出的三元组可能包含“发布、这是”这类弱关系后期可以在unique_triples里加入一个黑名单谓词表比如去掉“是、有、为”。去重用set是防止同一句话被多条规则重复命中导致边数量翻倍。如果处理的文本量很大我建议把spacy.load放到脚本顶层不要在每个函数里重复加载。批量处理时还可以用nlp.pipe(list_of_sents, batch_size64)这个方法一次处理一批句子比循环调用快很多def extract_by_dep_batch(sents, batch_size64): triples [] docs nlp.pipe(sents, batch_sizebatch_size) for doc in docs: # 和 extract_by_dep 内部逻辑一致 pass return triplesbatch_size是每次送到模型里的句子数量显存或内存小的调成 16性能好的可以调到 128。不设这个参数for循环逐句处理会明显变慢。3.3 输出三件套CSV、GraphML 和 Neo4j 导入语句抽好的三元组要落到磁盘否则程序一关就白干。我的输出习惯是三份同时生成import csv import networkx as nx def save_triples(triples, csv_pathtriples.csv, graphml_pathgraph.graphml): # 用 utf-8-sig 而不是 utf-8避免 Excel 打开中文乱码 with open(csv_path, w, encodingutf-8-sig, newline) as f: writer csv.writer(f) writer.writerow([source, relation, target]) writer.writerows(triples) G nx.DiGraph() # 边属性用 relation 字段保存谓词 G.add_edges_from((s, t, {relation: r}) for s, r, t in triples) nx.write_graphml(G, graphml_path) return GCSV 是给 Neo4j 用的GraphML 是给 Gephi 或 yEd 做可视化用的。参数说明encodingutf-8-sig是关键很多开发者用utf-8导入 Neo4j 后中文乱码换成带 BOM 的格式可以省掉一半编码问题nx.write_graphml需要lxml支持如果没安装会报ModuleNotFoundError记得先pip install lxml。Neo4j 导入我优先推荐LOAD CSV不写 Python 代码因为 Cypher 本身就能完成建节点和关系还自带去重LOAD CSV WITH HEADERS FROM file:///triples.csv AS row MERGE (s:Entity {name: row.source}) MERGE (t:Entity {name: row.target}) CALL apoc.create.relationship(s, row.relation, {}, t) YIELD rel RETURN count(rel);注意apoc.create.relationship可以动态创建不同类型的关系但要求 Neo4j 安装 APOC 插件。如果你的环境没装 APOC可以用MERGE (s)-[r:REL]-(t) SET r.name row.relation代替缺点是所有关系都是REL类型需要在属性里区分。3.4 验证中间结果用邻接矩阵看图谱是不是“一张网”导入 Neo4j 之前先在内存里用networkx检查图谱形状。我会重点看两个指标节点数和最大连通分量。如果图谱被拆成几十个小孤岛说明关系抽取漏了很多或者句子切分过碎。构建邻接矩阵是这一步最直观的手段import numpy as np def build_adjacency(G): nodes list(G.nodes()) idx {name: i for i, name in enumerate(nodes)} mat np.zeros((len(nodes), len(nodes)), dtypeint) for src, dst in G.edges(): mat[idx[src], idx[dst]] 1 return mat这段代码把有向图转成邻接矩阵。参数说明mat[i][j]为 1 表示节点i指向节点j如果想统计边权重可以把赋值1改成累加。邻接矩阵主要用于调试——打印出每个节点的出度入度比盯着图看更直观。4. 中文文本转图谱的 5 个坑与排查手段现象、原因、解法这一章是我做文本转知识图谱最常复盘的部分每条都是真实翻车记录。4.1 现象实体识别把“北京市人民医院”切成“北京/市/人民/医院”原因通用词典里没有“北京市人民医院”这个词jieba按最小分词策略切分。解决把完整实体名加进自定义词典同时把词频调高例如北京市人民医院 3000 nt这里3000是词频越大越容易被优先识别为整体。如果同一份文本里还有“北京”“人民医院”等词要注意词典词频不能过高否则会把其他正常句子里的词也合并掉。4.2 现象同一实体多种写法图谱里出现重复节点原因“阿里巴巴”和“阿里”在原文里同时出现字符串不同MERGE或add_edge会当成两个节点。解决在unique_triples之后加一层实体归一化维护一个同义词映射表ALIAS { 阿里巴巴: 阿里巴巴集团, 阿里: 阿里巴巴集团, 淘宝网: 阿里巴巴集团, } def normalize_entity(name): return ALIAS.get(name, name)归一化要谨慎别把“北京大学”和“北京”混成一个。我一般只合并能确认指向同一实体的简称比如公司名、产品代号。4.3 现象关系重复抽取边数量暴增原因一句话同时命中规则模板和依存句法比如“苹果公司发布手机”被规则抽一次又被依存句法抽一次。解决在unique_triples里对三元组去重后再去掉同义谓词比如把“位于”和“在”统一成located_in。真正暴增的根源往往是“关系谓词没归一化”所以去重前先做谓词字典映射。4.4 现象导入 Neo4j 中文乱码节点名变成“锟斤拷”原因CSV 文件用utf-8写入但 Neo4j 的LOAD CSV默认按系统区域解析或者文件本身带 BOM 导致解析异常。解决写 CSV 时使用utf-8-sig并用 Cypher 里设置文件编码更准确的做法是LOAD CSV WITH HEADERS FROM file:///triples.csv AS row同时在 Neo4j 配置文件或启动命令里指定-Dfile.encodingUTF-8。多数情况下utf-8-sig就能解决但要检查导入后的数据是否多出不可见字符。4.5 现象处理速度慢到怀疑人生原因在for循环里频繁调用spacy.load或者对每句话都跑一遍 NER、依存分析、词性标注。解决把nlp实例化放在脚本顶层用nlp.pipe批量处理如果不需要实体识别可以在spacy.load(..., disable[ner])里关掉 NER 组件。速度提升通常有 35 倍而且是纯收益不会影响关系抽取质量。5. 调参和验证别让图谱质量靠感觉很多项目做到“能出图”就停了但图能不能用需要用数据说话。这一章讲怎么量化质量以及三个最容易影响结果的参数。5.1 用 100 条人工标注算准确率和召回率而不是看“抽出了多少三元组”我见过有人汇报“从一千篇文本抽出了五千个三元组”听起来很唬人但抽出多少不重要抽对多少才重要。做法是随机抽 100 句话人工标出里面所有三元组然后和脚本结果对比y_true {(苹果公司, 发布, 手机), (苹果公司, 位于, 美国)} y_pred {(苹果公司, 发布, 手机), (苹果公司, 总部, 美国)} def evaluate(y_true, y_pred): tp len(y_pred y_true) precision tp / len(y_pred) if y_pred else 0 recall tp / len(y_true) if y_true else 0 f1 2 * precision * recall / (precision recall) if precision recall else 0 return precision, recall, f1这段代码计算精确率、召回率和 F1。参数说明y_true是人工标注集合y_pred是程序输出集合交集数量就是正确抽出的三元组数。精确率低说明“抽多了”需要收紧模板召回率低说明“抽漏了”需要补词典或扩模板。F1 超过 0.7 对这个任务来说已经是不错的基线。5.2 三个必调参数规则优先级、词典词频、批量大小规则优先级直接决定三元组质量。匹配时我会先排长模板再排短模板RULE_PATTERNS.sort(keylambda x: len(x[0]), reverseTrue)这个排序的核心逻辑是“长模板更具体、误伤更少”。第二个参数是词典词频词频决定了jieba是否把某个词当成整体通常从 500 到 3000 之间调具体依赖文本里该词的出现频率。第三个参数是batch_size它不改变抽取结果但会改变处理耗时也间接影响内存占用。如果使用 NER 模型还需要调置信度阈值。spacy的实体组件默认会返回低置信度结果我一般会对entity.ent_id_或概率值过滤只保留得分高的实体。但预训练模型没有直接暴露所有分数的 API所以我的做法是用规则结果作为硬标签模型结果只做候选补充再用人工抽查确认。5.3 常见误用知识图谱不是搜索引擎加速器文本转知识图谱做出来后经常被要求“帮我搜一下相关文档”。这里要泼一盆冷水知识图谱擅长回答关系类问题比如“A 和 B 之间有哪些路径”但不适合做全文检索也不适合做语义相似度搜索。如果你需要的是“找包含某关键词的段落”那用 ES 或 SQL LIKE 更合适。把图谱当数据库用查询性能往往不如传统关系型数据库。正确用法是把它当作“分析视图”用来发现人工阅读难以发现的间接关系。比如从一千篇公告里找出“哪些公司都投资了同一个项目”。这类问题用表格也能算但图谱的图遍历算法更直观也更容易可视化展示。6. 用可视化反哺迭代我每周都会做的一次图谱自检图谱不是一次性产物它需要回到业务里被使用再用反馈打磨。我每周都会做一次“自检”把最新三元组导入networkx画出全图再按度数排序检查那些连接了大量节点的中心节点。这个习惯帮我把很多错误的规则暴露出来比看指标抓错更快。可视化不需要专门的工具一段轻量代码就能完成import networkx as nx import matplotlib.pyplot as plt def inspect_graph(G, top_n10): # 按出度入度排序找出最活跃的节点 sorted_nodes sorted(G.degree(), keylambda x: x[1], reverseTrue) for node, deg in sorted_nodes[:top_n]: print(f{node}: {deg} 度) pos nx.spring_layout(G, seed42, k0.6, iterations50) nx.draw_networkx_nodes(G, pos, node_size[G.degree(n) * 20 for n in G.nodes()], node_color#8ECFC9, alpha0.8) nx.draw_networkx_edges(G, pos, edge_color#999, arrowsTrue, arrowsize12) nx.draw_networkx_labels(G, pos, font_size9) plt.show()参数说明seed42固定随机布局保证每次运行得到的图位置稳定k是节点之间的斥力系数值越小图越紧凑node_size按度数缩放直觉上最有问题的节点会变成大圆点。打印的度数列表能帮我快速定位“为什么某个通用词连了几百条边”这类异常。我踩过最深的一个坑是把所有抽取结果都塞进图里结果可视化后一团乱麻。后来我给自己立了个规矩每次可视化只保留和业务查询直接相关的三到五类关系其余先放 CSV 里存着。图谱干净了问题也就露出来了。文本转知识图谱这个方向真正花时间的从来不是跑模型而是把边界定清楚、把质量量出来、再不断地让算法结果回归到业务问题里。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/12 23:49:40

OTFS大规模MIMO信道估计:Matlab从LS到压缩感知实战

简介:这份资源面向通信工程、信号处理方向的研究生与工程师,聚焦高速移动场景下OTFS大规模MIMO系统的信道估计问题。OTFS通过将数据映射到时频网格以对抗多径与多普勒效应,而大规模天线阵列又显著提升了信道估计的复杂度,资源正是…

2026/10/12 23:49:40

ES和JS别再混为一谈:标准、引擎与云端兼容实战

1. 项目背景:为什么"ES与JS"总被混为一谈我最初接触前端时,也以为ECMAScript和JavaScript是同一个东西的两个名字,直到在一次云端Node.js服务联调里踩了坑,才真正把两者的关系捋清楚。那次服务在本地跑完全正常&#xf…

2026/10/12 23:49:40

大模型全链路实践:从环境搭建到推理部署的完整指南

简介:一份聚焦深度学习大模型从构建到部署全流程的实战指南,面向具备一定深度学习理论基础、正在推进大模型工程化落地的研发人员、数据科学家与技术爱好者。资料以Word文档形式提供,共1个文件,压缩包仅20KB,内容紧凑&…

2026/10/13 0:59:44

400G多模光模块标准详解:SR8与SR4.2架构、链路预算及避坑指南

简介:IEEE Std 802.3cm-2020是IEEE发布的以太网标准修正案,面向光通信产品开发与网络系统设计者,解决400Gbps以太网在多模光纤上的物理层规范与管理参数问题。该标准基于IEEE Std 802.3-2018及多个相关修正案,新增400GBASE-SR8与4…

2026/10/13 0:59:44

多段线简化算法实战:RDP与VW选型及工程避坑指南

我第一次被多段线简化算法逼到墙角,是在处理一条水域边界线的时候。数据源导出了八千多个顶点,画到地图上直接变成一团黑色煤球,每一段岸边弯道都被采样得密密麻麻。那时候我抱着“减几个点嘛”的想法,直接用间隔抽点,…

2026/10/13 0:59:44

FastMCP 2.x 干货笔记:把 OpenAI API 接入 AI SDK 的 TaoToken 配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/12 12:01:26

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/12 12:54:07

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/13 0:04:41

基于Triton部署YOLO目标检测:从训练脚本到线上服务

简介:这份资源面向希望将目标检测模型落地到生产环境的深度学习开发者与算法工程师,聚焦于用 Triton Inference Server 部署 YOLOv9 的完整实战方案,解决从训练权重到线上推理服务之间的工程化难题。压缩包共 16 个文件,约 886KB&…

2026/10/13 0:04:41

手把手教你云端部署Qwen-Image-2.1:从GPU选型到在线文生图服务

Qwen-Image-2.1 这个开源的图像生成模型,最近在内容创作和 AI 应用圈子里热度相当高。它属于通义 Qwen 系列里的视觉生成分支,让我眼前一亮的是它对中文长文本的理解能力——以前用那些老方案生成带中文文字的图,十个里九个是“乱码印刷体”&…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑