Alex He

...永远保持希望与激情...约会未来更强大的自己...

 

使用马尔可夫逻辑网(MLNs)进行分类实验

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

...

最后文件组织如下图:

Image(1)

分别给定训练集路径和测试集路劲,转换语料库为Alchemy格式的数据,数据的组织如下图:

Image(2)

转换格式的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

posted on 2012-11-19 08:39  Alex木头  阅读(1913)  评论(0编辑  收藏  举报

导航