寻找最佳文本相似度/距离算法来检测自然语言中的单词相关性

问题描述 投票:0回答:0

我对不同的 Python 库和用于测量文本距离/相似性的算法进行了一些研究:Levenshtein 距离、Jaro-Wrinkler、Hamming 等。到目前为止,似乎更适合我的目的的似乎是归一化汉明距离。然而,它仍然远远不足以满足我需要做的事情。让我更好地解释一下我想要什么,以便您可以更好地帮助我。

我从事历史语言学工作,我正在努力提高我们正在开发的一些语料库的 POS 和词条标签的可靠性。 “引理”是一类词的字典形式或代表词(例如,“do”是“does”、“did”、“do”和“done”的引理)。

这些语料库对自动标记提出了巨大的挑战,因为几个世纪以来文本中使用的句法(词序)和拼写存在巨大差异。语料库中的文本跨越 5 个世纪,来自不同的地理区域,因此以不同的语言方言书写。

我正在尝试做的一件事是使词形还原自动化。反过来,这将非常有助于改进 POS 标记。为了让您了解这个问题,让我们以“vermell”(加泰罗尼亚语中“红色”的意思,我们使用的语言之一)为例。我发明了一些案例,因为我没有记住所有可能的变体,但您可以大致了解问题的严重性。 'vermell' 及其变体可以以多种形式出现在文本中:

singular:
masc: 'vermell', 'vermel', 'uermell', 'uermel', 'varmell', 'uarmell'
fem: 'vermella', 'vermela', 'uermella', 'varmella'

plural:
masc: 'vermells', 'uermells', 'vermels'
fem:'vermelles', 'vermellas', 'uermellas', 'uermelles'

with different suffixes (superlatives, diminituves, etc) :

'vermellíssims', 'uermellíssima', 'vermelíssima', 'varmellisima', 'uarmelíssim', 'varmellota', 'vermellós', 'vermelloses', 'uarmellós

在这些语料库中,通常可以使用“u”代替“v”或使用“y”代替“i”。即使在同一作者的同一文本中,拼写为“h”的单词也经常会漏掉“h”。因此,变化是巨大的。然而,即使是现代语言使用者通常也能很容易地检测出这些词是否相关。当然,说这种语言的人对单词结构和词法了如指掌,因此可以立即看出,例如,“uermellíssima”与“vermell”相关,尽管很多字符都不一样。

在另一个问题(在 Python 中识别大型字符串列表中的项目之间的文本相似性的最有效方法是什么?)我寻求帮助以提高测量两个大型字符串列表之间字符串相似性的脚本的效率。在 Stack Overflow 贡献者的帮助下,我将我在本消息末尾重现的脚本放在一起。它使用汉明距离的实现,根据 64,000 种形式的字典处理 180,000 种独特形式的列表需要大约 2 个小时。我正在使用具有 10 个内核的 Mac Studio。

不过,结果还是比较不理想。您可以通过以下示例看到这一点:

beato ➝  beat: 0.8
beatriç ➝  tectriu: 0.5714285714285714
beatriç ➝  teatral: 0.5714285714285714
beatte ➝  beats: 0.6666666666666667
beatus ➝  certus: 0.6666666666666667
beatíssim ➝  nequíssim: 0.6666666666666667
beatíssim ➝  gravíssim: 0.6666666666666667

即使您不懂该语言(如果有人感兴趣,也可以使用中世纪的加泰罗尼亚语),您会发现这是非常错误的(使用 Levenshtein 或余弦距离等其他算法是没有希望的)。理想情况下,词条“beat”或“beats”应该是在所有这些情况下被选为“最接近”的词,除了“beatriç”,这是一个不在字典中的专有名词。然而,该算法做了它所做的。它不是为此而设计的。

所以,我的问题如下。有人知道实现我的目标的更好解决方案吗?我可以利用任何算法或工具来更好、更高效地完成这项工作吗?在更多专家程序员的帮助下使用并行化和调整代码帮助我更接近我的目标,但我不确定这是否是可行的方法。也许我看得不够仔细,但在 NLP 的所有工作中,我很惊讶没有其他算法可以在这种情况下做得更好。

一些现有的面向 AI/NLP 的 Python 库是否对此有帮助(Spacy、PyTorch、scikit-learn ...?)使用矢量化,将列表转换为数组,...?我是一名语言学家,而不是训练有素的程序员,所以如果您的回答涉及对我的脚本进行一些转换,那将会很有帮助,这样我就可以更好地处理它并理解每个部分在做什么。非常感谢。

from itertools import zip_longest
from bisect import insort
from joblib import Parallel, delayed
import line_profiler

profile = line_profiler.LineProfiler()

emmas = ['gran', 'vermell', 'groc', 'atens', 'Do', 'dOne', 'PUrpose', 'can', 'be', 'use', 'for', 'cannon', 'amuse', 'useful', 'user', 'become', 'downtown', 'develop', 'fulminate', 'deduce', 'de', 'bezant']

forms = ['preriarenos', 'Marinara', 'Grand', 'Gran', 'Grans', 'Grands', 'Grandeses', 'Grandullons', 'grand', 'grandissisimus', 'gran', 'grans', 'grands', 'grandeses', 'grandullons', 'grandullon', 'grandullones', 'uermell', 'uermells', 'vermell', 'vermells', 'vermella', 'vermelles', 'varmellíssimes', 'uarmellíssimes', 'uermellíssimes', 'uarnellíssimes', 'varmellíssima', 'uermella', 'uarmella', 'uarnella', 'varnella', 'uarnellas', 'varnellas', 'varmella', 'uermelles', 'grog', 'grogues', 'doNE', 'donE', 'doIng', 'purposeful', 'canonical', 'becareful', 'being', 'berate', 'best', 'bezant', 'full', 'fulmination', 'predict', 'downgrade', 'down', 'developing', 'deduct', 'deducing']

distances = {}

@delayed
def calc_distances(form, lemmas_low):
    form_distances = []
    for lemma in lemmas_low:
        char_matches = [c1 != c2 for c1, c2 in zip_longest(lemma, form)]
        dist = 1 - (sum(char_matches)/len(char_matches))
        if dist > 0.25:
            insort(form_distances, (dist, lemma))
    return (form, form_distances)

@profile
def profile_distance_calcs():
    lemmas_low = [lemma.lower() for lemma in lemmas]
    forms_low = [form.lower() for form in forms]
    results = Parallel(n_jobs=-1, prefer="threads")(calc_distances(form, lemmas_low) for form in forms_low)
    for form, form_distances in results:
        distances[form] = form_distances

    with open("potential_lemmas_hamming-like.txt", "w") as f:
        for form, form_distances in distances.items():
            for dist, lemma in reversed(form_distances[-2:]):
                f.write(f"{form} ➝  {lemma}: {dist}\n")

if __name__ == "__main__":
    profile_distance_calcs()
    profile.print_stats()
python text nlp corpus tagged-corpus
© www.soinside.com 2019 - 2024. All rights reserved.