知识图谱也会有脏数据?用 pySHACL 给 RDF 加一道数据校验
做 MySQL 的时候我们对“数据校验”其实很熟悉。比如用户表CREATETABLEuser(idBIGINTPRIMARYKEY,usernameVARCHAR(64)NOTNULL,ageINT);这里已经偷偷规定了很多规则id 必须有 username 不能为空 username 最长 64 age 必须是整数前端还可能继续限制年龄必须大于 0 手机号必须 11 位 邮箱必须符合格式所以数据库世界里我们很自然地知道数据不是能存进去就行还必须符合规则。但当我们开始学习RDF OWL Ontology Knowledge Graph以后很容易忽略一个问题知识图谱里的数据写错了怎么办比如我们定义Employee表示员工。员工应该至少有姓名 工号 所属部门结果某一天进来一条数据王小明 类型Employee 年龄二十八岁甚至王小明 email123456或者员工 A 同时属于 8 个主部门从 RDF 的角度来看这些数据可能依然能够组成三元组。但从我们的业务角度看明显有问题。这时候就需要今天要讲的东西SHACL。以及一个非常实用的 Python 开源项目RDFLib/pySHACLGitHubhttps://github.com/RDFLib/pySHACLpySHACL 做的事情可以用一句话概括拿一套 SHACL 规则检查你的 RDF 数据到底合不合格。一、先用人话理解 SHACL假设现在有一个员工王小明我们希望所有员工满足这些条件必须有姓名 姓名只能有一个 必须有邮箱 年龄必须是整数 年龄不能小于 18 必须属于一个部门这其实就是一套数据规则在普通程序里我们可能写ifuser.nameisNone:raiseValueError(姓名不能为空)ifuser.age18:raiseValueError(年龄不能小于 18)在 Java 里可能写NotNullprivateStringname;Min(18)privateIntegerage;那知识图谱呢也需要类似的机制。SHACL 就可以理解成RDF / Knowledge Graph 世界里的数据校验规则。SHACL 全称Shapes Constraint Language可以简单理解成Shape 一个数据应该长什么样比如EmployeeShape规定Employee 应该长什么样。二、Ontology 不是已经定义 Class 和 Property 了吗这是很多小白第一次接触 SHACL 时最疑惑的问题。我们不是已经有OWL Ontology了吗例如Employee有name age email belongsToDepartment为什么还需要 SHACL因为“描述知识”与“校验数据”不是完全一回事。Ontology 更关注这个世界有哪些概念 它们之间是什么关系 哪些类存在继承 可以推理出什么新知识而 SHACL 更关注这条数据合不合格可以简单记OWL 知识是什么意思SHACL 数据写得对不对举个例子。Ontology 告诉我们BackendEngineer 是 Employee 的一种这属于语义建模而 SHACL 告诉我们Employee 必须有一个 name age 必须是整数 email 最少有一个这是数据约束两者完全可以一起使用。三、我们先准备一份 RDF 数据假设公司知识图谱里有两个员工。新建data.ttl内容prefix ex: http://example.com/ . prefix xsd: http://www.w3.org/2001/XMLSchema# . ex:zhangsan a ex:Employee ; ex:name 张三 ; ex:age 28 ; ex:email zhangsanexample.com . ex:lisi a ex:Employee ; ex:name 李四 ; ex:age 十八岁 .第一位员工张三数据比较正常name 张三 age 28 email zhangsanexample.com第二位李四有两个问题。第一个age 十八岁这是字符串。我们希望它是整数第二个没有 email假设业务规定员工必须有邮箱那么这条数据就应该被检查出来。四、没有 SHACL 时会发生什么从 RDF 角度来看ex:lisi ex:age 十八岁 .依然是一条正常的三元组。结构依旧是Subject Predicate Object也就是李四 年龄 十八岁RDF 本身并不会像 MySQLINT字段那样直接阻止你保存。这也是知识图谱和传统关系数据库一个非常明显的区别。知识图谱非常灵活。但是灵活的代价就是数据质量更难控制。于是 SHACL 出场了。五、给 Employee 定义一个 Shape新建shapes.ttl我们先写一套非常简单的规则prefix sh: http://www.w3.org/ns/shacl# . prefix ex: http://example.com/ . prefix xsd: http://www.w3.org/2001/XMLSchema# . ex:EmployeeShape a sh:NodeShape ; sh:targetClass ex:Employee ; sh:property [ sh:path ex:name ; sh:minCount 1 ; sh:maxCount 1 ; ] ; sh:property [ sh:path ex:age ; sh:datatype xsd:integer ; sh:minCount 1 ; ] ; sh:property [ sh:path ex:email ; sh:minCount 1 ; ] .第一次看可能有点乱。我们拆开。六、sh:targetClass 是什么意思这里sh:targetClass ex:Employee ;意思就是这套规则检查所有 Employee。也就是说只要数据里出现ex:zhangsan a ex:Employee .那么zhangsan就需要接受EmployeeShape的检查。可以理解成EmployeeShape ↓ 检查所有 ↓ Employee七、姓名必须存在怎么写看这一段sh:property [ sh:path ex:name ; sh:minCount 1 ; sh:maxCount 1 ; ] ;这里sh:path ex:name表示我要检查 name 这个属性。然后sh:minCount 1表示至少出现一次。再sh:maxCount 1表示最多出现一次。合起来就是一个员工必须有且只能有一个姓名。如果ex:user001 a ex:Employee .完全没有name那么校验失败如果ex:user001 ex:name 张三 ; ex:name 李四 .两个姓名。同样校验失败八、这其实很像数据库约束你可以这样对照。数据库nameVARCHAR(64)NOTNULLSHACLsh:path ex:name ; sh:minCount 1 ;数据库必须唯一只有一个值SHACLsh:maxCount 1 ;虽然两者机制完全不同但对于刚入门的人这样理解非常直观。九、怎么规定 age 必须是整数我们写sh:property [ sh:path ex:age ; sh:datatype xsd:integer ; sh:minCount 1 ; ] ;这里最关键的是sh:datatype xsd:integer ;意思age 必须是整数类型所以ex:age 28 ;可以通过。但ex:age 十八岁 ;就不符合规则。是不是已经开始有点像类型系统了这也是为什么我很喜欢把 SHACL 给程序员解释成给 RDF 加上一层“类型 校验规则”。十、甚至还能限制年龄范围比如业务规定员工年龄不能低于 18 岁。可以继续增加sh:minInclusive 18 ;完整sh:property [ sh:path ex:age ; sh:datatype xsd:integer ; sh:minInclusive 18 ; sh:minCount 1 ; ] ;现在ex:age 28 ;通过。但是ex:age 16 ;失败。是不是已经非常接近ifage18:raiseValueError(...)了十一、SHACL 规则有了谁来执行这就轮到pySHACL出场了。pySHACL 是一个纯 Python 的 SHACL 验证器可以拿RDF Data Graph去对照SHACL Shapes Graph执行检查。整个过程data.ttl ↓ 真实 RDF 数据 shapes.ttl ↓ SHACL 校验规则 ↓ pySHACL ↓ Validation Report最终告诉你通过 或者 哪里没通过十二、安装 pySHACL安装很简单pipinstallpyshacl检查pyshacl--help如果能正常看到参数说明pyshacl基本安装完成。十三、最简单的一条命令现在目录demo/ ├── data.ttl └── shapes.ttl执行pyshacl\-sshapes.ttl\data.ttl这里-s表示SHACL Shapes Graph也就是我们的规则文件。而data.ttl就是待检查数据完整流程data.ttl shapes.ttl ↓ pyshacl ↓ 检查十四、如果数据全部正常假设ex:zhangsan a ex:Employee ; ex:name 张三 ; ex:age 28 ; ex:email zhangsanexample.com .满足name 至少一个 name 最多一个 age 是整数 email 至少一个那么结果类似Conforms: True这就表示数据符合规则。十五、如果数据不符合呢我们的李四存在age 类型错误 email 缺失那么验证结果会告诉你Conforms: False同时还会生成Validation Report指出哪个节点有问题 哪个属性有问题 违反了什么 Constraint这就比程序里简单打印数据错误有用太多了。十六、让输出更适合人看pySHACL 的命令行可以指定-fhuman例如pyshacl\-sshapes.ttl\-fhuman\data.ttl这样输出更偏人类可读特别适合本地调试。如果你后面想把结果交给程序处理还可以选择其他 RDF / JSON-LD 等输出格式。十七、也可以直接用 Python 调用如果你本身就在做Python AI Knowledge Graph GraphRAG那其实没必要每次跑命令行。可以直接frompyshaclimportvalidate然后frompyshaclimportvalidate conforms,results_graph,results_textvalidate(data_graphdata.ttl,shacl_graphshapes.ttl,)print(是否合法,conforms)print(results_text)运行python validate_graph.py如果conforms True说明验证通过如果False就可以查看results_text定位问题。十八、validate 为什么返回三个东西pySHACL 的validate()通常返回conforms,results_graph,results_text分别可以理解为conforms 最终通过没通过results_graph 结构化的验证报告 RDF Graphresults_text 方便人阅读的文本报告所以最简单ifconforms:print(数据合法)else:print(数据有问题)print(results_text)已经可以用于很多场景。十九、实际项目里怎么用假设你正在做企业知识库数据来源很多MySQL Excel 接口 人工录入 大模型抽取 PDF 爬虫最后全部转成RDF问题来了。不同来源的数据质量可能完全不同。比如 AI 从文档抽取出张三 职位Java但是你的知识模型规定职位应该是BackendEngineer FrontendEngineer ProductManager而Java应该属于Skill如果不做检查脏数据 ↓ 直接进入知识图谱 ↓ 越来越多 ↓ 查询结果开始奇怪 ↓ GraphRAG 最后也被污染这就是非常现实的问题。二十、所以可以在入库前增加一道校验架构可以变成PDF / Excel / MySQL / API ↓ 数据抽取 ↓ RDF 数据 ↓ pySHACL ↓ 是否符合 Shape ↙ ↘ 否 是 ↓ ↓ 错误队列 写入图谱这样SHACL 就变成了知识图谱的数据质量防火墙。这个说法对于程序员来说特别容易理解。二十一、例如 AI 自动抽取知识假设大模型从简历里提取{name:王小明,age:28岁,skill:[Go,Redis]}AI 看起来抽对了。但age 28岁如果我们的 Ontology 要求age integer那么它实际上是不符合规则的。我们把数据转成 RDFex:wangxiaoming a ex:Employee ; ex:name 王小明 ; ex:age 28岁 .pySHACL 马上就可以发现DatatypeConstraintComponent没有通过。然后我们就可以拦下来 ↓ 修复 ↓ 重新校验 ↓ 再进入知识图谱这在LLM Knowledge Graph场景里其实特别有价值。二十二、还能校验字符串长度比如员工编号不能太短。可以sh:property [ sh:path ex:employeeCode ; sh:minLength 5 ; ] ;如果E1太短。就会验证失败。二十三、还能使用正则表达式比如邮箱sh:property [ sh:path ex:email ; sh:pattern ^[^][^]\\.[^]$ ; ] ;那么zhangsanexample.com可以通过。而123456不能通过。这就特别像后端经常写的regex校验。二十四、还能限制值只能来自某几个选项例如员工状态只能active disabled left可以使用sh:in ( active disabled left ) ;于是active合法。但是abc不合法。和 Java 里的Enum是不是很像二十五、还可以检查对象类型假设Employee有一个belongsToDepartment我们希望它指向的必须是Department可以sh:property [ sh:path ex:belongsToDepartment ; sh:class ex:Department ; ] ;正确王小明 ↓ belongsToDepartment ↓ 研发部其中研发部 a Department但是如果王小明 ↓ belongsToDepartment ↓ Go那显然就奇怪了。SHACL 可以把这类问题找出来。二十六、一个比较完整的 EmployeeShape最后可以写成prefix sh: http://www.w3.org/ns/shacl# . prefix ex: http://example.com/ . prefix xsd: http://www.w3.org/2001/XMLSchema# . ex:EmployeeShape a sh:NodeShape ; sh:targetClass ex:Employee ; sh:property [ sh:path ex:name ; sh:minCount 1 ; sh:maxCount 1 ; sh:datatype xsd:string ; ] ; sh:property [ sh:path ex:age ; sh:minCount 1 ; sh:maxCount 1 ; sh:datatype xsd:integer ; sh:minInclusive 18 ; ] ; sh:property [ sh:path ex:email ; sh:minCount 1 ; sh:pattern ^[^][^]\\.[^]$ ; ] ; sh:property [ sh:path ex:belongsToDepartment ; sh:minCount 1 ; sh:maxCount 1 ; sh:class ex:Department ; ] .现在我们的员工就有了比较明确的数据规则姓名 必须存在 只能有一个 必须是字符串 年龄 必须存在 必须是整数 必须 18 邮箱 必须存在 必须符合格式 部门 必须存在 只能一个 必须指向 Department这已经非常接近真实项目了。二十七、pySHACL 还可以配合推理pySHACL 不仅仅能读取 RDF ↓ 直接验证还可以在验证前选择RDFS OWL RL等推理方式。Pythonconforms,graph,textvalidate(data.ttl,shacl_graphshapes.ttl,inferencerdfs,)或者命令行pyshacl\-sshapes.ttl\-irdfs\data.ttl为什么有用例如 Ontology 定义BackendEngineer ↓ subClassOf ↓ Employee然后王小明 a BackendEngineer虽然数据没有直接写王小明 a Employee但是经过 RDFS 推理后可以得到王小明 也是 Employee再进行EmployeeShape验证。这样推理 数据校验就串起来了。二十八、这和 Protégé 又是什么关系如果你看过前面 Protégé 那篇可以这样理解。Protégé设计 Ontology例如Employee Department Skill Employee belongsToDepartment Department然后pySHACL负责检查实际数据是不是符合规则可以形成Protégé ↓ 设计知识模型 SHACL ↓ 设计数据约束 pySHACL ↓ 执行数据校验 RDF Graph ↓ 合格的数据这就是比较完整的知识工程工作流。二十九、SHACL 和 Java Bean Validation 其实很像如果你是 Java 程序员可以直接这样类比。JavapublicclassUser{NotNullprivateStringusername;Min(18)privateIntegerage;}SHACLsh:property [ sh:path ex:username ; sh:minCount 1 ; ] ; sh:property [ sh:path ex:age ; sh:minInclusive 18 ; ] ;本质思路差不多Java Object Validation Annotation对应RDF Graph SHACL Shape而Hibernate Validator有点类似pySHACL虽然实现机制不同但作为入门理解非常好用。三十、为什么我觉得 pySHACL 很适合知识图谱新手因为很多知识图谱教程一开始就在讲RDF RDFS OWL SPARQL Reasoner Description Logic非常抽象。但 pySHACL 解决的是一个极其具体的问题我的数据到底对不对你只需要一份 RDF 数据 一份规则然后pyshacl-sshapes.ttl data.ttl马上就可以看到结果。学习反馈非常直接。三十一、它特别适合哪些项目第一种Knowledge Graph知识图谱入库前SHACL Validation保证数据质量。第二种GraphRAG大模型抽取实体关系后LLM ↓ Triples ↓ SHACL ↓ Knowledge Graph减少错误实体和关系进入图谱。第三种企业数据治理不同部门Excel MySQL API CSV统一映射成 RDF 后SHACL作为统一质量规则。第四种Ontology 项目Protégé 建模以后真正的数据需要一套独立验证机制。三十二、如果数据量非常大怎么办简单项目可以直接pySHACL RDFLib验证。但 pySHACL 现在也支持SPARQL Remote Graph Mode也就是数据不一定非得全部下载成一个本地 ttl还可以针对远程 SPARQL Graph 做验证。另外项目也提供HTTP REST Service模式。也就是说后面完全可以把pySHACL包装成一个知识图谱质量校验服务架构变成数据系统 ↓ POST /validate ↓ pySHACL Service ↓ Validation Report对于企业系统反而更加实用。三十三、CI/CD 里甚至也可以用这是我觉得非常有意思的一个玩法。假设你的 GitHub 仓库里ontology/ shapes/ data/每次有人提交 RDF 数据git pushGitHub Actions 自动执行pyshacl\-sshapes.ttl\data.ttlpySHACL 的 CLI 会通过不同退出码告诉我们0 数据符合规则 1 数据不符合规则于是 CIRDF 合法 ↓ ✅ Pass或者RDF 不合法 ↓ ❌ Build Failed是不是很像代码提交之前跑单元测试所以你甚至可以把 SHACL 理解成Knowledge Graph 的自动化测试。三十四、这可能才是 SHACL 最好理解的方式我们平时写代码有Unit Test验证代码。数据库有Constraint验证数据。API 有JSON Schema验证请求。Java 有Bean Validation验证对象。而 RDF / Knowledge GraphSHACL验证图数据。所以JSON → JSON Schema Java Object → Bean Validation RDF Graph → SHACL这样记基本就不会忘。三十五、最后总结如果你正在学习Ontology RDF OWL Knowledge Graph GraphRAG我非常建议顺手把SHACL也学一下。因为真实项目里一个知识图谱的问题通常不是能不能存数据而是存进去的数据到底靠不靠谱完整的数据链路应该更像原始数据 ↓ 解析 / LLM 抽取 ↓ RDF ↓ SHACL Rules ↓ pySHACL ↓ Validation ↓ 修复错误 ↓ 进入 Knowledge Graph ↓ SPARQL / GraphRAG / Agent其中 pySHACL 做的事情看起来很简单检查数据但它解决的是知识工程里一个非常重要的问题数据质量。如果前面的知识图谱里已经塞满了错误类型 错误关系 缺失属性 非法值那么后面不管SPARQL还是GraphRAG甚至AI Agent最终得到的结果都会受到影响。所以 Ontology 告诉系统这个世界应该是什么样。SHACL 告诉系统这批数据有没有按照这个世界的规则来。而 pySHACL就是负责真正执行这场“知识图谱体检”的工具。项目地址https://github.com/RDFLib/pySHACL
上一篇/下一篇内容由系统自动关联
返回资讯列表 →