选择工作平台

这里主要集中讲述,作者在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路上的第一步。后续将开始面对一些相对比较有难度的问题。这些问题将在后续的章节加以介绍。