一个文本纠错的小例子

文本纠错又称为拼写错误或者拼写检查,由于纯文本往往来源于手打或者OCR识别,很可能存在一些错误,因此此技术也是一大关键的文本预处理过程,一般存在两大纠错类型。

Non-word拼写错误

第一种是Non-word拼写错误,表示此词汇本身在字典中不存在,比如把“要求”误写为“药求”,把“correction”误拼写为“corrction”。

操作步骤:
这类问题的解决思路可分为两个步骤,首先找到字典中与错拼词汇相近的词作为候选词,接着基于特定算法找出与错拼词关联最高的一个或多个单词作为纠正选项。例如,对于错误单词“atress”,存在多个相近候选词[“actress”,“caress”,“stress”,“across”,“cress”],接下来通过算法计算得“actress”为纠正选项。那么如何确定候选项呢?对于英文而言比较简单,通过编辑距离运算(即对单词中的字母进行增删改操作)便可得到一系列相近候选词汇。而对于中文来说,存在两种相近模式,一是拼写相近,比如在拼音打字时出错,也适用于编辑距离,二则是字形相近,比如在五笔打字时出错,一般需要通过构建相近字词表查找候选词汇。而有了候选列表后,如何选出最有可能的纠正选项呢?可以根据贝叶斯定理,得到如下表达式:
P(候选项|错误单词)∝ P(候选项)* P(错误单词|候选项)
也就是说,某一候选项的可能性,取决于“此选项本身在语料中出现的可能性”和“人们意图打候选项时会错打成错误单词的可能性”的乘积。前者可视为uni-gram语言模型,需要计算词语的出现频次,当然也可以扩张至二阶或三阶,比如计算“错误单词左边单词+候选项+错误单词右边单词”在语料中的出现情况,以便更好地考虑语境信息;而后者需要基于历史的错误项、纠正项相对应的语料进行概率统计。

Real-word拼写错误
第二种情况是Real-word拼写错误,意思是指单词本身没有错误,但是不符合上下文语境,常常涉及语法语义层面的错误,比如把“我现在在公司里”错写成“我现在在公式里”,这类错误计算量较大,因为每个单词都是待纠错对象。
操作步骤
通常的解决方案与第一种情况类似,首先针对每个单词根据编辑距离、同音词、近形词等方式选出候选项(也包括单词本身);接下来计算基于候选项的语言模型,以及在候选项情况下出现错误单词的条件概率;如果综合计算而得单词本身出现在此语境中的概率较大,则不进行纠正,否则推荐纠正项。

相关知识点
综合而言,纠错涉及到的知识点有贝叶斯定理、语言模型、编辑距离、词表构建、语料统计等基础技术。接下来我们通过一个实例,简单展示对于错拼英文单词的纠错。

代码实例
我们的语料为“bayes_train_text.txt”,首先统计语料中各单词的出现情况:

import re,collections
 
# 提取语料库中的所有单词并且转化为小写
def words(text):
	return re.findall("[a-z]+", text.lower())
	 
#统计词频
def train(features):
	# 若单词不在语料库中,默认词频为1,避免先验概率为0的情况
    model = collections.defaultdict(lambda:1)
    for f in features:
        model[f]+=1
    return model
 
words_N = train(words(open("bayes_train_text.txt").read()))
	接下来计算错拼单词编辑距离为1以及2的所有候选项:
#英文字母
alphabet="abcdefghijklmnopqrstuvwxyz"
 
# 过滤非词典中的单词
def known(words):
    return set(w for w in words if w in words_N)
 
# 获取编辑距离为1的所有单词
def edits1(word):
    n = len(word)
    # 删除某一字母而得的相近词
    s1 = [word[0:i]+word[i+1:] for i in range(n)]
    # 相邻字母调换位置而得的相近词
    s2 = [word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)]
    # 替换某一字母
    s3 = [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet]
    # 插入某一字母
    s4 = [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet]
    edits1_words = set(s1+s2+s3+s4)
    edits1_words.remove(word)
    edits1_words = known(edits1_words)
    return edits1_words
 
# 获取编辑距离为2的所有单词
def edits2(word):
    edits2_words = set(e2 for e1 in edits1(word) for e2 in edits1(e1))
    edits2_words.remove(word)
    edits2_words = known(edits2_words)
    return edits2_words
	有了候选项之后,便可以通过一定算法找出最有可能的纠正项,由于我们这里没有历史的错误项、纠正项相对应的语料,因此只根据词语在语料中的出现频次来确定此词语的候选可能性:
def correct(word):
    if word not in words_N:
        candidates = edits1(word) or edits2(word)
        return max(candidates, key=lambda w:words_N[w])
    else:
        return None
	做一些简单实验:
print(correct("het"))
#输出he
print(correct("hete"))
#输出here

总结
通过此例子我们也可以察觉到,对于文本纠错来说,语料的收集至关重要,数据量越大、语料来源越是全面,模型纠错的效果就越好。另外,针对Real-word拼写错误,在实际应用中不可能对每个词进行排查,可以应用语言模型等方式对句子进行粗粒度的初步筛查,如果发现某个句子的出现可能性很小,再将其中的单词作为待纠正对象。

你可能感兴趣的:(算法,自然语言处理,计算机)