Быстрый расчет n-грамм

Я использую NLTK для поиска n-граммов в корпусе, но в некоторых случаях он занимает очень много времени. Я заметил, что вычисление n-граммов не является необычной особенностью в других пакетах (очевидно, у Haystack есть некоторые функции для этого). Означает ли это потенциально более быстрый способ поиска n-граммов в моем корпусе, если я откажусь от NLTK? Если да, то что я могу использовать для ускорения работы?

Ответы

Ответ 1

Поскольку вы не указали, хотите ли вы использовать n-граммы на уровне слов или символов, я просто буду считать первое, не теряя общности.

Я также предполагаю, что вы начинаете со списка токенов, представленных строками. То, что вы можете легко сделать, это записать n-граммовое извлечение самостоятельно.

def ngrams(tokens, MIN_N, MAX_N):
    n_tokens = len(tokens)
    for i in xrange(n_tokens):
        for j in xrange(i+MIN_N, min(n_tokens, i+MAX_N)+1):
            yield tokens[i:j]

Затем замените yield фактическим действием, которое вы хотите взять на каждый n-грамм (добавьте его в dict, сохраните его в базе данных, что угодно), чтобы избавиться от служебных данных генератора.

Наконец, если это действительно не достаточно быстро, конвертируйте приведенное выше в Cython и скомпилируйте его. Пример с использованием defaultdict вместо yield:

def ngrams(tokens, int MIN_N, int MAX_N):
    cdef Py_ssize_t i, j, n_tokens

    count = defaultdict(int)

    join_spaces = " ".join

    n_tokens = len(tokens)
    for i in xrange(n_tokens):
        for j in xrange(i+MIN_N, min(n_tokens, i+MAX_N)+1):
            count[join_spaces(tokens[i:j])] += 1

    return count

Ответ 2

Вы можете найти питонную, элегантную и быструю функцию генерации ngram, используя zip и оператор splat (*) здесь:

def find_ngrams(input_list, n):
  return zip(*[input_list[i:] for i in range(n)])

Ответ 3

Для n-граммов уровня персонажа вы можете использовать следующую функцию

def ngrams(text, n):
    n-=1
    return [text[i-n:i+1] for i,char in enumerate(text)][n:]