我对语言识别的问题一直很感兴趣,记得10年做全网爬虫时同事写了一个简单的网页语言检测,比较简单,只是判断unicode code point是否有足够数量落在中文的code point上,连基本的html标签都没有去除,当时也没有测试效果,很可能会把日本,韩文网页也当做中文页面了.
最近学习solr/lucene,里面就带有language detection功能,可以使用两个库,一个是tika的language detection,另一个是日本人写的托管在google code上的language detection,后者支持的语言数更多(53种哦),很快借助google和源码学习了这个好东西,思路值得借鉴.
看作者的slideshare上的介绍,这就是使用朴素贝叶斯进行分类的经典案例,特征采用ngram(最高3),当然之前要去噪声(越南文要做个normalize,去掉url,email,对于拉丁字母占比小于1/3的直接去掉拉丁字母以免干扰),还有抽取的特征并不完全是这些去掉噪声的字符,它对字符进行分类之后当做feature,具体就是如,阿拉伯文统一一类不关心具体字符,汉语按照常用程度分为122类,韩文一类等等,都是不关心具体字符,而关心所属分类(当然也有很多字符是不做这些分类的,字符编码即是特征),可以解决一般识别器对中日韩分类不准确,以及语料库不充分造成误判的问题.
具体到贝叶斯的计算过程,也很聪明.
cleaningText(); ArrayList<String> ngrams = extractNGrams(); langprob = new double[langlist.size()]; Random rand = new Random(); if (seed != null) rand.setSeed(seed); for (int t = 0; t < n_trial; ++t) { double[] prob = initProbability(); double alpha = this.alpha + rand.nextGausian() * ALPHA_WIDTH; for (int i = 0;; ++i) { int r = rand.nextInt(ngrams.size()); updateLangProb(prob, ngrams.get(r), alpha); if (i % 5 == 0) { // 感觉上这里最好还是加上i > 0更好 if (normalizeProb(prob) > CONV_THRESHOLD || i >= ITERATION_LIMIT) break; if (verbose) System.err.println("> " + sortProbability(prob)); } } for (int j = 0; j < langprob.length; ++j) langprob[j] += prob[j] / n_trial; if (verbose) System.out.println("==> " + sortProbability(prob)) }随机采样,采样过程中如果发现有一种语言的概率是压倒性的直接停止,这样做很快速.
initProbability可以载入一个priorMap,严谨一点priorMap中各语言的概率之和应该是1,就是预设的各个语言的概率,updateLangProb直接将原概率乘以P(F|L)(F是feature,L是Language),这里的alpha是为了做平滑,防止出现*0之后直接变未0 的情况,normalizeProb则是将单语言的概率除以所有语言的概率.CONV_THRESHOLD是0.99999,的确是令人信服的阈值,理论上的确是practical了.
参考资料4是一个detector使用的例子,需要loadProfile载入语料库(corpus)得到probability map,源码中GenProfile.java可以生成profile供使用.
最后说一句,很佩服作者!
参考:
1.http://wiki.apache.org/solr/LanguageDetection
2.http://www.slideshare.net/shuyo/language-detection-library-for-java
3.http://code.google.com/p/language-detection/
4.http://www.cnblogs.com/makemelaugh/archive/2012/09/26/2704802.html