1. 引子
一阶逻辑:
规则:如果A是人,那么A会死(人是会死的)
已知事实:柏拉图是人
推理事实:柏拉图会死
其思想就是 对于已知事实使用非常确定的规则进行推理,希望能发现未知的事实。
但是在现实世界中,很少有规则是没有意外的,比如:
好人有好报(才怪呢),郎才配女貌(也不一定),功到自然成(更是屁话),甚至连是人就会死都不一定(如果你相信电影《那个男人来自地球》所言其实的话)。
那末,我们如何对这类的知识进行建模呢?
马尔可父逻辑网提供了一种表示该知识的模型。它通过对规则引入权重来表示规则的确定性 ,如果权重非常大非常大(正无穷),则表示该规则非常之肯定,比如人是会死的;如果权重不是非常大,则表示该规则不一定很确定,比如吸烟会减少寿命;权重为零,则表示该规则有等于没有,比如读书会让人快乐(我乱扯的,嘿嘿);如果权重非常小非常小(负无穷),则表示该规则非常之肯定不成立,比如人是不会死的。 通过对已知事实结合规则构建一个马尔可夫逻辑网,并进行概率推理,计算出未知事实成真的概率。
大概思想就是这样。
2. 分类
这样的模型非常之适合知识推理,但是用于分类问题好像不是很明显。
这个时候我们需要对问题进行一些转换:
分类问题<==>指定对象类别<==>确定该对象是否属于该类别<==>是否能推导出该对象属于该类别
也就是说:
加入需要对文档doc进行分类,他可能属于C1,C2和C3,则我们需要通过已知的事实证据计算其能推导出C1(doc)、C2(doc)、C3(doc)的可能性分别有多大,最简单的一种方式就是选择概率最的类别为其分类类别。
3.实践
该例子为一个3类文本分类问题,使用MLN目前唯一的Alchemy作为实验平台:
依照Alchemy用户手册给出的语法和规则,定义各个文件如下:
1) three.mln
//predicate declarations
HasToken(doc, token)
Class0(doc)
Class1(doc)
Class2(doc)
//formulas
HasToken(d, +t) => Class0(d)
HasToken(d, +t) => Class1(d)
HasToken(d, +t) => Class2(d)
2) three-train.db
Class0(Doc37261)
Class0(Doc37913)
...
HasToken(Doc37261, ASheep)
HasToken(Doc37261, AVisualization)
HasToken(Doc37261, AXref)
...
3) three-test.db
HasToken(Doc39063, ASystems)
HasToken(Doc39063, AWet)
HasToken(Doc39063, ATIF)
...
权重学习命令: ../../bin/learnwts -d -i three.mln -o three-out.mln -t three-train.db -ne HasToken,Class0,Class1,Class2
学习到的规则及其权重如下:
//predicate declarations
HasToken(doc,token)
Class0(doc)
Class1(doc)
Class2(doc)
// 1.80043 HasToken(d,ASheep) => Class0(d)
1.80043 !HasToken(a1,ASheep) v Class0(a1)
// 2.96212 HasToken(d,AVisualization) => Class0(d)
2.96212 !HasToken(a1,AVisualization) v Class0(a1)
...
推理事实命令: ../../bin/infer -i three-out.mln -e three-test.db -r three.results -q Class0,Class1,Class2
推理结果为:
注:该次试验是失败的,效果也N不好,因为没有对特征进行任何处理,连词干化都没做
...
Class0(Doc61551) 4.9995e-05
Class0(Doc61552) 4.9995e-05
...
最后文件组织如下图:
分别给定训练集路径和测试集路劲,转换语料库为Alchemy格式的数据,数据的组织如下图:
转换格式的python脚本如下:
#encoding:utf-8
import os
import re
tp = re.compile(r'\W')
mlnfile = "three.mln"
traindb = "three-train.db"
testdb = "three-test.db"
validb = "three-validate.db"
#对指定文件路径生成token列表
def genetoken(filepath):
lines = open(filepath).readlines()
tokens = list()
line_tokens = [token for line in lines for token in tp.split(line) if len(token.strip())>0]
map(tokens.append, line_tokens)
tokens = map(upperToken, tokens)
return ["A"+token for token in list(set(tokens))]
#把token首字符变大写
def upperToken(token):
token = token[0].upper()+token[1:] if len(token) > 1 else token.upper()
return token
def getTitle(title):
return "Doc"+title
if __name__ == '__main__':
train_path = 'train'#train set path
test_path = 'test' #test set path
classIndex = dict()#类别索引
for index, subfile in enumerate(os.listdir(train_path)):
className = "Class" + str(index)
classIndex[subfile] = "Class" + str(index)
#生成训练集
for subdir in os.listdir(train_path):
className = classIndex[subdir]#类别名
subdir = os.path.join(train_path, subdir)#子目录,即类别目录
titledb = [className + "(" + getTitle(title) + ")" for title in os.listdir(subdir)]
#如Class1(Doc1111), Class2(Doc2222)
open(traindb,'a').write('\n'.join(titledb) + '\n\n')
for subdir in os.listdir(train_path):
className = classIndex[subdir]#类别名
subdir = os.path.join(train_path, subdir)#子目录,即类别目录
for docfile in os.listdir(subdir):
titleName = getTitle(docfile)
tokens = genetoken(os.path.join(subdir, docfile))
tokendb = ["HasToken(" + titleName + ", " + token + ")" for token in tokens]
open(traindb,'a').write('\n'.join(tokendb)+ '\n\n')
#生成验证集
for subdir in os.listdir(test_path):
className = classIndex[subdir]#类别名
subdir = os.path.join(test_path, subdir)
titledb = [className + "(" + getTitle(title) + ")" for title in os.listdir(subdir)]#如Class1(Doc1111), Class2(Doc2222)
open(validb,'a').write('\n'.join(titledb) + '\n\n')
for docfile in os.listdir(subdir):
titleName = getTitle(docfile)
tokens = genetoken(os.path.join(subdir, docfile))
tokendb = ["HasToken(" + titleName + ", " + token + ")" for token in tokens]
open(testdb,'a').write('\n'.join(tokendb) + '\n\n')
4. 尾巴
我必须坦白,如果数据量大了该程序根本没发工作,3*1000个文档集更不跑不动,最后只有减少数据量实验了,并且我现在开始考虑:
0) 该模型做文本分类是不适合滴,开始的大数据根本没法跑,减少到mini型数据还是跑了N个小时:Done learning discriminative weights. Time Taken for learning = 3 hrs, 6.43783 mins Total time = 3 hrs, 10.7546 mins
1) 该模型对实际应用的可能性,特别是对网络数据的知识抽取和知识发现;
2) 还有很多优化值得做,很可能该系统已经做了,只不过我不知道如何使用;
3) 还有很多工作可以做,开发很快更有效的推理学习算法,扩展模型;
对于该分类任务呢,还有不少工作没有做,比如确定最后类别,评价分类效果等等。
没办法,老板又派新活了,这个MLN只有先放到一边。那边书都没看完就乱扯一通确实非常不合适,但是什么都不写吧,又怕这么久的东西事是白看了。
5. 参考
Pedro Domingos, etc. Markov Logic : An Interface Layer for Artificial Intelligence. 2009.
徐从富等. 马尔可父逻辑网研究. 软件学报. 2011.
Alchemy用户手册. http://alchemy.cs.washington.edu/user-manual/manual.html