北京大学开源分词工具pkuseg 初试与使用感受

Source
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/meiqi0538/article/details/86181846

本部分内容部分来自:https://github.com/lancopku/PKUSeg-python

1.前言

最近看到一些博文介绍了北大的一个开源的中文分词工具包pkuseg。其中说到,它在多个分词数据集上都有非常高的分词准确率,我们所知道的,也经常使用的结巴分词误差率高达 18.55% 和 20.42,而北大的 pkuseg 只有 3.25% 与 4.32%。当然还有其他的分词工具,如:清华大学的THULAC,HanLp,pynlpir等工具。分词的重要性不言而喻,在看到相关介绍后也在第一时间去尝试一下,以下根据github开源信息做出实验,其使用的语言是python。github地址为:https://github.com/lancopku/PKUSeg-python

2.简介

pkuseg是由北京大学语言计算与机器学习研究组研制推出的一套全新的中文分词工具包。pkuseg具有如下几个特点:

  1. 高分词准确率。相比于其他的分词工具包,该工具包在不同领域的数据上都大幅提高了分词的准确度。根据测试结果,pkuseg分别在示例数据集(MSRA和CTB8)上降低了79.33%和63.67%的分词错误率。
  2. 多领域分词。该分词包训练了多种不同领域的分词模型。根据待分词的领域特点,用户可以自由地选择不同的模型。
  3. 支持用户自训练模型。支持用户使用全新的标注数据进行训练。

3.工具使用

3.1安装

方式1.程序包下载安装

pip install pkuseg
之后通过import pkuseg来引用

方式2.从github下载(需要下载模型文件)

将pkuseg文件放到目录下,通过import pkuseg使用
模型需要下载或自己训练。

3.2代码示例

1.使用默认模型及默认词典分词

在实际测试中,第一次加载模型时间比较长,并且分词的结果也没有给出各个词语的词性,如下图:
在这里插入图片描述
2.设置用户自定义词典

在实际测试中,可以看出,自定义词典确实起到作用,但是这个处理时间似乎有点小长,默认词典与自定义词典比较,代码如下

import pkuseg

lexicon = ['北京大学', '北京天安门']				  #希望分词时用户词典中的词固定不分开
segDefault = pkuseg.pkuseg()					#默认分词类型
seg = pkuseg.pkuseg(user_dict=lexicon)			#加载模型,给定用户词典
textDefault = segDefault.cut('我爱北京天安门')	    #进行分词
text = seg.cut('我爱北京天安门')					#进行分词
print(textDefault)
print(text)

运行结果如下:

loading model
finish
loading model
finish
['我', '爱', '北京', '天安门']
['我', '爱', '北京天安门']
[Finished in 40.2s]

3.模型训练

训练模型是需要解压的,具体内容可参考github上的内容,其中代码如下:

import pkuseg

seg = pkuseg.pkuseg(model_name='ctb8')	#假设用户已经下载好了ctb8的模型并放在了'ctb8'目录下,通过设置model_name加载该模型
text = seg.cut('我爱北京天安门')			#进行分词
print(text)

运行结果:

loading model
finish
['我', '爱', '北京', '天安门']
[Finished in 24.6s]

4.对于大型文本数据集,如果需要快速分词的话,我们也可以采用多线程的方式

读取文件,并将分词结果输出到一个文件中,在进行测试的时候,几经报错,也可没出结果,获取我的系统配置有问题,或者输入文件有问题,或者…这里就抛砖引玉,希望大牛能够调出来,代码如下:

import pkuseg

#对input.txt的文件分词输出到output.txt中,使用默认模型和词典,开20个进程
pkuseg.test('input.txt', 'output.txt', nthread=20)	

运行结果:

loading model
finish
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\spawn.py", line 105, in spawn_main
    exitcode = _main(fd)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\spawn.py", line 114, in _main
    prepare(preparation_data)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\spawn.py", line 225, in prepare
    _fixup_main_from_path(data['init_main_from_path'])
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\spawn.py", line 277, in _fixup_main_from_path
    run_name="__mp_main__")
  File "C:\Development\Python\Anaconda3\lib\runpy.py", line 263, in run_path
    pkg_name=pkg_name, script_name=fname)
  File "C:\Development\Python\Anaconda3\lib\runpy.py", line 96, in _run_module_code
    mod_name, mod_spec, pkg_name, script_name)
  File "C:\Development\Python\Anaconda3\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Users\JackPi\Desktop\test\pkusegtest\test.py", line 10, in <module>
    pkuseg.test('input.txt', 'output.txt', nthread=20)	
  File "C:\Development\Python\Anaconda3\lib\site-packages\pkuseg\__init__.py", line 263, in test
    p.start()
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\context.py", line 223, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\context.py", line 322, in _Popen
    return Popen(process_obj)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 33, in __init__
    prep_data = spawn.get_preparation_data(process_obj._name)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\spawn.py", line 143, in get_preparation_data
    _check_not_importing_main()
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\spawn.py", line 136, in _check_not_importing_main
    is not going to be frozen to produce an executable.''')
RuntimeError: 
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.
loading model
finish
Traceback (most recent call last):
  File "C:\Users\JackPi\Desktop\test\pkusegtest\test.py", line 10, in <module>
    pkuseg.test('input.txt', 'output.txt', nthread=20)	
  File "C:\Development\Python\Anaconda3\lib\site-packages\pkuseg\__init__.py", line 263, in test
    p.start()
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\context.py", line 223, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\context.py", line 322, in _Popen
    return Popen(process_obj)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Development\Python\Anaconda3\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
BrokenPipeError: [Errno 32] Broken pipe
[Finished in 42.5s]

其中的input.txt中的内容为:

外星文明探索是一个很特殊的学科,它对研究者的人生观影响很大。夜深人静的时候,从耳机中听着来自宇宙没有生命的噪音,这噪音隐隐约约的,好像比那些星星还永恒;有时又觉得那声音像大兴安岭的冬天里没完没了的寒风,让我感到很冷,那种孤独真是没法形容。地球生命真的是宇宙中偶然里的偶然,人类是这空荡荡的大殿里唯一一只蚂蚁。有时觉得生命真珍贵,一切都重如泰山;有时又觉得人是那么渺小,什么都不值一提。

5.重新训练一个分词模型

import pkuseg
#训练文件为'msr_training.utf8',测试文件为'msr_test_gold.utf8',模型存到'./models'目录下,开20个进程训练模型
pkuseg.train('msr_training.utf8', 'msr_test_gold.utf8', './models', nthread=20)	

这里我就没有去测试,感兴趣的可以去尝试一下。

6.参数说明

pkuseg.pkuseg(model_name='msra', user_dict='safe_lexicon')
model_name		模型路径。默认是'msra'表示我们预训练好的模型(仅对pip下载的用户)。用户可以填自己下载或训练的模型所在的路径如model_name='./models'。
user_dict 		设置用户词典。默认为'safe_lexicon'表示我们提供的一个中文词典(仅pip)。用户可以传入一个包含若干自定义单词的迭代器。

pkuseg.test(readFile, outputFile, model_name='msra', user_dict='safe_lexicon', nthread=10)
readFile		输入文件路径
outputFile		输出文件路径
model_name		同pkuseg.pkuseg
user_dict		同pkuseg.pkuseg
nthread			测试时开的进程数

pkuseg.train(trainFile, testFile, savedir, nthread=10)
trainFile		训练文件路径
testFile		测试文件路径
savedir			训练模型的保存路径
nthread			训练时开的进程数

4 各类分词工具包性能比较

在进行比较之前需要说明以下预训练模型

分词模式下,用户需要加载预训练好的模型。这里提供了三种在不同类型数据上训练得到的模型,根据具体需要,用户可以选择不同的预训练模型。以下是对预训练模型的说明:

MSRA: 在MSRA(新闻语料)上训练的模型。新版本代码采用的是此模型。

CTB8: 在CTB8(新闻文本及网络文本的混合型语料)上训练的模型。

WEIBO: 在微博(网络文本语料)上训练的模型。

其中,MSRA数据由第二届国际汉语分词评测比赛提供,CTB8数据由LDC提供,WEIBO数据由NLPCC分词比赛提供。

具体比较

官方数据中:在比较中选择THULAC、结巴分词等国内代表分词工具包与pkuseg做性能比较,测试环境选择Linux,在新闻数据(MSRA)和混合型文本(CTB8)数据上对不同工具包进行了准确率测试(数据在github上,可下载)。该比较使用了第二届国际汉语分词评测比赛提供的分词评价脚本。评测结果如下:

MSRA F-score Error Rate
jieba 81.45 18.55
THULAC 85.48 14.52
pkuseg 96.75 (+13.18%) 3.25 (-77.62%)
CTB8 F-score Error Rate
jieba 79.58 20.42
THULAC 87.77 12.23
pkuseg 95.64 (+8.97%) 4.36 (-64.35%)

通过上面的数据,可以说明jieba分词的准确率最低,清华大学的THULAC的准确率也没有pkuseg高。但是,我们需要注意的是:北大的这个分词工具是经过这些数据训练出来的,准确率高也是情理之中的事情。真正的好不好,就看使用者的反馈了。

5 相关论文及作者

1.论文

  • Xu Sun, Houfeng Wang, Wenjie Li. Fast Online Training with Frequency-Adaptive Learning Rates for Chinese Word Segmentation and New Word Detection. ACL. 253–262. 2012
  • Jingjing Xu, Xu Sun. Dependency-based Gated Recursive Neural Network for Chinese Word Segmentation. ACL 2016: 567-572

2.作者

Ruixuan Luo (罗睿轩), Jingjing Xu(许晶晶), Xu Sun (孙栩)

6 总结

从客观来看,该工具还是存在缺点的,虽然官方给出的准确率很高,但是在实际分词过程中,花费时间长,系统稳定性低,分词结果也不可以显示分词的词性,综合来讲与当前成熟的开源的分词工具还是有一定的差距,但是也不否定该工具具有的亮点,以及其非常高的准确性。分词是自然语言处理中底层并且非常重要的一环,当前做得也是比较成熟了,但是也存在很多缺陷,也是一个研究的方向。个人认为,真正做自然语言处理,到最后依然是需要回归到语言到底是什么,语言是如何发出的,语言的发展等等,当今基于统计的自然语言处理不容置疑也确实做出不错的成绩,但基本上都是基于语料的,模型训练的等等。在自然语言处理这一交叉学科上,其发展依然任重而道远,不能一味地崇拜机器学习,深度学习,回归语言本质,用数学的方式进行处理也将会是未来研究的方向。