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 json

server = "http://benchmarks:8983/solr/testSpellMeta/"

def spellWord(word, candidateNum = 70):
    #fuzzy + soundlike
    metaphone = 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:
        #score
        scores = [(sug['original_word'], scoreWord(sug, word)) for sug in suggestions]
        scores.sort(key=lambda candidate: candidate[1])
        return scores
    else:
        return []

def scoreWord(suggestion, misspelled):
    distance = float(levenshtain.dameraulevenshtein(suggestion['original_word'], misspelled))
    if distance == 0:
        distance = 1000
    fuzzy = 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月

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


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值