从n-gram中文文本纠错,到依存树中文语法纠错以及同义词查找

为什么80%的码农都做不了架构师?>>>
前记
本文简单地讲解如何使用n-gram模型结合汉字拼音来作中文错别字纠错,然后介绍最短编辑距离在中文搜索纠错方面的应用;最后从依赖树入手讲解如何作文本长距离纠错(语法纠错),并从该方法中得到一种启示,利用依赖树的特点结合ESA算法来做同义词的查找 。
n-gram模型
在中文错别字查错情景中,我们判断一个句子是否合法可以通过计算它的概率来得到,假设一个句子S = {w1, w2, ..., wn},则问题可以转换成如下形式:
P(S)被称为语言模型,即用来计算一个句子合法概率的模型 。
但是使用上式会出现很多问题,参数空间过大,信息矩阵严重稀疏,这时就有了n-gram模型,它基于马尔科夫模型假设,一个词的出现概率仅依赖于该词的前1个词或前几个词,则有
(1)一个词的出现仅依赖于前1个词,即(2-gram):
(2)一个词的出现仅依赖于前2个词,即(3-gram):
【从n-gram中文文本纠错,到依存树中文语法纠错以及同义词查找】当n-gram的n值越大时,对下一个词的约束力就越强,因为提供的信息越多,但同时模型就越复杂,问题越多,所以一般采用或 。下面举一个简单的例子,说明n-gram的具体使用:
n-gram模型通过计算极大似然估计()构造语言模型,这是对训练数据的最佳估计,对于其计算公式如下:
对于一个数据集,假设count(wi)统计如下(总共8493个单词):

从n-gram中文文本纠错,到依存树中文语法纠错以及同义词查找

文章插图
而count(wi, wi-1)统计如下:
则概率矩阵计算如下:
句子“I want to eatfood”成立的概率为:
P(I want to eatfood) = P(I) * P(want|I) * P(to|want) * P(eat|to) * P(|eat) * P(food|)
= (2533/8493) * 0.33 * 0.66 * 0.28 * 0.021 * 0.52 。
从n-gram中文文本纠错,到依存树中文语法纠错以及同义词查找

文章插图
接下来我们只需要训练确定一个阀值,只要概率值≥阀值就认为句子合法 。
为了避免数据溢出、提高性能,通常会使用取log后使用加法运算替代乘法运算,即
log(p1*p2*p3*p4) = log(p1) + log(p2) + log(p3) + log(p4)
可以发现,上述例子中的矩阵存在0值,在语料库数据集中没有出现的词对我们不能就简单地认为他们的概率为0,这时我们采用拉普拉斯矩阵平滑,把0值改为1值,设置成该词对出现的概率极小,这样就比较合理 。
有了上面例子,我们可以拿n-gram模型来做选择题语法填空,当然也可以拿来纠错 。中文文本的错别字存在局部性,即我们只需要选取合理的滑动窗口来检查是否存在错别字,下面举一个例子:
我们可以使用n-gram模型检查到“穿”字打错了,这时我们将“穿”字转换成拼音“chuan”,再从词典中查找“chuan”的候选词,一个一个试填,用n-gram检查,看是否合理 。这就是n-gram模型结合汉字拼音来做中文文本错别字纠错了 。汉字转拼音可以使用Java库。
import net.sourceforge.pinyin4j.PinyinHelper;import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;public class Keyven {public static void main(String[] args) {HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);String str = "我爱自然语言处理,Keyven";System.out.println(str);String[] pinyin = null;for (int i = 0; i < str.length(); ++i) {try {pinyin = PinyinHelper.toHanyuPinyinStringArray(str.charAt(i),format);} catch (BadHanyuPinyinOutputFormatCombination e) {e.printStackTrace();}if (pinyin == null) {System.out.print(str.charAt(i));} else {if (i != 0) {System.out.print(" ");}System.out.print(pinyin[0]);}}}}