2011-09-29 13 views
16

Używam NLTK do wyszukiwania n-gramów w korpusie, ale w niektórych przypadkach zajmuje to bardzo dużo czasu. Zauważyłem, że obliczanie n-gramów nie jest rzadką cechą w innych pakietach (najwyraźniej Haystack ma jakąś funkcjonalność). Czy to oznacza, że ​​istnieje potencjalnie szybszy sposób na znalezienie n-gramów w moim korpusie, jeśli zrezygnuję z NLTK? Jeśli tak, to w czym mogę przyspieszyć działanie?Szybkie obliczenia n-graficzne

+0

Więcej informacji dla zainteresowanych: http://packages.python.org/Whoosh/ngram s.html – Trindaz

+0

Powiązane pytanie: http://stackoverflow.com/questions/21883108/fast-optimize-n-gram-implementations-in-python – dmcc

Odpowiedz

21

Ponieważ nie wskazałeś, czy chcesz n-gramów na słowo, czy na poziomie postaci, po prostu przyjmiemy to pierwsze, bez utraty ogólności.

Zakładam też, że zaczynasz od listy tokenów, reprezentowanych przez ciągi. To, co możesz łatwo zrobić, to samemu napisać n-gramową ekstrakcję.

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] 

Następnie wymień yield z faktycznym działaniem chcesz wziąć na każdy n-gram (dodaj go do dict, należy go przechowywać w bazie danych, cokolwiek), aby pozbyć się napowietrznej generatora.

Wreszcie, jeśli naprawdę nie jest wystarczająco szybki, zamień powyższy kod na Cython i skompiluj go. Przykład z użyciem defaultdict zamiast 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

Nowsze wersje Cython rozpoznają w Pythonie instrukcje i przyspieszają je, jeśli to możliwe. Ponadto w wewnętrznej iteracji znajduje się odnośnik do metody. zdefiniowanie 'tokenjoiner = "" .join' poza pętlą i zastąpienie wewnętrznego ".join powinno przyspieszyć działanie. – rocksportrocker

+0

@rocksportrocker: dobre miejsce, dodano Twoją sugestię. –

+0

i można przepisać wewnętrzną linię za pomocą "count.get (....) + = 1" wprowadzić inny var, aby uniknąć wyszukiwania metod. – rocksportrocker

7

Można znaleźć pythonic, elegancki i szybki funkcję generacji Ngram użyciu zip i ikona (*) operatora here:

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

Na znak poziomu N- gramów można użyć następującą funkcję:

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