做网页检索系统的时候,当时还想加给它加上一个拼写纠错的功能(当然,只适合英文...),后来找到了个效果不错的方法给大家分享一下。

拼写纠错、校正是一个提高搜索引擎用户体验的很关键的一项能力。如我们在Google搜索 saferi,然后会看到它自动帮助我们纠正了输错的单词。

仔细想一想为什么它会知道我们输错的单词就是safari,我们会发现主要原因有两个;

1. 错写的单词与正确单词的拼写相似,容易错写;这里safari是否容易错写成saferi需要统计数据的支持;为了简化问题,我们认为字形越相近的错写率越高,用编辑距离来表示。字形相近要求单词之间编辑距离小于等于2,这里saferi与safari编辑距离为1,后面我们再具体了解编辑距离的定义。

2. 正确单词有很多,除去语义因素外最有可能的单词,也就是这个单词的使用频率了。所以我们确认的标准还有一项就是,单词使用频率。

下面介绍一个机器学习拼写检查方法,基于贝叶斯定理的拼写检查法,主要思想就是上面2条,列举所有可能的正确拼写,根据编辑距离以及词频从中选取可能性最大的用于校正。

原理:

用户输入的错误的单词记做w,用户想要输入的拼写正确的单词记做c,则

P(c | w) : 用户输错成w时,想要的单词是c的概率。

P(w | c) :   用户将c错写成w的概率,与编辑距离有关。

P(c) :    正确词是c的概率,可以认为是c的使用频率,需要数据训练。

根据贝叶斯公式

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

因为同一次纠正中w是不变的,所以公式中我们不必理会P(w),它是一个常量。比较 P(c | w) 就是比较 P(w | c) * P(c) 的大小。

一、P(c)

P(c)替换成“使用频率”,我们从足够大的文本库(词典)点击打开链接中统计出各个单词的出现频率,也可以将频率归一化缩小方便比较。

二、P(w | c)

P(w | c)替换成常数lambda * editDist

editDist编辑距离只计算editDist = 1与editDist = 2的,

editDist1,编辑距离为1的有下面几种情况:

(1)splits:将word依次按照每一位分割成前后两半。比如,'abc'会被分割成 [('', 'abc'), ('a', 'bc'), ('ab', 'c'), ('abc', '')] 。

  (2)beletes:依次删除word的每一位后、所形成的所有新词。比如,'abc'对应的deletes就是 ['bc', 'ac', 'ab'] 。

  (3)transposes:依次交换word的邻近两位,所形成的所有新词。比如,'abc'对应的transposes就是 ['bac', 'acb'] 。

  (4)replaces:将word的每一位依次替换成其他25个字母,所形成的所有新词。比如,'abc'对应的replaces就是 ['abc', 'bbc', 'cbc', ... , 'abx', ' aby', 'abz' ] ,一共包含78个词(26 × 3)。

  (5)inserts:在word的邻近两位之间依次插入一个字母,所形成的所有新词。比如,'abc' 对应的inserts就是['aabc', 'babc', 'cabc', ..., 'abcx', 'abcy', 'abcz'],一共包含104个词(26 × 4)。

editDist2则是在editDist1得到的单词集合的基础上再对它们作以上五种变换,得到所有编辑距离为2的单词(无论是否存在,在词典中不存在的记P(c) = 1)。

三、纠正规则

1. 如果拼写的单词在词典中出现的,直接返回。

2. 如果词典中不存在的,返回编辑距离为1和2的所有单词中,P(c | w) * P(c)最大的单词。

Java实现代码:

参考链接:https://github.com/toufuChew/webSearch/tree/master/src/QueryCorrection

词频统计(训练数据)

package QueryCorrection;import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;import IKAnalyze.IKAnalyze;
/*** 生成训练集* @author cq**/
public class Statistics {private static final String dict = "/Users/cq/Desktop/dict.txt";private static final String big = "/Users/cq/Downloads/big.txt";public Statistics(){File dictfile = new File(dict);if (dictfile.exists() && dictfile.length() != 0)return;HashMap<String, Integer> map = new HashMap<String, Integer>();try {BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File(big)));byte[] b = new byte[65535];int ch;try {while((ch = bin.read(b)) != -1){String txt = new String(b, "utf-8");String[] arr = IKAnalyze.CNAnalyzerBStr(txt); //会过滤停用词,如and,the...for (int i = 0; i < arr.length; i++) {if (map.containsKey(arr[i]))map.put(arr[i], map.get(arr[i]) + 1);elsemap.put(arr[i], 1);}}Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dictfile)));while(it.hasNext()){Map.Entry<String, Integer> entry = it.next();bw.write(entry.getKey() + " " + entry.getValue() + "\n");}bw.flush();bw.close();System.out.println("已完成统计!");} catch (IOException e) {e.printStackTrace();}} catch (FileNotFoundException e) {e.printStackTrace();}  }public static HashMap<String, Integer> words(){try {BufferedReader br = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(dict))));String row;HashMap<String, Integer> map = new HashMap<String, Integer>();try {while((row = br.readLine()) != null){String[] s = row.split(" ");map.put(s[0], Integer.valueOf(s[1]));}br.close();return map;} catch (IOException e) {e.printStackTrace();}} catch (FileNotFoundException e) {e.printStackTrace();}return null;}public static void main(String[] args){new Statistics();}
}

编辑距离处理及单词校正

package QueryCorrection;import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Pattern;public class Correction{private HashMap<String, Integer> map = null; //字典private static final String alphabet = "abcdefghijklmnopqrstuvwxyz";private static final double pow = 0.8;public Correction(){map = Statistics.words();}/*** 编辑距离为1的所有可能的词* @param word* @return*/private ArrayList<String> editDist1(String word){ArrayList<String> words = new ArrayList<String>();BinaryWord[] binary = new BinaryWord[word.length()];//splitfor (int i = 0; i < word.length(); i++)binary[i] = new BinaryWord(word.substring(0, i), word.substring(i));//deletesfor (int i = 0; i < binary.length; i++)if (binary[i].rw.length() > 1)words.add(binary[i].lw + binary[i].rw.substring(1));elsewords.add(binary[i].lw);//transposesfor (int i = 0; i < binary.length; i++)if (binary[i].rw.length() > 1)words.add(binary[i].lw + binary[i].rw.charAt(1) + binary[i].rw.charAt(0) + binary[i].rw.substring(2));//replaces & insertsfor (int i = 0; i < binary.length; i++)for (int j = 0; j < alphabet.length(); j++){words.add(binary[i].lw + alphabet.charAt(j) + binary[i].rw.substring(1));words.add(binary[i].lw + alphabet.charAt(j) + binary[i].rw); //inserts}//last insertsfor (int i = 0; i < alphabet.length(); i++)words.add(word + alphabet.charAt(i));return words;}/*** 编辑距离为2* @param word* @return*/private ArrayList<String> editDist2(String word){ArrayList<String> dist1 = editDist1(word);ArrayList<String> dist2 = new ArrayList<String>(); //只加入编辑距离为2且出现在字典中的词for (int i = 0; i < dist1.size(); i++){ArrayList<String> temp = editDist1(dist1.get(i));for (int j = 0; j < temp.size(); j++)if(map.containsKey(temp.get(j)))dist2.add(temp.get(j));}return dist2;}/*** 获得最大概率的词* @param arr* @param islegal 是否都是合法单词* @return*/public String maxPx(ArrayList<String> arr, boolean islegal){int frq = 0;String result = null;if (islegal){for (int i = 0; i < arr.size(); i++)if (frq < map.get(arr.get(i))){frq = map.get(arr.get(i));result = arr.get(i);}}elsefor (int i = 0; i < arr.size(); i++){if (map.containsKey(arr.get(i)))if (frq < map.get(arr.get(i))){frq = map.get(arr.get(i));result = arr.get(i);}}return result;}/*** 是否是合法单词(需要纠正)* @param word* @return*/private boolean legalSpell(String word){if (map.containsKey(word))return true;return false;}/*** 英文词纠正* 纠正数:1* new Correction(String word).correct()* @param null* @return*/public String correct(String wd){if (legalSpell(wd))return wd;String r1 = maxPx(editDist1(wd), false);int score1 = 0;if (r1 != null)score1 = map.get(r1);String r2 = maxPx(editDist2(wd), true);int score2 = 0;if (r2 != null)score2 = map.get(r2);if (score2 > score1 * pow)return r2;if (score2 == score1 * pow)return wd;return r1;}/*** 对多个词进行纠错* @param queryterm* @return false if no illegal word*/public boolean correct(String[] queryterm){boolean hascorrect = false;//String regex = "[\\u4e00-\\u9fa5]"; //非中文String regex = "[\\u4e00-\\u9fa5]+";for (int i = 0; i < queryterm.length; i++){if (!queryterm[i].matches(regex)){String temp = correct(queryterm[i]);if (temp.compareTo(queryterm[i]) != 0){hascorrect = true;queryterm[i] = temp;}}}return hascorrect;}public static void main(String[] args){String[] query = {"calendar", "conclsion", "ture","canlendae", "conclsion", "ture","中文"};System.out.println(new Correction().correct(query));long st = System.currentTimeMillis();for (int i = 0; i < query.length; i++)System.out.println(query[i]);System.out.println((System.currentTimeMillis() - st)/ 1000.0 + "s");}
}class BinaryWord{String lw = null;String rw = null;public BinaryWord(String lw, String rw){this.lw = lw;this.rw = rw;}
}

效果图:

单词拼写校正原理及实现(贝叶斯推断)相关推荐

  1. 贝叶斯推断及其互联网应用(三):拼写检查

    (这个系列的第一部分介绍了贝叶斯定理,第二部分介绍了如何过滤垃圾邮件,今天是第三部分.) 使用Google的时候,如果你拼错一个单词,它会提醒你正确的拼法. 比如,你不小心输入了seperate. G ...

  2. nltk 同义词替换 单词拼写校正 制作伪原创文章

    一.基于贝叶斯单词拼写校正 # -*- coding: utf-8 -*- # @Time : 2019/11/26 10:13 # @Author : # @FileName: word_check ...

  3. [转]贝叶斯推断及其互联网应用

    贝叶斯推断及其互联网应用(一):定理简介 作者: 阮一峰 日期: 2011年8月25日 一年前的这个时候,我正在翻译Paul Graham的<黑客与画家>. 那本书的第八章,写了一个非常具 ...

  4. 贝叶斯推断应用:垃圾邮件过滤

    附上新博客地址:月光森林 引入 仍然是"信息内容安全" 课程的一个实验总结.为了理解整个原理,不但重新复习了一边贝叶斯,还因为对"联合概率"理解不透彻,直接翻译 ...

  5. 贝叶斯推断及其互联网应用(二):过滤垃圾邮件

    上一次,我介绍了贝叶斯推断的原理,今天讲如何将它用于垃圾邮件过滤. ======================================== 贝叶斯推断及其互联网应用 作者:阮一峰 (接上文) ...

  6. 贝叶斯推断及其互联网应用(一)

    贝叶斯推断及其互联网应用(一) 投递人 itwriter 发布于 2011-08-25 14:03 评论(6) 有1981人阅读 原文链接 [收藏] « » 一年前的这个时候,我正在翻译Paul Gr ...

  7. 【数据挖掘】贝叶斯分类 ( 贝叶斯分类器 | 贝叶斯推断 | 逆向概率 | 贝叶斯公式 | 贝叶斯公式推导 | 使用贝叶斯公式求逆向概率 )

    文章目录 I . 贝叶斯分类器 II . 贝叶斯推断 ( 逆向概率 ) III . 贝叶斯推断 应用场景 ( 垃圾邮件过滤 ) IV . 贝叶斯方法 由来 V . 贝叶斯方法 VI . 贝叶斯公式 V ...

  8. 贝叶斯推断及其互联网应用(一):定理简介

    贝叶斯推断及其互联网应用(一):定理简介 作者: 阮一峰 日期: 2011年8月25日 一年前的这个时候,我正在翻译Paul Graham的<黑客与画家>. 那本书的第八章,写了一个非常具 ...

  9. 变分法和变分贝叶斯推断

    本文转载的原文链接:变分法和变分贝叶斯推断 另一篇CSDN链接学习:变分贝叶斯推断(Variational Bayes Inference)简介 变分法是17世纪末发展起来的一门数学分支,是泛函分析里 ...

最新文章

  1. 如何提升 CSS 选择器的性能?
  2. python yaml用法详解
  3. bmp调色板颜色信息重复_PASCAL VOC数据集-分割标签索引颜色对照及程序
  4. iOS UIlabel文字排版(改变字间距行间距)分类
  5. 最长公共子序列及其引申问题
  6. 计算机专业英语宋,机电一体化专业英语宋主民章.pdf
  7. linux 下后台运行python脚本
  8. loj 6085.「美团 CodeM 资格赛」优惠券
  9. 从PCA和SVD的关系拾遗
  10. 【Python】python list 迭代删除
  11. 【系列一之爬虫系列】爬取信息
  12. 谷歌云盘文件快速下载方法
  13. 计算机while语句知识点总结,while循环使用方法
  14. 计算机磁盘检查,使用磁盘检查工具进行硬盘诊断
  15. ubuntu 18改MAC桌面
  16. 一篇文章了解爬虫技术现状
  17. Mysql NDB Cluster搭建测试
  18. 双efi分区同时引导失败修复经验
  19. gl-matrix中lookAt的作用
  20. Java11实现X509TrustManager报错SSLHandshakeException分析及解决办法

热门文章

  1. 3dmax蒙皮不选中小人
  2. java+jsp+servlet+sqlserver(mysql)出租车管理系统
  3. 均匀化退火时间_常用退火工艺方法中的扩散退火
  4. MTK 驱动开发(20)---camera 基础知识1
  5. 第35课时_电荷泵电路
  6. item_password-获得淘口令真实url API调用说明
  7. SpringCloud eureka 高集群注册中心
  8. 好用的翻牌器,定时器适用于react vue 等等
  9. 令人耳目一新的20款英文手写字体
  10. linux设备驱动归纳总结(八):2.match.probe.remove