HanLP中文分词

HanLP使用

HanLP是由一系列模型与算法组成的Java工具包,目标是普及自然语言处理在生产环境中的应用。HanLP具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点。 在提供丰富功能的同时,HanLP内部模块坚持低耦合、模型坚持惰性加载、服务坚持静态提供、词典坚持明文发布,使用非常方便,同时自带一些语料处理工具,帮助用户训练自己的语料。

其他文档功能

入门指南

安装HanLP

HanLP将数据与程序分离,给予用户自定义的自由。 HanLP由三部分组成:HanLP = .jar + data + .properties ,请前往 项目主页 下载这三个部分。

1、下载jar

放入classpath并添加依赖。

2、下载数据集

HanLP 中的数据分为 词典 和 模型 ,其中 词典 是词法分析必需的, 模型 是句法分析必需的,data目录结构如下:

data
│
├─dictionary
└─model

用户可以自行增删替换,如果不需要句法分析功能的话,随时可以删除model文件夹。

可选数据集

数据包功能体积(MB)
data.full.zip 全部 255
data.standard.zip 全部词典,不含模型 47
data.mini.zip 小体积词典,不含模型 22

请各取所需。

3、配置文件

示例配置文件:

#本配置文件中的路径的根目录,根目录+其他路径=绝对路径
#Windows用户请注意,路径分隔符统一使用/
root=E:/JavaProjects/HanLP/
#核心词典路径
CoreDictionaryPath=data/dictionary/CoreNatureDictionary.txt
#2元语法词典路径
BiGramDictionaryPath=data/dictionary/CoreNatureDictionary.ngram.txt
#停用词词典路径
CoreStopWordDictionaryPath=data/dictionary/stopwords.txt
#同义词词典路径
CoreSynonymDictionaryDictionaryPath=data/dictionary/synonym/CoreSynonym.txt
#人名词典路径
PersonDictionaryPath=data/dictionary/person/nr.txt
#人名词典转移矩阵路径
PersonDictionaryTrPath=data/dictionary/person/nr.tr.txt
#繁简词典路径
TraditionalChineseDictionaryPath=data/dictionary/tc/TraditionalChinese.txt
#自定义词典路径,用;隔开多个自定义词典,空格开头表示在同一个目录,使用“文件名 词性”形式则表示这个词典的词性默认是该词性。优先级递减。
#另外data/dictionary/custom/CustomDictionary.txt是个高质量的词库,请不要删除
CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; 现代汉语补充词库.txt; 全国地名大全.txt ns; 人名词典.txt; 机构名词典.txt; 上海地名.txt ns;data/dictionary/person/nrf.txt nrf

配置文件的作用是告诉HanLP数据包的位置,只需修改第一行:

root=usr/home/HanLP/

为data的 父目录 即可,比如data目录是 /Users/hankcs/Documents/data ,那么 root=/Users/hankcs/Documents/ 。

  • 如果选用mini数据包的话,则需要修改配置文件:

    CoreDictionaryPath=data/dictionary/CoreNatureDictionary.mini.txt
    BiGramDictionaryPath=data/dictionary/CoreNatureDictionary.ngram.mini.txt
    

最后将HanLP.properties放入classpath即可:

对于Eclipse来讲
project/bin
对于IntelliJ IDEA来讲
project/target/classes
或者
project/out/production/project

Web项目的话可以放在如下位置:

Webapp/WEB-INF/lib
Webapp/WEB-INF/classes
Appserver/lib
JRE/lib

快速上手

HanLP 几乎所有的功能都可以通过工具类 HanLP 快捷调用,当你想不起来调用方法时,只需键入 HanLP. ,IDE应当会给出提示,并展示HanLP完善的文档。

推荐用户始终通过工具类 HanLP 调用,这么做的好处是,将来 HanLP 升级后,用户无需修改调用代码。

所有Demo都位于 com.hankcs.demo 下。

第一个Demo:

System.out.println(HanLP.segment("你好,欢迎使用HanLP!"));
  • 内存要求
    • HanLP 对词典的数据结构进行了长期的优化,可以应对绝大多数场景。哪怕 HanLP 的词典上百兆也无需担心,因为在内存中被精心压缩过。
    • 如果内存非常有限,请使用小词典。 HanLP 默认使用大词典,同时提供小词典,请参考配置文件章节。
    • 在一些句法分析场景中,需要加载几百兆的模型。如果发生 java.lang.OutOfMemoryError ,则建议使用JVM option -Xms1g -Xmx1g -Xmn512m
  • 写给正在编译 HanLP 的开发者
    • 如果你正在编译运行从Github检出的 HanLP 代码,并且没有下载data,那么首次加载词典/模型会发生一个 自动缓存 的过程。
    • 自动缓存 的目的是为了加速词典载入速度,在下次载入时,缓存的词典文件会带来毫秒级的加载速度。由于词典体积很大, 自动缓存 会耗费一些时间,请耐心等待。
    • 自动缓存 缓存的不是明文词典,而是双数组Trie树、DAWG、AhoCorasickDoubleArrayTrie等数据结构。

如果一切正常,您会得到类似于如下的输出:

[你好/vl, ,/w, 欢迎/v, 使用/v, HanLP/nx, !/w]

如果出现了问题,一般是由路径配置不对而引发的,请根据控制台输出的警告调整。比如:

核心词典/Users/hankcs/JavaProjects/HanLP/data/data/dictionary/CoreNatureDictionary.txt加载失败

说明HanLP.properties中的root项配置不对,应当去掉后缀 data/,改为:

root=/Users/hankcs/JavaProjects/HanLP/

分词

分词是HanLP最基础的功能,HanLP实现了许多种分词算法,每个分词器都支持特定的配置。

调用分词器

为了减小用户的记忆难度,特意将一些分词器封装为静态工具类, 并提供了简单的接口。

标准分词

标准分词是最常用的分词器,基于HMM-Viterbi实现,开启了中国人名识别和音译人名识别,调用方法如下:

List<Term> termList = HanLP.segment("商品和服务");
System.out.println(termList);
  • HanLP.segment 其实是对 StandardTokenizer.segment 的包装。
  • HanLP中有一系列“开箱即用”的静态分词器,以 Tokenizer 结尾,在接下来的例子中会继续介绍。

NLP分词

NLP分词 NLPTokenizer 会执行全部命名实体识别和词性标注。,调用方法如下:

List<Term> termList = NLPTokenizer.segment("中国科学院计算技术研究所的宗成庆教授正在教授自然语言处理课程");
System.out.println(termList);
  • NLP分词 NLPTokenizer 会执行全部命名实体识别和词性标注。
  • 所以速度比标准分词慢,并且有误识别的情况。

索引分词

索引分词 IndexTokenizer 是面向搜索引擎的分词器,能够对长词全切分,另外通过 term.offset 可以获取单词在文本中的偏移量。调用方法如下:

List<Term> termList = IndexTokenizer.segment("主副食品");
for (Term term : termList)
{
    System.out.println(term + " [" + term.offset + ":" + (term.offset + term.word.length()) + "]");
}

繁体分词

繁体分词 TraditionalChineseTokenizer 可以直接对繁体进行分词,输出切分后的繁体词语。调用方法如下:

List<Term> termList = TraditionalChineseTokenizer.segment("大衛貝克漢不僅僅是名著名球員,球場以外,其妻為前辣妹合唱團成員維多利亞·碧咸,亦由於他擁有突出外表、百變髮型及正面的形象,以至自己品牌的男士香水等商品,及長期擔任運動品牌Adidas的代言人,因此對大眾傳播媒介和時尚界等方面都具很大的影響力,在足球圈外所獲得的認受程度可謂前所未見。");
System.out.println(termList);
  • 繁体分词也像标准分词一样支持命名实体识别。

极速词典分词

极速分词是词典最长分词,速度极其快,精度一般。调用方法如下:

String text = "江西鄱阳湖干枯,中国最大淡水湖变成大草原";
System.out.println(SpeedTokenizer.segment(text));
long start = System.currentTimeMillis();
int pressure = 1000000;
for (int i = 0; i < pressure; ++i)
{
    SpeedTokenizer.segment(text);
}
double costTime = (System.currentTimeMillis() - start) / (double)1000;
System.out.printf("分词速度:%.2f字每秒", text.length() * pressure / costTime);

接下来介绍的分词器是由用户动态创建,使用场景不常见的分词器。

N-最短路径分词

N最短路分词器 NShortSegment 比最短路分词器( DijkstraSegment )慢,但是效果稍微好一些,对命名实体识别能力更强。调用方法如下:

Segment nShortSegment = new NShortSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);
Segment shortestSegment = new ViterbiSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);
String[] testCase = new String[]{
        "刘喜杰石国祥会见吴亚琴先进事迹报告团成员",
};
for (String sentence : testCase)
{
    System.out.println("N-最短分词:" + nShortSegment.seg(sentence) + "\n最短路分词:" + shortestSegment.seg(sentence));
}
  • 一般场景下最短路分词的精度已经足够,而且速度比N最短路分词器快几倍,请酌情选择。

CRF分词

基于CRF模型和BEMS标注训练得到的分词器。调用方法如下:

Segment segment = new CRFSegment();
segment.enablePartOfSpeechTagging(true);
List<Term> termList = segment.seg("你看过穆赫兰道吗");
System.out.println(termList);
for (Term term : termList)
{
    if (term.nature == null)
    {
        System.out.println("识别到新词:" + term.word);
    }
}
  • CRF对新词有很好的识别能力,但是无法利用自定义词典。
  • 也不支持命名实体识别,应用场景仅限于新词识别。

动态创建和配置

在上面的例子中,一些工具类包装了配置好的分词器。HanLP同时支持用户动态创建分词器和配置分词器。

创建分词器

既可以用new创建,也可以用工具类创建,推荐后者,可以应对未来的版本升级。

通过new关键字

分词器是Java对象,可以用传统的new关键字创建任意的分词器。

ViterbiSegment

也是最短路分词,最短路求解采用Viterbi算法:

Segment segment = new ViterbiSegment()
DijkstraSegment

依然是最短路分词,最短路求解采用Dijkstra算法:

Segment segment = new DijkstraSegment()
  • DijkstraSegment比ViterbiSegment慢一点,但是调试信息更加丰富。
NShortSegment

N最短分词器:

Segment segment = new NShortSegment()
  • 算法很美,速度很慢。
AhoCorasickSegment

使用AhoCorasickDoubleArrayTrie实现的最长分词器:

Segment segment = new AhoCorasickSegment()
  • 应该是速度最快的词典分词了。
CRFSegment

基于CRF的分词器:

Segment segment = new CRFSegment()
  • 应用场景不多。

通过HanLP.newSegment

通过此工厂方法得到的是当前版本速度和效果最平衡的分词器:

Segment segment = HanLP.newSegment();
  • 推荐用户始终通过工具类HanLP调用,这么做的好处是,将来HanLP升级后,用户无需修改调用代码。

配置分词器

所有分词器都是 Segment 的子类, Segment 提供以下配置接口:

/**
 * 设为索引模式
 *
 * @return
 */
public Segment enableIndexMode(boolean enable)

/**
 * 开启词性标注
 * @param enable
 * @return
 */
public Segment enablePartOfSpeechTagging(boolean enable)

/**
 * 开启人名识别
 * @param enable
 * @return
 */
public Segment enableNameRecognize(boolean enable)

/**
 * 开启地名识别
 * @param enable
 * @return
 */
public Segment enablePlaceRecognize(boolean enable)

/**
 * 开启机构名识别
 * @param enable
 * @return
 */
public Segment enableOrganizationRecognize(boolean enable)

/**
 * 是否启用用户词典
 *
 * @param enable
 */
public Segment enableCustomDictionary(boolean enable)

/**
 * 是否启用音译人名识别
 *
 * @param enable
 */
public Segment enableTranslatedNameRecognize(boolean enable)

/**
 * 是否启用日本人名识别
 *
 * @param enable
 */
public Segment enableJapaneseNameRecognize(boolean enable)

/**
 * 是否启用偏移量计算(开启后Term.offset才会被计算)
 * @param enable
 * @return
 */
public Segment enableOffset(boolean enable)

/**
 * 是否启用所有的命名实体识别
 * @param enable
 * @return
 */
public Segment enableAllNamedEntityRecognize(boolean enable)

用户可以使用链式语法对Segment执行创建和配置操作,一气呵成:

Segment shortestSegment = new ViterbiSegment().enableCustomDictionary(false).enablePlaceRecognize(true).enableOrganizationRecognize(true);

对于工具类中的分词器,也可以使用暴露出来的SEGMENT成员对其进行配置:

String text = "泽田依子是上外日本文化经济学院的外教";
System.out.println(StandardTokenizer.segment(text));
StandardTokenizer.SEGMENT.enableAllNamedEntityRecognize(true);
System.out.println(StandardTokenizer.segment(text));


词典

本章详细介绍HanLP中的词典格式,满足用户自定义的需要。HanLP中有许多词典,它们的格式都是相似的,形式都是文本文档,随时可以修改。

基本格式

词典分为词频词性词典和词频词典。

词频词性词典

每一行代表一个单词,格式遵从[单词] [词性A] [A的频次] [词性B] [B的频次] ...。

动态修改

用户甚至可以通过代码动态修改词频词性词典:

// 动态增加
CustomDictionary.add("孔雀女");
// 强行插入
CustomDictionary.insert("码农", "nz 1024");
// 删除词语(注释掉试试)
// CustomDictionary.remove("码农");
System.out.println(CustomDictionary.add("裸婚", "v 2 nz 1"));
System.out.println(CustomDictionary.get("裸婚"));

String text = "码农和孔雀女裸婚了";  // 怎么可能噗哈哈!

// AhoCorasickDoubleArrayTrie自动机分词
final char[] charArray = text.toCharArray();
CoreDictionary.trie.parseText(charArray, new AhoCorasickDoubleArrayTrie.IHit<CoreDictionary.Attribute>()
{
    @Override
    public void hit(int begin, int end, CoreDictionary.Attribute value)
    {
        System.out.printf("[%d:%d]=%s %s\n", begin, end, new String(charArray, begin, end - begin), value);
    }
});
// trie树分词
BaseSearcher searcher = CustomDictionary.getSearcher(text);
Map.Entry entry;
while ((entry = searcher.next()) != null)
{
    System.out.println(entry);
}

// 标准分词
System.out.println(HanLP.segment(text));
  • 基本说明
    • CustomDictionary 是一份全局的用户自定义词典,可以随时增删,影响全部分词器。
    • 另外可以在任何分词器中关闭它。通过代码动态增删不会保存到词典文件。
  • 追加词典
    • CustomDictionary 主词典文本路径是 data/dictionary/custom/CustomDictionary.txt ,用户可以在此增加自己的词语(不推荐);也可以单独新建一个文本文件,通过配置文件 CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; 我的词典.txt; 来追加词典(推荐)。
    • 始终建议将相同词性的词语放到同一个词典文件里,便于维护和分享。
  • 词典格式
    • 每一行代表一个单词,格式遵从 [单词] [词性A] [A的频次] [词性B] [B的频次] ... 如果不填词性则表示采用词典的默认词性。
    • 词典的默认词性默认是名词n,可以通过配置文件修改: 全国地名大全.txt ns; 如果词典路径后面空格紧接着词性,则该词典默认是该词性。
    • 关于用户词典的更多信息请参考 词频词典 一节。

词频词典

每一行代表一个单词,格式遵从[单词] [单词的频次]。 每一行的分隔符为空格符或制表符 少数词典有自己的专用格式,比如同义词词典兼容《同义词词林扩展版》的文本格式,而转移矩阵词典则是一个csv表格。

下文主要介绍通用词典,如不注明,词典特指通用词典。

数据结构

Trie树(字典树)是HanLP中使用最多的数据结构,为此,我实现了通用的Trie树,支持泛型、遍历、储存、载入。

用户自定义词典采用AhoCorasickDoubleArrayTrie和二分Trie树储存,其他词典采用基于双数组Trie树(DoubleArrayTrie)实现的AC自动机AhoCorasickDoubleArrayTrie。

储存形式

词典有两个形态:文本文件(filename.txt)和缓存文件(filename.txt.bin或filename.txt.trie.dat和filename.txt.trie.value)。

文本文件

采用明文储存,UTF-8编码,CRLF换行符。

缓存文件

就是一些二进制文件,通常在文本文件的文件名后面加上.bin表示。有时候是.trie.dat和.trie.value。后者是历史遗留产物,分别代表trie树的数组和值。 如果你修改了任何词典,只有删除缓存才能生效。

修改方法

HanLP的核心词典训练自人民日报2014语料,语料不是完美的,总会存在一些错误。这些错误可能会导致分词出现奇怪的结果,这时请打开调试模式排查问题:

HanLP.Config.enableDebug();

核心词性词频词典

比如你在 data/dictionary/CoreNatureDictionary.txt 中发现了一个不是词的词,或者词性标注得明显不对,那么你可以修改它,然后删除缓存文件使其生效。

核心二元文法词典

二元文法词典 data/dictionary/CoreNatureDictionary.ngram.txt 储存的是两个词的接续,如果你发现不可能存在这种接续时,删掉即可。 你也可以添加你认为合理的接续,但是这两个词必须同时在核心词典中才会生效。

命名实体识别词典

基于角色标注的命名实体识别比较依赖词典,所以词典的质量大幅影响识别质量。 这些词典的格式与原理都是类似的,请阅读相应的文章或代码修改它。 如果问题解决了,欢迎向我提交一个pull request,这是我在代码库中保留明文词典的原因,众人拾柴火焰高!

命名实体识别

本章将讲解HanLP中的命名实体识别功能。目前,命名实体识别是作为分词的一个后续过程而存在的。

中国人名识别

可以自动识别中国人名,标注为nr:

String[] testCase = new String[]{
    "签约仪式前,秦光荣、李纪恒、仇和等一同会见了参加签约的企业家。",
    "王国强、高峰、汪洋、张朝阳光着头、韩寒、小四",
    "张浩和胡健康复员回家了",
    "王总和小丽结婚了",
    "编剧邵钧林和稽道青说",
    "这里有关天培的有关事迹",
    "龚学平等领导,邓颖超生前",
    };
Segment segment = HanLP.newSegment().enableNameRecognize(true);
for (String sentence : testCase)
{
    List<Term> termList = segment.seg(sentence);
    System.out.println(termList);
}
  • 目前分词器基本上都默认开启了中国人名识别,比如HanLP.segment()接口中使用的分词器等等,用户不必手动开启;上面的代码只是为了强调。
  • 有一定的误命中率,比如误命中 关键年 ,则可以通过在 data/dictionary/person/nr.txt 加入一条 关键年 1 来排除关键年作为人名的可能性,也可以将 关键年 作为新词登记到自定义词典中。
  • 如果你通过上述办法解决了问题,欢迎向我提交pull request,词典也是宝贵的财富。
  • 算法详解 《实战HMM-Viterbi角色标注中国人名识别》

音译人名识别

可以自动识别音译人名,标注为nrf:

String[] testCase = new String[]{
            "一桶冰水当头倒下,微软的比尔盖茨、Facebook的扎克伯格跟桑德博格、亚马逊的贝索斯、苹果的库克全都不惜湿身入镜,这些硅谷的科技人,飞蛾扑火似地牺牲演出,其实全为了慈善。",
            "世界上最长的姓名是简森·乔伊·亚历山大·比基·卡利斯勒·达夫·埃利奥特·福克斯·伊维鲁莫·马尔尼·梅尔斯·帕特森·汤普森·华莱士·普雷斯顿。",
    };
Segment segment = HanLP.newSegment().enableTranslatedNameRecognize(true);
for (String sentence : testCase)
{
    List<Term> termList = segment.seg(sentence);
    System.out.println(termList);
}

日本人名识别

可以自动识别日本人名,标注为nrj:

String[] testCase = new String[]{
    "北川景子参演了林诣彬导演的《速度与激情3》",
    "林志玲亮相网友:确定不是波多野结衣?",
};
Segment segment = HanLP.newSegment().enableJapaneseNameRecognize(true);
for (String sentence : testCase)
{
    List<Term> termList = segment.seg(sentence);
    System.out.println(termList);
}

地名识别

可以自动识别地名,标注为ns:

String[] testCase = new String[]{
    "武胜县新学乡政府大楼门前锣鼓喧天",
    "蓝翔给宁夏固原市彭阳县红河镇黑牛沟村捐赠了挖掘机",
};
Segment segment = HanLP.newSegment().enablePlaceRecognize(true);
for (String sentence : testCase)
{
    List<Term> termList = segment.seg(sentence);
    System.out.println(termList);
}
  • 目前标准分词器都默认关闭了地名识别,用户需要手动开启;这是因为消耗性能,其实多数地名都收录在核心词典和用户自定义词典中。
  • 在生产环境中,能靠词典解决的问题就靠词典解决,这是最高效稳定的方法。
  • 算法详解 《实战HMM-Viterbi角色标注地名识别》

机构名识别

可以自动识别机构名,标注为nt:

String[] testCase = new String[]{
        "我在上海林原科技有限公司兼职工作,",
        "同时在上海外国语大学日本文化经济学院学习经济与外语。",
        "我经常在台川喜宴餐厅吃饭,",
        "偶尔去地中海影城看电影。",
};
Segment segment = HanLP.newSegment().enableOrganizationRecognize(true);
for (String sentence : testCase)
{
    List<Term> termList = segment.seg(sentence);
    System.out.println(termList);
}
  • 目前分词器默认关闭了机构名识别,用户需要手动开启;这是因为消耗性能,其实常用机构名都收录在核心词典和用户自定义词典中。
  • HanLP的目的不是演示动态识别,在生产环境中,能靠词典解决的问题就靠词典解决,这是最高效稳定的方法。
  • 算法详解 《层叠HMM-Viterbi角色标注模型下的机构名识别》

篇章理解

有时候我们想知道一篇文章中概况性的中心句是哪几句,或者提取关键字,HanLP提供了这些功能。

关键词提取

一句话调用,第一个参数指定文本,第二个参数指定需要提取几个关键字:

String content = "程序员(英文Programmer)是从事程序开发、维护的专业人员。一般将程序员分为程序设计人员和程序编码人员,但两者的界限并不非常清楚,特别是在中国。软件从业人员分为初级程序员、高级程序员、系统分析员和项目经理四大类。";
List<String> keywordList = HanLP.extractKeyword(content, 5);
System.out.println(keywordList);

自动摘要

同样是一句话调用,第一个参数指定文本,第二个参数指定需要提取几个句子:

String document = "算法可大致分为基本算法、数据结构的算法、数论算法、计算几何的算法、图的算法、动态规划以及数值分析、加密算法、排序算法、检索算法、随机化算法、并行算法、厄米变形模型、随机森林算法。\n" +
    "算法可以宽泛的分为三类,\n" +
    "一,有限的确定性算法,这类算法在有限的一段时间内终止。他们可能要花很长时间来执行指定的任务,但仍将在一定的时间内终止。这类算法得出的结果常取决于输入值。\n" +
    "二,有限的非确定算法,这类算法在有限的时间内终止。然而,对于一个(或一些)给定的数值,算法的结果并不是唯一的或确定的。\n" +
    "三,无限的算法,是那些由于没有定义终止定义条件,或定义的条件无法由输入的数据满足而不终止运行的算法。通常,无限算法的产生是由于未能确定的定义终止条件。";
List<String> sentenceList = HanLP.extractSummary(document, 3);
System.out.println(sentenceList);

短语提取

同样是一句话调用,第一个参数指定文本,第二个参数指定需要提取几个短语:

String text = "算法工程师\n" +
            "算法(Algorithm)是一系列解决问题的清晰指令,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。算法工程师就是利用算法处理事物的人。\n" +
            "\n" +
            "1职位简介\n" +
            "算法工程师是一个非常高端的职位;\n" +
            "专业要求:计算机、电子、通信、数学等相关专业;\n" +
            "学历要求:本科及其以上的学历,大多数是硕士学历及其以上;\n" +
            "语言要求:英语要求是熟练,基本上能阅读国外专业书刊;\n" +
            "必须掌握计算机相关知识,熟练使用仿真工具MATLAB等,必须会一门编程语言。\n" +
            "\n" +
            "2研究方向\n" +
            "视频算法工程师、图像处理算法工程师、音频算法工程师 通信基带算法工程师\n" +
            "\n" +
            "3目前国内外状况\n" +
            "目前国内从事算法研究的工程师不少,但是高级算法工程师却很少,是一个非常紧缺的专业工程师。算法工程师根据研究领域来分主要有音频/视频算法处理、图像技术方面的二维信息算法处理和通信物理层、雷达信号处理、生物医学信号处理等领域的一维信息算法处理。\n" +
            "在计算机音视频和图形图像技术等二维信息算法处理方面目前比较先进的视频处理算法:机器视觉成为此类算法研究的核心;另外还有2D转3D算法(2D-to-3D conversion),去隔行算法(de-interlacing),运动估计运动补偿算法(Motion estimation/Motion Compensation),去噪算法(Noise Reduction),缩放算法(scaling),锐化处理算法(Sharpness),超分辨率算法(Super Resolution),手势识别(gesture recognition),人脸识别(face recognition)。\n" +
            "在通信物理层等一维信息领域目前常用的算法:无线领域的RRM、RTT,传送领域的调制解调、信道均衡、信号检测、网络优化、信号分解等。\n" +
            "另外数据挖掘、互联网搜索算法也成为当今的热门方向。\n" +
            "算法工程师逐渐往人工智能方向发展。";
List<String> phraseList = HanLP.extractPhrase(text, 10);
System.out.println(phraseList);

简繁拼音转换

汉语少不了面对简体繁体、汉字拼音的相互转换。HanLP基于双数组Trie树AC自动机算法提供了性能极高的转换功能。

简繁转换

一句话调用:

System.out.println(HanLP.convertToTraditionalChinese("“以后等你当上皇后,就能买草莓庆祝了”"));
System.out.println(HanLP.convertToSimplifiedChinese("用筆記簿型電腦寫程式HelloWorld"));

拼音转换

一句话就能调用,这里展示了对音调、声母、韵母等的支持:

String text = "重载不是重任";
List<Pinyin> pinyinList = HanLP.convertToPinyinList(text);
System.out.print("原文,");
for (char c : text.toCharArray())
{
    System.out.printf("%c,", c);
}
System.out.println();

System.out.print("拼音(数字音调),");
for (Pinyin pinyin : pinyinList)
{
    System.out.printf("%s,", pinyin);
}
System.out.println();

System.out.print("拼音(符号音调),");
for (Pinyin pinyin : pinyinList)
{
    System.out.printf("%s,", pinyin.getPinyinWithToneMark());
}
System.out.println();

System.out.print("拼音(无音调),");
for (Pinyin pinyin : pinyinList)
{
    System.out.printf("%s,", pinyin.getPinyinWithoutTone());
}
System.out.println();

System.out.print("声调,");
for (Pinyin pinyin : pinyinList)
{
    System.out.printf("%s,", pinyin.getTone());
}
System.out.println();

System.out.print("声母,");
for (Pinyin pinyin : pinyinList)
{
    System.out.printf("%s,", pinyin.getShengmu());
}
System.out.println();

System.out.print("韵母,");
for (Pinyin pinyin : pinyinList)
{
    System.out.printf("%s,", pinyin.getYunmu());
}
System.out.println();

System.out.print("输入法头,");
for (Pinyin pinyin : pinyinList)
{
    System.out.printf("%s,", pinyin.getHead());
}
System.out.println();
  • HanLP不仅支持基础的汉字转拼音,还支持声母、韵母、音调、音标和输入法首字母首声母功能。
  • HanLP能够识别多音字,也能给繁体中文注拼音。
  • 最重要的是,HanLP采用的模式匹配升级到 AhoCorasickDoubleArrayTrie,性能大幅提升,能够提供毫秒级的响应速度!
  • 算法详解: 《汉字转拼音与简繁转换的Java实现》

依存句法解析

最大熵依存句法分析器

这是一个判别式汉语句法分析器的Java实现,基于最大熵模型和最大生成树模型,实现了中文依存句法的自动分析,在封闭测试集(取自训练集)上取得了99.20%的准确率(UA),分析速度达到 570.7句/秒。 调用方法:

System.out.println(HanLP.parseDependency("把市场经济奉行的等价交换原则引入党的生活和国家机关政务活动中"));

CRF依存句法分析器

这是一个基于CRF的中文依存句法分析器,内部CRF模型的特征函数采用 双数组Trie树(DoubleArrayTrie)储存,解码采用特化的维特比后向算法。相较于《最大熵依存句法分析器的实现》,分析速度翻了一倍,达到了1262.8655 sent/s 调用方法:

System.out.println(CRFDependencyParser.compute("把市场经济奉行的等价交换原则引入党的生活和国家机关政务活动中"));

输出格式

输出为CoNLL格式,具体说来:

CONLL标注格式包含10列,分别为:
———————————————————————————
ID   FORM    LEMMA   CPOSTAG POSTAG  FEATS   HEAD    DEPREL  PHEAD   PDEPREL
———————————————————————————


只用到前8列,其含义分别为:

1    ID      当前词在句子中的序号,1开始.
2    FORM    当前词语或标点
3    LEMMA   当前词语(或标点)的原型或词干,在中文中,此列与FORM相同
4    CPOSTAG 当前词语的词性(粗粒度)
5    POSTAG  当前词语的词性(细粒度)
6    FEATS   句法特征,在本次评测中,此列未被使用,全部以下划线代替。
7    HEAD    当前词语的中心词
8    DEPREL  当前词语与中心词的依存关系

在CONLL格式中,每个词语占一行,无值列用下划线'_'代替,列的分隔符为制表符'\t',行的分隔符为换行符'\n';句子与句子之间用空行分隔。

智能推荐

语义距离

可以方便地计算两个词汇之间的距离,意义越相近,距离越小:

String[] wordArray = new String[]
        {
                "香蕉",
                "苹果",
                "白菜",
                "水果",
                "蔬菜",
                "自行车",
                "公交车",
                "飞机",
                "买",
                "卖",
                "购入",
                "新年",
                "春节",
                "丢失",
                "补办",
                "办理",
                "送给",
                "寻找",
                "孩子",
                "教室",
                "教师",
                "会计",
        };
for (String a : wordArray)
{
    for (String b : wordArray)
    {
        System.out.println(a + "\t" + b + "\t之间的距离是\t" + CoreSynonymDictionary.distance(a, b));
    }
}
  • 设想的应用场景是搜索引擎对词义的理解,词与词并不只存在“同义词”与“非同义词”的关系,就算是同义词,它们之间的意义也是有微妙的差别的。
  • 算法上,为每个词分配一个语义ID,词与词的距离通过语义ID的差得到。语义ID通过《同义词词林扩展版》计算而来。

智能推荐

也就是文本推荐(句子级别,从一系列句子中挑出与输入句子最相似的那一个):

Suggester suggester = new Suggester();
String[] titleArray =
(
        "威廉王子发表演说 呼吁保护野生动物\n" +
        "《时代》年度人物最终入围名单出炉 普京马云入选\n" +
        "“黑格比”横扫菲:菲吸取“海燕”经验及早疏散\n" +
        "日本保密法将正式生效 日媒指其损害国民知情权\n" +
        "英报告说空气污染带来“公共健康危机”"
).split("\\n");
for (String title : titleArray)
{
    suggester.addSentence(title);
}

System.out.println(suggester.suggest("发言", 1));       // 语义
System.out.println(suggester.suggest("危机公共", 1));   // 字符
System.out.println(suggester.suggest("mayun", 1));      // 拼音
  • 在搜索引擎的输入框中,用户输入一个词,搜索引擎会联想出最合适的搜索词, HanLP 实现了类似的功能。
  • 可以动态调节每种识别器的权重

Lucene插件

兼容Lucene4.x,请前往项目主页下载此插件。

标准分词器

也即不做长词切分的分词器。 调用方法:

String text = "中华人民共和国很辽阔";
for (int i = 0; i < text.length(); ++i)
{
    System.out.print(text.charAt(i) + "" + i + " ");
}
System.out.println();
Analyzer analyzer = new HanLPAnalyzer();
TokenStream tokenStream = analyzer.tokenStream("field", text);
while (tokenStream.incrementToken())
{
    CharTermAttribute attribute = tokenStream.getAttribute(CharTermAttribute.class);
    // 偏移量
    OffsetAttribute offsetAtt = tokenStream.getAttribute(OffsetAttribute.class);
    // 距离
    PositionIncrementAttribute positionAttr = tokenStream.getAttribute(PositionIncrementAttribute.class);
    System.out.println(attribute + " " + offsetAtt.startOffset() + " " + offsetAtt.endOffset() + " " + positionAttr.getPositionIncrement());
}

索引分词器

长词全切分的索引分词模式。 调用方法:

String text = "中华人民共和国很辽阔";
for (int i = 0; i < text.length(); ++i)
{
    System.out.print(text.charAt(i) + "" + i + " ");
}
System.out.println();
Analyzer analyzer = new HanLPIndexAnalyzer();
TokenStream tokenStream = analyzer.tokenStream("field", text);
while (tokenStream.incrementToken())
{
    CharTermAttribute attribute = tokenStream.getAttribute(CharTermAttribute.class);
    // 偏移量
    OffsetAttribute offsetAtt = tokenStream.getAttribute(OffsetAttribute.class);
    // 距离
    PositionIncrementAttribute positionAttr = tokenStream.getAttribute(PositionIncrementAttribute.class);
    System.out.println(attribute + " " + offsetAtt.startOffset() + " " + offsetAtt.endOffset() + " " + positionAttr.getPositionIncrement());
}

自定义分词器

HanLP的Lucene插件提供灵活的构造HanLPTokenizer方法:

String text = "中华人民共和国很辽阔";
for (int i = 0; i < text.length(); ++i)
{
    System.out.print(text.charAt(i) + "" + i + " ");
}
System.out.println();
StringReader reader = new StringReader(text);
HashSet<String> filter = new HashSet<String>();
filter.add("很");
Tokenizer tokenizer = new HanLPTokenizer(IndexTokenizer.SEGMENT.enableOffset(true), reader, filter, false);
while (tokenizer.incrementToken())
{
    CharTermAttribute attribute = tokenizer.getAttribute(CharTermAttribute.class);
    // 偏移量
    OffsetAttribute offsetAtt = tokenizer.getAttribute(OffsetAttribute.class);
    // 距离
    PositionIncrementAttribute positionAttr = tokenizer.getAttribute(PositionIncrementAttribute.class);
    System.out.println(attribute + " " + offsetAtt.startOffset() + " " + offsetAtt.endOffset() + " " + positionAttr.getPositionIncrement());
}
posted @ 2017-12-22 19:23  0-1-world  阅读(1716)  评论(0)    收藏  举报