Construire des tables de bigrammes
Comptez les paires de mots consécutives dans un dictionnaire imbriqué qui mappe chaque mot à sa distribution de mots suivants.
- Comprendre ce qu'un bigramme capture sur les transitions de mot à mot
- Construire un dict imbriqué bigrams = {'the': {'cat': 3, 'dog': 1}, ...} à partir d'une liste de jetons
- Gérer les frontières de phrase et les mots de départ inconnus
- Inspecter la table de bigrammes pour vérifier l'exactitude
Des comptes de mots aux transitions de mots
La fréquence des mots vous dit quels mots apparaissent. Les bigrammes vous disent ce qui suit quoi. « The cat » est bien plus courant que « the refrigerator », une table de bigrammes capture cette relation. C’est la forme la plus simple d’un modèle de langue : étant donné un mot, quels mots ont tendance à venir ensuite ?
Les cellules ci-dessous réutilisent les fonctions load_corpus et tokenize des leçons 01 et 03. Chaque page de leçon démarre une session Python vierge, alors exécutez d’abord cette cellule de mise en place :
import csv
import string
with open("slm-corpus.csv", newline="") as f:
reader = csv.DictReader(f)
texts = [row["text"] for row in reader]
def load_corpus(path):
with open(path, newline="") as f:
reader = csv.DictReader(f)
return [row["text"] for row in reader]
def tokenize(text):
text = text.lower()
for char in string.punctuation:
text = text.replace(char, " ")
return text.split()
tokens = tokenize(" ".join(texts))Concepts clés
Qu’est-ce qu’un bigramme ?
Un bigramme est une paire de mots consécutifs. Dans la phrase « the cat sat on the mat », les bigrammes sont :
(the, cat), (cat, sat), (sat, on), (on, the), (the, mat)
Chaque paire représente une transition d’un mot au suivant. En comptant toutes les transitions du corpus, vous construisez un modèle statistique des séquences de mots.
Construire le dict imbriqué
La table de bigrammes est un dict de dicts. La clé externe est le mot courant ; le dict interne mappe les mots suivants à leurs comptes :
def build_bigrams(tokens):
bigrams = {}
for i in range(len(tokens) - 1):
current = tokens[i]
next_word = tokens[i + 1]
if current not in bigrams:
bigrams[current] = {}
bigrams[current][next_word] = bigrams[current].get(next_word, 0) + 1
return bigramsParcourez la liste de jetons avec une fenêtre glissante de taille 2. Pour chaque paire (tokens[i], tokens[i+1]), incrémentez le compte dans bigrams[tokens[i]][tokens[i+1]].
Exemple de parcours
Pour les jetons ["the", "cat", "sat", "the", "dog"] :
i=0: current="the", next="cat" → bigrams["the"]["cat"] = 1
i=1: current="cat", next="sat" → bigrams["cat"]["sat"] = 1
i=2: current="sat", next="the" → bigrams["sat"]["the"] = 1
i=3: current="the", next="dog" → bigrams["the"]["dog"] = 1
Résultat :
{
"the": {"cat": 1, "dog": 1},
"cat": {"sat": 1},
"sat": {"the": 1},
}Utiliser defaultdict pour un code plus propre
from collections import defaultdict
def build_bigrams(tokens):
bigrams = defaultdict(lambda: defaultdict(int))
for i in range(len(tokens) - 1):
bigrams[tokens[i]][tokens[i + 1]] += 1
return dict(bigrams)La lambda: defaultdict(int) crée automatiquement un nouveau dict interne pour chaque nouveau mot, pour que vous n’ayez jamais besoin de vérifier si une clé existe.
Inspecter la table de bigrammes
Vérifiez que votre table semble raisonnable :
bigrams = build_bigrams(tokens)
# How many words have followers?
print(f"Words with followers: {len(bigrams)}")
# Show the top word's followers
top_word = max(bigrams, key=lambda w: sum(bigrams[w].values()))
print(f"Most connected word: '{top_word}'")
print(f" Followers: {bigrams[top_word]}")Frontières de phrase
En construisant des bigrammes à partir de plusieurs phrases, le dernier mot d’une phrase et le premier mot de la suivante deviennent un bigramme. C’est généralement acceptable pour un petit modèle, le modèle ne connaît pas la structure de phrase de toute façon. Mais si vous voulez des résultats plus propres, vous pouvez ajouter des marqueurs de frontière de phrase :
def build_bigrams(tokens, add_boundaries=True):
bigrams = defaultdict(lambda: defaultdict(int))
for i in range(len(tokens) - 1):
bigrams[tokens[i]][tokens[i + 1]] += 1
if add_boundaries:
bigrams["<END>"] = defaultdict(int)
bigrams[tokens[-1]]["<END>"] = bigrams[tokens[-1]].get("<END>", 0) + 1
return dict(bigrams)Cela vous permet de suivre quels mots terminent couramment des phrases.
Essayez
Construisez une table de bigrammes à partir du corpus et répondez :
- Combien de paires de bigrammes uniques existent ?
- Quelles sont les 3 paires (mot, mot suivant) les plus courantes ?
- Est-ce que « the » a plus de mots suivants que n’importe quel autre mot ?
from collections import defaultdict
texts = load_corpus("slm-corpus.csv")
tokens = tokenize(" ".join(texts))
bigrams = build_bigrams(tokens)
total_pairs = sum(sum(f.values()) for f in bigrams.values())
print(f"Unique bigram pairs: {total_pairs}")Points clés
- Un bigramme est une paire de mots consécutifs, le modèle de séquence le plus simple
- La table de bigrammes est un dict imbriqué :
bigrams[word] = {follower: count} defaultdict(lambda: defaultdict(int))simplifie le comptage imbriqué- Les frontières de phrase peuvent être suivies avec des jetons spéciaux comme
<END>
Défi pratique
Écrivez une fonction most_common_bigram(bigrams) qui renvoie la paire (word, follower) la plus fréquente sous forme de tuple. Utilisez-la ensuite pour trouver le bigramme le plus courant du corpus.
def most_common_bigram(bigrams):
best = (None, None)
best_count = 0
for word, followers in bigrams.items():
for follower, count in followers.items():
if count > best_count:
best = (word, follower)
best_count = count
return best, best_count1. Qu'est-ce qu'un bigramme ?
2. Comment créez-vous des bigrammes à partir d'une liste de jetons ?
3. Si tokens = the, cat, sat, quels bigrammes obtenez-vous ?