【问题标题】:Algorithm that finds similar words based on their letters根据字母找到相似词的算法
【发布时间】:2017-08-21 15:57:06
【问题描述】:

我正在寻找一种方法来根据它们的共享字母来检测单词(文本字符串)的相似性。

我正在研究 Hash 函数,尤其是 Rabin-Karp 算法,以在较大的字符串中找到相似的单词。

但它不适用于我想要的情况: 在我的案例中,我根据德国银行认为“相似”的三个例子: 有“德意志银行”、“邮政银行”和“国家银行”。这三个人的名字中都有“银行”这个词,但只有德意志银行把它作为一个单独的词。 所以基本上是根据它们的共同特征来衡量单词的相似度。 我认为应该有一个限制,如果可能的话,应该只考虑 >=4 个字符的相似性。

如果我只是在寻找“银行”这个词,我会硬编码一些东西。但是我正在寻找一种方法来找到这种相似的名称/字符串而不知道它。

【问题讨论】:

  • 一个例子不足以定义您的问题。相似性度量需要更严格的定义。仅仅相似性可能还不够;您认为“automation”和“Claymation”(6 个字母)比“Deutche Bank”和“Bank of England”(4 个字母)更相似吗?你关心短语中的位置吗?你如何确定短语? ...简而言之,这还不足以得到一个体面的答案。
  • 您认为“automation”和“Claymation”(6 个字母)比“Deutche Bank”和“Bank of England”(4 个字母)更相似吗? ======== 是的,在我的情况下会更相似。
  • 很好...但是如何,为什么,...您仍然需要定义问题,而不仅仅是举一个例子。如果您只需要找到最长的常用字母字符串,那么您在发布之前所做的研究已经发现了用十几种语言中的任何一种进行编码。因此,我假设您有一个更复杂的问题 - 但您尚未阐明它。

标签: algorithm machine-learning data-mining


【解决方案1】:

听起来像是 Levenshtein Distance 的一个很好的用例。简而言之,这计算了从一个字符串到另一个字符串所需的“编辑”次数。您也可以通过在模型中添加发生编辑的概率来变得更好,但这可能不是必需的。通常我发现 2-edit 限制对于基本的拼写更正很有用。

维基页面:Levenshtein Distance

【讨论】:

    【解决方案2】:

    获取一个嵌入模型(有经过训练的 word2vec 或 glove 模型),它为您提供单词语义含义的密集向量表示。

    然后,您可以通过嵌入向量表示通过单词之间的余弦距离轻松找到语义相似度。与提出的通常信息检索技术(如 levenshtein 或公共子字符串/单词重叠)相比,这里的重大改进在于它实际上是基于这些单词的抽象上下文的相似性。所以Postbank 将非常接近BarclaysHSBC,因为它是一家银行公司,而不仅仅是共享子字符串。

    相反,一旦您通过嵌入获得了词的向量,您就可以使用任何聚类算法来找到具有相同语义相似性的词集群,即使不知道描述该集群的特定分类确实存在。

    【讨论】:

    • word2vec 仅适用于训练数据中出现频率足够高的单词。所以它可能根本不会给你任何有用的东西......
    【解决方案3】:

    为了比较任何两个给定的字符串,我们可以根据每个子字符串的长度超过我们的阈值,得出一个数字来表示匹配的好坏。

    将给定的字符串与所有其他字符串进行比较并找到匹配数最高的字符串会给我们最“相似”的字符串。

    我的方法假设子字符串匹配是有序的。如果子字符串的顺序无关紧要,这将不起作用,例如如果abcdXXefgh 应该与abcdYYefghefghYYabcd 相同。虽然可以通过最后检查c 的值并从那里计算匹配数来修改它。

    如果您想考虑字符串的长度或其他因素,可以稍微调整一下。


    我们可以使用类似于Levenshtein distance 的动态编程方法。

    所有可能前缀的距离可能存储在数组d[][] 中,其中d[i][j] 是字符串s 的第一个i 字符与字符串t 的第一个j 字符之间的距离。

    我们可以有一个额外的数组c 来跟踪当前子字符串匹配的长度。

    那么任何给定的单元格值[i,j] 将是最大值:

    • 当前子字符串匹配之前的成本 (d[i-length,j-length]) 加上当前匹配的成本(如果是 length < threshold,则为 0,与 length = c[i,j]
    • 在 Levenshtein distance 中定义:(直接复制值,这些操作没有成本)
      • 插入 (d[i, j-1])
      • 删除 (d[i-1, j])
      • 替换(相同或不同字符)(d[i-1, j-1])

    这个的伪代码:

    set each cell in c and d to 0
    
    threshold = 4     // shortest possible substring to consider
    
    for j = 1 to n
    for i = 1 to m
       currentSubstringCost = 0
       if s[i] == t[j]     // there's a matching substring
          c[i, j] = c[i-1, j-1]
          if c[i, j] >= threshold
              currentSubstringCost = d[i - c[i, j], j - c[i, j]]
                                     + calculateSubstringCost(c[i, j])
       d[i, j] = maximum(currentSubstringCost,      // substring match
                         d[i-1, j],                 // deletion
                         d[i, j-1],                 // insertion
                         d[i-1, j-1])               // substitution
    
    return d[m, n]
    

    您需要弄清楚如何计算任何给定子字符串 (calculateSubstringCost) 的成本。

    一种简单的方法是取子串长度的平方,因此长度为 4 的子串将花费 16,而长度为 6 的子串将花费 36

    您还可以通过在当前子字符串匹配之前使用另一个跟踪成本的数组来优化它只需要 2 行,这导致我们最多只需要向后看 1 行(例如,参见 above link) .

    【讨论】:

      【解决方案4】:

      如果我错了,请纠正我。从您的问题中,我了解到您需要找到所有有共同点的字符串。

      我们能否在所有字符串之间找到共同的子字符串。根据 Substring 的长度,我们可以给出一个分数。根据阈值,您可以决定字符串是否属于同一组。

      【讨论】:

      • 是的。也许添加一个规则,即公共 SubString 必须 >=4 个字符。
      猜你喜欢
      • 1970-01-01
      • 2015-09-19
      • 1970-01-01
      • 2011-08-13
      • 1970-01-01
      • 1970-01-01
      • 2020-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多