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} 模式才能输出 中国人 这个分词。
<div>use Lingua::ZH::Jieba;</div><div>binmode STDOUT, ":utf8";</div><div>
</div><div>my $jieba = Lingua::ZH::Jieba->new();</div><div>$jieba->insert_user_word("中国人");</div><div>
</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的分词包写了一个文本比对的例子
[root@L121 tmp]# perl jieba.pl
Similar is :0.815374248327211
[root@L121 tmp]# cat jieba.pl
use Lingua::ZH::Jieba;
use Smart::Comments;
binmode STDOUT, ":utf8";
my ($txt1, $txt2) = ("我来到北京清华大学,我是中国人","我来到北京清华大学,中国人民很友好");
my $jieba = Lingua::ZH::Jieba->new();
my $words_cutall1 = $jieba->cut( $txt1, { cut_all => 1 } );
my $words_cutall2 = $jieba->cut( $txt2, { cut_all => 1 } );
#print join('/', @$words_cutall1), "\n";
#print join('/', @$words_cutall2), "\n";
use v5.10;
my $all_list = get_all_words($words_cutall1, $words_cutall2);
my %h_1 = get_words_count($words_cutall1);
my %h_2 = get_words_count($words_cutall2);
my $sum = 0;
my $sum_1 = 0;
my $sum_2 = 0;
foreach my $k (get_all_words($words_cutall1, $words_cutall2)){
$h_1{$k} = 0 unless exists $h_1{$k};
$h_2{$k} = 0 unless exists $h_2{$k};
$sum_1 += $h_1{$k} * $h_1{$k};
$sum_2 += $h_2{$k} * $h_2{$k};
$sum += $h_1{$k} * $h_2{$k};
}
say "Similar is :", $sum/(sqrt($sum_1) * sqrt($sum_2));
sub get_words_count{
my $words = shift;
my %h;
$h{$_}++ for @$words;
return %h;
}
sub get_all_words{
my ($words1, $words2) = @_;
my @words = (@$words1, @$words2);
my %h;
$h{$_}++ for @words;
return keys %h;
}
复制代码
作者:
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