Classificazione del testo Reuters-21578 con Gensim e Keras

Reuters-21578 è una raccolta di circa 20K righe di notizie (vedere il riferimento per maggiori informazioni, download e note sul copyright), strutturate utilizzando SGML e categorizzate con 672 etichette. Sono suddivisi in cinque categorie principali:

    • Argomenti
    • Luoghi
    • Persone
    • Organizzazioni
    • Scambi

Tuttavia, la maggior parte di essi è inutilizzata e, osservando la distribuzione, è possibile notare una completa mancanza di omogeneità. Queste sono le 20 categorie principali (il prefisso è composto dalle due lettere iniziali di ogni categoria principale) con il numero di news-line correlate:

ID Nome Categoria Newslines
161 pl_usa Luoghi 12542
533 guadagnare Argomenti 3987
498 to_acq Argomenti 2448
158 pl_uk Luoghi 1489
84 pl_giappone Luoghi 1138
31 pl_canada Luoghi 1104
571 to_money-fx Argomenti 801
526 to_crude Argomenti 634
543 to_grain Argomenti 628
167 pl_ovest-germania Luoghi 567
624 to_trade Argomenti 552
553 a_interesse Argomenti 513
56 pl_francia Luoghi 469
185 or_ec Organizzazioni 349
23 pl_brasile Luoghi 332
628 to_wheat Argomenti 306
606 a_nave Argomenti 305
10 pl_australia Luoghi 270
517 to_corn Argomenti 254
37 pl_china Luoghi 223

Nell'”esperimento” (come notebook Jupyter) che può trovare su questo repository Github, ho definito una pipeline per un metodo di categorizzazione One-Vs-Rest utilizzando Word2Vec (implementato da Gensim), che è molto più efficace di un approccio standard bag-of-words o Tf-Idf, e le reti neurali LSTM (modellate con Keras con supporto Theano/GPU). La pipeline si basa sulle seguenti fasi (proprio come un approccio di analisi del sentimento):

    1. Acquisizione di categorie e documenti (suggerisco di vedere il codice completo su Github). Tuttavia, ho utilizzato BeautifulSoup per analizzare tutti i file SGML, eliminando tutti i tag indesiderati e una semplice regex per eliminare la firma finale.
    2. Tokenizzazione (con lemmatizzazione Wordnet e filtraggio delle stop-words, entrambi implementati dal framework NLTK)# Caricare le stop-word

 

# Load stop-words
stop_words = set(stopwords.words('english'))

# Initialize tokenizer
# It's also possible to try with a stemmer or to mix a stemmer and a lemmatizer
tokenizer = RegexpTokenizer('['a-zA-Z]+')

# Initialize lemmatizer
lemmatizer = WordNetLemmatizer()

# Tokenized document collection
newsline_documents = []

def tokenize(document):
 words = []

 for sentence in sent_tokenize(document):
 tokens = [lemmatizer.lemmatize(t.lower()) for t in tokenizer.tokenize(sentence) if t.lower() not in stop_words]
 words += tokens

 return words

Formazione Word2Vec:

# Create new Gensim Word2Vec model
w2v_model = Word2Vec(newsline_documents, size=num_features, min_count=1, window=10, workers=cpu_count())
w2v_model.init_sims(replace=True)
w2v_model.save(data_folder + 'reuters.word2vec')

Conversione Word2Vec

num_categories = len(selected_categories)
X = zeros(shape=(number_of_documents, document_max_num_words, num_features)).astype(float32)
Y = zeros(shape=(number_of_documents, num_categories)).astype(float32)

empty_word = zeros(num_features).astype(float32)

for idx, document in enumerate(newsline_documents):
    for jdx, word in enumerate(document):
        if jdx == document_max_num_words:
            break
            
        else:
            if word in w2v_model:
                X[idx, jdx, :] = w2v_model[word]
            else:
                X[idx, jdx, :] = empty_word

for idx, key in enumerate(document_Y.keys()):
    Y[idx, :] = document_Y[key]

Formazione della rete LSTM

model = Sequential()

model.add(LSTM(int(document_max_num_words*1.5), input_shape=(document_max_num_words, num_features)))
model.add(Dropout(0.3))
model.add(Dense(num_categories))
model.add(Activation('sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

# Train model
model.fit(X_train, Y_train, batch_size=128, nb_epoch=5, validation_data=(X_test, Y_test))

# Evaluate model
score, acc = model.evaluate(X_test, Y_test, batch_size=128)
    
print('Score: %1.4f' % score)
print('Accuracy: %1.4f' % acc)

Ho testato diversi modelli neurali e ho ottenuto i migliori risultati con uno strato LSTM di 150 celle (1,5 x numero di parole), un dropout di 0,3-0,4 e un ottimizzatore “Adam”. Una dimensione del batch inferiore a 64 può causare un rallentamento del tasso di apprendimento. Nei miei esperimenti, 128 è un valore eccellente. Dopo cinque epoche (dovrebbero essere incrementate in uno scenario reale), ho questi risultati:

Train on 15104 samples, validate on 6474 samples
Epoch 1/5
15104/15104 [==============================] - 38s - loss: 0.5610 - acc: 0.7168 - val_loss: 0.6814 - val_acc: 0.5828
Epoch 2/5
15104/15104 [==============================] - 40s - loss: 0.5994 - acc: 0.6597 - val_loss: 0.4435 - val_acc: 0.8230
Epoch 3/5
15104/15104 [==============================] - 37s - loss: 0.3949 - acc: 0.8477 - val_loss: 0.3765 - val_acc: 0.8557
Epoch 4/5
15104/15104 [==============================] - 38s - loss: 0.3567 - acc: 0.8707 - val_loss: 0.3415 - val_acc: 0.8735
Epoch 5/5
15104/15104 [==============================] - 37s - loss: 0.3408 - acc: 0.8761 - val_loss: 0.3269 - val_acc: 0.8837
6474/6474 [==============================] - 5s     
Score: 0.3269
Accuracy: 0.8837

Riferimento


Share this post on:
FacebookTwitterPinterestEmail