2015-05-12 23 views
13

In Python, sto usando NLTK's alignment module per creare allineamenti di parole tra testi paralleli. Allineare i bitexts può essere un processo che richiede molto tempo, specialmente se eseguito su corpora considerevoli. Sarebbe bello fare allineamenti in batch un giorno e utilizzare quegli allineamenti in seguito.Come salvare i modelli di allineamento NLTK Python per un uso successivo?

from nltk import IBMModel1 as ibm 
biverses = [list of AlignedSent objects] 
model = ibm(biverses, 20) 

with open(path + "eng-taq_model.txt", 'w') as f: 
    f.write(model.train(biverses, 20)) // makes empty file 

Una volta che creo un modello, come posso (1) salvarlo su disco e (2) riutilizzare in un secondo momento?

risposta

7

La risposta immediata è di fare la serializzazione, vedi https://wiki.python.org/moin/UsingPickle

Ma perché IBMModel1 restituisce una funzione lambda, non è possibile fare la serializzazione con il default pickle/cPickle (vedere https://github.com/nltk/nltk/blob/develop/nltk/align/ibm1.py#L74 e https://github.com/nltk/nltk/blob/develop/nltk/align/ibm1.py#L104)

Così abbiamo userò dill. In primo luogo, installare dill, vedere Can Python pickle lambda functions?

$ pip install dill 
$ python 
>>> import dill as pickle 

Poi:

>>> import dill 
>>> import dill as pickle 
>>> from nltk.corpus import comtrans 
>>> from nltk.align import IBMModel1 
>>> bitexts = comtrans.aligned_sents()[:100] 
>>> ibm = IBMModel1(bitexts, 20) 
>>> with open('model1.pk', 'wb') as fout: 
...  pickle.dump(ibm, fout) 
... 
>>> exit() 

Per utilizzare il modello in salamoia:

>>> import dill as pickle 
>>> from nltk.corpus import comtrans 
>>> bitexts = comtrans.aligned_sents()[:100] 
>>> with open('model1.pk', 'rb') as fin: 
...  ibm = pickle.load(fin) 
... 
>>> aligned_sent = ibm.align(bitexts[0]) 
>>> aligned_sent.words 
['Wiederaufnahme', 'der', 'Sitzungsperiode'] 

Se si tenta di serializzazione dell'oggetto IBMModel1, che è un funzione lambda, ti ritroverai con questo:

>>> import cPickle as pickle 
>>> from nltk.corpus import comtrans 
>>> from nltk.align import IBMModel1 
>>> bitexts = comtrans.aligned_sents()[:100] 
>>> ibm = IBMModel1(bitexts, 20) 
>>> with open('model1.pk', 'wb') as fout: 
...  pickle.dump(ibm, fout) 
... 
Traceback (most recent call last): 
    File "<stdin>", line 2, in <module> 
    File "/usr/lib/python2.7/copy_reg.py", line 70, in _reduce_ex 
    raise TypeError, "can't pickle %s objects" % base.__name__ 
TypeError: can't pickle function objects 

(Nota: il frammento di codice di cui sopra deriva dalla versione 3.0.0 NLTK)

In python3 con NLTK 3.0.0, sarà anche affrontare lo stesso problema perché IBMModel1 restituisce una funzione lambda:

[email protected]:~$ python3 
Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import pickle 
>>> from nltk.corpus import comtrans 
>>> from nltk.align import IBMModel1 
>>> bitexts = comtrans.aligned_sents()[:100] 
>>> ibm = IBMModel1(bitexts, 20) 
>>> with open('mode1.pk', 'wb') as fout: 
...  pickle.dump(ibm, fout) 
... 
Traceback (most recent call last): 
    File "<stdin>", line 2, in <module> 
_pickle.PicklingError: Can't pickle <function IBMModel1.train.<locals>.<lambda> at 0x7fa37cf9d620>: attribute lookup <lambda> on nltk.align.ibm1 failed' 

>>> import dill 
>>> with open('model1.pk', 'wb') as fout: 
...  dill.dump(ibm, fout) 
... 
>>> exit() 

[email protected]:~$ python3 
Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import dill 
>>> from nltk.corpus import comtrans 
>>> with open('model1.pk', 'rb') as fin: 
...  ibm = dill.load(fin) 
... 
>>> bitexts = comtrans.aligned_sents()[:100] 
>>> aligned_sent = ibm.aligned(bitexts[0]) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'IBMModel1' object has no attribute 'aligned' 
>>> aligned_sent = ibm.align(bitexts[0]) 
>>> aligned_sent.words 
['Wiederaufnahme', 'der', 'Sitzungsperiode'] 

(Nota: In python3, pickle è cPickle, vedi http://docs.pythonsprints.com/python3_porting/py-porting.html)

+0

I' Non sono sicuro di quello che hai provato, ma non ho visto nessun lambda e non ho avuto problemi a decapitare e disfare il "modello" con la sottaceti alla vaniglia. – alexis

+0

@alexis È interessante, hai ricevuto lo stesso errore della risposta aggiornata? – alvas

+0

Non ho ancora avuto la possibilità di provarlo; ma avrei potuto provare il pickling con python 2, che spiegherebbe la diversa esperienza (non avevo ancora capito che il modulo era cambiato così tanto). Ti farò sapere quando ci provo. – alexis

3

di discutere il salvataggio del modello di assetto, ma la tua domanda sembra essere di più salvando i bitex allineati che hai allineato: "Sarebbe bello fare allineamenti in batch un giorno e usare quegli allineamenti in seguito." Ho intenzione di rispondere a questa domanda.

Nell'ambiente nltk, il modo migliore per utilizzare una risorsa simile a un corpus per accedervi con un lettore di corpus. Il NLTK non viene fornito con corpus scrittori, ma il formato supportato dal AlignedCorpusReader del NLTK è molto facile da generare: (NLTK versione 3)

model = ibm(biverses, 20) # As in your question 

out = open("folder/newalignedtext.txt", "w") 
for pair in biverses: 
    asent = model.align(pair) 
    out.write(" ".join(asent.words)+"\n") 
    out.write(" ".join(asent.mots)+"\n") 
    out.write(str(asent.alignment)+"\n") 

out.close() 

Questo è tutto. In seguito è possibile ricaricare e utilizzare le vostre frasi allineati esattamente come ci si utilizza la comtrans corpus:

from nltk.corpus.reader import AlignedCorpusReader 

mycorpus = AlignedCorpusReader(r"folder", r".*\.txt") 
biverses_reloaded = mycorpus.aligned_sents() 

Come si può vedere, non è necessario l'oggetto di allineamento in sé. Le frasi allineate possono essere caricate con un lettore di corpus, e l'allineatore stesso è piuttosto inutile a meno che non si voglia esaminare le probabilità incorporate nello studio .

Commento: Non sono sicuro che chiamerei l'oggetto aligner un "modello". In NLTK 2, l'aligner non è impostato per allineare il nuovo testo, non ha nemmeno un metodo align(). In NLTK 3 la funzione align() può allineare il nuovo testo ma solo se usato da python 2; in Python 3 è rotto, apparentemente a causa delle regole più rigide per il confronto di oggetti di tipi diversi. Se tuttavia vuoi essere in grado di decapare e ricaricare l'aligner, sarò felice di aggiungerlo alla mia risposta; da quello che ho visto si può fare con vaniglia cPickle.

+0

La funzione di allineamento è un modello perché impara la probabilità per ogni parola della lingua di destinazione data una parola della lingua di origine. Sebbene sia possibile archiviarlo come una grande tabella hash, è l'autore del codice che ha deciso di archiviarlo come una funzione lambda che restituisce un valore predefinito. https://github.com/nltk/nltk/blob/develop/nltk/align/ibm1.py#L74 – alvas

+0

Quindi, data la probabilità appresa, è possibile assegnare le probabilità a nuovi dati, ecco perché si chiama un modello. Tuttavia, sono d'accordo con te sul fatto che non è naturale salvare il modello perché dati i nuovi dati, potresti semplicemente ricostruire il modello di probabilità. Vedi http://www.cs.columbia.edu/~mcollins/courses/nlp2011/notes/ibm12.pdf per la spiegazione teorica. – alvas

+0

BTW, 'nltk.align' non è danneggiato in python3. – alvas

1

se si vuole, e sembra che, è possibile memorizzare come un elenco AlignedSent:

from nltk.align import IBMModel1 as IBM 
from nltk.align import AlignedSent 
import dill as pickle 

biverses = [list of AlignedSent objects] 
model = ibm(biverses, 20) 

for sent in range(len(biverses)): 
    biverses[sent].alignment = model.align(biverses[sent]).alignment 

Dopo di che, è possibile salvarlo con aneto come sottaceto:

with open('alignedtext.pk', 'wb') as arquive: 
    pickle.dump(biverses, arquive)