这里主要集中讲述,作者在NLP(自然语言处理)方面相关的工作和结果。有关NLP相关的定义、文章、成果均可以在网上找到,这里不再赘述。作者所讲述的内容将分成几个部分,如需要展开的部分作者将单独写文章予以补充。
这些将部分包括:工作平台的选择、语料的处理、算法的实现和个人感悟、工具系统的介绍等几个部分。
一、工作平台的选择
工具平台的选择主要取决于计划要达到的目标和目的。选择比较流行的Python平台,可以在GitHub上找到很多工具包和资源,利于迅速搭建和测试。选择C/C++平台,可获得较高的效率,主要面对最终运行系统的成型。作者在这里选择了完全基于SQLServer平台。这里的完全是指,除了数据还包括程序代码。选择SQLServer平台的主要有以下几点原因:
- NLP相关的开发需要存储大量的数据。这些数据的保存,处理和备份最好依赖于一个成熟的平台。这样方案成熟,工具齐全,避免出现问题时,被搞得很狼狈。
- 相关的程序代码需要和数据集中存放,数据和代码要可以随时从备份中恢复,或者快速转移至其他设备上。很多时候,程序不经意的错误可能会导致大量数据作废,或者很多数据需要重新计算,经常备份是不可避免的。
- 可以在后台提供计算进程,无需单独书写后台任务。由于需要处理的数据量很大,更多情况下,计算需要很长时间,可以按照周来计算。如果平台可以提供专门的任务系统,这样可以不用单独书写任务计划,只需要直接部署即可。在后台运算的同时,前台的工作可以继续进行。
- 将来的数据和算法迁移会比较方便。将来如果数据和算法可投入实用,那么该平台应可以比较方便的扩展数据应用至其他平台。
基于这几点,作者选择了SQLServer 2019 Developer版本。这个版本基本没有什么限制,而且对于个人单机应用来说,是免费提供的,很适合目前的目标方案。在这里顺带提下作者曾经遇到过的状况。
- 搜集整理到的数据量很大:语料库(总计:数据记录4, 094, 469条,共749, 953, 358个Unicode),词典库(数据记录总计:17, 647, 045条,共110, 431, 336个Unicode)。mdf文件5.3G左右,ldf文件3.6G左右。
- 仅在计算词频统计上,就花费了将近10多天的时间。统计程序是在后台运行,占CPU大约12%左右,内存几乎占满。
- 反复调试和测试有关NLP程序时,经常需要重新进行计算处理。有很多问题是在大数据量样本处理时,才能暴露出来。
- 用于开发的计算机,因突然断电导致磁盘分区的块损坏。最后导致其中一个语料库表根本无法读取(读取时,Windows直接蓝屏)。最后利用之前数据库备份,才得以完全恢复至崩溃前的状态,有些数据就需要重新计算了。
当然选择SQLServer作为平台,也不是没有缺点,这里也谈下作者曾经遇到过的几个问题:
- 最初以SQLServer Express作开发平台,后来发现Express版本根本不支持SQLServer Agent,即不支持SQLServer的作业机制。改用SQLServer Developer版本后,这个问题得到解决。
- 刚开始开发时,没有建立合适的索引,导致计算效率奇低。每小时仅能处理1000多条语料断句,而且越跑越慢。在建立了合适的索引后,速度和效率就上去了(每小时能处理10000条语料断句)。但后来,又因建立了过多没用的索引,导致数据文件巨大,曾经达到过120G,备份文件都能达到50G。因此数据表的设计还是需要仔细考虑下。
- 在缺省所选字符集下,SQLServer对全角和半角不区分,有好处也有问题。在与外部正则处理模块对接时就出现了问题。外部正则处理模块可是区分全角和半角的。另外,SQLServer的字符串位置描述从1起,外部正则处理模块的字符串位置描述从0起。不了解这些细节差异会非常困扰程序的开发。
- SQLServer Management Studio 18不支持函数和存储过程的Debug调试,需要到Visual Studio 2019里面才可以调试。另外,自定义函数不允许打印信息。这种情况下会增加程序排错的难度,对开发者的素质要求比较高。
- 要传递表参数的时候,如果使用表作为参数,那么表只能是ReadOnly。因此,程序中大量使用了XML对象以及XQuery操作。在做解析时,则需要注意内容中所含特殊字符的转义问题。
- 语料库中含有一些不可见字符,会干扰字符串函数的准确执行以及XML解析。另外LEN函数的特点,可能会导致一些奇特的现象。如果不熟悉,这些细节问题会很困扰开发者。
无论选择如何的平台进行开发,合适的才是最好的。所谓合适,就是符合自己目标和预期结果的。不要一味追求某些不切实际的东西,避免浪费时间。
二、语料的处理
由于个人无法完成最初语料的准备工作,因此更多情况下是借助互联网的“前辈”们所提供的资料作为最初的语料资源。作者从网络上搜集了尽可能多的公开语料,存入到数据库中,另外也自己手工录入和导入了部分语料数据。
语料可以分为两大类:一类是计划作为词典使用的语料,另外一类是计划作为被分析对象使用的语料。
2.1词典语料
词典语料主要来源于网络“前辈”们的一些项目。作者下载后经过分类整理,大致的情况如下(总计:17, 647, 045条):
统计类别 | 统计数量 |
---|---|
财经 | 3846 |
餐饮 | 331797 |
常用词 | 156828 |
成语 | 50448 |
地点 | 44804 |
电影 | 12985 |
动漫 | 29605 |
动物 | 17287 |
法律 | 9896 |
分词21万 | 217375 |
分词30万 | 298032 |
分词35万 | 342525 |
公司 | 4847739 |
公司缩写 | 284839 |
古名 | 252053 |
股票 | 14763 |
基本词汇 | 696469 |
计算机 | 15289 |
历史 | 785158 |
名录 | 292971 |
名人 | 13658 |
农牧 | 46003 |
汽车 | 4153 |
亲近名 | 184716 |
日文名 | 185528 |
社会科学 | 271595 |
生活常识 | 689362 |
诗词 | 13703 |
食物 | 8974 |
搜狗 | 802755 |
体育 | 46992 |
网游 | 490812 |
网站 | 5231 |
文学 | 1729685 |
现代汉语词典 | 65144 |
新华字典 | 264434 |
行业 | 111 |
姓名 | 1145009 |
姓氏 | 1074 |
医学 | 262582 |
应用工程 | 387088 |
英文单词 | 103976 |
英文名 | 490003 |
娱艺 | 398421 |
职业 | 7569 |
自然科学 | 235864 |
组织机构 | 1087894 |
词典里面的语料只能说比较粗糙,有的做了拼音标记,有的做了解释注解,有的做了词性标记。精准程度远远达不到要求。在后续的使用过程中,其实关闭了很多“无效”的词条(大约关闭了80%以上)。有很多我甚至怀疑这些词典都是通过词频算出来的,根本不符合阅读习惯。
在词典语料中,作者参考工具词典,手工输入和补充了汉语常用的副词、连词、介词、叹词、助词、拟声词、量词、单位等。这些相对标准的词汇是和外部输入语料单独分开存储。
2.2被分析语料
被分析语料主要来源于网络新闻,网络小说,网络文学作品等。这其中包括了已公开和整理好的新闻资料库(如:搜狐新闻数据)。互联网上还流传着800G的网络小说文本库。语料虽多,但同时意味着计算量超大,以及存储空间是否够用的问题。遇到这种情况不宜求多,而更应集中于求精。毕竟这么多资料就像一堆原矿石,要提炼出所需的金属,那个过程是漫长的,这中间要丢掉很多矿渣。
目前读者只收集和整理了部分用于被分析的语料(总计:数据记录4, 094, 469条)。这些语料不都是由单句组成的,更多的是按照段落被导入数据库的。后者才是更常见的情况,所以还需要进行进一步的断句处理。
被分析语料往往有着很明显的来源倾向。作者收集的这些语料中,出现频率最高的词汇是:“公司”、“记者”。在分析这些语料的过程中可以明显感觉到如下特点:
- 新闻类语料多。多以“****报讯”,“记者****”开头。
- 股票、基金、公司、经济、体育类新闻偏多,所用词语不贴近生活。
- 还有大量可以说没什么用处的彩票分析、电脑推荐等资料。几乎完全与实际生活脱节。
- 虽然在语料库中,作者补充了不少文学作品(包括儿童童话、经典小说和世界名著),但是相对于原语料库还是偏少。
2.3语料的处理
SQLServer提供了外部数据导入功能,只要能把语料整理成合适的格式,就能比较顺利的导入到数据库中。这里需要注意以下几点:
- 文本编码。原始语料虽然都是.txt文件,但是其编码格式可能有好几种。最好能统一至SQLServer可以支持的字符集下,包括:GBK,Unicode。
- 分段处理。有些原始语料自行进行了分段处理,更多的是选择的是自然分段。这种利于导入和分析,有些原始语料选择了强制分段处理,这样可能会制造很多“断句”,这样的语料可以暂时放到一边。毕竟能用的语料太多了,不应在乎丢掉一些不方便处理的东西,不要把时间浪费在一些小事情上。
- 不可见字符。这个不可见字符分两类:一类不可见字符,在导入后,可以通过TRIM或者REPLACE裁剪掉;还有一类不可见字符,TRIM或者REPLACE对其完全无效。对于后者需要用稍微特殊的办法可以清除。这个可以查询网络上的相关文章。
- 导入时的数据类型和长度限制。推荐使用NVARCHAR(MAX)来实现导入,大致一次可以导入8000个Unicode。选择导入时,可以使用文本流进行导入。
- 导入完成后,不建议在语料库类做过多“清洗”。可能多数人都为了方便后续的开发和使用,会对这个库中的数据做一些预处理。这里需要注意几个问题:(1)做任何预处理之前,都需要对这个库或者这个表做备份。因为预处理的错误可能会导致语料库进入手工完全无法修复的“混乱”状态。甚至因为为了修正之前的错误,导致更加“混乱”的状态。(2)应当把这个库中的数据当作原始数据,它的状态更加接近与将来要面对的各种可能的输入。因此作者对这个库仅做过简单处理,其他更加复杂的处理并未施加在这些原始数据上。另外,也把这些数据当作对后续程序测试的一个多样性保证。
如果能顺利完成以上两个任务,基本算是完成NLP路上的第一步。后续将开始面对一些相对比较有难度的问题。这些问题将在后续的章节加以介绍。