Solr:创建拼写检查器

在上一篇文章中,我谈到了Solr Spellchecker的工作原理,然后向您展示了其性能的一些测试结果。 现在,我们将看到另一种拼写检查方法。
与其他方法一样,此方法使用两步过程。 相当快速的“候选单词”选择,然后对这些单词评分。 我们将从Solr使用的方法中选择不同的方法并测试其性能。 我们的主要目标是校正的有效性,第二个方面是结果的速度。 考虑到我们正在获得结果的正确性,我们可以忍受稍慢的性能。
我们的策略是使用特殊的Lucene索引,并使用模糊查询对其进行查询以获取候选列表。 然后,我们将使用Python脚本对候选人进行排名(如果获得更好的结果,可以在Solr拼写检查器子类中轻松对其进行转换)。

候选人选择

历史上,模糊查询一直被认为是与其他查询相比性能较慢的查询,但由于它们已在1.4版本中进行了优化,因此对于我们算法的第一部分而言,它们是一个不错的选择。 因此,这个想法将非常简单:我们将构建一个Lucene索引,其中每个文档都是一个字典单词。 当我们必须纠正一个拼写错误的单词时,我们将对该单词进行简单的模糊查询并获得结果列表。 结果将是与我们提供的单词相似的单词(即,编辑距离较小)。 我发现,大约有70名候选人可以使我们获得出色的成绩。
对于模糊查询,我们涵盖了所有错别字,因为正如我在上一篇文章中所说,大多数错字相对于正确单词的编辑距离均为1。 但是,尽管这是人们在键入时最常见的错误,但还有其他类型的错误。

我们可以找到三种拼写错误[Kukich] :

  1. 印刷错误
  2. 认知错误
  3. 语音错误
当人们知道正确的拼写但在打字时却使运动协调失误时,就会出现打字错误。 认知错误是由于人的知识不足引起的。 最后,语音错误是认知错误的一种特殊情况,认知错误是指听起来正确但拼写错误的单词。 我们已经用模糊查询解决了印刷错误,但是我们也可以为语音错误做些事情。 Solr的分析软件包中有一个语音过滤器,其中除其他外,还具有双重语音识别算法。 以相同的方式执行模糊查询以找到相似的单词,我们可以为该单词的等效语音索引并对其执行模糊查询。 我们必须手动获取该单词的等效词(因为Lucene查询解析器不会分析模糊查询),并使用该单词构造一个模糊查询。

简而言之,对于候选选择,我们使用以下solr模式构建索引:

<fieldType name="spellcheck_text" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true"><analyzer type="index"><tokenizer class="solr.KeywordTokenizerFactory"/><filter class="solr.LowerCaseFilterFactory"/><filter class="solr.PhoneticFilterFactory" encoder="DoubleMetaphone" maxCodeLength="20" inject="false"/></analyzer></fieldType><field name="original_word" type="string" indexed="true" stored="true" multiValued="false"/><field name="analyzed_word" type="spellcheck_text" indexed="true" stored="true" multiValued="false"/><field name="freq" type="tfloat" stored="true" multiValued="false"/>
如您所见,analyzered_word字段包含单词的“音似”。 频率字段将在算法的下一阶段中使用。 它只是该术语在语言中的出现频率。 我们如何估计语言中一个单词的出现频率? 计算大文本语料库中单词的出现频率。 在这种情况下,术语的来源是维基百科,我们使用Solr的TermComponents来计算每个术语在维基百科中出现的次数。
但是维基百科是由会出错的普通人编写的! 我们如何才能将其视为“正确的字典”? 我们利用撰写维基百科的人们的“集体知识”。 这个从维基百科提取的术语词典有很多术语! 超过1.800.00,其中大多数甚至都不是单词。 维基百科中可能正确拼写了高频单词。 从大量的单词库构建字典并考虑最正确的单词的这种方法并不新鲜。 在[Cucerzan]中,他们使用相同的概念,但使用查询日志来构建字典。 可以看出Google的“您的意思是”使用了类似的概念 。
我们可以在这里添加一些优化。 我发现我们可以删除一些单词并获得良好的结果。 例如,我删除了频率为1的单词和以数字开头的单词。 我们可以根据其他条件继续删除单词,但是我们将像这样保留它。
因此,建立索引的过程很简单,我们通过Solr的TermsComponent从Wikipedia索引中提取所有术语以及频率,然后使用SolrJ在Solr中创建索引。

候选人排名

现在的候选人排名。 对于算法的第二阶段,我们将利用信息论,尤其是噪声信道模型 。 应用于这种情况的嘈杂通道假定人类知道一个单词的正确拼写,但是通道中的一些噪声会引入错误,结果我们得到另一个拼写错误的单词。 我们凭直觉知道在尝试键入“ house”时我们不太可能会收到“ sarasa”,因此嘈杂的渠道模型引入了某种形式来确定错误的可能性。
例如,我们拼错了“ houze”,我们想知道哪一个是我们最想输入的单词。 为了实现这一点,我们拥有大量可能的单词词典,但并非所有单词都具有同等的可能性。 我们希望获得打算输入的可能性最高的单词。 在数学中称为条件概率; 假设我们键入“ houze”,那么每个正确单词成为我们想要的单词的可能性有多高。 条件概率的表示法是:P('house'|'houze')表示给定“ houze”时“ house”的概率
这个问题可以从两个角度看待:我们可能认为最常见的词更有可能,例如,“ house”比“ hose”更有可能,因为前者是一个更常见的词。 另一方面,我们也凭直觉认为“房屋”比“光合作用”更有可能,因为两个词之间的差异很大。 这两个方面均由贝叶斯定理正式推导:
我们必须最大化这种可能性,并且只有一个参数:正确的候选单词(在所示情况下为“ house”)。
因此,拼写错误的单词的概率将是恒定的,我们对此不感兴趣。 公式简化为
为了增加更多的结构,科学家给这两个因素命名。 P('houze'|'house')因子是错误模型(或通道模型),并且与通道在尝试写入第二个单词时引入此特定拼写错误的可能性有关。 第二个术语P('house')称为语言模型,它使我们了解单词在语言中的普遍程度。
到目前为止,我仅介绍了该模型的数学方面。 现在我们必须提出这两个概率的具体模型。 对于语言模型,我们可以在文本语料库中使用术语的频率。 从经验上我发现,使用频率的对数比单独使用频率要好得多。 也许这是因为我们要比不经常使用的术语减少更多频繁使用的术语的权重,而对数正是这样做的。
不仅存在一种构造渠道模型的方法。 已经提出了许多不同的想法。 我们将使用一个基于Damerau-Levenshtein距离的简单距离。 但是我也发现,第一阶段的模糊查询在找到候选者方面做得很好。 在某些数据集的一半以上的测试用例中,它首先给出了正确的单词。 因此,通道模型将是Damerau-Levenshtein距离和Lucene为模糊查询条件创建的得分的组合。

排名公式为:

我编写了一个小脚本(python),该脚本可以完成之前所说的所有操作:

from urllib import urlopen
import doubleMethaphone
import levenshtain
import jsonserver = "http://benchmarks:8983/solr/testSpellMeta/"def spellWord(word, candidateNum = 70):#fuzzy + soundlikemetaphone = doubleMethaphone.dm(word)query = "original_word:%s~ OR analyzed_word:%s~" % (word, metaphone[0])if metaphone[1] != None:query = query + " OR analyzed_word:%s~" % metaphone[1]doc = urlopen(server + "select?rows=%d&wt=json&fl=*,score&omitHeader=true&q=%s" % (candidateNum, query)).read( )response = json.loads(doc)suggestions = response['response']['docs']if len(suggestions) > 0:#scorescores = [(sug['original_word'], scoreWord(sug, word)) for sug in suggestions]scores.sort(key=lambda candidate: candidate[1])return scoreselse:return []def scoreWord(suggestion, misspelled):distance = float(levenshtain.dameraulevenshtein(suggestion['original_word'], misspelled))if distance == 0:distance = 1000fuzzy = suggestion['score']logFreq = suggestion['freq']return distance/(fuzzy*logFreq)
在前面的清单中,我必须做一些说明。 在第2行和第3行中,我们将第三方库用于Levenshtein距离和变音位算法。 在第8行中,我们正在收集70个候选人的列表。 该特定数字是根据经验找到的。 候选数越高,算法就越慢,而算法越少,则效果越差。 我们还将第30行的候选单词中的拼写错误的单词排除在外。由于我们使用Wikipedia作为来源,因此常见的是在词典中找到了拼写错误的单词。 因此,如果Leveshtain距离为0(相同的单词),我们将其距离加1000。

测验

我使用此算法进行了一些测试。 第一个将使用Peter Norvig在他的文章中使用的数据集。 我在大约80%的情况下都在第一个位置找到了该单词的正确建议!!! 那是一个非常好的结果。 具有相同数据集(但算法和训练集不同)的Norvig获得了67%

现在,让我们重复上一篇文章的一些测试,以查看改进。 在下表中,我向您显示结果。

测试集 %Solr %新 Solr时间[秒] 新时间[秒] 改善 时间损失
FAWTHROP1DAT.643 45,61% 81,91% 31,50 74,19 79,58% 135,55%
batch0.tab 28,70% 56,34% 21,95 47,05 96,30% 114,34%
SHEFFIELDDAT.643 60,42% 86,24% 19,29 35,12 42,75% 82,06%

我们可以看到我们在改正效果方面得到了很好的改进,但是大约需要两倍的时间。

未来的工作

我们如何改进此拼写检查器。 好了,研究候选人名单后,可以发现其中通常包含正确单词(95%的次数)。 因此,我们所有的努力都应旨在改进评分算法。
我们有许多改善渠道模型的方法; 几篇论文表明,根据语言统计数据计算更复杂的距离,对不同的字母变换加权,可以为我们提供更好的度量。 例如,我们知道写'houpe'的可能性比写'houze'的可能性小。
对于语言模型,可以通过向单词添加更多上下文来获得重大改进。 例如,如果我们拼错了“ nouse”,很难说出正确的单词是“ house”或“ mouse”。 但是,如果我们添加更多的单词“画我的鼻子”,则很明显,我们所寻找的单词是“房子”(除非您有与啮齿动物有关的奇怪习惯)。 这些也称为ngram(在这种情况下是单词,而不是字母)。 Google已提供了大量ngram,可以下载它们的频率。
最后但并非最不重要的一点是,可以通过用Java编写脚本来提高性能。 该算法的一部分在python中。
再见!
作为对所有感兴趣的人的更新,Robert Muir在“ Solr用户”列表中告诉我 ,有一个新的拼写检查器DirectSpellChecker,当时在后备箱中,现在应该是Solr 3.1的一部分。 它使用与本条目中介绍的技术类似的技术,而不会降低性能。
参考文献
[Kukich] Karen Kukich –自动纠正文本中的单词的技巧– ACM计算调查–第24卷第4期,1992年12月
[Cucerzan] S. Cucerzan和E. Brill拼写校正是一个利用Web用户集体知识的迭代过程。 2004年7月
Peter Norvig –如何编写拼写校正器

参考: emmaespina博客上的JCG合作伙伴 Emmanuel Espina 用Solr创建了拼写检查 工具 。


翻译自: https://www.javacodegeeks.com/2012/06/solr-creating-spellchecker.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372270.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django 和 html

下面是对应的形式&#xff0c;自定义的forms 转载于:https://www.cnblogs.com/kilen/p/6804047.html

【Java大系】Java快速教程

感谢原作者&#xff1a;Vamei 出处&#xff1a;http://www.cnblogs.com/vamei Java是面向对象语言。这门语言其实相当年轻&#xff0c;于1995年才出现&#xff0c;由Sun公司出品。James Gosling领导了Java的项目小组。该项目的最初只想为家电设计一门容易移植的语言。然而&am…

[dpdk] 读官方文档(3)

续前节&#xff0c; 测试小程序 1. 想编译测试程序首先需要设置两个环境变量&#xff0c;为什么呢&#xff0c;因为测试程序的Makefile里用了。。。 rpm装了打包好的devel包&#xff0c;这个rpm也会自带这两个环境变量。就是说写第三方程序的时候&#xff0c;习惯上&#xff0c…

逻辑表达式——黑纸白纸

一、问题描述 有A、B、C、D、E五个人&#xff0c;每人额头上都贴了一张黑色或白色的纸条。五人对坐&#xff0c;每人都可以看到其他人额头上的纸的颜色&#xff0c;但都不知道自己额头上的纸的颜色。五人互相观察后&#xff0c; A说&#xff1a;“我看见有三个人额头上贴的是白…

java 1.6u29 下载_jdk1.6 64位下载|JDK 6(Java SE Development Kit)下载6u43 64位官方版_java运行环境 IT猫扑网...

jdk1.6 64位适用于x64的系统安装的java运行环境&#xff0c;Java SE Development Kit6是java开发人员必备的产品&#xff0c;也叫做jdk6&#xff0c;欢迎下载使用。官方介绍适用于您的计算机(windows)的 Java 软件&#xff0c;即 Java Runtime Environment&#xff0c;也称为 J…

ZK 6中的MVVM初探

MVVM与MVC 在上一篇文章中&#xff0c;我们已经看到Ajax框架ZK如何采用CSS选择器启发的Controller来在View中连接UI组件并监听它们的事件。 在此ZK MVC模式下&#xff0c; View中的UI组件无需绑定到任何Controller方法或数据对象。 使用选择器模式作为将View状态和事件映射到Co…

消失循环的2023?你都做了什么? | 2023 年度总结

2023年度总结 -- 今年都做了什么事&#xff1f; 前言心态关键词感悟 记录申请软著独立游戏技术成长 共勉 前言 又到了一年一次年度总结的时候了。我们常常感叹时间飞逝&#xff0c;却又没办法让它放慢的脚步。那就将2023写下来&#xff0c;让它在时间的长河中留下一丝记忆。 心…

java语言基本语法_Java语言基本语法

Java语言基本语法一、标识符和关键字标识符在java语言中&#xff0c;用来标志类名、对象名、变量名、方法名、类型名、数组名、包名的有效字符序列&#xff0c;称为“标识符”&#xff1b;标识符由字母、数字、下划线、美元符号组成&#xff0c;且第一个字符不能是数字&#xf…

Maven的鸟瞰图

我们每天要做的一件事是使用Maven通过发出诸如mvn install之类的构建命令来构建我们的项目。 然后&#xff0c;Maven查看我们项目的配置文件&#xff08;亲切地称为POM&#xff09;&#xff0c;神奇地找出要执行的操作&#xff0c;并且&#xff0c;嘿&#xff0c;您的构建已完成…

node源码详解(五)

本作品采用知识共享署名 4.0 国际许可协议进行许可。转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource5 本博客同步在https://cnodejs.org/topic/56ed6735b705742136388fa6 本博客同步在http://www.cnblogs.com/papertree/p/5295344.html 在上一篇博客&#xff…

51Nod - 1381 硬币游戏

51Nod - 1381 硬币游戏 有一个简单但是很有趣的游戏。在这个游戏中有一个硬币还有一张桌子&#xff0c;这张桌子上有很多平行线&#xff08;如下图所示&#xff09;。两条相邻平行线之间的距离是1&#xff0c;硬币的半径是R&#xff0c;然后我们来抛硬币到桌子上&#xff0c;抛…

javascript 相关小的知识点集合

本文主要是列出一些javascript 相关的&#xff0c;不限于javascript的&#xff0c;容易记错或者遗忘的小知识&#xff0c;小技巧。 1、javascript中的false 在 JavaScript&#xff0c;常见的 false 值&#xff1a; 0, 0, 0, -0, false, ,null,undefined,NaN 要注意空数组([])和…

AOS – 另外一个独特的页面滚动动画库(CSS3)

AOS 是一个用于在页面滚动的时候呈现元素动画的工具库&#xff0c;你可能会觉得它和 WOWJS 一样&#xff0c;的确他们效果是类似的。但是AOS是 CSS3 动画驱动的库&#xff0c;当你滚动页面的时候能让元素动起来&#xff0c;当页面滚回顶部的时候&#xff0c;元素能够回到前一个…

JavaFX 2.0 Hello World

在讨论示例本身之前&#xff0c;我想向您展示如何在NetBeans中创建JavaFX应用程序。 &#xff08;如果尚未安装JavaFX和NetBeans&#xff0c;请参阅我以前的文章《 安装JavaFX 2.0和NetBeans 7.7.1》 &#xff09;单击“文件”菜单中的“新建项目”以打开项目向导。 然后选择“…

java 线程强制停止线程_java多线程之停止线程

在多线程开发中停止线程是非常重要的技术点。停止线程在Java语言中并不像break语句那样干脆。须要一些技巧性的处理。一、 异常法採用异常法来停止一个线程。首先我们须要了解一下两个方法的使用方法&#xff1a;1、interrupt()方法public class MyThread extends Thread{Over…

双系统Ubuntu分区扩容过程记录

本人电脑上安装了Win10 Ubuntu 12.04双系统。前段时间因为在Ubuntu上做项目要安装一个比较大的软件&#xff0c;导致Ubuntu根分区的空间不够了。于是&#xff0c;从硬盘又分出来一部分空间&#xff0c;分给Ubuntu。于是有了这篇Ubuntu扩容过程记录&#xff0c;也可以当作是一篇…

安装JAVA8要登录_JDK8的安装及环境配置

原文链接:https://www.cnblogs.com/chenxj/p/10137221.html1、下载JDK&#xff1b;b、或百度网盘&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1S14y4_3eN9G6oOVfhmbe_w提取码&#xff1a;0cf62、双击安装程序&#xff0c;点击下一步安装目录若不修改&#xff0c;可直…

早期访问中带有NetBeans的Oracle公共云Java服务

谁期望发生这种情况&#xff1a;Oracle正在开发公共云产品&#xff0c;并且即将开始正式启动的迹象已经出现。 在正式宣布之后将近一年&#xff0c;我被邀请加入所谓的“抢先体验”计划&#xff0c;以试驾新服务并提供反馈。 多亏负责产品的经理Reza Shafii &#xff0c;我才可…

App Engine中的Google Services身份验证,第2部分

在本教程的第一部分中&#xff0c; 我描述了如何使用OAuth进行Google API服务的访问/身份验证。 不幸的是&#xff0c;正如我稍后发现的那样&#xff0c;我使用的方法是OAuth 1.0&#xff0c;显然现在Google正式弃用了OAuth 1.0&#xff0c;改用OAuth 2.0版本。 显然&#xff0…

[51nod1297]管理二叉树

一个初始为空的二叉搜索树T&#xff0c;以及1到N的一个排列P: {a1, a2, ..., aN}。我们向这个二叉搜索树T添加这些数&#xff0c;从a1开始, 接下来是 a2, ...&#xff0c; 以aN结束。在每一个添加操作后&#xff0c;输出T上每对节点之间的距离之和。例如&#xff1a;4 7 3 1 8 …