2015-11-20 9 views
5

Chcę być w stanie uzyskać POS Tagi zdań jeden po drugim jak w ten sposób:Dlaczego pos_tag() jest tak boleśnie powolny i czy można tego uniknąć?

def __remove_stop_words(self, tokenized_text, stop_words): 

    sentences_pos = nltk.pos_tag(tokenized_text) 
    filtered_words = [word for (word, pos) in sentences_pos 
         if pos not in stop_words and word not in stop_words] 

    return filtered_words 

Ale problemem jest to, że pos_tag() trwa około sekundę za każdym zdaniu. Istnieje inna opcja użycia pos_tag_sents(), aby zrobić to w sposób wsadowy i przyspieszyć działanie. Ale moje życie byłoby łatwiejsze, gdybym mógł zrobić to zdanie w zdaniu.

Czy istnieje sposób, aby zrobić to szybciej?

+0

Jakiej wersji nltk używasz? (tj. 'nltk .__ version__') – unutbu

+0

to 3.1 (asdfasdf) – displayname

+0

Tło: http://stackoverflow.com/questions/33676526/pos-tagger-jest-wydajnie-slow#comment55128218_33677051 – tripleee

Odpowiedz

14

Dla NLTK w wersji 3.1, wewnątrz nltk/tag/__init__.py, pos_tag jest zdefiniowany następująco:

from nltk.tag.perceptron import PerceptronTagger 
def pos_tag(tokens, tagset=None): 
    tagger = PerceptronTagger() 
    return _pos_tag(tokens, tagset, tagger)  

więc każde wywołanie pos_tag pierwszą instancję PerceptronTagger który zajmuje trochę czasu, ponieważ wiąże loading a pickle file. _pos_tagsimply calls tagger.tag, gdy tagset jest None. Więc można zaoszczędzić trochę czasu poprzez załadowanie pliku raz i nazywając tagger.tag siebie zamiast dzwonić pos_tag:

from nltk.tag.perceptron import PerceptronTagger 
tagger = PerceptronTagger() 
def __remove_stop_words(self, tokenized_text, stop_words, tagger=tagger): 
    sentences_pos = tagger.tag(tokenized_text) 
    filtered_words = [word for (word, pos) in sentences_pos 
         if pos not in stop_words and word not in stop_words] 

    return filtered_words 

pos_tag_sents wykorzystuje tę samą sztuczkę jak powyżej - it instantiates PerceptronTagger once przed wywołaniem _pos_tag wielokrotnie. Więc otrzymasz porównywalną poprawę wydajności przy użyciu powyższego kodu, tak jak robiłbyś to przez refaktoryzację i wywołanie pos_tag_sents.


Ponadto, jeśli stop_words długa lista, można zaoszczędzić trochę czasu poprzez stop_words zestaw:

stop_words = set(stop_words) 

od sprawdzenia członkostwa w zestawie (np pos not in stop_words) jest O(1) (stały czas) podczas sprawdzania członkostwa na liście jest operacja O(n) (tj. wymaga czasu, który rośnie proporcjonalnie do długości listy).

+0

Teraz o tym mówię! To dziwna myśl, że muszę to zrobić w ten sposób. Chodzi mi o to, jak często zdarza się, że ktoś chce tylko oznaczyć * jednym * pojedynczym zdaniem? Byłoby interesujące dowiedzieć się, dlaczego twórcy zdecydowali, że to dobry pomysł, ale cieszę się, że mogę obejść ten problem. Dziękuję Ci! – displayname

+1

Cóż, 'pos_tag()' jest funkcją. Jeśli wywołasz funkcję, której oczekujesz, po wykonaniu tej funkcji uzyskasz wynik i nic nie pozostanie w pamięci. – thorsten

+3

@StefanFalk, 'pos_tag' ma być typem kaczki. Częściowo dlatego, aby zachować zgodność z NLTK <3.1. Zawsze używaj określonego taggera, gdy to możliwe. =) – alvas

Powiązane problemy