ukui-search遇到的问题:
目前来看内存占用过多主要由几个方面构成
- 中文分词库占用内存过多(主要)
- 处理大量数据时Qt可能会有内存释放不完全的问题(经测试,向QString中写入大量数据后,无法完全释放内存,原因暂时未知,目前使用多进程规避)
- 处理数据时可能会有多余的内存拷贝(零零碎碎,不会太多)
- xapian也有可能存在内存不释放,也暂时通过多进程规避
分词库的选择
jieba分词由Python开发而成,由其他开发者移植到了C++,或许他的分词效果很好,但速度和内存占用远远超过了我们的承受范围,趁春节几天算是试图入门NLP,也找找分词工具。
准确实用,7个优秀的开源中文分词库推荐 这位博主介绍了7个分词库,一一来看:
自己点链接看吧,链接是原版,用Python开发,后面有开发者用其他语言实现,我们就使用了cppjieba,其速度与内存超出了我们的承受范围。
看到HanLP提供了汉字转拼音接口,就随手试了一下。地址是https://www.hanlp.com/demonstrate.html,文本输入框中输入翟康宁
,功能选择拼音转换/汉字转拼音
,点击发送,返回结果如下:
{
"code": 0,
"data": [
{
"head": "d",
"pinyinWithToneMark": "dí",
"pinyinWithoutTone": "di",
"shengmu": "d",
"tone": 2,
"yunmu": "i"
},
{
"head": "k",
"pinyinWithToneMark": "kāng",
"pinyinWithoutTone": "kang",
"shengmu": "k",
"tone": 1,
"yunmu": "ang"
},
{
"head": "n",
"pinyinWithToneMark": "níng",
"pinyinWithoutTone": "ning",
"shengmu": "n",
"tone": 2,
"yunmu": "ing"
}
]
}
或许人名支持的不好,再试试多音字,这次发送的内容填仇姓来源于春秋宋国大夫仇牧
,看看结果是什么:
{
"code": 0,
"data": [
{
"head": "q",
"pinyinWithToneMark": "qiú",
"pinyinWithoutTone": "qiu",
"shengmu": "q",
"tone": 2,
"yunmu": "iu"
},
{
"head": "x",
"pinyinWithToneMark": "xìng",
"pinyinWithoutTone": "xing",
"shengmu": "x",
"tone": 4,
"yunmu": "ing"
},
{
"head": "l",
"pinyinWithToneMark": "lái",
"pinyinWithoutTone": "lai",
"shengmu": "l",
"tone": 2,
"yunmu": "ai"
},
{
"head": "y",
"pinyinWithToneMark": "yuán",
"pinyinWithoutTone": "yuan",
"shengmu": "y",
"tone": 2,
"yunmu": "uan"
},
{
"head": "y",
"pinyinWithToneMark": "yú",
"pinyinWithoutTone": "yu",
"shengmu": "y",
"tone": 2,
"yunmu": "u"
},
{
"head": "ch",
"pinyinWithToneMark": "chūn",
"pinyinWithoutTone": "chun",
"shengmu": "ch",
"tone": 1,
"yunmu": "un"
},
{
"head": "q",
"pinyinWithToneMark": "qiū",
"pinyinWithoutTone": "qiu",
"shengmu": "q",
"tone": 1,
"yunmu": "iu"
},
{
"head": "s",
"pinyinWithToneMark": "sòng",
"pinyinWithoutTone": "song",
"shengmu": "s",
"tone": 4,
"yunmu": "ong"
},
{
"head": "g",
"pinyinWithToneMark": "guó",
"pinyinWithoutTone": "guo",
"shengmu": "g",
"tone": 2,
"yunmu": "uo"
},
{
"head": "d",
"pinyinWithToneMark": "dài",
"pinyinWithoutTone": "dai",
"shengmu": "d",
"tone": 4,
"yunmu": "ai"
},
{
"head": "f",
"pinyinWithToneMark": "fū",
"pinyinWithoutTone": "fu",
"shengmu": "f",
"tone": 1,
"yunmu": "u"
},
{
"head": "ch",
"pinyinWithToneMark": "chóu",
"pinyinWithoutTone": "chou",
"shengmu": "ch",
"tone": 2,
"yunmu": "ou"
},
{
"head": "m",
"pinyinWithToneMark": "mù",
"pinyinWithoutTone": "mu",
"shengmu": "m",
"tone": 4,
"yunmu": "u"
}
]
}
第一个仇被识别出来了,第二个仇没有。
再试试繁体字(主要是看看他有几级字库),这次填長太息以掩涕兮,哀民生之多艱。余雖好修姱以鞿羁兮,謇朝谇而夕替。
,返回内容如下:
{
"code": 0,
"data": [
{
"head": "ch",
"pinyinWithToneMark": "cháng",
"pinyinWithoutTone": "chang",
"shengmu": "ch",
"tone": 2,
"yunmu": "ang"
},
{
"head": "t",
"pinyinWithToneMark": "tài",
"pinyinWithoutTone": "tai",
"shengmu": "t",
"tone": 4,
"yunmu": "ai"
},
{
"head": "x",
"pinyinWithToneMark": "xī",
"pinyinWithoutTone": "xi",
"shengmu": "x",
"tone": 1,
"yunmu": "i"
},
{
"head": "y",
"pinyinWithToneMark": "yǐ",
"pinyinWithoutTone": "yi",
"shengmu": "y",
"tone": 3,
"yunmu": "i"
},
{
"head": "y",
"pinyinWithToneMark": "yǎn",
"pinyinWithoutTone": "yan",
"shengmu": "y",
"tone": 3,
"yunmu": "an"
},
{
"head": "t",
"pinyinWithToneMark": "tì",
"pinyinWithoutTone": "ti",
"shengmu": "t",
"tone": 4,
"yunmu": "i"
},
{
"head": "x",
"pinyinWithToneMark": "xī",
"pinyinWithoutTone": "xi",
"shengmu": "x",
"tone": 1,
"yunmu": "i"
},
{
"head": "none",
"pinyinWithToneMark": "none",
"pinyinWithoutTone": "none",
"shengmu": "none",
"tone": 5,
"yunmu": "none"
},
{
"head": "a",
"pinyinWithToneMark": "āi",
"pinyinWithoutTone": "ai",
"shengmu": "none",
"tone": 1,
"yunmu": "ai"
},
{
"head": "m",
"pinyinWithToneMark": "mín",
"pinyinWithoutTone": "min",
"shengmu": "m",
"tone": 2,
"yunmu": "in"
},
{
"head": "sh",
"pinyinWithToneMark": "shēng",
"pinyinWithoutTone": "sheng",
"shengmu": "sh",
"tone": 1,
"yunmu": "eng"
},
{
"head": "zh",
"pinyinWithToneMark": "zhī",
"pinyinWithoutTone": "zhi",
"shengmu": "zh",
"tone": 1,
"yunmu": "i"
},
{
"head": "d",
"pinyinWithToneMark": "duō",
"pinyinWithoutTone": "duo",
"shengmu": "d",
"tone": 1,
"yunmu": "uo"
},
{
"head": "j",
"pinyinWithToneMark": "jiān",
"pinyinWithoutTone": "jian",
"shengmu": "j",
"tone": 1,
"yunmu": "ian"
},
{
"head": "none",
"pinyinWithToneMark": "none",
"pinyinWithoutTone": "none",
"shengmu": "none",
"tone": 5,
"yunmu": "none"
},
{
"head": "y",
"pinyinWithToneMark": "yú",
"pinyinWithoutTone": "yu",
"shengmu": "y",
"tone": 2,
"yunmu": "u"
},
{
"head": "s",
"pinyinWithToneMark": "suī",
"pinyinWithoutTone": "sui",
"shengmu": "s",
"tone": 1,
"yunmu": "ui"
},
{
"head": "h",
"pinyinWithToneMark": "hǎo",
"pinyinWithoutTone": "hao",
"shengmu": "h",
"tone": 3,
"yunmu": "ao"
},
{
"head": "x",
"pinyinWithToneMark": "xiū",
"pinyinWithoutTone": "xiu",
"shengmu": "x",
"tone": 1,
"yunmu": "iu"
},
{
"head": "k",
"pinyinWithToneMark": "kuā",
"pinyinWithoutTone": "kua",
"shengmu": "k",
"tone": 1,
"yunmu": "ua"
},
{
"head": "y",
"pinyinWithToneMark": "yǐ",
"pinyinWithoutTone": "yi",
"shengmu": "y",
"tone": 3,
"yunmu": "i"
},
{
"head": "j",
"pinyinWithToneMark": "jī",
"pinyinWithoutTone": "ji",
"shengmu": "j",
"tone": 1,
"yunmu": "i"
},
{
"head": "j",
"pinyinWithToneMark": "jī",
"pinyinWithoutTone": "ji",
"shengmu": "j",
"tone": 1,
"yunmu": "i"
},
{
"head": "x",
"pinyinWithToneMark": "xī",
"pinyinWithoutTone": "xi",
"shengmu": "x",
"tone": 1,
"yunmu": "i"
},
{
"head": "none",
"pinyinWithToneMark": "none",
"pinyinWithoutTone": "none",
"shengmu": "none",
"tone": 5,
"yunmu": "none"
},
{
"head": "j",
"pinyinWithToneMark": "jiǎn",
"pinyinWithoutTone": "jian",
"shengmu": "j",
"tone": 3,
"yunmu": "ian"
},
{
"head": "ch",
"pinyinWithToneMark": "cháo",
"pinyinWithoutTone": "chao",
"shengmu": "ch",
"tone": 2,
"yunmu": "ao"
},
{
"head": "s",
"pinyinWithToneMark": "suì",
"pinyinWithoutTone": "sui",
"shengmu": "s",
"tone": 4,
"yunmu": "ui"
},
{
"head": "e",
"pinyinWithToneMark": "ér",
"pinyinWithoutTone": "er",
"shengmu": "none",
"tone": 2,
"yunmu": "er"
},
{
"head": "x",
"pinyinWithToneMark": "xī",
"pinyinWithoutTone": "xi",
"shengmu": "x",
"tone": 1,
"yunmu": "i"
},
{
"head": "t",
"pinyinWithToneMark": "tì",
"pinyinWithoutTone": "ti",
"shengmu": "t",
"tone": 4,
"yunmu": "i"
},
{
"head": "none",
"pinyinWithToneMark": "none",
"pinyinWithoutTone": "none",
"shengmu": "none",
"tone": 5,
"yunmu": "none"
}
]
}
繁体字可以识别,但好
和朝
的音是错的,果然多音字是一大问题。。。
回到分词,《自然语言处理入门》是本好书,对于学习NLP有很大帮助,其作者何晗老师的HanLP也很强大,可惜没有C/C++实现,而且内存占用最少120MB(数据来自官网)。
作者狮子的魂,friso也是他写的,看看人家。不过看了一下这个项目之后突然觉得friso是阉割版,只有3种模式,而jcseg有7中模式,特别是最多模式,NLP模式和n-gram模式,MMSEG要学习学习,争取搞个C++版的。
后面的几个不想看了,都是go和Java的,不可能因为这个分词搞个JDK上去,还有几个C/C++的分词库:ICTCLAS,中科院出品,这个很牛逼,但商用要恰钱的。
c++ 中文分词介绍,这篇文章介绍了几种C++的中文分词库,有中科院的分词、LibMMSeg、SCWS等等。怕他哪一天没了,粘贴过来。
本文不是专业的介绍中文的知识,只是由于项目上可能需要中文分词,在网上找了一些资料,再次记录一下。 主要参考网站是oschina里面收录的内容: 中科院中文分词 ICTCLAS 这个据说效率挺高,但不是纯开源版本,里面有个文章12年7月1日失效,就是由于授权协议失效,所以要用到这个类库的时候要小心了,当然你可以购买版权。 还有一个重要的问题是官网打不开。http://www. ictclas .org 协议未知 中文分词软件包 LibMMSeg LibMMSeg 是Coreseek.com为 Sphinx 全文搜索引擎设计的中文分词软件包,其在 GPL协议 下发行的中文分词法,采用 Chih-Hao Tsai 的MMSEG算法。 LibMMSeg 采用C++开发,同时支持Linux平台和Windows平台,切分速度大约在500K/s(酷睿 2.4G);截至目前版本LibMMSeg没有为速度做过特殊优化,进一步的提升切分速度仍有空间。 官方网站http://www.coreseek.cn/opensource/mmseg/ 简易中文分词系统 SCWS 这是一套基于词频词典的机械中文分词引擎,采用的是自行采集的词频词典,并辅以一定程度上的专有名称、人名、地名、数字年代等规则集, SCWS 采用纯 C 代码开发,以 Unix-Like OS 为主要平台环境,提供共享函数库,方便植入各种现有软件系统。此外它支持 GBK,UTF-8,BIG5 等汉字编码 效率:准确: 95%, 召回: 91%, 速度: 1.2MB/sec 协议: BSD 许可协议开源发布 最新版本 2013-1-15: SCWS-1.2.1 Released. 中文句法分析器 ctbparser 一个用C++实现的 中文句法分析 工具包,采用的是中文宾州树库标准(Chinese Tree Bank),该句法分析工具采用了标准的图模型算法,即最大生成树算法(projective Maximum Spanning Tree)。 效率:ctbparser分词得到95.3% F1值,词性标注精度94.27%,句法分析精度81%。处理速度(包括分词、词性标注、句法分析)的速度是每秒30句,内存占用为270M。(操作系 统:64位CentOS 5,CPU: Intel(R) Xeon(R) E5405, 2.00GHz) 授权协议: LGPL 最新版本:2012-2-3 0.12版本 中文分词开源版 CRF CRF中文分词开源版仅仅包含CRF++软件包中分词解码器部分,简化了CRF++复杂代码结构,清除了分词解码器不需要的代码,大大提高了分词解码器的可读性和可懂度。 技术支持:http://langiner.blog.51cto.com/ 2010-08-20之后没有更新 授权协议:非开源 最新版本未知 中文分词库 NlpBamboo bamboo是一个中文语言处理系统。目前包括中文分词和词性标注部分。算法字构词的分词方法CRF++。 效率未知 网站:http://code.google.com/p/nlpbamboo/ 授权协议: BSD 最新版本 :2010-10 1.1.1版本,官方说明短期后面不会更新 C语言开源高性能中文分词器 friso friso中文分词器friso是使用c语言开发的一个开源中文分词器,使用流行的mmseg算法实现。 1。目前最高版本:friso 0.1,只支持UTF-8编码。【源码无需修改就能在各种平台下编译使用,加载完20万的词条,内存占用稳定为14M。】。 2。mmseg四种过滤算法,分词准确率达到了98.41%。 3。支持自定义词库。在dict文件夹下,可以随便添加/删除/更改词库和词库词条,并且对词库进行了分类。 4。词库使用了friso的Java版本jcseg的简化词库。 5。支持中英混合词的识别。例如:c语言,IC卡。 7。很好的英文支持,电子邮件,网址,小数,分数,百分数。 8。支持阿拉伯数字基本单字单位的识别,例如2012年,5吨,120斤。 9。自动英文圆角/半角,大写/小写转换。 二。分词速度 测试环境:2.8GHZ/2G/Ubuntu 简单模式:3.7M/秒 复杂模式:1.8M/秒 授权协议: LGPL 最新版本:2012-12-28日 综合来看 简易中文分词系统 SCWS和C语言开源高性能中文分词器 friso是在最近更新的,应该最近会有支持。 仅仅是个人理解。 下面是网上的一个人的评论: 但市场上提供免费甚至开源的分词引擎不多,中科院研发的ictclas30分词精确度和分词速度都非常不错,而且还有词性标注和自定义添加词的功能,可惜不开源。另外比较受欢迎的还有libmmseg和SCWS,因此都是开源的,不过经测试libmmseg的分词精度似乎不高,而SCWS由于使用了大量的递归,在生成词库的时候经常导致栈溢出(我是用vc2005编译的),需要自己将递归修改为循环,从演示的情况来看,SCWS的分词精度来算可以。 附录:编码转换 最新版的iconv(目前是libiconv-1.14.tar.gz)已经不再提供Visual C++的编译文件,最后一个支持VC编译的iconv版本是libiconv-1.11.1.tar.gz。虽然不是最新版,但是在Windows下编译,只好将就了,功能应该差不了多少。 下载libiconv-1.11.1.tar.gz并解压,打开命令提示行,进入libiconv-1.11.1目录,输入命令: nmake -f Makefile.msvc NO_NLS=1 MFLAGS=-MD nmake -f Makefile.msvc NO_NLS=1 MFLAGS=-MD PREFIX=C:\iconv install
懒得排版了,就酱8。。。 综合来看SCWS、LibMMSEG、friso这三个比较符合要求,但根据上面的博主的测试来看,SCWS需要把递归改成for循环,而且递归的效率。。。因为ukui-search是搜索工具,对算法的精度没有那么高(但最好也别太低),LibMMSEG上面也说了速度有待优化,如果博主说的没错的话,就暂定为friso,对于friso的源码的阅读与理解,后面再补。
QString为什么不能完全释放内存
这个占个坑,估计弄明白要另搞一篇。。。
多余的内存拷贝
这个属于对C++的使用和理解了,占坑
关于xapian的详细内容
占坑+1
原创文章转载请注明出处: 中文分词库简要对比