LTP是哈工大开源的一套中文语言处理系统,涵盖了基本功能:分词、词性标注、命名实体识别、依存句法分析、语义角色标注、语义依存分析等。


【开源中文分词工具探析】系列:

  1. 开源中文分词工具探析(一):ICTCLAS (NLPIR)
  2. 开源中文分词工具探析(二):Jieba
  3. 开源中文分词工具探析(三):Ansj
  4. 开源中文分词工具探析(四):THULAC
  5. 开源中文分词工具探析(五):FNLP
  6. 开源中文分词工具探析(六):Stanford CoreNLP
  7. 开源中文分词工具探析(七):LTP

1. 前言

同THULAC一样,LTP也是基于结构化感知器(Structured Perceptron, SP),以最大熵准则建模标注序列\(Y\)在输入序列\(X\)的情况下的score函数:

\[S(Y,X) = \sum_s \alpha_s \Phi_s(Y,X)
\]

其中,\(\Phi_s(Y,X)\)为本地特征函数。中文分词问题等价于给定\(X\)序列,求解score函数最大值对应的\(Y\)序列:

\[\mathop{\arg \max}_Y S(Y,X)
\]

2. 分解

以下源码分析基于版本3.4.0。

分词流程

分词流程与其他分词器别无二致,先提取字符特征,计算特征权重值,然后Viterbi解码。代码详见__ltp_dll_segmentor_wrapper::segment()

int segment(const char *str, std::vector<std::string> &words) {
ltp::framework::ViterbiFeatureContext ctx;
ltp::framework::ViterbiScoreMatrix scm;
ltp::framework::ViterbiDecoder decoder;
ltp::segmentor::Instance inst; int ret = preprocessor.preprocess(str, inst.raw_forms, inst.forms,
inst.chartypes); if (-1 == ret || 0 == ret) {
words.clear();
return 0;
} ltp::segmentor::SegmentationConstrain con;
con.regist(&(inst.chartypes));
build_lexicon_match_state(lexicons, &inst);
extract_features(inst, model, &ctx, false);
calculate_scores(inst, (*model), ctx, true, &scm); // allocate a new decoder so that the segmentor support multithreaded
// decoding. this modification was committed by niuox
decoder.decode(scm, con, inst.predict_tagsidx);
build_words(inst.raw_forms, inst.predict_tagsidx, words); return words.size();
}

训练模型

模型文件cws.model包含了类别、特征、权重、内部词典(internal lexicon)等。我用Java 重写了模型解析,代码如下:

DataInputStream is = new DataInputStream(new FileInputStream(path));
char[] octws = readCharArray(is, 128); // 1. read label
SmartMap label = readSmartMap(is);
int[] entries = readIntArray(is, label.numEntries); // 2. read feature Space
char[] space = readCharArray(is, 16);
int offset = readInt(is);
int sz = readInt(is);
SmartMap[] dicts = new SmartMap[sz];
for (int i = 0; i < sz; i++) {
dicts[i] = readSmartMap(is);
} // 3. read param
char[] param = readCharArray(is, 16);
int dim = readInt(is);
double[] w = readDoubleArray(is, dim);
double[] wSum = readDoubleArray(is, dim);
int lastTimestamp = readInt(is); // 4. read internal lexicon
SmartMap internalLexicon = readSmartMap(is); // read char array
private static char[] readCharArray(DataInputStream is, int length) throws IOException {
char[] chars = new char[length];
for (int i = 0; i < length; i++) {
chars[i] = (char) is.read();
}
return chars;
} // read int array
private static int[] readIntArray(DataInputStream is, int length) throws IOException {
byte[] bytes = new byte[4 * length];
is.read(bytes);
IntBuffer intBuffer = ByteBuffer.wrap(bytes)
.order(ByteOrder.LITTLE_ENDIAN)
.asIntBuffer();
int[] array = new int[length];
intBuffer.get(array);
return array;
}

LTP共用到了15类特征,故sz为15;特征是采用Map表示,LTP称之为SmartMap,看代码本质上是一个HashMap。分词工具测评结果表明,LTP分词速度较THULAC要慢。究其原因,THULAC采用双数组Trie来表示模型,特征检索速度要优于LTP。

特征

LTP所用到的特征大致可分为以下几类:

  • unigram字符特征 ch[-2], ch[-1], ch[0], ch[1], ch[2]
  • bigram字符特征 ch[-2]ch[-1], ch[-1]ch[0],ch[0]ch[1],ch[1]ch[2]
  • 字符类型特征 ct[-1], ct[0], ct[1]
  • 词典属性特征 ch[0]是否为词典开始字符、中间字符、结束字符

源码见extractor.cpp

Extractor::Extractor() {
// delimit feature templates
templates.push_back(new Template("1={c-2}"));
templates.push_back(new Template("2={c-1}"));
templates.push_back(new Template("3={c-0}"));
templates.push_back(new Template("4={c+1}"));
templates.push_back(new Template("5={c+2}"));
templates.push_back(new Template("6={c-2}-{c-1}"));
templates.push_back(new Template("7={c-1}-{c-0}"));
templates.push_back(new Template("8={c-0}-{c+1}"));
templates.push_back(new Template("9={c+1}-{c+2}"));
templates.push_back(new Template("14={ct-1}"));
templates.push_back(new Template("15={ct-0}"));
templates.push_back(new Template("16={ct+1}"));
templates.push_back(new Template("17={lex1}"));
templates.push_back(new Template("18={lex2}"));
templates.push_back(new Template("19={lex3}"));
} #define TYPE(x) (strutils::to_str(inst.chartypes[(x)]&0x07))
data.set("c-2", (idx - 2 < 0 ? BOS : inst.forms[idx - 2]));
data.set("c-1", (idx - 1 < 0 ? BOS : inst.forms[idx - 1]));
data.set("c-0", inst.forms[idx]);
data.set("c+1", (idx + 1 >= len ? EOS : inst.forms[idx + 1]));
data.set("c+2", (idx + 2 >= len ? EOS : inst.forms[idx + 2]));
data.set("ct-1", (idx - 1 < 0 ? BOT : TYPE(idx - 1)));
data.set("ct-0", TYPE(idx));
data.set("ct+1", (idx + 1 >= len ? EOT : TYPE(idx + 1)));
data.set("lex1", strutils::to_str(inst.lexicon_match_state[idx] & 0x0f));
data.set("lex2", strutils::to_str((inst.lexicon_match_state[idx] >> 4) & 0x0f));
data.set("lex3", strutils::to_str((inst.lexicon_match_state[idx] >> 8) & 0x0f));
#undef TYPE

开源中文分词工具探析(七):LTP的更多相关文章

  1. 开源中文分词工具探析(三):Ansj

    Ansj是由孙健(ansjsun)开源的一个中文分词器,为ICTLAS的Java版本,也采用了Bigram + HMM分词模型(可参考我之前写的文章):在Bigram分词的基础上,识别未登录词,以提高 ...

  2. 开源中文分词工具探析(四):THULAC

    THULAC是一款相当不错的中文分词工具,准确率高.分词速度蛮快的:并且在工程上做了很多优化,比如:用DAT存储训练特征(压缩训练模型),加入了标点符号的特征(提高分词准确率)等. 1. 前言 THU ...

  3. 开源中文分词工具探析(五):FNLP

    FNLP是由Fudan NLP实验室的邱锡鹏老师开源的一套Java写就的中文NLP工具包,提供诸如分词.词性标注.文本分类.依存句法分析等功能. [开源中文分词工具探析]系列: 中文分词工具探析(一) ...

  4. 开源中文分词工具探析(五):Stanford CoreNLP

    CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger).命名实体识别(named entity recognizer ...

  5. 开源中文分词工具探析(六):Stanford CoreNLP

    CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger).命名实体识别(named entity recognizer ...

  6. 中文分词工具探析(二):Jieba

    1. 前言 Jieba是由fxsjy大神开源的一款中文分词工具,一款属于工业界的分词工具--模型易用简单.代码清晰可读,推荐有志学习NLP或Python的读一下源码.与采用分词模型Bigram + H ...

  7. 中文分词工具探析(一):ICTCLAS (NLPIR)

    1. 前言 ICTCLAS是张华平在2000年推出的中文分词系统,于2009年更名为NLPIR.ICTCLAS是中文分词界元老级工具了,作者开放出了free版本的源代码(1.0整理版本在此). 作者在 ...

  8. 基于开源中文分词工具pkuseg-python,我用张小龙的3万字演讲做了测试

    做过搜索的同学都知道,分词的好坏直接决定了搜索的质量,在英文中分词比中文要简单,因为英文是一个个单词通过空格来划分每个词的,而中文都一个个句子,单独一个汉字没有任何意义,必须联系前后文字才能正确表达它 ...

  9. Java实现敏感词过滤 - IKAnalyzer中文分词工具

    IKAnalyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包. 官网: https://code.google.com/archive/p/ik-analyzer/ 本用例借助 I ...

随机推荐

  1. Petya and Array CodeForces - 1042D (树状数组)

    D. Petya and Array time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  2. html 的实践

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 错误代码:0x80070032 处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler”

    错误分析: vs2010默认采用的是.NET 4.0框架,4.0框架是独立的CLR,和.NET 2.0的不同,如果想运行.NET 4.0框架的网站,需要用aspnet_regiis注册.NET 4.0 ...

  4. C#多线程和线程池问题

    static void Main(string[] args) { Thread threadA = new Thread(ThreadMethod); //执行的必须是无返回值的方法 threadA ...

  5. How to show color in CSS

    转至:https://blog.csdn.net/CallMeQiuqiuqiu/article/details/54743459 http://www.w3school.com.cn/cssref/ ...

  6. 潭州课堂25班:Ph201805201 django 项目 第四十三课 后台 用户管理前后功能实现 (课堂笔记)

    用户的展示,编辑,删除, 把用户显示出来,用户名,员工(是,否), 超级用户(是, 否) 活跃状态,(非活跃示为删除) 在前台要显示该用户所属的用户组,在前台代码中是调用类的属性,所以在 user 的 ...

  7. Vue发送请求

    可以试试玩ajax请求,个人觉得axios用Promise包装了下,代码美观 axios请求使用方法      https://github.com/axios/axios#using-applica ...

  8. BZOJ4681 : [Jsoi2010]旅行

    将边按权值从小到大排序. 考虑一条路径,一定是最大的若干条边和最小的相应的没选的边进行交换. 这会导致存在一个分界线$L$,交换之后恰好选中前$L$小的边,且只允许$>L$的边与$\leq L$ ...

  9. Ruby用百度搜索爬虫

    Ruby用百度搜索爬虫 博主ruby学得断断续续,打算写一个有点用的小程序娱乐一下,打算用ruby通过百度通道爬取网络信息. 第三方库准备 mechanize:比较方便地处理网络请求,类似于Pytho ...

  10. 转 Multiple outputs from T4 made easy t4生成多文件

    原文:http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited Usage Initializat ...