NLP - 加速单词相似性匹配

时间:2017-05-13 00:29:57

标签: python pandas nlp nltk

我试图在pandas数据帧中找到两个单词之间的最大相似度。这是我的常规

import pandas as pd
from nltk.corpus import wordnet
import itertools

df = pd.DataFrame({'word_1':['desk', 'lamp', 'read'], 'word_2':['call','game','cook']})

def max_similarity(row):
    word_1 = row['word_1']
    word_2 = row['word_2']

    ret_val = max([(wordnet.wup_similarity(syn_1, syn_2) or 0) for 
       syn_1, syn_2 in itertools.product(wordnet.synsets(word_1), wordnet.synsets(word_2))])

    return ret_val

df['result'] = df.apply(lambda x: max_similarity(x), axis= 1)

它工作正常,但速度太慢。我正在寻找一种加快速度的方法。 wordnet占用大部分时间任何建议?用Cython?我打开使用其他软件包,例如spacy

2 个答案:

答案 0 :(得分:2)

由于您说您可以使用spacy作为NLP库,因此我们考虑一个简单的基准测试。我们将使用棕色新闻语料库通过将它分成两半来创建一些任意的单词对。

from nltk.corpus import brown

brown_corpus = list(brown.words(categories='news'))
brown_df = pd.DataFrame({
    'word_1':brown_corpus[:len(brown_corpus)//2],
    'word_2': brown_corpus[len(brown_corpus)//2:]
})

len(brown_df)
50277

可以使用Doc.similarity方法计算两个令牌/文档的余弦相似度。

import spacy
nlp = spacy.load('en')

def spacy_max_similarity(row):
    word_1 = nlp(row['word_1'])
    word_2 = nlp(row['word_2'])

    return word_1.similarity(word_2)

最后,将两种方法应用于数据框:

nltk_similarity = %timeit -o brown_df.apply(nltk_max_similarity, axis=1)
1 loop, best of 3: 59 s per loop

spacy_similarity = %timeit -o brown_df.apply(spacy_max_similarity, axis=1)
1 loop, best of 3: 8.88 s per loop

请注意,NLTK和spacy在测量相似度时使用不同的技术。 spacy使用已经使用word2vec算法预训练的单词向量。来自docs

  

使用单词向量和语义相似性

     

[...]

     

默认的英语模型会安装一百万个词汇表的向量   条目,使用在共同爬网上训练的300维向量   语料库使用GloVe算法。 GloVe常见的爬行向量有   成为实用NLP的事实上的标准。

nltk word similarity vs. spacy

答案 1 :(得分:1)

提高速度的一种方法是存储字对相似度。然后在重复的情况下,避免在循环中运行搜索功能。

import pandas as pd
from nltk.corpus import wordnet
import itertools

df = pd.DataFrame({'word_1':['desk', 'lamp', 'read'], 'word_2':['call','game','cook']})

word_similarities = dict()
def max_similarity(row):
    word_1 = row['word_1']
    word_2 = row['word_2']

    key = tuple(sorted([word_1, word_2])) # symmetric measure :)

    if key not in word_similarities:
        word_similarities[key] = max([
            (wordnet.wup_similarity(syn_1, syn_2) or 0)
            for syn_1, syn_2 in itertools.product(wordnet.synsets(word_1), wordnet.synsets(word_2))
        ])

    return word_similarities[key]

df['result'] = df.apply(lambda x: max_similarity(x), axis= 1)