免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3978 | 回复: 4

基于Ruby和中文分词的贝叶斯反垃圾解决方案 [复制链接]

论坛徽章:
0
发表于 2011-11-24 14:11 |显示全部楼层
本帖最后由 so_brave 于 2011-11-24 14:12 编辑

基于Ruby和中文分词的贝叶斯反垃圾解决方案








贝叶斯过滤技术是非常有效的反垃圾技术。在Ruby中,有几个Library实现了贝叶斯过滤,可惜的是它们都不支持中文分词。而mmseg则是比较流行的中文分词技术,在Ruby框架下,有rmmseg这个Library实现了mmseg分词法并自带了词库,非常方便。本文将这两种技术结合在一起,使得在Ruby下可以获得支持中文的贝叶斯过滤器。

安装及试用classifier

首先来通过简单的使用来熟悉一下贝叶斯反垃圾引擎,这里我们使用的gem叫做classfier,项目地址位于 "https://github.com/kitop/classifier":https://github.com/kitop/classifier

使用gem命令安装这个library:


Bash代码
  1. 1.sudo gem install classifier -v=1.3.3  
  2. 2.Password:   
  3. 3.Successfully installed classifier-1.3.3  
  4. 4.1 gem installed   
  5. 5.Installing ri documentation for classifier-1.3.3...   
  6. 6.Installing RDoc documentation for classifier-1.3.3...  
  7. sudo gem install classifier -v=1.3.3
  8. Password:
  9. Successfully installed classifier-1.3.3
  10. 1 gem installed
  11. Installing ri documentation for classifier-1.3.3...
  12. Installing RDoc documentation for classifier-1.3.3...
复制代码
为了方便说明,所以明确指定了使用1.3.3这个版本。但是这个版本有一个BUG,因此我们需要打个补丁。找到gem的安装目录:


Bash代码
  1. 1.gem list -d classifier   
  2. 2.[b]*[/b]LOCAL GEMS[b]*[/b]   
  3. 3.classifier (1.3.3)   
  4. 4.    Author: Lucas Carlson   
  5. 5.    Homepage: http://classifier.rufy.com/   
  6. 6.    Installed at: /Library/Ruby/Gems/1.8  
  7. 7.  
  8. 8.    A general classifier module to allow Bayesian and other types of   
  9. 9.    classifications.  
  10. gem list -d classifier
  11. [b]*[/b]LOCAL GEMS[b]*[/b]
  12. classifier (1.3.3)
  13.     Author: Lucas Carlson
  14.     Homepage: http://classifier.rufy.com/
  15.     Installed at: /Library/Ruby/Gems/1.8

  16.     A general classifier module to allow Bayesian and other types of
  17.     classifications.
复制代码
我的gem位于/Library/Ruby/Gems/1.8, 因此我进入这个目录,找到gems子目录,里面应该有classifier-1.3.3/
找到lib/classifier/bayes.rb这个文件,在45行下面添加一行代码:


Ruby代码
  1. 1.orig ||= 0  
  2. orig ||= 0
复制代码
修改后代码如下所示:

1.jpg



这样,我们便装好了这个gem,打开irb玩玩看:


Ruby代码
  1. 1.irb   
  2. 2.>> require 'classifier'  
  3. 3.=> true  
  4. 4.>> checker = Classifier::Bayes.new('spam', 'not spam')   
  5. 5.=> #<Classifier::Bayes:0x1010e9cd0 @total_words=0, @categories={:Spam=>{}, :"Not spam"=>{}}>   
  6. 6.>> checker.train :not_spam, "It's a good day!"  
  7. 7.=> {:"!"=>1, :it=>1, :"'"=>1, :good=>1, :dai=>1}   
  8. 8.>> checker.train :spam, "buy buy buy!"  
  9. 9.=> {:"!"=>1, :bui=>3}   
  10. 10.>> checker.classify "What a lovely day~"  
  11. 11.=> "Not spam"  
  12. 12.>> checker.classify "Buy some apple?"  
  13. 13.=> "Spam"  
  14. 14.>>   
  15. irb
  16. >> require 'classifier'
  17. => true
  18. >> checker = Classifier::Bayes.new('spam', 'not spam')
  19. => #<Classifier::Bayes:0x1010e9cd0 @total_words=0, @categories={:Spam=>{}, :"Not spam"=>{}}>
  20. >> checker.train :not_spam, "It's a good day!"
  21. => {:"!"=>1, :it=>1, :"'"=>1, :good=>1, :dai=>1}
  22. >> checker.train :spam, "buy buy buy!"
  23. => {:"!"=>1, :bui=>3}
  24. >> checker.classify "What a lovely day~"
  25. => "Not spam"
  26. >> checker.classify "Buy some apple?"
  27. => "Spam"
  28. >>
复制代码
可以看到贝叶斯引擎的原理是将句子拆分成词,并根据词频来进行归类判断,因此样本数量越大判断越准确。通过上面的例子,我们可以看到classifier这个库是通过自然空格对英文句子进行断句分词,很遗憾这种方式并不适用于中文。

classfier与中文的不兼容性

我们通过一个例子来说明classifier与中文的不兼容性。首先创建一个txt文档,写入一些中文,可将文件保存为sample.txt:
sample.txt

Bash代码  
1.人类在社会发展中,对于自然世界的认识和精神世界里的追求,源远流长,形成了巨大的精神财富,如文学、艺术、教育、科学等,这些以文字或符号加以记载和传播,就形成了我们所说的文化。历史上,尽管各民族的文化差异很大,但一项重大的科学成就,常常能够影响整个世界文化发展的进程。  
人类在社会发展中,对于自然世界的认识和精神世界里的追求,源远流长,形成了巨大的精神财富,如文学、艺术、教育、科学等,这些以文字或符号加以记载和传播,就形成了我们所说的文化。历史上,尽管各民族的文化差异很大,但一项重大的科学成就,常常能够影响整个世界文化发展的进程。


进入irb终端,试着将classfier应用于这段文字:


Bash代码
  1. 1. irb   
  2. 2.>> require 'classifier'  
  3. 3.=> true   
  4. 4.>> checker = Classifier::Bayes.new('spam', 'not spam')   
  5. 5.=> #<Classifier::Bayes:0x1010e9c30 @total_words=0, @categories={:Spam=>{}, :"Not spam"=>{}}>   
  6. 6.>> File.open('sample.txt').each_line{ |s| p checker.train :not_spam, s   
  7. 7.>>  }   
  8. 8.{:"\344\272\272\347\261\273..."=>1}   
  9. 9.=> #<File:sample.txt>   
  10. 10.>>   
  11. irb
  12. >> require 'classifier'
  13. => true
  14. >> checker = Classifier::Bayes.new('spam', 'not spam')
  15. => #<Classifier::Bayes:0x1010e9c30 @total_words=0, @categories={:Spam=>{}, :"Not spam"=>{}}>
  16. >> File.open('sample.txt').each_line{ |s| p checker.train :not_spam, s
  17. >>  }
  18. {:"\344\272\272\347\261\273..."=>1}
  19. => #<File:sample.txt>
  20. >>
复制代码
可以看到,classifier只生成了一个词频为1的大字串,把全段文字当成是一个单词保存了起来,这段文字并没有被正确地拆成中文词语并进行正确的词频统计。原因在于classfier仅内置了英文的拆词法,即通过空格来拆分单词,这样的方式显然是无法用于中文的,因此为了能让中文内容也用上classifier库,我们首先要做的是使其能将中文段落正确的拆成词,RMMSeg的用处就在这里。

RMMSeg的安装及使用

而mmseg是很流行的中文分词算法,在ruby下有中文分词器的实现RMMSeg,使用它的好处是它完全用ruby实现,并且自带词库,十分方便。我们先来了解一下如何使用这个分词器,首先是用gem来安装它:


Bash代码
  1. 1.sudo gem install rmmseg  
  2. sudo gem install rmmseg
复制代码
成功安装后,我们仍然可以通过IRB试用下这个library。进入irb终端,测试一下RMMSeg是否可以正常工作,仍然使用前面创建的中文文件sample.txt来做测试:


Bash代码
  1. 1. irb   
  2. 2.>> require 'rmmseg'  
  3. 3.=> true   
  4. 4.>> include RMMSeg   
  5. 5.=> Object   
  6. 6.>> File.open('sample.txt').each_line{ |s| p segment(s) }   
  7. 7.["人类", "在", "社会", "发展中", ",", "对于", "自然", "世界", "的", "认识", "和", "精神世界", "里", "的", "追求", ",", "源远流长", ",", "形成", "了", "巨大", "的", "精神", "财富", ",", "如", "文学", "、", "艺术", "、", "教育", "、", "科学", "等", ",", "这些", "以", "文字", "或", "符号", "加以", "记载", "和", "传播", ",", "就", "形成", "了", "我们", "所说", "的", "文化", "。", "历史上", ",", "尽管", "各民族", "的", "文化", "差异", "很大", ",", "但", "一项", "重大", "的", "科学", "成就", ",", "常常", "能够", "影响", "整个", "世界文化", "发展", "的", "进程", "。"]   
  8. 8.=> #<File:sample.txt>  
  9. irb
  10. >> require 'rmmseg'
  11. => true
  12. >> include RMMSeg
  13. => Object
  14. >> File.open('sample.txt').each_line{ |s| p segment(s) }
  15. ["人类", "在", "社会", "发展中", ",", "对于", "自然", "世界", "的", "认识", "和", "精神世界", "里", "的", "追求", ",", "源远流长", ",", "形成", "了", "巨大", "的", "精神", "财富", ",", "如", "文学", "、", "艺术", "、", "教育", "、", "科学", "等", ",", "这些", "以", "文字", "或", "符号", "加以", "记载", "和", "传播", ",", "就", "形成", "了", "我们", "所说", "的", "文化", "。", "历史上", ",", "尽管", "各民族", "的", "文化", "差异", "很大", ",", "但", "一项", "重大", "的", "科学", "成就", ",", "常常", "能够", "影响", "整个", "世界文化", "发展", "的", "进程", "。"]
  16. => #<File:sample.txt>
复制代码
可以看到,通过RMMSeg的segment命令,一段中文文字被切成了词。接下来的工作,是改造classifier库的断字部分,让它使用RMMSeg提供的中文断字能力。

改造classifier,集成RMMSeg,使之支持中文分词

classifier的分词代码位于其函数库根目录的lib/classifier/extensions/word_hash.rb中。我们打开这个文件,在代码一开始添加:


Ruby代码
  1. 1.require 'rmmseg'  
  2. 2.include RMMSeg  
  3. require 'rmmseg'
  4. include RMMSeg
复制代码
然后找到word_hash这个函数,将基于空格的分词方法改成RMMSeg给出的segment方法进行分词:


Ruby代码
  1. 1.def word_hash   
  2. 2.    #word_hash_for_words(gsub(/[^\w\s]/,"").split + gsub(/[\w]/," ").split)   
  3. 3.    word_hash_for_words(segment(gsub(/[^\w\s]/,"")) + gsub(/[\w]/," ").split)   
  4. 4.end  
  5. def word_hash
  6.         #word_hash_for_words(gsub(/[^\w\s]/,"").split + gsub(/[\w]/," ").split)
  7.         word_hash_for_words(segment(gsub(/[^\w\s]/,"")) + gsub(/[\w]/," ").split)
  8. end
复制代码
修改后的代码如下所示:
2.jpg

3.jpg


改造完成后,我们便可以用刚才的sample.txt重新做下测试,看看classifier是否可以针对中文正常工作了:


Bash代码
  1. 1.irb   
  2. 2.>> require 'classifier'  
  3. 3.=> true   
  4. 4.>> checker = Classifier::Bayes.new('spam', 'not spam')   
  5. 5.=> #<Classifier::Bayes:0x10109c318 @categories={:Spam=>{}, :"Not spam"=>{}}, @total_words=0>   
  6. 6.>> File.open('sample.txt').each_line{ |s| p checker.train :not_spam, s   
  7. 7.>> }   
  8. 8.{:重大=>1, :我们=>1, :科学=>2, :源远流长=>1, :发展中=>1, :世界文化=>1, :各民族=>1, :符号=>1, :如=>1, :认识=>1, :成就=>1, :所说=>1, :等=>1, :形成=>2, :,=>9, :发展=>1, :差异=>1, :加以=>1, :文学=>1, :和=>2, :常常=>1, :文化=>2, :这些=>1, :了=>2, :对于=>1, :进程=>1, :很大=>1, :记载=>1, :、=>3, :精神世界=>1, :人类=>1, :能够=>1, :。=>2, :以=>1, :巨大=>1, :自然=>1, :但=>1, :传播=>1, :艺术=>1, :里=>1, :在=>1, :影响=>1, :历史上=>1, :文字=>1, :精神=>1, :世界=>1, :一项=>1, :就=>1, :教育=>1, :追求=>1, :社会=>1, :整个=>1, :尽管=>1, :或=>1, :财富=>1, :的=>7}   
  9. 9.=> #<File:sample.txt>   
  10. 10.>>   
  11. irb
  12. >> require 'classifier'
  13. => true
  14. >> checker = Classifier::Bayes.new('spam', 'not spam')
  15. => #<Classifier::Bayes:0x10109c318 @categories={:Spam=>{}, :"Not spam"=>{}}, @total_words=0>
  16. >> File.open('sample.txt').each_line{ |s| p checker.train :not_spam, s
  17. >> }
  18. {:重大=>1, :我们=>1, :科学=>2, :源远流长=>1, :发展中=>1, :世界文化=>1, :各民族=>1, :符号=>1, :如=>1, :认识=>1, :成就=>1, :所说=>1, :等=>1, :形成=>2, :,=>9, :发展=>1, :差异=>1, :加以=>1, :文学=>1, :和=>2, :常常=>1, :文化=>2, :这些=>1, :了=>2, :对于=>1, :进程=>1, :很大=>1, :记载=>1, :、=>3, :精神世界=>1, :人类=>1, :能够=>1, :。=>2, :以=>1, :巨大=>1, :自然=>1, :但=>1, :传播=>1, :艺术=>1, :里=>1, :在=>1, :影响=>1, :历史上=>1, :文字=>1, :精神=>1, :世界=>1, :一项=>1, :就=>1, :教育=>1, :追求=>1, :社会=>1, :整个=>1, :尽管=>1, :或=>1, :财富=>1, :的=>7}
  19. => #<File:sample.txt>
  20. >>
复制代码
可以看到,中文被正确地拆分并加以词频统计了。我们得到了一个支持中文的贝叶斯反垃圾引擎。

试用支持中文的classifier

接下来可以玩玩看classifier是否能正确区分开垃圾内容非垃圾内容了,我们分别创建一个垃圾内容样本spam.txt及非垃圾内容样本not_spam.txt:
spam.txt

Bash代码
  1. 1.咆哮体!伤不起!这就是咆哮体!!啊啊!!啊啊!!!有木有!!!有木有啊!!!!!   
  2. 咆哮体!伤不起!这就是咆哮体!!啊啊!!啊啊!!!有木有!!!有木有啊!!!!!
复制代码
not_spam.txt

Bash代码
  1. 1.人类在社会发展中,对于自然世界的认识和精神世界里的追求,源远流长,形成了巨大的精神财富,如文学、艺术、教育、科学等,这些以文字或符号加以记载和传播,就形成了我们所说的文化。历史上,尽管各民族的文化差异很大,但一项重大的科学成就,常常能够影响整个世界文化发展的进程。  
  2. 人类在社会发展中,对于自然世界的认识和精神世界里的追求,源远流长,形成了巨大的精神财富,如文学、艺术、教育、科学等,这些以文字或符号加以记载和传播,就形成了我们所说的文化。历史上,尽管各民族的文化差异很大,但一项重大的科学成就,常常能够影响整个世界文化发展的进程。
复制代码
接下来创建一个待分检垃圾样本test.txt:
test.txt

Bash代码
  1. 1.老子两年前选了法语课!!!!!!!!于是踏上了尼玛不归路啊!!!!!!!!!!谁跟老子讲法语是世界上最油煤的语言啊!!!!!!!!尼玛听的哪个外太空的法语啊!!!!!!!!!!!跟吐痰一样一样一样的啊 有木有!!!!!!!!!谁再跟老子讲法语是世界上最油煤的语言 老子一口浓痰咸死你啊!!!!!!!!!!!!!!!   
  2. 老子两年前选了法语课!!!!!!!!于是踏上了尼玛不归路啊!!!!!!!!!!谁跟老子讲法语是世界上最油煤的语言啊!!!!!!!!尼玛听的哪个外太空的法语啊!!!!!!!!!!!跟吐痰一样一样一样的啊 有木有!!!!!!!!!谁再跟老子讲法语是世界上最油煤的语言 老子一口浓痰咸死你啊!!!!!!!!!!!!!!!
复制代码
我们会将spam.txt里面的内容标记为垃圾内容,将not_spam.txt里面的内容标记为非垃圾内容。而test.txt里面的内容是待测试内容,我们期待的结果是classifier将其识别为垃圾内容,即Spam。下面是测试过程及结果:


Bash代码
  1. 1. irb   
  2. 2.>> require 'classifier'  
  3. 3.=> true   
  4. 4.>> checker = Classifier::Bayes.new('spam', 'not spam')   
  5. 5.=> #<Classifier::Bayes:0x10109c1d8 @categories={:Spam=>{}, :"Not spam"=>{}}, @total_words=0>   
  6. 6.>> File.open('spam.txt').each_line{ |s| p checker.train :spam, s }   
  7. 7.{:伤=>1, :木=>2, :不起=>1, :这就=>1, :咆哮=>2, :是=>1, :体=>2, :啊=>5, :!=>17, :有=>4}   
  8. 8.=> #<File:spam.txt>   
  9. 9.>> File.open('not_spam.txt').each_line{ |s| p checker.train :not_spam, s }   
  10. 10.{:重大=>1, :我们=>1, :科学=>2, :源远流长=>1, :发展中=>1, :世界文化=>1, :各民族=>1, :符号=>1, :如=>1, :认识=>1, :成就=>1, :所说=>1, :等=>1, :形成=>2, :,=>9, :发展=>1, :差异=>1, :加以=>1, :文学=>1, :和=>2, :常常=>1, :文化=>2, :这些=>1, :了=>2, :对于=>1, :进程=>1, :很大=>1, :记载=>1, :、=>3, :精神世界=>1, :人类=>1, :能够=>1, :。=>2, :以=>1, :巨大=>1, :自然=>1, :但=>1, :传播=>1, :艺术=>1, :里=>1, :在=>1, :影响=>1, :历史上=>1, :文字=>1, :精神=>1, :世界=>1, :一项=>1, :就=>1, :教育=>1, :追求=>1, :社会=>1, :整个=>1, :尽管=>1, :或=>1, :财富=>1, :的=>7}   
  11. 11.=> #<File:not_spam.txt>   
  12. 12.>> File.open('test.txt').each_line{ |s|  p checker.classify s }   
  13. 13."Spam"  
  14. 14.=> #<File:test.txt>   
  15. 15.>>   
  16. irb
  17. >> require 'classifier'
  18. => true
  19. >> checker = Classifier::Bayes.new('spam', 'not spam')
  20. => #<Classifier::Bayes:0x10109c1d8 @categories={:Spam=>{}, :"Not spam"=>{}}, @total_words=0>
  21. >> File.open('spam.txt').each_line{ |s| p checker.train :spam, s }
  22. {:伤=>1, :木=>2, :不起=>1, :这就=>1, :咆哮=>2, :是=>1, :体=>2, :啊=>5, :!=>17, :有=>4}
  23. => #<File:spam.txt>
  24. >> File.open('not_spam.txt').each_line{ |s| p checker.train :not_spam, s }
  25. {:重大=>1, :我们=>1, :科学=>2, :源远流长=>1, :发展中=>1, :世界文化=>1, :各民族=>1, :符号=>1, :如=>1, :认识=>1, :成就=>1, :所说=>1, :等=>1, :形成=>2, :,=>9, :发展=>1, :差异=>1, :加以=>1, :文学=>1, :和=>2, :常常=>1, :文化=>2, :这些=>1, :了=>2, :对于=>1, :进程=>1, :很大=>1, :记载=>1, :、=>3, :精神世界=>1, :人类=>1, :能够=>1, :。=>2, :以=>1, :巨大=>1, :自然=>1, :但=>1, :传播=>1, :艺术=>1, :里=>1, :在=>1, :影响=>1, :历史上=>1, :文字=>1, :精神=>1, :世界=>1, :一项=>1, :就=>1, :教育=>1, :追求=>1, :社会=>1, :整个=>1, :尽管=>1, :或=>1, :财富=>1, :的=>7}
  26. => #<File:not_spam.txt>
  27. >> File.open('test.txt').each_line{ |s|  p checker.classify s }
  28. "Spam"
  29. => #<File:test.txt>
  30. >>
复制代码
可以看到,test.txt中的内容按照预期被识别为垃圾内容。

参考资料

http://www.iteye.com/news/1380 - robbin写的rmmseg介绍

https://github.com/logankoester/classifier - classifier项目网址

http://rmmseg.rubyforge.org/ - rmmseg文档

http://baike.baidu.com/view/4169613.htm - 咆哮体
1.jpg
2.jpg
3.jpg

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
发表于 2011-11-24 14:14 |显示全部楼层
好高级

论坛徽章:
0
发表于 2011-11-24 14:27 |显示全部楼层
不错,是原创么?我也搞过bayes,不过不是用ruby。

论坛徽章:
0
发表于 2011-11-25 12:29 |显示全部楼层
不错不错

论坛徽章:
0
发表于 2011-11-27 00:32 |显示全部楼层
ding
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP