
简介这份PDF文档围绕中医与现代营养学融合的健康饮食知识图谱构建展开面向医疗与营养学研究人员、健康顾问及亚健康人群旨在解决网络饮食信息混杂、缺乏统一知识库的问题。资源包共1个PDF文件大小约2.22MB内容为期刊论文全文涵盖摘要、引言、本体构建、知识抽取与融合、Neo4j存储与可视化等完整章节。文中采用自顶向下方法借助Protégé构建模式层结合权威书籍与美食网站开源知识完成数据层将现代营养学与中医食疗、九种体质等知识整合为可查询的图谱。读者可从中获取知识图谱构建的完整技术路线、本体设计思路与图数据库落地方法理解如何从营养素与个人体质双重视角提供科学饮食建议。目前已有79人学习适合希望将知识图谱技术应用于健康领域的读者参考。1. 从「吃什么」到「怎么查」一份把中医体质和营养素塞进同一张图里的知识图谱打开外卖软件推荐算法能根据你的历史订单猜你想吃辣还是想吃甜但它猜不到你最近舌苔厚腻、大便黏滞更不会告诉你「痰湿体质少碰奶茶」。这就是当下健康饮食类产品的通病要么只讲热量和宏量营养素要么只谈性味归经两套话语体系各说各话。这份资源要解决的正是这个断层——它把中医饮食学和现代营养学拆成实体与关系重新组装进一个基于本体构建的知识图谱用 Neo4j 做存储和可视化。说得直白点它让「湿热体质的人适合吃哪些低 GI 食材」这种跨体系查询变成一条 Cypher 语句就能跑出来的结果。适合谁看做健康管理类应用的开发者、想拿知识图谱练手的数据方向学生以及需要给营养建议找结构化依据的产品经理。它不是一份即插即用的 API而是一套从本体设计到数据落地的完整施工图。2. 本体建模七步法怎么把「性味归经」和「维生素 A」放进同一个模式层知识图谱的骨架是本体本体没搭好后面灌再多数据也是散沙。这份资源用的是七步法工具选 Protégé 5.5.0配合 OntoGraf 插件做可视化。为什么不用自动化本体构建原文说得很实在自动抽取的概念精细度不可控而健康饮食这个领域恰恰要求概念边界清晰——「寒性」和「凉性」差一个字适用体质就变了。所以它走的是人工手动编辑路线先定顶层概念再逐级往下切。2.1 顶层类的划分逻辑与复用判断七步法的第一步是确定领域和范围第二步判断是否复用现有本体。这里有个关键决策目前不存在同时覆盖中医饮食学和现代营养学的现成本体库所以不复用但参考已有饮食知识图谱的概念和术语。顶层类最终定为三个饮食类、中医养生学类、现代营养学类。饮食类下面分食材和菜品两个分支食材依据《中国食物成分表》第 6 版切出植物食材和动物食材再往下细分为菌藻类、水果类、谷类及其制品等 31 个植物亚类和鱼虾蟹贝类、乳类及制品等 24 个动物亚类。菜品则按种类、口味、烹饪工艺三个维度展开口味又分基本型和复合型基本型 9 类复合型 42 类。中医养生学类下面挂体质、功效、性、味、归经五个分支体质就是那 9 种平和质、气虚质、阳虚质、阴虚质、痰湿质、湿热质、血瘀质、气郁质、特禀质。现代营养学类分营养素和相关人群营养素再分宏量营养素、维生素、矿物质。2.2 对象属性与数据属性的定义规则类分完了接下来定义属性。这份资源把属性分成两类对象属性描述节点与节点的关系数据属性描述节点与字面量的关系。对象属性里比较核心的有「取材于」「属于烹饪工艺」「具有口味」「属于菜品种类」「有利于/不利于」「富含/缺乏」「具有性」「具有味」「归经于」「具有功效」「有宜于/不宜于」。注意「有利于/不利于」和「有宜于/不宜于」是两组不同的关系前者连接食材/菜品与相关人群或体质后者连接性/味与体质。数据属性则包括食材编号、产地、菜品名、烹饪方式、功能描述、营养素功能、人群特征等。在 Protégé 里建好类和属性后用 OntoGraf 插件可以直观看到类之间的关联。这一步做完模式层就算立住了。下面这段 Python 代码演示的是用 rdflib 加载本体文件并查询所有对象属性的基本操作实际项目中可以用它做本体一致性检查from rdflib import Graph, RDF, RDFS, OWL # 加载 Protégé 导出的 OWL 文件 g Graph() g.parse(diet_ontology.owl, formatxml) # 查询所有对象属性及其定义域和值域 query PREFIX owl: http://www.w3.org/2002/07/owl# PREFIX rdfs: http://www.w3.org/2000/01/rdf-schema# SELECT ?prop ?domain ?range WHERE { ?prop a owl:ObjectProperty . OPTIONAL { ?prop rdfs:domain ?domain } OPTIONAL { ?prop rdfs:range ?range } } for row in g.query(query): print(f属性: {row.prop.split(#)[-1]}, 定义域: {row.domain}, 值域: {row.range})逻辑说明这段代码做的是本体自检确认每个对象属性的定义域和值域没有遗漏。参数上diet_ontology.owl是 Protégé 导出的本体文件路径格式选 XML 是因为 Protégé 默认导出 RDF/XML。如果查询结果里某个属性的 domain 或 range 为空说明建模时漏了约束需要回 Protégé 补上。3. 数据层施工从爬虫到 Neo4j 的完整链路与参数配置模式层是图纸数据层是砖瓦。这份资源的数据来源分两块网站和书籍。网站包括几个美食菜谱站和养生资讯站书籍包括《中国食物成分表》《本草纲目》《九种体质营养方案》《食品营养学》等。网站数据用 Scrapy 爬XPath 解析预处理后存 Excel书籍数据先 OCR 识别再人工校对同样存 Excel。然后通过字段映射完成实体抽取和关系抽取。3.1 Scrapy 爬虫的关键配置与 XPath 解析爬虫部分的核心是并发控制和解析规则。原文提到用 Scrapy 框架做高速并发爬取这里给一个可复现的爬虫配置片段import scrapy class RecipeSpider(scrapy.Spider): name recipe_spider # 并发请求数控制在 8避免对目标站造成压力 custom_settings { CONCURRENT_REQUESTS: 8, DOWNLOAD_DELAY: 0.5, RETRY_TIMES: 2, USER_AGENT: Mozilla/5.0 (compatible; DietKG/1.0) } def parse(self, response): # 提取菜品名称、食材列表、烹饪工艺 for item in response.xpath(//div[classrecipe-item]): yield { dish_name: item.xpath(.//h2/text()).get(), ingredients: item.xpath(.//ul[classingredients]/li/text()).getall(), cooking_method: item.xpath(.//span[classmethod]/text()).get(), flavor: item.xpath(.//span[classflavor]/text()).get() }逻辑说明CONCURRENT_REQUESTS设 8 是平衡速度和目标站承受能力的经验值DOWNLOAD_DELAY设 0.5 秒是礼貌爬取的基本操作。XPath 里的//div[classrecipe-item]需要根据目标站实际 DOM 结构调整不同菜谱站的 class 名不一样这是最容易翻车的地方——建议先用浏览器开发者工具确认结构再写规则。3.2 基于规则的知识抽取与字段映射书籍类数据的抽取分两种策略。营养素信息因为数量固定采用人工提取保证准确率《本草纲目》内容量大采用规则加人工结合的方式。规则的核心逻辑是食材实体出现位置固定性味归经和功效是固定表达可以直接匹配。比如「性」的取值只有平、寒、热、温、凉五种「味」只有辛、甘、苦、咸、酸五种用正则就能圈定范围。抽取完成后把 Excel 表字段映射到本体模式层。这一步的本质是Excel 的列名对应本体里的数据属性行与行之间的关联对应对象属性。比如「食材编号」列映射到数据属性食材编号「食材名」和「功效」两列如果出现在同一行就生成一条具有功效对象属性关系。3.3 py2neo 写入 Neo4j 的批量操作与约束设置数据融合之后写入 Neo4j。原文用的是 py2neo 库这里给一个批量创建节点和关系的模板from py2neo import Graph, Node, Relationship # 连接 Neo4j默认 bolt 端口 7687 graph Graph(bolt://localhost:7687, auth(neo4j, your_password)) # 先建约束避免重复节点 graph.run(CREATE CONSTRAINT IF NOT EXISTS FOR (n:Ingredient) REQUIRE n.code IS UNIQUE) graph.run(CREATE CONSTRAINT IF NOT EXISTS FOR (n:Dish) REQUIRE n.name IS UNIQUE) # 批量创建食材节点 ingredients [ {code: V001, name: 菠菜, nature: 凉, flavor: 甘}, {code: V002, name: 生姜, nature: 温, flavor: 辛}, ] for ing in ingredients: node Node(Ingredient, **ing) graph.merge(node, Ingredient, code) # 创建关系食材具有性 graph.run( MATCH (i:Ingredient {name: 菠菜}), (n:Nature {name: 凉}) MERGE (i)-[:具有性]-(n) )逻辑说明merge方法比create安全它会在节点已存在时跳过而不是报错。约束constraint一定要在建节点之前创建否则数据量上来后去重会非常痛苦。auth参数里的密码是 Neo4j 安装时设置的默认用户是neo4j。如果连接报错先检查 Neo4j 服务是否启动、bolt 端口是否被防火墙拦截。4. 避坑与排查本体建模和数据融合阶段最容易翻车的五个地方4.1 本体类层次过深导致 Protégé 推理机卡死现象在 Protégé 里运行推理机如 HermiT时界面无响应CPU 占用飙升。原因食材亚类分了 55 个每个亚类下面还有实例类层次超过五层后推理复杂度指数上升。解决把不需要推理的类标记为「已定义类」而非「原始类」或者把实例数据从本体文件里剥离只保留模式层做推理数据层单独用 Neo4j 管理。4.2 同义食材未合并导致图谱中出现重复节点现象查询「西红柿」时只返回一个节点但「番茄」作为独立节点存在两者之间的关系完全隔离。原因知识融合阶段只做了字段映射没有做同义词归并。解决在写入 Neo4j 之前加一步实体对齐用同义词表或编辑距离做匹配把「西红柿/番茄」「土豆/马铃薯」这类同义实体统一到同一个唯一标识下。原文表 4 里给了示例但实际数据中同义表述远不止这些建议维护一个持续更新的同义词字典。4.3 OCR 识别把「归经」内容错切成乱码现象从《本草纲目》扫描版识别出的文本里「归肺经」变成「归月市经」「性平」变成「性干」。原因古籍扫描件字体特殊通用 OCR 模型对竖排和异体字识别率低。解决不要指望 OCR 一步到位识别后必须人工校对尤其是性味归经这类关键字段。可以先用规则过滤——如果识别出的「性」不在平、寒、热、温、凉五个字里直接标记为待校对。4.4 Neo4j 批量写入时事务过大导致内存溢出现象用 py2neo 一次性提交上万条节点创建请求程序报OutOfMemoryError或连接超时。原因Neo4j 的单个事务有内存上限批量操作没有分批。解决每 500 到 1000 条提交一次用graph.run配合UNWIND语句做批量写入比逐条merge快一个数量级。另外写入前先建索引CREATE INDEX FOR (n:Ingredient) ON (n.name)。4.5 爬虫被目标站封 IP 后没有降级方案现象爬了不到两百条菜谱数据后续请求全部返回 403。原因并发数设太高或者没有遵守目标站的 robots.txt。解决把CONCURRENT_REQUESTS降到 2 到 4DOWNLOAD_DELAY加到 1 秒以上同时准备一个备用数据源。如果目标站有公开 API优先走 API 而不是爬页面。血泪经验是爬虫跑通只是开始能稳定跑完才是本事。5. 进阶玩法用 Cypher 做跨体系查询和体质推荐验证图谱建好之后真正的价值在于查询。这份资源最有意思的地方是它把中医体质和营养素放在了同一张图里这意味着你可以写出跨体系的查询语句。比如「找出所有适合痰湿体质且富含膳食纤维的食材」这条查询同时用到了中医养生学的关系和现代营养学的关系。// 查询适合痰湿体质且膳食纤维含量高于 2g/100g 的食材 MATCH (i:Ingredient)-[:有宜于]-(c:Constitution {name: 痰湿质}) MATCH (i)-[:富含]-(n:Nutrient {name: 膳食纤维}) WHERE n.content_per_100g 2.0 RETURN i.name AS 食材, n.content_per_100g AS 纤维含量 ORDER BY n.content_per_100g DESC逻辑说明第一条MATCH走的是中医养生学的关系路径第二条MATCH走的是现代营养学的关系路径两条路径在食材节点上交汇。WHERE子句里的content_per_100g是数据属性需要在导入数据时确保这个字段有值。如果查询返回空先检查有宜于关系的方向是否正确——原文表 2 里定义的是「性/味」指向「体质」但食材和体质之间用的是「有利于/不利于」别搞混了。再进一步可以做一个简单的体质推荐验证给定一个体质类型查询所有「有宜于」该体质的食材再按营养素含量排序输出前十条。这个查询可以用来验证图谱的数据完整性——如果某个体质查出来的食材少于五条说明数据覆盖不够需要补充数据源。// 体质推荐验证统计每种体质关联的食材数量 MATCH (c:Constitution)-[:有宜于]-(i:Ingredient) RETURN c.name AS 体质, count(i) AS 推荐食材数 ORDER BY 推荐食材数 ASC这条查询跑出来的结果如果某一行数字特别小就是数据盲区。我一般会把这个查询作为数据质量巡检的固定动作每次更新数据后跑一遍低于阈值的体质类型就重点补数据。从那以后我每次做完图谱导入都强制走一遍这个统计查询确认没有哪个体质被漏掉。希望帮到你。本文还有配套的精品资源点击获取