转载 中文分词详解与其应用概述

我,人懒,代码写了不少,文章没写多少。
今,为落伍,所以写点技术性文字,不知道有没有人看。

这里,我没打算把所有代码贴出,其实也是我代码写的差,贴出来会污染环境。
但,如果你会代码的话,看了我的思路,自己写也快,不懂的,给代码也没用,而思路能让你对分词有所了解。
这里抛玉引砖,引金砖!千万别往我身上拍砖。

马上入题……

分词作用简述:(更详细的见Google)

何为中文分词?
把 “何为中文分词” 分为 “何” “为” “中文” “分词” 这就是中文分词。

有什么用?
人工智能:   让机器怎么理解你的话?你就是要告诉笨机器 中文词,一句话它是无法理解的。
搜索:   用”中文的几种分词技术“ 来搜到 ”中文分词的几种技术“   (你的网站的文章搜索可以做到吗?Google、百度为什么可以?)
文章自动归类: 类人工智能,分析词频,接着什么贝叶斯等等,让电脑自动学习。
文章排重:   文章重复不只是标题一样,标题一样的也未必重复。可以用词频分析的办法来排重,这个比自动归类容易多了。
翻译:   中英如何对译? 不是字,是词。
等等……

-----只想知道什么是分词的朋友,谢谢你们看我的粗糙文字,大家可以走了。下面讲分词方法,可以不看了。

常见的分词方法有哪些?
有基于字符串匹配、基于统计的分词、基于理解的分词(我想中科院这方面比海量牛吧)
其中基于理解的分词最难,基于字符串匹配最容易。基于统计的分词需要一个词频库,这东西不好弄。
我个人当然是小打小闹,能实行简单的分词,搜索下文章,排除些重复内容。所以只讲”基于字符串匹配“

基于字符串匹配常用的有:正向最大匹配,逆向最大匹配,后者正确率高于前者。(写文字累,不展开太多了,但还是举几个列子帮助理解吧)
1。 ”中华人民共和国“   可分为一个词,也可分为”中华“ ”人民“ ”共和国“,实践表明用 最大匹配法 正确率明显高于 最小匹配(实际上最小匹配几乎不用)
2。 ”技术和服务“   可分为 ”技术“ ”和“ ”服务“(逆向)   也可分为 ”技术“ ”和服“ ”务“(正向),实践表明用 逆向最大匹配法 正确率要高于 正向最大匹配
现在大家自动那种效率好了吧,如果结合”的,了“等噪声词可写出更准确的程序

------分词也描述完了,下面开是程序的编写思路了,如果分词对你没用的朋友也可以离开了,离开前帮顶下哦。

再次说明下:我这个小程序是用搜索(分词加MSSQL全文搜索),与自动排重(提取关键信息)的
有经验的我们一起交流,没接触过的又想掌握的请耐心往下看,可能要云里雾里的,因为我的文字粗糙。

分词程序需要一个词库,好的词库我们买不起(几千到上万RBM),我还是用免费的拼音加加的词库,需要的自己下。
我的开发语言是 C# ,外加一个免费词库(词库格式就是每个词一行),由于用于网站信息管理,所以还有mssql

下面分两点讲,分词程序和自动排重(分词看不明白的可以看自动排重,不关联)、搜索只是简单的结合分词来应用MSSQL的全文搜索这里不讲。

-------分词 -------自认为上面的不足以做为申请落伍的砝码,请老大看下面

我这里讲解最简单的”正向最大匹配“,最后一句带过我的“逆向最大匹配法”思路。

首先大家可能想到:这还不简单,逐字的拿来和中文词匹配下不就得了?
是得,这是可以得,也是最常见得方法(这里我不讲这种),但问题是“中国”是词“中华人民共和国”也是词,你需要决定中文词最大长度是多少,3?5?8? (一般取5或6,小了不准确,高了性能非常差)

有没有其它方法?
1。 有,这么想,假如对“中华人民共和国站起来了”,我们能知道 “中” 在词库里面又没有 做过第一个字的词(如:中间),没有就可以认为 ”中X“(X代表未读入的字,也可以认为是任意字) 肯定不是词,指针后移再去比较。如果有,者还不能肯定 “中X” 是词,但有机会是。所以再 读入一个字 “中华” 继续类似的找,如果词库里面有 “中华”为开头的词,就再继续,直到“中华人民共和国站” 就没有那样开头的词了,所以可以认为“中华人民共和国”是一个词。--这就是正向最大匹配

2。继续深入考虑,万一对“中华人民共和三个火 星人民交往了”(这里不这么通,反正例子),我们按上面思路,在词库里找到有以“中华人民共和”开头的词后,接着就在词库里找不到了以”中华人民共和三“ 开头的词了。按上面方法我们就可以认为”中华人民共和“是个词了,(这样的错误率不算太高,如果你可以容忍,那请跳过下面两段话后继续)

3。怎么解决这个问题?你可能马上想到,再比较下认为是词的 ”中华人民共和“ 在词库里有没有完整的词,好办法,那就用这办法试试,结果发现 完整的”中华人民共和“ 这词,所以认为”中华人民共和“不是词。

4。那什么才是词呢?回退,是的,我们减少读入的字,变为”中华人民共“,仍然找不到,再退 ”中华人民“ 终于找到了。所以认为”中华人民“是词了。(这个看词库,如果你认为词库里没这词,那就是“中华”才是最后分出来的词)

结束了?没有,思路讲完了,但大家有想过性能吗?假设词库有10万条词,你分的文章1万字,那要多久才能完成?

性能有什么决定?算法?对,但上面算法的优点是简单容易实现,而且性能也还算可以。那词库呢?是的,我们上面只提词库,还没考虑怎么组织词库在内存里面的形式,总不能每比较一次都打开词库读吧。

那 词库怎么组织?用数组?非计算机专业出生的都容易这么想(我也是非计算机专业,也曾这么想过),答案是否定的,大家都知道数据库索引的作用(还不清楚的, 请添加10W条记录,再select就明白了),那我们也来个索引,再具体点,选择最常用的 哈希表 (不知道的就认为它可以提高查找性能的就行了)。

考 虑上面的 1。 的思路,我们很显然要把 每个词 逐渐分解 (如:“中国人"要分为”中“ ”中国“ ”中国人“,不明白再回去看 1。) 同时,考虑到特定的应用(搜索和排重),我认为单字成词的都可以扔到不要,所以可以把“中国人"只分为 ”中国“ ”中国人“(前提是我不需要单字成词的字,我认为词都是有两个字以上组成的),这样词库又优化了不少吧!这里我们可以以分解后的字为 哈希表 的key,值随便,可以是1。再考虑,可以在把 key(也就是分解后的字的长度)为关键字,而以分解后的字为key的哈希表为值再词分组。
最后的结构如下图:

|key:2,value:HashTable|key:3,value:HashTable|key:4,value:HashTable|……
               ┃
      ┎──┸──────┰───────┰─────┄┄┄┄┄
|key:中国,value:1|key:中间,value:1|key:关于,value:1|

上图,只给了 长度是 2 的分解词库所得的子串的表结构, 长度是 3 的类似, key就是”中国人“,”中间物“等等。

如 果要考虑 4。 的思路,那上面的哈希表还不够,因为没办法判断那个词是词库里存在的完整词(因为分解词后,没保留完整词的信息)。写到这里我突然想到可以用把完整的词的 value设为2,如|key:中国,value:2|而|key:中国人民共,value:1| (哈,刚刚想到的,写文章对思路有好处)。
我本来的做法是再建一张哈希表,当然性能会比这个好,但内存也要比这个耗一点,这里再提下我3分钟以前的想法,再建张HashTable,只存词库里存在的完整词,而上面的分解词的HashTable就不要完整的词条了。

词库也组织好了,再回到程序上,首先当然是HTML过滤啦。
接着就是匹配中文部分(英文可单独提取出来处理,这个正则可以搞定),给正则部分代码
// 中文分词 单字成词的已舍弃
//([^\u4E00-\u9FFF]+) 匹配所有 非中文字符(英文、数字、标点)
reg = new Regex(@"([\u4E00-\u9FFF]+)");
foreach (Match m in reg.Matches(s)){//对m进行分词就是罗}
再下面就是对每个匹配都进行分词,分词按上面思路,只要逐次查找在HashTbale里面有没有该Key,对应的value是不是2。就可以分出来了。(细节自己理解下,再考虑考虑)

----------最后提下逆向匹配和排重,如果你能坚持住看我那么多枯燥的文字,实在是太感激了。

逆向最大匹配:可以把构词库的HashTable里的每个Key都倒序,如”中国“变成”国中“(先倒序好再加入哈希表哦:))
分词是也倒序读入(不要先倒好序再从前往后读入,而是直接从后面往前面读),去查哈希表。明白了吧。性能很不错的。
如果为了提高 查全率,可以把两种分词都用上,这样把”技术和服务“ 分为 ”技术“ ”和服“ ”服务“

排重:先对文章内容分好词,按词频排好序,取前面的N个(取个6、7、8个就很够了,不要太多)高频词,更具高频词生成MD5序列(这样可以提高性能哦,海量的思路提醒我的)如果MD5相同,就认为重复了。
这个对付那些从不同地方采集过来的,文章又有可能被别人稍微修改过一些的,标题也不同的。但实际内容确是相同的文章很有效,绝对推荐。