目录
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
首先,需要对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,