基于贝叶斯方法的英文单词模糊自动校对技术及其应用研究

来源:互联网 发布:淘宝禁售管理规范 编辑:程序博客网 时间:2024/05/01 09:10


摘要:概述了英文文本自动校对技术的产生背景,分析了英文文本的特点,并对英文文本校对的技术难点和解决方法以及国内外的研究现状进行了阐述。在此基础上重点讨论了基于贝叶斯方法的英文单词自动校对技术的实现。

关键字:贝叶斯方法;模糊自动校对;非词错误(单词错误)

 

English words based on the Bayesianapproach to fuzzy automatic proofreading technology and its application

Abstract: An overview of theEnglish text ,automatic proofreading technology

Background ,and analysis of the characteristics of the Englishtext ,and English

text ,proofreading ,technical difficulties and solutions ,aswell as domestic and

international research status are described .On this basis,focused on the automatic

proofreading technology to achieve the English word based onBayesian methods.

Key words:  Bayesian methods ; Fuzzy Automatic Proofreading;Non-word error (word

error)

 

 

1 引言及相关研究

         英文文本自动校对是自然语言处理的主要应用领域之一。对英文文本自动校对的已经经历了相当长的时间。早期的研究方向主要是针对光学字符识别(Optical Character Recognition)、语音识别以及程序代码中的拼写错误。而现今英文文本自动校对技术主要应用于机读文本(Machine-Readable Text)的自动校对。对于英文文本自动校对的研究最早应用于IBM Thomas J.Watson 研究中心研制的IBM/360和IBM/370,即使用UNIX实现了一个TYPO英文拼写检查器;之后在1971年,斯坦福大小的Ralph Gorin在DEC-10机上实现了一个英文拼写检查程序Spell.

    近些年,随着文本自动校对的不断改善和进步,一些成果已经慢慢走近平常人的生活:如Word中自带的英文拼写检查功能、Google等搜索引擎中的文本自动校对和提示功能等。

    在应用键盘录入英文字符时,常见的错误有以下几种:非词错误、真词错误和句法语义错误。非词错误是指文本中那些被词边界分隔出得字符串,根本就不是词典中的词。如以下错误:raech→reach,thew→they等就是非词错误。造成这种错误的原因是由于粗心地输入造成的,这些错误可以概括为替换错误、易位错误、丢失错误和插入错误等。真词错误是由于输入人员粗心地输入所形成的可以在字典中查找到的单词,但却不是想要的单词。如在输入from时发生了真词错误是from变成了form,而form也是字典中的单词。真诚错误虽说不像非词错误那样导致输入的单词不存在,但是往往会导致所输入的单词与上下文搭配不当,并不是当前语境所需要的单词。句法语义错误则是由于真词错误造成的或在输入时丢失某个单词甚至一整行文本。一般而言,可以将非词错误称为单词错误,而将真词错误称为上下文相关的文本错误。

    本文主要针对的是非词错误的自动校对的研究。非词错误的纠错方法主要有误拼词典法、词形距离法、最小编辑距离法、相似键法、骨架键法、N-gram法、基于规则的技术、词典及神经网络技术等。

    误拼字典法:收集大规模真实文本中拼写出错的英文单词,并给出相应的正确拼写,建造一个无歧义的误拼字典。在进行英文单词拼写检查时,查找误拼字典。若命中则说明该单词拼写有误,该单词的正确拼写为纠错建议。该方法的特点是侦错和纠错一体化,效率高。但是英文拼写错误具有随机性,很难保证误拼字典的无歧义性和全面性。因此查准率低、校对效果差。

    词形距离法:是一种基于最大相似度和最小串间距离的英文校对法。核心思想是构造单向的似然性函数,若该单词在词典中则单词拼写正确;否则,按照似然性函数在词典中找到一个与误拼单词最相似的词作为纠错候选词。该方法的特点是节省存储空间,能反应一定的常见拼写错误统计规律,是一种模糊校对的方法。

    最小编辑距离法:通过计算误拼字符串与词典中某个词间的最小编辑距离来确定纠错候选词。所谓最小编辑距离是指将一个词串转换为另一词串所需的最少的编辑操作次数,而该编辑操作则包括:插入、删除、易位和替换等。

    相似键法:相似键技术是将每个字符串与一个键相对应。使那些拼写相似的字符串具有相同或相似的键。当计算出某个误拼字符串的键值之后,它将给出一个指针,指向所有与该误拼字符串相似的单词,并将它们作为给误拼字符串的纠错建议。

    骨架键法:通过构建骨架键词典,在英文单词出现错误时,先抽取出该错误单词的骨架键,然后再去查骨架键词典,将词典中与该词具有相同骨架键的正确单词作为该单词的纠错建议。

    N-gram法:基于n元文法,通过对大规模英文文本的统计得到单词与单词间的转移概率矩阵。当检测到某英文单词不在词典中时,查转移概率矩阵,取转移概率大于某给定阈值的单词作为纠错建议。

    基于规则的技术:利用规则的形式将通常的拼写错误模式进行表示,这些规则可用来将拼写错误变为有效的单词。对于一个误拼字符串,应用所有合适的规则从词典中找到一些与之相对应的单词作为结果,并对每个结果根据事先赋予生成它的规则的概率估计计算一个数值,根据这个数值对所有候选结果排序。

 

2.贝叶斯方法介绍

         贝叶斯方法是概率论中的一个重要的方法,其应用延伸到各个问题领域,特别是需要做出概率测试的地方,同时贝叶斯方法也是机器学习的核心方法之一。究其主要原因是:现实世界本身是不确定的,人类的观察能力是有限的,某些时候我们需要提供一个猜测或者假设。

    现在给出一个具体的例子来了解一下贝叶斯方法:一所学校里面有60%的男生,40%的女生。男生总是选择穿长裤,而女生则一半穿长裤一半穿裙子。根据以上信息我们可以计算出随机选择一个学生,该学生穿长裤的概率和穿裙子的概率各有多大?

    假设学校里面人的总数是 U 个。60% 的男生都穿长裤,于是我们得到了 U * P(Boy) *P(Pants|Boy) 个穿长裤的(男生)(其中 P(Boy) 是男生的概率 = 60%,这里可以简单的理解为男生的比例;P(Pants|Boy) 是条件概率,即在 Boy 这个条件下穿长裤的概率是多大,这里是 100% ,因为所有男生都穿长裤)。40% 的女生里面又有一半(50%)是穿长裤的,于是我们又得到了 U * P(Girl) * P(Pants|Girl) 个穿长裤的(女生)。加起来一共是 U * P(Boy) * P(Pants|Boy) + U * P(Girl) * P(Pants|Girl) 个穿长裤的,其中有 U * P(Girl) * P(Pants|Girl) 个女生。形式化的结果为:P(Girl|Pants)= P(Girl) * P(Pants|Girl) / [P(Boy) * P(Pants|Boy) + P(Girl) * P(Pants|Girl)]

    贝叶斯公式的一般形式是:P(B|A) =P(A|B)*P(B)/[P(A|B)*P(B) + P(A|~B)*P(~B)],实质即:P(B|A)*P(A)=P(AB).

 

3.拼写纠正理论

         问题:argmaxP(猜测想输入的单词|实际输入的单词),即给定一个单词,选择和它最相似的拼写正确的单词。如果单词拼写正确那么无须进一步的操作。

    分析:上述问题可以形式化为:argmaxcP(c|w) ,按照上文贝叶斯公式可以变形为argmaxcP(w|c)*P(c)/P(w) ,即用户可以输错任何词,因此对于任何c而言,出现w的概率P(w)都是一样的。

    P(c):文章中出现一个正确拼写单词c的概率。这个概率完全由英语这种语言特点所决定,可以称其为语言模型。

    P(w|c):用户想输入c,但是却敲成w的概率。代表用户会以多大的概率把c敲成w,可以称其为误差模型。

    argmaxc:用来枚举所有可能的c并且选择概率最大的。

 

4.英文单词模糊自动校对器的实现

    第一步:首先计算P(c),可以读入一个巨大的文本文件big.txt.其中包含大约几百万个单词,相当于语料库。

    第二步:如果输入的单词就是语料库中的单词则不进行后续操作。如果用户输入的单词不在词典中,则产生编辑距离(Edit Distance)为2的所有可能单词。所谓编辑距离为1就是对用户输入的单词进行删除1个字符、添加一个字符、交换相邻字符、替换一个字符所产生的所有单词。因此编辑距离为2即对编辑距离为1的这些单词再进行一次编辑操作。最后产生的单词集可能会很大,但是只保留语料库中存在的单词。对于语料库中不存在的单词不予显示。

    第三步:假设事件c是我们猜测用户可能想要输入的单词,而事件w是用户实际输入的错误单词,根据贝叶斯公式可知: P(c|w) = P(w|c) * P(c) / P(w)。这里的P(w)对于每个单词都是一样的,可以忽略。而P(w|c)是误差模型(Error Model),是用户想要输入w却输入c的概率,这是需要大量样本数据和事实依据来得到的,为了简单起见也忽略掉。因此,我们可以找出编辑距离为2的单词集中P(c)概率最大的几个来提示用户。产生编辑距离为2的单词时,应该让编辑距离为1的单词具有更高的优先级。并且当用户输入的单词长度较长时,产生编辑距离为2的单词可能会花费一些时间。所以可以优化为首先产生编辑距离为1的单词,如果与词典做差集后为空,再产生编辑距离为2的单词。

    开发工具是eclipse。CPU是Inter i5处理器,内存:3G,系统是Windows xp

Java代码:

package seu.wjm;

import java.io.BufferedReader;

import java.io.FileReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.util.Collections;

import java.util.Comparator;

import java.util.HashMap;

import java.util.HashSet;

import java.util.LinkedList;

import java.util.List;

import java.util.Map;

import java.util.Map.Entry;

import java.util.Set;

import java.util.regex.Pattern;

 

 

/**

 * 针对英文单词模糊自动校对器的实现

 * @author wjm

 *

 */

public class SpellCheckWithEnglish {

         privatestatic final char[] ALPHABET ="abcdefghijklmnopqrstuvwxyz".toCharArray();

   

    public staticvoid main(String[] args) throws Exception {

           newSpellCheckWithEnglish().start();

    }

   

    public voidstart() throws IOException {

           //1.Build language model

                //langModel为语言模型对象,是HashMap<String,Double>类型

                //dictionary为langModel中key的集合,即单词的集合(语料库)

          Map<String, Double> langModel =buildLanguageModel("d:\\spellCheck\\big.txt");

          Set<String> dictionary = langModel.keySet();

          

          

          System.out.println("Please input a word:");

           //2.Read user input loop

          BufferedReader reader = new BufferedReader(newInputStreamReader(System.in));

           Stringinput;

           while((input = reader.readLine()) != null) {

                 input = input.trim().toLowerCase();

                 //当程序输入bye,那么程序结束

                 if ("bye".equals(input))

                       break;

                 //若语料库中包含输入的单词,那么程序继续

                 if (dictionary.contains(input)){

                           System.out.println("语料库中包含"+ input + "");

                           continue;

                  }

                           

                     

                 long startTime = System.currentTimeMillis();

                 

               /* 

                 // 3.Build set for word in edit distance and remove inexistent in dictionary

                 Set<String> wordsInEditDistance2 =buildEditDistance2Set(langModel, input);

                 wordsInEditDistance2.retainAll(dictionary);*/

 

                //3 wordsInEditDistance代表的是编辑距离

                 //buildEditDistance1Set是编辑距离为1的字符的集合

                 //buildEditDistance2Set是编辑距离为2的字符的集合

                 Set<String> wordsInEditDistance = buildEditDistance1Set(langModel,input);

                 //保留仅在语料库中出现的编辑距离为1的字符集

                 wordsInEditDistance.retainAll(dictionary);

                 if (wordsInEditDistance.isEmpty()) {

                       wordsInEditDistance = buildEditDistance2Set(langModel, input);

                       wordsInEditDistance.retainAll(dictionary);

                       if (wordsInEditDistance.isEmpty()) {

                              System.out.println("Failed to check this spell");

                               continue;

                       }

                  }

 

                 // 4.Calculate Bayes's probability

                  // c - correct word we guess, w - wrongword user input in reality

                 // argmax P(c|w) = argmax P(w|c) * P(c) / P(w)

                 // we ignore P(w) here, because it's the same for all words

                 List<String> guessWords = guessCorrectWord(langModel,wordsInEditDistance);

                 System.out.printf("Do you mean %s ? Cost time: %.3fsecond(s)\n",

                              guessWords.toString(), (System.currentTimeMillis() - startTime) /1000D);

           }

          

    }

   

    /**

     *buildLanguageModel方法建立语言模型

     * @paramsample

     * @return

     * @throwsIOException

     */

    privateMap<String, Double> buildLanguageModel(String sample)

                 throws IOException {

           Map<String,Double> langModel = new HashMap<String, Double>();

          BufferedReader reader = new BufferedReader(new FileReader(sample));

           //正则表达式:+一次或多次匹配前面的字符或子表达式

           //*零次或多次匹配前面的字符或子表达式

           //?零次或一次匹配前面的字符或子表达式

           Patternpattern = Pattern.compile("[a-zA-Z]+");

           Stringline;

          //totalCnt:单词出现的频数

           inttotalCnt = 0;

           while((line = reader.readLine()) != null) {

                                //按照空格进行单词分隔

                 String[] words = line.split(" ");

                 for (String word : words) {

                       if (pattern.matcher(word).matches()) {

                                          //toLowerCase()转换为小写字母

                               word =word.toLowerCase();

                               //langModel为语言模型对象,是HashMap<String,Double>类型

                               //其中get(word),word为关键字。

                               //通过get(key)获取相应的value,即概率

                               Double wordCnt =langModel.get(word);

                               if (wordCnt ==null)

                                     langModel.put(word, 1D);

                               else

                                     langModel.put(word, wordCnt + 1D);

                               totalCnt++;

                       }

                  }

           }

          reader.close();

          

          //setValue设置value的值,即通过除法求出相应的概率

           for(Entry<String, Double> entry : langModel.entrySet())

                 entry.setValue(entry.getValue() / totalCnt);

          

           returnlangModel;

    }

   

    /**

     * 建立编辑距离为1的字符集合,返回类型为Set<String>

     * @paramlangModel

     * @param input

     * @return

     */

    privateSet<String> buildEditDistance1Set(

                 Map<String, Double> langModel,

                 String input) {

          Set<String> wordsInEditDistance = new HashSet<String>();

           char[]characters = input.toCharArray();

          

           //Deletion: delete letter[i]

           //删除一个字符

           for (int i = 0; i < input.length();i++)

                 wordsInEditDistance.add(input.substring(0,i) + input.substring(i+1));

          

           //Transposition: swap letter[i] and letter[i+1]

           //交换相邻的两个字符

           for (inti = 0; i < input.length()-1; i++)

                 wordsInEditDistance.add(input.substring(0,i) + characters[i+1] +

                               characters[i] +input.substring(i+2));

          

           //Alteration: change letter[i] to a-z

           //用其他字符代替一个字符

           for (inti = 0; i < input.length(); i++)

                 for (char c : ALPHABET)

                       wordsInEditDistance.add(input.substring(0,i) + c +input.substring(i+1));

          

           // Insertion:insert new letter a-z

           //插入一个新的字符

           for (inti = 0; i < input.length()+1; i++)

                 for (char c : ALPHABET)

                       wordsInEditDistance.add(input.substring(0,i) + c + input.substring(i));

           

           returnwordsInEditDistance;

    }

   

    /**

     * 建立编辑距离为2的字符集合,返回类型为Set<String>

     * 在编辑距离为1的字符集合的基础上再次进行建立编辑距离为1的字符集合的操作

     * @paramlangModel

     * @param input

     * @return

     */

    privateSet<String> buildEditDistance2Set(

                 Map<String, Double> langModel,

                 String input) {

          Set<String> wordsInEditDistance1 =buildEditDistance1Set(langModel, input);

          Set<String> wordsInEditDistance2 = new HashSet<String>();

           for(String editDistance1 : wordsInEditDistance1)

                 wordsInEditDistance2.addAll(buildEditDistance1Set(langModel,editDistance1));

          wordsInEditDistance2.addAll(wordsInEditDistance1);

           returnwordsInEditDistance2;

    }

   

    /**

     * 返回猜测的可能符合要求的单词

     * @paramlangModel

     * @paramwordsInEditDistance

     * @return

     */

    privateList<String> guessCorrectWord(

                 final Map<String, Double> langModel,

                 Set<String> wordsInEditDistance) {

          List<String> words = newLinkedList<String>(wordsInEditDistance);

           //将满足要求的单词进行排序显示输出

          Collections.sort(words, new Comparator<String>() {

                 @Override

                 public int compare(String word1, String word2) {

                           //按照概率的大小,从大到小进行输出

                       return langModel.get(word2).compareTo(langModel.get(word1));

                  }

           });

           //若符合条件的集合中元素个数大于5个,则仅输出概率最高的前五个单词

           returnwords.size() > 5 ? words.subList(0, 5) : words;

    }

 

}

5.实验中存在的问题以及待继续的工作

    经过1000次的统计,得出以下结论:达到了简洁, 快速开发和运行速度这三个目标, 不过准确率不算太好.该算法用于英文单词的模糊自动校对,基本上达到了预期的效果。平均匹配率可以达到80%以上。但是对于较短的并且较为常用的单词,该算法所得的匹配率明显不符合要求。同时对于较长的单词,可能返回的结果并不是预期的值。虽然结果不是特别准确,但是却刺激了我的兴趣,希望通过对算法进行适当地改进可以提高算法结果的准确率。该算法仅仅解决了单个单词的自动校对问题,尚且没有达到例如Google输入栏中输入若干个单词可以进行整个句子或者文本的匹配的程度。若想达到文本自动匹配,需要针对更大的语料库进行训练,利用文本上下文的同现与搭配特征或者利用规则或语言学知识进行自动匹配。总的来说,还是通过训练-测试的方法,依据的理论基础仍然是贝叶斯公式。

 

 

参考文献:

[1].Spellcheckingby computer   by Roger Mitton

[2].SPEECHand LANGUAGE PROCESSING   by DanielJurafsky and James H.Martin

[3].张仰森,俞士汶. 文本自动校对技术研究综述.计算机应用研究 2005-08-24

[4].AUTOMATICSPELLING CORRECTION IN SCIENTIFIC AND SCHOLARLY TEXT by JOSEPH J.

POLLOCKand ANTONIO ZAMORA   Communications ofthe ACM 1984

[5].Techniquesfor Automatically Correcting Words in Text by KAREN KUKICH  ACM 1992

[6].汪维嘉,陈芙蓉,秦进,陆汝占.一种基于窗口技术的中文文本自动校对方法贵州大学学报  2003年5月第20卷第2期

 

原创粉丝点击