2022-08-02 用 HanLP 分词时如何自定义词典
发布于 2022年08月02日  (最近编辑 2022年10月14日 )
9 分钟 • 4456 字
在分词的过程中,碰到一个这样的句子:
‘公司产品品质持续提升,单晶硅片用料比例大幅高于行业平均,单晶硅料价格上涨。’
import hanlp
tok = hanlp.load(hanlp.pretrained.tok.COARSE_ELECTRA_SMALL_ZH)
sentence = '公司产品品质持续提升,单晶硅片用料比例大幅高于行业平均,单晶硅料价格上涨。'
sen_list = tok(sentence)
print(sen_list)
['公司', '产品', '品质', '持续', '提升', ',', '单晶', '硅', '片', '用', '料', '比例', '大幅', '高于', '行业', '平均', ',', '单晶', '硅', '料', '价格', '上涨', '。']
可以看出来,这里“单晶硅片”,“单晶硅料”, 被分为了“单晶”“硅”“料”和“单晶”“硅”“片”。
如果我们想要把“单晶硅”分出来。可以设置自定义词典。tok下面有两个参数:dict_force和dict_combine,通过设置这两个参数就可以达到自定义词典的效果。
dict_force和dict_combine有什么区别:
dict_force是强制模式,强制模式的优先级高于统计模型。如果强制模式用于所有文本,会对其他句子进行干扰,所以强制模式一般不用于所有文本,但是可以针对某个特定句子打补丁。
dict_combine是合并模式,合并模式的优先级低于统计模型。就是说句子先用统计模型分词,然后在这个分词的基础上,再进行最长匹配并合并。
先看一下dict_combine的例子:
tok.dict_force = None
tok.dict_combine = {'单晶硅'}
sentence = '公司产品品质持续提升,单晶硅片用料比例大幅高于行业平均,单晶硅料价格上涨。'
['公司', '产品', '品质', '持续', '提升', ',', '单晶硅', '片', '用', '料', '比例', '大幅', '高于', '行业', '平均', ',', '单晶硅', '料', '价格', '上涨', '。']
我们一般会用dict_combine,这样就把“单晶硅”分出来了。
如果在dict_combine里面,同时有’单晶硅片’,‘单晶硅’,这两者都能被分出来。
tok.dict_force = None
tok.dict_combine = {'单晶硅片','单晶硅'}
['公司', '产品', '品质', '持续', '提升', ',', '单晶硅片', '用', '料', '比例', '大幅', '高于', '行业', '平均', ',', '单晶硅', '料', '价格', '上涨', '。']
可以看出,dict_combine的原理是在这个的基础上['公司', '产品', '品质', '持续', '提升', ',', '单晶', '硅', '片', '用', '料', '比例', '大幅', '高于', '行业', '平均', ',', '单晶', '硅', '料', '价格', '上涨', '。']
进行最长匹配,再合并。所以一个被合并成了”单晶硅片”,一个合并成”单晶硅”。为什么不是链各个“单晶硅”,因为“单晶硅片”的长度大于“单晶硅”,按最长的匹配合并。
同理,如果dict_combine里面是“单晶硅”和“硅料”,那么硅料将不会被分出来。
但是自定义词典不是永远都有效的。HANLP这里有说明,在自定义词典下,分词是结合了统计模型和自定义词典之后的分词,并不是完全会按照我们的自定义词典来。
如果我们想把单晶硅片,硅料和但单晶硅都切分正确,如果把它们都放到dict_combine里面,硅料是不会被切分对的。
tok.dict_combine = {'硅料','单晶硅片' ,'单晶硅'}
sentence = '公司产品品质持续提升,单晶硅片用料比例大幅高于行业平均,单晶硅料价格上涨,单晶硅价格也上涨。'
['公司', '产品', '品质', '持续', '提升', ',', '单晶硅片', '用', '料', '比例', '大幅', '高于', '行业', '平均', ',', '单晶硅', '料', '价格', '上涨', ',', '单晶硅', '价格', '也', '上涨', '。']
这时,可以把硅料放在dict_force里面,这样就能同时把三者都分出来。
tok.dict_force = {'硅料'}
tok.dict_combine = {'单晶硅片','单晶硅'}
sentence = '公司产品品质持续提升,单晶硅片用料比例大幅高于行业平均,单晶硅料价格上涨,单晶硅价格也上涨。'
sen_list = tok(sentence)
print(sen_list)
['公司', '产品', '品质', '持续', '提升', ',', '单晶硅片', '用', '料', '比例', '大幅', '高于', '行业', '平均', ',', '单晶', '硅料', '价格', '上涨', ',', '单晶硅', '价格', '也', '上涨', '。']
如果不是这种情况,我们不要用dict_force,因为它极有可能影响其他地方。这是教程里面的一个例子。可以看出,后面这句分错了。
tok.dict_force = {'川普'}
word_list = tok(["首相和川普通电话", "银川普通人与川普通电话讲四川普通话"])
print(word_list)
[['首相', '和', '川普', '通电话'], ['银', '川普', '通人', '与', '川普', '通电话', '讲', '四', '川普', '通话']]
如果把‘普通人’也加到dict_force里面,为啥第二句还是会被分成“川普”呢?
tok.dict_combine = {'硅料','单晶硅'}
[['首相', '和', '川普', '通电话'], ['银', '川普', '通人', '与', '川普', '通电话', '讲', '四', '川普', '通话']]
原文是这样解释的:
即便是将普通人
或普通话
加入到词典中也无济于事,因为在正向最长匹配第二个句子的过程中,会匹配到川普
而不会匹配后两者。
我的理解是dict_combine不是按照最长匹配的,而是在发生冲突的时候,取了“川普”,舍弃了“普通人”。当然我不知道这里的取舍规则是怎么样的。总之在这种情况下我们可以给第一句话打个补丁。
tok.dict_force = {'川普通电话': ['川普', '通', '电话']}
tok(["首相和川普通电话", "银川普通人与川普通电话讲四川普通话"])
[['首相', '和', '川普', '通', '电话'],
['银川', '普通人', '与', '川普', '通', '电话', '讲', '四川', '普通话']]
打补丁的意思是说,针对特殊案例进行校正。这种补丁不存在泛化性,只有出现完全一样的字符片段才会按照dict_force里面分词。
tok.dict_force = {'川普通电话': ['川普', '通', '电话']}
word_list = tok(["首相和川普通电话", "银川普通人与川普通电话讲四川普通话", "四川普通人"])
[['首相', '和', '川普', '通', '电话'], ['银川', '普通人', '与', '川普', '通', '电话', '讲', '四川', '普通话'], ['四川', '普通人']]
总结一下:
用自定义词典时,一般使用dict_combine,注意dict_combine里面的会用最长匹配。
如果dict_combine里面的词有冲突,可以考虑把最短的词放在dict_force里面。
如果要用dict_force,最好是用打补丁的方式,注明在什么样的字符串才用dict_force里面的分词方式。
参考文章:https://github.com/hankcs/HanLP/blob/doc-zh/plugins/hanlp_demo/hanlp_demo/zh/tok_stl.ipynb