加载模型

HanLP的工作流程是先加载模型,模型的标示符存储在hanlp.pretrained这个包中,按照NLP任务归类。

import hanlp
hanlp.pretrained.tok.ALL # 语种见名称最后一个字段或相应语料库

调用hanlp.load进行加载,模型会自动下载到本地缓存。自然语言处理分为许多任务,分词只是最初级的一个。

tok = hanlp.load(hanlp.pretrained.tok.COARSE_ELECTRA_SMALL_ZH)
tok

进阶知识

你可以通过加载不同的模型实现各种颗粒度、各种分词标准、各种领域的中文分词。其中,coarse和fine模型训练自9970万字的大型综合语料库,覆盖新闻、社交媒体、金融、法律等多个领域,是已知范围内全世界最大的中文分词语料库。语料库规模决定实际效果,面向生产环境的语料库应当在千万字量级。欢迎用户在自己的语料上训练或微调模型以适应新领域。语料库标注标准决定最终的分词标准,模型的准确率决定多大程度上再现该分词标准。更多背景知识请参考《自然语言处理入门》

执行分词

tok(['商品和服务。', '晓美焰来到北京立方庭参观自然语义科技公司'])

细分标准

你可以通过加载FINE_ELECTRA_SMALL_ZH模型实现细粒度中文分词:

tok_fine = hanlp.load(hanlp.pretrained.tok.FINE_ELECTRA_SMALL_ZH)

无论哪个模型,分词器的接口是完全一致的:

tok_fine('晓美焰来到北京立方庭参观自然语义科技公司')

无限长度

众所周知,Transformer的输入有长度限制(通常是512)。幸运地是,HanLP的滑动窗口技巧完美地突破了该限制。只要你的内存(显存)足够,HanLP就可以处理无限长的句子。

并行分词

无论是CPU还是GPU,同时传入多个句子都将并行分词。也就是说,仅花费1个句子的时间可以处理多个句子。然而工作研究中的文本通常是一篇文档,而不是许多句子。此时可以利用HanLP提供的分句功能和流水线模式优雅应对,既能处理长文本又能并行化。只需创建一个流水线pipeline,第一级管道分句,第二级管道分词:

HanLP = hanlp.pipeline() \
    .append(hanlp.utils.rules.split_sentence) \
    .append(tok)
HanLP('量体裁衣,HanLP提供RESTful和native两种API。两者在语义上保持一致,在代码上坚持开源。')

返回结果是每个句子的分词list,如果要将它们合并到一个list里该怎么办呢?聪明的用户可能已经想到了,再加一级lambda管道:

HanLP.append(lambda sents: sum(sents, []))
print(HanLP('量体裁衣,HanLP提供RESTful和native两种API。两者在语义上保持一致,在代码上坚持开源。'))

自定义词典

智者千虑,必有一失。模型偶尔也会犯错误,比如某个旧版本模型在不挂词典时会犯以下错误(最新版已经修复):

tok = hanlp.load('https://file.hankcs.com/hanlp/tok/coarse_electra_small_20220220_013548.zip')
tok.dict_force = tok.dict_combine = None
tok("首相和川普通电话")

上面分词任务两个成员变量dict_forcedict_combine为自定义词典:

tok.dict_combine, tok.dict_force

HanLP支持合并和强制两种优先级的自定义词典,以满足不同场景的需求。

强制模式

强制模式dict_force优先输出正向最长匹配到的自定义词条,在这个案例中,用户的第一反应也许是将川普加入到dict_force中,强制分词器输出川普

tok.dict_force = {'川普'}
tok(["首相和川普通电话", "银川普通人与川普通电话讲四川普通话"])

然而与大众的朴素认知不同,词典优先级最高未必是好事。极有可能匹配到不该分出来的自定义词语,导致歧义。即便是将普通人普通话加入到词典中也无济于事,因为在正向最长匹配第二个句子的过程中,会匹配到川普而不会匹配后两者。这也解释了为什么自定义词典中存在的词可能分不出来:当歧义发生时,两个词语发生交叉冲突,自然有所取舍,无法同时输出两者。那种同时输出句子或长单词中所有可能的单词,并且允许单词交叉的算法,并非分词,而是多模式字符串匹配。你需要基本的算法知识才能理解这一点,总之一般情况下应当慎用强制模式,详见《自然语言处理入门》第二章。

自定义词语越长,越不容易发生歧义。这启发我们将强制模式拓展为强制校正功能。强制校正原理相似,但会将匹配到的自定义词条替换为相应的分词结果:

tok.dict_force = {'川普通电话': ['川普', '通', '电话']}
tok(["首相和川普通电话", "银川普通人与川普通电话讲四川普通话"])

强制校正是一种短平快的规则补丁,需要针对每种可能产生歧义的语境,截取一个片段执行校正。当你积累了很多歧义片段与相应的校正补丁后,其实就应该考虑微调模型。微调可以让模型增量式学习这些歧义语境,摆脱对补丁规则的依赖,同时举一反三应对新的语境。从错误中积累经验,用经验预测未来,这就是机器学习与人工智能的魅力。

事实上,“川普通电话”这种例子不需要词典即可分对。只需提供给神经网络足够的上下文线索(这也是真实文本所具备的),告诉神经网络“川普是美国总统”:

tok.dict_force = tok.dict_combine = None
print(tok(["首相和川普通电话,川普是美国总统。", "银川普通人与川普通电话讲四川普通话,川普是美国总统。"]))

在上面的例子中,虽然词典对“川普”没有施加任何影响,但是更丰富的上下文促进了神经网络对语境的理解,使其得出了正确的结果。深度学习中的神经网络似乎展示了些许智能,感兴趣的初学者可参考《自然语言处理入门》

合并模式

合并模式的优先级低于统计模型,即dict_combine会在统计模型的分词结果上执行最长匹配并合并匹配到的词条。一般情况下,推荐使用该模式。比如,将“美国总统”加入dict_combine后会合并['美国', '总统'],而不会合并['美国', '总', '统筹部']['美国总统', '筹部']

tok.dict_force = None
tok.dict_combine = {'美国总统'}
print(tok(["首相和川普通电话,川普是美国总统。", "银川普通人与川普通电话讲四川普通话,川普是美国总统。", "美国总统筹部部长是谁?"]))

空格单词

含有空格、制表符等(Transformer tokenizer去掉的字符)的词语需要用tuple的形式提供:

tok.dict_combine = {('iPad', 'Pro'), '2个空格'}
tok("如何评价iPad Pro ?iPad  Pro有2个空格")

聪明的用户请继续阅读,tuple词典中的字符串其实等价于该字符串的所有可能的切分方式:

In [15]:


dict(tok.dict_combine.config["dictionary"]).keys()


Out[15]:


dict_keys([('iPad', 'Pro'), ('2个空格',), ('2', '个', '空格'), ('2', '个', '空', '格'), ('2', '个空格'), ('2', '个空', '格'), ('2个', '空', '格'), ('2个', '空格'), ('2个空', '格')])


单词位置

HanLP支持输出每个单词在文本中的原始位置,以便用于搜索引擎等场景。在词法分析中,非语素字符(空格、换行、制表符等)会被剔除,此时需要额外的位置信息才能定位每个单词:

tok.config.output_spans = True
sent = '2021 年\nHanLPv2.1 为生产环境带来次世代最先进的多语种NLP技术。'
word_offsets = tok(sent)
print(word_offsets)


返回格式为三元组(单词,单词的起始下标,单词的终止下标),下标以字符级别计量。


for word, begin, end in word_offsets:
    assert word == sent[begin:end]



多语种支持

得益于语言无关的设计,以及大规模多语种语料库,最近HanLP发布了支持130种语言的单任务分词器。用法与中文分词器相同:

mul = hanlp.load(hanlp.pretrained.tok.UD_TOK_MMINILMV2L6)
print(mul([
    'In 2021, HanLPv2.1 delivers state-of-the-art multilingual NLP techniques to production environments.',
    '2021年、HanLPv2.1は次世代の最先端多言語NLP技術を本番環境に導入します。',
    '2021年 HanLPv2.1为生产环境带来次世代最先进的多语种NLP技术。',
    '奈須きのこは1973年11月28日に千葉県円空山で生まれ、ゲーム制作会社「ノーツ」の設立者だ。'
]))

目前,多语种分词器的效果并不如单语种好。欢迎在你自己的单语种语料上自行训练新模型,也欢迎开源你的语料和模型。