-
Notifications
You must be signed in to change notification settings - Fork 499
第一期w2:知识表示
(鲍捷,2016-06-14)
知识表示(Knowledge Representation,KR,也译为知识表现)是如何将结构化数据组织,以便于机器处理和人的理解的方法。从结构推导出新的结构,这就是推理。传统上KR属于逻辑的分支,但在实践中我们会用很简单、可读、可维护的数据结构。
经典的教科书中的KR,主要关注的是如何方便机器处理。但是在现实的工程中,如何方便人的理解也是极为关键的。在工程实践中,人才是知识不能被处理好、不能快速交换、不能规模化的核心。
知识表现的瓶颈不在于机器处理能力的不足,而在于人的认知能力的不足。因此,我们在学习知识表现方法的时候,要始终牢记知识的可读性、可维护性要远远比它的表达力、计算速度重要。知识是为人阅读而设计的,只是偶尔被机器执行。
作为工程师,我们时时刻刻把可行性放在心中。传统的知识图谱的KR,从逻辑和推理讲起,有一阶逻辑(first-order logic)和描述逻辑(description logic),后来又有逻辑程序(logic program)和生成规则(Production Rule)。但是我反对从逻辑开始理解知识图谱。语义网和知识图谱是关于数据的,关于结构促进数据的流动,用结构化数据增进其他系统的自动化能力的。逻辑只是很小的一个插件。
语义网,或者现在的知识图谱,在应用中,核心问题不是“应该怎么样”,而是“不得不怎么样”。语义推理对数据质量的要求很高,在工程上成本就承受不了。现实的应用,只能逐步提高数据的质量,从数据清洗开始就要承担巨额的投入,然后做实体抽取,实体链接,对齐,消歧,关系抽取,对齐,词库提取,本体建模,这每一步都是海一样的银子砸进去。
然后在推理中,推理规则都需要人生成。现在机器生成规则的能力很弱,几乎不可用。仅仅是属性和直接关系的查找可以机器做,稍微复杂的长程关系都需要人来写。这在工程部署中有巨大的困难,因为这样的工程师很少,写出来的东西可维护性,性能,普适性都成问题。所以现实中的系统,很少有做推理的。即使是做推理,也很少是一阶逻辑推理,一般也就是if then else,命题逻辑。很多时候还要容忍数据中的噪声,和正则表达式等结合在一起用。所以学术派的推理机,一般都用不了。
所以我们会了解RDF和OWL,但是并不推荐使用这些W3C标准作为工程的选择。我们认为JSON和关系数据库是工程中成本较小的解决方案。在某些特定场合,图的表示也是合理的。这会在之后的“知识存储”的章节继续讨论。
其他一些我的个人观点
- 我对关联数据的看法 http://baojie.org/blog/2014/12/21/on-linked-data/
- 语义网的工具演化 http://baojie.org/blog/2014/02/12/semantic-tool-evolution/
- 语义网不需要描述逻辑 http://baojie.org/blog/2013/02/05/semantic-web-and-description-logic/
在知识提取之后,如何表示知识?其实知识并不神秘,只是一些数据之间的关系。在计算机中的表示,就是数据结构。传统上,我们把那些能够导出新的关系的关系(比如“爸爸的爸爸是爷爷”,这里面“爸爸”和“爷爷”都是关系)看成知识。但是在目前的知识图谱实践中,并不会有严格的区分,我们把各种结构都可以看成广义的知识。
不过不是所有的数据结构我们会用知识工程的方法来处理,比如集合、哈希表、队列、堆栈、链表等等,一般丢给软件工程去处理。另一类特殊的结构,二维表,我们丢给数据工程去解决。当然数据工程和知识工程之间没有严格的界限,二维表和复杂知识结构表示之间也有交叉,暂留后述。
知识表现的数据结构,一般来说是那些“复杂”的结构,最常见的就是图(graph)和树(tree)。
知识表现的图,是“有类型的边”(typed edge),分析方法和一般的图论和社交关系图谱中分析的无类型的边很不相同。传统的Web结构只有“链接”这一种关系。在2000年代中,语义网试图给链接加上类型说明,如某人主页声明工作于某公司,这就有“工作”关系。这里,类型就是边的“元数据”(metadata)。后来,发现在应用中还需要给边,当然还有节点,添加更多的元数据,这就形成了有类型的边构成的图谱结构,上面每个边和每个节点都拥有元数据。
图的知识表现,演化出两个流派。一个是RDF图,一个是属性图(Property Graph)。RDF图是W3C的官方标准,得到了政府资金和一些大公司的大力支持,但是最终市场表现平平。属性图是草根自发的,最终得到了市场的认可,现在主要是中小企业、创业公司在用。RDF图是科学顶层设计出来的,属性图是工程实战中总结出来的。它们发展轨迹的不同也再次证明,好的架构一般是总结出来的,不是凭空设计出来的。
RDF图的基础是三元组,用URI命名节点和连接节点,有严格的语义,约束比较多。属性图没有严格的语义,可以比较自由地声明节点和边的属性。RDF的优势在于推理,但是三元组的组织使稍复杂的关系的表达很困难,具体后述。属性图不定义推理,但是可以通过查询语言(如Gremlin)来做模式(pattern)的查找和图上的遍历(traverse),可以实现特设的(ad-hoc)的推理。也待到图数据库部分细说。
用图表示知识,丰富的知识结构主要表现为图上的边,各种推理算法就是在图上推导出边的算法。在传统图论里,有可达性(reachability)推理,有大量的优化研究。一些基本的传递性的推理,如分类树(taxonomy),是可以转化为图可达性推理的。但是大量的其他类型的推理,没有成熟的工程系统和算法可用。现有的图数据库,都局限很大,工程上成本很高。
图表示的另一个问题是对混合表示不是很友好。因为知识提取的成本是很高的,所以现实的工程中我们很难一步到位生成纯结构化的数据表示,我们的数据往往是结构化和非结构化(主要是文本)混合的。其中结构化的比例,结构化的质量,可能是在应用的过程中逐步提升的。开始的时候可能文本的比例比较大。虽然RDF图和属性图上的节点都可以有文本属性,但是图的索引还是与文本索引大不同,在实际使用中需要依赖集成Lucene之类的全文检索引擎。由于文本不是“一等公民”,很多建模难以实现,比如在RDF里文本不能作为三元组的主语。
由于图表示很复杂,最广为接受的知识组织其实是树(taxonomy,hierarchy)。这是人的认知决定的。计算机发展这么多年,界面元素的组织,被广为接受的也只有树、列表、表格。那些看起来很复杂的知识库,其基础也都是树。
所以树形的JSON最终脱颖而出,不是偶然的。它符合人的认知,满足了结构化和非结构化混合表示的需要,兼容现有的工程实现。JSON表示被称为“文档”(document),2009年以来兴起了很多文档数据库(document database)。最近又有了PostgeSQL和OrientDB这样混合关系与文档的数据库,可以实现可读性好、工程兼容性好、表达力也还够用的知识表示。
JSON是很简单的数据格式。JSON成为Web API的事实标准,部分实现了当初语义网的一些目标。但是几年前,我和一位语义网领域的知名教授聊到语义数据的表示问题,我提到了JSON,他表示没听说过。这让我很震惊,学术界何以对工业界数据表示的事实标准如此不关心呢?
我们做研究,一定要从实践中来,到实践中去。实际的数据是什么样,用什么样的成本能获得这些数据,这都不是随便能假设的。JSON在和XML的竞争中胜利,基于JSON的REST服务框架在和基于XML的SOAP的竞争中胜利,不是偶然的。因为JSON和REST更符合人的认知的需要,生成他们的成本低,理解他们的成本低,工程师容易理解,最终就用起来了。所以现在XML和SOAP虽然是“国际标准”,但在Web上用的人很少,JSON和REST这些“野路子”一统天下。这和属性图数据库超越RDF数据库是一个道理。
在Python中使用JSON超级简单,JSON和Python的字典很像,可以转换。看官方文档即可
掌握下面这些库会让你处理json和字典的时候更开心
- attrdict https://github.com/bcj/AttrDict a['foo']['bar']可以写做a.foo.bar 或a['foo'].bar。可读/写属性,可递归访问属性,继承dict的各种方法
- marisa-trie https://github.com/kmike/marisa-trie 超级节约内存的字典
- DAWG http://dawg.readthedocs.org/en/latest/ 另一个超级节约内存的字典
- orderedmultidict https://github.com/gruns/orderedmultidict 多值有序字典
- jsonpickle http://jsonpickle.github.io/ JSON持久化。支持更复杂数据的存储
- jq http://stedolan.github.io/jq/tutorial/ 命令行上的json处理和查询
- pjson https://github.com/igorgue/pjson 在命令行上彩色打印json
- jsonlint https://github.com/zaach/jsonlint 格式化json
- jsawk https://github.com/micha/jsawk json的awk,一个快速的命令上的查询工具
- json-diff https://www.npmjs.org/package/json-diff 比较两个json
以上都是良心推荐,经多年工程实战考验的趁手工具。掌握了这些即使学不好知识图谱,也可以成为不错的数据科学家 :D
还有一些高级的话题,json pointer, json schema, xml2json, csv2json,暂时不提。我们只需要知道,json的工具链极为丰富。很多时候我们处理数据,就是卡在这些“小”工具上。你要是用了RDF,就会在无数小地方上因为缺少这些小工具而痛苦。
最后要隆重推荐一下YAML:JSON的超集,有更简洁的语法 http://yaml.org/
警告 Yaml可能有严重的世界观副作用,过敏者请谨慎使用。
YAML在我看来比JSON的可读性更好,更加Pythonic(因为其语法接近Python)。当然有人可能会不喜欢缩进,不过Python社区的智力一般比较高,不会有这种偏见。YAML里可以有节点之间的链接,因此可以表示图。此外yaml里可以写!注!释!我认为YAML是天然的最好的知识图谱表示语法。
PyYAML是Python里的Yaml处理库 http://pyyaml.org/wiki/PyYAML
不过Yaml解析的速度比json慢得多,大概只有1/10。但是我们要牢记,知识表示最重要的是对人的友好,不是对机器的友好。速度不是大的问题,大部分的知识库都不是特别大。
最后多说一句无关的话,很多语言都有可读性更好的类Python语法。下面是我收集的一个列表
yaml -- readable json
coffeescript -- readable javascript
RapydScript -- readable javascript
groovy -- readable java
markdown -- readable html
RapydML -- readable html
jade -- readable html template
SASS,Stylus -- readable css
Turtle -- readable RDF
有一本经典的编程书《The Art of Readable Code》 https://book.douban.com/subject/5442971/ 。我觉得同样的在知识表示里,我们应该追求“易读知识的艺术”。工程上,这是特别重要的一件事。
虽然在大部分的应用场景下我都不会推荐大家使用RDF和OWL,但了解一下它们还是很有必要的,当是打免疫针。当然这两个语言非常的复杂,官方文档打印出来有1000页厚,展开讲的话一个学期也讲不完。好在我们是工程师,只关心如何应用,不需要全面了解。
但基础的RDF是非常非常简单的,一页纸就能说清楚。RDF的基本单元是三元组(triple)。每个三元组是(主语 谓语 宾语)这样的元组 tuple。主谓宾的取值称为“资源”(Resource,也就是RDF里的R)。资源可以是一个网址(URI),一个字符串或数字(严格来讲都是带类型的字符串,称为literal),或者一个“空节点”(blank node)。主谓宾有一些限制,这里不细说,看后面提到的文档。
有两种特殊类型的资源。rdfs:Class代表类。rdf:Property代表二元关系。有一种特殊的关系叫rdf:type ,声明一个资源属于某一个类。
用RDF建模,就要把所有的数据结构分割为三元组。这对我们智人是很麻烦的事情,因为我们的认知里还会有定语、状语、补语,所以RDF提供了一些很麻烦的变通方法,例如reification。空节点也是一种方法。这些在实践中都会带来无穷无尽的烦恼。
一个三元组就是一个关系。在RDF里我们可以声明一些规则,从一些关系推导出另一些关系。这些规则我们称为“schema”,所以有了RDFS(RDF Schema)。这些规则用一些词汇(可以类比编程语言里的保留字,不过RDF里任何词汇都可以被重定义和扩展)表示,如subClassOf subPropertyOf domain range。
RDF里的推理规则有十几条,其中最常用的大概就是父类子类关系(subClassOf)。有了它就可以表示分类树,这种最常见的知识组织。后来在一些领域大家需要其他的一些推理规则,就又添加了几十条规则,例如要表达女儿都是女生、哥哥的哥哥还是哥哥、爸爸的爸爸是爷爷、每人只有一个亲爸爸,等等。这些规则被称为OWL,其中O代表Ontology(本体)。我们不必关心本体的哲学定义,只要知道它是一些数据和推理规则的集合就好了。
RDF和OWL都有严格的语义。一种叫模型论语义,是一种非常可怕的东西!它是一个高阶的语义,充斥着难懂的话,什么“映射”、“外延”、“解释”、“蕴涵” 之类。模型论语义是这样的使人快活,可是没有它,别人也便这么过。因为还有基于规则的语义;这是一种不完备的语义,因为有些推理可能不能100%得到模型论要求的结果。不过对于应用,这种不完备性基本无所谓。
- RDF和OWL语义 http://blog.memect.cn/?p=871 我的两个ppt,讲解了RDF和OWL的模型论语义
RDF和OWL的语法和基本使用,可以看官方的文档,还不算太难懂(英文)
- RDF 1.1 Primer https://www.w3.org/TR/rdf11-primer/
- OWL 2 Primer http://www.w3.org/TR/owl2-primer/
各种语法里,优先推荐用 Turtle 语法,因为它简洁....得不像RDF http://www.w3.org/TR/turtle/
Python里的rdflib包可以很方便处理RDF。推荐按rdflib的文档过一遍例子,加深对RDF的理解 http://rdflib.readthedocs.io/en/stable/
这个2011年的综述,提到了各种RDF相关的Python包:RdfLib RdfAlchemy Fuxi ORDF Django-RDF Djubby Redland SuRF PySparql Sparta Oort Virtuoso pySesame pynappl HTTP4Store py4s
- Survey of Pythonic tools for RDF and Linked Data programming http://www.michelepasin.org/blog/2011/02/24/survey-of-pythonic-tools-for-rdf-and-linked-data-programming/
本文没包括的还有:rdfQuery PySWIP pyDatalog PyLog FLiP seth sparrow pymantic pyRDFa djubby pySPARQL。感兴趣的可以查查。我个人很喜欢pyDatalog,虽然不是RDF的推理机,但大部分RDF的可以完成的建模用pyDatalog也都能做,我觉得更自然些。
参考手册
- 语义网速查表 http://ebiquity.umbc.edu/resource/html/id/94/ (丁力写的)
- OWL语法速查表 https://www.w3.org/TR/2012/REC-owl2-quick-reference-20121211/ (我写的)
JSON-LD是RDF的JSON语法,其中LD代表Linked Data。它要解决的是RDF没有好的Web兼容语法问题。经典的RDF语法是XML的,不仅罗嗦和丑陋,也集成了XML“重”的一些特征,适合“企业级”(如今这个词差不多就是恐龙、笨拙、难用的代名词)应用。JSON-LD就是想提供一个和互联网事实标准更兼容的、“轻”的语法。
JSON-LD基本思想是(我个人的理解)
- 尽可能用对人友好的字符串来写作,而不是象在传统RDF里用难以理解的URI。为了解释字符串,就引入了@context,把字符串定义在一定的上下文下——这些上下文本身一般是URI。这是比XML domain更友好的设计,增强了可读性。
- 引入模块化组织,加强可读性和可维护性。传统的RDF的组织粒度太低,在三元组层面。JSON-LD把同一个主语的三元组组织在一个 { } 块下,方便写作和理解。块的主语可以用@id 属性声明。更高层面上它还提供了@graph 声明,你可以把它理解成一个子模块,模块里的内容可以共享一些元数据(比如上下文和注释)。
JSON的官方文档:
JSON-LD本身现在还没有普及起来。wikidata在用,谷歌的knowlege graph也在用,但大多数人还不知道。我个人认为这是个好东西,虽然难以预料未来能不能火起来。
JSON-LD体现了两个对人友好的特性:可读性和模块化。第一代的Web知识语言如RDF和OWL,可以类比为知识的“汇编语言”,对机器很友好,对人不友好。Turtle和JSON-LD这类第二代语言,开始“高级化”,注意了方便人来写作和阅读,注意了引入适应人的认知需要的模块。
模块化机制引入RDF,前后花了十多年的时间。我自己从2004-2010也参与了一些工作。可以说,中间大家都犯了很多错误,走到今天的知识图谱很不容易。今后大家肯定还会继续犯错误。但是如果大家能多想想人的需要,而不仅是机器的需要,可能会少犯些错误吧。
最后再展开说说我对Web上知识表示的展望,基本基于我一篇老博文《语义网的高级语言》(2012-11-27)
在谈论语义网的时候,要和RDF路线区分开来。
和一些人谈到语义网,他们说:“语义网死了”。如果从RDF的角度来说,是的——虽然W3C路线的支持者还不承认。
但是这种观点,就如同计算机在只有机器语言,没有高级语言的时候就断言:“计算机死了”。
我大胆提出两个假设
- RDF是一门低级语言,只适合机器使用——如同机器语言或者汇编语言
- 语义网需要一门高级语言,面向工程师(人),用来做大规模知识库的写作、重用
为什么说RDF是低级机器语言?
- 用URL来寻址并不错。但是把精确寻址的任务交给人,要求人来设计URL,就如同在C编程中要求人对每个变量赋予内存地址。 RDF是一个“平坦”(flat)的语言,缺少内部的组织单元。有很多建议,引入诸如package, named graph这样的组织单元,但目前还没有达成共识或广泛采用。
- RDF的语法,即使是Turtle,也没有可读性,理解和重用起来非常困难。
- RDF缺少“宏”或者构造高层次组织的能力。其实SPARQL弥补了一点,就是graph pattern;一些语言如SPIN,把graph pattern作为可重用的单元,甚至可以生成新的数据。如果把这个能力作为RDF原生的能力就好了。
2010年RDF Working Group开预备会议,我也与会了( https://www.w3.org/2009/12/rdf-ws/papers/ws33 )。现在回来看,我那时的想法是错误的:为RDF引入更精确的语义,基于上下文(context)的组织和寻址,并不合适——虽然Pat Hayes后来很喜欢这个想法并在工作组内推一个类似的想法。
RDF的问题不是逻辑太少了,而是逻辑太多了。
知识工程的问题往往是太多考虑机器的需要,而不太考虑人的需要。而知识工程的瓶颈,又恰恰在人而不在机器。
三元组的问题在于模型的进化能力有限。想为一句话再加个时间戳?想表示Provenance?在RDF制定的早期,就提出了reification作为弥补。但是后来所有人都讨厌这个丑陋的补丁。后来陆续有四元组、五元组、六元组、Context、Named Graph等等各种其他的补丁。越搞越复杂,越来越没人懂。所以我认为,为知识表现专门开发一门语言没必要。特别是在RDF里Literals是二等公民(比如subject不允许是字符串!!),和它们真实的地位不相称。直接利用现有高级语言,如Python或Javascript,某个子集就好,不需要搞三元组。
RDF 1.1现在的几个努力方向:JSON语法,Named Graph, Turtle Syntax,这些都是好的。但是还不够。我甚至怀疑,在RDF框架内能不能达到易用性的目的。
因为从一开始,RDF就被设计成machine understandable语言。这本是好的,至少在1999年。但是一个缺少高级语言的情况,就好像编程语言的早期。结果就是知识工程的人月神话。
现在的情况也很象Web发明的时候:在Internet上,TCP/IP是面向机器的低级语言,而HTML和URL是面向人的高级语言。我觉得,现在有一个强烈的需要来设计一个Semantic Web的高级语言。
这样的高级语言要有什么特征呢?我觉得大体有这样几点
- 支持多粒度的知识/数据组织和重用
- 用字符串而不是URL来寻址。不追求addressing uniqueness, 而是probable and eventual addressing uniqueness
- 支持知识的分布式传输(按一定粒度)
- 使用目前主流程序员熟悉的语法形式。
- 尽可能少重新发明轮子——比如rdf:plainLiteral(我是作者之一)这样的字符串类型就没什么必要
- 支持结构化和非结构化数据的混合表达(RDF有Literal,不过,那个太局限了)
- 这个语言的文档不要提什么“语义”(有几个程序员关心SQL的语义?),不要规定什么schema
- 把推理转化为图的操作或者编程语言内置的运算。在这之外的推理都先不考虑。
- 从一开始就设计成在cluster上能运行的语言
- 拜托,用程序员看的懂的语言和例子写文档。
其实这样的语言雏形的一些部分,在不同的技术平台上都已经自发出现了。语义维基,图数据库,新一代检索引擎,都包含了上述部分概念。有心人要做的,就是一个有机的组合。我想,在我写这一段的时候,大概已经有人开始做了。
我甚至觉得,都没有必要引入一个新的高级语言语法,就在现有的某种贴近RDF的编程语言里,做少量的增加就能实现目的。最理想的就是Python。为什么这么说?JSON本身就是Python的数据结构。而几乎所有的数据API都吃JSON。Python的类与属性定义与关系就是RDF的翻版。
其实更合适的是Lisp。但是Lisp对抽象思维要求太高,社区又太小。做面向Web的开发,为了工程经济性(人力上的),还是Python比较合适。
自己设计电子邮件的结构化表示。
电子邮件里有几类可以利用的结构化信息
-
邮件本身的元数据,也就是MIME头 https://en.wikipedia.org/wiki/MIME 如发信人、收信、发信时间等。可以利用python的mime或flanker包来解析
-
邮件编码层的结构关系,如附件、部分(part),也可以在MIME层分解
-
单个邮件内部的语法层关系,如段落、引用、签名档
-
邮件之间的关系,如回复。MIME头里的In-Reply-To属性记录了这种关系
-
邮件内部的实体,如签名档里提到的人名字和电话号码
-
邮件内部实体和段落之间的语义关系,如某个段落回复某个段落
试分析上次分享的Mbox格式的源数据,选择一种自己顺手的数据格式,把电子邮件做结构化表示。没有标准答案,按自己的理解,选择自己认为合适的知识组织粒度、投入的成本。只要能自园其说。
如何提交作业:fork kg-beijing 仓库,在class1/week2/homework目录下建一个自己id的子目录,把自己的代码、运行结果放在里面,提交pull request
#KG小组北京一期成员github账号:
姓名 账号
- 胡杨 superhy
- 徐卓夫 ipush
- 侯月源 moonscar
- 田昌海 Jamestch
- 高晓燕 elisagao
- 侯立莎 yimiwawa
- 耿新鹏 xpgeng
- 梁方舟 pklfz
- 郑胤 Lan09 (TBD)
- 王鸿霄 wang101
- 李靖 L0113408
- 方东昊 Spirit-Dongdong
- 丁海星 godlikedog
- 付 鹏 pengfoo
- 张梦迪 mandyzore
- 佟海奇 tongtongqi
- 郭兴雨 buptguo
- 张志瑛 minenki
- 曹志远 smartczy
- 周祥 ucaszx
- 杨凯文 gentlekevin
- 王震 newle
- 鲍捷 baojie