编译、使用工具包训练模型、在程序中调用语言技术平台
LTP使用编译工具CMake构建项目。在安装LTP之前,你需要首先安装CMake。CMake的网站在这里。如果你是Windows用户,请下载CMake的二进制安装包;如果你是Linux,Mac OS或Cygwin的用户,可以通过编译源码的方式安装CMake,当然,你也可以使用Linux的软件源来安装。
第一步:构建VC Project
在项目文件夹下新建一个名为build的文件夹,使用CMake Gui,在source code中填入项目文件夹,在binaries中填入build文件夹。然后Configure -> Generate。
或者在命令行build 路径下运行
cmake ..
第二步:编译
构建后得到ALL_BUILD、RUN_TESTS、ZERO_CHECK三个VC Project。使用VS打开ALL_BUILD项目,选择Release方式构建项目。
注:由于boost::multi_array与VS2010不兼容,并不能使用Debug方式构建项目。
Linux、Mac OSX和Cygwin的用户,可以直接在项目根目录下使用命令
./configure
./make
进行编译。
编译成功后,会在./bin文件夹下生成如下一些二进制程序:
程序名 | 说明 |
---|---|
ltp_test | LTP调用程序 |
ltp_server* | LTP Server程序 |
在lib文件夹下生成以下一些静态链接库
程序名 | 说明 |
---|---|
splitsnt.lib | 分句lib库 |
segmentor.lib | 分词lib库 |
postagger.lib | 词性标注lib库 |
parser.lib | 依存句法分析lib库 |
ner.lib | 命名实体识别lib库 |
srl.lib | 语义角色标注lib库 |
同时,在tools/train文件夹下会产生如下一些二进制程序:
程序名 | 说明 |
---|---|
otcws | 分词的训练和测试套件 |
otpos | 词性标注的训练和测试套件 |
lgdpj | 依存句法分析训练和测试套件 |
maxent* | 最大熵工具包,用于训练命名实体识别和语义角色标注模型 |
SRLExtract* | 语义角色标注训练程序 |
SRLGetInstance* |
在window版本中ltp_server、Maxent、SRLExtract、SRLGetInstance并不被编译。
一般来讲,基于统计机器学习方法构建的自然语言处理工具通常包括两部分,即:算法逻辑以及模型。模型从数据中学习而得,通常保存在文件中以持久化;而算法逻辑则与程序对应。
ltp提供一整套算法逻辑以及模型,其中的模型包括:
模型名 | 说明 |
---|---|
cws.model | 分词模型,单文件 |
pos.model | 词性标注模型,单文件 |
ner.model | 命名实体识别模型,单文件 |
parser.model | 依存句法分析模型,单文件 |
srl_data/ | 语义角色标注模型,多文件 |
ltp_test是一个整合ltp中各模块的命令行工具。他完成加载模型,依照指定方法执行分析的功能。ltp_test加载的模型通过配置文件指定。配置文件的样例如下:
segmentor-model = new_ltp_data/cws.model
postagger-model = new_ltp_data/pos.model
parser-model = new_ltp_data/parser.model
ner-model = ltp_data/ner.model
srl-data = ltp_data/srl_data
其中,
ltp_test的使用方法如下:
./bin/ltp_test [配置文件] [分析目标] [待分析文件]
分析结果以xml格式显示在stdout中。关于xml如何表示分析结果,请参考理解Web Service Client结果一节。
otcws是ltp分词模型的训练套件,用户可以使用otcws训练获得ltp的分词模型。
编译之后,在tools/train下面会产生名为otcws的二进制程序。调用方法是
./otcws [config_file]
otcws分别支持从人工切分数据中训练分词模型和调用分词模型对句子进行切分。人工切分的句子的样例如下:
对外 , 他们 代表 国家 。
otcws主要通过配置文件指定执行的工作,其中主要有两类配置文件:训练配置和测试配置。
训练配置的配置文件样例如下所示。
[train]
train-file = data/ctb5-train.seg
holdout-file = data/ctb5-holdout.seg
algorithm = pa
model-name = model/ctb5-seg
max-iter = 5
其中,
测试配置的配置文件样例如下所示。
[test]
test-file = data/ctb5-test.seg
model-file = model/ctb5-seg.4.model
其中,
切分结果将输入到标准io中。
[train]与[test]两个配置组不能同时存在。对于otpos, otner, lgdpj三个工具包同样。
otpos是ltp分词模型的训练套件,用户可以使用otpos训练获得ltp的分词模型。
编译之后,在tools/train下面会产生名为otpos的二进制程序。调用方法是
./otpos [config_file]
otpos分别支持从人工切分并标注词性的数据中训练词性标注模型和调用词性标注模型对切分好的句子进行词性标注。人工标注的词性标注句子样例如下:
对外_v ,_wp 他们_r 代表_v 国家_n 。_wp
otpos主要通过配置文件指定执行的工作,其中主要有两类配置文件:训练配置和测试配置。
训练配置的配置文件样例如下所示。
[train]
train-file = data/ctb5-train.pos
holdout-file = data/ctb5-holdout.pos
algorithm = pa
model-name = model/ctb5-pos
max-iter = 5
其中,
测试配置的配置文件样例如下所示。
[test]
test-file = data/ctb5-test.pos
model-file = model/ctb5-pos.3.model
其中,
词性标注结果将输入到标准io中。
otner是ltp命名实体识别模型的训练套件,用户可以使用otner训练获得ltp的命名实体识别模型。
编译之后,在tools/train下面会产生名为otner的二进制程序。调用方法是
./otner [config_file]
otner分别支持从人工标注的数据中训练命名实体识别模型和调用命名实体识别模型对句子进行标注。人工标注的句子的样例如下:
党中央/ni#B-Ni 国务院/ni#E-Ni 要求/v#O ,/wp#O 动员/v#O 全党/n#O 和/c#O 全/a#O社会/n#O 的/u#O 力量/n#O
Otner主要通过配置文件指定执行的工作,其中主要有两类配置文件:训练配置和测试配置。
训练配置的配置文件样例如下所示。
[train]
train-file = data/ctb5-train.ner
holdout-file = data/ctb5-holdout.ner
algorithm = pa
model-name = model/ctb5-ner
max-iter = 5
其中,
测试配置的配置文件样例如下所示。
[test]
test-file = data/ctb5-test.ner
model-file = model/ctb5-ner.4.model
其中,
命名实体识别结果将输入到标准io中。
lgdpj是ltp依存句法分析模型的训练套件,用户可以使用lgdpj训练获得ltp的依存句法分析模型。
编译之后,在tools/train下面会产生名为lgdpj的二进制程序。调用方法是
./lgdpj [config_file]。
lgdpj分别支持从人工标注依存句法的数据中训练依存句法分析模型和调用依存句法分析模型对句子进行依存句法分析。人工标注的词性标注依存句法的句子遵从conll格式,其样例如下:
1 对外 _ v _ _ 4 ADV _ _
2 , _ wp _ _ 1 WP _ _
3 他们 _ r _ _ 4 SBV _ _
4 代表 _ v _ _ 0 HED _ _
5 国家 _ n _ _ 4 VOB _ _
6 。 _ wp _ _ 4 WP _ _
lgdpj主要通过配置文件指定执行的工作,其中主要有两类配置文件:训练配置和测试配置。
训练配置的配置文件样例如下所示。
[model]
labeled = 1
decoder-name = 2o-carreras
[feature]
use-postag-unigram = 0
use-dependency = 1
use-dependency-unigram = 1
use-dependency-bigram = 1
use-dependency-surrounding = 1
use-dependency-between = 1
use-sibling = 1
use-sibling-basic = 1
use-sibling-linear = 1
use-grand = 1
use-grand-basic = 1
use-grand-linear = 1
[train]
train-file = data/conll/ldc-train.conll
holdout-file = data/conll/ldc-holdout.conll
max-iter = 5
algorithm = pa
model-name = model/parser/ldc-o2carreras
其中,
测试配置的配置文件样例如下所示。
[test]
test-file = data/conll/ldc-test.conll
model-file = model/parser/ldc-o2carreras.2.model
其中,
依存句法分析结果将输入到标准io中。
分词主要提供三个接口:
void * segmentor_create_segmentor
参数名 | 参数描述 |
---|---|
const char * path | 指定模型文件的路径 |
const char * lexicon_path | 指定外部词典路径。如果lexicon_path为NULL,则不加载外部词典 |
int segmentor_release_segmentor
参数名 | 参数描述 |
---|---|
void * segmentor | 待销毁分词器的指针 |
int segmentor_segment
参数名 | 参数描述 |
---|---|
void * segmentor | 分词器的指针 |
const std::string & line | 待分词句子 |
std::vector<std::string> & words | 结果分词序列 |
一个简单的实例程序可以说明分词接口的用法:
#include <iostream>
#include <string>
#include "segment_dll.h"
int main(int argc, char * argv[]) {
if (argc < 2) {
std::cerr << "cws [model path]" << std::endl;
return 1;
}
void * engine = segmentor_create_segmentor(argv[1]);
if (!engine) {
return -1;
}
std::vector<std::string> words;
int len = segmentor_segment(engine,
"爱上一匹野马,可我的家里没有草原。", words);
for (int i = 0; i < len; ++ i) {
std::cout << words[i] << "|";
}
std::cout << std::endl;
segmentor_release_segmentor(engine);
return 0;
}
实例程序通过命令行参数指定模型文件路径。第11行加载模型文件,并将分词器指针存储在engine中。第16行运行分词逻辑,并将结果存储在名为words的std::vector<std::string>中。第22行释放分词模型。
调用分词接口的程序在编译的时,需要链接segmentor.a(MSVC下需链接segmentor.lib)。
void * postagger_create_postagger
参数名 | 参数描述 |
---|---|
const char * path | 词性标注模型路径 |
int postagger_release_postagger
参数名 | 参数描述 |
---|---|
void * postagger | 待销毁的词性标注器的指针 |
int postagger_postag
参数名 | 参数描述 |
---|---|
void * postagger | 词性标注器的指针 |
const std::vector< std::string > & words | 待标注的词序列 |
std::vector< std::string > & tags | 词性标注结果,序列中的第i个元素是第i个词的词性 |
一个简单的实例程序可以说明词性标注接口的用法:
#include <iostream>
#include <vector>
#include "postag_dll.h"
int main(int argc, char * argv[]) {
if (argc < 1) {
return -1;
}
void * engine = postagger_create_postagger(argv[1]);
if (!engine) {
return -1;
}
std::vector words;
words.push_back("我");
words.push_back("是");
words.push_back("中国人");
std::vector tags;
postagger_postag(engine, words, tags);
for (int i = 0; i < tags.size(); ++ i) {
std::cout << words[i] << "/" << tags[i];
if (i == tags.size() - 1) std::cout << std::endl;
else std::cout << " ";
}
postagger_release_postagger(engine);
return 0;
}
实例程序通过命令行参数指定模型文件路径。第11行加载模型文件,并将词性标注器指针存储在engine中。第18至20行构造分词序列,第24行运行词性标注逻辑,并将结果存储在名为tags的std::vector<std::string>中。第33行释放分词模型。
调用词性标注接口的程序在编译的时,需要链接postagger.a(MSVC下需链接postagger.lib)。
命名实体识别主要提供三个接口
void * ner_create_recognizer
参数名 | 参数描述 |
---|---|
const char * path | 命名实体识别模型路径 |
int ner_release_recognizer
参数名 | 参数描述 |
---|---|
void * recognizer | 待销毁的命名实体识别器的指针 |
int ner_recognize
参数名 | 参数描述 |
---|---|
void * recognizer | 命名实体识别器的指针 |
const std::vector< std::string > & words | 待识别的词序列 |
const std::vector< std::string > & postags | 待识别的词的词性序列 |
std::vector<std::string> & tags | 命名实体识别结果,命名实体识别的结果为O时表示这个词不是命名实体,否则为{POS}-{TYPE}形式的标记,POS代表这个词在命名实体中的位置,TYPE表示命名实体类型 |
#include <iostream>
#include <vector>
#include "ner_dll.h"
int main(int argc, char * argv[]) {
if (argc < 2) {
std::cerr << "usage: ./ner [model_path]" << std::endl;
return -1;
}
void * engine = ner_create_recognizer(argv[1]);
if (!engine) {
std::cerr << "failed to load model" << std::endl;
return -1;
}
std::vector<std::string> words;
std::vector<std::string> postags;
words.push_back("中国"); postags.push_back("ns");
words.push_back("国际"); postags.push_back("n");
words.push_back("广播"); postags.push_back("n");
words.push_back("电台"); postags.push_back("n");
words.push_back("创办"); postags.push_back("v");
words.push_back("于"); postags.push_back("p");
words.push_back("1941年"); postags.push_back("m");
words.push_back("12月"); postags.push_back("m");
words.push_back("3日"); postags.push_back("m");
words.push_back("。"); postags.push_back("wp");
std::vector<std::string> tags;
ner_recognize(engine, words, postags, tags);
for (int i = 0; i < tags.size(); ++ i) {
std::cout << words[i] << "\t" << postags[i] << "\t" << tags[i] << std::endl;
}
ner_release_recognizer(engine);
return 0;
}
示例程序通过命令行参数指定模型文件路径。第11行加载模型文件,并将命名实体识别器指针存储在engine中。第21至30行构造分词序列words和词性标注序列postags,第34行运行词性标注逻辑,并将结果存储在名为tags的std::vectorstd::string中。第40行释放分词模型。
调用命名实体识别接口的程序在编译的时,需要链接ner.a(MSVC下需链接ner.lib)。
依存句法分析主要提供三个接口:
void * parser_create_parser
参数名 | 参数描述 |
---|---|
const char * path | 依存句法分析模型路径 |
int parser_release_parser
参数名 | 参数描述 |
---|---|
void * parser | 待销毁的依存句法分析器的指针 |
int parser_parse
参数名 | 参数描述 |
---|---|
void * parser | 依存句法分析器的指针 |
const std::vector< std::string > & words | 待分析的词序列 |
const std::vector< std::string > & postags | 待分析的词的词性序列 |
std::vector & heads | 结果依存弧,heads[i]代表第i个词的父亲节点的编号 |
std::vector<std::string> & deprels | 结果依存弧关系类型 |
一个简单的实例程序可以说明依存句法分析接口的用法:
#include <iostream>
#include <vector>
#include "parser_dll.h"
int main(int argc, char * argv[]) {
if (argc < 2) {
return -1;
}
void * engine = parser_create_parser(argv[1]);
if (!engine) {
return -1;
}
std::vector words;
std::vector postags;
words.push_back("一把手"); postags.push_back("n");
words.push_back("亲自"); postags.push_back("d");
words.push_back("过问"); postags.push_back("v");
words.push_back("。"); postags.push_back("wp");
std::vector heads;
std::vector deprels;
parser_parse(engine, words, postags, heads, deprels);
for (int i = 0; i < heads.size(); ++ i) {
std::cout << words[i] << "\t" << postags[i] << "\t"
<< heads[i] << "\t" << deprels[i] << std::endl;
}
parser_release_parser(engine);
return 0;
}
示例程序通过命令行参数指定模型文件路径。第11行加载模型文件,并将依存句法分析器指针存储在engine中。第19至22行构造分词序列words和词性标注序列postags,第27行运行词性标注逻辑,并将依存弧关系存储在heads中,将依存弧关系类型存储在deprels中。第34行释放依存句法分析模型。
调用依存句法分析接口的程序在编译的时,需要链接parser.a(MSVC下需链接parser.lib)。