To Top
首页 > 自然语言处理 > 正文

crf++用法

标签:nlp, natural language processing, crfpp, crf++


目录

github:https://github.com/taku910/crfpp

下载链接:google drive

主页:https://taku910.github.io/crfpp/

参考博客

安装

开发机:/home/users/daiwenkai/crfpp/brand 从下载链接google drive下载CRF++-0.58.tar.gz,解压

./configure 
make
make install

训练

./crf_learn 
CRF++: Yet Another CRF Tool Kit
Copyright (C) 2005-2013 Taku Kudo, All rights reserved.

Usage: /home/users/daiwenkai/crfpp/CRF++-0.58/.libs/lt-crf_learn [options] files
 -f, --freq=INT              use features that occuer no less than INT(default 1)
 -m, --maxiter=INT           set INT for max iterations in LBFGS routine(default 10k)
 -c, --cost=FLOAT            set FLOAT for cost parameter(default 1.0)
 -e, --eta=FLOAT             set FLOAT for termination criterion(default 0.0001)
 -C, --convert               convert text model to binary model
 -t, --textmodel             build also text model file for debugging
 -a, --algorithm=(CRF|MIRA)  select training algorithm
 -p, --thread=INT            number of threads (default auto-detect)
 -H, --shrinking-size=INT    set INT for number of iterations variable needs to  be optimal before considered for shrinking. (default 20)
 -v, --version               show the version and exit
 -h, --help                  show this help and exit

训练数据格式:

毎 k   B
日 k   I
新 k   I
聞 k   I
社 k   I
特 k   B
別 k   I
顧 k   B
問 k   I                                                 
4 n   B

这里第一列是待分词的日文字,第二列暂且认为其是词性标记,第三列是字标注中的2-tag(B, I)标记,这个很重要,对于我们需要准备的训练集,主要是把这一列的标记做好,不过需要注意的是,其断句是靠空行来完成的。

特征模板

# Unigram
U00:%x[-2,0]
U01:%x[-1,0]
U02:%x[0,0]
U03:%x[1,0]
U04:%x[2,0]
U05:%x[-2,0]/%x[-1,0]/%x[0,0]
U06:%x[-1,0]/%x[0,0]/%x[1,0]
U07:%x[0,0]/%x[1,0]/%x[2,0]
U08:%x[-1,0]/%x[0,0]                                          
U09:%x[0,0]/%x[1,0]

# Bigram
B

关于CRF++中特征模板的说明和举例,请大家参考官方文档上的“Preparing feature templates”这一节,而以下部分的说明拿上述日文分词数据举例。在特征模板文件中,每一行(如U00:%x[-2,0])代表一个特征,而宏“%x[行位置,列位置]”则代表了相对于当前指向的token的行偏移和列的绝对位置,以上述训练集为例,如果当前扫描到“新 k I”这一行,

毎 k   B
日 k   I
新 k   I   <== 扫描到这一行,代表当前位置
聞 k   I
社 k   I
特 k   B
別 k   I
顧 k   B
問 k   I
4 n   B

那么依据特征模板文件抽取的特征如下:

# Unigram
U00:%x[-2,0] ==> 毎
U01:%x[-1,0] ==> 日
U02:%x[0,0]  ==> 新
U03:%x[1,0]  ==> 聞
U04:%x[2,0]  ==> 社
U05:%x[-2,0]/%x[-1,0]/%x[0,0] ==> 每/日/新
U06:%x[-1,0]/%x[0,0]/%x[1,0]  ==> 日/新/聞
U07:%x[0,0]/%x[1,0]/%x[2,0]   ==> 新/聞/社
U08:%x[-1,0]/%x[0,0]          ==> 日/新
U09:%x[0,0]/%x[1,0]           ==> 新/聞

# Bigram
B

CRF++里将特征分成两种类型,一种是Unigram的,“U”起头,另外一种是Bigram的,“B”起头。对于Unigram的特征,假如一个特征模板是”U01:%x[-1,0]”, CRF++会自动的生成一组特征函数(func1 … funcN) 集合:

func1 = if (output = B and feature="U01:日") return 1 else return 0
func2 = if (output = I and feature="U01:日") return 1 else return 0
....
funcXX = if (output = B and feature="U01:問") return 1  else return 0
funcXY = if (output = I and feature="U01:問") return 1  else return 0

生成的特征函数的数目 = (L * N),其中L是输出的类型的个数,这里L=2,是B/I这两个tag,N是通过模板扩展出来的所有单个字符串(特征)的个数(即所有的x[-1,0]),这里指的是在扫描所有训练集的过程中找到的日文字(特征)。

而Bigram特征主要是当前的token和前面一个位置token的自动组合生成的bigram特征集合。最后需要注意的是U01和U02这些标志位,与特征token组合到一起主要是区分“U01:問”和“U02:問”这类特征,虽然抽取的日文”字”特征是一样的,但是在CRF++中这是有区别的特征。

测试

 ./crf_test --help
CRF++: Yet Another CRF Tool Kit
Copyright (C) 2005-2013 Taku Kudo, All rights reserved.

Usage: /home/users/daiwenkai/crfpp/CRF++-0.58/.libs/lt-crf_test [options] files
 -m, --model=FILE         set FILE for model file
 -n, --nbest=INT          output n-best results
 -v, --verbose=INT        set INT for verbose level
 -c, --cost-factor=FLOAT  set cost factor
 -o, --output=FILE        use FILE as output file
 -v, --version            show the version and exit
 -h, --help               show this help and exit

测试数据格式

よ h   I
っ h   I
て h   I
私 k   B
た h   B
ち h   I
の h   B                                                     
世 k   B
代 k   I
が h   B

同样也有3列,第一列是日文字,第二列第三列与上面是相似的,不过在测试集里第三列主要是占位作用。事实上,CRF++对于训练集和测试集文件格式的要求是比较灵活的,首先需要多列,但不能不一致,既在一个文件里有的行是两列,有的行是三列;其次第一列代表的是需要标注的“字或词”,最后一列是输出位”标记tag”,如果有额外的特征,例如词性什么的,可以加到中间列里,所以训练集或者测试集的文件最少要有两列。

测试结果

最后多了一列,就是预测的tag

よ h   I   B
っ h   I   I
て h   I   B
私 k   B   B
た h   B   B
ち h   I   I
の h   B   B                                      
世 k   B   B
代 k   I   I
が h   B   B

处理中文

我们来抄一下傲哥的代码brand_filter/brand_tagger.py

def _parse_string_to_char(word):
    """format a string to character list:
    a Chinese character or a ascii string will be treat as an item(char)
    in the list.
    Args:
        word, a gbk encoded string;
    Returns:
        a list of Chinese characters and ascii strings
    """
    char_list = list()
    last_char = ''
    idx = 0 
    while idx < len(word):
        # the value of the first byte of a Chinese character is >= 128
        if ord(word[idx]) >= 128:
            if len(last_char) > 0:
                char_list.append(last_char)
                last_char = ''
            char_list.append(word[idx: idx + 2]) 
            idx += 2
        elif word[idx] == ' ':
            if len(last_char) > 0:
                char_list.append(last_char)
            last_char = ''
            idx += 1
        else:
            last_char += word[idx]
            idx += 1
    if len(last_char) > 0:
        char_list.append(last_char)
    return char_list

python接口

首先,需要对setup.py进行修改:

#!/usr/bin/env python

from distutils.core import setup,Extension,os
import string

setup(name = "mecab-python",
      py_modules=["CRFPP"],
      ext_modules = [Extension("_CRFPP",
                               ["CRFPP_wrap.cxx",],
                               include_dirs = ["/home/users/daiwenkai/crfpp/CRF++-0.58/"], # 新增这行 
                               library_dirs = ["/home/users/daiwenkai/crfpp/CRF++-0.58/.libs/"], # 新增这行
                               libraries=["crfpp", "pthread"])
                     ])  

接下来就是安装过程了(可以参考神奇的gcc48

cd CRF++-0.58/python
python setup.py build
python setup.py install
#当然,这里有坑,如果使用gcc48编译的话,呵呵了,如果gcc34编译的,完美兼容
export LD_LIBRARY_PATH=~/crfpp/CRF++-0.58.gcc3.4.5/.libs/:$LD_LIBRARY_PATH 
python -c "import CRFPP"

然后我们看一下test.py

import CRFPP
import sys

try:
    # -v 3: access deep information like alpha,beta,prob
    # -nN: enable nbest output. N should be >= 2
    tagger = CRFPP.Tagger("-m ../model -v 3 -n2")

    # clear internal context
    tagger.clear()

    # add context
    tagger.add("Confidence NN")
    tagger.add("in IN")
    tagger.add("the DT")
    tagger.add("pound NN")
    tagger.add("is VBZ")
    tagger.add("widely RB")
    tagger.add("expected VBN")
    tagger.add("to TO")
    tagger.add("take VB")
    tagger.add("another DT")
    tagger.add("sharp JJ")
    tagger.add("dive NN")
    tagger.add("if IN")
    tagger.add("trade NN")
    tagger.add("figures NNS")
    tagger.add("for IN")
    tagger.add("September NNP")

    print "column size: " , tagger.xsize()
    print "token size: " , tagger.size()
    print "tag size: " , tagger.ysize()

    print "tagset information:"
    ysize = tagger.ysize()
    for i in range(0, ysize-1):
        print "tag " , i , " " , tagger.yname(i)

    # parse and change internal stated as 'parsed'
    tagger.parse()

    print "conditional prob=" , tagger.prob(), " log(Z)=" , tagger.Z()

    size = tagger.size()
    xsize = tagger.xsize()
    for i in range(0, (size - 1)):
       for j in range(0, (xsize-1)):
          print tagger.x(i, j) , "\t",
       print tagger.y2(i) , "\t",
       print "Details",
       for j in range(0, (ysize-1)):
          print "\t" , tagger.yname(j) , "/prob=" , tagger.prob(i,j),"/alpha=" , tagger.alpha(i, j),"/beta=" , tagger.beta(i, j),
       print "\n",

    print "nbest outputs:"
    for n in range(0, 9):
        if (not tagger.next()):
            continue
        print "nbest n=" , n , "\tconditional prob=" , tagger.prob()
        # you can access any information using tagger.y()...

    print "Done"

except RuntimeError, e:
    print "RuntimeError: ", e,

原创文章,转载请注明出处!
本文链接:http://daiwk.github.io/posts/nlp-crfpp.html
上篇: Deep Recurrent Models with Fast-Forward Connections for Neural Machine Translation(rnn_nmt_baidu)
下篇: 机器学习common

comment here..