分词

分词是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));

线程安全性

除了配置方法不作保证外,任何分词器都是线程安全的。