J'ai une méthode qui prend un paramètre String et utilise NLTK pour décomposer la chaîne en phrases, puis en mots. Ensuite, il convertit chaque mot en minuscules et crée enfin un dictionnaire de la fréquence de chaque mot.
import nltk
from collections import Counter
def freq(string):
f = Counter()
sentence_list = nltk.tokenize.sent_tokenize(string)
for sentence in sentence_list:
words = nltk.Word_tokenize(sentence)
words = [Word.lower() for Word in words]
for Word in words:
f[Word] += 1
return f
Je suis censé optimiser davantage le code ci-dessus pour accélérer le temps de prétraitement et je ne sais pas comment le faire. La valeur de retour doit évidemment être exactement la même que ci-dessus, donc je suis censé utiliser nltk bien qu'il ne soit pas explicitement requis pour le faire.
Une façon d'accélérer le code ci-dessus? Merci.
Si vous voulez juste une liste plate de jetons, notez que Word_tokenize
Appellerait sent_tokenize
Implicitement, voir https://github.com/nltk/nltk/blob/develop/nltk /tokenize/init.py#L98
_treebank_Word_tokenize = TreebankWordTokenizer().tokenize
def Word_tokenize(text, language='english'):
"""
Return a tokenized copy of *text*,
using NLTK's recommended Word tokenizer
(currently :class:`.TreebankWordTokenizer`
along with :class:`.PunktSentenceTokenizer`
for the specified language).
:param text: text to split into sentences
:param language: the model name in the Punkt corpus
"""
return [token for sent in sent_tokenize(text, language)
for token in _treebank_Word_tokenize(sent)]
En utilisant un corpus brun comme exemple, avec Counter(Word_tokenize(string_corpus))
:
>>> from collections import Counter
>>> from nltk.corpus import brown
>>> from nltk import sent_tokenize, Word_tokenize
>>> string_corpus = brown.raw() # Plaintext, str type.
>>> start = time.time(); fdist = Counter(Word_tokenize(string_corpus)); end = time.time() - start
>>> end
12.662328958511353
>>> fdist.most_common(5)
[(u',', 116672), (u'/', 89031), (u'the/at', 62288), (u'.', 60646), (u'./', 48812)]
>>> sum(fdist.values())
1423314
~ 1,4 million de mots ont pris 12 secondes (sans enregistrer le corpus à jetons) sur ma machine avec les spécifications:
alvas@ubi:~$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 69
model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
stepping : 1
microcode : 0x17
cpu MHz : 1600.027
cache size : 3072 KB
physical id : 0
siblings : 4
core id : 0
cpu cores : 2
$ cat /proc/meminfo
MemTotal: 12004468 kB
Enregistrer le corpus à jetons d'abord tokenized_corpus = [Word_tokenize(sent) for sent in sent_tokenize(string_corpus)]
, puis utiliser Counter(chain*(tokenized_corpus))
:
>>> from itertools import chain
>>> start = time.time(); tokenized_corpus = [Word_tokenize(sent) for sent in sent_tokenize(string_corpus)]; fdist = Counter(chain(*tokenized_corpus)); end = time.time() - start
>>> end
16.421464920043945
Utilisation de ToktokTokenizer()
>>> from collections import Counter
>>> import time
>>> from itertools import chain
>>> from nltk.corpus import brown
>>> from nltk import sent_tokenize, Word_tokenize
>>> from nltk.tokenize import ToktokTokenizer
>>> toktok = ToktokTokenizer()
>>> string_corpus = brown.raw()
>>> start = time.time(); tokenized_corpus = [toktok.tokenize(sent) for sent in sent_tokenize(string_corpus)]; fdist = Counter(chain(*tokenized_corpus)); end = time.time() - start
>>> end
10.00472116470337
Utilisation de MosesTokenizer()
:
>>> from nltk.tokenize.moses import MosesTokenizer
>>> moses = MosesTokenizer()
>>> start = time.time(); tokenized_corpus = [moses.tokenize(sent) for sent in sent_tokenize(string_corpus)]; fdist = Counter(chain(*tokenized_corpus)); end = time.time() - start
>>> end
30.783339023590088
>>> start = time.time(); tokenized_corpus = [moses.tokenize(sent) for sent in sent_tokenize(string_corpus)]; fdist = Counter(chain(*tokenized_corpus)); end = time.time() - start
>>> end
30.559681177139282
Pourquoi utiliser MosesTokenizer
Il a été implémenté de telle manière qu'il existe un moyen de retourner les jetons en chaîne, c'est-à-dire "detokenize".
>>> from nltk.tokenize.moses import MosesTokenizer, MosesDetokenizer
>>> t, d = MosesTokenizer(), MosesDetokenizer()
>>> sent = "This ain't funny. It's actually hillarious, yet double Ls. | [] < > [ ] & You're gonna shake it off? Don't?"
>>> expected_tokens = [u'This', u'ain', u''t', u'funny.', u'It', u''s', u'actually', u'hillarious', u',', u'yet', u'double', u'Ls.', u'|', u'[', u']', u'<', u'>', u'[', u']', u'&', u'You', u''re', u'gonna', u'shake', u'it', u'off', u'?', u'Don', u''t', u'?']
>>> expected_detokens = "This ain't funny. It's actually hillarious, yet double Ls. | [] < > [] & You're gonna shake it off? Don't?"
>>> tokens = t.tokenize(sent)
>>> tokens == expected_tokens
True
>>> detokens = d.detokenize(tokens)
>>> " ".join(detokens) == expected_detokens
True
Utilisation de ReppTokenizer()
:
>>> repp = ReppTokenizer('/home/alvas/repp')
>>> start = time.time(); sentences = sent_tokenize(string_corpus); tokenized_corpus = repp.tokenize_sents(sentences); fdist = Counter(chain(*tokenized_corpus)); end = time.time() - start
>>> end
76.44129395484924
Pourquoi utiliser ReppTokenizer
?
Il renvoie l'offset des jetons de la chaîne d'origine.
>>> sents = ['Tokenization is widely regarded as a solved problem due to the high accuracy that rulebased tokenizers achieve.' ,
... 'But rule-based tokenizers are hard to maintain and their rules language specific.' ,
... 'We evaluated our method on three languages and obtained error rates of 0.27% (English), 0.35% (Dutch) and 0.76% (Italian) for our best models.'
... ]
>>> tokenizer = ReppTokenizer('/home/alvas/repp/') # doctest: +SKIP
>>> for sent in sents: # doctest: +SKIP
... tokenizer.tokenize(sent) # doctest: +SKIP
...
(u'Tokenization', u'is', u'widely', u'regarded', u'as', u'a', u'solved', u'problem', u'due', u'to', u'the', u'high', u'accuracy', u'that', u'rulebased', u'tokenizers', u'achieve', u'.')
(u'But', u'rule-based', u'tokenizers', u'are', u'hard', u'to', u'maintain', u'and', u'their', u'rules', u'language', u'specific', u'.')
(u'We', u'evaluated', u'our', u'method', u'on', u'three', u'languages', u'and', u'obtained', u'error', u'rates', u'of', u'0.27', u'%', u'(', u'English', u')', u',', u'0.35', u'%', u'(', u'Dutch', u')', u'and', u'0.76', u'%', u'(', u'Italian', u')', u'for', u'our', u'best', u'models', u'.')
>>> for sent in tokenizer.tokenize_sents(sents):
... print sent
...
(u'Tokenization', u'is', u'widely', u'regarded', u'as', u'a', u'solved', u'problem', u'due', u'to', u'the', u'high', u'accuracy', u'that', u'rulebased', u'tokenizers', u'achieve', u'.')
(u'But', u'rule-based', u'tokenizers', u'are', u'hard', u'to', u'maintain', u'and', u'their', u'rules', u'language', u'specific', u'.')
(u'We', u'evaluated', u'our', u'method', u'on', u'three', u'languages', u'and', u'obtained', u'error', u'rates', u'of', u'0.27', u'%', u'(', u'English', u')', u',', u'0.35', u'%', u'(', u'Dutch', u')', u'and', u'0.76', u'%', u'(', u'Italian', u')', u'for', u'our', u'best', u'models', u'.')
>>> for sent in tokenizer.tokenize_sents(sents, keep_token_positions=True):
... print sent
...
[(u'Tokenization', 0, 12), (u'is', 13, 15), (u'widely', 16, 22), (u'regarded', 23, 31), (u'as', 32, 34), (u'a', 35, 36), (u'solved', 37, 43), (u'problem', 44, 51), (u'due', 52, 55), (u'to', 56, 58), (u'the', 59, 62), (u'high', 63, 67), (u'accuracy', 68, 76), (u'that', 77, 81), (u'rulebased', 82, 91), (u'tokenizers', 92, 102), (u'achieve', 103, 110), (u'.', 110, 111)]
[(u'But', 0, 3), (u'rule-based', 4, 14), (u'tokenizers', 15, 25), (u'are', 26, 29), (u'hard', 30, 34), (u'to', 35, 37), (u'maintain', 38, 46), (u'and', 47, 50), (u'their', 51, 56), (u'rules', 57, 62), (u'language', 63, 71), (u'specific', 72, 80), (u'.', 80, 81)]
[(u'We', 0, 2), (u'evaluated', 3, 12), (u'our', 13, 16), (u'method', 17, 23), (u'on', 24, 26), (u'three', 27, 32), (u'languages', 33, 42), (u'and', 43, 46), (u'obtained', 47, 55), (u'error', 56, 61), (u'rates', 62, 67), (u'of', 68, 70), (u'0.27', 71, 75), (u'%', 75, 76), (u'(', 77, 78), (u'English', 78, 85), (u')', 85, 86), (u',', 86, 87), (u'0.35', 88, 92), (u'%', 92, 93), (u'(', 94, 95), (u'Dutch', 95, 100), (u')', 100, 101), (u'and', 102, 105), (u'0.76', 106, 110), (u'%', 110, 111), (u'(', 112, 113), (u'Italian', 113, 120), (u')', 120, 121), (u'for', 122, 125), (u'our', 126, 129), (u'best', 130, 134), (u'models', 135, 141), (u'.', 141, 142)]
TL; DR
Avantages des différents tokenizers
Word_tokenize()
appelle implicitement sent_tokenize()
ToktokTokenizer()
est la plus rapideMosesTokenizer()
est capable de détokéniser le texteReppTokenizer()
est capable de fournir des décalages de jetonsQ: Existe-t-il un tokenizer rapide qui peut détokenizer et me fournit également des décalages et également effectuer la tokenisation de phrases en NLTK?
R: Je ne pense pas, essayez gensim
ou spacy
.
Votre code est implicitement créant beaucoup d'instances list
potentiellement très longues qui n'ont pas besoin d'être là, par exemple:
words = [Word.lower() for Word in words]
En utilisant le [...]
syntaxe pour compréhension de la liste crée une liste de longueur n pour n jetons trouvés dans votre entrée, mais tout ce que vous voulez faire est d'obtenir la fréquence de chaque jeton, pas de les stocker réellement:
f[Word] += 1
Par conséquent, vous devez utiliser un générateur à la place:
words = (Word.lower() for Word in words)
De même, nltk.tokenize.sent_tokenize
et nltk.tokenize.Word_tokenize
les deux semblent produire des listes en sortie, ce qui est encore inutile; Essayez d'utiliser une fonction de bas niveau, par ex. nltk.tokenize.api.StringTokenizer.span_tokenize
, qui génère simplement un itérateur qui produit des décalages de jetons pour votre flux d'entrée, c'est-à-dire des paires d'indices de votre chaîne d'entrée représentant chaque jeton.
Voici un exemple n'utilisant aucune liste intermédiaire:
def freq(string):
'''
@param string: The string to get token counts for. Note that this should already have been normalized if you wish it to be so.
@return: A new Counter instance representing the frequency of each token found in the input string.
'''
spans = nltk.tokenize.WhitespaceTokenizer().span_tokenize(string)
# Yield the relevant slice of the input string representing each individual token in the sequence
tokens = (string[begin : end] for (begin, end) in spans)
return Counter(tokens)
Avis de non-responsabilité: Je n'ai pas profilé cela, il est donc possible que, par exemple le peuple NLTK a fait Word_tokenize
incroyablement rapide mais négligé span_tokenize
; Assurez-vous toujours de profiler votre application.
N'utilisez pas de listes lorsque les générateurs suffiront: chaque fois que vous créez une liste juste pour la jeter après l'avoir utilisée une fois, Dieu tue un chaton.