对于英文句子来说,可以通过空格
来切分单词,如
// 今天天气不错
the weather is nice today
可以很简单的把该句子中的单词区分出来
the/weather/is/nice/today
在中文里面,就没有那么方便的区分方法了。当然,如果你习惯这样说话:
今天 天气 不错
大家也不会打你,只会觉得你像个“结巴”
(点题了!)
在中文里面的字
和英文单词
是两个不同的东西。在读书的时候,最痛苦的一件事就是学习文言文,我想了一下,有大于等于三个原因:
我们常说中文博大精深,历史原因就不细究了,简单来说就是,我们的祖先在中文上的造诣非常高,好几层楼那么高,研究非常透彻,一句话能说出几个意思。我们自小在中文环境下成长,经过千锤百炼,读写是没问题的,但是计算机要怎么理解一句话呢?先从分词开始。
计算机学习分词的过程,和人类是很像的(或许这是局限性),目前有几种:
结巴分词
。我们学习中文的时候,也有这样的过程,
结巴分词
是国内程序员用python
开发的一个中文
分词模块, 源码被托管在Github
为了方便说明,下面截取了部分文档和例子。
# encoding=utf-8
import jieba
seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list)) # 全模式
seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list)) # 精确模式
seg_list = jieba.cut("他来到了网易杭研大厦") # 默认是精确模式
print(", ".join(seg_list))
seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造") # 搜索引擎模式
print(", ".join(seg_list))
输出:
【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学
【精确模式】: 我/ 来到/ 北京/ 清华大学
【新词识别】:他, 来到, 了, 网易, 杭研, 大厦 (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了)
【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造
HMM
模型,使用了 Viterbi
算法针对结巴分词的原理,网上的文章写的非常详细了,这里就不再赘述了。有兴趣的读者可以看看
有国人实现了PHP版本:
尤其是这个扩展jonnywang/phpjieba
实现,支持PHP7,果断安装了。
PHP的LNMP架构在Web开发领域常年占据一定的市场,那么是否可以使用结巴分词呢?当然可以,不过,我们知道在FPM模式下,PHP的生命周期非常短,前面我们了解到,结巴分词使用前缀字典树
建立词库,该操作需要一定的时间和耗费内存(默认词典dict.txt占用将近1G)。那么,在常规FPM模式下,假设开启8个worker,那就需要大约8G内存分配。而且,在应对大量请求时,频繁的申请/销毁操作并不合理。所以,在FPM模式下,使用结巴分词不合适。
我们想到,和应用强耦合在一起不是个好办法,把结巴分词独立出来作为一个公共服务,通过不同的接口(HTTP,unixsocket)给其他应用提供服务是个不错的方案。
在考察该方案前,我们需要解决几个问题:
我们第一时间想到了Swoole,有下面的优势:
SIGUSR1
信号,进行柔性重启(重启Worker进程)Base模式比Process模式少了两次ipc的过程,性能会更好些。
Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
Server Software: swoole-http-server
Server Hostname: 127.0.0.1
Server Port: 9501
Document Path: /
Document Length: 204 bytes
Concurrency Level: 1000
Time taken for tests: 8.499 seconds
Complete requests: 10000
Failed requests: 0
Keep-Alive requests: 10000
Total transferred: 3580000 bytes
Total body sent: 2180000
HTML transferred: 2040000 bytes
Requests per second: 1176.63 [#/sec] (mean)
Time per request: 849.883 [ms] (mean)
Time per request: 0.850 [ms] (mean, across all concurrent requests)
Transfer rate: 411.36 [Kbytes/sec] received
250.49 kb/s sent
661.86 kb/s total
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 5 14.1 0 69
Processing: 29 800 181.2 840 1260
Waiting: 4 800 181.2 840 1260
Total: 30 805 174.0 840 1275
Percentage of the requests served within a certain time (ms)
50% 840
66% 855
75% 866
80% 870
90% 894
95% 912
98% 1139
99% 1214
100% 1275 (longest request)
Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
Server Software: swoole-http-server
Server Hostname: 127.0.0.1
Server Port: 9501
Document Path: /
Document Length: 204 bytes
Concurrency Level: 1000
Time taken for tests: 4.746 seconds
Complete requests: 10000
Failed requests: 0
Keep-Alive requests: 10000
Total transferred: 3580000 bytes
Total body sent: 2180000
HTML transferred: 2040000 bytes
Requests per second: 2106.85 [#/sec] (mean)
Time per request: 474.643 [ms] (mean)
Time per request: 0.475 [ms] (mean, across all concurrent requests)
Transfer rate: 736.57 [Kbytes/sec] received
448.53 kb/s sent
1185.10 kb/s total
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 9 28.0 0 148
Processing: 0 415 407.8 421 1270
Waiting: 0 415 407.8 421 1270
Total: 0 423 409.2 443 1282
Percentage of the requests served within a certain time (ms)
50% 443
66% 822
75% 827
80% 830
90% 838
95% 850
98% 1157
99% 1225
100% 1282 (longest request)