2013-01-02 7 views
5

Mi piacerebbe incorporare un tagger personalizzato in un'applicazione Web (in esecuzione su Pyramid) che sto sviluppando. Ho il tagger che funziona bene sul mio computer locale utilizzando NLTK, ma ho letto che NLTK è relativamente lento per la produzione.NLTK su un'applicazione web di produzione

Sembra che il modo standard di memorizzare il tagger sia quello di metterlo sottaceto. Sulla mia macchina, ci vogliono alcuni secondi per caricare il file pickle da 11,7 MB.

  1. NLTK è anche pratico per la produzione? Dovrei guardare scikit-learn o anche qualcosa come Mahout?

  2. Se NLTK è abbastanza buono, qual è il modo migliore per assicurarsi che usi correttamente la memoria, ecc.?

+1

In primo luogo, stai usando Python 2? Se è così, stai usando 'pickle' o' cPickle'? Passare a 'cPickle' (o a Python 3, dove le due implementazioni sono unite in un singolo modulo) potrebbe trasformare i tuoi pochi secondi in poche decine di millisecondi. In alternativa, se è necessario utilizzare lo stesso tagger per tutte le richieste, perché caricarlo per ogni richiesta? Caricalo una volta (o una volta per processo, o qualsiasi altra cosa - non conosco Pyramid), e quindi non importa quanto tempo ci vuole. – abarnert

+2

Per prove su quanto sopra, provare questo: 'p = cPickle.dumps (intervallo (1250000)); print timeit.timeit (lambda: pickle.loads (s), number = 1); print timeit.timeit (lambda: cPickle.loads (s), number = 1) '. Ottengo 4,96 vs 0,35 sul mio sistema. – abarnert

+0

Sto usando Python 2 e normale pickle. Daremo un'occhiata a cPickle. Sì, non ero del tutto sicuro del modo migliore per caricare il file. Devo esaminare le opzioni di caricamento per Pyramid – abroekhof

risposta

5

Corro text-processing e il suo associato NLP APIs e utilizza circa 2 dozzine di diversi modelli in salamoia, che vengono caricati da un'applicazione Django (gunicorn dietro nginx). I modelli vengono caricati non appena sono necessari e, una volta caricati, rimangono in memoria. Ciò significa che ogni volta che riavvio il server gunicorn, le prime richieste che richiedono un modello devono attendere alcuni secondi per il caricamento, ma ogni richiesta successiva ottiene l'utilizzo del modello già memorizzato nella RAM. Il riavvio avviene solo quando distribuisco nuove funzionalità, che di solito comporta l'aggiornamento dei modelli, quindi ho bisogno di ricaricarli comunque. Quindi, se non ti aspetti di apportare modifiche al codice molto spesso e non hai requisiti elevati in termini di richieste coerenti, probabilmente non hai bisogno di un demone separato.

Oltre al tempo di caricamento iniziale, il fattore limitante principale è la memoria. Attualmente ho solo 1 processo di lavoro, perché quando tutti i modelli vengono caricati in memoria, un singolo processo può richiedere fino a 1 GB (YMMV, e per un singolo file pickle da 11 MB, i requisiti di memoria saranno molto più bassi). L'elaborazione di una singola richiesta con un modello già caricato è abbastanza veloce (di solito < 50ms) che attualmente non ho bisogno di più di 1 operatore e, se lo faccio, la soluzione più semplice è quella di aggiungere abbastanza RAM per eseguire più processi di lavoro.

Se si è preoccupati per la memoria, quindi esaminare in scikit-imparare, poiché i modelli equivalenti possono utilizzare una memoria molto meno di NLTK. Ma non sono necessariamente più veloci o più accurati.

+0

Grazie per la risposta completa, è molto utile. Potresti indicarmi la modalità di caricamento dei modelli in memoria durante l'inizializzazione? Ho cercato su Google "cache di Django", ecc., Ma mi piacerebbe assicurarmi di guardare il materiale corretto. – abroekhof

+0

In alcuni dei miei moduli dichiaro un dict vuoto, quindi nelle funzioni che richiedono i modelli, controllo se il modello è nel dict e, in caso contrario, utilizzo nltk.data.load con il nome file pickle per caricarlo e memorizzare un riferimento nel dett. Quindi tutte le chiamate future a tale funzione utilizzeranno il modello già caricato. Se vuoi caricare immediatamente il modello, puoi farlo a livello di modulo e solo dichiararlo, come "mymodel = nltk.data.load ('path/to/model.pickle')". – Jacob

2

Il modo migliore per ridurre la latenza di start-up è quello di eseguire il tagger come demone (servizio persistente) che il web app invia frammenti di testo per etichettare. In questo modo il tagger viene caricato solo all'avvio del sistema e se/quando il daemon deve essere riavviato.

Solo tu puoi decidere se NLTK è abbastanza veloce per le tue esigenze. Una volta caricato il tagger, probabilmente avrai notato che il NLTK può taggare diverse pagine di testo senza ritardi percettibili. Ma il consumo di risorse e il numero di utenti concorrenti potrebbero complicare le cose.

+0

Sì, la velocità di codifica di NLTK sembra sufficiente una volta caricata, quindi si tratterà di cosa succede con più utenti. I demoni sono un'area completamente nuova per me. Ciò comporterebbe l'utilizzo di qualcosa come Twisted o Pyro e un sistema di accodamento? – abroekhof

+0

Ci sono innumerevoli opzioni, a seconda del sistema operativo, del livello previsto di cuncurrency, del numero di CPU (si potrebbe avere un gruppo di "lavoratori"), ecc. La risposta di Jacob descrive una possibile configurazione. – alexis