Chinaunix

标题: 结巴中文分词的Perl扩展 [打印本页]

作者: zhouzhen1    时间: 2017-04-16 22:42
标题: 结巴中文分词的Perl扩展
最近抽空写了一个Lingua::ZH::Jieba模块。

https://metacpan.org/pod/Lingua::ZH::Jieba

我是用SWIG去包的CppJieba,那是结巴的一个C++实现。实际上用SWIG做这一类C++的Perl binding还是很方便的,到今天这个程度(0.003版)我断断续续写了几次,包括在github上把Travis和AppVeyor两个CI都设置起来,加起来估计也没到8小时。

欢迎大家试用。


作者: fayland    时间: 2017-04-17 09:13
楼主++++++
作者: L_WC    时间: 2017-04-17 17:19
本帖最后由 L_WC 于 2017-04-19 12:49 编辑

回复 1# zhouzhen1

首先感谢lz, 刚好前段时间有个需求要用分词,我用perl调的python的jieba....如果这个perl的好用了,很有帮助。。。谢谢
刚使用了一下,
1. 有个问题想请教一下,extract的score 是根据什么来的。

2 . 在 $jieba->insert_user_word("中国人");  之后只有 {cut_all => 1} 模式才能输出 中国人 这个分词。

  1. <div>use Lingua::ZH::Jieba;</div><div>binmode STDOUT, ":utf8";</div><div>
  2. </div><div>my $jieba = Lingua::ZH::Jieba->new();</div><div>$jieba->insert_user_word("中国人");</div><div>
  3. </div><div>#my $words_cutall = $jieba->cut("我来到北京清华大学,我是中国人", { cut_all => 1 } );</div><div>#my $words_cutall = $jieba->cut("我来到北京清华大学,我是中国人" );</div><div>my $words_cutall = $jieba->cut("我来到北京清华大学,我是中国人", { no_hmm => 1 } );</div><div>print join('/', @$words_cutall), "\n";</div>
复制代码

。。。。update 一下 2017/4/19  用lz的分词包写了一个文本比对的例子
  1. [root@L121 tmp]# perl jieba.pl
  2. Similar is :0.815374248327211
  3. [root@L121 tmp]# cat  jieba.pl
  4. use Lingua::ZH::Jieba;
  5. use Smart::Comments;
  6. binmode STDOUT, ":utf8";

  7. my ($txt1, $txt2) = ("我来到北京清华大学,我是中国人","我来到北京清华大学,中国人民很友好");

  8. my $jieba = Lingua::ZH::Jieba->new();
  9. my $words_cutall1 = $jieba->cut( $txt1, { cut_all => 1 } );
  10. my $words_cutall2 = $jieba->cut( $txt2, { cut_all => 1 } );
  11. #print join('/', @$words_cutall1), "\n";
  12. #print join('/', @$words_cutall2), "\n";

  13. use v5.10;
  14. my $all_list = get_all_words($words_cutall1, $words_cutall2);
  15. my %h_1 = get_words_count($words_cutall1);
  16. my %h_2 = get_words_count($words_cutall2);

  17. my $sum = 0;
  18. my $sum_1 = 0;
  19. my $sum_2 = 0;
  20. foreach my $k (get_all_words($words_cutall1, $words_cutall2)){
  21. $h_1{$k} = 0 unless exists $h_1{$k};
  22. $h_2{$k} = 0 unless exists $h_2{$k};
  23. $sum_1 += $h_1{$k} * $h_1{$k};
  24. $sum_2 += $h_2{$k} * $h_2{$k};
  25. $sum += $h_1{$k} * $h_2{$k};
  26. }

  27. say "Similar is :", $sum/(sqrt($sum_1) * sqrt($sum_2));

  28. sub get_words_count{
  29. my $words = shift;
  30. my %h;
  31. $h{$_}++ for @$words;
  32. return %h;
  33. }


  34. sub get_all_words{
  35. my ($words1, $words2) = @_;
  36. my @words = (@$words1, @$words2);
  37. my %h;
  38. $h{$_}++ for @words;
  39. return   keys %h;
  40. }
复制代码



作者: Okelani    时间: 2017-04-19 14:28
感.谢. l.z.
作者: zhouzhen1    时间: 2017-05-05 22:20
回复 3# L_WC

一段时间没上,忙成狗了。。不好意思你的两个问题我目前还不好回答,因为我只是wrap了CppJieba的项目(https://github.com/yanyiwu/cppjieba,它是Python Jieba用C++的一个实现),但是我并不了解其中的NLP原理。改天我研究一下再回复你吧。
作者: zhouzhen1    时间: 2017-06-04 17:34
回复 3# L_WC

我今天看了一下,关于你的两个问题大致上是这么回事。

1. extract的score是根据什么来的。

cppjieba的KeywordExtractor用的是称为tf-idf的方法。具体可以看 wikipedia上的说明 。简而言之,这个方法综合了两个数据,一个是tf(term frequency)即词语在目标文章中的出现量,词语出现次数越多该tf值越大,反映了词语在目标文章中的重要程度;另一个idf(inverse document frequency)是对大量文章的统计结果,一个词越是普通在各种文章出现的概率越大(比如英文中的the, a,中文的“你”、“我”这种),其idf值越小,这样可以压低这种泛用的词语,因为这种词语显然没必要做关键词。最后总的权重就是tf与idf的乘积。

我的package里面用的是cppjieba的字典数据,里面有一个文件就是idf数据,它应该是其作者从大量文档中统计出来的。

2. 在 $jieba->insert_user_word("中国人");  之后只有 {cut_all => 1} 模式才能输出 中国人 这个分词。

结巴分词用到了Trie作为运行时内部字典存储的数据结构,分词的过程是在整个Trie字典中找到用户给定句子的所有可能的DAG子图。再从这些子图中找出权重最大的结果。具体可以看网上的一些文章比如http://blog.sina.com.cn/s/blog_8267db980102wq41.html 以及http://blog.csdn.net/rav009/article/details/12196623

具体到“中国人”这个自定义词上,简而言之就是它的DAG可以是“中国/人”,也可以是连起来的“中国人”。而现在insert_user_word()方法在cppjieba的默认实现是以最小权重插入,这导致了“中国/人”这种分法的权重更大。至于文档例子中的“男默女泪”(我直接从cppjieba的例子中拿的)插入后会优先得到连起来的“男默女泪”,估计是因为“男默/女泪”的分法是通过一种算法猜测出来的,其优先级更低。







欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2