MMSEG恐怕是最简单易实现而且效果还可以的基于字典的机械分词算法。http://nzinfo.spaces.live.com/Blog/cns!67694E0B61E3E8D2!344.entry
当前我的程序放在http://code.google.com/p/chinese-word-segmentaion/
网上有一个C++版本的源代码,但是我运行老是字典载入不成功。我看了下他的代码内部用的map而没有用hash map,这应该会影响效率。当然这个好改但是完全弄懂别人的代码很费劲,于是我自己重写了一个。另外那个网上的程序时用UTF8表示的,我的程序内部完全采用UTF16表示,利用u16string作为key.这样统一处理hash比较快,英文什么的也是UTF16所以处理方便。其余的输入文件完全可以用软件或者程序内部写代码转换成UTF16格式。
具体效率等等还有待优化验证。另外我没有完全实现MMSEG算法,当前简单实现了下所以暂时忽略了MMSEG之中的规则4.而且我的字典也是自己在网上随便找的一个处理了一下,词比较少算上单字一共775534个词。网上那个默认的字典应该是109228个词。
- 规则1:取最大匹配的chunk (Rule 1: Maximum matching)
- 规则2:取平均词长最大的chunk (Rule 2: Largest average word length)
- 规则3:取词长标准差最小的chunk (Rule 3: Smallest variance of word lengths)
- 规则4:取单字词自由语素度之和最大的chunk (Rule 4: Largest sum of degree of morphemic freedom of one-character words)
即使在当前比较粗糙的情况下,效果还是勉强凑合的,虽然由于有些词字典中不存在以及我没有实现规则4当前,影响了效果。我用平凡的世界验证了下(由于文字区分的问题还有点bug,主要是像这个文本中的"没有被识为标点,这个造成了段错误)。恩,大部分不对的地方是由于字典中缺少那个词,比如“快到”没有,“无终无影”没有,这个没办法字典里是“无影无踪”, “足不出户”也没有,连“响过"都没有,看来回头试试搜狗输入法的字典吧。
往往还没等落地
往$往还$没$等$落地$ 这个分的比较郁闷,因为词典里有 往往,也有 往还 , 最终这里选择了往还 ,“没等”这个词当前字典里没有:(
发这么一篇不成熟的东西是希望能和同样喜欢分词的朋友多交流。
当前的程序太粗糙了,因为是用深度优先暴力搜索索引复杂度很高,尤其是局长长度比较大的时候,当前机械式的将长句子中间割断,毕竟正常文本中的长句子很少。
就是用MMSEG算法的步骤1,2,3以及一个简单的字典我用SIGHAN 2005提供的测试语料测试了一下pku_test.txt.呵呵,结果很惨,不过毕竟是简单的开始,
希望有时间继续完善一下程序。
恩,速度很慢。。。 主要是算法实现的原因,当前指数级别的:)这个时间是全部时间包括读字典建立字典分词,写入到输出文件的全部时间。
pku_test_utf16是一个UTF16格式的文本大小为345K。
allen:~/study/my_project/chinese-word-segmentaion/build/bin$ time ./create_dict /mnt/hgfs/ubuntu.data/icwb2-data/testing/pku_test_utf16.txt
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from test_dict
[ RUN ] test_dict.perf
[ OK ] test_dict.perf (7436 ms)
[----------] 1 test from test_dict (7437 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (7441 ms total)
[ PASSED ] 1 test.
real 0m7.492s
user 0m5.904s
sys 0m1.120s
恩,正确度也比较惨,比较式初始版本。。 还有字典比较弱。。。
allen:/mnt/hgfs/ubuntu.data/icwb2-data$ perl scripts/score gold/pku_training_words.txt gold/pku_test_gold.txt result.unicode.text > score.txt
=== SUMMARY:
=== TOTAL INSERTIONS: 13007
=== TOTAL DELETIONS: 3195
=== TOTAL SUBSTITUTIONS: 13570
=== TOTAL NCHANGE: 29772
=== TOTAL TRUE WORD COUNT: 104372
=== TOTAL TEST WORD COUNT: 114184
=== TOTAL TRUE WORDS RECALL: 0.839
=== TOTAL TEST WORDS PRECISION: 0.767
=== F MEASURE: 0.802
=== OOV Rate: 0.058
=== OOV Recall Rate: 0.232
=== IV Recall Rate: 0.876
### result.unicode.text 13007 3195 13570 29772 104372 114184 0.839 0.767 0.802 0.058 0.232 0.876
我又换了一个更大一点的词典试了下,精度稍有提高到了83%
allen:~/study/my_project/chinese-word-segmentaion/build/bin$ time ./create_dict /mnt/hgfs/ubuntu.data/icwb2-data/testing/pku_test_utf16.txt
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from test_dict
[ RUN ] test_dict.perf
finshed set up dict
[ OK ] test_dict.perf (10693 ms)
[----------] 1 test from test_dict (10693 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (10694 ms total)
[ PASSED ] 1 test.
real 0m10.745s
user 0m8.365s
sys 0m1.680s
pku_test_utf16.txt是我将原测试文本转换成了UTF16格式,大小为345K,内容应该就是那个98人民日报社语料。
这样看来大概每秒30k,考虑到我的主机1G内存,虚拟机768M,虚拟机还很慢,算法还是暴力搜索指数级的,这个速度还可以了,
至少说明了SGI STL的 unorederd_map速度还是很快的:)
=== SUMMARY:
=== TOTAL INSERTIONS: 8061
=== TOTAL DELETIONS: 4066
=== TOTAL SUBSTITUTIONS: 10045
=== TOTAL NCHANGE: 22172
=== TOTAL TRUE WORD COUNT: 104372
=== TOTAL TEST WORD COUNT: 108367
=== TOTAL TRUE WORDS RECALL: 0.865
=== TOTAL TEST WORDS PRECISION: 0.833
=== F MEASURE: 0.849
=== OOV Rate: 0.058
=== OOV Recall Rate: 0.388
=== IV Recall Rate: 0.894
### result.unicode.text2 8061 4066 10045 22172 104372 108367 0.865 0.833 0.849 0.058 0.388 0.894
//用更新的词典运行对《平凡的世界》分词,效果如下,对比原始词典效果稍好
《 平凡 的 世界 》 作者 : 路 遥
第一章
1 9 7 5 年 二 、 三月 间 , 一个 平平常常 的 日子 , 细 蒙蒙 的 雨丝 夹 着 一星半点 的 雪花 , 正 纷纷 淋淋 地 向 大地 飘洒 着 。 时令 已 快 到 惊蛰 , 雪 当然 再 不会 存留 , 往 往还 没 等 落地 , 就 已经 消失 得 无 踪 无 影 了 。 黄土高原 严寒 而 漫长 的 冬天 看来 就要 过去 , 但 那 真正 温暖 的 春天 还 远远 地 没有 到来 。
在 这样 雨雪 交加 的 日子 里 , 如果 没有什么 紧 要事 , 人们 宁愿 一整天 足不出户 。 因此 , 县城 的 大街小巷 倒 也 比 平时 少 了 许多 嘈杂 。 街巷 背阴 的 地方 。 冬天 残留 的 积雪 和 冰 溜子 正在 雨点 的 敲击 下 蚀 化 , 石板 街上 到处 都 漫 流 着 肮脏 的 污水 。 风 依然 是 寒冷 的 。 空荡荡 的 街道 上 , 有 时会 偶尔 走 过来 一个 乡下人 , 破 毡帽 护 着 脑门 , 胳膊 上 挽 一 筐子 土豆 或 萝卜 , 有气无力 地 呼唤 着 买主 。 唉 , 城市 在 这样 的 日子 里 完全 丧失 了 生气 , 变得 没有 一点 可爱 之 处 了 。
只有 在 半山腰 县立 高 中的 大 院坝 里 , 此刻 却 自有 一番 热闹 景象 。 午饭 铃声 刚刚 响 过 , 从 一 排排 高低 错落 的 石 窑洞 里 , 就 跑 出来 了 一群 一 伙 的 男男女女 。 他们 把 碗筷 敲 得 震 天价 响 , 踏 泥 带 水 、 叫 叫 嚷嚷 地 跑 过 院坝 , 向 南面 总务处 那 一排 窑洞 的 墙根 下 蜂 涌 而 去 。 偌大 一个 院子 , 霎时 就 被 这 纷乱 的 人群 踩踏 成 了 一片 烂泥 滩 。 与此同时 , 那些 家 在 本 城 的 走读生 们 , 也 正 三三两两 涌出 东面 学校 的 大门 。 他们 撑 着 雨伞 , 一路 说说笑笑 , 通过 一 段 早年间 用 横 石片 插 起 的 长长的 下坡路 , 不多时 便 纷纷 消失 在 城市 的 大街小巷 中 。
在校园内 的 南 墙根 下 , 现在 已经 按 班级 排 起 了 十几 路 纵队 。 各 班 的 值日生 正在 忙碌 地 给 众人 分 饭菜 。 每个人 的 饭菜 都 是 昨天 登记 好 并 付 了 饭票 的 , 因此 程序 并不 复杂 , 现在 值日生 只是 按 饭 表 付给 每人 预订 的 一 份 。 菜 分 甲 、 乙 、 丙 三等 。 甲 菜 以 土豆 、 白菜 、 粉条 为主 , 里面 有些 叫 人 嘴 馋 的 大 肉片 , 每份 三 毛 钱 ; 乙 菜 其它 内容 和 甲 菜 一样 , 只是 没有 肉 , 每份 一 毛 五 分钱 。 丙 菜 可 就 差 远 了 , 清水 煮 白萝卜 似乎 只是 为了 掩饰 这 过分 的 清淡 , 才 在 里面 象征性 地 漂 了 几点 辣子 油花 。 不过 , 这 菜 价钱 倒 也 便宜 , 每份 五 分钱 。
各 班 的 甲 菜 只是 在 小 脸盆 里 盛 一点 , 看来 吃 得 起 肉 菜 的 学生 没有 几个 。 丙 菜 也 用 小 脸盆 盛 一点 , 说明 吃 这种 下等 伙食 的 人 也 没有 多少 。 只有 乙 菜 各 班 都 用 烧 瓷 大 脚盆 盛 着 , 海 海 漫漫 的 , 显然 大部分 人 都 吃 这种 既 不 奢侈 也不 寒酸 的 菜 。 主食 也 分 三等 : 白 面馍 , 玉米 面馍 , 高粱 面馍 ; 白 、 黄 、 黑 , 颜色 就 表 明了 一种 差别 ; 学生 们 戏称 欧洲 、 亚洲 、 非洲 。
//原始词典分词结果
下面是一个当前的结果,我先将文本分成了句子,利用一个句子iterator,然后对每个句子调用MMSEG算法分词.我的程序实现了对UTF16格式文本的读
//单字的iterator和读sentence的iterator
平凡$的$世界$
作者
作者$
路遥
路$遥$
第一章
第一章$
年二
年$二$
三月间
三月$间$
一个平平常常的日子
一个$平平常常$的$日子$
细蒙蒙的雨丝夹着一星半点的雪花
细$蒙蒙$的$雨丝$夹$着$一$星$半点$的$雪花$
正纷纷淋淋地向大地飘洒着
正$纷纷$淋$淋$地$向$大地$飘洒$着$
时令已快到惊蛰
时令$已$快$到$惊蛰$
雪当然再不会存留
雪$当然$再$不会$存留$
往往还没等落地
往$往还$没$等$落地$
就已经消失得无踪无影了
就$已经$消失$得$无$踪$无$影$了$
黄土高原严寒而漫长的冬天看来就要过去
黄土高原$严寒$而$漫长$的$冬天$看来$就$要$过去$
但那真正温暖的春天还远远地没有到来
但$那$真正$温暖$的$春天$还$远远$地$没有$到来$
在这样雨雪交加的日子里
在$这样$雨$雪$交$加$的$日子$里$
如果没有什么紧要事
如果$没有什么$紧$要事$
人们宁愿一整天足不出户
人们$宁愿$一$整天$足$不$出$户$
因此
因此$
县城的大街小巷倒也比平时少了许多嘈杂
县城$的$大街小巷$倒$也$比$平时$少$了$许多$嘈杂$
街巷背阴的地方
街$巷$背阴$的$地方$
冬天残留的积雪和冰溜子正在雨点的敲击下蚀化
冬天$残留$的$积$雪$和$冰$溜$子$正在$雨点$的$敲击$下$蚀$化$
石板街上到处都漫流着肮脏的污水
石板$街上$到处$都$漫$流$着$肮脏$的$污水$
风依然是寒冷的
风$依然$是$寒冷$的$
空荡荡的街道上
空荡荡$的$街道$上$
有时会偶尔走过来一个乡下人
有时$会$偶尔$走$过来$一个$乡下$人$
破毡帽护着脑门
破$毡$帽$护$着$脑门$
胳膊上挽一筐子土豆或萝卜
胳膊$上$挽$一$筐$子$土豆$或$萝卜$
有气无力地呼唤着买主
有气无力$地$呼唤$着$买主$
唉
唉$
城市在这样的日子里完全丧失了生气
城市$在$这样$的$日子$里$完全$丧失$了$生气$
变得没有一点可爱之处了
变得$没有$一点$可爱$之$处$了$
只有在半山腰县立高中的大院坝里
只有$在$半$山腰$县$立$高$中的$大院$坝$里$
此刻却自有一番热闹景象
此刻$却$自$有$一$番$热闹$景象$
午饭铃声刚刚响过
午饭$铃声$刚刚$响$过$
从一排排高低错落的石窑洞里
从$一排$排$高低$错落$的$石$窑洞$里$
就跑出来了一群一伙的男男女女
就$跑$出来$了$一群$一$伙$的$男$男女$女$
他们把碗筷敲得震天价响
他们$把$碗$筷$敲$得$震$天$价$响$
踏泥带水
踏$泥$带$水$
叫叫嚷嚷地跑过院坝
叫$叫嚷$嚷$地$跑$过$院$坝$
向南面总务处那一排窑洞的墙根下蜂涌而去
向$南面$总务$处$那$一排$窑洞$的$墙根$下$蜂$涌$而$去$
偌大一个院子
偌大$一个$院子$
霎时就被这纷乱的人群踩踏成了一片烂泥滩
霎$时$就$被$这$纷乱$的$人群$踩踏$成$了$一片$烂泥$滩$
与此同时
与此同时$
那些家在本城的走读生们
那些$家$在$本$城$的$走$读$生$们$
也正三三两两涌出东面学校的大门
也$正$三$三$两$两$涌$出$东面$学校$的$大门$
他们撑着雨伞
他们$撑$着$雨伞$
一路说说笑笑
一$路$说说$笑$笑$
通过一段早年间用横石片插起的长长的下坡路
通过$一$段$早$年间$用$横$石片$插$起$的$长$长$的$下坡路$
不多时便纷纷消失在城市的大街小巷中
不$多$时$便$纷纷$消失$在$城市$的$大街小巷$中$
在校园内的南墙根下
在校园内$的$南$墙根$下$
现在已经按班级排起了十几路纵队
现在$已经$按$班级$排$起$了$十几$路$纵队$
各班的值日生正在忙碌地给众人分饭菜
各$班$的$值日生$正在$忙碌$地$给$众人$分$饭菜$
每个人的饭菜都是昨天登记好并付了饭票的
每个人$的$饭菜$都$是$昨天$登记$好$并$付$了$饭$票$的$
因此程序并不复杂
因此$程序$并不$复杂$
现在值日生只是按饭表付给每人预订的一份
现在$值日生$只是$按$饭$表$付给$每人$预订$的$一$份$
菜分甲
菜$分$甲$
乙
乙$
丙三等
丙$三$等$
甲菜以土豆
甲$菜$以$土豆$
白菜
白菜$
粉条为主
粉$条$为主$
里面有些叫人嘴馋的大肉片
里面$有些$叫$人$嘴$馋$的$大$肉片$
每份三毛钱
每$份$三$毛$钱$
乙菜其它内容和甲菜一样
乙$菜$其它$内容$和$甲$菜$一样$
只是没有肉
只是$没有$肉$
每份一毛五分钱
每$份$一$毛$五$分钱$
丙菜可就差远了
丙$菜$可$就$差$远$了$
清水煮白萝卜似乎只是为了掩饰这过分的清淡
清水$煮$白萝卜$似乎$只是$为了$掩饰$这$过分$的$清淡$
才在里面象征性地漂了几点辣子油花
才$在$里面$象征性$地$漂$了$几点$辣子$油$花$
不过
不过$
这菜价钱倒也便宜
这$菜$价钱$倒$也$便宜$
每份五分钱
每$份$五$分钱$
各班的甲菜只是在小脸盆里盛一点
各$班$的$甲$菜$只是$在$小$脸盆$里$盛$一点$
看来吃得起肉菜的学生没有几个
看来$吃$得$起$肉$菜$的$学生$没有$几个$
丙菜也用小脸盆盛一点
丙$菜$也$用$小$脸盆$盛$一点$
说明吃这种下等伙食的人也没有多少
说明$吃$这种$下$等$伙食$的$人$也$没有$多少$
只有乙菜各班都用烧瓷大脚盆盛着
只有$乙$菜$各$班$都$用$烧$瓷$大脚$盆$盛$着$
海海漫漫的
海$海$漫$漫$的$
显然大部分人都吃这种既不奢侈也不寒酸的菜
显然$大部分$人$都$吃$这种$既$不$奢侈$也不$寒$酸$的$菜$
主食也分三等
主食$也$分$三$等$
白面馍
白面$馍$
玉米面馍
玉$米面$馍$
高粱面馍
高粱$面$馍$
白
白$
黄
黄$
黑
黑$
颜色就表明了一种差别
颜色$就$表$明了$一种$差别$
学生们戏称欧洲
学生$们$戏$称$欧洲$
亚洲
亚洲$
非洲
非洲$
从排队的这一片黑鸦鸦的人群看来
从$排队$的$这$一片$黑$鸦$鸦$的$人群$看来$
他们大部分都来自农村
他们$大部分$都$来自$农村$
脸上和身上或多或少都留有体力劳动的痕迹
脸$上$和$身上$或多或少$都$留$有$体力劳动$的$痕迹$
除过个把人的衣装和他们的农民家长一样土气外
除$过$个把$人$的$衣装$和$他们$的$农民$家长$一样$土$气$外$
这些已被自己的父辈看作是“先生”的人
这些$已$被$自己$的$父$辈$看作$是$“$先生$”$的$人$
穿戴都还算体面
穿戴$都$还$算$体面$