Une introduction à Bag of Words et comment le coder en Python pour la PNL

Bag of Words (BOW) est une méthode pour extraire des fonctionnalités de documents texte. Ces fonctionnalités peuvent être utilisées pour entraîner des algorithmes d'apprentissage automatique. Il crée un vocabulaire de tous les mots uniques apparaissant dans tous les documents de l'ensemble d'apprentissage.

En termes simples, c'est une collection de mots pour représenter une phrase avec un nombre de mots et en ignorant principalement l'ordre dans lequel ils apparaissent.

BOW est une approche largement utilisée avec:

  1. Traitement du langage naturel
  2. Récupération d'informations à partir de documents
  3. Classifications de documents

À un niveau élevé, cela implique les étapes suivantes.

Les vecteurs générés peuvent être saisis dans votre algorithme d'apprentissage automatique.

Commençons par un exemple à comprendre en prenant quelques phrases et en générant des vecteurs pour celles-ci.

Considérez les deux phrases ci-dessous.

1. "John likes to watch movies. Mary likes movies too."
2. "John also likes to watch football games."

Ces deux phrases peuvent également être représentées avec une collection de mots.

1. ['John', 'likes', 'to', 'watch', 'movies.', 'Mary', 'likes', 'movies', 'too.']
2. ['John', 'also', 'likes', 'to', 'watch', 'football', 'games']

En outre, pour chaque phrase, supprimez plusieurs occurrences du mot et utilisez le nombre de mots pour le représenter.

1. {"John":1,"likes":2,"to":1,"watch":1,"movies":2,"Mary":1,"too":1}
2. {"John":1,"also":1,"likes":1,"to":1,"watch":1,"football":1, "games":1}

En supposant que ces phrases font partie d'un document, vous trouverez ci-dessous la fréquence combinée des mots pour l'ensemble de notre document. Les deux phrases sont prises en compte.

 {"John":2,"likes":3,"to":2,"watch":2,"movies":2,"Mary":1,"too":1, "also":1,"football":1,"games":1}

Le vocabulaire ci-dessus de tous les mots d'un document, avec leur nombre de mots respectif, sera utilisé pour créer les vecteurs pour chacune des phrases.

La longueur du vecteur sera toujours égale à la taille du vocabulaire. Dans ce cas, la longueur du vecteur est de 11.

Afin de représenter nos phrases originales dans un vecteur, chaque vecteur est initialisé avec tous les zéros - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Ceci est suivi d'une itération et d'une comparaison avec chaque mot de notre vocabulaire, et de l'incrémentation de la valeur vectorielle si la phrase contient ce mot.

John likes to watch movies. Mary likes movies too.[1, 2, 1, 1, 2, 1, 1, 0, 0, 0]
John also likes to watch football games.[1, 1, 1, 1, 0, 0, 0, 1, 1, 1]

Par exemple, dans la phrase 1, le mot likesapparaît en deuxième position et apparaît deux fois. Donc le deuxième élément de notre vecteur pour la phrase 1 sera 2: [1, 2, 1, 1, 2, 1, 1, 0, 0, 0]

Le vecteur est toujours proportionnel à la taille de notre vocabulaire.

Un gros document où le vocabulaire généré est énorme peut aboutir à un vecteur avec beaucoup de valeurs 0. C'est ce qu'on appelle un vecteur clairsemé .Les vecteurs épars nécessitent plus de mémoire et de ressources de calcul lors de la modélisation. Le grand nombre de positions ou de dimensions peut rendre le processus de modélisation très difficile pour les algorithmes traditionnels.

Codage de notre algorithme BOW

L'entrée de notre code sera plusieurs phrases et la sortie sera les vecteurs.

Le tableau d'entrée est le suivant:

["Joe waited for the train", "The train was late", "Mary and Samantha took the bus",
"I looked for Mary and Samantha at the bus station",
"Mary and Samantha arrived at the bus station early but waited until noon for the bus"]

Étape 1: Tokenize une phrase

Nous allons commencer par supprimer les mots vides des phrases.

Les mots vides sont des mots qui ne contiennent pas assez de signification pour être utilisés sans notre algorithme. Nous ne voudrions pas que ces mots prennent de la place dans notre base de données ou prennent un temps de traitement précieux. Pour cela, nous pouvons les supprimer facilement en stockant une liste de mots que vous considérez comme des mots vides.

La tokenisation consiste à diviser une séquence de chaînes en morceaux tels que des mots, des mots-clés, des phrases, des symboles et d'autres éléments appelés jetons . Les jetons peuvent être des mots individuels, des phrases ou même des phrases entières. Lors du processus de tokenisation, certains caractères tels que les signes de ponctuation sont supprimés.

def word_extraction(sentence): ignore = ['a', "the", "is"] words = re.sub("[^\w]", " ", sentence).split() cleaned_text = [w.lower() for w in words if w not in ignore] return cleaned_text

Pour une implémentation plus robuste des mots vides, vous pouvez utiliser la bibliothèque python nltk . Il a un ensemble de mots prédéfinis par langue. Voici un exemple:

import nltkfrom nltk.corpus import stopwords set(stopwords.words('english'))

Étape 2: appliquer la tokenisation à toutes les phrases

def tokenize(sentences): words = [] for sentence in sentences: w = word_extraction(sentence) words.extend(w) words = sorted(list(set(words))) return words

La méthode itère toutes les phrases et ajoute le mot extrait dans un tableau.

Le résultat de cette méthode sera:

['and', 'arrived', 'at', 'bus', 'but', 'early', 'for', 'i', 'joe', 'late', 'looked', 'mary', 'noon', 'samantha', 'station', 'the', 'took', 'train', 'until', 'waited', 'was']

Étape 3: Construire du vocabulaire et générer des vecteurs

Utilisez les méthodes définies aux étapes 1 et 2 pour créer le vocabulaire du document et extraire les mots des phrases.

def generate_bow(allsentences): vocab = tokenize(allsentences) print("Word List for Document \n{0} \n".format(vocab));
for sentence in allsentences: words = word_extraction(sentence) bag_vector = numpy.zeros(len(vocab)) for w in words: for i,word in enumerate(vocab): if word == w: bag_vector[i] += 1 print("{0}\n{1}\n".format(sentence,numpy.array(bag_vector)))

Voici l'entrée et l'exécution définies de notre code:

allsentences = ["Joe waited for the train train", "The train was late", "Mary and Samantha took the bus",
"I looked for Mary and Samantha at the bus station",
"Mary and Samantha arrived at the bus station early but waited until noon for the bus"]
generate_bow(allsentences)

Les vecteurs de sortie pour chacune des phrases sont:

Output:
Joe waited for the train train[0. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 2. 0. 1. 0.]
The train was late[0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0. 1.]
Mary and Samantha took the bus[1. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0. 1. 0. 0. 0. 0.]
I looked for Mary and Samantha at the bus station[1. 0. 1. 1. 0. 0. 1. 1. 0. 0. 1. 1. 0. 1. 1. 0. 0. 0. 0. 0. 0.]
Mary and Samantha arrived at the bus station early but waited until noon for the bus[1. 1. 1. 2. 1. 1. 1. 0. 0. 0. 0. 1. 1. 1. 1. 0. 0. 0. 1. 1. 0.]

As you can see, each sentence was compared with our word list generated in Step 1. Based on the comparison, the vector element value may be incremented. These vectors can be used in ML algorithms for document classification and predictions.

We wrote our code and generated vectors, but now let’s understand bag of words a bit more.

Insights into bag of words

The BOW model only considers if a known word occurs in a document or not. It does not care about meaning, context, and order in which they appear.

This gives the insight that similar documents will have word counts similar to each other. In other words, the more similar the words in two documents, the more similar the documents can be.

Limitations of BOW

  1. Semantic meaning: the basic BOW approach does not consider the meaning of the word in the document. It completely ignores the context in which it’s used. The same word can be used in multiple places based on the context or nearby words.
  2. Vector size: For a large document, the vector size can be huge resulting in a lot of computation and time. You may need to ignore words based on relevance to your use case.

This was a small introduction to the BOW method. The code showed how it works at a low level. There is much more to understand about BOW. For example, instead of splitting our sentence in a single word (1-gram), you can split in the pair of two words (bi-gram or 2-gram). At times, bi-gram representation seems to be much better than using 1-gram. These can often be represented using N-gram notation. I have listed some research papers in the resources section for more in-depth knowledge.

You do not have to code BOW whenever you need it. It is already part of many available frameworks like CountVectorizer in sci-kit learn.

Our previous code can be replaced with:

from sklearn.feature_extraction.text import CountVectorizervectorizer = CountVectorizer()X = vectorizer.fit_transform(allsentences)print(X.toarray())

It’s always good to understand how the libraries in frameworks work, and understand the methods behind them. The better you understand the concepts, the better use you can make of frameworks.

Thanks for reading the article. The code shown is available on my GitHub.

You can follow me on Medium, Twitter, and LinkedIn, For any questions, you can reach out to me on email (praveend806 [at] gmail [dot] com).

Resources to read more on bag of words

  1. Wikipedia-BOW
  2. Understanding Bag-of-Words Model: A Statistical Framework
  3. Semantics-Preserving Bag-of-Words Models and Applications