使用Google Web 1T 5-gram

时间:2021-01-01 03:11:29

转自:http://www.jiangfeng.me/blog/160

=======================================

最近的工作用到了Google的海量语言模型,因其海量,使用它时远没有想像当中那么简单。经过一周时间的探索,总算找到一种较为合理的方法,记录一下:-)

Google语言模型的一些基本情况和背景可以参考52nlp上的这篇介绍,以及LDC上的介绍Web 1T 5-gram Version 1。压缩的语言模型(1-5 gram)大小为24G,完全解压之后应该就是1T了。这么大的语言模型,无论是模型的训练(Google提供的是n-gram的计数文件)还是加载过程,几乎都不现实,我一想就头疼。对于一般用户而言,这么大的语言资源的确是收藏意义大于使用。

关于模型的训练

首先纠结的问题是要不要对其进行训练。用过SRILM的人都知道,SRILM可以将训练过程分割成两部分,先从Corpus生成计数文件,也就是Google所提供给我们的形式;再根据计数文件训练Language Model。而且一旦有了Model,就能很好很方便地满足我计算句子概率的需求,SRILM提供这样的接口。

可是最终我还是放弃了,心想如果只用到bigram或许还现实一点,更高阶的我心里没底,至少训练时间会很漫长,而且内存是最大的障碍,这道障碍,似乎无法逾越。值得一提的是:SRILM的FAQ中提供了一种针对google ngram的训练方法,读者不妨尝试一下:http://www-speech.sri.com/projects/srilm/manpages/srilm-faq.7.html (B6)
既然选择不训练语言模型,那么数据平滑以及概率、困惑度计算的工作就需要自己来完成了。这也并非难事,只要有ngram的频率信息,什么事情都好办。

如何有效地索引数据

一开始借鉴康维鹏师兄的方法,尝试使用Lucene对其进行索引。方法是将每个(ngram, count)作为一个Document。试验了unigram和bigram,效果还不错。只是处理起来比较麻烦,由于索引文件比源文件大30%左右,需要人为地将索引文件分成多个,Search时根据ngram的区间来确定读取哪个索引文件。缺点显而易见,比如对于2-gram和3-gram,这种方法重复存储了太多的内容。不过也不失为一种方法。

那能不能使用数据库呢?顺着这个思路我找到了Web1T5-Easy,也就是我现在所用的方法。Web1T5-Easy是一个德国人Stefan Evert用Perl写的将Google Web 1T ngram导入SQLite的工具包。试用了一下,还是非常不错的。下载地址

使用前需要安装DBI、DBD-SQLite,对版本要求比较严格。使用的一些细节,比如如何建库,如何查询等等,在README里写的很详细。下面简述一下过程:

第一步,创建词表。

  • perl perl/mk_vocab_db.perl –normalize 1gram/vocab.gz vocabulary.dat

normalize选项负责将所有字符串转换成小写,同时,将数字存储为“NUM”,标点符号存储为“PUN”,一些未知由“UNK”表示。这样做能一定程度上减少存储空间。另外,在创建词表的过程中,将每个词映射到一个整型的id,在之后的ngram数据表中就可以用id来表示,这也是一种非常简单有效的压缩手段。

第二步就是基于创建好的词表构建ngram数据库了。

  • perl perl/mk_ngram_db.perl –normalize –temp=${TMPDIR} 1 1-grams.dat vocabulary.dat
  • perl perl/mk_ngram_db.perl –normalize –temp=${TMPDIR} 2 2-grams.dat vocabulary.dat ${WEB1T5}/2gms/*.gz
  • perl perl/mk_ngram_db.perl –normalize –temp=${TMPDIR} 3 3-grams.dat vocabulary.dat ${WEB1T5}/3gms/*.gz
  • ……

目前暂时用到3-gram,在2.67GHz,16G内存的服务器上跑,一共用了两天时间。其中normalize过程占用了大部分的时间。工具包中同时提供查询脚本,可以方便地进行精确及模糊查找,以及简单的正则表达式查找。不过一旦使用正则表达式或者模糊查找,速度将急剧减缓。另外,Web1T5-Easy还提供一个简单的Web-Service,也非常不错。