Teaching - TAL

Page principale de l'UE


Traitement Automatique de la Langue

Transformation en sacs de mots, vectorisation des documents

Solution 0: construction d'un dictionnaire à la main

lien vers le tutoriel

Solution 1: conviviale (mais lente), sklearn

Le module sklearn, plutôt spécialisé dans les modèles statistiques, dispose d'un outil de vectorisation des documents textuels. Cet outils est très simple à utiliser... mais très lent.

Distinguons l'usage de base (peu intéressant), la paramétrisation (essentielle) et les traitements suivants (transformation de type tf-idf).

Usage de base

import sklearn.feature_extraction.text as txt

alldocs = ... # liste contenant l'ensemble des documents du corpus d'apprentissage
vec = txt.CountVectorizer() # outil de vectorisation
bow = vec.fit_transform(alltxt) # instatiation de l'outil de vectorisation sur le corpus
X = bow.tocsr() # passage vers les matrices sparses compatibles avec les modèles statistiques de sklearn

Paramétrisation

L'intérêt de cet outil de vectorisation réside dans les options disponibles:

  • Etude sur les mots, les lettres
  • N-grams
  • Elimination des stopwords
    • Note: l'implémentation ci-dessous repose sur les listes issues de nltk, il faut probablement les télécharger (import nltk puis nltk.download() )
  • Mode de séparation des mots (garder ou pas les apostrophes...)
  • fréquences de coupure
from nltk.corpus import stopwords

    ###################################
    # bags of words: parametrage
    analyzer = u'word' # {‘word’, ‘char’, ‘char_wb’}
    ngram_range = (1, 1) # unigrammes
    languages = ['french', 'english', 'german', 'spanish']
    stop_words = []
    for l in languages:
        for w in stopwords.words(l):
            stop_words.append(w.decode('utf-8'))
    lowercase = True
    token = u"[\\w']+\\w\\b" #
    max_df=1.0 #default
    # mots apparaissant plus de 5 fois
    min_df=5. * 1./len(alltxt) # on enleve les mots qui apparaissent moins de 5 fois
    max_features = 10000
    binary=True # presence coding
    strip_accents = u'ascii' #  {‘ascii’, ‘unicode’, None}
    #
    vec = txt.CountVectorizer(encoding=u'utf-8', charset=None,  strip_accents=strip_accents, lowercase=lowercase, preprocessor=None, stop_words=stop_words, token_pattern=token, ngram_range=ngram_range, analyzer=analyzer, max_df=max_df, min_df=min_df, max_features=max_features, vocabulary=None, binary=binary)

Il faut jouer avec ces paramètres pour comprendre ce que ça implique en terme de codage. Une grande partie de la performance du classifieur de document est liée au bon paramétrage de la vectorisation.

Transformation/normalisation

Certains choix ont déjà été effectués (codage présentiel ou comptage)... Mais il reste encore beaucoup de transformations possibles autour des données (approches fréquentielles, tf-idf...). Ces transformations sont disponibles dans un outil adhoc:

import sklearn.feature_extraction.text as txt
[...] # definition de l'outil de vectorisation
bow = vec.fit_transform(alltxt)
# pres => normalisation
transformer = txt.TfidfTransformer(use_idf=False, smooth_idf=False)
bow = transformer.fit_transform(bow)

Transformation inverse

Afin de vérifier que tout s'est bien passé, pensez à repasser un document vectorisé dans l'espace des mots:

print vec.inverse_transform(bow[0])

Note importante: vous aurez aussi besoin de cette fonction pour ré-interpréter les coefficients des modèles appris. Cette fonction permet de retrouver le mot associé à un index.

Solution 2: gensim

Note: si la librairie n'est pas installée, il faut l'installer (en local):

  pip install gensim --user --proxy=proxy.ufr-info-p6.jussieu.fr:3128

En utilisant gensim, la vectorisation est plus rapide, mais certaines options sont absentes et le passage aux N-gram est plus fastidieuse.

import re
from gensim import corpora, models

alldocs = # liste des documents
splitters = u'; |, |\*|\. | |\'|'
# dictionnaire unigrammes
alldocs_mots = [re.split(splitters, doc.lower()) for doc in alldocs] # chaque document eclaté comme une liste de mots
dictionary = corpora.Dictionary(alldocs_mots)

Si on souhaite des bigrammes, il faut le faire à la main:

# dictionnaire bigrammmes: c'est plus compliqué!
dictionary = corpora.Dictionary([u"{0}_{1}".format(l[i],l[i+1]) for i in xrange(len(l)-1)] for l in alldocs_mots) # bigrams

La gestion des fréquences, sélection des mots pertinents, élimination des stopwords, se fait également à la main:

# trouver les ids des stopwords
stop_ids = [dictionary.token2id[stopword] for stopword in stoplist   if stopword in dictionary.token2id]
# trouver les ids des mots peu fréquents
lowfreq_ids = [tokenid for tokenid, docfreq in dictionary.dfs.iteritems() if docfreq < 10 ]
dictionary.filter_tokens(stop_ids + lowfreq_ids) # remove stop words and words that appear rarely
dictionary.compactify()

Vectorisation des documents:

bows = [dictionary.doc2bow(text) for text in alldocs_mots] # attention, text=liste de mots

Usages du dictionnaire:

# Trouver le mot associé à un index
dictionary[428]
# -> u'god'

# Trouver l'index associé à un mot
dictionary.token2id['god']
# -> 428