Skip to content
Snippets Groups Projects
Commit f6b2d8a7 authored by Thomas Gerald's avatar Thomas Gerald
Browse files

Upload New File

parent 3163b935
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id:f4bdb240-9099-411b-a859-daaf3269c900 tags:
## Implémentation de l'algorithme BPE
L'objectif de ce premier TP est de mettre en place l'algorithme de tokenization BPE. Pour rappel le principe consiste à rassembler les mots ou ``tokens'' apparaissant le plus de fois successivement.
Par exemple si l'on considère le corpus contenant les mots de la table suivante (avec le nombre d’occurrences de chaque mot) :
| mots | occurence |
|------|-----------|
| manger | 2 |
| voter | 3 |
| lent | 1 |
| lentement | 2 |
Et que les "tokens" initiaux sont les lettres de l'alphabet alors, c'est le sufixe "er" qui sera ajouté à la liste des sous-mots (tokens) dans un premier temps.
Les étapes pour réaliser l'algorithme BPE sont les suivantes :
1. Télécharger un corpus de textes (ici une page wikipedia)
2. Découper le texte en mots (en utilisant le caractère "espace") et compter le nombre d’occurrences de chaque mot
3. Initialiser le dictionnaire de mots avec les tokens initiaux (les lettres de l’alphabet)
4. Faire tourner l'algorithme BPE (apprendre le vocabulaire)
5. Tester la décomposition en tokens sur des phrases choisies (on applique les règles de fusion)
%% Cell type:markdown id:ee88b07e-4de8-436e-9dc7-f405c69c9042 tags:
### Étape 1: Télécharger un corpus
%% Cell type:code id:5352d48f-27ca-41ad-9265-0e128578a5ec tags:
``` python
import urllib.request
import re
import numpy as np
import collections
import json
url_request = 'https://fr.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext&redirects=1&titles=Gr%C3%A8ce_antique'
raw_page = urllib.request.urlopen(url_request)
json_page = json.load(raw_page)
with open('wikitest.json', 'w') as f:
json.dump(json_page, f)
```
%% Cell type:markdown id:f8b6b7a2-74b4-4ba2-aa4b-93885a286fb2 tags:
### Étape 2 : Découper le texte en mots
Pour découper le texte en mot nous utiliserons la regex suivante ```r'(\b[^\s]+\b)'```. Pour compter les mots nous utiliserons l'objet Counter de python.
1. Stocker dans **count_words** chaque mot ainsi que le nombre d’occurrences
2. En regardant la documentation donnez les 10 mots les plus fréquents (vous les stockerez dans most_commons_words).
%% Cell type:code id:6d7341ff-61bf-4a5a-9d0d-4ae8fd8797ce tags:
``` python
from collections import Counter
corpus = list(json_page['query']['pages'].values())[0]['extract']
word_regex = re.compile(r'(\b[^\s]+\b)')
words = word_regex.findall(corpus)
count_words = collections.Counter(words)
most_commons_words = [k for k, v in count_words.most_common(10)]
most_commons_words
```
%% Output
['de', 'la', 'et', 'des', 'les', 'à', 'le', 'en', 'qui', 'dans']
%% Cell type:markdown id:7a7a38b8-97f5-4851-b642-f7b40c023491 tags:
### Étape 3 : Initialiser le dictionnaire de mots avec les tokens initiaux (les lettre de l'aplhabet)
Créer le vocabulaire initial dans la variable vocab. Combien de tokens initiaux avez-vous ?
%% Cell type:code id:901f7ea5-9d71-4f8c-95bf-32b098c3241c tags:
``` python
vocab = list({char for word in count_words.keys() for char in word })
vocab.sort()
```
%% Cell type:markdown id:efd79ab8-b1b1-4802-ac80-39b12cb5917f tags:
### Étape 4 : Apprendre le tokenizer
Pour apprendre le tokenizer nous avons besoins de plusieurs fonctions:
1. Une fonction pour calculer la fréquence de chacune des pairs de ``tokens''
2. Une fonction pour fusionner un paire
Plusieurs variables sont nécessaires:
1. **vocab** contenant le vocabulaire courant
2. **merge_rules** contenant toutes les règles de fusion (un dictionnaire contenant comme clef un couple de tokens à fusionner et le résultat de la fusion des tokens). Par exemple : {('e', 's'), 'es', ('en', 't') :'ent'}
3. **splits** Un dictionnaire contenant le découpage courant du corpus avec pour clef le mot et comme valeur la liste des "tokens"
%% Cell type:code id:8357b6bd-9414-4263-9bf5-ef249614cb04 tags:
``` python
# En première étape splits contient les mots décomposer en caractères
splits = {word: [c for c in word] for word in count_words.keys()}
{k: splits[k] for k in list(splits.keys())[:5]}
```
%% Output
{'La': ['L', 'a'],
'Grèce': ['G', 'r', 'è', 'c', 'e'],
'antique': ['a', 'n', 't', 'i', 'q', 'u', 'e'],
'est': ['e', 's', 't'],
'une': ['u', 'n', 'e']}
%% Cell type:markdown id:722df20d-fae1-41f1-96bc-fecb6b10a0ca tags:
#### Calculer la fréquences des pairs de tokens
Créer un fonction **compute_pair_freqs** qui étant donné les mots décomposés en tokens (dictionnaire splits) et la fréquence des mots retourne la fréquence de chaque couple de tokens (attention seulement les sous-mots successifs).
%% Cell type:code id:cb995e01-74f4-432f-9ddb-5c676afc32c8 tags:
``` python
def compute_pair_freqs(splits, word_freqs):
pair_freqs = {}
for word, freq in word_freqs.items():
split = splits[word]
if len(split) == 1:
continue
for i in range(len(split) - 1):
pair = (split[i], split[i + 1])
if(pair not in pair_freqs):
pair_freqs[pair] = 0
pair_freqs[pair] += freq
return pair_freqs
```
%% Cell type:code id:d806c606-8cd3-43ff-8e14-aea74f9d4172 tags:
``` python
pair_freqs = compute_pair_freqs(splits, count_words)
{k: pair_freqs[k] for k in list(pair_freqs.keys())[:5]}
```
%% Output
{('L', 'a'): 188,
('G', 'r'): 266,
('r', 'è'): 279,
('è', 'c'): 316,
('c', 'e'): 1342}
%% Cell type:markdown id:72e05d19-df89-4b0b-a2ad-ba5af727b1e5 tags:
#### Retrouver la paire la plus fréquente et fusionner une pair
1. Créer une fonction **most_frequent(pair_freqs)** retournant la paire de token la plus fréquente.
2. Créer une fonction **merge_pair()** qui étant donnée une paire, l'objet splits retourne la nouvelle séparation en token des données (splits))
%% Cell type:code id:c608944a tags:
``` python
```
%% Cell type:code id:9b60759e-0bb4-4969-bc86-ee39fd3dc7bb tags:
``` python
def most_frequent(pair_freqs):
return max(pair_freqs.items(), key=lambda x: x[1])
most_frequent(pair_freqs)
```
%% Output
(('e', 's'), 6895)
%% Cell type:code id:dd44467a-326b-499c-9995-8661fd102bec tags:
``` python
def merge_pair(a, b, splits):
for word in splits.keys():
split = splits[word]
if len(split) == 1:
continue
i = 0
while i < len(split) - 1:
if split[i] == a and split[i + 1] == b:
split = split[:i] + [a + b] + split[i + 2 :]
else:
i += 1
splits[word] = split
return splits
```
%% Cell type:code id:462376c4-6abc-4d4c-8bb6-a7e4d5bb96bb tags:
``` python
new_splits = merge_pair(*most_frequent(pair_freqs)[0], splits)
print(new_splits['grecques'])
```
%% Output
['g', 'r', 'e', 'c', 'q', 'u', 'es']
%% Cell type:markdown id:1a9cdfeb-ade8-4ac6-9cd5-9049090ca142 tags:
#### Appliquer l'algorithme jusqu'a obtenir la taille du vocabulaire souhaitée
Créer un objet BPE qui prend en argument un corpus, un nombre de mots et applique l'algorithme BPE. L'algorithme stocke dans l'attribut vocab le vcocabulaire final et dans merge_rule les règles de fusion.
%% Cell type:code id:c0367f37-9b40-4ae7-8ec6-ca7ab86ae687 tags:
``` python
class BPE:
def __init__(self, corpus, vocabulary_size=500):
super().__init__()
self.word_regex = re.compile(r'(\b[^\s]+\b)')
words = self.word_regex.findall(corpus)
# counting words
count_words = collections.Counter(words)
# create initial vocab
self.vocab = list({char for word in count_words.keys() for char in word })
self.vocab.sort()
# create the initial split
splits = {word: [c for c in word] for word in count_words.keys()}
# initialise merge_rules
self.merge_rules = {}
while len(self.vocab) < vocabulary_size:
pair_freqs = compute_pair_freqs(splits, count_words)
# I considered the format (('e', 's'), 6848) for best_pair
best_pair = most_frequent(pair_freqs)
splits = merge_pair(*best_pair[0], splits)
self.merge_rules[best_pair[0]] = best_pair[0][0] + best_pair[0][1]
self.vocab.append(''.join(best_pair[0]))
def tokenize(self, text):
words = self.word_regex.findall(text)
splits = [[l for l in word] for word in words]
for pair, merge in self.merge_rules.items():
for idx, split in enumerate(splits):
i = 0
while i < len(split) - 1:
if split[i] == pair[0] and split[i + 1] == pair[1]:
split = split[:i] + [merge] + split[i + 2 :]
else:
i += 1
splits[idx] = split
return sum(splits, [])
```
%% Cell type:code id:c282a2a1-f23c-4b22-b858-35a0049250c1 tags:
``` python
my_bpe = BPE(corpus)
```
%% Cell type:code id:b225b59b-6103-4b3f-b776-ea2b71714d64 tags:
``` python
texte = '''culture grecques développée en Grèce '''
my_bpe.tokenize(texte)[:12]
```
%% Output
['culture', 'grecques', 'dévelop', 'p', 'ée', 'en', 'Grèce']
%% Cell type:markdown id:a6f478a3-5f52-43c4-9b48-0e912304985a tags:
#### Testez en modifiant les paramètres ou le corpus
Tester l'algorithme avec différents hyper-paramètres ou données
%% Cell type:code id:e33bdef2-8392-44f8-a846-81249fd60ac9 tags:
``` python
{k: my_bpe.merge_rules[k] for k in list(my_bpe.merge_rules.keys())[:12]}
```
%% Output
{('e', 's'): 'es',
('n', 't'): 'nt',
('q', 'u'): 'qu',
('r', 'e'): 're',
('o', 'n'): 'on',
('d', 'e'): 'de',
('l', 'e'): 'le',
('l', 'a'): 'la',
('t', 'i'): 'ti',
('i', 's'): 'is',
('e', 'nt'): 'ent',
('e', 'n'): 'en'}
%% Cell type:code id:7513567a-5237-4dad-9500-bf2cded9b8a2 tags:
``` python
```
%% Cell type:code id:2ccb41e3 tags:
``` python
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment