-تعجبی ندارد که نمونههایی با طولهای متغییر، از ۳۲ تا ۶۷ بدست میآوریم. همطولسازی پویا به این معنی است که نمونههای موجود در این بَتچ باید همگی با طول ۶۷، که بزرگترین طول داخل بَتچ میباشد، همطول شده باشند. بدون همطولسازی پویا، همه نمونهها در کل دیتاسِت باید به اندازه بزرگترین طول یا بزرگترین طولِ قابل پذیرش برای مدل، همطول شوند. اجازه دهید بررسی کنیم آیا `data_collator` ما بَتچ را به درستی همطول میکند:
+تعجبی ندارد که نمونههایی با طولهای متغییر، از ۳۲ تا ۶۷ بدست میآوریم. همطولسازی پویا به این معنی است که نمونههای موجود در این بَتچ باید همگی با طول ۶۷، که بزرگترین طول داخل بَتچ میباشد، همطول شده باشند. بدون همطولسازی پویا، همه نمونهها در کل دیتاسِت باید به اندازه بزرگترین طول یا بزرگترین طول قابل پذیرش برای مدل، همطول شوند. اجازه دهید بررسی کنیم آیا `data_collator` ما بَتچ را به درستی همطول میکند:
sont énormes
En dehors de quelques exceptions (comme DistilBERT), la stratégie générale pour obtenir de meilleure performance consiste à augmenter la taille des modèles ainsi que la quantité de données utilisées pour l'entraînement de ces derniers.
@@ -133,9 +133,9 @@ Nous verrons plus en détails chacune de ces architectures plus tard.
## Les couches d'attention
-Une caractéristique clé des *transformers* est qu'ils sont construits avec des couches spéciales appelées couches d'attention. En fait, le titre du papier introduisant l'architecture *transformer* s'e nome [*Attention Is All You Need*](https://arxiv.org/abs/1706.03762) ! Nous explorerons les détails des couches d'attention plus tard dans le cours. Pour l'instant, tout ce que vous devez savoir est que cette couche indique au modèle de prêter une attention spécifique à certains mots de la phrase que vous lui avez passée (et d'ignorer plus ou moins les autres) lors du traitement de la représentation de chaque mot.
+Une caractéristique clé des *transformers* est qu'ils sont construits avec des couches spéciales appelées couches d'attention. En fait, le titre du papier introduisant l'architecture *transformer* se nomme [*Attention Is All You Need*](https://arxiv.org/abs/1706.03762) ! Nous explorerons les détails des couches d'attention plus tard dans le cours. Pour l'instant, tout ce que vous devez savoir est que cette couche indique au modèle de prêter une attention spécifique à certains mots de la phrase que vous lui avez passée (et d'ignorer plus ou moins les autres) lors du traitement de la représentation de chaque mot.
-Pour mettre cela en contexte, considérons la tâche de traduire un texte de l'anglais au français. Étant donné l'entrée « You like this course », un modèle de traduction devra également s'intéresser au mot adjacent « You » pour obtenir la traduction correcte du mot « like », car en français le verbe « like » se conjugue différemment selon le sujet. Le reste de la phrase n'est en revanche pas utile pour la traduction de ce mot. Dans le même ordre d'idées, pour traduire « this », le modèle devra également faire attention au mot « course » car « this » se traduit différemment selon que le nom associé est masculin ou féminin. Là encore, les autres mots de la phrase n'auront aucune importance pour la traduction de « this ». Avec des phrases plus complexes (et des règles de grammaire plus complexes), le modèle devra prêter une attention particulière aux mots qui pourraient apparaître plus loin dans la phrase pour traduire correctement chaque mot.
+Pour mettre cela en contexte, considérons la tâche de traduire un texte de l'anglais au français. Étant donné l'entrée « *You like this course* », un modèle de traduction devra également s'intéresser au mot adjacent « *You* » pour obtenir la traduction correcte du mot « *like* », car en français le verbe « *like* » se conjugue différemment selon le sujet. Le reste de la phrase n'est en revanche pas utile pour la traduction de ce mot. Dans le même ordre d'idées, pour traduire « *this* », le modèle devra également faire attention au mot « *course* » car « *this* » se traduit différemment selon que le nom associé est masculin ou féminin. Là encore, les autres mots de la phrase n'auront aucune importance pour la traduction de « *this* ». Avec des phrases plus complexes (et des règles de grammaire plus complexes), le modèle devra prêter une attention particulière aux mots qui pourraient apparaître plus loin dans la phrase pour traduire correctement chaque mot.
Le même concept s'applique à toute tâche associée au langage naturel : un mot en lui-même a un sens, mais ce sens est profondément affecté par le contexte, qui peut être n'importe quel autre mot (ou mots) avant ou après le mot étudié.
@@ -158,9 +158,9 @@ Notez que la première couche d'attention dans un bloc décodeur prête attentio
Le *masque d'attention* peut également être utilisé dans l'encodeur/décodeur pour empêcher le modèle de prêter attention à certains mots spéciaux. Par exemple, le mot de remplissage spécial (le *padding*) utilisé pour que toutes les entrées aient la même longueur lors du regroupement de phrases.
-## Architectures contre *checkpoints*
+## Architectures contre
-En approfondissant l'étude des *transformers* dans ce cours, vous verrez des mentions d'*architectures* et de *checkpoints* ainsi que de *modèles*. Ces termes ont tous des significations légèrement différentes :
+En approfondissant l'étude des
. Ces termes ont tous des significations légèrement différentes :
* **Architecture** : c'est le squelette du modèle, la définition de chaque couche et chaque opération qui se produit au sein du modèle.
* **Checkpoints** : ce sont les poids qui seront chargés dans une architecture donnée.
diff --git a/chapters/fr/chapter1/8.mdx b/chapters/fr/chapter1/8.mdx
index acf86a09c..e3e3c2310 100644
--- a/chapters/fr/chapter1/8.mdx
+++ b/chapters/fr/chapter1/8.mdx
@@ -23,8 +23,10 @@ print([r["token_str"] for r in result])
```
```python out
-['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic'] # [avocat, charpentier, médecin, serveur, mécanicien]
-['nurse', 'waitress', 'teacher', 'maid', 'prostitute'] # ["infirmière", "serveuse", "professeur", "femme de chambre", "prostituée"]
+['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic']
+# [avocat, charpentier, médecin, serveur, mécanicien]
+['nurse', 'waitress', 'teacher', 'maid', 'prostitute']
+# ["infirmière", "serveuse", "professeur", "femme de chambre", "prostituée"]
```
Lorsque l'on demande au modèle de remplacer le mot manquant dans ces deux phrases, il ne propose qu'un seul métier ne portant pas la marque du genre (*waiter*/*waitress* → serveur/serveuse). Les autres sont des métiers habituellement associés à un genre spécifique : et oui malheureusement, prostituée a été retenu dans les 5 premiers choix du modèle, mot associé à « femme » et à « travail » par le modèle. Cela se produit même si BERT est l'un des rare *transformers* qui n'a pas été construit avec des données récupérées par *scrapping* sur internet, mais à l'aide de données en apparence neutres. En effet, il est entraîné sur les jeux de donnés [Wikipédia Anglais](https://huggingface.co/datasets/wikipedia) et [BookCorpus](https://huggingface.co/datasets/bookcorpus)).
diff --git a/chapters/fr/chapter2/1.mdx b/chapters/fr/chapter2/1.mdx
index bf0c05190..77a53ca12 100644
--- a/chapters/fr/chapter2/1.mdx
+++ b/chapters/fr/chapter2/1.mdx
@@ -1,24 +1,24 @@
-# Introduction
-
-Comme vous l'avez vu dans le [Chapitre 1](/course/fr/chapter1), les *transformers* sont généralement très grands. Pouvant aller de plusieurs millions à des dizaines de milliards de paramètres, l'entraînement et le déploiement de ces modèles est une entreprise compliquée. De plus, avec de nouveaux modèles publiés presque quotidiennement et ayant chacun sa propre implémentation, les essayer tous n'est pas une tâche facile.
-
-La bibliothèque 🤗 *Transformers* a été créée pour résoudre ce problème. Son objectif est de fournir une API unique à travers laquelle tout modèle de *transformers* peut être chargé, entraîné et sauvegardé. Les principales caractéristiques de la bibliothèque sont :
-
-- **la facilité d'utilisation** : en seulement deux lignes de code il est possible de télécharger, charger et utiliser un modèle de NLP à l'état de l'art pour faire de l'inférence,
-- **la flexibilité** : au fond, tous les modèles sont de simples classes PyTorch `nn.Module` ou TensorFlow `tf.keras.Model` et peuvent être manipulés comme n'importe quel autre modèle dans leurs *frameworks* d'apprentissage automatique respectifs,
-- **la simplicité** : pratiquement aucune abstraction n'est faite dans la bibliothèque. Avoir tout dans un fichier est un concept central : la passe avant d'un modèle est entièrement définie dans un seul fichier afin que le code lui-même soit compréhensible et piratable.
-
-Cette dernière caractéristique rend 🤗 *Transformers* très différent des autres bibliothèques d'apprentissage automatique.
-Les modèles ne sont pas construits sur des modules partagés entre plusieurs fichiers. Au lieu de cela, chaque modèle possède ses propres couches.
-En plus de rendre les modèles plus accessibles et compréhensibles, cela vous permet d'expérimenter des choses facilement sur un modèle sans affecter les autres.
-
-Ce chapitre commence par un exemple de bout en bout où nous utilisons un modèle et un *tokenizer* ensemble pour reproduire la fonction `pipeline()` introduite dans le [Chapitre 1](/course/chapter1).
-Ensuite, nous aborderons l'API *model* : nous nous plongerons dans les classes de modèle et de configuration, nous verrons comment charger un modèle et enfin comment il traite les entrées numériques pour produire des prédictions.
-
-Nous examinerons ensuite l'API *tokenizer* qui est l'autre composant principal de la fonction `pipeline()`.
-Les *tokenizers* s'occupent de la première et de la dernière étape du traitement en gérant la conversion du texte en entrées numériques pour le réseau neuronal et la reconversion en texte lorsqu'elle est nécessaire.
-Enfin, nous montrerons comment gérer l'envoi de plusieurs phrases à travers un modèle dans un batch préparé et nous conclurons le tout en examinant de plus près la fonction `tokenizer()`.
-
-
+# Introduction
+
+Comme vous l'avez vu dans le [chapitre 1](/course/fr/chapter1), les *transformers* sont généralement très grands. Pouvant aller de plusieurs millions à des dizaines de milliards de paramètres, l'entraînement et le déploiement de ces modèles est une entreprise compliquée. De plus, avec de nouveaux modèles publiés presque quotidiennement et ayant chacun sa propre implémentation, les essayer tous n'est pas une tâche facile.
+
+La bibliothèque 🤗 *Transformers* a été créée pour résoudre ce problème. Son objectif est de fournir une API unique à travers laquelle tout modèle de *transformers* peut être chargé, entraîné et sauvegardé. Les principales caractéristiques de la bibliothèque sont :
+
+- **La facilité d'utilisation** : en seulement deux lignes de code il est possible de télécharger, charger et utiliser un modèle de NLP à l'état de l'art pour faire de l'inférence,
+- **La flexibilité** : au fond, tous les modèles sont de simples classes PyTorch `nn.Module` ou TensorFlow `tf.keras.Model` et peuvent être manipulés comme n'importe quel autre modèle dans leurs *frameworks* d'apprentissage automatique respectifs,
+- **La simplicité** : pratiquement aucune abstraction n'est faite dans la bibliothèque. Avoir tout dans un fichier est un concept central : la passe avant d'un modèle est entièrement définie dans un seul fichier afin que le code lui-même soit compréhensible et piratable.
+
+Cette dernière caractéristique rend 🤗 *Transformers* très différent des autres bibliothèques d'apprentissage automatique.
+Les modèles ne sont pas construits sur des modules partagés entre plusieurs fichiers. Au lieu de cela, chaque modèle possède ses propres couches.
+En plus de rendre les modèles plus accessibles et compréhensibles, cela vous permet d'expérimenter des choses facilement sur un modèle sans affecter les autres.
+
+Ce chapitre commence par un exemple de bout en bout où nous utilisons un modèle et un *tokenizer* ensemble pour reproduire la fonction `pipeline()` introduite dans le [chapitre 1](/course/fr/chapter1).
+Ensuite, nous aborderons l'API *model* : nous nous plongerons dans les classes de modèle et de configuration, nous verrons comment charger un modèle et enfin comment il traite les entrées numériques pour produire des prédictions.
+
+Nous examinerons ensuite l'API *tokenizer* qui est l'autre composant principal de la fonction `pipeline()`.
+Les *tokenizers* s'occupent de la première et de la dernière étape du traitement en gérant la conversion du texte en entrées numériques pour le réseau neuronal et la reconversion en texte lorsqu'elle est nécessaire.
+Enfin, nous montrerons comment gérer l'envoi de plusieurs phrases à travers un modèle dans un batch préparé et nous conclurons le tout en examinant de plus près la fonction `tokenizer()`.
+
+
diff --git a/chapters/fr/chapter2/2.mdx b/chapters/fr/chapter2/2.mdx
index 64f5e296b..baaf88030 100644
--- a/chapters/fr/chapter2/2.mdx
+++ b/chapters/fr/chapter2/2.mdx
@@ -32,7 +32,7 @@ Il s'agit de la première section dont le contenu est légèrement différent se
{/if}
-Commençons par un exemple complet en regardant ce qui s'est passé en coulisses lorsque nous avons exécuté le code suivant dans le [Chapitre 1](/course/chapter1) :
+Commençons par un exemple complet en regardant ce qui s'est passé en coulisses lorsque nous avons exécuté le code suivant dans le [chapitre 1](/course/chapter1) :
```python
from transformers import pipeline
@@ -40,7 +40,8 @@ from transformers import pipeline
classifier = pipeline("sentiment-analysis")
classifier(
[
- "I've been waiting for a HuggingFace course my whole life.", # J'ai attendu un cours de HuggingFace toute ma vie.
+ "I've been waiting for a HuggingFace course my whole life.",
+ # J'ai attendu un cours de HuggingFace toute ma vie.
"I hate this so much!", # Je déteste tellement ça !
]
)
@@ -53,7 +54,7 @@ la sortie :
{'label': 'NEGATIVE', 'score': 0.9994558095932007}]
```
-Comme nous l'avons vu dans le [Chapitre 1](/course/fr/chapter1), ce pipeline regroupe trois étapes : le prétraitement, le passage des entrées dans le modèle et le post-traitement.
+Comme nous l'avons vu dans le [chapitre 1](/course/fr/chapter1), ce pipeline regroupe trois étapes : le prétraitement, le passage des entrées dans le modèle et le post-traitement.
@@ -62,14 +63,14 @@ Comme nous l'avons vu dans le [Chapitre 1](/course/fr/chapter1), ce pipeline reg
Passons rapidement en revue chacun de ces éléments.
-## Prétraitement avec un *tokenizer*
+## Prétraitement avec un
tokenizer
Comme d'autres réseaux de neurones, les *transformers* ne peuvent pas traiter directement le texte brut, donc la première étape de notre pipeline est de convertir les entrées textuelles en nombres afin que le modèle puisse les comprendre. Pour ce faire, nous utilisons un *tokenizer*, qui sera responsable de :
- diviser l'entrée en mots, sous-mots, ou symboles (comme la ponctuation) qui sont appelés *tokens*,
- associer chaque *token* à un nombre entier,
- ajouter des entrées supplémentaires qui peuvent être utiles au modèle.
-Tout ce prétraitement doit être effectué exactement de la même manière que celui appliqué lors du pré-entraînement du modèle. Nous devons donc d'abord télécharger ces informations depuis le [*Model Hub*](https://huggingface.co/models). Pour ce faire, nous utilisons la classe `AutoTokenizer` et sa méthode `from_pretrained()`. En utilisant le nom du *checkpoint* de notre modèle, elle va automatiquement récupérer les données associées au *tokenizer* du modèle et les mettre en cache (afin qu'elles ne soient téléchargées que la première fois que vous exécutez le code ci-dessous).
+Tout ce prétraitement doit être effectué exactement de la même manière que celui appliqué lors du pré-entraînement du modèle. Nous devons donc d'abord télécharger ces informations depuis le [*Hub*](https://huggingface.co/models). Pour ce faire, nous utilisons la classe `AutoTokenizer` et sa méthode `from_pretrained()`. En utilisant le nom du *checkpoint* de notre modèle, elle va automatiquement récupérer les données associées au *tokenizer* du modèle et les mettre en cache (afin qu'elles ne soient téléchargées que la première fois que vous exécutez le code ci-dessous).
Puisque le *checkpoint* par défaut du pipeline `sentiment-analysis` (analyse de sentiment) est `distilbert-base-uncased-finetuned-sst-2-english` (vous pouvez voir la carte de ce modèle [ici](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)), nous exécutons ce qui suit :
@@ -89,7 +90,8 @@ Pour spécifier le type de tenseurs que nous voulons récupérer (PyTorch, Tenso
{#if fw === 'pt'}
```python
raw_inputs = [
- "I've been waiting for a HuggingFace course my whole life.", # J'ai attendu un cours de HuggingFace toute ma vie.
+ "I've been waiting for a HuggingFace course my whole life.",
+ # J'ai attendu un cours de HuggingFace toute ma vie.
"I hate this so much!", # Je déteste tellement ça !
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
@@ -98,7 +100,8 @@ print(inputs)
{:else}
```python
raw_inputs = [
- "I've been waiting for a HuggingFace course my whole life.", # J'ai attendu un cours de HuggingFace toute ma vie.
+ "I've been waiting for a HuggingFace course my whole life.",
+ # J'ai attendu un cours de HuggingFace toute ma vie.
"I hate this so much!", # Je déteste tellement ça !
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="tf")
@@ -175,7 +178,7 @@ Pour chaque entrée du modèle, nous récupérons un vecteur en grande dimension
Si cela ne fait pas sens, ne vous inquiétez pas. Nous expliquons tout plus tard.
-Bien que ces états cachés puissent être utiles en eux-mêmes, ils sont généralement les entrées d'une autre partie du modèle, connue sous le nom de *tête*. Dans le [Chapitre 1](/course/fr/chapter1), les différentes tâches auraient pu être réalisées avec la même architecture mais en ayant chacune d'elles une tête différente.
+Bien que ces états cachés puissent être utiles en eux-mêmes, ils sont généralement les entrées d'une autre partie du modèle, connue sous le nom de *tête*. Dans le [chapitre 1](/course/fr/chapter1), les différentes tâches auraient pu être réalisées avec la même architecture mais en ayant chacune d'elles une tête différente.
### Un vecteur de grande dimension ?
@@ -210,7 +213,7 @@ print(outputs.last_hidden_state.shape)
```
{/if}
-Notez que les sorties des modèles de la bibliothèque 🤗 Transformers se comportent comme des `namedtuples` ou des dictionnaires. Vous pouvez accéder aux éléments par attributs (comme nous l'avons fait), par clé (`outputs["last_hidden_state"]`), ou même par l’index si vous savez exactement où se trouve la chose que vous cherchez (`outputs[0]`).
+Notez que les sorties des modèles de la bibliothèque 🤗 *Transformers* se comportent comme des `namedtuples` ou des dictionnaires. Vous pouvez accéder aux éléments par attributs (comme nous l'avons fait), par clé (`outputs["last_hidden_state"]`), ou même par l’index si vous savez exactement où se trouve la chose que vous cherchez (`outputs[0]`).
### Les têtes des modèles : donner du sens aux chiffres
Les têtes des modèles prennent en entrée le vecteur de grande dimension des états cachés et le projettent sur une autre dimension. Elles sont généralement composées d'une ou de quelques couches linéaires :
@@ -289,7 +292,7 @@ tensor([[-1.5607, 1.6123],
```
{/if}
-Notre modèle a prédit `[-1.5607, 1.6123]` pour la première phrase et `[ 4.1692, -3.3464]` pour la seconde. Ce ne sont pas des probabilités mais des *logits*, les scores bruts, non normalisés, produits par la dernière couche du modèle. Pour être convertis en probabilités, ils doivent passer par une couche [SoftMax](https://fr.wikipedia.org/wiki/Fonction_softmax) (tous les modèles de la bibliothèque 🤗 Transformers sortent les logits car la fonction de perte de l'entraînement fusionne généralement la dernière fonction d'activation, comme la SoftMax, avec la fonction de perte réelle, comme l'entropie croisée) :
+Notre modèle a prédit `[-1.5607, 1.6123]` pour la première phrase et `[ 4.1692, -3.3464]` pour la seconde. Ce ne sont pas des probabilités mais des *logits*, les scores bruts, non normalisés, produits par la dernière couche du modèle. Pour être convertis en probabilités, ils doivent passer par une couche [SoftMax](https://fr.wikipedia.org/wiki/Fonction_softmax) (tous les modèles de la bibliothèque 🤗 *Transformers* sortent les logits car la fonction de perte de l'entraînement fusionne généralement la dernière fonction d'activation, comme la SoftMax, avec la fonction de perte réelle, comme l'entropie croisée) :
{#if fw === 'pt'}
```py
diff --git a/chapters/fr/chapter2/3.mdx b/chapters/fr/chapter2/3.mdx
index 96e555191..9fce3d02f 100644
--- a/chapters/fr/chapter2/3.mdx
+++ b/chapters/fr/chapter2/3.mdx
@@ -1,231 +1,231 @@
-
-
-# Les modèles
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-{#if fw === 'pt'}
-
-{:else}
-
-{/if}
-
-{#if fw === 'pt'}
-Dans cette section, nous allons examiner de plus près la création et l'utilisation d'un modèle. Nous utiliserons la classe `AutoModel` qui est pratique lorsque vous voulez instancier n'importe quel modèle à partir d'un checkpoint.
-
-La classe `AutoModel` et toutes les classes apparentées sont en fait de simples *wrappers* sur la grande variété de modèles disponibles dans la bibliothèque. C'est une enveloppe intelligente car elle peut automatiquement deviner l'architecture appropriée pour votre *checkpoint* et ensuite instancier un modèle avec cette architecture.
-
-{:else}
-Dans cette section, nous allons examiner de plus près la création et l'utilisation d'un modèle. Nous utiliserons la classe `TFAutoModel` qui est pratique lorsque vous voulez instancier n'importe quel modèle à partir d'un checkpoint.
-
-La classe `TFAutoModel` et toutes les classes apparentées sont en fait de simples *wrappers* sur la grande variété de modèles disponibles dans la bibliothèque. C'est une enveloppe intelligente car elle peut automatiquement deviner l'architecture appropriée pour votre *checkpoint* et ensuite instancier un modèle avec cette architecture.
-
-{/if}
-
-Cependant, si vous connaissez le type de modèle que vous voulez utiliser, vous pouvez utiliser directement la classe qui définit son architecture. Voyons comment cela fonctionne avec un modèle BERT.
-
-## Création d’un *transformer*
-
-La première chose que nous devons faire pour initialiser un modèle BERT est de charger un objet configuration :
-
-{#if fw === 'pt'}
-```py
-from transformers import BertConfig, BertModel
-
-# Construire la configuration
-config = BertConfig()
-
-# Construire le modèle à partir de la configuration
-model = BertModel(config)
-```
-{:else}
-```py
-from transformers import BertConfig, TFBertModel
-
-# Construire la configuration
-config = BertConfig()
-
-# Construire le modèle à partir de la configuration
-model = TFBertModel(config)
-```
-{/if}
-
-La configuration contient de nombreux attributs qui sont utilisés pour construire le modèle :
-```py
-print(config)
-```
-
-```python out
-BertConfig {
- [...]
- "hidden_size": 768,
- "intermediate_size": 3072,
- "max_position_embeddings": 512,
- "num_attention_heads": 12,
- "num_hidden_layers": 12,
- [...]
-}
-```
-
-Bien que vous n'ayez pas encore vu ce que font tous ces attributs, vous devriez en reconnaître certains : l'attribut `hidden_size` définit la taille du vecteur `hidden_states`, et `num_hidden_layers` définit le nombre de couches que le *transformer* possède.
-
-### Différentes méthodes de chargement
-
-Un modèle créé à partir de la configuration par défaut est initialisé avec des valeurs aléatoires :
-
-
-{#if fw === 'pt'}
-```py
-from transformers import BertConfig, BertModel
-
-config = BertConfig()
-model = BertModel(config)
-
-# Le modèle est initialisé de façon aléatoire !
-```
-{:else}
-```py
-from transformers import BertConfig, TFBertModel
-
-config = BertConfig()
-model = TFBertModel(config)
-
-# Le modèle est initialisé de façon aléatoire !
-```
-{/if}
-
-Le modèle peut être utilisé tel quel mais il produira du charabia. En effet, il doit d'abord être entraîné. Nous pourrions entraîner le modèle à partir de zéro sur la tâche qui nous intéresse mais comme vous l'avez vu dans le [Chapitre 1](/course/fr/chapter1) cela nécessiterait beaucoup de temps et de données. De plus cela aurait un impact environnemental non négligeable. Pour éviter les efforts inutiles et redondants, il est impératif de pouvoir partager et réutiliser les modèles qui ont déjà été entraînés.
-
-Charger un *transformer* qui a déjà été entraîné est simple : nous pouvons le faire en utilisant la méthode `from_pretrained()` :
-
-
-{#if fw === 'pt'}
-```py
-from transformers import BertModel
-
-model = BertModel.from_pretrained("bert-base-cased")
-```
-
-Comme vu précédemment, nous pouvons remplacer `BertModel` par la classe équivalente `AutoModel`. A partir de maintenant nous ferons cela car cela produit un code agnostique *checkpoint*, c’est-à-dire que si votre code fonctionne pour un *checkpoint* donné, il devrait fonctionner sans problème avec un autre. Cela s'applique même si l'architecture est différente du moment que le *checkpoint* a été entraîné pour une tâche similaire (par exemple, une tâche d'analyse de sentiments).
-
-{:else}
-```py
-from transformers import TFBertModel
-
-model = TFBertModel.from_pretrained("bert-base-cased")
-```
-
-Comme vu précédemment, nous pouvons remplacer `TFBertModel` par la classe équivalente `TFAutoModel`. A partir de maintenant nous ferons cela car cela produit un code agnostique *checkpoint*, c’est-à-dire que si votre code fonctionne pour un *checkpoint* donné, il devrait fonctionner sans problème avec un autre. Cela s'applique même si l'architecture est différente du moment que le *checkpoint* a été entraîné pour une tâche similaire (par exemple, une tâche d'analyse de sentiments).
-
-{/if}
-
-Dans l'exemple de code ci-dessus, nous n'avons pas utilisé `BertConfig` et avons à la place chargé un modèle pré-entraîné via l'identifiant `bert-base-cased`. Il s'agit d'un *checkpoint* qui a été entraîné par les auteurs de BERT eux-mêmes. Vous pouvez trouver davantage de détails à son sujet dans la [fiche du modèle](https://huggingface.co/bert-base-cased).
-
-Ce modèle est maintenant initialisé avec tous les poids du *checkpoint*. Il peut être utilisé directement pour l'inférence sur les tâches sur lesquelles il a été entraîné. Il peut également être *finetuné* sur une nouvelle tâche. En entraînant avec des poids pré-entraînés plutôt qu'à partir de zéro, nous pouvons rapidement obtenir de bons résultats.
-
-Les poids ont été téléchargés et mis en cache (afin que les futurs appels à la méthode `from_pretrained()` ne les retéléchargent pas) dans le dossier cache, qui est par défaut *~/.cache/huggingface/transformers*. Vous pouvez personnaliser votre dossier de cache en définissant la variable d'environnement `HF_HOME`.
-
-L'identifiant utilisé pour charger le modèle peut être l'identifiant de n'importe quel modèle sur le *Model Hub* du moment qu'il est compatible avec l'architecture BERT. La liste complète des *checkpoints* de BERT disponibles peut être trouvée [ici](https://huggingface.co/models?filter=bert).
-
-### Méthodes de sauvegarde
-
-Sauvegarder un modèle est aussi facile que d'en charger un. Nous utilisons la méthode `save_pretrained()`, qui est analogue à la méthode `from_pretrained()` :
-
-
-```py
-model.save_pretrained("directory_on_my_computer")
-```
-
-Cela enregistre deux fichiers sur votre disque :
-
-{#if fw === 'pt'}
-```
-ls directory_on_my_computer
-
-config.json pytorch_model.bin
-```
-{:else}
-```
-ls directory_on_my_computer
-
-config.json tf_model.h5
-```
-{/if}
-
-Si vous jetez un coup d'œil au fichier *config.json*, vous reconnaîtrez les attributs nécessaires pour construire l'architecture du modèle. Ce fichier contient également certaines métadonnées, comme l'origine du *checkpoint* et la version de la bibliothèque 🤗 *Transformers* que vous utilisiez lors du dernier enregistrement du point *checkpoint*.
-
-{#if fw === 'pt'}
-Le fichier *pytorch_model.bin* est connu comme le *dictionnaire d'état*. Il contient tous les poids de votre modèle. Les deux fichiers vont de pair : la configuration est nécessaire pour connaître l'architecture de votre modèle, tandis que les poids du modèle sont les paramètres de votre modèle.
-
-{:else}
-Le fichier *tf_model.h5* est connu comme le *dictionnaire d'état*. Il contient tous les poids de votre modèle. Les deux fichiers vont de pair : la configuration est nécessaire pour connaître l'architecture de votre modèle, tandis que les poids du modèle sont les paramètres de votre modèle.
-
-{/if}
-
-### Utilisation d'un *transformer* pour l'inférence
-
-Maintenant que vous savez comment charger et sauvegarder un modèle, essayons de l'utiliser pour faire quelques prédictions. Les *transformers* ne peuvent traiter que des nombres. Des nombres que le *tokenizer* génère. Mais avant de parler des *tokenizers*, explorons les entrées que le modèle accepte.
-
-Les *tokenizers* se chargent de passer les entrées vers les tenseurs du *framework* approprié. Pour vous aider à comprendre ce qui se passe, jetons un coup d'œil rapide à ce qui doit être fait avant d'envoyer les entrées au modèle.
-
-Disons que nous avons les séquences suivantes :
-
-
-```py
-sequences = ["Hello!", "Cool.", "Nice!"]
-```
-
-Le *tokenizer* les convertit en indices de vocabulaire qui sont généralement appelés *input IDs*. Chaque séquence est maintenant une liste de nombres ! La sortie résultante est :
-
-```py no-format
-encoded_sequences = [
- [101, 7592, 999, 102],
- [101, 4658, 1012, 102],
- [101, 3835, 999, 102],
-]
-```
-
-Il s'agit d'une liste de séquences encodées : une liste de listes. Les tenseurs n'acceptent que des formes rectangulaires (pensez aux matrices). Ce « tableau » est déjà de forme rectangulaire, donc le convertir en tenseur est facile :
-
-{#if fw === 'pt'}
-```py
-import torch
-
-model_inputs = torch.tensor(encoded_sequences)
-```
-{:else}
-```py
-import tensorflow as tf
-
-model_inputs = tf.constant(encoded_sequences)
-```
-{/if}
-
-### Utilisation des tenseurs comme entrées du modèle
-
-L'utilisation des tenseurs avec le modèle est extrêmement simple, il suffit d'appeler le modèle avec les entrées :
-
-
-```py
-output = model(model_inputs)
-```
-
-Bien que le modèle accepte un grand nombre d'arguments différents, seuls les identifiants d'entrée sont nécessaires. Nous expliquerons plus tard ce que font les autres arguments et quand ils sont nécessaires. Avant cela, regardons de plus près les *tokenizers*, cet outil qui construit les entrées qu'un *transformer* peut comprendre.
+
+
+# Les modèles
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+{#if fw === 'pt'}
+
+{:else}
+
+{/if}
+
+{#if fw === 'pt'}
+Dans cette section, nous allons examiner de plus près la création et l'utilisation d'un modèle. Nous utiliserons la classe `AutoModel` qui est pratique lorsque vous voulez instancier n'importe quel modèle à partir d'un checkpoint.
+
+La classe `AutoModel` et toutes les classes apparentées sont en fait de simples *wrappers* sur la grande variété de modèles disponibles dans la bibliothèque. C'est une enveloppe intelligente car elle peut automatiquement deviner l'architecture appropriée pour votre *checkpoint* et ensuite instancier un modèle avec cette architecture.
+
+{:else}
+Dans cette section, nous allons examiner de plus près la création et l'utilisation d'un modèle. Nous utiliserons la classe `TFAutoModel` qui est pratique lorsque vous voulez instancier n'importe quel modèle à partir d'un checkpoint.
+
+La classe `TFAutoModel` et toutes les classes apparentées sont en fait de simples *wrappers* sur la grande variété de modèles disponibles dans la bibliothèque. C'est une enveloppe intelligente car elle peut automatiquement deviner l'architecture appropriée pour votre *checkpoint* et ensuite instancier un modèle avec cette architecture.
+
+{/if}
+
+Cependant, si vous connaissez le type de modèle que vous voulez utiliser, vous pouvez utiliser directement la classe qui définit son architecture. Voyons comment cela fonctionne avec un modèle BERT.
+
+## Création d’un
transformer
+
+La première chose que nous devons faire pour initialiser un modèle BERT est de charger un objet configuration :
+
+{#if fw === 'pt'}
+```py
+from transformers import BertConfig, BertModel
+
+# Construire la configuration
+config = BertConfig()
+
+# Construire le modèle à partir de la configuration
+model = BertModel(config)
+```
+{:else}
+```py
+from transformers import BertConfig, TFBertModel
+
+# Construire la configuration
+config = BertConfig()
+
+# Construire le modèle à partir de la configuration
+model = TFBertModel(config)
+```
+{/if}
+
+La configuration contient de nombreux attributs qui sont utilisés pour construire le modèle :
+```py
+print(config)
+```
+
+```python out
+BertConfig {
+ [...]
+ "hidden_size": 768,
+ "intermediate_size": 3072,
+ "max_position_embeddings": 512,
+ "num_attention_heads": 12,
+ "num_hidden_layers": 12,
+ [...]
+}
+```
+
+Bien que vous n'ayez pas encore vu ce que font tous ces attributs, vous devriez en reconnaître certains : l'attribut `hidden_size` définit la taille du vecteur `hidden_states`, et `num_hidden_layers` définit le nombre de couches que le *transformer* possède.
+
+### Différentes méthodes de chargement
+
+Un modèle créé à partir de la configuration par défaut est initialisé avec des valeurs aléatoires :
+
+
+{#if fw === 'pt'}
+```py
+from transformers import BertConfig, BertModel
+
+config = BertConfig()
+model = BertModel(config)
+
+# Le modèle est initialisé de façon aléatoire !
+```
+{:else}
+```py
+from transformers import BertConfig, TFBertModel
+
+config = BertConfig()
+model = TFBertModel(config)
+
+# Le modèle est initialisé de façon aléatoire !
+```
+{/if}
+
+Le modèle peut être utilisé tel quel mais il produira du charabia. En effet, il doit d'abord être entraîné. Nous pourrions entraîner le modèle à partir de zéro sur la tâche qui nous intéresse mais comme vous l'avez vu dans le [chapitre 1](/course/fr/chapter1) cela nécessiterait beaucoup de temps et de données. De plus cela aurait un impact environnemental non négligeable. Pour éviter les efforts inutiles et redondants, il est impératif de pouvoir partager et réutiliser les modèles qui ont déjà été entraînés.
+
+Charger un *transformer* qui a déjà été entraîné est simple : nous pouvons le faire en utilisant la méthode `from_pretrained()` :
+
+
+{#if fw === 'pt'}
+```py
+from transformers import BertModel
+
+model = BertModel.from_pretrained("bert-base-cased")
+```
+
+Comme vu précédemment, nous pouvons remplacer `BertModel` par la classe équivalente `AutoModel`. A partir de maintenant nous ferons cela car cela produit un code agnostique *checkpoint*, c’est-à-dire que si votre code fonctionne pour un *checkpoint* donné, il devrait fonctionner sans problème avec un autre. Cela s'applique même si l'architecture est différente du moment que le *checkpoint* a été entraîné pour une tâche similaire (par exemple, une tâche d'analyse de sentiments).
+
+{:else}
+```py
+from transformers import TFBertModel
+
+model = TFBertModel.from_pretrained("bert-base-cased")
+```
+
+Comme vu précédemment, nous pouvons remplacer `TFBertModel` par la classe équivalente `TFAutoModel`. A partir de maintenant nous ferons cela car cela produit un code agnostique *checkpoint*, c’est-à-dire que si votre code fonctionne pour un *checkpoint* donné, il devrait fonctionner sans problème avec un autre. Cela s'applique même si l'architecture est différente du moment que le *checkpoint* a été entraîné pour une tâche similaire (par exemple, une tâche d'analyse de sentiments).
+
+{/if}
+
+Dans l'exemple de code ci-dessus, nous n'avons pas utilisé `BertConfig` et avons à la place chargé un modèle pré-entraîné via l'identifiant `bert-base-cased`. Il s'agit d'un *checkpoint* qui a été entraîné par les auteurs de BERT eux-mêmes. Vous pouvez trouver davantage de détails à son sujet dans la [fiche du modèle](https://huggingface.co/bert-base-cased).
+
+Ce modèle est maintenant initialisé avec tous les poids du *checkpoint*. Il peut être utilisé directement pour l'inférence sur les tâches sur lesquelles il a été entraîné. Il peut également être *finetuné* sur une nouvelle tâche. En entraînant avec des poids pré-entraînés plutôt qu'à partir de zéro, nous pouvons rapidement obtenir de bons résultats.
+
+Les poids ont été téléchargés et mis en cache (afin que les futurs appels à la méthode `from_pretrained()` ne les retéléchargent pas) dans le dossier cache, qui est par défaut *~/.cache/huggingface/transformers*. Vous pouvez personnaliser votre dossier de cache en définissant la variable d'environnement `HF_HOME`.
+
+L'identifiant utilisé pour charger le modèle peut être l'identifiant de n'importe quel modèle sur le *Model Hub* du moment qu'il est compatible avec l'architecture BERT. La liste complète des *checkpoints* de BERT disponibles peut être trouvée [ici](https://huggingface.co/models?filter=bert).
+
+### Méthodes de sauvegarde
+
+Sauvegarder un modèle est aussi facile que d'en charger un. Nous utilisons la méthode `save_pretrained()`, qui est analogue à la méthode `from_pretrained()` :
+
+
+```py
+model.save_pretrained("directory_on_my_computer")
+```
+
+Cela enregistre deux fichiers sur votre disque :
+
+{#if fw === 'pt'}
+```
+ls directory_on_my_computer
+
+config.json pytorch_model.bin
+```
+{:else}
+```
+ls directory_on_my_computer
+
+config.json tf_model.h5
+```
+{/if}
+
+Si vous jetez un coup d'œil au fichier *config.json*, vous reconnaîtrez les attributs nécessaires pour construire l'architecture du modèle. Ce fichier contient également certaines métadonnées, comme l'origine du *checkpoint* et la version de la bibliothèque 🤗 *Transformers* que vous utilisiez lors du dernier enregistrement du point *checkpoint*.
+
+{#if fw === 'pt'}
+Le fichier *pytorch_model.bin* est connu comme le *dictionnaire d'état*. Il contient tous les poids de votre modèle. Les deux fichiers vont de pair : la configuration est nécessaire pour connaître l'architecture de votre modèle, tandis que les poids du modèle sont les paramètres de votre modèle.
+
+{:else}
+Le fichier *tf_model.h5* est connu comme le *dictionnaire d'état*. Il contient tous les poids de votre modèle. Les deux fichiers vont de pair : la configuration est nécessaire pour connaître l'architecture de votre modèle, tandis que les poids du modèle sont les paramètres de votre modèle.
+
+{/if}
+
+### Utilisation d'un
transformer pour l'inférence
+
+Maintenant que vous savez comment charger et sauvegarder un modèle, essayons de l'utiliser pour faire quelques prédictions. Les *transformers* ne peuvent traiter que des nombres. Des nombres que le *tokenizer* génère. Mais avant de parler des *tokenizers*, explorons les entrées que le modèle accepte.
+
+Les *tokenizers* se chargent de passer les entrées vers les tenseurs du *framework* approprié. Pour vous aider à comprendre ce qui se passe, jetons un coup d'œil rapide à ce qui doit être fait avant d'envoyer les entrées au modèle.
+
+Disons que nous avons les séquences suivantes :
+
+
+```py
+sequences = ["Hello!", "Cool.", "Nice!"]
+```
+
+Le *tokenizer* les convertit en indices de vocabulaire qui sont généralement appelés *input IDs*. Chaque séquence est maintenant une liste de nombres ! La sortie résultante est :
+
+```py no-format
+encoded_sequences = [
+ [101, 7592, 999, 102],
+ [101, 4658, 1012, 102],
+ [101, 3835, 999, 102],
+]
+```
+
+Il s'agit d'une liste de séquences encodées : une liste de listes. Les tenseurs n'acceptent que des formes rectangulaires (pensez aux matrices). Ce « tableau » est déjà de forme rectangulaire, donc le convertir en tenseur est facile :
+
+{#if fw === 'pt'}
+```py
+import torch
+
+model_inputs = torch.tensor(encoded_sequences)
+```
+{:else}
+```py
+import tensorflow as tf
+
+model_inputs = tf.constant(encoded_sequences)
+```
+{/if}
+
+### Utilisation des tenseurs comme entrées du modèle
+
+L'utilisation des tenseurs avec le modèle est extrêmement simple, il suffit d'appeler le modèle avec les entrées :
+
+
+```py
+output = model(model_inputs)
+```
+
+Bien que le modèle accepte un grand nombre d'arguments différents, seuls les identifiants d'entrée sont nécessaires. Nous expliquerons plus tard ce que font les autres arguments et quand ils sont nécessaires. Avant cela, regardons de plus près les *tokenizers*, cet outil qui construit les entrées qu'un *transformer* peut comprendre.
diff --git a/chapters/fr/chapter2/4.mdx b/chapters/fr/chapter2/4.mdx
index f407cfed1..2015763fb 100644
--- a/chapters/fr/chapter2/4.mdx
+++ b/chapters/fr/chapter2/4.mdx
@@ -1,253 +1,253 @@
-
-
-# Les *tokenizers*
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-
-
-Les *tokenizers* sont l'un des principaux composants du pipeline de NLP. Ils ont un seul objectif : traduire le texte en données pouvant être traitées par le modèle. Les modèles ne pouvant traiter que des nombres, les *tokenizers* doivent convertir nos entrées textuelles en données numériques. Dans cette section, nous allons explorer ce qui se passe exactement dans le pipeline de tokénisation.
-
-Dans les tâches de NLP, les données traitées sont généralement du texte brut. Voici un exemple de ce type de texte :
-
-
-```
-Jim Henson was a puppeteer # Jim Henson était un marionnettiste.
-```
-
-Les modèles ne pouvant traiter que des nombres, nous devons trouver un moyen de convertir le texte brut en nombres. C'est ce que font les *tokenizers* et il existe de nombreuses façons de procéder. L'objectif est de trouver la représentation la plus significative, c'est-à-dire celle qui a le plus de sens pour le modèle, et si possible qui soit la plus petite.
-
-Voyons quelques exemples d'algorithmes de tokénisation et essayons de répondre à certaines des questions que vous pouvez vous poser à ce sujet.
-
-## *Tokenizer* basé sur les mots
-
-
-
-
-Le premier type de *tokenizer* qui vient à l'esprit est celui basé sur les mots. Il est généralement très facile à utiliser et configurable avec seulement quelques règles. Il donne souvent des résultats décents. Par exemple, dans l'image ci-dessous, l'objectif est de diviser le texte brut en mots et de trouver une représentation numérique pour chacun d'eux :
-
-
-
-
-
-
-Il existe différentes façons de diviser le texte. Par exemple, nous pouvons utiliser les espaces pour segmenter le texte en mots en appliquant la fonction `split()` de Python :
-
-```py
-tokenized_text = "Jim Henson was a puppeteer".split()
-print(tokenized_text)
-```
-
-```python out
-['Jim', 'Henson', 'was', 'a', 'puppeteer'] # ['Jim', 'Henson', était, 'un', 'marionnettiste']
-```
-
-Il existe également des variantes des *tokenizers* basés sur les mots qui ont des règles supplémentaires pour la ponctuation. Avec ce type de *tokenizers* nous pouvons nous retrouver avec des « vocabulaires » assez larges, où un vocabulaire est défini par le nombre total de *tokens* indépendants que nous avons dans notre corpus.
-
-Un identifiant est attribué à chaque mot, en commençant par 0 et en allant jusqu'à la taille du vocabulaire. Le modèle utilise ces identifiants pour identifier chaque mot.
-
-Si nous voulons couvrir complètement une langue avec un *tokenizer* basé sur les mots, nous devons avoir un identifiant pour chaque mot de la langue que nous traitons, ce qui génère une énorme quantité de *tokens*. Par exemple, il y a plus de 500 000 mots dans la langue anglaise. Ainsi pour associer chaque mot à un identifiant, nous devrions garder la trace d'autant d'identifiants. De plus, des mots comme « chien » sont représentés différemment de mots comme « chiens ». Le modèle n'a initialement aucun moyen de savoir que « chien » et « chiens » sont similaires : il identifie les deux mots comme non apparentés. Il en va de même pour d'autres mots similaires, comme « maison » et « maisonnette » que le modèle ne considérera pas comme similaires au départ.
-
-Enfin, nous avons besoin d'un *token* personnalisé pour représenter les mots qui ne font pas partie de notre vocabulaire. C'est ce qu'on appelle le *token* « inconnu » souvent représenté par « [UNK] » (de l’anglais « unknown ») ou « <unk> ; ». C'est généralement un mauvais signe si vous constatez que le *tokenizer* produit un nombre important de ce jeton spécial. Cela signifie qu’il n'a pas été en mesure de récupérer une représentation sensée d'un mot et que vous perdez des informations en cours de route. L'objectif de l'élaboration du vocabulaire est de faire en sorte que le *tokenizer* transforme le moins de mots possible en *token* inconnu.
-
-Une façon de réduire la quantité de *tokens* inconnus est d'aller un niveau plus profond, en utilisant un *tokenizer* basé sur les caractères.
-
-
-## *Tokenizer* basé sur les caractères
-
-
-
-Les *tokenizers* basés sur les caractères divisent le texte en caractères, plutôt qu'en mots. Cela présente deux avantages principaux :
-
-- le vocabulaire est beaucoup plus petit
-- il y a beaucoup moins de *tokens* hors vocabulaire (inconnus) puisque chaque mot peut être construit à partir de caractères.
-
-Mais là aussi, des questions se posent concernant les espaces et la ponctuation :
-
-
-
-
-
-
-
-Cette approche n'est pas non plus parfaite. Puisque la représentation est maintenant basée sur des caractères plutôt que sur des mots, on pourrait dire intuitivement qu’elle est moins significative : chaque caractère ne signifie pas grand-chose en soi, alors que c'est le cas pour les mots. Toutefois, là encore, cela diffère selon la langue. En chinois, par exemple, chaque caractère est porteur de plus d'informations qu'un caractère dans une langue latine.
-
-Un autre élément à prendre en compte est que nous nous retrouverons avec une très grande quantité de *tokens* à traiter par notre modèle. Alors qu’avec un *tokenizer* basé sur les mots, pour un mot donné on aurait qu'un seul *token*, avec un *tokenizer* basé sur les caractères, cela peut facilement se transformer en 10 *tokens* voire plus.
-
-Pour obtenir le meilleur des deux mondes, nous pouvons utiliser une troisième technique qui combine les deux approches : la *tokénisation en sous-mots*.
-
-
-## Tokénisation en sous-mots
-
-
-
-Les algorithmes de tokenisation en sous-mots reposent sur le principe selon lequel les mots fréquemment utilisés ne doivent pas être divisés en sous-mots plus petits, mais les mots rares doivent être décomposés en sous-mots significatifs.
-
-Par exemple, le mot « maisonnette » peut être considéré comme un mot rare et peut être décomposé en « maison » et « ette ». Ces deux mots sont susceptibles d'apparaître plus fréquemment en tant que sous-mots autonomes, alors qu'en même temps le sens de « maison » est conservé par le sens composite de « maison » et « ette ».
-
-Voici un exemple montrant comment un algorithme de tokenisation en sous-mots tokeniserait la séquence « Let's do tokenization ! » :
-
-
-
-
-
-
-
-Ces sous-mots finissent par fournir beaucoup de sens sémantique. Par exemple, ci-dessus, « tokenization » a été divisé en « token » et « ization » : deux *tokens* qui ont un sens sémantique tout en étant peu encombrants (seuls deux *tokens* sont nécessaires pour représenter un long mot). Cela nous permet d'avoir une couverture relativement bonne avec de petits vocabulaires et presque aucun *token* inconnu.
-
-Cette approche est particulièrement utile dans les langues agglutinantes comme le turc, où l'on peut former des mots complexes (presque) arbitrairement longs en enchaînant des sous-mots.
-
-
-### Et plus encore !
-
-Il existe de nombreuses autres techniques. Pour n'en citer que quelques-unes :
-
-- le *Byte-level BPE* utilisé par exemple dans le GPT-2
-- le *WordPiece* utilisé par exemple dans BERT
-- *SentencePiece* ou *Unigram*, utilisés dans plusieurs modèles multilingues.
-
-Vous devriez maintenant avoir une connaissance suffisante du fonctionnement des tokenizers pour commencer à utiliser l'API.
-
-
-## Chargement et sauvegarde
-
-Le chargement et la sauvegarde des *tokenizers* est aussi simple que pour les modèles. En fait, c'est basé sur les deux mêmes méthodes : `from_pretrained()` et `save_pretrained()`. Ces méthodes vont charger ou sauvegarder l'algorithme utilisé par le *tokenizer* (un peu comme l'*architecture* du modèle) ainsi que son vocabulaire (un peu comme les *poids* du modèle).
-
-Le chargement du *tokenizer* de BERT entraîné avec le même *checkpoint* que BERT se fait de la même manière que le chargement du modèle, sauf que nous utilisons la classe `BertTokenizer` :
-
-
-```py
-from transformers import BertTokenizer
-
-tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
-```
-
-{#if fw === 'pt'}
-Similaire à `AutoModel`, la classe `AutoTokenizer` récupère la classe de *tokenizer* appropriée dans la bibliothèque basée sur le nom du *checkpoint*. Elle peut être utilisée directement avec n'importe quel *checkpoint* :
-
-{:else}
-Similaire à `TFAutoModel`, la classe `AutoTokenizer` récupère la classe de *tokenizer* appropriée dans la bibliothèque basée sur le nom du *checkpoint*. Elle peut être utilisée directement avec n'importe quel *checkpoint* :
-
-{/if}
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-```
-
-Nous pouvons à présent utiliser le *tokenizer* comme indiqué dans la section précédente :
-
-```python
-tokenizer("Using a Transformer network is simple")
-```
-
-```python out
-{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
- 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
- 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
-```
-
-La sauvegarde d'un tokenizer est identique à celle d'un modèle :
-
-```py
-tokenizer.save_pretrained("directory_on_my_computer")
-```
-
-Nous parlerons plus en détail des `token_type_ids` au [Chapitre 3](/course/fr/chapter3) et nous expliquerons la clé `attention_mask` un peu plus tard. Tout d'abord, voyons comment les `input_ids` sont générés. Pour ce faire, nous devons examiner les méthodes intermédiaires du *tokenizer*.
-
-## Encodage
-
-
-
-
-La traduction d'un texte en chiffres est connue sous le nom d’*encodage*. L'encodage se fait en deux étapes : la tokenisation, suivie de la conversion en identifiants d'entrée.
-
-Comme nous l'avons vu, la première étape consiste à diviser le texte en mots (ou parties de mots, symboles de ponctuation, etc.), généralement appelés *tokens*. De nombreuses règles peuvent régir ce processus. C'est pourquoi nous devons instancier le *tokenizer* en utilisant le nom du modèle afin de nous assurer que nous utilisons les mêmes règles que celles utilisées lors du pré-entraînement du modèle.
-
-La deuxième étape consiste à convertir ces *tokens* en nombres afin de construire un tenseur à partir de ceux-ci ainsi que de les transmettre au modèle. Pour ce faire, le *tokenizer* possède un *vocabulaire*, qui est la partie que nous téléchargeons lorsque nous l'instancions avec la méthode `from_pretrained()`. Encore une fois, nous devons utiliser le même vocabulaire que celui utilisé lors du pré-entraînement du modèle.
-
-Pour mieux comprendre les deux étapes, nous allons les explorer séparément. A noter que nous utilisons des méthodes effectuant séparément des parties du pipeline de tokenisation afin de montrer les résultats intermédiaires de ces étapes. Néanmoins, en pratique, il faut appeler le *tokenizer* directement sur vos entrées (comme indiqué dans la section 2).
-
-### Tokenisation
-
-Le processus de tokenisation est effectué par la méthode `tokenize()` du *tokenizer* :
-
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-
-sequence = "Using a Transformer network is simple"
-tokens = tokenizer.tokenize(sequence)
-
-print(tokens)
-```
-
-La sortie de cette méthode est une liste de chaînes de caractères ou de *tokens* :
-
-```python out
-['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']
-```
-
-Ce *tokenizer* est un *tokenizer* de sous-mots : il découpe les mots jusqu'à obtenir des *tokens* qui peuvent être représentés par son vocabulaire. C'est le cas ici avec `transformer` qui est divisé en deux *tokens* : `transform` et `##er`.
-
-### De *tokens* aux identifiants d'entrée
-
-La conversion en identifiants d'entrée est gérée par la méthode `convert_tokens_to_ids()` du *tokenizer* :
-
-
-```py
-ids = tokenizer.convert_tokens_to_ids(tokens)
-
-print(ids)
-```
-
-```python out
-[7993, 170, 11303, 1200, 2443, 1110, 3014]
-```
-
-Une fois converties en tenseur dans le *framework* approprié, ces sorties peuvent ensuite être utilisées comme entrées d'un modèle, comme nous l'avons vu précédemment dans ce chapitre.
-
-
-
-✏️ **Essayez !** Reproduisez les deux dernières étapes (tokénisation et conversion en identifiants d'entrée) sur les phrases des entrées que nous avons utilisées dans la section 2 (« I've been waiting for a HuggingFace course my whole life. » et « I hate this so much! »). Vérifiez que vous obtenez les mêmes identifiants d'entrée que nous avons obtenus précédemment !
-
-
-
-## Décodage
-
-Le *décodage* va dans l'autre sens : à partir d'indices du vocabulaire nous voulons obtenir une chaîne de caractères. Cela peut être fait avec la méthode `decode()` comme suit :
-
-
-```py
-decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
-print(decoded_string)
-```
-
-```python out
-'Using a Transformer network is simple'
-```
-
-Notez que la méthode `decode` non seulement reconvertit les indices en *tokens* mais regroupe également les *tokens* faisant partie des mêmes mots. Le but étant de produire une phrase lisible. Ce comportement sera extrêmement utile lorsque dans la suite du cours nous utiliserons des modèles pouvant produire du nouveau texte (soit du texte généré à partir d'un *prompt*, soit pour des problèmes de séquence à séquence comme la traduction ou le résumé de texte).
-
-Vous devriez maintenant comprendre les opérations atomiques qu'un *tokenizer* peut gérer : tokenisation, conversion en identifiants, et reconversion des identifiants en chaîne de caractères. Cependant, nous n'avons fait qu'effleurer la partie émergée de l'iceberg. Dans la section suivante, nous allons pousser notre approche jusqu'à ses limites et voir comment les surmonter.
+
+
+# Les
tokenizers
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+
+
+Les *tokenizers* sont l'un des principaux composants du pipeline de NLP. Ils ont un seul objectif : traduire le texte en données pouvant être traitées par le modèle. Les modèles ne pouvant traiter que des nombres, les *tokenizers* doivent convertir nos entrées textuelles en données numériques. Dans cette section, nous allons explorer ce qui se passe exactement dans le pipeline de tokénisation.
+
+Dans les tâches de NLP, les données traitées sont généralement du texte brut. Voici un exemple de ce type de texte :
+
+
+```
+Jim Henson was a puppeteer # Jim Henson était un marionnettiste
+```
+
+Les modèles ne pouvant traiter que des nombres, nous devons trouver un moyen de convertir le texte brut en nombres. C'est ce que font les *tokenizers* et il existe de nombreuses façons de procéder. L'objectif est de trouver la représentation la plus significative, c'est-à-dire celle qui a le plus de sens pour le modèle, et si possible qui soit la plus petite.
+
+Voyons quelques exemples d'algorithmes de tokénisation et essayons de répondre à certaines des questions que vous pouvez vous poser à ce sujet.
+
+##
Tokenizer basé sur les mots
+
+
+
+
+Le premier type de *tokenizer* qui vient à l'esprit est celui basé sur les mots. Il est généralement très facile à utiliser et configurable avec seulement quelques règles. Il donne souvent des résultats décents. Par exemple, dans l'image ci-dessous, l'objectif est de diviser le texte brut en mots et de trouver une représentation numérique pour chacun d'eux :
+
+
+
+
+
+
+Il existe différentes façons de diviser le texte. Par exemple, nous pouvons utiliser les espaces pour segmenter le texte en mots en appliquant la fonction `split()` de Python :
+
+```py
+tokenized_text = "Jim Henson was a puppeteer".split()
+print(tokenized_text)
+```
+
+```python out
+['Jim', 'Henson', 'was', 'a', 'puppeteer'] # ['Jim', 'Henson', était, 'un', 'marionnettiste']
+```
+
+Il existe également des variantes des *tokenizers* basés sur les mots qui ont des règles supplémentaires pour la ponctuation. Avec ce type de *tokenizers* nous pouvons nous retrouver avec des « vocabulaires » assez larges, où un vocabulaire est défini par le nombre total de *tokens* indépendants que nous avons dans notre corpus.
+
+Un identifiant est attribué à chaque mot, en commençant par 0 et en allant jusqu'à la taille du vocabulaire. Le modèle utilise ces identifiants pour identifier chaque mot.
+
+Si nous voulons couvrir complètement une langue avec un *tokenizer* basé sur les mots, nous devons avoir un identifiant pour chaque mot de la langue que nous traitons, ce qui génère une énorme quantité de *tokens*. Par exemple, il y a plus de 500 000 mots dans la langue anglaise. Ainsi pour associer chaque mot à un identifiant, nous devrions garder la trace d'autant d'identifiants. De plus, des mots comme « chien » sont représentés différemment de mots comme « chiens ». Le modèle n'a initialement aucun moyen de savoir que « chien » et « chiens » sont similaires : il identifie les deux mots comme non apparentés. Il en va de même pour d'autres mots similaires, comme « maison » et « maisonnette » que le modèle ne considérera pas comme similaires au départ.
+
+Enfin, nous avons besoin d'un *token* personnalisé pour représenter les mots qui ne font pas partie de notre vocabulaire. C'est ce qu'on appelle le *token* « inconnu » souvent représenté par « [UNK] » (de l’anglais « unknown ») ou « <unk> ; ». C'est généralement un mauvais signe si vous constatez que le *tokenizer* produit un nombre important de ce jeton spécial. Cela signifie qu’il n'a pas été en mesure de récupérer une représentation sensée d'un mot et que vous perdez des informations en cours de route. L'objectif de l'élaboration du vocabulaire est de faire en sorte que le *tokenizer* transforme le moins de mots possible en *token* inconnu.
+
+Une façon de réduire la quantité de *tokens* inconnus est d'aller un niveau plus profond, en utilisant un *tokenizer* basé sur les caractères.
+
+
+##
Tokenizer basé sur les caractères
+
+
+
+Les *tokenizers* basés sur les caractères divisent le texte en caractères, plutôt qu'en mots. Cela présente deux avantages principaux :
+
+- le vocabulaire est beaucoup plus petit
+- il y a beaucoup moins de *tokens* hors vocabulaire (inconnus) puisque chaque mot peut être construit à partir de caractères.
+
+Mais là aussi, des questions se posent concernant les espaces et la ponctuation :
+
+
+
+
+
+
+
+Cette approche n'est pas non plus parfaite. Puisque la représentation est maintenant basée sur des caractères plutôt que sur des mots, on pourrait dire intuitivement qu’elle est moins significative : chaque caractère ne signifie pas grand-chose en soi, alors que c'est le cas pour les mots. Toutefois, là encore, cela diffère selon la langue. En chinois, par exemple, chaque caractère est porteur de plus d'informations qu'un caractère dans une langue latine.
+
+Un autre élément à prendre en compte est que nous nous retrouverons avec une très grande quantité de *tokens* à traiter par notre modèle. Alors qu’avec un *tokenizer* basé sur les mots, pour un mot donné on aurait qu'un seul *token*, avec un *tokenizer* basé sur les caractères, cela peut facilement se transformer en 10 *tokens* voire plus.
+
+Pour obtenir le meilleur des deux mondes, nous pouvons utiliser une troisième technique qui combine les deux approches : la *tokénisation en sous-mots*.
+
+
+## Tokénisation en sous-mots
+
+
+
+Les algorithmes de tokenisation en sous-mots reposent sur le principe selon lequel les mots fréquemment utilisés ne doivent pas être divisés en sous-mots plus petits, mais les mots rares doivent être décomposés en sous-mots significatifs.
+
+Par exemple, le mot « maisonnette » peut être considéré comme un mot rare et peut être décomposé en « maison » et « ette ». Ces deux mots sont susceptibles d'apparaître plus fréquemment en tant que sous-mots autonomes, alors qu'en même temps le sens de « maison » est conservé par le sens composite de « maison » et « ette ».
+
+Voici un exemple montrant comment un algorithme de tokenisation en sous-mots tokeniserait la séquence « *Let's do tokenization* ! » :
+
+
+
+
+
+
+
+Ces sous-mots finissent par fournir beaucoup de sens sémantique. Par exemple, ci-dessus, « *tokenization* » a été divisé en « *token* » et « *ization* » : deux *tokens* qui ont un sens sémantique tout en étant peu encombrants (seuls deux *tokens* sont nécessaires pour représenter un long mot). Cela nous permet d'avoir une couverture relativement bonne avec de petits vocabulaires et presque aucun *token* inconnu.
+
+Cette approche est particulièrement utile dans les langues agglutinantes comme le turc, où l'on peut former des mots complexes (presque) arbitrairement longs en enchaînant des sous-mots.
+
+
+### Et plus encore !
+
+Il existe de nombreuses autres techniques. Pour n'en citer que quelques-unes :
+
+- le *Byte-level BPE* utilisé par exemple dans le GPT-2
+- le *WordPiece* utilisé par exemple dans BERT
+- *SentencePiece* ou *Unigram*, utilisés dans plusieurs modèles multilingues.
+
+Vous devriez maintenant avoir une connaissance suffisante du fonctionnement des tokenizers pour commencer à utiliser l'API.
+
+
+## Chargement et sauvegarde
+
+Le chargement et la sauvegarde des *tokenizers* est aussi simple que pour les modèles. En fait, c'est basé sur les deux mêmes méthodes : `from_pretrained()` et `save_pretrained()`. Ces méthodes vont charger ou sauvegarder l'algorithme utilisé par le *tokenizer* (un peu comme l'*architecture* du modèle) ainsi que son vocabulaire (un peu comme les *poids* du modèle).
+
+Le chargement du *tokenizer* de BERT entraîné avec le même *checkpoint* que BERT se fait de la même manière que le chargement du modèle, sauf que nous utilisons la classe `BertTokenizer` :
+
+
+```py
+from transformers import BertTokenizer
+
+tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
+```
+
+{#if fw === 'pt'}
+Similaire à `AutoModel`, la classe `AutoTokenizer` récupère la classe de *tokenizer* appropriée dans la bibliothèque basée sur le nom du *checkpoint*. Elle peut être utilisée directement avec n'importe quel *checkpoint* :
+
+{:else}
+Similaire à `TFAutoModel`, la classe `AutoTokenizer` récupère la classe de *tokenizer* appropriée dans la bibliothèque basée sur le nom du *checkpoint*. Elle peut être utilisée directement avec n'importe quel *checkpoint* :
+
+{/if}
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+```
+
+Nous pouvons à présent utiliser le *tokenizer* comme indiqué dans la section précédente :
+
+```python
+tokenizer("Using a Transformer network is simple")
+```
+
+```python out
+{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
+ 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
+ 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
+```
+
+La sauvegarde d'un tokenizer est identique à celle d'un modèle :
+
+```py
+tokenizer.save_pretrained("directory_on_my_computer")
+```
+
+Nous parlerons plus en détail des `token_type_ids` au [chapitre 3](/course/fr/chapter3) et nous expliquerons la clé `attention_mask` un peu plus tard. Tout d'abord, voyons comment les `input_ids` sont générés. Pour ce faire, nous devons examiner les méthodes intermédiaires du *tokenizer*.
+
+## Encodage
+
+
+
+
+La traduction d'un texte en chiffres est connue sous le nom d’*encodage*. L'encodage se fait en deux étapes : la tokenisation, suivie de la conversion en identifiants d'entrée.
+
+Comme nous l'avons vu, la première étape consiste à diviser le texte en mots (ou parties de mots, symboles de ponctuation, etc.), généralement appelés *tokens*. De nombreuses règles peuvent régir ce processus. C'est pourquoi nous devons instancier le *tokenizer* en utilisant le nom du modèle afin de nous assurer que nous utilisons les mêmes règles que celles utilisées lors du pré-entraînement du modèle.
+
+La deuxième étape consiste à convertir ces *tokens* en nombres afin de construire un tenseur à partir de ceux-ci ainsi que de les transmettre au modèle. Pour ce faire, le *tokenizer* possède un *vocabulaire*, qui est la partie que nous téléchargeons lorsque nous l'instancions avec la méthode `from_pretrained()`. Encore une fois, nous devons utiliser le même vocabulaire que celui utilisé lors du pré-entraînement du modèle.
+
+Pour mieux comprendre les deux étapes, nous allons les explorer séparément. A noter que nous utilisons des méthodes effectuant séparément des parties du pipeline de tokenisation afin de montrer les résultats intermédiaires de ces étapes. Néanmoins, en pratique, il faut appeler le *tokenizer* directement sur vos entrées (comme indiqué dans la section 2).
+
+### Tokenisation
+
+Le processus de tokenisation est effectué par la méthode `tokenize()` du *tokenizer* :
+
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+
+sequence = "Using a Transformer network is simple"
+tokens = tokenizer.tokenize(sequence)
+
+print(tokens)
+```
+
+La sortie de cette méthode est une liste de chaînes de caractères ou de *tokens* :
+
+```python out
+['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']
+```
+
+Ce *tokenizer* est un *tokenizer* de sous-mots : il découpe les mots jusqu'à obtenir des *tokens* qui peuvent être représentés par son vocabulaire. C'est le cas ici avec `transformer` qui est divisé en deux *tokens* : `transform` et `##er`.
+
+### De
tokens aux identifiants d'entrée
+
+La conversion en identifiants d'entrée est gérée par la méthode `convert_tokens_to_ids()` du *tokenizer* :
+
+
+```py
+ids = tokenizer.convert_tokens_to_ids(tokens)
+
+print(ids)
+```
+
+```python out
+[7993, 170, 11303, 1200, 2443, 1110, 3014]
+```
+
+Une fois converties en tenseur dans le *framework* approprié, ces sorties peuvent ensuite être utilisées comme entrées d'un modèle, comme nous l'avons vu précédemment dans ce chapitre.
+
+
+
+✏️ **Essayez !** Reproduisez les deux dernières étapes (tokénisation et conversion en identifiants d'entrée) sur les phrases des entrées que nous avons utilisées dans la section 2 (« I've been waiting for a HuggingFace course my whole life. » et « I hate this so much! »). Vérifiez que vous obtenez les mêmes identifiants d'entrée que nous avons obtenus précédemment !
+
+
+
+## Décodage
+
+Le *décodage* va dans l'autre sens : à partir d'indices du vocabulaire nous voulons obtenir une chaîne de caractères. Cela peut être fait avec la méthode `decode()` comme suit :
+
+
+```py
+decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
+print(decoded_string)
+```
+
+```python out
+'Using a Transformer network is simple'
+```
+
+Notez que la méthode `decode` non seulement reconvertit les indices en *tokens* mais regroupe également les *tokens* faisant partie des mêmes mots. Le but étant de produire une phrase lisible. Ce comportement sera extrêmement utile lorsque dans la suite du cours nous utiliserons des modèles pouvant produire du nouveau texte (soit du texte généré à partir d'un *prompt*, soit pour des problèmes de séquence à séquence comme la traduction ou le résumé de texte).
+
+Vous devriez maintenant comprendre les opérations atomiques qu'un *tokenizer* peut gérer : tokenisation, conversion en identifiants, et reconversion des identifiants en chaîne de caractères. Cependant, nous n'avons fait qu'effleurer la partie émergée de l'iceberg. Dans la section suivante, nous allons pousser notre approche jusqu'à ses limites et voir comment les surmonter.
diff --git a/chapters/fr/chapter2/5.mdx b/chapters/fr/chapter2/5.mdx
index 8983c0590..fb85ff966 100644
--- a/chapters/fr/chapter2/5.mdx
+++ b/chapters/fr/chapter2/5.mdx
@@ -51,12 +51,13 @@ checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-sequence = "I've been waiting for a HuggingFace course my whole life." # J'ai attendu un cours d’HuggingFace toute ma vie.
+sequence = "I've been waiting for a HuggingFace course my whole life."
+# J'ai attendu un cours d’HuggingFace toute ma vie.
tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = torch.tensor(ids)
-# This line will fail.
+# Cette ligne va échouer.
model(input_ids)
```
@@ -72,7 +73,8 @@ checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
-sequence = "I've been waiting for a HuggingFace course my whole life." # J'ai attendu un cours d’HuggingFace toute ma vie.
+sequence = "I've been waiting for a HuggingFace course my whole life."
+# J'ai attendu un cours d’HuggingFace toute ma vie.
tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
@@ -125,7 +127,8 @@ checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-sequence = "I've been waiting for a HuggingFace course my whole life." # J'ai attendu un cours d’HuggingFace toute ma vie.
+sequence = "I've been waiting for a HuggingFace course my whole life."
+# J'ai attendu un cours d’HuggingFace toute ma vie.
tokens = tokenizer.tokenize(sequence)
@@ -191,9 +194,9 @@ Il s'agit d'un batch de deux séquences identiques !
-Utiliser des *batchs* permet au modèle de fonctionner lorsque vous lui donnez plusieurs séquences. Utiliser plusieurs séquences est aussi simple que de construire un batch avec une seule séquence. Il y a cependant un deuxième problème. Lorsque vous essayez de regrouper deux phrases (ou plus), elles peuvent être de longueurs différentes. Si vous avez déjà travaillé avec des tenseurs, vous savez qu'ils doivent être de forme rectangulaire. Vous ne pourrez donc pas convertir directement la liste des identifiants d'entrée en un tenseur. Pour contourner ce problème, nous avons l'habitude de *rembourrer* (le *padding* en anglais) les entrées.
+Utiliser des *batchs* permet au modèle de fonctionner lorsque vous lui donnez plusieurs séquences. Utiliser plusieurs séquences est aussi simple que de construire un batch avec une seule séquence. Il y a cependant un deuxième problème. Lorsque vous essayez de regrouper deux phrases (ou plus), elles peuvent être de longueurs différentes. Si vous avez déjà travaillé avec des tenseurs, vous savez qu'ils doivent être de forme rectangulaire. Vous ne pourrez donc pas convertir directement la liste des identifiants d'entrée en un tenseur. Pour contourner ce problème, nous avons l'habitude de *rembourrer*/*remplir* (le *padding* en anglais) les entrées.
-## *Padding* des entrées
+##
Padding des entrées
La liste de listes suivante ne peut pas être convertie en un tenseur :
@@ -329,7 +332,7 @@ Remarquez comment la dernière valeur de la deuxième séquence est un identifia
-✏️ **Essayez !** Appliquez la tokenisation manuellement sur les deux phrases utilisées dans la section 2 (« I've been waiting for a HuggingFace course my whole life. » et « I hate this so much! »). Passez-les dans le modèle et vérifiez que vous obtenez les mêmes logits que dans la section 2. Ensuite regroupez-les en utilisant le jeton de *padding* et créez le masque d'attention approprié. Vérifiez que vous obtenez les mêmes résultats qu’en passant par le modèle !
+✏️ **Essayez !** Appliquez la tokenisation manuellement sur les deux phrases utilisées dans la section 2 (« I've been waiting for a HuggingFace course my whole life. » et « I hate this so much! »). Passez-les dans le modèle et vérifiez que vous obtenez les mêmes logits que dans la section 2. Ensuite regroupez-les en utilisant le jeton de *padding* et créez le masque d'attention approprié. Vérifiez que vous obtenez les mêmes résultats qu’en passant par le modèle !
diff --git a/chapters/fr/chapter2/6.mdx b/chapters/fr/chapter2/6.mdx
index 00580b774..9602b2a96 100644
--- a/chapters/fr/chapter2/6.mdx
+++ b/chapters/fr/chapter2/6.mdx
@@ -33,7 +33,8 @@ from transformers import AutoTokenizer
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-sequence = "I've been waiting for a HuggingFace course my whole life." # J'ai attendu un cours d’HuggingFace toute ma vie.
+sequence = "I've been waiting for a HuggingFace course my whole life."
+# J'ai attendu un cours d’HuggingFace toute ma vie.
model_inputs = tokenizer(sequence)
```
@@ -44,7 +45,8 @@ Comme nous allons le voir dans les quelques exemples ci-dessous, cette méthode
```py
-sequence = "I've been waiting for a HuggingFace course my whole life." # J'ai attendu un cours d’HuggingFace toute ma vie.
+sequence = "I've been waiting for a HuggingFace course my whole life."
+# J'ai attendu un cours d’HuggingFace toute ma vie.
model_inputs = tokenizer(sequence)
@@ -68,8 +70,7 @@ Il est possible de faire du *padding* selon plusieurs objectifs :
# Remplit les séquences jusqu'à la longueur maximale de la séquence
model_inputs = tokenizer(sequences, padding="longest")
-# Remplit les séquences jusqu'à la longueur maximale du modèle
-# (512 for BERT or DistilBERT)
+# Remplit les séquences jusqu'à la longueur maximale du modèle (512 pour BERT ou DistilBERT)
model_inputs = tokenizer(sequences, padding="max_length")
# Remplit les séquences jusqu'à la longueur maximale spécifiée
@@ -85,7 +86,7 @@ sequences = [
] # « J'ai attendu un cours de HuggingFace toute ma vie. », « Moi aussi ! »
# Tronque les séquences qui sont plus longues que la longueur maximale du modèle
-# (512 for BERT or DistilBERT)
+# (512 pour BERT ou DistilBERT)
model_inputs = tokenizer(sequences, truncation=True)
# Tronque les séquences qui sont plus longues que la longueur maximale spécifiée
@@ -116,7 +117,8 @@ Si nous jetons un coup d'œil aux identifiants d'entrée renvoyés par le *token
```py
-sequence = "I've been waiting for a HuggingFace course my whole life." # « J'ai attendu un cours de HuggingFace toute ma vie. »
+sequence = "I've been waiting for a HuggingFace course my whole life."
+# « J'ai attendu un cours de HuggingFace toute ma vie. »
model_inputs = tokenizer(sequence)
print(model_inputs["input_ids"])
@@ -145,7 +147,7 @@ print(tokenizer.decode(ids))
Le *tokenizer* a ajouté le mot spécial `[CLS]` au début et le mot spécial `[SEP]` à la fin. C'est parce que le modèle a été pré-entraîné avec ces mots, donc pour obtenir les mêmes résultats pour l'inférence, nous devons également les ajouter. Notez que certains modèles n'ajoutent pas de mots spéciaux, ou en ajoutent des différents. Les modèles peuvent aussi ajouter ces mots spéciaux seulement au début, ou seulement à la fin. Dans tous les cas, le *tokenizer* sait lesquels sont attendus et s'en occupe pour vous.
-## Conclusion : du *tokenizer* au modèle
+## Conclusion : du
tokenizer au modèle
Maintenant que nous avons vu toutes les étapes individuelles que l'objet `tokenizer` utilise lorsqu'il est appliqué sur des textes, voyons une dernière fois comment il peut gérer plusieurs séquences (*padding*), de très longues séquences (*troncation*) et plusieurs types de tenseurs avec son API principale :
diff --git a/chapters/fr/chapter2/7.mdx b/chapters/fr/chapter2/7.mdx
index c6fac5c9a..2b6c11798 100644
--- a/chapters/fr/chapter2/7.mdx
+++ b/chapters/fr/chapter2/7.mdx
@@ -1,12 +1,12 @@
-# Utilisation de base terminée !
-
-Bravo à vous pour avoir suivi le cours jusqu'ici ! Pour récapituler, dans ce chapitre vous avez :
-- appris les blocs de construction de base d'un *transformer*,
-- appris ce qui constitue un pipeline de tokenisation,
-- vu comment utiliser un *transformer* en pratique,
-- appris comment tirer parti d'un *tokenizer* pour convertir du texte en tenseurs compréhensibles par le modèle,
-- configurer ensemble un *tokenizer* et un modèle afin de passer du texte aux prédictions,
-- appris les limites des identifiants d'entrée et ce que sont que les masques d'attention,
-- joué avec des méthodes de *tokenizer* polyvalentes et configurables.
-
-À partir de maintenant, vous devriez être en mesure de naviguer librement dans la documentation 🤗 *Transformers*. Le vocabulaire vous semblera familier et vous avez vu les méthodes que vous utiliserez la plupart du temps.
+# Utilisation de base terminée !
+
+Bravo à vous pour avoir suivi le cours jusqu'ici ! Pour récapituler, dans ce chapitre vous avez :
+- appris les blocs de construction de base d'un *transformer*,
+- appris ce qui constitue un pipeline de tokenisation,
+- vu comment utiliser un *transformer* en pratique,
+- appris comment tirer parti d'un *tokenizer* pour convertir du texte en tenseurs compréhensibles par le modèle,
+- configurer ensemble un *tokenizer* et un modèle afin de passer du texte aux prédictions,
+- appris les limites des identifiants d'entrée et ce que sont que les masques d'attention,
+- joué avec des méthodes de *tokenizer* polyvalentes et configurables.
+
+À partir de maintenant, vous devriez être en mesure de naviguer librement dans la documentation 🤗 *Transformers*. Le vocabulaire vous semblera familier et vous avez vu les méthodes que vous utiliserez la plupart du temps.
diff --git a/chapters/fr/chapter2/8.mdx b/chapters/fr/chapter2/8.mdx
index 7ea13a174..a3fa30228 100644
--- a/chapters/fr/chapter2/8.mdx
+++ b/chapters/fr/chapter2/8.mdx
@@ -1,307 +1,307 @@
-
-
-
-
-# Quiz de fin de chapitre
-
-### 1. Quel est l'ordre du pipeline de modélisation du langage ?
-
-
-
tokenizer donne un sens à ces prédictions et les reconvertit en texte si nécessaire.",
- explain: " Le modèle ne peut pas comprendre le texte ! Le tokenizer doit d'abord tokeniser le texte et le convertir en identifiants afin qu'il soit compréhensible par le modèle."},
- {
- text: " Tout d'abord, le tokenizer, qui traite le texte et renvoie des identifiants. Puis le modèle traite ces identifiants et produit une prédiction, qui peut être du texte.",
- explain: " La prédiction du modèle ne peut pas être du texte immédiatement. Le tokenizer doit être utilisé afin de reconvertir la prédiction en texte !"},
- {
- text: " Le tokenizer traite le texte et renvoie des identifiants. Le modèle traite ces identifiants et produit une prédiction. Le tokenizer peut alors être utilisé à nouveau pour reconvertir ces prédictions en texte.",
- explain: " C’est correct ! Le tokenizer peut être utilisé à la fois pour la tokenisation et la dé-tokénisation.",
- correct: true
- }
- ]}
-/>
-
-### 2. Combien de dimensions le tenseur produit par le transformer de base possède-t-il et quelles sont-elles ?
-
-
-transformers gèrent les batchs, même avec une seule séquence ce serait une taille de batch de 1 !"
- },
- {
- text: "3: la longueur de la séquence, la taille du batch et la taille cachée.",
- explain: "C’est correct !",
- correct: true
- }
- ]}
-/>
-
-### 3. Lequel des éléments suivants est un exemple de tokenisation en sous-mots ?
-
-
-
-### 4. Qu'est-ce qu'une tête de modèle ?
-
-transformer de base qui redirige les tenseurs vers leurs couches correctes.",
- explain: "Incorrect ! Il n'y a pas de tel composant."
- },
- {
- text: "Également connu sous le nom de mécanisme d'auto-attention, il adapte la représentation d'un token en fonction des autres tokens de la séquence.",
- explain: "Incorrect ! La couche d'auto-attention contient des têtes d'attention mais ce ne sont pas des têtes d'adaptation."
- },
- {
- text: "Un composant supplémentaire, généralement constitué d'une ou plusieurs couches, pour convertir les prédictions du transformer en une sortie spécifique à la tâche.",
- explain: "C'est exact. Les têtes d'adaptation, aussi appelées simplement têtes, se présentent sous différentes formes : têtes de modélisation du langage, têtes de réponse aux questions, têtes de classification des séquences, etc.",
- correct: true
- }
- ]}
-/>
-
-{#if fw === 'pt'}
-### 5. Qu'est-ce qu'un AutoModel?
-
-checkpoints et modèles soient capables de gérer plusieurs langues, il n'existe pas d'outils intégrés pour la sélection automatique des checkpoints en fonction de la langue. Vous devez vous rendre sur le Hub des modèles pour trouver le meilleur checkpoint pour votre tâche !"
- }
- ]}
-/>
-
-{:else}
-### 5. What is an AutoModel?
-
-checkpoints et modèles soient capables de gérer plusieurs langues, il n'existe pas d'outils intégrés pour la sélection automatique des checkpoints en fonction de la langue. Vous devez vous rendre sur le Hub des modèles pour trouver le meilleur checkpoint pour votre tâche !"
- }
- ]}
-/>
-
-{/if}
-
-### 6. Quelles sont les techniques à connaître lors de la mise en batch de séquences de longueurs différentes ?
-
-
-padding",
- explain: "Oui, le padding est une façon correcte d'égaliser les séquences pour qu'elles tiennent dans une forme rectangulaire. Mais est-ce le seul moyen ?",
- correct: true
- },
- {
- text: "Les masques d'attention ",
- explain: "Absolument ! Les masques d'attention sont d'une importance capitale lorsqu'on manipule des séquences de longueurs différentes. Ce n'est cependant pas la seule technique à laquelle il faut faire attention.",
- correct: true
- }
- ]}
-/>
-
-### 7. Quel est l'intérêt d'appliquer une fonction SoftMax aux logits produits par un modèle de classification de séquences ?
-
-
-
-### 8. Autour de quelle méthode s'articule la majeure partie de l'API tokenizer ?
-
-encode, car elle peut encoder du texte en identifiants et des identifiants en prédictions.",
- explain: "Faux ! Bien que la méthode encode
existe sur les tokenizer, elle n'existe pas sur les modèles."
- },
- {
- text: "Appeler directement l'objet tokenizer",
- explain: " Exactement ! La méthode __call__
du tokenizer est une méthode très puissante qui peut traiter à peu près tout. C'est également la méthode utilisée pour récupérer les prédictions d'un modèle.",
- correct: true
- },
- {
- text: "pad
",
- explain: "C'est faux ! Le padding est très utile mais ce n'est qu'une partie de l'API tokenizer."
- },
- {
- text: "tokenize
",
- explain: "La méthode tokenize
est est sans doute l'une des méthodes les plus utiles, mais elle ne constitue pas le cœur de l'API tokenizer."
- }
- ]}
-/>
-
-### 9. Que contient la variable `result` dans cet exemple de code ?
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-result = tokenizer.tokenize("Hello!")
-```
-
-token.",
- explain: " Absolument ! Convertissez cela en identifiants, et donnez-les à un modèle !",
- correct: true
- },
- {
- text: "Une liste d'identifiants",
- explain: "C'est faux, c'est à cela que la méthode __call__
ou la méthode convert_tokens_to_ids
sert !"
- },
- {
- text: "Une chaîne contenant tous les tokens",
- explain: "Ce serait sous-optimal car le but est de diviser la chaîne de caractères en plusieurs éléments."
- }
- ]}
-/>
-
-{#if fw === 'pt'}
-### 10. Y a-t-il un problème avec le code suivant ?
-
-
-```py
-from transformers import AutoTokenizer, AutoModel
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-model = AutoModel.from_pretrained("gpt2")
-
-encoded = tokenizer("Hey!", return_tensors="pt")
-result = model(**encoded)
-```
-
-tokenizer qui a été entraîné avec un checkpoint différent est rarement une bonne idée. Le modèle n'a pas été entraîné pour donner du sens à la sortie de ce tokenizer donc la sortie du modèle (s'il peut même fonctionner !) n'aura aucun sens."
- },
- {
- text: " Le tokenizer et le modèle doivent toujours provenir du même checkpoint.",
- explain: "C’est juste !",
- correct: true
- },
- {
- text: " C'est une bonne pratique de faire du padding et de troncage avec le tokenizer car chaque entrée est un batch.",
- explain: "Il est vrai que chaque entrée de modèle doit être un batch. Cependant, tronquer ou compléter cette séquence n'aurait pas nécessairement de sens puisqu'il n'y en a qu'une seule. Il s'agit là de techniques permettant de mettre en batch une liste de phrases."
- }
- ]}
-/>
-
-{:else}
-### 10. Y a-t-il un problème avec le code suivant ?
-
-```py
-from transformers import AutoTokenizer, TFAutoModel
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-model = TFAutoModel.from_pretrained("gpt2")
-
-encoded = tokenizer("Hey!", return_tensors="pt")
-result = model(**encoded)
-```
-
-tokenizer qui a été entraîné avec un checkpoint différent est rarement une bonne idée. Le modèle n'a pas été entraîné pour donner du sens à la sortie de ce tokenizer donc la sortie du modèle (s'il peut même fonctionner !) n'aura aucun sens."
- },
- {
- text: " Le tokenizer et le modèle doivent toujours provenir du même checkpoint.",
- explain: "C’est juste !",
- correct: true
- },
- {
- text: " C'est une bonne pratique de faire du padding et de troncage avec le tokenizer car chaque entrée est un batch.",
- explain: "Il est vrai que chaque entrée de modèle doit être un batch. Cependant, tronquer ou compléter cette séquence n'aurait pas nécessairement de sens puisqu'il n'y en a qu'une seule. Il s'agit là de techniques permettant de mettre en batch une liste de phrases."
- }
- ]}
-/>
-
-{/if}
+
+
+
+
+# Quiz de fin de chapitre
+
+### 1. Quel est l'ordre du pipeline de modélisation du langage ?
+
+
+tokenizer donne un sens à ces prédictions et les reconvertit en texte si nécessaire.",
+ explain: " Le modèle ne peut pas comprendre le texte ! Le tokenizer doit d'abord tokeniser le texte et le convertir en identifiants afin qu'il soit compréhensible par le modèle."},
+ {
+ text: " Tout d'abord, le tokenizer, qui traite le texte et renvoie des identifiants. Puis le modèle traite ces identifiants et produit une prédiction, qui peut être du texte.",
+ explain: " La prédiction du modèle ne peut pas être du texte immédiatement. Le tokenizer doit être utilisé afin de reconvertir la prédiction en texte !"},
+ {
+ text: " Le tokenizer traite le texte et renvoie des identifiants. Le modèle traite ces identifiants et produit une prédiction. Le tokenizer peut alors être utilisé à nouveau pour reconvertir ces prédictions en texte.",
+ explain: " Le tokenizer peut être utilisé à la fois pour la tokenisation et la dé-tokénisation.",
+ correct: true
+ }
+ ]}
+/>
+
+### 2. Combien de dimensions le tenseur produit par le transformer de base possède-t-il et quelles sont-elles ?
+
+
+transformers gèrent les batchs, même avec une seule séquence ce serait une taille de batch de 1 !"
+ },
+ {
+ text: "3: la longueur de la séquence, la taille du batch et la taille cachée.",
+ explain: "",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. Lequel des éléments suivants est un exemple de tokenisation en sous-mots ?
+
+
+
+### 4. Qu'est-ce qu'une tête de modèle ?
+
+transformer de base qui redirige les tenseurs vers leurs couches correctes.",
+ explain: "Il n'y a pas de tel composant."
+ },
+ {
+ text: "Également connu sous le nom de mécanisme d'auto-attention, il adapte la représentation d'un token en fonction des autres tokens de la séquence.",
+ explain: "La couche d'auto-attention contient des têtes d'attention mais ce ne sont pas des têtes d'adaptation."
+ },
+ {
+ text: "Un composant supplémentaire, généralement constitué d'une ou plusieurs couches, pour convertir les prédictions du transformer en une sortie spécifique à la tâche.",
+ explain: "Les têtes d'adaptation, aussi appelées simplement têtes, se présentent sous différentes formes : têtes de modélisation du langage, têtes de réponse aux questions, têtes de classification des séquences, etc.",
+ correct: true
+ }
+ ]}
+/>
+
+{#if fw === 'pt'}
+### 5. Qu'est-ce qu'un AutoModel?
+
+checkpoints et modèles soient capables de gérer plusieurs langues, il n'existe pas d'outils intégrés pour la sélection automatique des checkpoints en fonction de la langue. Vous devez vous rendre sur le Hub des modèles pour trouver le meilleur checkpoint pour votre tâche !"
+ }
+ ]}
+/>
+
+{:else}
+### 5. What is an AutoModel?
+
+checkpoints et modèles soient capables de gérer plusieurs langues, il n'existe pas d'outils intégrés pour la sélection automatique des checkpoints en fonction de la langue. Vous devez vous rendre sur le Hub des modèles pour trouver le meilleur checkpoint pour votre tâche !"
+ }
+ ]}
+/>
+
+{/if}
+
+### 6. Quelles sont les techniques à connaître lors de la mise en batch de séquences de longueurs différentes ?
+
+
+padding",
+ explain: "Le padding est une façon correcte d'égaliser les séquences pour qu'elles tiennent dans une forme rectangulaire. Mais est-ce le seul moyen ?",
+ correct: true
+ },
+ {
+ text: "Les masques d'attention ",
+ explain: "Les masques d'attention sont d'une importance capitale lorsqu'on manipule des séquences de longueurs différentes. Ce n'est cependant pas la seule technique à laquelle il faut faire attention.",
+ correct: true
+ }
+ ]}
+/>
+
+### 7. Quel est l'intérêt d'appliquer une fonction SoftMax aux logits produits par un modèle de classification de séquences ?
+
+
+
+### 8. Autour de quelle méthode s'articule la majeure partie de l'API tokenizer ?
+
+encode, car elle peut encoder du texte en identifiants et des identifiants en prédictions.",
+ explain: "Bien que la méthode encode
existe sur les tokenizer, elle n'existe pas sur les modèles."
+ },
+ {
+ text: "Appeler directement l'objet tokenizer",
+ explain: "La méthode __call__
du tokenizer est une méthode très puissante qui peut traiter à peu près tout. C'est également la méthode utilisée pour récupérer les prédictions d'un modèle.",
+ correct: true
+ },
+ {
+ text: "pad
",
+ explain: "Le padding est très utile mais ce n'est qu'une partie de l'API tokenizer."
+ },
+ {
+ text: "tokenize
",
+ explain: "La méthode tokenize
est est sans doute l'une des méthodes les plus utiles, mais elle ne constitue pas le cœur de l'API tokenizer."
+ }
+ ]}
+/>
+
+### 9. Que contient la variable `result` dans cet exemple de code ?
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+result = tokenizer.tokenize("Hello!")
+```
+
+token.",
+ explain: "Convertissez cela en identifiants, et donnez-les à un modèle !",
+ correct: true
+ },
+ {
+ text: "Une liste d'identifiants",
+ explain: "C'est à cela que la méthode __call__
ou la méthode convert_tokens_to_ids
sert !"
+ },
+ {
+ text: "Une chaîne contenant tous les tokens",
+ explain: "Ce serait sous-optimal car le but est de diviser la chaîne de caractères en plusieurs éléments."
+ }
+ ]}
+/>
+
+{#if fw === 'pt'}
+### 10. Y a-t-il un problème avec le code suivant ?
+
+
+```py
+from transformers import AutoTokenizer, AutoModel
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+model = AutoModel.from_pretrained("gpt2")
+
+encoded = tokenizer("Hey!", return_tensors="pt")
+result = model(**encoded)
+```
+
+tokenizer qui a été entraîné avec un checkpoint différent est rarement une bonne idée. Le modèle n'a pas été entraîné pour donner du sens à la sortie de ce tokenizer donc la sortie du modèle (s'il peut même fonctionner !) n'aura aucun sens."
+ },
+ {
+ text: " Le tokenizer et le modèle doivent toujours provenir du même checkpoint.",
+ explain: "",
+ correct: true
+ },
+ {
+ text: " C'est une bonne pratique de faire du padding et de troncage avec le tokenizer car chaque entrée est un batch.",
+ explain: "Il est vrai que chaque entrée de modèle doit être un batch. Cependant, tronquer ou compléter cette séquence n'aurait pas nécessairement de sens puisqu'il n'y en a qu'une seule. Il s'agit là de techniques permettant de mettre en batch une liste de phrases."
+ }
+ ]}
+/>
+
+{:else}
+### 10. Y a-t-il un problème avec le code suivant ?
+
+```py
+from transformers import AutoTokenizer, TFAutoModel
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+model = TFAutoModel.from_pretrained("gpt2")
+
+encoded = tokenizer("Hey!", return_tensors="pt")
+result = model(**encoded)
+```
+
+tokenizer qui a été entraîné avec un checkpoint différent est rarement une bonne idée. Le modèle n'a pas été entraîné pour donner du sens à la sortie de ce tokenizer donc la sortie du modèle (s'il peut même fonctionner !) n'aura aucun sens."
+ },
+ {
+ text: " Le tokenizer et le modèle doivent toujours provenir du même checkpoint.",
+ explain: "",
+ correct: true
+ },
+ {
+ text: " C'est une bonne pratique de faire du padding et de troncage avec le tokenizer car chaque entrée est un batch.",
+ explain: "Il est vrai que chaque entrée de modèle doit être un batch. Cependant, tronquer ou compléter cette séquence n'aurait pas nécessairement de sens puisqu'il n'y en a qu'une seule. Il s'agit là de techniques permettant de mettre en batch une liste de phrases."
+ }
+ ]}
+/>
+
+{/if}
diff --git a/chapters/fr/chapter3/2.mdx b/chapters/fr/chapter3/2.mdx
index 498b99153..297c260f5 100644
--- a/chapters/fr/chapter3/2.mdx
+++ b/chapters/fr/chapter3/2.mdx
@@ -35,7 +35,8 @@ checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
- "I've been waiting for a HuggingFace course my whole life.", # J'ai attendu un cours de HuggingFace toute ma vie.
+ "I've been waiting for a HuggingFace course my whole life.",
+ # J'ai attendu un cours de HuggingFace toute ma vie.
"This course is amazing!", # Ce cours est incroyable !
]
batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
@@ -62,7 +63,8 @@ checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
- "I've been waiting for a HuggingFace course my whole life.", # J'ai attendu un cours de HuggingFace toute ma vie.
+ "I've been waiting for a HuggingFace course my whole life.",
+ # J'ai attendu un cours de HuggingFace toute ma vie.
"This course is amazing!", # Ce cours est incroyable !
]
batch = dict(tokenizer(sequences, padding=True, truncation=True, return_tensors="tf"))
@@ -77,7 +79,7 @@ Evidemment, entraîner un modèle avec seulement deux phrases ne va pas donner d
Dans cette section, nous allons utiliser comme exemple le jeu de données MRPC (*Microsoft Research Paraphrase Corpus*) présenté dans un [papier](https://www.aclweb.org/anthology/I05-5002.pdf) par William B. Dolan et Chris Brockett. Ce jeu de données contient 5801 paires de phrases avec un label indiquant si ces paires sont des paraphrases ou non (i.e. si elles ont la même signification). Nous l'avons choisi pour ce chapitre parce que c'est un petit jeu de données et cela rend donc simples les expériences d'entraînement sur ce jeu de données.
-### Charger un jeu de données depuis le *Hub*
+### Charger un jeu de données depuis le Hub
{#if fw === 'pt'}
@@ -127,8 +129,10 @@ raw_train_dataset[0]
```python out
{'idx': 0,
'label': 1,
- 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .', # Amrozi a accusé son frère, qu'il a appelé « le témoin », de déformer délibérément son témoignage.
- 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'} # Se référant à lui uniquement comme « le témoin », Amrozi a accusé son frère de déformer délibérément son témoignage.
+ 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
+ # Amrozi a accusé son frère, qu'il a appelé « le témoin », de déformer délibérément son témoignage.
+ 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
+ # Se référant à lui uniquement comme « le témoin », Amrozi a accusé son frère de déformer délibérément son témoignage.
```
Nous pouvons voir que les étiquettes sont déjà des entiers, donc nous n'aurons pas à faire de prétraitement ici. Pour savoir quel entier correspond à quel label, nous pouvons inspecter les `features` de notre `raw_train_dataset`. Cela nous indiquera le type de chaque colonne :
@@ -187,7 +191,7 @@ inputs
}
```
-Nous avons discuté des clés `input_ids` et `attention_mask` dans le [Chapitre 2](/course/fr/chapter2), mais nous avons laissé de côté les `token_type_ids`. Dans cet exemple, c'est ce qui indique au modèle quelle partie de l'entrée est la première phrase et quelle partie est la deuxième phrase.
+Nous avons discuté des clés `input_ids` et `attention_mask` dans le [chapitre 2](/course/fr/chapter2), mais nous avons laissé de côté les `token_type_ids`. Dans cet exemple, c'est ce qui indique au modèle quelle partie de l'entrée est la première phrase et quelle partie est la deuxième phrase.
@@ -218,13 +222,13 @@ Comme vous pouvez le voir, les parties de l'entrée correspondant à `[CLS] sent
Notez que si vous choisissez un autre *checkpoint*, vous n'aurez pas nécessairement les `token_type_ids` dans vos entrées tokenisées (par exemple, ils ne sont pas retournés si vous utilisez un modèle DistilBERT). Ils ne sont retournés que lorsque le modèle sait quoi faire avec eux, parce qu'il les a vus pendant son pré-entraînement.
-Ici, BERT est pré-entraîné avec les *tokens* de type ID et en plus de l'objectif de modélisation du langage masqué dont nous avons abordé dans [Chapitre 1](/course/fr/chapter1), il a un objectif supplémentaire appelé _prédiction de la phrase suivante_. Le but de cette tâche est de modéliser la relation entre des paires de phrases.
+Ici, BERT est pré-entraîné avec les *tokens* de type ID et en plus de l'objectif de modélisation du langage masqué dont nous avons abordé dans [chapitre 1](/course/fr/chapter1), il a un objectif supplémentaire appelé _prédiction de la phrase suivante_. Le but de cette tâche est de modéliser la relation entre des paires de phrases.
Avec la prédiction de la phrase suivante, on fournit au modèle des paires de phrases (avec des *tokens* masqués de manière aléatoire) et on lui demande de prédire si la deuxième phrase suit la première. Pour rendre la tâche non triviale, la moitié du temps, les phrases se suivent dans le document d'origine dont elles ont été extraites, et l'autre moitié du temps, les deux phrases proviennent de deux documents différents.
En général, vous n'avez pas besoin de vous inquiéter de savoir s'il y a ou non des `token_type_ids` dans vos entrées tokenisées : tant que vous utilisez le même *checkpoint* pour le *tokenizer* et le modèle, tout ira bien puisque le *tokenizer* sait quoi fournir à son modèle.
-Maintenant que nous avons vu comment notre *tokenizer* peut traiter une paire de phrases, nous pouvons l'utiliser pour tokeniser l'ensemble de notre jeu de données : comme dans le [chapitre précédent](/course/fr/chapter2), nous pouvons fournir au *tokenizer* une liste de paires de phrases en lui donnant la liste des premières phrases, puis la liste des secondes phrases. Ceci est également compatible avec les options de remplissage et de troncature que nous avons vues dans le [Chapitre 2](/course/fr/chapter2). Voici donc une façon de prétraiter le jeu de données d'entraînement :
+Maintenant que nous avons vu comment notre *tokenizer* peut traiter une paire de phrases, nous pouvons l'utiliser pour tokeniser l'ensemble de notre jeu de données : comme dans le [chapitre précédent](/course/fr/chapter2), nous pouvons fournir au *tokenizer* une liste de paires de phrases en lui donnant la liste des premières phrases, puis la liste des secondes phrases. Ceci est également compatible avec les options de remplissage et de troncature que nous avons vues dans le [chapitre 2](/course/fr/chapter2). Voici donc une façon de prétraiter le jeu de données d'entraînement :
```py
tokenized_dataset = tokenizer(
@@ -280,7 +284,7 @@ Notre `tokenize_function` retourne un dictionnaire avec les clés `input_ids`, `
La dernière chose que nous devrons faire est de remplir tous les exemples à la longueur de l'élément le plus long lorsque nous regroupons les éléments, une technique que nous appelons le *padding dynamique*.
-### *Padding* dynamique
+### Padding dynamique
diff --git a/chapters/fr/chapter3/3.mdx b/chapters/fr/chapter3/3.mdx
index a23832502..13563fb0d 100644
--- a/chapters/fr/chapter3/3.mdx
+++ b/chapters/fr/chapter3/3.mdx
@@ -1,6 +1,6 @@
-# *Finetuner* un modèle avec l'API Trainer
+# Finetuner un modèle avec l'API Trainer
-💡 Si vous voulez télécharger automatiquement votre modèle sur le *Hub* pendant l'entraînement, passez `push_to_hub=True` dans le `TrainingArguments`. Nous en apprendrons plus à ce sujet au [Chapitre 4](/course/fr/chapter4/3).
+💡 Si vous voulez télécharger automatiquement votre modèle sur le *Hub* pendant l'entraînement, passez `push_to_hub=True` dans le `TrainingArguments`. Nous en apprendrons plus à ce sujet au [chapitre 4](/course/fr/chapter4/3).
@@ -56,7 +56,7 @@ from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
```
-Vous remarquerez que contrairement au [Chapitre 2](/course/fr/chapter2), vous obtenez un message d'avertissement après l'instanciation de ce modèle pré-entraîné. C'est parce que BERT n'a pas été pré-entraîné à la classification de paires de phrases, donc la tête du modèle pré-entraîné a été supprimée et une nouvelle tête adaptée à la classification de séquences a été ajoutée à la place. Les messages d'avertissement indiquent que certains poids n'ont pas été utilisés (ceux correspondant à la tête de pré-entraînement abandonnée) et que d'autres ont été initialisés de manière aléatoire (ceux pour la nouvelle tête). Il conclut en vous encourageant à entraîner le modèle, ce qui est exactement ce que nous allons faire maintenant.
+Vous remarquerez que contrairement au [chapitre 2](/course/fr/chapter2), vous obtenez un message d'avertissement après l'instanciation de ce modèle pré-entraîné. C'est parce que BERT n'a pas été pré-entraîné à la classification de paires de phrases, donc la tête du modèle pré-entraîné a été supprimée et une nouvelle tête adaptée à la classification de séquences a été ajoutée à la place. Les messages d'avertissement indiquent que certains poids n'ont pas été utilisés (ceux correspondant à la tête de pré-entraînement abandonnée) et que d'autres ont été initialisés de manière aléatoire (ceux pour la nouvelle tête). Il conclut en vous encourageant à entraîner le modèle, ce qui est exactement ce que nous allons faire maintenant.
Une fois que nous avons notre modèle, nous pouvons définir un `Trainer` en lui passant tous les objets construits jusqu'à présent : le `model`, le `training_args`, les jeux de données d'entraînement et de validation, notre `data_collator`, et notre `tokenizer` :
@@ -162,7 +162,7 @@ Cette fois, il indiquera la perte et les mesures de validation à la fin de chaq
Le `Trainer` fonctionnera sur plusieurs GPUs ou TPUs et fournit beaucoup d'options, comme l'entraînement en précision mixte (utilisez `fp16 = True` dans vos arguments d'entraînement). Nous passerons en revue tout ce qu'il supporte dans le chapitre 10.
-Ceci conclut l'introduction au *fine-tuning* en utilisant l'API `Trainer`. Un exemple d'utilisation pour les tâches de NLP les plus communes es donné dans le [Chapitre 7](/course/fr/chapter7), mais pour l'instant regardons comment faire la même chose en PyTorch pur.
+Ceci conclut l'introduction au *fine-tuning* en utilisant l'API `Trainer`. Un exemple d'utilisation pour les tâches de NLP les plus communes es donné dans le [chapitre 7](/course/fr/chapter7), mais pour l'instant regardons comment faire la même chose en PyTorch pur.
diff --git a/chapters/fr/chapter3/3_tf.mdx b/chapters/fr/chapter3/3_tf.mdx
index bc96a7d05..6be37c3a3 100644
--- a/chapters/fr/chapter3/3_tf.mdx
+++ b/chapters/fr/chapter3/3_tf.mdx
@@ -1,190 +1,190 @@
-
-
-# *Finetuner* un modèle avec Keras
-
-
-
-Une fois que vous avez fait tout le travail de prétraitement des données dans la dernière section, il ne vous reste que quelques étapes pour entraîner le modèle. Notez, cependant, que la commande `model.fit()` s'exécutera très lentement sur un CPU. Si vous n'avez pas de GPU, vous pouvez avoir accès à des GPUs ou TPUs gratuits sur [Google Colab](https://colab.research.google.com/).
-
-Les exemples de code ci-dessous supposent que vous avez déjà exécuté les exemples de la section précédente. Voici un bref résumé de ce dont vous avez besoin :
-
-```py
-from datasets import load_dataset
-from transformers import AutoTokenizer, DataCollatorWithPadding
-import numpy as np
-
-raw_datasets = load_dataset("glue", "mrpc")
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-
-def tokenize_function(example):
- return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
-
-
-tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
-
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
-
-tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
- columns=["attention_mask", "input_ids", "token_type_ids"],
- label_cols=["labels"],
- shuffle=True,
- collate_fn=data_collator,
- batch_size=8,
-)
-
-tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
- columns=["attention_mask", "input_ids", "token_type_ids"],
- label_cols=["labels"],
- shuffle=False,
- collate_fn=data_collator,
- batch_size=8,
-)
-```
-
-### Entraînement
-
-Les modèles TensorFlow importés depuis 🤗 *Transformers* sont déjà des modèles Keras. Voici une courte introduction à Keras.
-
-
-
-Cela signifie qu'une fois que nous disposons de nos données, très peu de travail est nécessaire pour commencer à entraîner sur celles-ci.
-
-
-
-Comme dans le [chapitre précédent](/course/fr/chapter2), nous allons utiliser la classe `TFAutoModelForSequenceClassification`, avec deux étiquettes :
-
-```py
-from transformers import TFAutoModelForSequenceClassification
-
-model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-```
-
-Vous remarquerez que, contrairement au [Chapitre 2](/course/fr/chapter2), vous obtenez un message d'avertissement après l'instanciation de ce modèle pré-entraîné. Ceci est dû au fait que BERT n'a pas été pré-entraîné à la classification de paires de phrases, donc la tête du modèle pré-entraîné a été supprimée et une nouvelle tête adaptée à la classification de séquences a été insérée à la place. Les messages d'avertissement indiquent que certains poids n'ont pas été utilisés (ceux correspondant à la tête de pré-entraînement abandonnée) et que d'autres ont été initialisés de manière aléatoire (ceux pour la nouvelle tête). Il conclut en vous encourageant à entraîner le modèle, ce qui est exactement ce que nous allons faire maintenant.
-
-Pour *finetuner* le modèle sur notre jeu de données, nous devons simplement `compiler()` notre modèle et ensuite passer nos données à la méthode `fit()`. Cela va démarrer le processus de *finetuning* (qui devrait prendre quelques minutes sur un GPU) et rapporter la perte d'entraînement au fur et à mesure, plus la perte de validation à la fin de chaque époque.
-
-
-
-Notez que les modèles 🤗 *Transformers* ont une capacité spéciale que la plupart des modèles Keras n'ont pas. Ils peuvent automatiquement utiliser une perte appropriée qu'ils calculent en interne. Ils utiliseront cette perte par défaut si vous ne définissez pas un argument de perte dans `compile()`. Notez que pour utiliser la perte interne, vous devrez passer vos labels comme faisant partie de l'entrée, et non pas comme un label séparé, ce qui est la façon normale d'utiliser les labels avec les modèles Keras. Vous verrez des exemples de cela dans la partie 2 du cours, où la définition de la fonction de perte correcte peut être délicate. Pour la classification des séquences, cependant, une fonction de perte standard de Keras fonctionne bien, et c'est donc ce que nous utiliserons ici.
-
-
-
-```py
-from tensorflow.keras.losses import SparseCategoricalCrossentropy
-
-model.compile(
- optimizer="adam",
- loss=SparseCategoricalCrossentropy(from_logits=True),
- metrics=["accuracy"],
-)
-model.fit(
- tf_train_dataset,
- validation_data=tf_validation_dataset,
-)
-```
-
-
-
-Notez un piège très commun ici. Vous *pouvez* simplement passer le nom de la perte comme une chaîne à Keras, mais par défaut Keras supposera que vous avez déjà appliqué une fonction softmax à vos sorties. Cependant, de nombreux modèles produisent les valeurs juste avant l'application de la softmax, que l'on appelle aussi les *logits*. Nous devons indiquer à la fonction de perte que c'est ce que fait notre modèle, et la seule façon de le faire est de l'appeler directement, plutôt que par son nom avec une chaîne.
-
-
-
-
-### Améliorer les performances d'entraînement
-
-
-
-Si vous essayez le code ci-dessus, il fonctionne certainement, mais vous constaterez que la perte ne diminue que lentement ou sporadiquement. La cause principale est le *taux d'apprentissage*. Comme pour la perte, lorsque nous transmettons à Keras le nom d'un optimiseur sous forme de chaîne de caractères, Keras initialise cet optimiseur avec des valeurs par défaut pour tous les paramètres, y compris le taux d'apprentissage. Cependant, nous savons depuis longtemps que les *transformers* bénéficient d'un taux d'apprentissage beaucoup plus faible que celui par défaut d'Adam, qui est de 1e-3, également écrit comme 10 à la puissance -3, ou 0,001. 5e-5 (0,00005), qui est environ vingt fois inférieur, est un bien meilleur point de départ.
-
-En plus de réduire le taux d'apprentissage, nous avons une deuxième astuce dans notre manche : nous pouvons réduire lentement le taux d'apprentissage au cours de l'entraînement. Dans la littérature, on parle parfois de *décroissance* ou d'*annulation* du taux d'apprentissage.le taux d'apprentissage. Dans Keras, la meilleure façon de le faire est d'utiliser un *planificateur du taux d'apprentissage*. Un bon planificateur à utiliser est `PolynomialDecay`. Malgré son nom, avec les paramètres par défaut, il diminue simplement de façon linéaire le taux d'apprentissage de la valeur initiale à la valeur finale au cours de l'entraînement, ce qui est exactement ce que nous voulons. Afin d'utiliser correctement un planificateur, nous devons lui dire combien de temps l'entraînement va durer. Nous calculons cela comme `num_train_steps` ci-dessous.
-
-```py
-from tensorflow.keras.optimizers.schedules import PolynomialDecay
-
-batch_size = 8
-num_epochs = 3
-# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans l'ensemble de données, divisé par la taille du batch puis multiplié
-# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un lot tf.data.Dataset
-# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size.
-num_train_steps = len(tf_train_dataset) * num_epochs
-lr_scheduler = PolynomialDecay(
- initial_learning_rate=5e-5, end_learning_rate=0.0, decay_steps=num_train_steps
-)
-from tensorflow.keras.optimizers import Adam
-
-opt = Adam(learning_rate=lr_scheduler)
-```
-
-
-
-La bibliothèque 🤗 *Transformers* possède également une fonction `create_optimizer()` qui créera un optimiseur `AdamW` avec un taux d'apprentissage décroissant. Il s'agit d'un raccourci pratique que vous verrez en détail dans les prochaines sections du cours.
-
-
-
-Nous avons maintenant notre tout nouvel optimiseur et nous pouvons essayer de nous entraîner avec lui. Tout d'abord, rechargeons le modèle pour réinitialiser les modifications apportées aux poids lors de l'entraînement que nous venons d'effectuer, puis nous pouvons le compiler avec le nouvel optimiseur :
-
-```py
-import tensorflow as tf
-
-model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
-model.compile(optimizer=opt, loss=loss, metrics=["accuracy"])
-```
-
-Maintenant, on *fit* :
-
-```py
-model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3)
-```
-
-
-
-💡 Si vous voulez télécharger automatiquement votre modèle sur le *Hub* pendant l'entraînement, vous pouvez passer un `PushToHubCallback` dans la méthode `model.fit()`. Nous en apprendrons davantage à ce sujet au [Chapitre 4](/course/fr/chapter4/3).
-
-
-
-### Prédictions du modèle
-
-
-
-
-Entraîner et regarder la perte diminuer, c'est très bien, mais que faire si l'on veut réellement obtenir des résultats du modèle entraîné, soit pour calculer des métriques, soit pour utiliser le modèle en production ? Pour ce faire, nous pouvons simplement utiliser la méthode `predict()`. Ceci retournera les *logits* de la tête de sortie du modèle, un par classe.
-
-```py
-preds = model.predict(tf_validation_dataset)["logits"]
-```
-
-Nous pouvons convertir ces logits en prédictions de classe du modèle en utilisant `argmax` pour trouver le logit le plus élevé, qui correspond à la classe la plus probable :
-
-```py
-class_preds = np.argmax(preds, axis=1)
-print(preds.shape, class_preds.shape)
-```
-
-```python out
-(408, 2) (408,)
-```
-
-Maintenant, utilisons ces `preds` pour calculer des métriques ! Nous pouvons charger les métriques associées au jeu de données MRPC aussi facilement que nous avons chargé le jeu de données, cette fois avec la fonction `load_metric()`. L'objet retourné a une méthode `compute()` que nous pouvons utiliser pour faire le calcul de la métrique :
-
-```py
-from datasets import load_metric
-
-metric = load_metric("glue", "mrpc")
-metric.compute(predictions=class_preds, references=raw_datasets["validation"]["label"])
-```
-
-```python out
-{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
-```
-
-Les résultats exacts que vous obtiendrez peuvent varier, car l'initialisation aléatoire de la tête du modèle peut modifier les métriques obtenues. Ici, nous pouvons voir que notre modèle a une précision de 85,78% sur l'ensemble de validation et un score F1 de 89,97. Ce sont les deux métriques utilisées pour évaluer les résultats sur le jeu de données MRPC pour le benchmark GLUE. Le tableau du papier de [BERT](https://arxiv.org/pdf/1810.04805.pdf) indique un score F1 de 88,9 pour le modèle de base. Il s'agissait du modèle `uncased` alors que nous utilisons actuellement le modèle `cased`, ce qui explique le meilleur résultat.
-
-Ceci conclut l'introduction à le *finetuning* en utilisant l'API Keras. Un exemple d'application de cette méthode aux tâches les plus courantes du traitement automatique des langues sera présenté au [Chapitre 7](/course/fr/chapter7). Si vous souhaitez affiner vos connaissances de l'API Keras, essayez *finetuner* un modèle sur le jeu de données GLUE SST-2, en utilisant le traitement des données que vous avez effectué dans la section 2.
+
+
+# Finetuner un modèle avec Keras
+
+
+
+Une fois que vous avez fait tout le travail de prétraitement des données dans la dernière section, il ne vous reste que quelques étapes pour entraîner le modèle. Notez, cependant, que la commande `model.fit()` s'exécutera très lentement sur un CPU. Si vous n'avez pas de GPU, vous pouvez avoir accès à des GPUs ou TPUs gratuits sur [Google Colab](https://colab.research.google.com/).
+
+Les exemples de code ci-dessous supposent que vous avez déjà exécuté les exemples de la section précédente. Voici un bref résumé de ce dont vous avez besoin :
+
+```py
+from datasets import load_dataset
+from transformers import AutoTokenizer, DataCollatorWithPadding
+import numpy as np
+
+raw_datasets = load_dataset("glue", "mrpc")
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+
+
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
+
+tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
+ columns=["attention_mask", "input_ids", "token_type_ids"],
+ label_cols=["labels"],
+ shuffle=True,
+ collate_fn=data_collator,
+ batch_size=8,
+)
+
+tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
+ columns=["attention_mask", "input_ids", "token_type_ids"],
+ label_cols=["labels"],
+ shuffle=False,
+ collate_fn=data_collator,
+ batch_size=8,
+)
+```
+
+### Entraînement
+
+Les modèles TensorFlow importés depuis 🤗 *Transformers* sont déjà des modèles Keras. Voici une courte introduction à Keras.
+
+
+
+Cela signifie qu'une fois que nous disposons de nos données, très peu de travail est nécessaire pour commencer à entraîner sur celles-ci.
+
+
+
+Comme dans le [chapitre précédent](/course/fr/chapter2), nous allons utiliser la classe `TFAutoModelForSequenceClassification`, avec deux étiquettes :
+
+```py
+from transformers import TFAutoModelForSequenceClassification
+
+model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+```
+
+Vous remarquerez que, contrairement au [chapitre 2](/course/fr/chapter2), vous obtenez un message d'avertissement après l'instanciation de ce modèle pré-entraîné. Ceci est dû au fait que BERT n'a pas été pré-entraîné à la classification de paires de phrases, donc la tête du modèle pré-entraîné a été supprimée et une nouvelle tête adaptée à la classification de séquences a été insérée à la place. Les messages d'avertissement indiquent que certains poids n'ont pas été utilisés (ceux correspondant à la tête de pré-entraînement abandonnée) et que d'autres ont été initialisés de manière aléatoire (ceux pour la nouvelle tête). Il conclut en vous encourageant à entraîner le modèle, ce qui est exactement ce que nous allons faire maintenant.
+
+Pour *finetuner* le modèle sur notre jeu de données, nous devons simplement `compiler()` notre modèle et ensuite passer nos données à la méthode `fit()`. Cela va démarrer le processus de *finetuning* (qui devrait prendre quelques minutes sur un GPU) et rapporter la perte d'entraînement au fur et à mesure, plus la perte de validation à la fin de chaque époque.
+
+
+
+Notez que les modèles 🤗 *Transformers* ont une capacité spéciale que la plupart des modèles Keras n'ont pas. Ils peuvent automatiquement utiliser une perte appropriée qu'ils calculent en interne. Ils utiliseront cette perte par défaut si vous ne définissez pas un argument de perte dans `compile()`. Notez que pour utiliser la perte interne, vous devrez passer vos labels comme faisant partie de l'entrée, et non pas comme un label séparé, ce qui est la façon normale d'utiliser les labels avec les modèles Keras. Vous verrez des exemples de cela dans la partie 2 du cours, où la définition de la fonction de perte correcte peut être délicate. Pour la classification des séquences, cependant, une fonction de perte standard de Keras fonctionne bien, et c'est donc ce que nous utiliserons ici.
+
+
+
+```py
+from tensorflow.keras.losses import SparseCategoricalCrossentropy
+
+model.compile(
+ optimizer="adam",
+ loss=SparseCategoricalCrossentropy(from_logits=True),
+ metrics=["accuracy"],
+)
+model.fit(
+ tf_train_dataset,
+ validation_data=tf_validation_dataset,
+)
+```
+
+
+
+Notez un piège très commun ici. Vous *pouvez* simplement passer le nom de la perte comme une chaîne à Keras, mais par défaut Keras supposera que vous avez déjà appliqué une fonction softmax à vos sorties. Cependant, de nombreux modèles produisent les valeurs juste avant l'application de la softmax, que l'on appelle aussi les *logits*. Nous devons indiquer à la fonction de perte que c'est ce que fait notre modèle, et la seule façon de le faire est de l'appeler directement, plutôt que par son nom avec une chaîne.
+
+
+
+
+### Améliorer les performances d'entraînement
+
+
+
+Si vous essayez le code ci-dessus, il fonctionne certainement, mais vous constaterez que la perte ne diminue que lentement ou sporadiquement. La cause principale est le *taux d'apprentissage*. Comme pour la perte, lorsque nous transmettons à Keras le nom d'un optimiseur sous forme de chaîne de caractères, Keras initialise cet optimiseur avec des valeurs par défaut pour tous les paramètres, y compris le taux d'apprentissage. Cependant, nous savons depuis longtemps que les *transformers* bénéficient d'un taux d'apprentissage beaucoup plus faible que celui par défaut d'Adam, qui est de 1e-3, également écrit comme 10 à la puissance -3, ou 0,001. 5e-5 (0,00005), qui est environ vingt fois inférieur, est un bien meilleur point de départ.
+
+En plus de réduire le taux d'apprentissage, nous avons une deuxième astuce dans notre manche : nous pouvons réduire lentement le taux d'apprentissage au cours de l'entraînement. Dans la littérature, on parle parfois de *décroissance* ou d'*annulation* du taux d'apprentissage.le taux d'apprentissage. Dans Keras, la meilleure façon de le faire est d'utiliser un *planificateur du taux d'apprentissage*. Un bon planificateur à utiliser est `PolynomialDecay`. Malgré son nom, avec les paramètres par défaut, il diminue simplement de façon linéaire le taux d'apprentissage de la valeur initiale à la valeur finale au cours de l'entraînement, ce qui est exactement ce que nous voulons. Afin d'utiliser correctement un planificateur, nous devons lui dire combien de temps l'entraînement va durer. Nous calculons cela comme `num_train_steps` ci-dessous.
+
+```py
+from tensorflow.keras.optimizers.schedules import PolynomialDecay
+
+batch_size = 8
+num_epochs = 3
+# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans l'ensemble de données, divisé par la taille du batch puis multiplié
+# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un lot tf.data.Dataset
+# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size.
+num_train_steps = len(tf_train_dataset) * num_epochs
+lr_scheduler = PolynomialDecay(
+ initial_learning_rate=5e-5, end_learning_rate=0.0, decay_steps=num_train_steps
+)
+from tensorflow.keras.optimizers import Adam
+
+opt = Adam(learning_rate=lr_scheduler)
+```
+
+
+
+La bibliothèque 🤗 *Transformers* possède également une fonction `create_optimizer()` qui créera un optimiseur `AdamW` avec un taux d'apprentissage décroissant. Il s'agit d'un raccourci pratique que vous verrez en détail dans les prochaines sections du cours.
+
+
+
+Nous avons maintenant notre tout nouvel optimiseur et nous pouvons essayer de nous entraîner avec lui. Tout d'abord, rechargeons le modèle pour réinitialiser les modifications apportées aux poids lors de l'entraînement que nous venons d'effectuer, puis nous pouvons le compiler avec le nouvel optimiseur :
+
+```py
+import tensorflow as tf
+
+model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
+model.compile(optimizer=opt, loss=loss, metrics=["accuracy"])
+```
+
+Maintenant, on *fit* :
+
+```py
+model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3)
+```
+
+
+
+💡 Si vous voulez télécharger automatiquement votre modèle sur le *Hub* pendant l'entraînement, vous pouvez passer un `PushToHubCallback` dans la méthode `model.fit()`. Nous en apprendrons davantage à ce sujet au [chapitre 4](/course/fr/chapter4/3).
+
+
+
+### Prédictions du modèle
+
+
+
+
+Entraîner et regarder la perte diminuer, c'est très bien, mais que faire si l'on veut réellement obtenir des résultats du modèle entraîné, soit pour calculer des métriques, soit pour utiliser le modèle en production ? Pour ce faire, nous pouvons simplement utiliser la méthode `predict()`. Ceci retournera les *logits* de la tête de sortie du modèle, un par classe.
+
+```py
+preds = model.predict(tf_validation_dataset)["logits"]
+```
+
+Nous pouvons convertir ces logits en prédictions de classe du modèle en utilisant `argmax` pour trouver le logit le plus élevé, qui correspond à la classe la plus probable :
+
+```py
+class_preds = np.argmax(preds, axis=1)
+print(preds.shape, class_preds.shape)
+```
+
+```python out
+(408, 2) (408,)
+```
+
+Maintenant, utilisons ces `preds` pour calculer des métriques ! Nous pouvons charger les métriques associées au jeu de données MRPC aussi facilement que nous avons chargé le jeu de données, cette fois avec la fonction `load_metric()`. L'objet retourné a une méthode `compute()` que nous pouvons utiliser pour faire le calcul de la métrique :
+
+```py
+from datasets import load_metric
+
+metric = load_metric("glue", "mrpc")
+metric.compute(predictions=class_preds, references=raw_datasets["validation"]["label"])
+```
+
+```python out
+{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
+```
+
+Les résultats exacts que vous obtiendrez peuvent varier, car l'initialisation aléatoire de la tête du modèle peut modifier les métriques obtenues. Ici, nous pouvons voir que notre modèle a une précision de 85,78% sur l'ensemble de validation et un score F1 de 89,97. Ce sont les deux métriques utilisées pour évaluer les résultats sur le jeu de données MRPC pour le benchmark GLUE. Le tableau du papier de [BERT](https://arxiv.org/pdf/1810.04805.pdf) indique un score F1 de 88,9 pour le modèle de base. Il s'agissait du modèle `uncased` alors que nous utilisons actuellement le modèle `cased`, ce qui explique le meilleur résultat.
+
+Ceci conclut l'introduction à le *finetuning* en utilisant l'API Keras. Un exemple d'application de cette méthode aux tâches les plus courantes du traitement automatique des langues sera présenté au [chapitre 7](/course/fr/chapter7). Si vous souhaitez affiner vos connaissances de l'API Keras, essayez *finetuner* un modèle sur le jeu de données GLUE SST-2, en utilisant le traitement des données que vous avez effectué dans la section 2.
diff --git a/chapters/fr/chapter3/4.mdx b/chapters/fr/chapter3/4.mdx
index a56a8bf9e..8279747f4 100644
--- a/chapters/fr/chapter3/4.mdx
+++ b/chapters/fr/chapter3/4.mdx
@@ -292,7 +292,7 @@ La première ligne à ajouter est la ligne d'importation. La deuxième ligne ins
Ensuite, le gros du travail est fait dans la ligne qui envoie les *dataloaders*, le modèle, et l'optimiseur à `accelerator.prepare()`. Cela va envelopper ces objets dans le conteneur approprié pour s'assurer que votre entraînement distribué fonctionne comme prévu. Les changements restants à faire sont la suppression de la ligne qui met le batch sur le `device` (encore une fois, si vous voulez le garder, vous pouvez juste le changer pour utiliser `accelerator.device`) et le remplacement de `loss.backward()` par `accelerator.backward(loss)`.
-⚠️ Afin de bénéficier de la rapidité offerte par les TPUs du Cloud, nous vous recommandons de rembourrer vos échantillons à une longueur fixe avec les arguments `padding="max_length"` et `max_length` du *tokenizer*.
+⚠️ Afin de bénéficier de la rapidité offerte par les TPUs du Cloud, nous vous recommandons de rembourrer vos échantillons à une longueur fixe avec les arguments `padding="max_length"` et `max_length` du tokenizer.
Si vous souhaitez faire un copier-coller pour jouer, voici à quoi ressemble la boucle d'entraînement complète avec 🤗 *Accelerate* :
diff --git a/chapters/fr/chapter3/5.mdx b/chapters/fr/chapter3/5.mdx
index 767ec59aa..db244e545 100644
--- a/chapters/fr/chapter3/5.mdx
+++ b/chapters/fr/chapter3/5.mdx
@@ -1,6 +1,6 @@
-# *Finetuning*, coché !
+# Finetuning, coché !
C'était amusant ! Dans les deux premiers chapitres, vous avez appris à connaître les modèles et les *tokenizers*, et vous savez maintenant comment les *finetuner* pour vos propres données. Pour récapituler, dans ce chapitre vous :
diff --git a/chapters/fr/chapter3/6.mdx b/chapters/fr/chapter3/6.mdx
index edafb9c0a..44c0fdf44 100644
--- a/chapters/fr/chapter3/6.mdx
+++ b/chapters/fr/chapter3/6.mdx
@@ -20,7 +20,7 @@ Testez ce que vous avez appris dans ce chapitre !
},
{
text: "Confusion",
- explain: "Correct ! La confusion n'est pas l'une des six émotions de base.",
+ explain: "La confusion n'est pas l'une des six émotions de base.",
correct: true
},
{
@@ -111,12 +111,12 @@ Testez ce que vous avez appris dans ce chapitre !
},
{
text: "C'est lorsque vous remplissez vos entrées lorsque le batch est créé, à la longueur maximale des phrases à l'intérieur de ce batch.",
- explain: "C'est exact ! La partie dynamique vient du fait que la taille de chaque batch est déterminée au moment de la création, et que tous vos batchs peuvent avoir des formes différentes.",
+ explain: "La partie dynamique vient du fait que la taille de chaque batch est déterminée au moment de la création, et que tous vos batchs peuvent avoir des formes différentes.",
correct: true
},
{
text: "C'est lorsque vous remplissez vos entrées de sorte que chaque phrase ait le même nombre de tokens que la précédente dans le jeu de données.",
- explain: "C'est incorrect, et cela n'a pas de sens de regarder l'ordre dans le jeu de données puisque nous le mélangeons pendant l'entraînement."
+ explain: "Cela n'a pas de sens de regarder l'ordre dans le jeu de données puisque nous le mélangeons pendant l'entraînement."
},
]}
/>
@@ -131,7 +131,7 @@ Testez ce que vous avez appris dans ce chapitre !
},
{
text: "Elle rassemble tous les échantillons dans un batch.",
- explain: "Correct ! Vous pouvez passer la fonction de rassemblement comme argument d'une fonction DataLoader
. Nous avons utilisé la fonction DataCollatorWithPadding
qui remplit tous les éléments d'un batch pour qu'ils aient la même longueur.",
+ explain: "Vous pouvez passer la fonction de rassemblement comme argument d'une fonction DataLoader
. Nous avons utilisé la fonction DataCollatorWithPadding
qui remplit tous les éléments d'un batch pour qu'ils aient la même longueur.",
correct: true
},
{
@@ -155,7 +155,7 @@ Testez ce que vous avez appris dans ce chapitre !
},
{
text: "La tête du modèle pré-entraîné est supprimée et une nouvelle tête adaptée à la tâche est insérée à la place.",
- explain: "Correct. Par exemple, lorsque nous avons utilisé l'AutoModelForSequenceClassification
avec bert-base-uncased
, nous avons eu des messages d'avertissement lors de l'instanciation du modèle. La tête pré-entraînée n'est pas utilisée pour la tâche de classification de séquences, elle est donc supprimée et une nouvelle tête est instanciée avec des poids aléatoires..",
+ explain: "Par exemple, lorsque nous avons utilisé l'AutoModelForSequenceClassification
avec bert-base-uncased
, nous avons eu des messages d'avertissement lors de l'instanciation du modèle. La tête pré-entraînée n'est pas utilisée pour la tâche de classification de séquences, elle est donc supprimée et une nouvelle tête est instanciée avec des poids aléatoires..",
correct: true
},
{
@@ -175,7 +175,7 @@ Testez ce que vous avez appris dans ce chapitre !
choices={[
{
text: "Contenir tous les hyperparamètres utilisés pour l'entraînement et l'évaluation avec le Trainer
.",
- explain: "Correct !",
+ explain: "",
correct: true
},
{
@@ -207,7 +207,7 @@ Testez ce que vous avez appris dans ce chapitre !
},
{
text: "Elle permet à nos boucles d'entraînement de fonctionner avec des stratégies distribuées.",
- explain: "Correct ! Avec 🤗 Accelerate, vos boucles d'entraînement fonctionneront pour plusieurs GPUs et TPUs.",
+ explain: "Avec 🤗 Accelerate, vos boucles d'entraînement fonctionneront pour plusieurs GPUs et TPUs.",
correct: true
},
{
@@ -228,7 +228,7 @@ Testez ce que vous avez appris dans ce chapitre !
},
{
text: "La tête du modèle pré-entraîné est supprimée et une nouvelle tête adaptée à la tâche est insérée à la place.",
- explain: "Correct. Par exemple, lorsque nous avons utilisé TFAutoModelForSequenceClassification
avec bert-base-uncased
, nous avons eu des messages d'avertissement lors de l'instanciation du modèle. La tête pré-entraînée n'est pas utilisée pour la tâche de classification de séquences, elle est donc supprimée et une nouvelle tête est instanciée avec des poids aléatoires..",
+ explain: "Par exemple, lorsque nous avons utilisé TFAutoModelForSequenceClassification
avec bert-base-uncased
, nous avons eu des messages d'avertissement lors de l'instanciation du modèle. La tête pré-entraînée n'est pas utilisée pour la tâche de classification de séquences, elle est donc supprimée et une nouvelle tête est instanciée avec des poids aléatoires..",
correct: true
},
{
@@ -252,12 +252,12 @@ Testez ce que vous avez appris dans ce chapitre !
},
{
text: "Vous pouvez tirer parti des méthodes existantes telles que compile()
, fit()
et predict()
.",
- explain: "Correct ! Une fois que vous disposez des données, l'entraînement sur celles-ci ne demande que très peu de travail.",
+ explain: "Une fois que vous disposez des données, l'entraînement sur celles-ci ne demande que très peu de travail.",
correct: true
},
{
text: "Vous apprendrez à connaître Keras ainsi que transformers.",
- explain: "Correct, mais nous cherchons quelque chose d'autre :)",
+ explain: "Mais nous cherchons quelque chose d'autre :)",
correct: true
},
{
@@ -282,7 +282,7 @@ Testez ce que vous avez appris dans ce chapitre !
},
{
text: "En utilisant un callable avec la signature metric_fn(y_true, y_pred)
.",
- explain: "Correct !",
+ explain: " ",
correct: true
},
{
diff --git a/chapters/fr/chapter4/1.mdx b/chapters/fr/chapter4/1.mdx
index b9c2057a4..7a0420461 100644
--- a/chapters/fr/chapter4/1.mdx
+++ b/chapters/fr/chapter4/1.mdx
@@ -1,17 +1,17 @@
-# Le *Hub* d'Hugging Face
-
-Le [*Hub* d'Hugging Face](https://huggingface.co/), notre site internet principal, est une plateforme centrale qui permet à quiconque de découvrir, d'utiliser et de contribuer à de nouveaux modèles et jeux de données de pointe. Il héberge une grande variété de modèles, dont plus de 10 000 sont accessibles au public. Nous nous concentrerons sur les modèles dans ce chapitre, et nous examinerons les jeux de données au chapitre 5.
-
-Les modèles présents dans le *Hub* ne sont pas limités à 🤗 *Transformers* ou même au NLP. Il existe des modèles de [Flair](https://github.com/flairNLP/flair) et [AllenNLP](https://github.com/allenai/allennlp) pour le NLP, [Asteroid](https://github.com/asteroid-team/asteroid) et [pyannote](https://github.com/pyannote/pyannote-audio) pour l'audio, et [timm](https://github.com/rwightman/pytorch-image-models) pour la vision, pour n'en citer que quelques-uns.
-
-Chacun de ces modèles est hébergé sous forme de dépôt Git, ce qui permet le suivi des versions et la reproductibilité. Partager un modèle sur le *Hub*, c'est l'ouvrir à la communauté et le rendre accessible à tous ceux qui souhaitent l'utiliser facilement, ce qui leur évite d'avoir à entraîner eux-mêmes un modèle et simplifie le partage et l'utilisation.
-
-En outre, le partage d'un modèle sur le *Hub* déploie automatiquement une API d'inférence hébergée pour ce modèle. Toute personne de la communauté est libre de la tester directement sur la page du modèle, avec des entrées personnalisées et des *widgets* appropriés.
-
-La meilleure partie est que le partage ainsi que l'utilisation de n'importe quel modèle public sur le *Hub* sont totalement gratuits ! [Des plans payants](https://huggingface.co/pricing) existent également si vous souhaitez partager des modèles en privé.
-
-La vidéo ci-dessous montre comment naviguer sur le *Hub* :
-
-
-
-Avoir un compte huggingface.co est nécessaire pour suivre cette partie car nous allons créer et gérer des répertoires sur le *Hub* : [créer un compte](https://huggingface.co/join)
+# Le Hub d'Hugging Face
+
+Le [*Hub* d'Hugging Face](https://huggingface.co/), notre site internet principal, est une plateforme centrale qui permet à quiconque de découvrir, d'utiliser et de contribuer à de nouveaux modèles et jeux de données de pointe. Il héberge une grande variété de modèles, dont plus de 10 000 sont accessibles au public. Nous nous concentrerons sur les modèles dans ce chapitre, et nous examinerons les jeux de données au chapitre 5.
+
+Les modèles présents dans le *Hub* ne sont pas limités à 🤗 *Transformers* ou même au NLP. Il existe des modèles de [Flair](https://github.com/flairNLP/flair) et [AllenNLP](https://github.com/allenai/allennlp) pour le NLP, [Asteroid](https://github.com/asteroid-team/asteroid) et [pyannote](https://github.com/pyannote/pyannote-audio) pour l'audio, et [timm](https://github.com/rwightman/pytorch-image-models) pour la vision, pour n'en citer que quelques-uns.
+
+Chacun de ces modèles est hébergé sous forme de dépôt Git, ce qui permet le suivi des versions et la reproductibilité. Partager un modèle sur le *Hub*, c'est l'ouvrir à la communauté et le rendre accessible à tous ceux qui souhaitent l'utiliser facilement, ce qui leur évite d'avoir à entraîner eux-mêmes un modèle et simplifie le partage et l'utilisation.
+
+En outre, le partage d'un modèle sur le *Hub* déploie automatiquement une API d'inférence hébergée pour ce modèle. Toute personne de la communauté est libre de la tester directement sur la page du modèle, avec des entrées personnalisées et des *widgets* appropriés.
+
+La meilleure partie est que le partage ainsi que l'utilisation de n'importe quel modèle public sur le *Hub* sont totalement gratuits ! [Des plans payants](https://huggingface.co/pricing) existent également si vous souhaitez partager des modèles en privé.
+
+La vidéo ci-dessous montre comment naviguer sur le *Hub* :
+
+
+
+Avoir un compte huggingface.co est nécessaire pour suivre cette partie car nous allons créer et gérer des répertoires sur le *Hub* : [créer un compte](https://huggingface.co/join)
\ No newline at end of file
diff --git a/chapters/fr/chapter4/2.mdx b/chapters/fr/chapter4/2.mdx
index b6cc65512..86579685f 100644
--- a/chapters/fr/chapter4/2.mdx
+++ b/chapters/fr/chapter4/2.mdx
@@ -1,97 +1,97 @@
-
-
-# Utilisation de modèles pré-entraînés
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Le *Hub* rend simple la sélection d'un modèle et permet alors que celui-ci puisse être utilisé dans toute bibliothèque en aval en seulement quelques lignes de code. Voyons comment utiliser concrètement l'un de ces modèles et comment contribuer au développement de la communauté.
-
-Supposons que nous recherchions un modèle basé sur le français, capable de remplir des masques.
-
-
-
-
-
-Nous choisissons le *checkpoint* `camembert-base` pour essayer. L'identifiant `camembert-base` est tout ce dont nous avons besoin pour commencer à utiliser le modèle ! Comme vous l'avez vu dans les chapitres précédents, nous pouvons l'instancier en utilisant la fonction `pipeline()` :
-
-```py
-from transformers import pipeline
-
-camembert_fill_mask = pipeline("fill-mask", model="camembert-base")
-results = camembert_fill_mask("Le camembert est :)")
-```
-
-```python out
-[
- {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'},
- {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'},
- {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'},
- {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'},
- {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'}
-]
-```
-
-Comme vous pouvez le constater, le chargement d'un modèle dans un pipeline est extrêmement simple. La seule chose à laquelle vous devez faire attention est que le *checkpoint* choisi soit adapté à la tâche pour laquelle il va être utilisé. Par exemple, ici nous chargeons le *checkpoint* `camembert-base` dans le pipeline `fill-mask`, ce qui est tout à fait correct. Mais si nous chargerions ce *checkpoint* dans le pipeline `text-classification`, les résultats n'auraient aucun sens car la tête de `camembert-base` n'est pas adaptée à cette tâche ! Nous recommandons d'utiliser le sélecteur de tâche dans l'interface du *Hub* afin de sélectionner les *checkpoints* appropriés :
-
-
-
-
-
-Vous pouvez également instancier le *checkpoint* en utilisant directement l'architecture du modèle :
-
-{#if fw === 'pt'}
-```py
-from transformers import CamembertTokenizer, CamembertForMaskedLM
-
-tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
-model = CamembertForMaskedLM.from_pretrained("camembert-base")
-```
-
-Cependant, nous recommandons d'utiliser les classes [`Auto*`](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes) à la place, car elles sont par conception indépendantes de l'architecture. Alors que l'exemple de code précédent limite les utilisateurs aux *checkpoints* chargeables dans l'architecture CamemBERT, l'utilisation des classes `Auto*` facilite le changement de *checkpoint* :
-
-```py
-from transformers import AutoTokenizer, AutoModelForMaskedLM
-
-tokenizer = AutoTokenizer.from_pretrained("camembert-base")
-model = AutoModelForMaskedLM.from_pretrained("camembert-base")
-```
-{:else}
-```py
-from transformers import CamembertTokenizer, TFCamembertForMaskedLM
-
-tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
-model = TFCamembertForMaskedLM.from_pretrained("camembert-base")
-```
-
-Cependant, nous recommandons d'utiliser les classes [`TFAuto*`](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes) à la place, car elles sont par conception indépendantes de l'architecture. Alors que l'exemple de code précédent limite les utilisateurs aux *checkpoints* chargeables dans l'architecture CamemBERT, l'utilisation des classes `TFAuto*` facilite le changement de *checkpoint* :
-
-
-```py
-from transformers import AutoTokenizer, TFAutoModelForMaskedLM
-
-tokenizer = AutoTokenizer.from_pretrained("camembert-base")
-model = TFAutoModelForMaskedLM.from_pretrained("camembert-base")
-```
-{/if}
-
-
-Lorsque vous utilisez un modèle pré-entraîné, assurez-vous de vérifier comment il a été entraîné, sur quels jeux de données, ses limites et ses biais. Toutes ces informations doivent être indiquées dans sa carte.
-
+
+
+# Utilisation de modèles pré-entraînés
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Le *Hub* rend simple la sélection d'un modèle et permet alors que celui-ci puisse être utilisé dans toute bibliothèque en aval en seulement quelques lignes de code. Voyons comment utiliser concrètement l'un de ces modèles et comment contribuer au développement de la communauté.
+
+Supposons que nous recherchions un modèle basé sur le français, capable de remplir des masques.
+
+
+
+
+
+Nous choisissons le *checkpoint* `camembert-base` pour essayer. L'identifiant `camembert-base` est tout ce dont nous avons besoin pour commencer à utiliser le modèle ! Comme vous l'avez vu dans les chapitres précédents, nous pouvons l'instancier en utilisant la fonction `pipeline()` :
+
+```py
+from transformers import pipeline
+
+camembert_fill_mask = pipeline("fill-mask", model="camembert-base")
+results = camembert_fill_mask("Le camembert est :)")
+```
+
+```python out
+[
+ {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'},
+ {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'},
+ {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'},
+ {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'},
+ {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'}
+]
+```
+
+Comme vous pouvez le constater, le chargement d'un modèle dans un pipeline est extrêmement simple. La seule chose à laquelle vous devez faire attention est que le *checkpoint* choisi soit adapté à la tâche pour laquelle il va être utilisé. Par exemple, ici nous chargeons le *checkpoint* `camembert-base` dans le pipeline `fill-mask`, ce qui est tout à fait correct. Mais si nous chargerions ce *checkpoint* dans le pipeline `text-classification`, les résultats n'auraient aucun sens car la tête de `camembert-base` n'est pas adaptée à cette tâche ! Nous recommandons d'utiliser le sélecteur de tâche dans l'interface du *Hub* afin de sélectionner les *checkpoints* appropriés :
+
+
+
+
+
+Vous pouvez également instancier le *checkpoint* en utilisant directement l'architecture du modèle :
+
+{#if fw === 'pt'}
+```py
+from transformers import CamembertTokenizer, CamembertForMaskedLM
+
+tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
+model = CamembertForMaskedLM.from_pretrained("camembert-base")
+```
+
+Cependant, nous recommandons d'utiliser les classes [`Auto*`](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes) à la place, car elles sont par conception indépendantes de l'architecture. Alors que l'exemple de code précédent limite les utilisateurs aux *checkpoints* chargeables dans l'architecture CamemBERT, l'utilisation des classes `Auto*` facilite le changement de *checkpoint* :
+
+```py
+from transformers import AutoTokenizer, AutoModelForMaskedLM
+
+tokenizer = AutoTokenizer.from_pretrained("camembert-base")
+model = AutoModelForMaskedLM.from_pretrained("camembert-base")
+```
+{:else}
+```py
+from transformers import CamembertTokenizer, TFCamembertForMaskedLM
+
+tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
+model = TFCamembertForMaskedLM.from_pretrained("camembert-base")
+```
+
+Cependant, nous recommandons d'utiliser les classes [`TFAuto*`](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes) à la place, car elles sont par conception indépendantes de l'architecture. Alors que l'exemple de code précédent limite les utilisateurs aux *checkpoints* chargeables dans l'architecture CamemBERT, l'utilisation des classes `TFAuto*` facilite le changement de *checkpoint* :
+
+
+```py
+from transformers import AutoTokenizer, TFAutoModelForMaskedLM
+
+tokenizer = AutoTokenizer.from_pretrained("camembert-base")
+model = TFAutoModelForMaskedLM.from_pretrained("camembert-base")
+```
+{/if}
+
+
+Lorsque vous utilisez un modèle pré-entraîné, assurez-vous de vérifier comment il a été entraîné, sur quels jeux de données, ses limites et ses biais. Toutes ces informations doivent être indiquées dans sa carte.
+
diff --git a/chapters/fr/chapter4/3.mdx b/chapters/fr/chapter4/3.mdx
index cd60c7b66..fabdd4a36 100644
--- a/chapters/fr/chapter4/3.mdx
+++ b/chapters/fr/chapter4/3.mdx
@@ -197,17 +197,17 @@ Le *package* `huggingface_hub` offre plusieurs méthodes et classes qui sont uti
```python no-format
from huggingface_hub import (
- # User management
+ # Gestion des utilisateurs
login,
logout,
whoami,
- # Repository creation and management
+ # Création et gestion du dépôt
create_repo,
delete_repo,
update_repo_visibility,
- # And some methods to retrieve/change information about the content
+ # Et quelques méthodes pour récupérer/changer des informations sur le contenu
list_models,
list_datasets,
list_metrics,
@@ -274,7 +274,7 @@ C'est là que votre modèle sera hébergé. Pour commencer à le remplir, vous p
Le fichier README est en Markdown. N'hésitez pas à vous lâcher avec lui ! La troisième partie de ce chapitre est consacrée à la construction d'une carte de modèle. Celles-ci sont d'une importance capitale pour valoriser votre modèle, car c'est par elles que vous indiquez aux autres ce qu'il peut faire.
-Si vous regardez l'onglet « Fichiers et versions », vous verrez qu'il n'y a pas encore beaucoup de fichiers : juste le *README.md* que vous venez de créer et le fichier *.gitattributes* qui garde la trace des gros fichiers.
+Si vous regardez l'onglet « *Files and versions* », vous verrez qu'il n'y a pas encore beaucoup de fichiers : juste le *README.md* que vous venez de créer et le fichier *.gitattributes* qui garde la trace des gros fichiers.
diff --git a/chapters/fr/chapter4/4.mdx b/chapters/fr/chapter4/4.mdx
index 745d846ba..4f2086aea 100644
--- a/chapters/fr/chapter4/4.mdx
+++ b/chapters/fr/chapter4/4.mdx
@@ -6,7 +6,7 @@ Documenter le processus d'entraînement et d'évaluation aide les autres à comp
Par conséquent, la création d'une carte de modèle définissant clairement votre modèle est une étape très importante. Nous vous donnons ici quelques conseils qui vous aideront à le faire. La création de la fiche de modèle se fait par le biais du fichier *README.md* que vous avez vu précédemment, qui est un fichier Markdown.
-Le concept de carte de modèle provient d'une direction de recherche de Google, partagée pour la première fois dans l'article ["*Model Cards for Model Reporting*"](https://arxiv.org/abs/1810.03993) par Margaret Mitchell et al. De nombreuses informations contenues dans ce document sont basées sur cet article et nous vous recommandons d'y jeter un coup d'œil pour comprendre pourquoi les cartes de modèles sont si importantes dans un monde qui valorise la reproductibilité, la réutilisation et l'équité.
+Le concept de carte de modèle provient d'une direction de recherche de Google, partagée pour la première fois dans l'article [« *Model Cards for Model Reporting* »](https://arxiv.org/abs/1810.03993) par Margaret Mitchell et al. De nombreuses informations contenues dans ce document sont basées sur cet article et nous vous recommandons d'y jeter un coup d'œil pour comprendre pourquoi les cartes de modèles sont si importantes dans un monde qui valorise la reproductibilité, la réutilisation et l'équité.
La carte de modèle commence généralement par une très brève présentation de haut niveau de l'objet du modèle, suivie de détails supplémentaires dans les sections suivantes :
@@ -81,4 +81,4 @@ datasets:
Ces métadonnées sont analysées par le *Hub* qui identifie alors ce modèle comme étant un modèle français, avec une licence MIT, entraîné sur le jeu de données Oscar.
-La [spécification complète de la carte du modèle](https://github.com/huggingface/hub-docs/blame/main/modelcard.md) permet de spécifier les langues, les licences, les balises, les jeux de données, les mesures, ainsi que les résultats d'évaluation obtenus par le modèle lors de l'entraînement.
\ No newline at end of file
+La [spécification complète de la carte du modèle](https://github.com/huggingface/hub-docs/blame/main/modelcard.md) permet de spécifier les langues, les licences, les balises, les jeux de données, les mesures, ainsi que les résultats d'évaluation obtenus par le modèle lors de l'entraînement.
diff --git a/chapters/fr/chapter4/5.mdx b/chapters/fr/chapter4/5.mdx
index 366b68a81..4365a6733 100644
--- a/chapters/fr/chapter4/5.mdx
+++ b/chapters/fr/chapter4/5.mdx
@@ -1,7 +1,7 @@
-# Fin de la première partie du cours !
-
-C'est la fin de la première partie du cours ! La partie 2 sera publiée le 15 novembre 2021 avec un grand événement communautaire, pour plus d'informations voir [ici](https://huggingface.co/blog/course-launch-event).
-
-Vous devriez maintenant être capable de *finetuner* un modèle pré-entraîné sur un problème de classification de texte (phrases simples ou paires de phrases) et de télécharger le résultat sur le *Hub*. Pour vous assurer que vous maîtrisez cette première section, vous devriez refaire ça sur un problème qui vous intéresse (et pas nécessairement en anglais si vous parlez une autre langue) ! Vous pouvez trouver de l'aide dans les [forums Hugging Face](https://discuss.huggingface.co/) et partager votre projet dans [ce sujet](https://discuss.huggingface.co/t/share-your-projects/6803) une fois que vous avez terminé.
-
-Nous sommes impatients de voir ce que vous allez construire avec cet outil !
+# Fin de la première partie du cours !
+
+C'est la fin de la première partie du cours ! La partie 2 sera publiée le 15 novembre 2021 avec un grand événement communautaire, pour plus d'informations voir [ici](https://huggingface.co/blog/course-launch-event).
+
+Vous devriez maintenant être capable de *finetuner* un modèle pré-entraîné sur un problème de classification de texte (phrases simples ou paires de phrases) et de télécharger le résultat sur le *Hub*. Pour vous assurer que vous maîtrisez cette première section, vous devriez refaire ça sur un problème qui vous intéresse (et pas nécessairement en anglais si vous parlez une autre langue) ! Vous pouvez trouver de l'aide dans les [forums d'Hugging Face](https://discuss.huggingface.co/) et partager votre projet dans [ce sujet](https://discuss.huggingface.co/t/share-your-projects/6803) une fois que vous avez terminé.
+
+Nous sommes impatients de voir ce que vous allez construire avec cet outil !
\ No newline at end of file
diff --git a/chapters/fr/chapter4/6.mdx b/chapters/fr/chapter4/6.mdx
index 375cb8655..a180d67fa 100644
--- a/chapters/fr/chapter4/6.mdx
+++ b/chapters/fr/chapter4/6.mdx
@@ -1,223 +1,223 @@
-
-
-
-
-# Quiz de fin de chapitre
-
-Testons ce que vous avez appris dans ce chapitre !
-
-### 1. A quoi sont limités les modèles du *Hub* ?
-
-
Transformers.",
- explain: "Si les modèles de la bibliothèque 🤗 Transformers sont pris en charge sur le Hub, ils ne sont pas les seuls !"
- },
- {
- text: "Tous les modèles avec une interface similaire à 🤗 Transformers.",
- explain: "Aucune exigence d'interface n'est fixée lors du téléchargement de modèles vers le Hub."
- },
- {
- text: "Il n'y a pas de limites.",
- explain: "C'est vrai ! Il n'y a pas de limites au téléchargement de modèles sur le Hub.",
- correct: true
- },
- {
- text: "Des modèles qui sont d'une certaine manière liés au NLP.",
- explain: "Aucune exigence n'est fixée concernant le domaine d'application !"
- }
- ]}
-/>
-
-### 2. Comment pouvez-vous gérer les modèles sur le *Hub* ?
-
-Hub sont de simples dépôts Git exploitant git-lfs
pour les fichiers volumineux.",
- correct: true
- }
- ]}
-/>
-
-### 3. Que pouvez-vous faire en utilisant l'interface web du *Hub* ?
-
-Forker » un dépôt existant.",
- explain: "« Forker » un dépôt n'est pas possible sur le Hub."
- },
- {
- text: "Créer un nouveau dépôt de modèles.",
- explain: "Correct ! Ce n'est pas tout ce que vous pouvez faire, cependant.",
- correct: true
- },
- {
- text: "Gérer et modifier des fichiers.",
- explain: "Correct ! Ce n'est pas la seule bonne réponse, cependant.",
- correct: true
- },
- {
- text: "Télécharger des fichiers.",
- explain: "C'est vrai ! Mais ce n'est pas tout.",
- correct: true
- },
- {
- text: "Voir les différences entre les versions.",
- explain: "Correct ! Ce n'est pas tout ce que vous pouvez faire.",
- correct: true
- }
- ]}
-/>
-
-### 4. Qu'est-ce qu'une carte de modèle ?
-
-tokenizer.",
- explain: "It is indeed a description of the model, but it's an important piece: if it's incomplete or absent the model's utility is drastically reduced."
- },
- {
- text: "A way to ensure reproducibility, reusability, and fairness.",
- explain: "Correct! Sharing the right information in the model card will help users leverage your model and be aware of its limits and biases. ",
- correct: true
- },
- {
- text: "A Python file that can be run to retrieve information about the model.",
- explain: "Model cards are simple Markdown files."
- }
- ]}
-/>
-
-### 5. Lesquels de ces objets de la bibliothèque 🤗 *Transformers* peuvent être directement partagés sur le Hub avec `push_to_hub()` ?
-
-{#if fw === 'pt'}
-tokenizer",
- explain: "Correct ! Tous les tokenizers ont la méthode push_to_hub
et l'utiliser poussera tous les fichiers du tokenizer (vocabulaire, architecture du tokenizer, etc.) vers un dépôt donné. Ce n'est pas la seule bonne réponse, cependant !",
- correct: true
- },
- {
- text: "Une configuration de modèle",
- explain: "C'est vrai ! Toutes les configurations de modèles ont la méthode push_to_hub
et son utilisation les poussera vers un dépôt donné. Que pouvez-vous partager d'autre ?",
- correct: true
- },
- {
- text: "Un modèle",
- explain: "Correct ! Tous les modèles ont la méthode push_to_hub
qui le pushra ainsi que leurs fichiers de configuration, vers un dépôt donné. Ce n'est pas tout ce que vous pouvez partager, cependant.",
- correct: true
- },
- {
- text: "Trainer",
- explain: "C'est exact. Le Trainer
implémente aussi la méthode push_to_hub
. L'utiliser téléchargera le modèle, sa configuration, le tokenizer et une ébauche de carte de modèle vers un dépôt donné. Essayez une autre réponse !",
- correct: true
- }
- ]}
-/>
-{:else}
-tokenizer",
- explain: "C'est vrai ! Toutes les configurations de modèles ont la méthode push_to_hub
et son utilisation les poussera vers un dépôt donné. Que pouvez-vous partager d'autre ?",
- correct: true
- },
- {
- text: "Une configuration de modèle",
- explain: "C'est vrai ! Toutes les configurations de modèles ont la méthode push_to_hub
et son utilisation les poussera vers un dépôt donné. Que pouvez-vous partager d'autre ?",
- correct: true
- },
- {
- text: "Un modèle",
- explain: "Correct ! Tous les modèles ont la méthode push_to_hub
qui le pushra ainsi que leurs fichiers de configuration, vers un dépôt donné. Ce n'est pas tout ce que vous pouvez partager, cependant.",
- correct: true
- },
- {
- text: "Tout ce qui précède avec un callback dédié",
- explain: "C'est exact. Le PushToHubCallback
enverra régulièrement tous ces objets à un dépôt pendant l'entraînement.",
- correct: true
- }
- ]}
-/>
-{/if}
-
-### 6. Quelle est la première étape lorsqu'on utilise la méthode `push_to_hub()` ou les outils CLI ?
-
-notebook.",
- explain: "Correct ! Cela affichera un widget pour vous permettre de vous authentifier.",
- correct: true
- },
- ]}
-/>
-
-### 7. Vous utilisez un modèle et un *tokenizer*, comment pouvez-vous les télécharger sur le *Hub* ?
-
-tokenizer.",
- explain: "Correct !",
- correct: true
- },
- {
- text: "Au sein du moteur d'exécution Python, en les enveloppant dans une balise huggingface_hub
.",
- explain: "Les modèles et les tokenizers bénéficient déjà de huggingface_hub
: pas besoin d'emballage supplémentaire !"
- },
- {
- text: "En les sauvegardant sur le disque et en appelant transformers-cli upload-model
.",
- explain: "La commande upload-model
n'existe pas."
- }
- ]}
-/>
-
-### 8. Quelles opérations git pouvez-vous faire avec la classe `Repository` ?
-
-commit.",
- explain: "Correct, la méthode git_commit()
est là pour ça.",
- correct: true
- },
- {
- text: "Un pull.",
- explain: "C'est le but de la méthode git_pull()
.",
- correct: true
- },
- {
- text: "Un push.",
- explain: "La méthode git_push()
fait ça.",
- correct: true
- },
- {
- text: "Un merge.",
- explain: "Non, cette opération ne sera jamais possible avec cette API."
- }
- ]}
-/>
+
+
+
+
+# Quiz de fin de chapitre
+
+Testons ce que vous avez appris dans ce chapitre !
+
+### 1. A quoi sont limités les modèles du *Hub* ?
+
+Transformers.",
+ explain: "Si les modèles de la bibliothèque 🤗 Transformers sont pris en charge sur le Hub, ils ne sont pas les seuls !"
+ },
+ {
+ text: "Tous les modèles avec une interface similaire à 🤗 Transformers.",
+ explain: "Aucune exigence d'interface n'est fixée lors du téléchargement de modèles vers le Hub."
+ },
+ {
+ text: "Il n'y a pas de limites.",
+ explain: "Il n'y a pas de limites au téléchargement de modèles sur le Hub.",
+ correct: true
+ },
+ {
+ text: "Des modèles qui sont d'une certaine manière liés au NLP.",
+ explain: "Aucune exigence n'est fixée concernant le domaine d'application !"
+ }
+ ]}
+/>
+
+### 2. Comment pouvez-vous gérer les modèles sur le *Hub* ?
+
+Hub sont de simples dépôts Git exploitant git-lfs
pour les fichiers volumineux.",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. Que pouvez-vous faire en utilisant l'interface web du *Hub* ?
+
+Forker » un dépôt existant.",
+ explain: "« Forker » un dépôt n'est pas possible sur le Hub."
+ },
+ {
+ text: "Créer un nouveau dépôt de modèles.",
+ explain: "Ce n'est pas tout ce que vous pouvez faire, cependant.",
+ correct: true
+ },
+ {
+ text: "Gérer et modifier des fichiers.",
+ explain: "Ce n'est pas la seule bonne réponse, cependant.",
+ correct: true
+ },
+ {
+ text: "Télécharger des fichiers.",
+ explain: "Mais ce n'est pas tout.",
+ correct: true
+ },
+ {
+ text: "Voir les différences entre les versions.",
+ explain: "Ce n'est pas tout ce que vous pouvez faire.",
+ correct: true
+ }
+ ]}
+/>
+
+### 4. Qu'est-ce qu'une carte de modèle ?
+
+tokenizer.",
+ explain: "Il s'agit bien d'une description du modèle, mais c'est un élément important : s'il est incomplet ou absent, l'utilité du modèle est considérablement réduite."
+ },
+ {
+ text: "Un moyen d'assurer la reproductibilité, la réutilisation et l'équité..",
+ explain: "Le fait de partager les bonnes informations dans la fiche du modèle aidera les utilisateurs à tirer parti de votre modèle et à être conscients de ses limites et de ses biais.",
+ correct: true
+ },
+ {
+ text: "Un fichier Python qui peut être exécuté pour récupérer des informations sur le modèle.",
+ explain: "Les cartes de modèle sont de simples fichiers Markdown."
+ }
+ ]}
+/>
+
+### 5. Lesquels de ces objets de la bibliothèque 🤗 *Transformers* peuvent être directement partagés sur le Hub avec `push_to_hub()` ?
+
+{#if fw === 'pt'}
+tokenizer",
+ explain: "Tous les tokenizers ont la méthode push_to_hub
et l'utiliser poussera tous les fichiers du tokenizer (vocabulaire, architecture du tokenizer, etc.) vers un dépôt donné. Ce n'est pas la seule bonne réponse, cependant !",
+ correct: true
+ },
+ {
+ text: "Une configuration de modèle",
+ explain: "Toutes les configurations de modèles ont la méthode push_to_hub
et son utilisation les poussera vers un dépôt donné. Que pouvez-vous partager d'autre ?",
+ correct: true
+ },
+ {
+ text: "Un modèle",
+ explain: "Tous les modèles ont la méthode push_to_hub
qui le pushra ainsi que leurs fichiers de configuration, vers un dépôt donné. Ce n'est pas tout ce que vous pouvez partager, cependant.",
+ correct: true
+ },
+ {
+ text: "Trainer",
+ explain: "Le Trainer
implémente aussi la méthode push_to_hub
. L'utiliser téléchargera le modèle, sa configuration, le tokenizer et une ébauche de carte de modèle vers un dépôt donné. Essayez une autre réponse !",
+ correct: true
+ }
+ ]}
+/>
+{:else}
+tokenizer",
+ explain: "Toutes les configurations de modèles ont la méthode push_to_hub
et son utilisation les poussera vers un dépôt donné. Que pouvez-vous partager d'autre ?",
+ correct: true
+ },
+ {
+ text: "Une configuration de modèle",
+ explain: "Toutes les configurations de modèles ont la méthode push_to_hub
et son utilisation les poussera vers un dépôt donné. Que pouvez-vous partager d'autre ?",
+ correct: true
+ },
+ {
+ text: "Un modèle",
+ explain: "Tous les modèles ont la méthode push_to_hub
qui le pushra ainsi que leurs fichiers de configuration, vers un dépôt donné. Ce n'est pas tout ce que vous pouvez partager, cependant.",
+ correct: true
+ },
+ {
+ text: "Tout ce qui précède avec un callback dédié",
+ explain: "Le PushToHubCallback
enverra régulièrement tous ces objets à un dépôt pendant l'entraînement.",
+ correct: true
+ }
+ ]}
+/>
+{/if}
+
+### 6. Quelle est la première étape lorsqu'on utilise la méthode `push_to_hub()` ou les outils CLI ?
+
+notebook.",
+ explain: "Cela affichera un widget pour vous permettre de vous authentifier.",
+ correct: true
+ },
+ ]}
+/>
+
+### 7. Vous utilisez un modèle et un *tokenizer*, comment pouvez-vous les télécharger sur le *Hub* ?
+
+tokenizer.",
+ explain: " ",
+ correct: true
+ },
+ {
+ text: "Au sein du moteur d'exécution Python, en les enveloppant dans une balise huggingface_hub
.",
+ explain: "Les modèles et les tokenizers bénéficient déjà de huggingface_hub
: pas besoin d'emballage supplémentaire !"
+ },
+ {
+ text: "En les sauvegardant sur le disque et en appelant transformers-cli upload-model
.",
+ explain: "La commande upload-model
n'existe pas."
+ }
+ ]}
+/>
+
+### 8. Quelles opérations git pouvez-vous faire avec la classe `Repository` ?
+
+commit.",
+ explain: "La méthode git_commit()
est là pour ça.",
+ correct: true
+ },
+ {
+ text: "Un pull.",
+ explain: "C'est le but de la méthode git_pull()
.",
+ correct: true
+ },
+ {
+ text: "Un push.",
+ explain: "La méthode git_push()
fait ça.",
+ correct: true
+ },
+ {
+ text: "Un merge.",
+ explain: "Cette opération ne sera jamais possible avec cette API."
+ }
+ ]}
+/>
\ No newline at end of file
diff --git a/chapters/fr/chapter5/1.mdx b/chapters/fr/chapter5/1.mdx
index 145c6ecb0..2817734c9 100644
--- a/chapters/fr/chapter5/1.mdx
+++ b/chapters/fr/chapter5/1.mdx
@@ -1,17 +1,17 @@
-# Introduction
-
-Dans le [Chapitre 3](/course/fr/chapter3) vous avez eu un premier aperçu de la bibliothèque 🤗 *Datasets* et qu'il y a trois étapes principales pour *finetuner* un modèle :
-
-1. charger un jeu de données à partir du *Hub* d’Hugging Face.
-2. Prétraiter les données avec `Dataset.map()`.
-3. Charger et calculer des métriques.
-
-Mais ce n'est qu'effleurer la surface de ce que 🤗 *Datasets* peut faire ! Dans ce chapitre, nous allons plonger profondément dans cette bibliothèque. En cours de route, nous trouverons des réponses aux questions suivantes :
-
-* que faire lorsque votre jeu de données n'est pas sur le *Hub* ?
-* comment découper et trancher un jeu de données ? (Et si on a _vraiment_ besoin d'utiliser Pandas ?)
-* que faire lorsque votre jeu de données est énorme et va monopoliser la RAM de votre ordinateur portable ?
-* qu'est-ce que c'est que le « *memory mapping* » et Apache Arrow ?
-* comment créer votre propre jeu de données et le pousser sur le *Hub* ?
-
-Les techniques apprises dans ce chapitre vous prépareront aux tâches avancées de tokenisation et de *finetuning* du [Chapitre 6](/course/fr/chapter6) et du [Chapitre 7](/course/fr/chapter7). Alors prenez un café et commençons !
+# Introduction
+
+Dans le [chapitre 3](/course/fr/chapter3) vous avez eu un premier aperçu de la bibliothèque 🤗 *Datasets* et des trois étapes principales pour *finetuner* un modèle :
+
+1. chargement d'un jeu de données à partir du *Hub* d’Hugging Face,
+2. prétraitement des données avec `Dataset.map()`,
+3. chargement et calcul des métriques.
+
+Mais ce n'est qu'effleurer la surface de ce que 🤗 *Datasets* peut faire ! Dans ce chapitre, nous allons plonger profondément dans cette bibliothèque. En cours de route, nous trouverons des réponses aux questions suivantes :
+
+* que faire lorsque votre jeu de données n'est pas sur le *Hub* ?
+* comment découper et trancher un jeu de données ? (Et si on a _vraiment_ besoin d'utiliser Pandas ?)
+* que faire lorsque votre jeu de données est énorme et va monopoliser la RAM de votre ordinateur portable ?
+* qu'est-ce que c'est que le « *memory mapping* » et Apache Arrow ?
+* comment créer votre propre jeu de données et le pousser sur le *Hub* ?
+
+Les techniques apprises dans ce chapitre vous prépareront aux tâches avancées de tokenisation du [chapitre 6](/course/fr/chapter6) et de *finetuning* du [chapitre 7](/course/fr/chapter7). Alors prenez un café et commençons !
\ No newline at end of file
diff --git a/chapters/fr/chapter5/2.mdx b/chapters/fr/chapter5/2.mdx
index 7f4f93a2b..f05424005 100644
--- a/chapters/fr/chapter5/2.mdx
+++ b/chapters/fr/chapter5/2.mdx
@@ -1,165 +1,167 @@
-# Que faire si mon jeu de données n'est pas sur le *Hub* ?
-
-
-
-Vous savez comment utiliser le [*Hub* d’Hugging Face](https://huggingface.co/datasets) pour télécharger des jeux de données mais en pratique vous vous retrouverez souvent à travailler avec des données stockées sur votre ordinateur portable ou sur un serveur distant. Dans cette section, nous allons vous montrer comment 🤗 *Datasets* peut être utilisé pour charger des jeux de données qui ne sont pas disponibles sur le *Hub* d’Hugging Face.
-
-
-
-## Travailler avec des jeux de données locaux et distants
-
-🤗 *Datasets* fournit des scripts de chargement de jeux de données locaux et distants. Il prend en charge plusieurs formats de données courants, tels que :
-
-| Format des données | Script de chargement | Exemple |
-| :----------------: | :------------------: | :-----------------------------------------------------: |
-| CSV & TSV | `csv` | `load_dataset("csv", data_files="my_file.csv")` |
-| Fichiers texte | `text` | `load_dataset("text", data_files="my_file.txt")` |
-| JSON & Lignes JSON | `json` | `load_dataset("json", data_files="my_file.jsonl")` |
-| DataFrames en Pickle | `pandas` | `load_dataset("pandas", data_files="my_dataframe.pkl")` |
-
-Comme indiqué dans le tableau, pour chaque format de données, nous avons juste besoin de spécifier le type de script de chargement dans la fonction `load_dataset()`, ainsi qu'un argument `data_files` qui spécifie le chemin vers un ou plusieurs fichiers. Commençons par charger un jeu de données à partir de fichiers locaux puis plus tard comment faire la même chose avec des fichiers distants.
-
-## Charger un jeu de données local
-
-Pour cet exemple, nous utilisons le jeu de données [SQuAD-it](https://github.com/crux82/squad-it/) qui est un grand jeu de données pour la tâche de *Question Awnswering* en italien.
-
-Les échantillons d’entraînement et de test sont hébergés sur GitHub, nous pouvons donc les télécharger avec une simple commande `wget` :
-
-```python
-!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz
-!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz
-```
-
-Cela télécharge deux fichiers compressés appelés *SQuAD_it-train.json.gz* et *SQuAD_it-test.json.gz* que nous pouvons décompresser avec la commande Linux `gzip` :
-
-```python
-!gzip -dkv SQuAD_it-*.json.gz
-```
-
-```bash
-SQuAD_it-test.json.gz: 87.4% -- replaced with SQuAD_it-test.json
-SQuAD_it-train.json.gz: 82.2% -- replaced with SQuAD_it-train.json
-```
-
-Nous pouvons voir que les fichiers compressés ont été remplacés par _SQuAD_it-train.json_ et _SQuAD_it-text.json_, et que les données sont stockées au format JSON.
-
-
-
-✎ Si vous vous demandez pourquoi il y a un caractère `!` dans les commandes shell ci-dessus, c'est parce que nous les exécutons dans un *notebook* Jupyter. Supprimez simplement le préfixe si vous souhaitez télécharger et décompresser le jeu de données dans un terminal.
-
-
-
-Pour charger un fichier JSON avec la fonction `load_dataset()`, nous avons juste besoin de savoir si nous avons affaire à du JSON ordinaire (similaire à un dictionnaire imbriqué) ou à des lignes JSON (JSON séparé par des lignes). Comme de nombreux jeux de données de questions-réponses, SQuAD-it utilise le format imbriqué où tout le texte est stocké dans un champ `data`. Cela signifie que nous pouvons charger le jeu de données en spécifiant l'argument `field` comme suit :
-
-```py
-from datasets import load_dataset
-
-squad_it_dataset = load_dataset("json", data_files="SQuAD_it-train.json", field="data")
-```
-
-Par défaut, le chargement de fichiers locaux crée un objet `DatasetDict` avec un échantillon `train`. Nous pouvons le voir en inspectant l'objet `squad_it_dataset` :
-
-```py
-squad_it_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['title', 'paragraphs'],
- num_rows: 442
- })
-})
-```
-
-Cela nous montre le nombre de lignes et les noms de colonnes associés à l’échantillon d’entraînement. Nous pouvons afficher l'un des exemples en indexant l’échantillon `train` comme suit :
-
-```py
-squad_it_dataset["train"][0]
-```
-
-```python out
-{
- "title": "Terremoto del Sichuan del 2008", # Séisme du Sichuan 2008
- "paragraphs": [
- {
- "context": "Il terremoto del Sichuan del 2008 o il terremoto...", # Le tremblement de terre du Sichuan de 2008 ou le...
- "qas": [
- {
- "answers": [{"answer_start": 29, "text": "2008"}],
- "id": "56cdca7862d2951400fa6826",
- "question": "In quale anno si è verificato il terremoto nel Sichuan?", # En quelle année le tremblement de terre du Sichuan a-t-il eu lieu ?
- },
- ...
- ],
- },
- ...
- ],
-}
-```
-
-Super, nous avons chargé notre premier jeu de données local ! Mais ce que nous voulons vraiment, c'est inclure à la fois les échantillons `train` et `test` dans un seul objet `DatasetDict` afin que nous puissions appliquer les fonctions `Dataset.map()` sur les deux à la fois . Pour ce faire, nous pouvons fournir un dictionnaire à l'argument `data_files` qui associe chaque nom des échantillons à un fichier associé à cet échantillon :
-
-```py
-data_files = {"train": "SQuAD_it-train.json", "test": "SQuAD_it-test.json"}
-squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
-squad_it_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['title', 'paragraphs'],
- num_rows: 442
- })
- test: Dataset({
- features: ['title', 'paragraphs'],
- num_rows: 48
- })
-})
-```
-
-C'est exactement ce que nous voulions. Désormais, nous pouvons appliquer diverses techniques de prétraitement pour nettoyer les données, tokeniser les avis, etc.
-
-
-
-L'argument `data_files` de la fonction `load_dataset()` est assez flexible et peut être soit un chemin de fichier unique, une liste de chemins de fichiers, ou un dictionnaire qui fait correspondre les noms des échantillons aux chemins de fichiers. Vous pouvez également regrouper les fichiers correspondant à un motif spécifié selon les règles utilisées par le shell Unix. Par exemple, vous pouvez regrouper tous les fichiers JSON d'un répertoire en une seule division en définissant `data_files="*.json"`. Voir la [documentation](https://huggingface.co/docs/datasets/loading.html#local-and-remote-files) de 🤗 *Datasets* pour plus de détails.
-
-
-
-Les scripts de chargement de 🤗 *Datasets* prennent en charge la décompression automatique des fichiers d'entrée. Nous aurions donc pu ignorer l'utilisation de `gzip` en pointant l'argument `data_files` directement sur les fichiers compressés :
-
-```py
-data_files = {"train": "SQuAD_it-train.json.gz", "test": "SQuAD_it-test.json.gz"}
-squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
-```
-
-Cela peut être utile si vous ne souhaitez pas décompresser manuellement de nombreux fichiers GZIP. La décompression automatique s'applique également à d'autres formats courants tels que ZIP et TAR. Il vous suffit donc de pointer `data_files` vers les fichiers compressés et vous êtes prêt à partir !
-
-Maintenant que vous savez comment charger des fichiers locaux sur votre ordinateur portable ou de bureau, examinons le chargement de fichiers distants.
-
-## Charger un jeu de données distant
-
-Si vous travaillez en tant que *data scientist* ou codeur dans une entreprise, il y a de fortes chances que les juex de données que vous souhaitez analyser soient stockés sur un serveur distant. Heureusement, charger des fichiers distants est aussi simple que de charger des fichiers locaux ! Au lieu de fournir un chemin vers les fichiers locaux, nous pointons l'argument `data_files` de `load_dataset()` vers une ou plusieurs URL où les fichiers distants sont stockés. Par exemple, pour le jeu de données SQuAD-it hébergé sur GitHub, nous pouvons simplement faire pointer `data_files` vers les URL _SQuAD_it-*.json.gz_ comme suit :
-
-```py
-url = "https://github.com/crux82/squad-it/raw/master/"
-data_files = {
- "train": url + "SQuAD_it-train.json.gz",
- "test": url + "SQuAD_it-test.json.gz",
-}
-squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
-```
-
-Cela renvoie le même objet `DatasetDict` obtenu ci-dessus mais nous évite de télécharger et de décompresser manuellement les fichiers _SQuAD_it-*.json.gz_. Ceci conclut notre incursion dans les différentes façons de charger des jeux de données qui ne sont pas hébergés sur le *Hub* d’Hugging Face. Maintenant que nous avons un jeu de données avec lequel jouer, mettons la main à la pâte avec diverses techniques de gestion des données !
-
-
-
-✏️ **Essayez !** Choisissez un autre jeu de données hébergé sur GitHub ou dans le [*UCI Machine Learning Repository*](https://archive.ics.uci.edu/ml/index.php) et essayez de le charger localement et à distance en utilisant les techniques présentées ci-dessus. Pour obtenir des points bonus, essayez de charger un jeu de données stocké au format CSV ou texte (voir la [documentation](https://huggingface.co/docs/datasets/loading.html#local-and-remote-files) pour plus d'informations sur ces formats).
-
-
+# Que faire si mon jeu de données n'est pas sur le Hub ?
+
+
+
+Vous savez comment utiliser le [*Hub*](https://huggingface.co/datasets) pour télécharger des jeux de données mais en pratique vous vous retrouverez souvent à travailler avec des données stockées sur votre ordinateur portable ou sur un serveur distant. Dans cette section, nous allons vous montrer comment 🤗 *Datasets* peut être utilisé pour charger des jeux de données qui ne sont pas disponibles sur le *Hub* d’Hugging Face.
+
+
+
+## Travailler avec des jeux de données locaux et distants
+
+🤗 *Datasets* fournit des scripts de chargement de jeux de données locaux et distants. La bibliothèque prend en charge plusieurs formats de données courants, tels que :
+
+| Format des données | Script de chargement | Exemple |
+| :----------------: | :------------------: | :-----------------------------------------------------: |
+| CSV & TSV | `csv` | `load_dataset("csv", data_files="my_file.csv")` |
+| Fichiers texte | `text` | `load_dataset("text", data_files="my_file.txt")` |
+| JSON & JSON Lines | `json` | `load_dataset("json", data_files="my_file.jsonl")` |
+| DataFrames en Pickle | `pandas` | `load_dataset("pandas", data_files="my_dataframe.pkl")` |
+
+Comme indiqué dans le tableau, pour chaque format de données, nous avons juste besoin de spécifier le type de script de chargement dans la fonction `load_dataset()`, ainsi qu'un argument `data_files` qui spécifie le chemin vers un ou plusieurs fichiers. Commençons par charger un jeu de données à partir de fichiers locaux puis plus tard comment faire la même chose avec des fichiers distants.
+
+## Charger un jeu de données local
+
+Pour cet exemple, nous utilisons le jeu de données [SQuAD-it](https://github.com/crux82/squad-it/) qui est un grand jeu de données pour la tâche de *Question Awnswering* en italien.
+
+Les échantillons d’entraînement et de test sont hébergés sur GitHub, nous pouvons donc les télécharger avec une simple commande `wget` :
+
+```python
+!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz
+!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz
+```
+
+Cela télécharge deux fichiers compressés appelés *SQuAD_it-train.json.gz* et *SQuAD_it-test.json.gz* que nous pouvons décompresser avec la commande Linux `gzip` :
+
+```python
+!gzip -dkv SQuAD_it-*.json.gz
+```
+
+```bash
+SQuAD_it-test.json.gz: 87.4% -- replaced with SQuAD_it-test.json
+SQuAD_it-train.json.gz: 82.2% -- replaced with SQuAD_it-train.json
+```
+
+Nous pouvons voir que les fichiers compressés ont été remplacés par _SQuAD_it-train.json_ et _SQuAD_it-text.json_, et que les données sont stockées au format JSON.
+
+
+
+✎ Si vous vous demandez pourquoi il y a un caractère `!` dans les commandes *shell* ci-dessus, c'est parce que nous les exécutons dans un *notebook* Jupyter. Supprimez simplement le préfixe si vous souhaitez télécharger et décompresser le jeu de données dans un terminal.
+
+
+
+Pour charger un fichier JSON avec la fonction `load_dataset()`, nous avons juste besoin de savoir si nous avons affaire à du JSON ordinaire (similaire à un dictionnaire imbriqué) ou à des lignes JSON (JSON séparé par des lignes). Comme de nombreux jeux de données de questions-réponses, SQuAD-it utilise le format imbriqué où tout le texte est stocké dans un champ `data`. Cela signifie que nous pouvons charger le jeu de données en spécifiant l'argument `field` comme suit :
+
+```py
+from datasets import load_dataset
+
+squad_it_dataset = load_dataset("json", data_files="SQuAD_it-train.json", field="data")
+```
+
+Par défaut, le chargement de fichiers locaux crée un objet `DatasetDict` avec un échantillon `train`. Nous pouvons le voir en inspectant l'objet `squad_it_dataset` :
+
+```py
+squad_it_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['title', 'paragraphs'],
+ num_rows: 442
+ })
+})
+```
+
+Cela nous montre le nombre de lignes et les noms de colonnes associés à l’échantillon d’entraînement. Nous pouvons afficher l'un des exemples en indexant l’échantillon `train` comme suit :
+
+```py
+squad_it_dataset["train"][0]
+```
+
+```python out
+{
+ "title": "Terremoto del Sichuan del 2008", # Séisme du Sichuan 2008
+ "paragraphs": [
+ {
+ "context": "Il terremoto del Sichuan del 2008 o il terremoto...",
+ # Le tremblement de terre du Sichuan de 2008 ou le...
+ "qas": [
+ {
+ "answers": [{"answer_start": 29, "text": "2008"}],
+ "id": "56cdca7862d2951400fa6826",
+ "question": "In quale anno si è verificato il terremoto nel Sichuan?",
+ # En quelle année le tremblement de terre du Sichuan a-t-il eu lieu ?
+ },
+ ...
+ ],
+ },
+ ...
+ ],
+}
+```
+
+Super, nous avons chargé notre premier jeu de données local ! Mais ce que nous voulons vraiment, c'est inclure à la fois les échantillons `train` et `test` dans un seul objet `DatasetDict` afin que nous puissions appliquer les fonctions `Dataset.map()` sur les deux à la fois . Pour ce faire, nous pouvons fournir un dictionnaire à l'argument `data_files` qui associe chaque nom des échantillons à un fichier associé à cet échantillon :
+
+```py
+data_files = {"train": "SQuAD_it-train.json", "test": "SQuAD_it-test.json"}
+squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
+squad_it_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['title', 'paragraphs'],
+ num_rows: 442
+ })
+ test: Dataset({
+ features: ['title', 'paragraphs'],
+ num_rows: 48
+ })
+})
+```
+
+C'est exactement ce que nous voulions. Désormais, nous pouvons appliquer diverses techniques de prétraitement pour nettoyer les données, tokeniser les avis, etc.
+
+
+
+L'argument `data_files` de la fonction `load_dataset()` est assez flexible et peut être soit un chemin de fichier unique, une liste de chemins de fichiers, ou un dictionnaire qui fait correspondre les noms des échantillons aux chemins de fichiers. Vous pouvez également regrouper les fichiers correspondant à un motif spécifié selon les règles utilisées par le shell Unix. Par exemple, vous pouvez regrouper tous les fichiers JSON d'un répertoire en une seule division en définissant `data_files="*.json"`. Voir la [documentation](https://huggingface.co/docs/datasets/loading.html#local-and-remote-files) de 🤗 *Datasets* pour plus de détails.
+
+
+
+Les scripts de chargement de 🤗 *Datasets* prennent en charge la décompression automatique des fichiers d'entrée. Nous aurions donc pu ignorer l'utilisation de `gzip` en pointant l'argument `data_files` directement sur les fichiers compressés :
+
+```py
+data_files = {"train": "SQuAD_it-train.json.gz", "test": "SQuAD_it-test.json.gz"}
+squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
+```
+
+Cela peut être utile si vous ne souhaitez pas décompresser manuellement de nombreux fichiers GZIP. La décompression automatique s'applique également à d'autres formats courants tels que ZIP et TAR. Il vous suffit donc de pointer `data_files` vers les fichiers compressés et vous êtes prêt à partir !
+
+Maintenant que vous savez comment charger des fichiers locaux sur votre ordinateur portable ou de bureau, examinons le chargement de fichiers distants.
+
+## Charger un jeu de données distant
+
+Si vous travaillez en tant que *data scientist* ou codeur dans une entreprise, il y a de fortes chances que les jeux de données que vous souhaitez analyser soient stockés sur un serveur distant. Heureusement, charger des fichiers distants est aussi simple que de charger des fichiers locaux ! Au lieu de fournir un chemin vers les fichiers locaux, nous pointons l'argument `data_files` de `load_dataset()` vers une ou plusieurs URL où les fichiers distants sont stockés. Par exemple, pour le jeu de données SQuAD-it hébergé sur GitHub, nous pouvons simplement faire pointer `data_files` vers les URL _SQuAD_it-*.json.gz_ comme suit :
+
+```py
+url = "https://github.com/crux82/squad-it/raw/master/"
+data_files = {
+ "train": url + "SQuAD_it-train.json.gz",
+ "test": url + "SQuAD_it-test.json.gz",
+}
+squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
+```
+
+Cela renvoie le même objet `DatasetDict` obtenu ci-dessus mais nous évite de télécharger et de décompresser manuellement les fichiers _SQuAD_it-*.json.gz_. Ceci conclut notre incursion dans les différentes façons de charger des jeux de données qui ne sont pas hébergés sur le *Hub*. Maintenant que nous avons un jeu de données avec lequel jouer, mettons la main à la pâte avec diverses techniques de gestion des données !
+
+
+
+✏️ **Essayez !** Choisissez un autre jeu de données hébergé sur GitHub ou dans le [*UCI Machine Learning Repository*](https://archive.ics.uci.edu/ml/index.php) et essayez de le charger localement et à distance en utilisant les techniques présentées ci-dessus. Pour obtenir des points bonus, essayez de charger un jeu de données stocké au format CSV ou texte (voir la [documentation](https://huggingface.co/docs/datasets/loading.html#local-and-remote-files) pour plus d'informations sur ces formats).
+
+
diff --git a/chapters/fr/chapter5/3.mdx b/chapters/fr/chapter5/3.mdx
index ef1ad0367..1962c4dad 100644
--- a/chapters/fr/chapter5/3.mdx
+++ b/chapters/fr/chapter5/3.mdx
@@ -1,743 +1,752 @@
-# Il est temps de trancher et de découper
-
-
-
-La plupart du temps, les données avec lesquelles vous travaillez ne sont pas parfaitement préparées pour l’entraînements de modèles. Dans cette section, nous allons explorer les différentes fonctionnalités fournies par 🤗 *Datasets* pour nettoyer vos jeux de données.
-
-
-
-## Trancher et découper nos données
-
-Semblable à Pandas, 🤗 *Datasets* fournit plusieurs fonctions pour manipuler le contenu des objets `Dataset` et `DatasetDict`. Nous avons déjà rencontré la méthode `Dataset.map()` dans le [Chapitre 3](/course/fr/chapter3) et dans cette section nous allons explorer certaines des autres fonctions à notre disposition.
-
-Pour cet exemple, nous utiliserons le [*Drug Review Dataset*](https://archive.ics.uci.edu/ml/datasets/Drug+Review+Dataset+%28Drugs.com%29) qui est hébergé sur [*UC Irvine Machine Learning Repository*](https://archive.ics.uci.edu/ml/index.php) et contenant des avis de patients sur divers médicaments ainsi que la condition traitée et une note de 10 étoiles sur la satisfaction du patient.
-
-Nous devons d'abord télécharger et extraire les données, ce qui peut être fait avec les commandes `wget` et `unzip` :
-
-```py
-!wget "https://archive.ics.uci.edu/ml/machine-learning-databases/00462/drugsCom_raw.zip"
-!unzip drugsCom_raw.zip
-```
-
-Étant donné que TSV n'est qu'une variante de CSV qui utilise des tabulations au lieu de virgules comme séparateurs, nous pouvons charger ces fichiers en utilisant le script de chargement `csv` et en spécifiant l'argument `delimiter` dans la fonction `load_dataset()` comme suit :
-
-```py
-from datasets import load_dataset
-
-data_files = {"train": "drugsComTrain_raw.tsv", "test": "drugsComTest_raw.tsv"}
-# \t is the tab character in Python
-drug_dataset = load_dataset("csv", data_files=data_files, delimiter="\t")
-```
-
-Une bonne pratique lors de toute sorte d'analyse de données consiste à prélever un petit échantillon aléatoire pour avoir une idée rapide du type de données avec lesquelles vous travaillez. Dans 🤗 *Datasets*, nous pouvons créer un échantillon aléatoire en enchaînant les fonctions `Dataset.shuffle()` et `Dataset.select()` :
-
-```py
-drug_sample = drug_dataset["train"].shuffle(seed=42).select(range(1000))
-# Un coup d'œil sur les premiers exemples
-drug_sample[:3]
-```
-
-```python out
-{'Unnamed: 0': [87571, 178045, 80482],
- 'drugName': ['Naproxen', 'Duloxetine', 'Mobic'],
- 'condition': ['Gout, Acute', 'ibromyalgia', 'Inflammatory Conditions'], #['Goutte aiguë', 'ibromyalgie', 'Affections inflammatoires']
- 'review': ['"like the previous person mention, I'm a strong believer of aleve, it works faster for my gout than the prescription meds I take. No more going to the doctor for refills.....Aleve works!"', # comme la personne précédente l'a mentionné, je suis un fervent partisan de l'aleve, il fonctionne plus rapidement pour ma goutte que les médicaments sur ordonnance que je prends. Je n'ai plus besoin d'aller chez le médecin pour des renouvellements.....Aleve fonctionne !"
- '"I have taken Cymbalta for about a year and a half for fibromyalgia pain. It is great\r\nas a pain reducer and an anti-depressant, however, the side effects outweighed \r\nany benefit I got from it. I had trouble with restlessness, being tired constantly,\r\ndizziness, dry mouth, numbness and tingling in my feet, and horrible sweating. I am\r\nbeing weaned off of it now. Went from 60 mg to 30mg and now to 15 mg. I will be\r\noff completely in about a week. The fibro pain is coming back, but I would rather deal with it than the side effects."', # J'ai pris du Cymbalta pendant environ un an et demi pour des douleurs de la fibromyalgie. C'est un excellent analgésique et un antidépresseur, mais les effets secondaires l'ont emporté sur tous les avantages que j'en ai tirés. J'ai eu des problèmes d'agitation, de fatigue constante, de vertiges, de bouche sèche, d'engourdissement, de picotements dans les pieds, et de transpiration horrible. Je suis en train de m'en sevrer maintenant. Je suis passée de 60 mg à 30 mg et maintenant à 15 mg. Je l'arrêterai complètement dans environ une semaine. La douleur de la fibrose revient, mais je préfère la supporter plutôt que les effets secondaires.
- '"I have been taking Mobic for over a year with no side effects other than an elevated blood pressure. I had severe knee and ankle pain which completely went away after taking Mobic. I attempted to stop the medication however pain returned after a few days."'], # J'ai pris Mobic pendant plus d'un an sans effets secondaires autres qu'une pression sanguine élevée. J'avais de fortes douleurs au genou et à la cheville qui ont complètement disparu après avoir pris Mobic. J'ai essayé d'arrêter le médicament mais la douleur est revenue après quelques jours."
- 'rating': [9.0, 3.0, 10.0],
- 'date': ['September 2, 2015', 'November 7, 2011', 'June 5, 2013'], #['2 septembre 2015', '7 novembre 2011', '5 juin 2013']
- 'usefulCount': [36, 13, 128]}
-```
-
-Notez que nous avons corrigé la graine dans `Dataset.shuffle()` à des fins de reproductibilité. `Dataset.select()` attend un itérable d'indices, nous avons donc passé `range(1000)` pour récupérer les 1 000 premiers exemples du jeu de données mélangé. À partir de cet échantillon, nous pouvons déjà voir quelques bizarreries dans notre jeu de données :
-
-* la colonne `Unnamed: 0` ressemble étrangement à un identifiant anonyme pour chaque patient,
-* la colonne `condition` comprend un mélange d'étiquettes en majuscules et en minuscules,
-* les avis sont de longueur variable et contiennent un mélange de séparateurs de lignes Python (`\r\n`) ainsi que des codes de caractères HTML comme `&\#039;`.
-
-Voyons comment nous pouvons utiliser 🤗 *Datasets* pour traiter chacun de ces problèmes. Pour tester l'hypothèse de l'ID patient pour la colonne `Unnamed : 0`, nous pouvons utiliser la fonction `Dataset.unique()` pour vérifier que le nombre d'ID correspond au nombre de lignes dans chaque division :
-
-```py
-for split in drug_dataset.keys():
- assert len(drug_dataset[split]) == len(drug_dataset[split].unique("Unnamed: 0"))
-```
-
-Cela semble confirmer notre hypothèse, alors nettoyons un peu en renommant la colonne `Unnamed: 0` en quelque chose d'un peu plus interprétable. Nous pouvons utiliser la fonction `DatasetDict.rename_column()` pour renommer la colonne sur les deux divisions en une seule fois :
-
-```py
-drug_dataset = drug_dataset.rename_column(
- original_column_name="Unnamed: 0", new_column_name="patient_id"
-)
-drug_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
- num_rows: 161297
- })
- test: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
- num_rows: 53766
- })
-})
-```
-
-
-
-✏️ **Essayez !** Utilisez la fonction ` Dataset.unique()` pour trouver le nombre de médicaments et de conditions uniques dans les échantillons d'entraînement et de test.
-
-
-
-Ensuite, normalisons toutes les étiquettes `condition` en utilisant `Dataset.map()`. Comme nous l'avons fait avec la tokenisation dans le [chapitre 3](/course/fr/chapter3), nous pouvons définir une fonction simple qui peut être appliquée sur toutes les lignes de chaque division dans `drug_dataset` :
-
-```py
-def lowercase_condition(example):
- return {"condition": example["condition"].lower()}
-
-
-drug_dataset.map(lowercase_condition)
-```
-
-```python out
-AttributeError: 'NoneType' object has no attribute 'lower'
-```
-
-Oh non, nous rencontrons un problème avec notre fonction ! À partir de l'erreur, nous pouvons déduire que certaines des entrées de la colonne `condition` sont `None` ne pouvant donc pas être mises en minuscules car ce ne sont pas des chaînes. Supprimons ces lignes en utilisant `Dataset.filter()`, qui fonctionne de manière similaire à `Dataset.map()` et attend une fonction qui reçoit un seul exemple issu du jeu de données. Au lieu d'écrire une fonction explicite comme :
-
-```py
-def filter_nones(x):
- return x["condition"] is not None
-```
-
-puis exécuter `drug_dataset.filter(filter_nones)`, nous pouvons le faire en une seule ligne en utilisant une _fonction lambda_. En Python, les fonctions lambda sont de petites fonctions que vous pouvez définir sans les nommer explicitement. Ils prennent la forme générale :
-
-```
-lambda :
-```
-
-où `lambda` est l'un des [mots clés](https://docs.python.org/3/reference/lexical_analysis.html#keywords) spéciaux de Python, `` est une liste/ensemble de valeurs séparées par des virgules qui définissent les entrées de la fonction et `` représente les opérations que vous souhaitez exécuter. Par exemple, nous pouvons définir une simple fonction lambda qui met au carré un nombre comme suit :
-
-```
-lambda x : x * x
-```
-
-Pour appliquer cette fonction à une entrée, nous devons l'envelopper ainsi que l'entrée entre parenthèses :
-
-```py
-(lambda x: x * x)(3)
-```
-
-```python out
-9
-```
-
-De même, nous pouvons définir des fonctions lambda avec plusieurs arguments en les séparant par des virgules. Par exemple, nous pouvons calculer l'aire d'un triangle comme suit :
-
-```py
-(lambda base, height: 0.5 * base * height)(4, 8)
-```
-
-```python out
-16.0
-```
-
-Les fonctions lambda sont pratiques lorsque vous souhaitez définir de petites fonctions à usage unique (pour plus d'informations à leur sujet, nous vous recommandons de lire l'excellent [tutoriel Real Python](https://realpython.com/python-lambda/) d'André Burgaud) . Dans le contexte de la bibliothèque 🤗 *Datasets*, nous pouvons utiliser des fonctions lambda pour définir des opérations simples de mappage et de filtrage. Utilisons cette astuce pour éliminer les entrées `None` dans notre jeu de données :
-
-```py
-drug_dataset = drug_dataset.filter(lambda x: x["condition"] is not None)
-```
-
-Avec les entrées `None` supprimées, nous pouvons normaliser notre colonne `condition` :
-
-```py
-drug_dataset = drug_dataset.map(lowercase_condition)
-# Vérification que la mise en minuscule a fonctionné
-drug_dataset["train"]["condition"][:3]
-```
-
-```python out
-['left ventricular dysfunction', 'adhd', 'birth control']
-```
-
-Ça marche ! Maintenant que nous avons nettoyé les étiquettes, examinons le nettoyage des avis eux-mêmes.
-
-## Création de nouvelles colonnes
-
-Chaque fois que vous avez affaire à des avis de clients, une bonne pratique consiste à vérifier le nombre de mots dans chaque avis. Une critique peut être un simple mot comme « Génial ! » ou un essai complet avec des milliers de mots. Selon le cas d'usage, vous devrez gérer ces extrêmes différemment. Pour calculer le nombre de mots dans chaque révision, nous utiliserons une heuristique approximative basée sur la division de chaque texte par des espaces.
-
-Définissons une fonction simple qui compte le nombre de mots dans chaque avis :
-
-```py
-def compute_review_length(example):
- return {"review_length": len(example["review"].split())}
-```
-
-Contrairement à notre fonction `lowercase_condition()`, `compute_review_length()` renvoie un dictionnaire dont la clé ne correspond pas à l'un des noms de colonne du jeu de données. Dans ce cas, lorsque `compute_review_length()` est passé à `Dataset.map()`, il est appliqué à toutes les lignes du jeu de données pour créer une nouvelle colonne `review_length` :
-
-```py
-drug_dataset = drug_dataset.map(compute_review_length)
-# Inspecter le premier exemple d'entraînement
-drug_dataset["train"][0]
-```
-
-```python out
-{'patient_id': 206461,
- 'drugName': 'Valsartan',
- 'condition': 'left ventricular dysfunction', # dysfonctionnement du ventricule gauche
- 'review': '"It has no side effect, I take it in combination of Bystolic 5 Mg and Fish Oil"', # Il n'a aucun effet secondaire, je le prends en combinaison avec Bystolic 5 mg et de l'huile de poisson.
- 'rating': 9.0,
- 'date': 'May 20, 2012', # 20 mai 2012
- 'usefulCount': 27,
- 'review_length': 17}
-```
-
-Comme prévu, nous pouvons voir qu'une colonne `review_length` a été ajoutée à notre jeu d'entraînement. Nous pouvons trier cette nouvelle colonne avec `Dataset.sort()` pour voir à quoi ressemblent les valeurs extrêmes :
-
-```py
-drug_dataset["train"].sort("review_length")[:3]
-```
-
-```python out
-{'patient_id': [103488, 23627, 20558],
- 'drugName': ['Loestrin 21 1 / 20', 'Chlorzoxazone', 'Nucynta'],
- 'condition': ['birth control', 'muscle spasm', 'pain'], # contraception, spasme musculaire, douleur.
- 'review': ['"Excellent."', '"useless"', '"ok"'], # Excellent, inutile, ok
- 'rating': [10.0, 1.0, 6.0],
- 'date': ['November 4, 2008', 'March 24, 2017', 'August 20, 2016'], # 4 novembre 2008, 24 mars 2017, 20 août 2016
- 'usefulCount': [5, 2, 10],
- 'review_length': [1, 1, 1]}
-```
-
-Comme nous le soupçonnions, certaines critiques ne contiennent qu'un seul mot, ce qui, bien que cela puisse convenir à l'analyse des sentiments, n’est pas informatif si nous voulons prédire la condition.
-
-
-
-🙋 Une autre façon d'ajouter de nouvelles colonnes à un jeu de données consiste à utiliser la fonction `Dataset.add_column()`. Cela vous permet de donner la colonne sous forme de liste Python ou de tableau NumPy et peut être utile dans les situations où `Dataset.map()` n'est pas bien adapté à votre analyse.
-
-
-
-Utilisons la fonction `Dataset.filter()` pour supprimer les avis contenant moins de 30 mots. De la même manière que nous l'avons fait avec la colonne `condition`, nous pouvons filtrer les avis très courts en exigeant que les avis aient une longueur supérieure à ce seuil :
-
-```py
-drug_dataset = drug_dataset.filter(lambda x: x["review_length"] > 30)
-print(drug_dataset.num_rows)
-```
-
-```python out
-{'train': 138514, 'test': 46108}
-```
-
-Comme vous pouvez le constater, cela a supprimé environ 15 % des avis de nos jeux d'entraînement et de test d'origine.
-
-
-
-✏️ **Essayez !** Utilisez la fonction `Dataset.sort()` pour inspecter les avis avec le plus grand nombre de mots. Consultez la [documentation](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasets.Dataset.sort) pour voir quel argument vous devez utiliser pour trier les avis par longueur dans l'ordre décroissant.
-
-
-
-La dernière chose à laquelle nous devons faire face est la présence de caractères HTML dans nos avis. Nous pouvons utiliser le module `html` de Python pour supprimer ces caractères, comme ceci :
-
-```py
-import html
-
-text = "I'm a transformer called BERT"
-html.unescape(text)
-```
-
-```python out
-"I'm a transformer called BERT"
-```
-
-Nous utilisons `Dataset.map()` pour démasquer tous les caractères HTML de notre corpus :
-
-```python
-drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])})
-```
-
-Comme vous pouvez le voir, la méthode `Dataset.map()` est très utile pour le traitement des données. Et nous n'avons même pas effleuré la surface de tout ce qu'elle peut faire !
-
-## Les superpouvoirs de la méthode `map()`
-
-La méthode `Dataset.map()` prend un argument `batched` qui, s'il est défini sur `True`, l'amène à envoyer un batch d'exemples à la fonction map en une seule fois (la taille du batch est configurable mais est fixé par défaut à 1 000). Par exemple, la fonction `map()` précédente qui supprime tout le code HTML prend un peu de temps à s'exécuter (vous pouvez lire le temps pris dans les barres de progression). On peut accélérer cela en traitant plusieurs éléments en même temps à l'aide d'une compréhension de liste.
-
-Lorsque vous spécifiez `batched=True`, la fonction reçoit un dictionnaire avec les champs du jeu de données mais chaque valeur est maintenant une _liste de valeurs_ et non plus une seule valeur. La valeur retournée par `Dataset.map()` devrait être la même : un dictionnaire avec les champs que nous voulons mettre à jour ou ajouter à notre jeu de données, et une liste de valeurs. Par exemple, voici une autre façon de supprimer tous les caractères HTML, mais en utilisant `batched=True` :
-
-```python
-new_drug_dataset = drug_dataset.map(
- lambda x: {"review": [html.unescape(o) for o in x["review"]]}, batched=True
-)
-```
-
-Si vous exécutez ce code dans un *notebook*, vous verrez que cette commande s'exécute beaucoup plus rapidement que la précédente. Et ce n'est pas parce que nos critiques ont déjà été scannées au format HTML. Si vous ré-exécutez l'instruction de la section précédente (sans `batched=True`), cela prendra le même temps qu'avant. En effet, les compréhensions de liste sont généralement plus rapides que l'exécution du même code dans une boucle `for` et nous gagnons également en performances en accédant à de nombreux éléments en même temps au lieu d'un par un.
-
-L'utilisation de `Dataset.map()` avec `batched=True` est essentielle pour les *tokenizers rapides* que nous rencontrerons dans le [Chapitre 6](/course/fr/chapter6) et qui peuvent rapidement tokeniser de grandes listes de textes. Par exemple, pour tokeniser toutes les critiques de médicaments avec un *tokenizer* rapide nous pouvons utiliser une fonction comme celle-ci :
-
-```python
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-
-
-def tokenize_function(examples):
- return tokenizer(examples["review"], truncation=True)
-```
-
-Comme vous l'avez vu dans le [Chapitre 3](/course/fr/chapter3), nous pouvons passer un ou plusieurs exemples au *tokenizer*. Nous pouvons donc utiliser cette fonction avec ou sans `batched=True`. Profitons-en pour comparer les performances des différentes options. Dans un *notebook*, vous pouvez chronométrer une instruction d'une ligne en ajoutant `%time` avant la ligne de code que vous souhaitez mesurer :
-
-```python no-format
-%time tokenized_dataset = drug_dataset.map(tokenize_function, batched=True)
-```
-
-Vous pouvez également chronométrer une cellule entière en mettant `%%time` au début de la cellule. Sur le matériel sur lequel nous avons exécuté cela, cela affichait 10,8 s pour cette instruction (c'est le nombre écrit après "Wall time").
-
-
-
-✏️ **Essayez !** Exécutez la même instruction avec et sans `batched=True`, puis essayez-le avec un *tokenizer* lent (ajoutez `use_fast=False` dans la méthode `AutoTokenizer.from_pretrained()`) afin que vous puissiez voir quels temps vous obtenez sur votre matériel.
-
-
-
-Voici les résultats que nous avons obtenus avec et sans batching, avec un *tokenizer* rapide et un lent :
-
-Options | *Tokenizer* rapide | *Tokenizer* lent
-:--------------:|:----------------:|:-----------------:
-`batched=True` | 10.8s | 4min41s
-`batched=False` | 59.2s | 5min3s
-
-Cela signifie que l'utilisation d'un *tokenizer* rapide avec l'option `batched=True` est 30 fois plus rapide que son homologue lent sans batch. C'est vraiment incroyable ! C'est la raison principale pour laquelle les *tokenizers* rapides sont la valeur par défaut lors de l'utilisation de `AutoTokenizer` (et pourquoi ils sont appelés « rapides »). Ils sont capables d'atteindre une telle vitesse car en coulisses le code de tokenisation est exécuté en Rust qui est un langage facilitant la parallélisation de l'exécution du code.
-
-La parallélisation est également la raison du gain de vitesse de près de 6 fois obtenue par le *tokenizer* rapide avec batch. Vous ne pouvez pas paralléliser une seule opération de tokenisation, mais lorsque vous souhaitez *tokeniser* de nombreux textes en même temps, vous pouvez simplement répartir l'exécution sur plusieurs processus. Chacun responsable de ses propres textes.
-
-`Dataset.map()` possède aussi ses propres capacités de parallélisation. Comme elles ne sont pas soutenus par Rust, un *tokenizer* lent ne peut pas rattraper un rapide mais cela peut toujours être utile (surtout si vous utilisez un *tokenizer* qui n'a pas de version rapide). Pour activer le multitraitement, utilisez l'argument `num_proc` et spécifiez le nombre de processus à utiliser dans votre appel à `Dataset.map()` :
-
-```py
-slow_tokenizer = AutoTokenizer.from_pretrained("bert-base-cased", use_fast=False)
-
-
-def slow_tokenize_function(examples):
- return slow_tokenizer(examples["review"], truncation=True)
-
-
-tokenized_dataset = drug_dataset.map(slow_tokenize_function, batched=True, num_proc=8)
-```
-
-Vous pouvez faire des tests pour déterminer le nombre optimal de processus à utiliser. Dans notre cas 8 semble produire le meilleur gain de vitesse. Voici les chiffres que nous avons obtenus avec et sans multitraitement :
-
-Options | *Tokenizer* rapide | *Tokenizer* lent
-:----------------------------:|:----------------:|:---------------:
-`batched=True` | 10.8s | 4min41s
-`batched=False` | 59.2s | 5min3s
-`batched=True`, `num_proc=8` | 6.52s | 41.3s
-`batched=False`, `num_proc=8` | 9.49s | 45.2s
-
-Ce sont des résultats beaucoup plus raisonnables pour le *tokenizer* lent mais les performances du *tokenizer* rapide ont également été considérablement améliorées. Notez, cependant, que ce ne sera pas toujours le cas : pour des valeurs de `num_proc` autres que 8, nos tests ont montré qu'il était plus rapide d'utiliser `batched=True` sans cette option. En général, nous ne recommandons pas d'utiliser le multitraitement pour les *tokenizers* rapides avec `batched=True`.
-
-
-
-Utiliser `num_proc` pour accélérer votre traitement est généralement une bonne idée tant que la fonction que vous utilisez n'effectue pas déjà une sorte de multitraitement.
-
-
-
-Toutes ces fonctionnalités condensées en une seule méthode sont déjà assez étonnantes, mais il y a plus ! Avec `Dataset.map()` et `batched=True` vous pouvez modifier le nombre d'éléments dans votre jeu de données. Ceci est très utile dans de nombreuses situations où vous souhaitez créer plusieurs fonctionnalités d'entraînement à partir d'un exemple. Nous devrons le faire dans le cadre du prétraitement de plusieurs des tâches de traitement du langage naturel que nous entreprendrons dans le [Chapitre 7](/course/fr/chapter7).
-
-
-
-💡 En machine learning, un _exemple_ est généralement défini comme l'ensemble de _features_ que nous donnons au modèle. Dans certains contextes, ces caractéristiques seront l'ensemble des colonnes d'un `Dataset`, mais dans d'autres (comme ici et pour la réponse aux questions), plusieurs caractéristiques peuvent être extraites d'un seul exemple et appartenir à une seule colonne.
-
-
-
-Voyons comment cela fonctionne ! Ici, nous allons tokeniser nos exemples et les tronquer à une longueur maximale de 128 mais nous demanderons au *tokenizer* de renvoyer *tous* les morceaux des textes au lieu du premier. Cela peut être fait avec `return_overflowing_tokens=True` :
-
-```py
-def tokenize_and_split(examples):
- return tokenizer(
- examples["review"],
- truncation=True,
- max_length=128,
- return_overflowing_tokens=True,
- )
-```
-
-Testons cela sur un exemple avant d'utiliser `Dataset.map()` sur le jeu de données :
-
-```py
-result = tokenize_and_split(drug_dataset["train"][0])
-[len(inp) for inp in result["input_ids"]]
-```
-
-```python out
-[128, 49]
-```
-
-Notre premier exemple du jeu d’entraînement est devenu deux caractéristiques car il a été segmenté à plus que le nombre maximum de *tokens* que nous avons spécifié : le premier de longueur 128 et le second de longueur 49. Faisons maintenant cela pour tous les éléments du jeu de données !
-
-```py
-tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
-```
-
-```python out
-ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000
-```
-
-Oh non ! Cela n'a pas fonctionné ! Pourquoi ? L'examen du message d'erreur nous donne un indice : il y a une incompatibilité dans les longueurs de l'une des colonnes. L'une étant de longueur 1 463 et l'autre de longueur 1 000. Si vous avez consulté la [documentation](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasets.Dataset.map) de `Dataset.map()`, vous vous souvenez peut-être qu'il s'agit du nombre d'échantillons passés à la fonction que nous mappons. Ici, ces 1 000 exemples ont donné 1 463 nouvelles caractéristiques, entraînant une erreur de forme.
-
-Le problème est que nous essayons de mélanger deux jeux de données différents de tailles différentes : les colonnes `drug_dataset` auront un certain nombre d'exemples (les 1 000 dans notre erreur), mais le `tokenized_dataset` que nous construisons en aura plus (le 1 463 dans le message d'erreur). Cela ne fonctionne pas pour un `Dataset`, nous devons donc soit supprimer les colonnes de l'ancien jeu de données, soit leur donner la même taille que dans le nouveau jeu de données. Nous pouvons faire la première option avec l'argument `remove_columns` :
-
-```py
-tokenized_dataset = drug_dataset.map(
- tokenize_and_split, batched=True, remove_columns=drug_dataset["train"].column_names
-)
-```
-
-Maintenant, cela fonctionne sans erreur. Nous pouvons vérifier que notre nouveau jeu de données contient beaucoup plus d'éléments que le jeu de données d'origine en comparant les longueurs :
-
-```py
-len(tokenized_dataset["train"]), len(drug_dataset["train"])
-```
-
-```python out
-(206772, 138514)
-```
-
-Nous avons mentionné que nous pouvions également résoudre le problème de longueur non concordante en donnant aux anciennes colonnes la même taille que les nouvelles. Pour ce faire, nous avons besoin du champ `overflow_to_sample_mapping` que le *tokenizer* renvoie lorsque nous définissons `return_overflowing_tokens=True`. Il nous donne une correspondance entre un nouvel index de caractéristique et l'index de l'échantillon dont il est issu. Grâce à cela, nous pouvons associer chaque clé présente dans notre jeu de données d'origine à une liste de valeurs de la bonne taille en répétant les valeurs de chaque exemple autant de fois qu'il génère de nouvelles caractéristiques :
-
-```py
-def tokenize_and_split(examples):
- result = tokenizer(
- examples["review"],
- truncation=True,
- max_length=128,
- return_overflowing_tokens=True,
- )
- # Extract mapping between new and old indices
- sample_map = result.pop("overflow_to_sample_mapping")
- for key, values in examples.items():
- result[key] = [values[i] for i in sample_map]
- return result
-```
-
-Nous pouvons voir que cela fonctionne avec `Dataset.map()` sans que nous ayons besoin de supprimer les anciennes colonnes :
-
-```py
-tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
-tokenized_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
- num_rows: 206772
- })
- test: Dataset({
- features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
- num_rows: 68876
- })
-})
-```
-
-Nous obtenons le même nombre de caractéristiques d'entraînement qu'auparavant, mais ici nous avons conservé tous les anciens champs. Si vous en avez besoin pour un post-traitement après l'application de votre modèle, vous pouvez utiliser cette approche.
-
-Vous avez maintenant vu comment 🤗 *Datasets* peut être utilisé pour prétraiter un jeu de données de différentes manières. Bien que les fonctions de traitement de 🤗 *Datasets* couvrent la plupart de vos besoins, il peut arriver que vous deviez passer à Pandas pour accéder à des fonctionnalités plus puissantes, telles que `DataFrame.groupby()` ou des API de haut niveau pour la visualisation. Heureusement, 🤗 *Datasets* est conçu pour être interopérable avec des bibliothèques telles que Pandas, NumPy, PyTorch, TensorFlow et JAX. Voyons comment cela fonctionne.
-
-## De `Dataset` à `DataFrame` et vice versa
-
-
-
-Pour permettre la conversion entre diverses bibliothèques tierces, 🤗 *Datasets* fournit une fonction `Dataset.set_format()`. Cette fonction ne modifie que le _format de sortie_ du jeu de données. Vous pouvez donc facilement passer à un autre format sans affecter le _format de données_ sous-jacent, qui est Apache Arrow. Le formatage se fait sur place. Pour démontrer, convertissons notre jeu de données vers Pandas :
-
-```py
-drug_dataset.set_format("pandas")
-```
-
-Maintenant, lorsque nous accédons aux éléments du jeu de données, nous obtenons un `pandas.DataFrame` au lieu d'un dictionnaire :
-
-```py
-drug_dataset["train"][:3]
-```
-
-
-
-
- |
- patient_id |
- drugName |
- condition |
- review |
- rating |
- date |
- usefulCount |
- review_length |
-
-
-
-
- 0 |
- 95260 |
- Guanfacine |
- adhd |
- "My son is halfway through his fourth week of Intuniv..." |
- 8.0 |
- April 27, 2010 |
- 192 |
- 141 |
-
-
- 1 |
- 92703 |
- Lybrel |
- birth control |
- "I used to take another oral contraceptive, which had 21 pill cycle, and was very happy- very light periods, max 5 days, no other side effects..." |
- 5.0 |
- December 14, 2009 |
- 17 |
- 134 |
-
-
- 2 |
- 138000 |
- Ortho Evra |
- birth control |
- "This is my first time using any form of birth control..." |
- 8.0 |
- November 3, 2015 |
- 10 |
- 89 |
-
-
-
-
-Créons un `pandas.DataFrame` pour l'ensemble d'entraînement en sélectionnant tous les éléments de `drug_dataset["train"]` :
-
-```py
-train_df = drug_dataset["train"][:]
-```
-
-
-
-🚨 Sous le capot, `Dataset.set_format()` change le format de retour pour la méthode `__getitem__()`. Cela signifie que lorsque nous voulons créer un nouvel objet comme `train_df` à partir d'un `Dataset` au format `"pandas"`, nous devons découper tout le jeu de données pour obtenir un `pandas.DataFrame`. Vous pouvez vérifier par vous-même que le type de `drug_dataset["train"]` est `Dataset`, quel que soit le format de sortie.
-
-
-
-
-De là, nous pouvons utiliser toutes les fonctionnalités Pandas que nous voulons. Par exemple, nous pouvons faire un chaînage sophistiqué pour calculer la distribution de classe parmi les entrées `condition` :
-
-```py
-frequencies = (
- train_df["condition"]
- .value_counts()
- .to_frame()
- .reset_index()
- .rename(columns={"index": "condition", "condition": "frequency"})
-)
-frequencies.head()
-```
-
-
-
-
- |
- condition |
- frequency |
-
-
-
-
- 0 |
- birth control |
- 27655 |
-
-
- 1 |
- depression |
- 8023 |
-
-
- 2 |
- acne |
- 5209 |
-
-
- 3 |
- anxiety |
- 4991 |
-
-
- 4 |
- pain |
- 4744 |
-
-
-
-
-
-Et une fois que nous avons terminé notre analyse Pandas, nous pouvons toujours créer un nouvel objet `Dataset` en utilisant la fonction `Dataset.from_pandas()` comme suit :
-
-
-```py
-from datasets import Dataset
-
-freq_dataset = Dataset.from_pandas(frequencies)
-freq_dataset
-```
-
-```python out
-Dataset({
- features: ['condition', 'frequency'],
- num_rows: 819
-})
-```
-
-
-
-✏️ **Essayez !** Calculez la note moyenne par médicament et stockez le résultat dans un nouveau jeu de données.
-
-
-
-Ceci conclut notre visite des différentes techniques de prétraitement disponibles dans 🤗 *Datasets*. Pour compléter la section, créons un ensemble de validation pour préparer le jeu de données à l’entraînement d'un classifieur. Avant cela, nous allons réinitialiser le format de sortie de `drug_dataset` de `"pandas"` à `"arrow"` :
-
-```python
-drug_dataset.reset_format()
-```
-
-## Création d'un ensemble de validation
-
-Bien que nous ayons un jeu de test que nous pourrions utiliser pour l'évaluation, il est recommandé de ne pas toucher au jeu de test et de créer un jeu de validation séparé pendant le développement. Une fois que vous êtes satisfait des performances de vos modèles sur l'ensemble de validation, vous pouvez effectuer une dernière vérification d'intégrité sur l'ensemble test. Ce processus permet d'atténuer le risque de surentraînement sur le jeu de test et de déployer un modèle qui échoue sur des données du monde réel.
-
-🤗 *Datasets* fournit une fonction `Dataset.train_test_split()` basée sur la célèbre fonctionnalité de `scikit-learn`. Utilisons-la pour diviser notre ensemble d'entraînement `train` et `validation` (nous définissons l'argument `seed` pour la reproductibilité) :
-
-```py
-drug_dataset_clean = drug_dataset["train"].train_test_split(train_size=0.8, seed=42)
-# Rename the default "test" split to "validation"
-drug_dataset_clean["validation"] = drug_dataset_clean.pop("test")
-# Add the "test" set to our `DatasetDict`
-drug_dataset_clean["test"] = drug_dataset["test"]
-drug_dataset_clean
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
- num_rows: 110811
- })
- validation: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
- num_rows: 27703
- })
- test: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
- num_rows: 46108
- })
-})
-```
-
-Génial, nous avons maintenant préparé un jeu de données prêt pour l'entraînement de certains modèles ! Dans la [section 5](/course/fr/chapter5/5), nous vous montrerons comment télécharger des jeux de données sur le *Hub*. Mais pour l'instant, terminons notre analyse en examinant quelques façons d'enregistrer des jeux de données sur votre ordinateur local.
-
-## Enregistrer un jeu de données
-
-
-
-Bien que 🤗 *Datasets* mette en cache chaque jeu de données téléchargé et les opérations qui y sont effectuées, il y a des moments où vous voudrez enregistrer un jeu de données sur le disque (par exemple, au cas où le cache serait supprimé). Comme indiqué dans le tableau ci-dessous, 🤗 *Datasets* fournit trois fonctions principales pour enregistrer votre jeu de données dans différents formats :
-
-| Format de données | Fonction |
-| :---------------: | :----------------------: |
-| Arrow | `Dataset.save_to_disk()` |
-| CSV | `Dataset.to_csv()` |
-| JSON | `Dataset.to_json()` |
-
-Par exemple, enregistrons notre jeu de données nettoyé au format Arrow :
-
-```py
-drug_dataset_clean.save_to_disk("drug-reviews")
-```
-
-Cela créera un répertoire avec la structure suivante :
-
-```
-drug-reviews/
-├── dataset_dict.json
-├── test
-│ ├── dataset.arrow
-│ ├── dataset_info.json
-│ └── state.json
-├── train
-│ ├── dataset.arrow
-│ ├── dataset_info.json
-│ ├── indices.arrow
-│ └── state.json
-└── validation
- ├── dataset.arrow
- ├── dataset_info.json
- ├── indices.arrow
- └── state.json
-```
-
-où nous pouvons voir que chaque division est associée à sa propre table *dataset.arrow* et à certaines métadonnées dans *dataset_info.json* et *state.json*. Vous pouvez considérer le format Arrow comme un tableau sophistiqué de colonnes et de lignes optimisé pour la création d'applications hautes performances qui traitent et transportent de grands ensembles de données.
-
-Une fois le jeu de données enregistré, nous pouvons le charger en utilisant la fonction `load_from_disk()` comme suit :
-
-```py
-from datasets import load_from_disk
-
-drug_dataset_reloaded = load_from_disk("drug-reviews")
-drug_dataset_reloaded
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
- num_rows: 110811
- })
- validation: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
- num_rows: 27703
- })
- test: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
- num_rows: 46108
- })
-})
-```
-
-Pour les formats CSV et JSON, nous devons stocker chaque fractionnement dans un fichier séparé. Pour ce faire, vous pouvez parcourir les clés et les valeurs de l'objet `DatasetDict` :
-
-```py
-for split, dataset in drug_dataset_clean.items():
- dataset.to_json(f"drug-reviews-{split}.jsonl")
-```
-
-Cela enregistre chaque fractionnement au [format JSON Lines](https://jsonlines.org), où chaque ligne du jeu de données est stockée sous la forme d'une seule ligne de JSON. Voici à quoi ressemble le premier exemple :
-
-```py
-!head -n 1 drug-reviews-train.jsonl
-```
-
-```python out
-{"patient_id":141780,"drugName":"Escitalopram","condition":"depression","review":"\"I seemed to experience the regular side effects of LEXAPRO, insomnia, low sex drive, sleepiness during the day. I am taking it at night because my doctor said if it made me tired to take it at night. I assumed it would and started out taking it at night. Strange dreams, some pleasant. I was diagnosed with fibromyalgia. Seems to be helping with the pain. Have had anxiety and depression in my family, and have tried quite a few other medications that haven't worked. Only have been on it for two weeks but feel more positive in my mind, want to accomplish more in my life. Hopefully the side effects will dwindle away, worth it to stick with it from hearing others responses. Great medication.\"","rating":9.0,"date":"May 29, 2011","usefulCount":10,"review_length":125}
-```
-
-Nous pouvons ensuite utiliser les techniques de [section 2](/course/fr/chapter5/2) pour charger les fichiers JSON comme suit :
-
-```py
-data_files = {
- "train": "drug-reviews-train.jsonl",
- "validation": "drug-reviews-validation.jsonl",
- "test": "drug-reviews-test.jsonl",
-}
-drug_dataset_reloaded = load_dataset("json", data_files=data_files)
-```
-
-Et c'est tout pour notre excursion dans la manipulation des données avec 🤗 *Datasets* ! Maintenant que nous disposons d'un ensemble de données nettoyé pour entraîner un modèle, voici quelques idées que vous pouvez essayer :
-
-1. Utilisez les techniques du [Chapitre 3](/course/fr/chapter3) pour entraîner un classifieur capable de prédire l'état du patient en fonction de l'examen du médicament.
-2. Utilisez le pipeline `summarization` du [Chapitre 1](/course/fr/chapter1) pour générer des résumés des révisions.
-
-Ensuite, nous verrons comment 🤗 *Datasets* peut vous permettre de travailler avec d'énormes jeux de données sans faire exploser votre ordinateur portable !
+# Il est temps de trancher et de découper
+
+
+
+La plupart du temps, les données avec lesquelles vous travaillez ne sont pas parfaitement préparées pour l’entraînements de modèles. Dans cette section, nous allons explorer les différentes fonctionnalités fournies par 🤗 *Datasets* pour nettoyer vos jeux de données.
+
+
+
+## Trancher et découper nos données
+
+Semblable à Pandas, 🤗 *Datasets* fournit plusieurs fonctions pour manipuler le contenu des objets `Dataset` et `DatasetDict`. Nous avons déjà rencontré la méthode `Dataset.map()` dans le [chapitre 3](/course/fr/chapter3) et dans cette section nous allons explorer certaines des autres fonctions à notre disposition.
+
+Pour cet exemple, nous utiliserons le [*Drug Review Dataset*](https://archive.ics.uci.edu/ml/datasets/Drug+Review+Dataset+%28Drugs.com%29) qui est hébergé sur [*UC Irvine Machine Learning Repository*](https://archive.ics.uci.edu/ml/index.php) et contenant des avis de patients sur divers médicaments ainsi que la condition traitée et une note de 10 étoiles sur la satisfaction du patient.
+
+Nous devons d'abord télécharger et extraire les données, ce qui peut être fait avec les commandes `wget` et `unzip` :
+
+```py
+!wget "https://archive.ics.uci.edu/ml/machine-learning-databases/00462/drugsCom_raw.zip"
+!unzip drugsCom_raw.zip
+```
+
+Étant donné que TSV n'est qu'une variante de CSV qui utilise des tabulations au lieu de virgules comme séparateurs, nous pouvons charger ces fichiers en utilisant le script de chargement `csv` et en spécifiant l'argument `delimiter` dans la fonction `load_dataset()` comme suit :
+
+```py
+from datasets import load_dataset
+
+data_files = {"train": "drugsComTrain_raw.tsv", "test": "drugsComTest_raw.tsv"}
+# \t is the tab character in Python
+drug_dataset = load_dataset("csv", data_files=data_files, delimiter="\t")
+```
+
+Une bonne pratique lors de toute sorte d'analyse de données consiste à prélever un petit échantillon aléatoire pour avoir une idée rapide du type de données avec lesquelles vous travaillez. Dans 🤗 *Datasets*, nous pouvons créer un échantillon aléatoire en enchaînant les fonctions `Dataset.shuffle()` et `Dataset.select()` :
+
+```py
+drug_sample = drug_dataset["train"].shuffle(seed=42).select(range(1000))
+# Un coup d'œil sur les premiers exemples
+drug_sample[:3]
+```
+
+```python out
+{'Unnamed: 0': [87571, 178045, 80482],
+ 'drugName': ['Naproxen', 'Duloxetine', 'Mobic'],
+ 'condition': ['Gout, Acute', 'ibromyalgia', 'Inflammatory Conditions'],
+ #['Goutte aiguë', 'ibromyalgie', 'Affections inflammatoires']
+ 'review': ['"like the previous person mention, I'm a strong believer of aleve, it works faster for my gout than the prescription meds I take. No more going to the doctor for refills.....Aleve works!"',
+ # comme la personne précédente l'a mentionné, je suis un fervent partisan de l'aleve, il fonctionne plus rapidement pour ma goutte que les médicaments sur ordonnance que je prends. Je n'ai plus besoin d'aller chez le médecin pour des renouvellements.....Aleve fonctionne !"
+ '"I have taken Cymbalta for about a year and a half for fibromyalgia pain. It is great\r\nas a pain reducer and an anti-depressant, however, the side effects outweighed \r\nany benefit I got from it. I had trouble with restlessness, being tired constantly,\r\ndizziness, dry mouth, numbness and tingling in my feet, and horrible sweating. I am\r\nbeing weaned off of it now. Went from 60 mg to 30mg and now to 15 mg. I will be\r\noff completely in about a week. The fibro pain is coming back, but I would rather deal with it than the side effects."',
+ # J'ai pris du Cymbalta pendant environ un an et demi pour des douleurs de la fibromyalgie. C'est un excellent analgésique et un antidépresseur, mais les effets secondaires l'ont emporté sur tous les avantages que j'en ai tirés. J'ai eu des problèmes d'agitation, de fatigue constante, de vertiges, de bouche sèche, d'engourdissement, de picotements dans les pieds, et de transpiration horrible. Je suis en train de m'en sevrer maintenant. Je suis passée de 60 mg à 30 mg et maintenant à 15 mg. Je l'arrêterai complètement dans environ une semaine. La douleur de la fibrose revient, mais je préfère la supporter plutôt que les effets secondaires.
+ '"I have been taking Mobic for over a year with no side effects other than an elevated blood pressure. I had severe knee and ankle pain which completely went away after taking Mobic. I attempted to stop the medication however pain returned after a few days."'],
+ # J'ai pris Mobic pendant plus d'un an sans effets secondaires autres qu'une pression sanguine élevée. J'avais de fortes douleurs au genou et à la cheville qui ont complètement disparu après avoir pris Mobic. J'ai essayé d'arrêter le médicament mais la douleur est revenue après quelques jours."
+ 'rating': [9.0, 3.0, 10.0],
+ 'date': ['September 2, 2015', 'November 7, 2011', 'June 5, 2013'],
+ #['2 septembre 2015', '7 novembre 2011', '5 juin 2013']
+ 'usefulCount': [36, 13, 128]}
+```
+
+Notez que nous avons corrigé la graine dans `Dataset.shuffle()` à des fins de reproductibilité. `Dataset.select()` attend un itérable d'indices, nous avons donc passé `range(1000)` pour récupérer les 1 000 premiers exemples du jeu de données mélangé. À partir de cet échantillon, nous pouvons déjà voir quelques bizarreries dans notre jeu de données :
+
+* la colonne `Unnamed: 0` ressemble étrangement à un identifiant anonyme pour chaque patient,
+* la colonne `condition` comprend un mélange d'étiquettes en majuscules et en minuscules,
+* les avis sont de longueur variable et contiennent un mélange de séparateurs de lignes Python (`\r\n`) ainsi que des codes de caractères HTML comme `&\#039;`.
+
+Voyons comment nous pouvons utiliser 🤗 *Datasets* pour traiter chacun de ces problèmes. Pour tester l'hypothèse de l'ID patient pour la colonne `Unnamed : 0`, nous pouvons utiliser la fonction `Dataset.unique()` pour vérifier que le nombre d'ID correspond au nombre de lignes dans chaque division :
+
+```py
+for split in drug_dataset.keys():
+ assert len(drug_dataset[split]) == len(drug_dataset[split].unique("Unnamed: 0"))
+```
+
+Cela semble confirmer notre hypothèse, alors nettoyons un peu en renommant la colonne `Unnamed: 0` en quelque chose d'un peu plus interprétable. Nous pouvons utiliser la fonction `DatasetDict.rename_column()` pour renommer la colonne sur les deux divisions en une seule fois :
+
+```py
+drug_dataset = drug_dataset.rename_column(
+ original_column_name="Unnamed: 0", new_column_name="patient_id"
+)
+drug_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
+ num_rows: 161297
+ })
+ test: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
+ num_rows: 53766
+ })
+})
+```
+
+
+
+✏️ **Essayez !** Utilisez la fonction ` Dataset.unique()` pour trouver le nombre de médicaments et de conditions uniques dans les échantillons d'entraînement et de test.
+
+
+
+Ensuite, normalisons toutes les étiquettes `condition` en utilisant `Dataset.map()`. Comme nous l'avons fait avec la tokenisation dans le [chapitre 3](/course/fr/chapter3), nous pouvons définir une fonction simple qui peut être appliquée sur toutes les lignes de chaque division dans `drug_dataset` :
+
+```py
+def lowercase_condition(example):
+ return {"condition": example["condition"].lower()}
+
+
+drug_dataset.map(lowercase_condition)
+```
+
+```python out
+AttributeError: 'NoneType' object has no attribute 'lower'
+```
+
+Oh non, nous rencontrons un problème avec notre fonction ! À partir de l'erreur, nous pouvons déduire que certaines des entrées de la colonne `condition` sont `None` ne pouvant donc pas être mises en minuscules car ce ne sont pas des chaînes. Supprimons ces lignes en utilisant `Dataset.filter()`, qui fonctionne de manière similaire à `Dataset.map()` et attend une fonction qui reçoit un seul exemple issu du jeu de données. Au lieu d'écrire une fonction explicite comme :
+
+```py
+def filter_nones(x):
+ return x["condition"] is not None
+```
+
+puis exécuter `drug_dataset.filter(filter_nones)`, nous pouvons le faire en une seule ligne en utilisant une _fonction lambda_. En Python, les fonctions lambda sont de petites fonctions que vous pouvez définir sans les nommer explicitement. Ils prennent la forme générale :
+
+```
+lambda :
+```
+
+où `lambda` est l'un des [mots clés](https://docs.python.org/3/reference/lexical_analysis.html#keywords) spéciaux de Python, `` est une liste/ensemble de valeurs séparées par des virgules qui définissent les entrées de la fonction et `` représente les opérations que vous souhaitez exécuter. Par exemple, nous pouvons définir une simple fonction lambda qui met au carré un nombre comme suit :
+
+```
+lambda x : x * x
+```
+
+Pour appliquer cette fonction à une entrée, nous devons l'envelopper ainsi que l'entrée entre parenthèses :
+
+```py
+(lambda x: x * x)(3)
+```
+
+```python out
+9
+```
+
+De même, nous pouvons définir des fonctions lambda avec plusieurs arguments en les séparant par des virgules. Par exemple, nous pouvons calculer l'aire d'un triangle comme suit :
+
+```py
+(lambda base, height: 0.5 * base * height)(4, 8)
+```
+
+```python out
+16.0
+```
+
+Les fonctions lambda sont pratiques lorsque vous souhaitez définir de petites fonctions à usage unique (pour plus d'informations à leur sujet, nous vous recommandons de lire l'excellent [tutoriel Real Python](https://realpython.com/python-lambda/) d'André Burgaud) . Dans le contexte de la bibliothèque 🤗 *Datasets*, nous pouvons utiliser des fonctions lambda pour définir des opérations simples de « mappage » et de filtrage. Utilisons cette astuce pour éliminer les entrées `None` dans notre jeu de données :
+
+```py
+drug_dataset = drug_dataset.filter(lambda x: x["condition"] is not None)
+```
+
+Avec les entrées `None` supprimées, nous pouvons normaliser notre colonne `condition` :
+
+```py
+drug_dataset = drug_dataset.map(lowercase_condition)
+# Vérification que la mise en minuscule a fonctionné
+drug_dataset["train"]["condition"][:3]
+```
+
+```python out
+['left ventricular dysfunction', 'adhd', 'birth control']
+```
+
+Ça marche ! Maintenant que nous avons nettoyé les étiquettes, examinons le nettoyage des avis eux-mêmes.
+
+## Création de nouvelles colonnes
+
+Chaque fois que vous avez affaire à des avis de clients, une bonne pratique consiste à vérifier le nombre de mots dans chaque avis. Une critique peut être un simple mot comme « Génial ! » ou un essai complet avec des milliers de mots. Selon le cas d'usage, vous devrez gérer ces extrêmes différemment. Pour calculer le nombre de mots dans chaque révision, nous utiliserons une heuristique approximative basée sur la division de chaque texte par des espaces.
+
+Définissons une fonction simple qui compte le nombre de mots dans chaque avis :
+
+```py
+def compute_review_length(example):
+ return {"review_length": len(example["review"].split())}
+```
+
+Contrairement à notre fonction `lowercase_condition()`, `compute_review_length()` renvoie un dictionnaire dont la clé ne correspond pas à l'un des noms de colonne du jeu de données. Dans ce cas, lorsque `compute_review_length()` est passé à `Dataset.map()`, il est appliqué à toutes les lignes du jeu de données pour créer une nouvelle colonne `review_length` :
+
+```py
+drug_dataset = drug_dataset.map(compute_review_length)
+# Inspecter le premier exemple d'entraînement
+drug_dataset["train"][0]
+```
+
+```python out
+{'patient_id': 206461,
+ 'drugName': 'Valsartan',
+ 'condition': 'left ventricular dysfunction', # dysfonctionnement du ventricule gauche
+ 'review': '"It has no side effect, I take it in combination of Bystolic 5 Mg and Fish Oil"',
+ # Il n'a aucun effet secondaire, je le prends en combinaison avec Bystolic 5 mg et de l'huile de poisson.
+ 'rating': 9.0,
+ 'date': 'May 20, 2012', # 20 mai 2012
+ 'usefulCount': 27,
+ 'review_length': 17}
+```
+
+Comme prévu, nous pouvons voir qu'une colonne `review_length` a été ajoutée à notre jeu d'entraînement. Nous pouvons trier cette nouvelle colonne avec `Dataset.sort()` pour voir à quoi ressemblent les valeurs extrêmes :
+
+```py
+drug_dataset["train"].sort("review_length")[:3]
+```
+
+```python out
+{'patient_id': [103488, 23627, 20558],
+ 'drugName': ['Loestrin 21 1 / 20', 'Chlorzoxazone', 'Nucynta'],
+ 'condition': ['birth control', 'muscle spasm', 'pain'],
+ # contraception, spasme musculaire, douleur.
+ 'review': ['"Excellent."', '"useless"', '"ok"'], # Excellent, inutile, ok
+ 'rating': [10.0, 1.0, 6.0],
+ 'date': ['November 4, 2008', 'March 24, 2017', 'August 20, 2016'],
+ # 4 novembre 2008, 24 mars 2017, 20 août 2016
+ 'usefulCount': [5, 2, 10],
+ 'review_length': [1, 1, 1]}
+```
+
+Comme nous le soupçonnions, certaines critiques ne contiennent qu'un seul mot, ce qui, bien que cela puisse convenir à l'analyse des sentiments, n’est pas informatif si nous voulons prédire la condition.
+
+
+
+🙋 Une autre façon d'ajouter de nouvelles colonnes à un jeu de données consiste à utiliser la fonction `Dataset.add_column()`. Cela vous permet de donner la colonne sous forme de liste Python ou de tableau NumPy et peut être utile dans les situations où `Dataset.map()` n'est pas bien adapté à votre analyse.
+
+
+
+Utilisons la fonction `Dataset.filter()` pour supprimer les avis contenant moins de 30 mots. De la même manière que nous l'avons fait avec la colonne `condition`, nous pouvons filtrer les avis très courts en exigeant que les avis aient une longueur supérieure à ce seuil :
+
+```py
+drug_dataset = drug_dataset.filter(lambda x: x["review_length"] > 30)
+print(drug_dataset.num_rows)
+```
+
+```python out
+{'train': 138514, 'test': 46108}
+```
+
+Comme vous pouvez le constater, cela a supprimé environ 15 % des avis de nos jeux d'entraînement et de test d'origine.
+
+
+
+✏️ **Essayez !** Utilisez la fonction `Dataset.sort()` pour inspecter les avis avec le plus grand nombre de mots. Consultez la [documentation](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasets.Dataset.sort) pour voir quel argument vous devez utiliser pour trier les avis par longueur dans l'ordre décroissant.
+
+
+
+La dernière chose à laquelle nous devons faire face est la présence de caractères HTML dans nos avis. Nous pouvons utiliser le module `html` de Python pour supprimer ces caractères, comme ceci :
+
+```py
+import html
+
+text = "I'm a transformer called BERT"
+html.unescape(text)
+```
+
+```python out
+"I'm a transformer called BERT"
+```
+
+Nous utilisons `Dataset.map()` pour démasquer tous les caractères HTML de notre corpus :
+
+```python
+drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])})
+```
+
+Comme vous pouvez le voir, la méthode `Dataset.map()` est très utile pour le traitement des données. Et nous n'avons même pas effleuré la surface de tout ce qu'elle peut faire !
+
+## Les superpouvoirs de la méthode `map()`
+
+La méthode `Dataset.map()` prend un argument `batched` qui, s'il est défini sur `True`, l'amène à envoyer un batch d'exemples à la fonction *map* en une seule fois (la taille du batch est configurable mais est fixé par défaut à 1 000). Par exemple, la fonction `map()` précédente qui supprime tout le code HTML prend un peu de temps à s'exécuter (vous pouvez lire le temps pris dans les barres de progression). On peut accélérer cela en traitant plusieurs éléments en même temps à l'aide d'une compréhension de liste.
+
+Lorsque vous spécifiez `batched=True`, la fonction reçoit un dictionnaire avec les champs du jeu de données mais chaque valeur est maintenant une _liste de valeurs_ et non plus une seule valeur. La valeur retournée par `Dataset.map()` devrait être la même : un dictionnaire avec les champs que nous voulons mettre à jour ou ajouter à notre jeu de données, et une liste de valeurs. Par exemple, voici une autre façon de supprimer tous les caractères HTML, mais en utilisant `batched=True` :
+
+```python
+new_drug_dataset = drug_dataset.map(
+ lambda x: {"review": [html.unescape(o) for o in x["review"]]}, batched=True
+)
+```
+
+Si vous exécutez ce code dans un *notebook*, vous verrez que cette commande s'exécute beaucoup plus rapidement que la précédente. Et ce n'est pas parce que nos critiques ont déjà été scannées au format HTML. Si vous ré-exécutez l'instruction de la section précédente (sans `batched=True`), cela prendra le même temps qu'avant. En effet, les compréhensions de liste sont généralement plus rapides que l'exécution du même code dans une boucle `for` et nous gagnons également en performances en accédant à de nombreux éléments en même temps au lieu d'un par un.
+
+L'utilisation de `Dataset.map()` avec `batched=True` est essentielle pour les *tokenizers rapides* que nous rencontrerons dans le [chapitre 6](/course/fr/chapter6) et qui peuvent rapidement tokeniser de grandes listes de textes. Par exemple, pour tokeniser toutes les critiques de médicaments avec un *tokenizer* rapide nous pouvons utiliser une fonction comme celle-ci :
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+
+
+def tokenize_function(examples):
+ return tokenizer(examples["review"], truncation=True)
+```
+
+Comme vous l'avez vu dans le [chapitre 3](/course/fr/chapter3), nous pouvons passer un ou plusieurs exemples au *tokenizer*. Nous pouvons donc utiliser cette fonction avec ou sans `batched=True`. Profitons-en pour comparer les performances des différentes options. Dans un *notebook*, vous pouvez chronométrer une instruction d'une ligne en ajoutant `%time` avant la ligne de code que vous souhaitez mesurer :
+
+```python no-format
+%time tokenized_dataset = drug_dataset.map(tokenize_function, batched=True)
+```
+
+Vous pouvez également chronométrer une cellule entière en mettant `%%time` au début de la cellule. Sur le matériel sur lequel nous avons exécuté cela, cela affichait 10,8 s pour cette instruction (c'est le nombre écrit après "Wall time").
+
+
+
+✏️ **Essayez !** Exécutez la même instruction avec et sans `batched=True`, puis essayez-le avec un *tokenizer* lent (ajoutez `use_fast=False` dans la méthode `AutoTokenizer.from_pretrained()`) afin que vous puissiez voir quels temps vous obtenez sur votre matériel.
+
+
+
+Voici les résultats que nous avons obtenus avec et sans *batching*, avec un *tokenizer* rapide et un lent :
+
+Options | *Tokenizer* rapide | *Tokenizer* lent
+:--------------:|:----------------:|:-----------------:
+`batched=True` | 10.8s | 4min41s
+`batched=False` | 59.2s | 5min3s
+
+Cela signifie que l'utilisation d'un *tokenizer* rapide avec l'option `batched=True` est 30 fois plus rapide que son homologue lent sans batch. C'est vraiment incroyable ! C'est la raison principale pour laquelle les *tokenizers* rapides sont la valeur par défaut lors de l'utilisation de `AutoTokenizer` (et pourquoi ils sont appelés « rapides »). Ils sont capables d'atteindre une telle vitesse car en coulisses le code de tokenisation est exécuté en Rust qui est un langage facilitant la parallélisation de l'exécution du code.
+
+La parallélisation est également la raison du gain de vitesse de près de 6 fois obtenue par le *tokenizer* rapide avec batch. Vous ne pouvez pas paralléliser une seule opération de tokenisation, mais lorsque vous souhaitez tokeniser de nombreux textes en même temps, vous pouvez simplement répartir l'exécution sur plusieurs processus. Chacun responsable de ses propres textes.
+
+`Dataset.map()` possède aussi ses propres capacités de parallélisation. Comme elles ne sont pas soutenus par Rust, un *tokenizer* lent ne peut pas rattraper un rapide mais cela peut toujours être utile (surtout si vous utilisez un *tokenizer* qui n'a pas de version rapide). Pour activer le multitraitement, utilisez l'argument `num_proc` et spécifiez le nombre de processus à utiliser dans votre appel à `Dataset.map()` :
+
+```py
+slow_tokenizer = AutoTokenizer.from_pretrained("bert-base-cased", use_fast=False)
+
+
+def slow_tokenize_function(examples):
+ return slow_tokenizer(examples["review"], truncation=True)
+
+
+tokenized_dataset = drug_dataset.map(slow_tokenize_function, batched=True, num_proc=8)
+```
+
+Vous pouvez faire des tests pour déterminer le nombre optimal de processus à utiliser. Dans notre cas 8 semble produire le meilleur gain de vitesse. Voici les chiffres que nous avons obtenus avec et sans multitraitement :
+
+Options | *Tokenizer* rapide | *Tokenizer* lent
+:----------------------------:|:----------------:|:---------------:
+`batched=True` | 10.8s | 4min41s
+`batched=False` | 59.2s | 5min3s
+`batched=True`, `num_proc=8` | 6.52s | 41.3s
+`batched=False`, `num_proc=8` | 9.49s | 45.2s
+
+Ce sont des résultats beaucoup plus raisonnables pour le *tokenizer* lent mais les performances du *tokenizer* rapide ont également été considérablement améliorées. Notez, cependant, que ce ne sera pas toujours le cas : pour des valeurs de `num_proc` autres que 8, nos tests ont montré qu'il était plus rapide d'utiliser `batched=True` sans cette option. En général, nous ne recommandons pas d'utiliser le multitraitement pour les *tokenizers* rapides avec `batched=True`.
+
+
+
+Utiliser `num_proc` pour accélérer votre traitement est généralement une bonne idée tant que la fonction que vous utilisez n'effectue pas déjà une sorte de multitraitement.
+
+
+
+Toutes ces fonctionnalités condensées en une seule méthode sont déjà assez étonnantes, mais il y a plus ! Avec `Dataset.map()` et `batched=True` vous pouvez modifier le nombre d'éléments dans votre jeu de données. Ceci est très utile dans de nombreuses situations où vous souhaitez créer plusieurs fonctionnalités d'entraînement à partir d'un exemple. Nous devrons le faire dans le cadre du prétraitement de plusieurs des tâches de traitement du langage naturel que nous entreprendrons dans le [chapitre 7](/course/fr/chapter7).
+
+
+
+💡 En apprentissage automatique, un _exemple_ est généralement défini comme l'ensemble de _features_ que nous donnons au modèle. Dans certains contextes, ces caractéristiques seront l'ensemble des colonnes d'un `Dataset`, mais dans d'autres (comme ici et pour la réponse aux questions), plusieurs caractéristiques peuvent être extraites d'un seul exemple et appartenir à une seule colonne.
+
+
+
+Voyons comment cela fonctionne ! Ici, nous allons tokeniser nos exemples et les tronquer à une longueur maximale de 128 mais nous demanderons au *tokenizer* de renvoyer *tous* les morceaux des textes au lieu du premier. Cela peut être fait avec `return_overflowing_tokens=True` :
+
+```py
+def tokenize_and_split(examples):
+ return tokenizer(
+ examples["review"],
+ truncation=True,
+ max_length=128,
+ return_overflowing_tokens=True,
+ )
+```
+
+Testons cela sur un exemple avant d'utiliser `Dataset.map()` sur le jeu de données :
+
+```py
+result = tokenize_and_split(drug_dataset["train"][0])
+[len(inp) for inp in result["input_ids"]]
+```
+
+```python out
+[128, 49]
+```
+
+Notre premier exemple du jeu d’entraînement est devenu deux caractéristiques car il a été segmenté à plus que le nombre maximum de *tokens* que nous avons spécifié : le premier de longueur 128 et le second de longueur 49. Faisons maintenant cela pour tous les éléments du jeu de données !
+
+```py
+tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
+```
+
+```python out
+ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000
+```
+
+Oh non ! Cela n'a pas fonctionné ! Pourquoi ? L'examen du message d'erreur nous donne un indice : il y a une incompatibilité dans les longueurs de l'une des colonnes. L'une étant de longueur 1 463 et l'autre de longueur 1 000. Si vous avez consulté la [documentation](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasets.Dataset.map) de `Dataset.map()`, vous vous souvenez peut-être qu'il s'agit du nombre d'échantillons passés à la fonction que nous mappons. Ici, ces 1 000 exemples ont donné 1 463 nouvelles caractéristiques, entraînant une erreur de forme.
+
+Le problème est que nous essayons de mélanger deux jeux de données différents de tailles différentes : les colonnes `drug_dataset` auront un certain nombre d'exemples (les 1 000 dans notre erreur), mais le `tokenized_dataset` que nous construisons en aura plus (le 1 463 dans le message d'erreur). Cela ne fonctionne pas pour un `Dataset`, nous devons donc soit supprimer les colonnes de l'ancien jeu de données, soit leur donner la même taille que dans le nouveau jeu de données. Nous pouvons faire la première option avec l'argument `remove_columns` :
+
+```py
+tokenized_dataset = drug_dataset.map(
+ tokenize_and_split, batched=True, remove_columns=drug_dataset["train"].column_names
+)
+```
+
+Maintenant, cela fonctionne sans erreur. Nous pouvons vérifier que notre nouveau jeu de données contient beaucoup plus d'éléments que le jeu de données d'origine en comparant les longueurs :
+
+```py
+len(tokenized_dataset["train"]), len(drug_dataset["train"])
+```
+
+```python out
+(206772, 138514)
+```
+
+Nous avons mentionné que nous pouvions également résoudre le problème de longueur non concordante en donnant aux anciennes colonnes la même taille que les nouvelles. Pour ce faire, nous avons besoin du champ `overflow_to_sample_mapping` que le *tokenizer* renvoie lorsque nous définissons `return_overflowing_tokens=True`. Il nous donne une correspondance entre un nouvel index de caractéristique et l'index de l'échantillon dont il est issu. Grâce à cela, nous pouvons associer chaque clé présente dans notre jeu de données d'origine à une liste de valeurs de la bonne taille en répétant les valeurs de chaque exemple autant de fois qu'il génère de nouvelles caractéristiques :
+
+```py
+def tokenize_and_split(examples):
+ result = tokenizer(
+ examples["review"],
+ truncation=True,
+ max_length=128,
+ return_overflowing_tokens=True,
+ )
+ # Extract mapping between new and old indices
+ sample_map = result.pop("overflow_to_sample_mapping")
+ for key, values in examples.items():
+ result[key] = [values[i] for i in sample_map]
+ return result
+```
+
+Nous pouvons voir que cela fonctionne avec `Dataset.map()` sans que nous ayons besoin de supprimer les anciennes colonnes :
+
+```py
+tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
+tokenized_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
+ num_rows: 206772
+ })
+ test: Dataset({
+ features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
+ num_rows: 68876
+ })
+})
+```
+
+Nous obtenons le même nombre de caractéristiques d'entraînement qu'auparavant, mais ici nous avons conservé tous les anciens champs. Si vous en avez besoin pour un post-traitement après l'application de votre modèle, vous pouvez utiliser cette approche.
+
+Vous avez maintenant vu comment 🤗 *Datasets* peut être utilisé pour prétraiter un jeu de données de différentes manières. Bien que les fonctions de traitement de 🤗 *Datasets* couvrent la plupart de vos besoins, il peut arriver que vous deviez passer à Pandas pour accéder à des fonctionnalités plus puissantes, telles que `DataFrame.groupby()` ou des API de haut niveau pour la visualisation. Heureusement, 🤗 *Datasets* est conçu pour être interopérable avec des bibliothèques telles que Pandas, NumPy, PyTorch, TensorFlow et JAX. Voyons comment cela fonctionne.
+
+## De `Dataset` à `DataFrame` et vice versa
+
+
+
+Pour permettre la conversion entre diverses bibliothèques tierces, 🤗 *Datasets* fournit une fonction `Dataset.set_format()`. Cette fonction ne modifie que le _format de sortie_ du jeu de données. Vous pouvez donc facilement passer à un autre format sans affecter le _format de données_ sous-jacent, qui est Apache Arrow. Le formatage se fait sur place. Pour démontrer, convertissons notre jeu de données vers Pandas :
+
+```py
+drug_dataset.set_format("pandas")
+```
+
+Maintenant, lorsque nous accédons aux éléments du jeu de données, nous obtenons un `pandas.DataFrame` au lieu d'un dictionnaire :
+
+```py
+drug_dataset["train"][:3]
+```
+
+
+
+
+ |
+ patient_id |
+ drugName |
+ condition |
+ review |
+ rating |
+ date |
+ usefulCount |
+ review_length |
+
+
+
+
+ 0 |
+ 95260 |
+ Guanfacine |
+ adhd |
+ "My son is halfway through his fourth week of Intuniv..." |
+ 8.0 |
+ April 27, 2010 |
+ 192 |
+ 141 |
+
+
+ 1 |
+ 92703 |
+ Lybrel |
+ birth control |
+ "I used to take another oral contraceptive, which had 21 pill cycle, and was very happy- very light periods, max 5 days, no other side effects..." |
+ 5.0 |
+ December 14, 2009 |
+ 17 |
+ 134 |
+
+
+ 2 |
+ 138000 |
+ Ortho Evra |
+ birth control |
+ "This is my first time using any form of birth control..." |
+ 8.0 |
+ November 3, 2015 |
+ 10 |
+ 89 |
+
+
+
+
+Créons un `pandas.DataFrame` pour l'ensemble d'entraînement en sélectionnant tous les éléments de `drug_dataset["train"]` :
+
+```py
+train_df = drug_dataset["train"][:]
+```
+
+
+
+🚨 Sous le capot, `Dataset.set_format()` change le format de retour pour la méthode `__getitem__()`. Cela signifie que lorsque nous voulons créer un nouvel objet comme `train_df` à partir d'un `Dataset` au format `"pandas"`, nous devons découper tout le jeu de données pour obtenir un `pandas.DataFrame`. Vous pouvez vérifier par vous-même que le type de `drug_dataset["train"]` est `Dataset`, quel que soit le format de sortie.
+
+
+
+
+De là, nous pouvons utiliser toutes les fonctionnalités Pandas que nous voulons. Par exemple, nous pouvons faire un chaînage sophistiqué pour calculer la distribution de classe parmi les entrées `condition` :
+
+```py
+frequencies = (
+ train_df["condition"]
+ .value_counts()
+ .to_frame()
+ .reset_index()
+ .rename(columns={"index": "condition", "condition": "frequency"})
+)
+frequencies.head()
+```
+
+
+
+
+ |
+ condition |
+ frequency |
+
+
+
+
+ 0 |
+ birth control |
+ 27655 |
+
+
+ 1 |
+ depression |
+ 8023 |
+
+
+ 2 |
+ acne |
+ 5209 |
+
+
+ 3 |
+ anxiety |
+ 4991 |
+
+
+ 4 |
+ pain |
+ 4744 |
+
+
+
+
+
+Et une fois que nous avons terminé notre analyse Pandas, nous pouvons toujours créer un nouvel objet `Dataset` en utilisant la fonction `Dataset.from_pandas()` comme suit :
+
+
+```py
+from datasets import Dataset
+
+freq_dataset = Dataset.from_pandas(frequencies)
+freq_dataset
+```
+
+```python out
+Dataset({
+ features: ['condition', 'frequency'],
+ num_rows: 819
+})
+```
+
+
+
+✏️ **Essayez !** Calculez la note moyenne par médicament et stockez le résultat dans un nouveau jeu de données.
+
+
+
+Ceci conclut notre visite des différentes techniques de prétraitement disponibles dans 🤗 *Datasets*. Pour compléter la section, créons un ensemble de validation pour préparer le jeu de données à l’entraînement d'un classifieur. Avant cela, nous allons réinitialiser le format de sortie de `drug_dataset` de `"pandas"` à `"arrow"` :
+
+```python
+drug_dataset.reset_format()
+```
+
+## Création d'un ensemble de validation
+
+Bien que nous ayons un jeu de test que nous pourrions utiliser pour l'évaluation, il est recommandé de ne pas toucher au jeu de test et de créer un jeu de validation séparé pendant le développement. Une fois que vous êtes satisfait des performances de vos modèles sur l'ensemble de validation, vous pouvez effectuer une dernière vérification d'intégrité sur l'ensemble test. Ce processus permet d'atténuer le risque de surentraînement sur le jeu de test et de déployer un modèle qui échoue sur des données du monde réel.
+
+🤗 *Datasets* fournit une fonction `Dataset.train_test_split()` basée sur la célèbre fonctionnalité de `scikit-learn`. Utilisons-la pour diviser notre ensemble d'entraînement `train` et `validation` (nous définissons l'argument `seed` pour la reproductibilité) :
+
+```py
+drug_dataset_clean = drug_dataset["train"].train_test_split(train_size=0.8, seed=42)
+# Rename the default "test" split to "validation"
+drug_dataset_clean["validation"] = drug_dataset_clean.pop("test")
+# Add the "test" set to our `DatasetDict`
+drug_dataset_clean["test"] = drug_dataset["test"]
+drug_dataset_clean
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
+ num_rows: 110811
+ })
+ validation: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
+ num_rows: 27703
+ })
+ test: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
+ num_rows: 46108
+ })
+})
+```
+
+Génial, nous avons maintenant préparé un jeu de données prêt pour l'entraînement de certains modèles ! Dans la [section 5](/course/fr/chapter5/5), nous vous montrerons comment télécharger des jeux de données sur le *Hub*. Mais pour l'instant, terminons notre analyse en examinant quelques façons d'enregistrer des jeux de données sur votre ordinateur local.
+
+## Enregistrer un jeu de données
+
+
+
+Bien que 🤗 *Datasets* mette en cache chaque jeu de données téléchargé et les opérations qui y sont effectuées, il y a des moments où vous voudrez enregistrer un jeu de données sur le disque (par exemple, au cas où le cache serait supprimé). Comme indiqué dans le tableau ci-dessous, 🤗 *Datasets* fournit trois fonctions principales pour enregistrer votre jeu de données dans différents formats :
+
+| Format de données | Fonction |
+| :---------------: | :----------------------: |
+| Arrow | `Dataset.save_to_disk()` |
+| CSV | `Dataset.to_csv()` |
+| JSON | `Dataset.to_json()` |
+
+Par exemple, enregistrons notre jeu de données nettoyé au format Arrow :
+
+```py
+drug_dataset_clean.save_to_disk("drug-reviews")
+```
+
+Cela créera un répertoire avec la structure suivante :
+
+```
+drug-reviews/
+├── dataset_dict.json
+├── test
+│ ├── dataset.arrow
+│ ├── dataset_info.json
+│ └── state.json
+├── train
+│ ├── dataset.arrow
+│ ├── dataset_info.json
+│ ├── indices.arrow
+│ └── state.json
+└── validation
+ ├── dataset.arrow
+ ├── dataset_info.json
+ ├── indices.arrow
+ └── state.json
+```
+
+où nous pouvons voir que chaque division est associée à sa propre table *dataset.arrow* et à certaines métadonnées dans *dataset_info.json* et *state.json*. Vous pouvez considérer le format Arrow comme un tableau sophistiqué de colonnes et de lignes optimisé pour la création d'applications hautes performances qui traitent et transportent de grands ensembles de données.
+
+Une fois le jeu de données enregistré, nous pouvons le charger en utilisant la fonction `load_from_disk()` comme suit :
+
+```py
+from datasets import load_from_disk
+
+drug_dataset_reloaded = load_from_disk("drug-reviews")
+drug_dataset_reloaded
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
+ num_rows: 110811
+ })
+ validation: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
+ num_rows: 27703
+ })
+ test: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
+ num_rows: 46108
+ })
+})
+```
+
+Pour les formats CSV et JSON, nous devons stocker chaque fractionnement dans un fichier séparé. Pour ce faire, vous pouvez parcourir les clés et les valeurs de l'objet `DatasetDict` :
+
+```py
+for split, dataset in drug_dataset_clean.items():
+ dataset.to_json(f"drug-reviews-{split}.jsonl")
+```
+
+Cela enregistre chaque fractionnement au [format JSON Lines](https://jsonlines.org), où chaque ligne du jeu de données est stockée sous la forme d'une seule ligne de JSON. Voici à quoi ressemble le premier exemple :
+
+```py
+!head -n 1 drug-reviews-train.jsonl
+```
+
+```python out
+{"patient_id":141780,"drugName":"Escitalopram","condition":"depression","review":"\"I seemed to experience the regular side effects of LEXAPRO, insomnia, low sex drive, sleepiness during the day. I am taking it at night because my doctor said if it made me tired to take it at night. I assumed it would and started out taking it at night. Strange dreams, some pleasant. I was diagnosed with fibromyalgia. Seems to be helping with the pain. Have had anxiety and depression in my family, and have tried quite a few other medications that haven't worked. Only have been on it for two weeks but feel more positive in my mind, want to accomplish more in my life. Hopefully the side effects will dwindle away, worth it to stick with it from hearing others responses. Great medication.\"","rating":9.0,"date":"May 29, 2011","usefulCount":10,"review_length":125}
+ # Il semble que je ressente les effets secondaires habituels de LEXAPRO : insomnie, baisse de la libido, somnolence pendant la journée. Je le prends le soir parce que mon médecin m'a dit de le prendre le soir s'il me fatiguait. J'ai supposé que ce serait le cas et j'ai commencé à le prendre la nuit. Rêves étranges, certains agréables. On m'a diagnostiqué une fibromyalgie. Il semble que ce médicament aide à soulager la douleur. J'ai eu de l'anxiété et de la dépression dans ma famille, et j'ai essayé plusieurs autres médicaments qui n'ont pas fonctionné. Cela ne fait que deux semaines que je prends ce médicament, mais je me sens plus positif dans mon esprit et je veux accomplir davantage dans ma vie. J'espère que les effets secondaires vont s'estomper, cela vaut la peine de s'y tenir d'après les réponses des autres. C'est un excellent médicament.
+```
+
+Nous pouvons ensuite utiliser les techniques de [section 2](/course/fr/chapter5/2) pour charger les fichiers JSON comme suit :
+
+```py
+data_files = {
+ "train": "drug-reviews-train.jsonl",
+ "validation": "drug-reviews-validation.jsonl",
+ "test": "drug-reviews-test.jsonl",
+}
+drug_dataset_reloaded = load_dataset("json", data_files=data_files)
+```
+
+Et c'est tout pour notre excursion dans la manipulation des données avec 🤗 *Datasets* ! Maintenant que nous disposons d'un ensemble de données nettoyé pour entraîner un modèle, voici quelques idées que vous pouvez essayer :
+
+1. Utilisez les techniques du [chapitre 3](/course/fr/chapter3) pour entraîner un classifieur capable de prédire l'état du patient en fonction de l'examen du médicament.
+2. Utilisez le pipeline `summarization` du [chapitre 1](/course/fr/chapter1) pour générer des résumés des révisions.
+
+Ensuite, nous verrons comment 🤗 *Datasets* peut vous permettre de travailler avec d'énormes jeux de données sans faire exploser votre ordinateur portable !
diff --git a/chapters/fr/chapter5/4.mdx b/chapters/fr/chapter5/4.mdx
index 8657e3b62..dc286c718 100644
--- a/chapters/fr/chapter5/4.mdx
+++ b/chapters/fr/chapter5/4.mdx
@@ -1,296 +1,296 @@
-# Données massives ? 🤗 *Datasets* à la rescousse !
-
-
-
-
-De nos jours, il n'est pas rare de travailler avec des jeux de données de plusieurs gigaoctets surtout si vous envisagez de pré-entraîner un *transformer* comme BERT ou GPT-2 à partir de zéro. Dans ces cas, même _charger_ les données peut être un défi. Par exemple, le corpus WebText utilisé pour pré-entraîner GPT-2 se compose de plus de 8 millions de documents et de 40 Go de texte. Le charger dans la RAM de votre ordinateur portable est susceptible de lui donner une crise cardiaque !
-
-Heureusement, 🤗 *Datasets* a été conçu pour surmonter ces limitations. Il vous libère des problèmes de gestion de la mémoire en traitant les jeux de données comme des fichiers _mappés en mémoire_, ainsi que des limites du disque dur en faisant du _streaming_ sur les entrées dans un corpus.
-
-
-
-Dans cette section, nous allons explorer ces fonctionnalités de 🤗 *Datasets* avec un énorme corpus de 825 Go connu sous le nom de [The Pile](https://pile.eleuther.ai). Commençons !
-
-## Qu'est-ce que *The Pile* ?
-
-The Pile est un corpus de texte en anglais créé par [EleutherAI](https://www.eleuther.ai) pour entraîner des modèles de langage à grande échelle. Il comprend une gamme variée de jeux de données, couvrant des articles scientifiques, des référentiels de code GitHub et du texte Web filtré. Le corpus d’entraînement est disponible en [morceaux de 14 Go](https://mystic.the-eye.eu/public/AI/pile/) et vous pouvez aussi télécharger plusieurs des [composants individuels]( https://mystic.the-eye.eu/public/AI/pile_preliminary_components/). Commençons par jeter un coup d'œil au jeu de données PubMed Abstracts, qui est un corpus de résumés de 15 millions de publications biomédicales sur [PubMed](https://pubmed.ncbi.nlm.nih.gov/). Le jeu de données est au [format JSON Lines](https://jsonlines.org) et est compressé à l'aide de la bibliothèque `zstandard`. Nous devons donc d'abord installer cette bibliothèque :
-
-```py
-!pip install zstandard
-```
-
-Ensuite, nous pouvons charger le jeu de données en utilisant la méthode pour les fichiers distants que nous avons apprise dans [section 2](/course/fr/chapter5/2) :
-
-```py
-from datasets import load_dataset
-
-# Cela prend quelques minutes à exécuter, alors allez prendre un thé ou un café en attendant :)
-data_files = "https://mystic.the-eye.eu/public/AI/pile_preliminary_components/PUBMED_title_abstracts_2019_baseline.jsonl.zst"
-pubmed_dataset = load_dataset("json", data_files=data_files, split="train")
-pubmed_dataset
-```
-
-```python out
-Dataset({
- features: ['meta', 'text'],
- num_rows: 15518009
-})
-```
-
-Nous pouvons voir qu'il y a 15 518 009 lignes et 2 colonnes dans notre jeu de données. C'est beaucoup !
-
-
-
-✎ Par défaut, 🤗 *Datasets* décompresse les fichiers nécessaires pour charger un jeu de données. Si vous souhaitez conserver de l'espace sur le disque dur, vous pouvez passer `DownloadConfig(delete_extracted=True)` à l'argument `download_config` de `load_dataset()`. Voir la [documentation](https://huggingface.co/docs/datasets/package_reference/builder_classes.html?#datasets.utils.DownloadConfig) pour plus de détails.
-
-
-
-Inspectons le contenu du premier exemple :
-
-```py
-pubmed_dataset[0]
-```
-
-```python out
-{'meta': {'pmid': 11409574, 'language': 'eng'},
- 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'
-# Épidémiologie de l'hypoxémie chez les enfants souffrant d'une infection aiguë des voies respiratoires inférieures. Déterminer la prévalence de l'hypoxémie chez les enfants de moins de 5 ans souffrant d'une infection aiguë des voies respiratoires inférieures (IAVI), les facteurs de risque de l'hypoxémie chez les enfants de moins de 5 ans souffrant d'une IAVI, et l'association de l'hypoxémie à un risque accru de décès chez les enfants du même âge ...
-}
-```
-
-Cela ressemble au résumé d'un article médical. Voyons maintenant combien de RAM nous avons utilisé pour charger le jeu de données !
-
-## La magie du *memory mapping*
-
-Un moyen simple de mesurer l'utilisation de la mémoire dans Python consiste à utiliser la bibliothèque [`psutil`](https://psutil.readthedocs.io/en/latest/) qui peut être installée avec `pip` comme suit :
-
-```python
-!pip install psutil
-```
-
-Elle fournit une classe `Process` qui permet de vérifier l'utilisation de la mémoire du processus en cours :
-
-```py
-import psutil
-
-# Process.memory_info est exprimé en octets, donc convertir en mégaoctets
-print(f"RAM used: {psutil.Process().memory_info().rss / (1024 * 1024):.2f} MB")
-```
-
-```python out
-RAM used: 5678.33 MB
-```
-
-Ici, l'attribut `rss` fait référence à la _taille de l'ensemble résident_, qui est la fraction de mémoire qu'un processus occupe dans la RAM. Cette mesure inclut également la mémoire utilisée par l'interpréteur Python et les bibliothèques que nous avons chargées, de sorte que la quantité réelle de mémoire utilisée pour charger le jeu de données est un peu plus petite. À titre de comparaison, voyons la taille du jeu de données sur le disque en utilisant l'attribut `dataset_size`. Comme le résultat est exprimé en octets comme précédemment, nous devons le convertir manuellement en gigaoctets :
-
-```py
-print(f"Number of files in dataset : {pubmed_dataset.dataset_size}")
-size_gb = pubmed_dataset.dataset_size / (1024**3)
-print(f"Dataset size (cache file) : {size_gb:.2f} GB")
-```
-
-```python out
-Number of files in dataset : 20979437051
-Dataset size (cache file) : 19.54 GB
-```
-
-Malgré sa taille de près de 20 Go, nous pouvons charger et accéder au jeu de données avec beaucoup moins de RAM !
-
-
-
-✏️ **Essayez !** Choisissez l'un des [sous-ensembles](https://mystic.the-eye.eu/public/AI/pile_preliminary_components/) de The Pile qui est plus grand que la RAM de votre ordinateur portable ou de bureau. Chargez le avec 🤗 *Datasets* et mesurez la quantité de RAM utilisée. Notez que pour obtenir une mesure précise, vous devrez le faire dans un nouveau processus. Vous pouvez trouver les tailles décompressées de chaque sous-ensemble dans le tableau 1 du papier de [The Pile](https://arxiv.org/abs/2101.00027).
-
-
-
-Si vous êtes familier avec Pandas, ce résultat pourrait surprendre en raison de la célèbre [règle d'or](https://wesmckinney.com/blog/apache-arrow-pandas-internals/) de Wes Kinney selon laquelle vous avez généralement besoin de 5 à 10 fois plus de RAM que la taille de votre jeu de données. Alors, comment 🤗 *Datasets* résout-il ce problème de gestion de la mémoire ? 🤗 *Datasets* traite chaque jeu de données comme un [fichier mappé en mémoire](https://en.wikipedia.org/wiki/Memory-mapped_file). Cela fournit un mappage entre la RAM et le stockage du système de fichiers permettant à la bibliothèque d'accéder et d'opérer sur des éléments du jeu de données sans avoir besoin de le charger entièrement en mémoire.
-
-Les fichiers mappés en mémoire peuvent également être partagés entre plusieurs processus ce qui permet de paralléliser des méthodes telles que `Dataset.map()` sans avoir à déplacer ou copier le jeu de données. Sous le capot, ces capacités sont toutes réalisées par le format de mémoire [Apache Arrow](https://arrow.apache.org) et [`pyarrow`](https://arrow.apache.org/docs/python/index .html), qui accélèrent le chargement et le traitement des données. (Pour plus de détails sur Apache Arrow et les comparaisons avec Pandas, consultez [l'article de blog de Dejan Simic](https://towardsdatascience.com/apache-arrow-read-dataframe-with-zero-memory-69634092b1a).) Pour voir ceci en action, effectuons un petit test de vitesse en itérant sur tous les éléments du jeu de données PubMed Abstracts :
-
-```py
-import timeit
-
-code_snippet = """batch_size = 1000
-
-for idx in range(0, len(pubmed_dataset), batch_size):
- _ = pubmed_dataset[idx:idx + batch_size]
-"""
-
-time = timeit.timeit(stmt=code_snippet, number=1, globals=globals())
-print(
- f"Iterated over {len(pubmed_dataset)} examples (about {size_gb:.1f} GB) in "
- f"{time:.1f}s, i.e. {size_gb/time:.3f} GB/s"
-)
-```
-
-```python out
-'Iterated over 15518009 examples (about 19.5 GB) in 64.2s, i.e. 0.304 GB/s'
-```
-
-Ici, nous avons utilisé le module `timeit` de Python pour mesurer le temps d'exécution pris par `code_snippet`. Vous pourrez généralement itérer sur un jeu de données à une vitesse de quelques dixièmes de Go/s à plusieurs Go/s. Cela fonctionne très bien pour la grande majorité des applications, mais vous devrez parfois travailler avec un jeu de données trop volumineux pour être même stocké sur le disque dur de votre ordinateur portable. Par exemple, si nous essayions de télécharger The Pile dans son intégralité, nous aurions besoin de 825 Go d'espace disque libre ! Pour gérer ces cas, 🤗 *Datasets* fournit une fonctionnalité de streaming qui nous permet de télécharger et d'accéder aux éléments à la volée, sans avoir besoin de télécharger l'intégralité du jeu de données. Voyons comment cela fonctionne.
-
-
-
-💡 Dans les *notebooks* Jupyter, vous pouvez également chronométrer les cellules à l'aide de la fonction magique [`%%timeit`](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit).
-
-
-
-## Jeux de données en continu
-
-Pour activer le streaming du jeu de données, il vous suffit de passer l'argument `streaming=True` à la fonction `load_dataset()`. Par exemple, chargeons à nouveau le jeu de données PubMed Abstracts mais en mode streaming :
-
-```py
-pubmed_dataset_streamed = load_dataset(
- "json", data_files=data_files, split="train", streaming=True
-)
-```
-
-Au lieu du familier `Dataset` que nous avons rencontré ailleurs dans ce chapitre, l'objet retourné avec `streaming=True` est un `IterableDataset`. Comme son nom l'indique, pour accéder aux éléments d'un `IterableDataset`, nous devons parcourir celui-ci. Nous pouvons accéder au premier élément de notre jeu de données diffusé comme suit :
-
-
-```py
-next(iter(pubmed_dataset_streamed))
-```
-
-```python out
-{'meta': {'pmid': 11409574, 'language': 'eng'},
- 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
-```
-
-Les éléments d'un jeu de données diffusé en continu peuvent être traités à la volée à l'aide de `IterableDataset.map()`, ce qui est utile pendant l’entraînement si vous avez besoin de tokeniser les entrées. Le processus est exactement le même que celui que nous avons utilisé pour tokeniser notre jeu de données dans [Chapitre 3](/course/fr/chapter3), à la seule différence que les sorties sont renvoyées une par une :
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
-tokenized_dataset = pubmed_dataset_streamed.map(lambda x: tokenizer(x["text"]))
-next(iter(tokenized_dataset))
-```
-
-```python out
-{'input_ids': [101, 4958, 5178, 4328, 6779, ...], 'attention_mask': [1, 1, 1, 1, 1, ...]}
-```
-
-
-
-💡 Pour accélérer la tokenisation avec le streaming, vous pouvez passer `batched=True`, comme nous l'avons vu dans la dernière section. Il traitera les exemples batch par batch. La taille de batch par défaut est de 1 000 et peut être spécifiée avec l'argument `batch_size`.
-
-
-
-Vous pouvez également mélanger un jeu de données diffusé en continu à l'aide de `IterableDataset.shuffle()`, mais contrairement à `Dataset.shuffle()`, cela ne mélange que les éléments dans un `buffer_size` prédéfini :
-
-```py
-shuffled_dataset = pubmed_dataset_streamed.shuffle(buffer_size=10_000, seed=42)
-next(iter(shuffled_dataset))
-```
-
-```python out
-{'meta': {'pmid': 11410799, 'language': 'eng'},
- 'text': 'Randomized study of dose or schedule modification of granulocyte colony-stimulating factor in platinum-based chemotherapy for elderly patients with lung cancer ...'
-# Étude randomisée sur la modification de la dose ou du calendrier d'administration du facteur de stimulation des colonies de granulocytes dans le cadre d'une chimiothérapie à base de platine chez les patients âgés atteints de cancer du poumon ...
-}
-```
-
-Dans cet exemple, nous avons sélectionné un exemple aléatoire parmi les 10 000 premiers exemples du tampon. Une fois qu'un exemple est accédé, sa place dans le tampon est remplie avec l'exemple suivant dans le corpus (c'est-à-dire le 10 001e exemple dans le cas ci-dessus). Vous pouvez également sélectionner des éléments d'un jeu de données diffusé en continu à l'aide des fonctions `IterableDataset.take()` et `IterableDataset.skip()`, qui agissent de la même manière que `Dataset.select()`. Par exemple, pour sélectionner les 5 premiers exemples dans le jeu de données PubMed Abstracts, nous pouvons procéder comme suit :
-
-```py
-dataset_head = pubmed_dataset_streamed.take(5)
-list(dataset_head)
-```
-
-```python out
-[{'meta': {'pmid': 11409574, 'language': 'eng'},
- 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'
-# Épidémiologie de l'hypoxémie chez les enfants atteints d'une infection aiguë des voies respiratoires inférieures ...},
- {'meta': {'pmid': 11409575, 'language': 'eng'},
- 'text': 'Clinical signs of hypoxaemia in children with acute lower respiratory infection: indicators of oxygen therapy ...'
-# Signes cliniques d'hypoxémie chez les enfants atteints d'une infection aiguë des voies respiratoires inférieures : indicateurs de l'oxygénothérapie ...},
- {'meta': {'pmid': 11409576, 'language': 'eng'},
- 'text': "Hypoxaemia in children with severe pneumonia in Papua New Guinea ..."
-# Hypoxémie chez les enfants atteints de pneumonie grave en Papouasie-Nouvelle-Guinée ...},
- {'meta': {'pmid': 11409577, 'language': 'eng'},
- 'text': 'Oxygen concentrators and cylinders ...'
-# Concentrateurs et bouteilles d'oxygène...},
- {'meta': {'pmid': 11409578, 'language': 'eng'},
- 'text': 'Oxygen supply in rural africa: a personal experience ...'
-# L'approvisionnement en oxygène dans les zones rurales africaines : une expérience personnelle ...}]
-```
-
-De même, vous pouvez utiliser la fonction `IterableDataset.skip()` pour créer des fractionnements d'entraînement et de validation à partir d'un jeu de données mélangé comme suit :
-
-```py
-# Ignorer les 1 000 premiers exemples et inclure le reste dans l'ensemble d'apprentissage.
-train_dataset = shuffled_dataset.skip(1000)
-# Prendre les 1 000 premiers exemples pour l'ensemble de validation.
-validation_dataset = shuffled_dataset.take(1000)
-```
-
-Terminons notre exploration du streaming des jeux de données avec une application commune : combiner plusieurs jeux de données pour créer un seul corpus. 🤗 *Datasets* fournit une fonction `interleave_datasets()` qui convertit une liste d'objets `IterableDataset` en un seul `IterableDataset`, où les éléments du nouveau jeu de données sont obtenus en alternant entre les exemples source. Cette fonction est particulièrement utile lorsque vous essayez de combiner de grands jeux de données. Par exemple, streamons FreeLaw, un sous-ensemble de The Pile et qui est un jeu de données de 51 Go d'avis juridiques de tribunaux américains :
-
-```py
-law_dataset_streamed = load_dataset(
- "json",
- data_files="https://mystic.the-eye.eu/public/AI/pile_preliminary_components/FreeLaw_Opinions.jsonl.zst",
- split="train",
- streaming=True,
-)
-next(iter(law_dataset_streamed))
-```
-
-```python out
-{'meta': {'case_ID': '110921.json',
- 'case_jurisdiction': 'scotus.tar.gz',
- 'date_created': '2010-04-28T17:12:49Z'},
- 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}
-```
-
-Ce jeu de données est suffisamment volumineux pour solliciter la RAM de la plupart des ordinateurs portables, mais nous avons pu le charger et y accéder sans transpirer ! Combinons maintenant les jeux de données FreeLaw et PubMed Abstracts avec la fonction `interleave_datasets()` :
-
-```py
-from itertools import islice
-from datasets import interleave_datasets
-
-combined_dataset = interleave_datasets([pubmed_dataset_streamed, law_dataset_streamed])
-list(islice(combined_dataset, 2))
-```
-
-```python out
-[{'meta': {'pmid': 11409574, 'language': 'eng'},
- 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
- {'meta': {'case_ID': '110921.json',
- 'case_jurisdiction': 'scotus.tar.gz',
- 'date_created': '2010-04-28T17:12:49Z'},
- 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}]
-```
-
-Ici, nous avons utilisé la fonction `islice()` du module `itertools` de Python pour sélectionner les deux premiers exemples du jeu de données combiné. Nous pouvons voir qu'ils correspondent aux premiers exemples de chacun des deux jeux de données source.
-
-Enfin, si vous souhaitez streamer The Pile dans son intégralité de 825 Go, vous pouvez récupérer tous les fichiers préparés comme suit :
-
-```py
-base_url = "https://mystic.the-eye.eu/public/AI/pile/"
-data_files = {
- "train": [base_url + "train/" + f"{idx:02d}.jsonl.zst" for idx in range(30)],
- "validation": base_url + "val.jsonl.zst",
- "test": base_url + "test.jsonl.zst",
-}
-pile_dataset = load_dataset("json", data_files=data_files, streaming=True)
-next(iter(pile_dataset["train"]))
-```
-
-```python out
-{'meta': {'pile_set_name': 'Pile-CC'},
- 'text': 'It is done, and submitted. You can play “Survival of the Tastiest” on Android, and on the web...'}
-```
-
-
-
-✏️ **Essayez !** Utilisez l'un des grands corpus Common Crawl comme [`mc4`](https://huggingface.co/datasets/mc4) ou [`oscar`](https://huggingface.co/datasets/oscar) pour créer en streaming un jeu de données multilingue représentant les proportions de langues parlées dans un pays de votre choix. Par exemple, les quatre langues nationales en Suisse sont l'allemand, le français, l'italien et le romanche. Vous pouvez donc essayer de créer un corpus suisse en échantillonnant les sous-ensembles Oscar en fonction de leur proportion parlée.
-
-
-
-Vous disposez maintenant de tous les outils dont vous avez besoin pour charger et traiter des jeux de données de toutes formes et tailles. Cependant à moins que vous ne soyez exceptionnellement chanceux, il arrivera un moment dans votre cheminement en traitement du langage naturel où vous devrez réellement créer un jeu de données pour résoudre un problème donné. C'est le sujet de la section suivante !
+# Données massives ? 🤗 Datasets à la rescousse !
+
+
+
+
+De nos jours, il n'est pas rare de travailler avec des jeux de données de plusieurs gigaoctets surtout si vous envisagez de pré-entraîner un *transformer* comme BERT ou GPT-2 à partir de zéro. Dans ces cas, même _charger_ les données peut être un défi. Par exemple, le corpus WebText utilisé pour pré-entraîner GPT-2 se compose de plus de 8 millions de documents et de 40 Go de texte. Le charger dans la RAM de votre ordinateur portable est susceptible de lui donner une crise cardiaque !
+
+Heureusement, 🤗 *Datasets* a été conçu pour surmonter ces limitations. Il vous libère des problèmes de gestion de la mémoire en traitant les jeux de données comme des fichiers _mappés en mémoire_, ainsi que des limites du disque dur en faisant du _streaming_ sur les entrées dans un corpus.
+
+
+
+Dans cette section, nous allons explorer ces fonctionnalités de 🤗 *Datasets* avec un énorme corpus de 825 Go connu sous le nom de [*The Pile*](https://pile.eleuther.ai). Commençons !
+
+## Qu'est-ce que The Pile ?
+
+*The Pile* est un corpus de texte en anglais créé par [EleutherAI](https://www.eleuther.ai) pour entraîner des modèles de langage à grande échelle. Il comprend une gamme variée de jeux de données, couvrant des articles scientifiques, des référentiels de code GitHub et du texte Web filtré. Le corpus d’entraînement est disponible en [morceaux de 14 Go](https://mystic.the-eye.eu/public/AI/pile/) et vous pouvez aussi télécharger plusieurs des [composants individuels]( https://mystic.the-eye.eu/public/AI/pile_preliminary_components/). Commençons par jeter un coup d'œil au jeu de données *PubMed Abstracts*, qui est un corpus de résumés de 15 millions de publications biomédicales sur [PubMed](https://pubmed.ncbi.nlm.nih.gov/). Le jeu de données est au [format JSON Lines](https://jsonlines.org) et est compressé à l'aide de la bibliothèque `zstandard`. Nous devons donc d'abord installer cette bibliothèque :
+
+```py
+!pip install zstandard
+```
+
+Ensuite, nous pouvons charger le jeu de données en utilisant la méthode pour les fichiers distants que nous avons apprise dans [section 2](/course/fr/chapter5/2) :
+
+```py
+from datasets import load_dataset
+
+# Cela prend quelques minutes à exécuter, alors allez prendre un thé ou un café en attendant :)
+data_files = "https://mystic.the-eye.eu/public/AI/pile_preliminary_components/PUBMED_title_abstracts_2019_baseline.jsonl.zst"
+pubmed_dataset = load_dataset("json", data_files=data_files, split="train")
+pubmed_dataset
+```
+
+```python out
+Dataset({
+ features: ['meta', 'text'],
+ num_rows: 15518009
+})
+```
+
+Nous pouvons voir qu'il y a 15 518 009 lignes et 2 colonnes dans notre jeu de données. C'est beaucoup !
+
+
+
+✎ Par défaut, 🤗 *Datasets* décompresse les fichiers nécessaires pour charger un jeu de données. Si vous souhaitez conserver de l'espace sur le disque dur, vous pouvez passer `DownloadConfig(delete_extracted=True)` à l'argument `download_config` de `load_dataset()`. Voir la [documentation](https://huggingface.co/docs/datasets/package_reference/builder_classes.html?#datasets.utils.DownloadConfig) pour plus de détails.
+
+
+
+Inspectons le contenu du premier exemple :
+
+```py
+pubmed_dataset[0]
+```
+
+```python out
+{'meta': {'pmid': 11409574, 'language': 'eng'},
+ 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'
+# Épidémiologie de l'hypoxémie chez les enfants souffrant d'une infection aiguë des voies respiratoires inférieures. Déterminer la prévalence de l'hypoxémie chez les enfants de moins de 5 ans souffrant d'une infection aiguë des voies respiratoires inférieures (IAVI), les facteurs de risque de l'hypoxémie chez les enfants de moins de 5 ans souffrant d'une IAVI, et l'association de l'hypoxémie à un risque accru de décès chez les enfants du même âge ...
+}
+```
+
+Cela ressemble au résumé d'un article médical. Voyons maintenant combien de RAM nous avons utilisé pour charger le jeu de données !
+
+## La magie du memory mapping
+
+Un moyen simple de mesurer l'utilisation de la mémoire dans Python consiste à utiliser la bibliothèque [`psutil`](https://psutil.readthedocs.io/en/latest/) qui peut être installée avec `pip` comme suit :
+
+```python
+!pip install psutil
+```
+
+Elle fournit une classe `Process` qui permet de vérifier l'utilisation de la mémoire du processus en cours :
+
+```py
+import psutil
+
+# Process.memory_info est exprimé en octets, donc convertir en mégaoctets
+print(f"RAM used: {psutil.Process().memory_info().rss / (1024 * 1024):.2f} MB")
+```
+
+```python out
+RAM used: 5678.33 MB
+```
+
+Ici, l'attribut `rss` fait référence à la _taille de l'ensemble résident_, qui est la fraction de mémoire qu'un processus occupe dans la RAM. Cette mesure inclut également la mémoire utilisée par l'interpréteur Python et les bibliothèques que nous avons chargées, de sorte que la quantité réelle de mémoire utilisée pour charger le jeu de données est un peu plus petite. À titre de comparaison, voyons la taille du jeu de données sur le disque en utilisant l'attribut `dataset_size`. Comme le résultat est exprimé en octets comme précédemment, nous devons le convertir manuellement en gigaoctets :
+
+```py
+print(f"Number of files in dataset : {pubmed_dataset.dataset_size}")
+size_gb = pubmed_dataset.dataset_size / (1024**3)
+print(f"Dataset size (cache file) : {size_gb:.2f} GB")
+```
+
+```python out
+Number of files in dataset : 20979437051
+Dataset size (cache file) : 19.54 GB
+```
+
+Malgré sa taille de près de 20 Go, nous pouvons charger et accéder au jeu de données avec beaucoup moins de RAM !
+
+
+
+✏️ **Essayez !** Choisissez l'un des [sous-ensembles](https://mystic.the-eye.eu/public/AI/pile_preliminary_components/) de *The Pile* qui est plus grand que la RAM de votre ordinateur portable ou de bureau. Chargez-le avec 🤗 *Datasets* et mesurez la quantité de RAM utilisée. Notez que pour obtenir une mesure précise, vous devrez le faire dans un nouveau processus. Vous pouvez trouver les tailles décompressées de chaque sous-ensemble dans le tableau 1 du papier de [*The Pile*](https://arxiv.org/abs/2101.00027).
+
+
+
+Si vous êtes familier avec Pandas, ce résultat pourrait surprendre en raison de la célèbre [règle d'or](https://wesmckinney.com/blog/apache-arrow-pandas-internals/) de Wes Kinney selon laquelle vous avez généralement besoin de 5 à 10 fois plus de RAM que la taille de votre jeu de données. Alors, comment 🤗 *Datasets* résout-il ce problème de gestion de la mémoire ? 🤗 *Datasets* traite chaque jeu de données comme un [fichier mappé en mémoire](https://en.wikipedia.org/wiki/Memory-mapped_file). Cela fournit un mappage entre la RAM et le stockage du système de fichiers permettant à la bibliothèque d'accéder et d'opérer sur des éléments du jeu de données sans avoir besoin de le charger entièrement en mémoire.
+
+Les fichiers mappés en mémoire peuvent également être partagés entre plusieurs processus ce qui permet de paralléliser des méthodes telles que `Dataset.map()` sans avoir à déplacer ou copier le jeu de données. Sous le capot, ces capacités sont toutes réalisées par le format de mémoire [Apache Arrow](https://arrow.apache.org) et [`pyarrow`](https://arrow.apache.org/docs/python/index.html), qui accélèrent le chargement et le traitement des données. (Pour plus de détails sur Apache Arrow et les comparaisons avec Pandas, consultez [l'article de blog de Dejan Simic](https://towardsdatascience.com/apache-arrow-read-dataframe-with-zero-memory-69634092b1a)). Pour voir ceci en action, effectuons un petit test de vitesse en itérant sur tous les éléments du jeu de données *PubMed Abstracts* :
+
+```py
+import timeit
+
+code_snippet = """batch_size = 1000
+
+for idx in range(0, len(pubmed_dataset), batch_size):
+ _ = pubmed_dataset[idx:idx + batch_size]
+"""
+
+time = timeit.timeit(stmt=code_snippet, number=1, globals=globals())
+print(
+ f"Iterated over {len(pubmed_dataset)} examples (about {size_gb:.1f} GB) in "
+ f"{time:.1f}s, i.e. {size_gb/time:.3f} GB/s"
+)
+```
+
+```python out
+'Iterated over 15518009 examples (about 19.5 GB) in 64.2s, i.e. 0.304 GB/s'
+```
+
+Ici, nous avons utilisé le module `timeit` de Python pour mesurer le temps d'exécution pris par `code_snippet`. Vous pourrez généralement itérer sur un jeu de données à une vitesse de quelques dixièmes de Go/s à plusieurs Go/s. Cela fonctionne très bien pour la grande majorité des applications, mais vous devrez parfois travailler avec un jeu de données trop volumineux pour être même stocké sur le disque dur de votre ordinateur portable. Par exemple, si nous essayions de télécharger *The Pile* dans son intégralité, nous aurions besoin de 825 Go d'espace disque libre ! Pour gérer ces cas, 🤗 *Datasets* fournit une fonctionnalité de streaming qui nous permet de télécharger et d'accéder aux éléments à la volée, sans avoir besoin de télécharger l'intégralité du jeu de données. Voyons comment cela fonctionne.
+
+
+
+💡 Dans les *notebooks* Jupyter, vous pouvez également chronométrer les cellules à l'aide de la fonction magique [`%%timeit`](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit).
+
+
+
+## Jeux de données en continu
+
+Pour activer le streaming du jeu de données, il vous suffit de passer l'argument `streaming=True` à la fonction `load_dataset()`. Par exemple, chargeons à nouveau le jeu de données *PubMed Abstracts* mais en mode streaming :
+
+```py
+pubmed_dataset_streamed = load_dataset(
+ "json", data_files=data_files, split="train", streaming=True
+)
+```
+
+Au lieu du familier `Dataset` que nous avons rencontré ailleurs dans ce chapitre, l'objet retourné avec `streaming=True` est un `IterableDataset`. Comme son nom l'indique, pour accéder aux éléments d'un `IterableDataset`, nous devons parcourir celui-ci. Nous pouvons accéder au premier élément de notre jeu de données diffusé comme suit :
+
+
+```py
+next(iter(pubmed_dataset_streamed))
+```
+
+```python out
+{'meta': {'pmid': 11409574, 'language': 'eng'},
+ 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
+```
+
+Les éléments d'un jeu de données diffusé en continu peuvent être traités à la volée à l'aide de `IterableDataset.map()`, ce qui est utile pendant l’entraînement si vous avez besoin de tokeniser les entrées. Le processus est exactement le même que celui que nous avons utilisé pour tokeniser notre jeu de données dans le [chapitre 3](/course/fr/chapter3), à la seule différence que les sorties sont renvoyées une par une :
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
+tokenized_dataset = pubmed_dataset_streamed.map(lambda x: tokenizer(x["text"]))
+next(iter(tokenized_dataset))
+```
+
+```python out
+{'input_ids': [101, 4958, 5178, 4328, 6779, ...], 'attention_mask': [1, 1, 1, 1, 1, ...]}
+```
+
+
+
+💡 Pour accélérer la tokenisation avec le streaming, vous pouvez passer `batched=True`, comme nous l'avons vu dans la dernière section. Il traitera les exemples batch par batch. La taille de batch par défaut est de 1 000 et peut être spécifiée avec l'argument `batch_size`.
+
+
+
+Vous pouvez également mélanger un jeu de données diffusé en continu à l'aide de `IterableDataset.shuffle()`, mais contrairement à `Dataset.shuffle()`, cela ne mélange que les éléments dans un `buffer_size` prédéfini :
+
+```py
+shuffled_dataset = pubmed_dataset_streamed.shuffle(buffer_size=10_000, seed=42)
+next(iter(shuffled_dataset))
+```
+
+```python out
+{'meta': {'pmid': 11410799, 'language': 'eng'},
+ 'text': 'Randomized study of dose or schedule modification of granulocyte colony-stimulating factor in platinum-based chemotherapy for elderly patients with lung cancer ...'
+# Étude randomisée sur la modification de la dose ou du calendrier d'administration du facteur de stimulation des colonies de granulocytes dans le cadre d'une chimiothérapie à base de platine chez les patients âgés atteints de cancer du poumon ...
+}
+```
+
+Dans cet exemple, nous avons sélectionné un exemple aléatoire parmi les 10 000 premiers exemples du tampon. Une fois qu'un exemple est accédé, sa place dans le tampon est remplie avec l'exemple suivant dans le corpus (c'est-à-dire le 10 001e exemple dans le cas ci-dessus). Vous pouvez également sélectionner des éléments d'un jeu de données diffusé en continu à l'aide des fonctions `IterableDataset.take()` et `IterableDataset.skip()`, qui agissent de la même manière que `Dataset.select()`. Par exemple, pour sélectionner les 5 premiers exemples dans le jeu de données *PubMed Abstracts*, nous pouvons procéder comme suit :
+
+```py
+dataset_head = pubmed_dataset_streamed.take(5)
+list(dataset_head)
+```
+
+```python out
+[{'meta': {'pmid': 11409574, 'language': 'eng'},
+ 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'
+# Épidémiologie de l'hypoxémie chez les enfants atteints d'une infection aiguë des voies respiratoires inférieures ...},
+ {'meta': {'pmid': 11409575, 'language': 'eng'},
+ 'text': 'Clinical signs of hypoxaemia in children with acute lower respiratory infection: indicators of oxygen therapy ...'
+# Signes cliniques d'hypoxémie chez les enfants atteints d'une infection aiguë des voies respiratoires inférieures : indicateurs de l'oxygénothérapie ...},
+ {'meta': {'pmid': 11409576, 'language': 'eng'},
+ 'text': "Hypoxaemia in children with severe pneumonia in Papua New Guinea ..."
+# Hypoxémie chez les enfants atteints de pneumonie grave en Papouasie-Nouvelle-Guinée ...},
+ {'meta': {'pmid': 11409577, 'language': 'eng'},
+ 'text': 'Oxygen concentrators and cylinders ...'
+# Concentrateurs et bouteilles d'oxygène...},
+ {'meta': {'pmid': 11409578, 'language': 'eng'},
+ 'text': 'Oxygen supply in rural africa: a personal experience ...'
+# L'approvisionnement en oxygène dans les zones rurales africaines : une expérience personnelle ...}]
+```
+
+De même, vous pouvez utiliser la fonction `IterableDataset.skip()` pour créer des fractionnements d'entraînement et de validation à partir d'un jeu de données mélangé comme suit :
+
+```py
+# Ignorer les 1 000 premiers exemples et inclure le reste dans l'ensemble d'apprentissage.
+train_dataset = shuffled_dataset.skip(1000)
+# Prendre les 1 000 premiers exemples pour l'ensemble de validation.
+validation_dataset = shuffled_dataset.take(1000)
+```
+
+Terminons notre exploration du streaming des jeux de données avec une application commune : combiner plusieurs jeux de données pour créer un seul corpus. 🤗 *Datasets* fournit une fonction `interleave_datasets()` qui convertit une liste d'objets `IterableDataset` en un seul `IterableDataset`, où les éléments du nouveau jeu de données sont obtenus en alternant entre les exemples source. Cette fonction est particulièrement utile lorsque vous essayez de combiner de grands jeux de données. Par exemple, streamons FreeLaw, un sous-ensemble de *The Pile* et qui est un jeu de données de 51 Go d'avis juridiques de tribunaux américains :
+
+```py
+law_dataset_streamed = load_dataset(
+ "json",
+ data_files="https://mystic.the-eye.eu/public/AI/pile_preliminary_components/FreeLaw_Opinions.jsonl.zst",
+ split="train",
+ streaming=True,
+)
+next(iter(law_dataset_streamed))
+```
+
+```python out
+{'meta': {'case_ID': '110921.json',
+ 'case_jurisdiction': 'scotus.tar.gz',
+ 'date_created': '2010-04-28T17:12:49Z'},
+ 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}
+```
+
+Ce jeu de données est suffisamment volumineux pour solliciter la RAM de la plupart des ordinateurs portables, mais nous avons pu le charger et y accéder sans transpirer ! Combinons maintenant les jeux de données FreeLaw et *PubMed Abstracts* avec la fonction `interleave_datasets()` :
+
+```py
+from itertools import islice
+from datasets import interleave_datasets
+
+combined_dataset = interleave_datasets([pubmed_dataset_streamed, law_dataset_streamed])
+list(islice(combined_dataset, 2))
+```
+
+```python out
+[{'meta': {'pmid': 11409574, 'language': 'eng'},
+ 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
+ {'meta': {'case_ID': '110921.json',
+ 'case_jurisdiction': 'scotus.tar.gz',
+ 'date_created': '2010-04-28T17:12:49Z'},
+ 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}]
+```
+
+Ici, nous avons utilisé la fonction `islice()` du module `itertools` de Python pour sélectionner les deux premiers exemples du jeu de données combiné. Nous pouvons voir qu'ils correspondent aux premiers exemples de chacun des deux jeux de données source.
+
+Enfin, si vous souhaitez streamer *The Pile* dans son intégralité de 825 Go, vous pouvez récupérer tous les fichiers préparés comme suit :
+
+```py
+base_url = "https://mystic.the-eye.eu/public/AI/pile/"
+data_files = {
+ "train": [base_url + "train/" + f"{idx:02d}.jsonl.zst" for idx in range(30)],
+ "validation": base_url + "val.jsonl.zst",
+ "test": base_url + "test.jsonl.zst",
+}
+pile_dataset = load_dataset("json", data_files=data_files, streaming=True)
+next(iter(pile_dataset["train"]))
+```
+
+```python out
+{'meta': {'pile_set_name': 'Pile-CC'},
+ 'text': 'It is done, and submitted. You can play “Survival of the Tastiest” on Android, and on the web...'}
+```
+
+
+
+✏️ **Essayez !** Utilisez l'un des grands corpus Common Crawl comme [`mc4`](https://huggingface.co/datasets/mc4) ou [`oscar`](https://huggingface.co/datasets/oscar) pour créer en streaming un jeu de données multilingue représentant les proportions de langues parlées dans un pays de votre choix. Par exemple, les quatre langues nationales en Suisse sont l'allemand, le français, l'italien et le romanche. Vous pouvez donc essayer de créer un corpus suisse en échantillonnant les sous-ensembles Oscar en fonction de leur proportion parlée.
+
+
+
+Vous disposez maintenant de tous les outils dont vous avez besoin pour charger et traiter des jeux de données de toutes formes et tailles. Cependant à moins que vous ne soyez exceptionnellement chanceux, il arrivera un moment dans votre cheminement en traitement du langage naturel où vous devrez réellement créer un jeu de données pour résoudre un problème donné. C'est le sujet de la section suivante !
diff --git a/chapters/fr/chapter5/5.mdx b/chapters/fr/chapter5/5.mdx
index 36bcca83c..4781cd83c 100644
--- a/chapters/fr/chapter5/5.mdx
+++ b/chapters/fr/chapter5/5.mdx
@@ -1,467 +1,467 @@
-# Création de votre propre jeu de données
-
-
-
-Parfois, le jeu de données dont vous avez besoin pour créer une application de NLP n'existe pas. Vous devrez donc le créer vous-même. Dans cette section, nous allons vous montrer comment créer un corpus de [problèmes GitHub](https://github.com/features/issues/), qui sont couramment utilisés pour suivre les bogues ou les fonctionnalités dans les dépôts GitHub. Ce corpus pourrait être utilisé à diverses fins, notamment :
-
-* explorer combien de temps il faut pour fermer les problèmes ouverts ou les *pull requests*
-* entraîner d'un _classificateur multilabel_ capable d'étiqueter les problèmes avec des métadonnées basées sur la description du problème (par exemple, "bogue", "amélioration" ou "question")
-* créer un moteur de recherche sémantique pour trouver les problèmes correspondant à la requête d'un utilisateur
-
-Ici, nous nous concentrerons sur la création du corpus, et dans la section suivante, nous aborderons l'application de recherche sémantique. Pour garder les choses méta, nous utiliserons les problèmes GitHub associés à un projet open source populaire : 🤗 *Datasets* ! Voyons comment obtenir les données et explorons les informations contenues dans ces problèmes.
-
-## Obtenir les données
-
-Vous pouvez trouver tous les problèmes dans 🤗 *Datasets* en accédant à l'[onglet « Issues »](https://github.com/huggingface/datasets/issues) du dépôt. Comme le montre la capture d'écran suivante, au moment de la rédaction, il y avait 331 problèmes ouverts et 668 problèmes fermés.
-
-
-
-
-
-Si vous cliquez sur l'un de ces problèmes, vous constaterez qu'il contient un titre, une description et un ensemble d'étiquettes qui caractérisent le problème. Un exemple est montré dans la capture d'écran ci-dessous.
-
-
-
-
-
-Pour télécharger tous les problèmes du dépôt, nous utilisons l'[API REST GitHub](https://docs.github.com/en/rest) pour interroger le point de terminaison [`Issues`](https://docs.github.com/en/rest/reference/issues#list-repository-issues). Ce point de terminaison renvoie une liste d'objets JSON. Chaque objet contenant un grand nombre de champs qui incluent le titre et la description ainsi que des métadonnées sur l'état du problème, etc.
-
-Un moyen pratique de télécharger les problèmes consiste à utiliser la bibliothèque `requests`, qui est la méthode standard pour effectuer des requêtes HTTP en Python. Vous pouvez installer la bibliothèque en exécutant :
-
-```python
-!pip install requests
-```
-
-Une fois la bibliothèque installée, vous pouvez envoyer des requêtes GET au point de terminaison `Issues` en appelant la fonction `requests.get()`. Par exemple, vous pouvez exécuter la commande suivante pour récupérer le premier numéro sur la première page :
-
-```py
-import requests
-
-url = "https://api.github.com/repos/huggingface/datasets/issues?page=1&per_page=1"
-response = requests.get(url)
-```
-
-L'objet `response` contient de nombreuses informations utiles sur la requête, y compris le code d'état HTTP :
-
-```py
-response.status_code
-```
-
-```python out
-200
-```
-
-où un statut `200` signifie que la requête a réussi (vous pouvez trouver une liste des codes de statut HTTP possibles [ici](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)). Ce qui nous intéresse vraiment, cependant, c'est le _payload_, qui peut être consulté dans différents formats comme les octets, les chaînes ou JSON. Comme nous savons que nos problèmes sont au format JSON, examinons la charge utile comme suit :
-
-```py
-response.json()
-```
-
-```python out
-[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
- 'repository_url': 'https://api.github.com/repos/huggingface/datasets',
- 'labels_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/labels{/name}',
- 'comments_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/comments',
- 'events_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/events',
- 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
- 'id': 968650274,
- 'node_id': 'MDExOlB1bGxSZXF1ZXN0NzEwNzUyMjc0',
- 'number': 2792,
- 'title': 'Update GooAQ',
- 'user': {'login': 'bhavitvyamalik',
- 'id': 19718818,
- 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
- 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
- 'gravatar_id': '',
- 'url': 'https://api.github.com/users/bhavitvyamalik',
- 'html_url': 'https://github.com/bhavitvyamalik',
- 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
- 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
- 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
- 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
- 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
- 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
- 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
- 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
- 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
- 'type': 'User',
- 'site_admin': False},
- 'labels': [],
- 'state': 'open',
- 'locked': False,
- 'assignee': None,
- 'assignees': [],
- 'milestone': None,
- 'comments': 1,
- 'created_at': '2021-08-12T11:40:18Z',
- 'updated_at': '2021-08-12T12:31:17Z',
- 'closed_at': None,
- 'author_association': 'CONTRIBUTOR',
- 'active_lock_reason': None,
- 'pull_request': {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/2792',
- 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
- 'diff_url': 'https://github.com/huggingface/datasets/pull/2792.diff',
- 'patch_url': 'https://github.com/huggingface/datasets/pull/2792.patch'},
- 'body': '[GooAQ](https://github.com/allenai/gooaq) dataset was recently updated after splits were added for the same. This PR contains new updated GooAQ with train/val/test splits and updated README as well.',
- 'performed_via_github_app': None}]
-```
-
-Waouh, ça fait beaucoup d'informations ! Nous pouvons voir des champs utiles comme `title`, `body` et `number` qui décrivent le problème, ainsi que des informations sur l'utilisateur GitHub qui a ouvert le problème.
-
-
-
-✏️ **Essayez !** Cliquez sur quelques-unes des URL pour avoir une idée du type d'informations auxquelles chaque problème GitHub est lié.
-
-
-
-Comme décrit dans la [documentation GitHub](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting), les requêtes non authentifiées sont limitées à 60 requêtes par heure. Bien que vous puissiez augmenter le paramètre de requête `per_page` pour réduire le nombre de requêtes que vous effectuez, vous atteindrez toujours la limite de débit sur tout dépôt contenant des milliers de problèmes. Donc, à la place, vous devez suivre les [instructions de GitHub](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) sur la création d'un _jeton d'accès personnel_ afin que vous peut augmenter la limite de débit à 5 000 requêtes par heure. Une fois que vous avez votre *token*, vous pouvez l'inclure dans l'en-tête de la requête :
-
-```py
-GITHUB_TOKEN = xxx # Copy your GitHub token here
-headers = {"Authorization": f"token {GITHUB_TOKEN}"}
-```
-
-
-
-⚠️ Ne partagez pas un *notebook* avec votre `GITHUB_TOKEN` collé dedans. Nous vous recommandons de supprimer la dernière cellule une fois que vous l'avez exécutée pour éviter de divulguer accidentellement ces informations. Mieux encore, stockez le jeton dans un fichier *.env* et utilisez la [bibliothèque `python-dotenv`](https://github.com/theskumar/python-dotenv) pour le charger automatiquement pour vous en tant que variable d'environnement.
-
-
-
-Maintenant que nous avons notre jeton d'accès, créons une fonction qui peut télécharger tous les problèmes depuis un référentiel GitHub :
-
-```py
-import time
-import math
-from pathlib import Path
-import pandas as pd
-from tqdm.notebook import tqdm
-
-
-def fetch_issues(
- owner="huggingface",
- repo="datasets",
- num_issues=10_000,
- rate_limit=5_000,
- issues_path=Path("."),
-):
- if not issues_path.is_dir():
- issues_path.mkdir(exist_ok=True)
-
- batch = []
- all_issues = []
- per_page = 100 # Number of issues to return per page
- num_pages = math.ceil(num_issues / per_page)
- base_url = "https://api.github.com/repos"
-
- for page in tqdm(range(num_pages)):
- # Query with state=all to get both open and closed issues
- query = f"issues?page={page}&per_page={per_page}&state=all"
- issues = requests.get(f"{base_url}/{owner}/{repo}/{query}", headers=headers)
- batch.extend(issues.json())
-
- if len(batch) > rate_limit and len(all_issues) < num_issues:
- all_issues.extend(batch)
- batch = [] # Flush batch for next time period
- print(f"Reached GitHub rate limit. Sleeping for one hour ...")
- time.sleep(60 * 60 + 1)
-
- all_issues.extend(batch)
- df = pd.DataFrame.from_records(all_issues)
- df.to_json(f"{issues_path}/{repo}-issues.jsonl", orient="records", lines=True)
- print(
- f"Downloaded all the issues for {repo}! Dataset stored at {issues_path}/{repo}-issues.jsonl"
- )
-```
-
-Désormais, lorsque nous appellerons `fetch_issues()`, tous les problèmes seront téléchargés par batchs pour éviter de dépasser la limite de GitHub sur le nombre de requêtes par heure. Le résultat sera stocké dans un fichier _repository_name-issues.jsonl_, où chaque ligne est un objet JSON qui représente un problème. Utilisons cette fonction pour saisir tous les problèmes de 🤗 *Datasets* :
-
-```py
-# En fonction de votre connexion Internet, l'exécution peut prendre plusieurs minutes...
-fetch_issues()
-```
-
-Une fois les problèmes téléchargés, nous pouvons les charger localement en utilisant nos nouvelles compétences de [section 2](/course/fr/chaper5/2) :
-
-```py
-issues_dataset = load_dataset("json", data_files="datasets-issues.jsonl", split="train")
-issues_dataset
-```
-
-```python out
-Dataset({
- features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'timeline_url', 'performed_via_github_app'],
- num_rows: 3019
-})
-```
-
-Génial, nous avons créé notre premier jeu de données à partir de rien ! Mais pourquoi y a-t-il plusieurs milliers de problèmes alors que l'[onglet « Issues »](https://github.com/huggingface/datasets/issues) de la librairie 🤗 *Datasets* n'affiche qu'environ 1 000 problèmes au total 🤔 ? Comme décrit dans la [documentation GitHub](https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user), c'est parce que nous avons téléchargé toutes les *pull requests* également :
-
-> L'API REST v3 de GitHub considère chaque *pull request* comme un problème, mais chaque problème n'est pas une *pull request*. Pour cette raison, les points de terminaison « Issues » peuvent renvoyer à la fois des problèmes et des *pull requests* dans la réponse. Vous pouvez identifier les *pull requests* par la clé `pull_request`. Sachez que l'identifiant d'une *pull request* renvoyée par les points de terminaison « Issues » sera un identifiant de problème.
-
-Étant donné que le contenu des « Issues » et des *pull request* est assez différent, procédons à un prétraitement mineur pour nous permettre de les distinguer.
-
-## Nettoyer les données
-
-L'extrait ci-dessus de la documentation de GitHub nous indique que la colonne `pull_request` peut être utilisée pour différencier les *issues* et les *pull requests*. Regardons un échantillon aléatoire pour voir quelle est la différence. Comme nous l'avons fait dans [section 3](/course/fr/chapter5/3), nous allons enchaîner `Dataset.shuffle()` et `Dataset.select()` pour créer un échantillon aléatoire, puis compresser `html_url` et ` pull_request` afin que nous puissions comparer les différentes URL :
-
-```py
-sample = issues_dataset.shuffle(seed=666).select(range(3))
-
-# Print out the URL and pull request entries
-for url, pr in zip(sample["html_url"], sample["pull_request"]):
- print(f">> URL: {url}")
- print(f">> Pull request: {pr}\n")
-```
-
-```python out
->> URL: https://github.com/huggingface/datasets/pull/850
->> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/850', 'html_url': 'https://github.com/huggingface/datasets/pull/850', 'diff_url': 'https://github.com/huggingface/datasets/pull/850.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/850.patch'}
-
->> URL: https://github.com/huggingface/datasets/issues/2773
->> Pull request: None
-
->> URL: https://github.com/huggingface/datasets/pull/783
->> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/783', 'html_url': 'https://github.com/huggingface/datasets/pull/783', 'diff_url': 'https://github.com/huggingface/datasets/pull/783.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/783.patch'}
-```
-
-Ici, nous pouvons voir que chaque *pull request* est associée à diverses URL, tandis que les problèmes ordinaires ont une entrée `None`. Nous pouvons utiliser cette distinction pour créer une nouvelle colonne `is_pull_request` qui vérifie si le champ `pull_request` est `None` ou non :
-
-```py
-issues_dataset = issues_dataset.map(
- lambda x: {"is_pull_request": False if x["pull_request"] is None else True}
-)
-```
-
-
-
-✏️ **Essayez !** Calculez le temps moyen nécessaire pour résoudre les problèmes dans 🤗 *Datasets*. Vous pouvez trouver la fonction `Dataset.filter()` utile pour filtrer les demandes d'extraction et les problèmes ouverts. Vous pouvez utiliser la fonction `Dataset.set_format()` pour convertir le jeu de données en un `DataFrame` afin que vous puissiez facilement manipuler les horodatages `created_at` et `closed_at`. Pour les points bonus, calculez le temps moyen nécessaire pour fermer les *pull_requests*.
-
-
-
-Bien que nous puissions continuer à nettoyer davantage le jeu de données en supprimant ou en renommant certaines colonnes, il est généralement recommandé de le conserver aussi brut que possible à ce stade afin qu'il puisse être facilement utilisé dans plusieurs applications.
-
-Avant de pousser notre jeu de données vers le *Hub* d’Hugging Face, traitons une chose manquante : les commentaires associés à chaque problème et *pull requests*. Nous les ajouterons ensuite avec l'API GitHub REST !
-
-## Enrichir le jeu de données
-
-Comme le montre la capture d'écran suivante, les commentaires associés à un problème ou à une *pull request* fournissent une riche source d'informations, en particulier si nous souhaitons créer un moteur de recherche pour répondre aux requêtes des utilisateurs sur la bibliothèque.
-
-
-
-
-
-L'API REST GitHub fournit un point de terminaison [`Comments`](https://docs.github.com/en/rest/reference/issues#list-issue-comments) qui renvoie tous les commentaires associés à un numéro de problème. Testons le point de terminaison pour voir ce qu'il renvoie :
-
-```py
-issue_number = 2792
-url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
-response = requests.get(url, headers=headers)
-response.json()
-```
-
-```python out
-[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/comments/897594128',
- 'html_url': 'https://github.com/huggingface/datasets/pull/2792#issuecomment-897594128',
- 'issue_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
- 'id': 897594128,
- 'node_id': 'IC_kwDODunzps41gDMQ',
- 'user': {'login': 'bhavitvyamalik',
- 'id': 19718818,
- 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
- 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
- 'gravatar_id': '',
- 'url': 'https://api.github.com/users/bhavitvyamalik',
- 'html_url': 'https://github.com/bhavitvyamalik',
- 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
- 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
- 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
- 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
- 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
- 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
- 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
- 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
- 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
- 'type': 'User',
- 'site_admin': False},
- 'created_at': '2021-08-12T12:21:52Z',
- 'updated_at': '2021-08-12T12:31:17Z',
- 'author_association': 'CONTRIBUTOR',
- 'body': "@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?",
- 'performed_via_github_app': None}]
-```
-
-Nous pouvons voir que le commentaire est stocké dans le champ `body`. Ecrivons donc une fonction simple qui renvoie tous les commentaires associés à un problème en sélectionnant le contenu `body` pour chaque élément dans `response.json()` :
-
-```py
-def get_comments(issue_number):
- url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
- response = requests.get(url, headers=headers)
- return [r["body"] for r in response.json()]
-
-
-# Testez notre fonction fonctionne comme prévu
-get_comments(2792)
-```
-
-```python out
-["@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?"]
-```
-
-Cela a l'air bien. Utilisons `Dataset.map()` pour ajouter une nouvelle colonne `comments` à chaque problème de notre jeu de données :
-
-```py
-# Selon votre connexion internet, cela peut prendre quelques minutes...
-issues_with_comments_dataset = issues_dataset.map(
- lambda x: {"comments": get_comments(x["number"])}
-)
-```
-
-La dernière étape consiste à enregistrer le jeu de données augmentées avec nos données brutes afin que nous puissions les pousser tous les deux vers le *Hub* :
-
-```py
-issues_with_comments_dataset.to_json("issues-datasets-with-comments.jsonl")
-```
-
-## Téléchargement du jeu de données sur le *Hub* d’Hugging Face
-
-
-
-Maintenant que nous avons notre jeu de données augmenté, il est temps de le pousser vers le *Hub* afin que nous puissions le partager avec la communauté ! Pour télécharger le jeu de données, nous utilisons la [bibliothèque 🤗 *Hub*](https://github.com/huggingface/huggingface_hub), qui nous permet d'interagir avec le *Hub* d’Hugging Face via une API Python. 🤗 *Hub* est préinstallé avec 🤗 *Transformers*, nous pouvons donc l'utiliser directement. Par exemple, nous pouvons utiliser la fonction `list_datasets()` pour obtenir des informations sur tous les ensembles de données publics actuellement hébergés sur le *Hub*:
-
-```py
-from huggingface_hub import list_datasets
-
-all_datasets = list_datasets()
-print(f"Number of datasets on Hub: {len(all_datasets)}")
-print(all_datasets[0])
-```
-
-```python out
-Number of datasets on Hub: 1487
-Dataset Name: acronym_identification, Tags: ['annotations_creators:expert-generated', 'language_creators:found', 'languages:en', 'licenses:mit', 'multilinguality:monolingual', 'size_categories:10K
-
-✏️ **Essayez !** Utilisez votre nom d'utilisateur et votre mot de passe Hugging Face pour obtenir un jeton et créer un dépôt vide appelé `github-issues`. N'oubliez pas de **n'enregistrez jamais vos informations d'identification** dans Colab ou tout autre référentiel car ces informations peuvent être exploitées par de mauvais individus.
-
-
-
-Ensuite, clonons le dépôt du Hub sur notre machine locale et copions-y notre fichier jeu de données. 🤗 *Hub* fournit une classe `Repository` pratique qui encapsule de nombreuses commandes Git courantes. Donc pour cloner le dépôt distant, nous devons simplement fournir l'URL et le chemin local vers lesquels nous souhaitons cloner :
-
-```py
-from huggingface_hub import Repository
-
-repo = Repository(local_dir="github-issues", clone_from=repo_url)
-!cp datasets-issues-with-comments.jsonl github-issues/
-```
-
-Par défaut, diverses extensions de fichiers (telles que *.bin*, *.gz* et *.zip*) sont suivies avec Git LFS afin que les fichiers volumineux puissent être versionnés dans le même workflow Git. Vous pouvez trouver une liste des extensions de fichiers suivis dans le fichier *.gitattributes* du référentiel. Pour inclure le format JSON Lines dans la liste, nous pouvons exécuter la commande suivante :
-
-```py
-repo.lfs_track("*.jsonl")
-```
-
-Ensuite, nous pouvons utiliser `Repository.push_to_hub()` pour pousser le jeu de données vers le *Hub* :
-
-```py
-repo.push_to_hub()
-```
-
-Si nous naviguons vers l'URL contenue dans `repo_url`, nous devrions maintenant voir que notre fichier de jeu de données a été téléchargé.
-
-
-
-
-
-À partir de là, n'importe qui peut télécharger le jeu de données en fournissant simplement `load_dataset()` avec l'ID du référentiel comme argument `path` :
-
-```py
-remote_dataset = load_dataset("lewtun/github-issues", split="train")
-remote_dataset
-```
-
-```python out
-Dataset({
- features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
- num_rows: 2855
-})
-```
-
-Cool, nous avons poussé notre jeu de données vers le *Hub* et il est disponible pour que d'autres puissent l'utiliser ! Il ne reste plus qu'une chose importante à faire : ajouter une _carte de jeu de données_ qui explique comment le corpus a été créé et fournit d'autres informations utiles à la communauté.
-
-
-
-💡 Vous pouvez également télécharger un jeu de données sur le *Hub* directement depuis le terminal en utilisant `huggingface-cli` et un peu de magie Git. Consultez le [guide de 🤗 *Datasets*](https://huggingface.co/docs/datasets/share.html#add-a-community-dataset) pour savoir comment procéder.
-
-
-
-## Création d'une carte pour un jeu de données
-
-Des jeux de données bien documentés sont plus susceptibles d'être utiles aux autres (y compris à vous-même) car ils fournissent le contexte permettant aux utilisateurs de décider si le jeu de données est pertinent pour leur tâche et d'évaluer les biais potentiels ou les risques associés à l'utilisation du jeu de données.
-
-Sur le *Hub*, ces informations sont stockées dans le fichier *README.md* de chaque dépôt de jeux de données. Il y a deux étapes principales que vous devez suivre avant de créer ce fichier :
-
-1. Utilisez l'[application `datasets-tagging`](https://huggingface.co/datasets/tagging/) pour créer des balises de métadonnées au format YAML. Ces balises sont utilisées pour une variété de fonctionnalités de recherche sur le *Hub* d’Hugging Face et garantissent que votre jeu de données peut être facilement trouvé par les membres de la communauté. Puisque nous avons créé un jeu de données personnalisé ici, vous devrez cloner le référentiel `datasets-tagging` et exécuter l'application localement. Voici à quoi ressemble l'interface :
-
-
-
-
-
-2. Lisez le [guide de 🤗 *Datasets*](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) sur la création des cartes informatives des jeux de données et utilisez-le comme modèle.
-
-Vous pouvez créer le fichier *README.md* directement sur le *Hub* et vous pouvez trouver un modèle de carte dans le dépot `lewtun/github-issues`. Une capture d'écran de la carte remplie est illustrée ci-dessous.
-
-
-
-
-
-
-
-
-✏️ **Essayez !** Utilisez l'application `dataset-tagging` et [le guide de 🤗 *Datasets*](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) pour compléter le fichier *README.md* de votre jeu de données de problèmes GitHub.
-
-
-C’ets tout ! Nous avons vu dans cette section que la création d'un bon jeu de données peut être assez complexe, mais heureusement, le télécharger et le partager avec la communauté ne l'est pas. Dans la section suivante, nous utiliserons notre nouveau jeu de données pour créer un moteur de recherche sémantique avec 🤗 *Datasets* qui peut faire correspondre les questions aux problèmes et commentaires les plus pertinents.
-
-
-
-✏️ **Essayez !** Suivez les étapes que nous avons suivies dans cette section pour créer un jeu de données de problèmes GitHub pour votre bibliothèque open source préférée (choisissez autre chose que 🤗 *Datasets*, bien sûr !). Pour obtenir des points bonus, *finetunez* un classifieur multilabel pour prédire les balises présentes dans le champ `labels`.
-
-
+# Création de votre propre jeu de données
+
+
+
+Parfois, le jeu de données dont vous avez besoin pour créer une application de NLP n'existe pas. Vous devrez donc le créer vous-même. Dans cette section, nous allons vous montrer comment créer un corpus de [problèmes GitHub](https://github.com/features/issues/), qui sont couramment utilisés pour suivre les bogues ou les fonctionnalités dans les dépôts GitHub. Ce corpus pourrait être utilisé à diverses fins, notamment :
+
+* explorer combien de temps il faut pour fermer les problèmes ouverts ou les *pull requests*
+* entraîner d'un _classificateur multilabel_ capable d'étiqueter les problèmes avec des métadonnées basées sur la description du problème (par exemple : « bug », « amélioration » ou « question »)
+* créer un moteur de recherche sémantique pour trouver les problèmes correspondant à la requête d'un utilisateur
+
+Ici, nous nous concentrerons sur la création du corpus, et dans la section suivante, nous aborderons l'application de recherche sémantique. Pour garder les choses méta, nous utiliserons les problèmes GitHub associés à un projet open source populaire : 🤗 *Datasets* ! Voyons comment obtenir les données et explorons les informations contenues dans ces problèmes.
+
+## Obtenir les données
+
+Vous pouvez trouver tous les problèmes dans 🤗 *Datasets* en accédant à l'[onglet « Issues »](https://github.com/huggingface/datasets/issues) du dépôt. Comme le montre la capture d'écran suivante, au moment de la rédaction, il y avait 331 problèmes ouverts et 668 problèmes fermés.
+
+
+
+
+
+Si vous cliquez sur l'un de ces problèmes, vous constaterez qu'il contient un titre, une description et un ensemble d'étiquettes qui caractérisent le problème. Un exemple est montré dans la capture d'écran ci-dessous.
+
+
+
+
+
+Pour télécharger tous les problèmes du dépôt, nous utilisons l'[API REST GitHub](https://docs.github.com/en/rest) pour interroger le point de terminaison [`Issues`](https://docs.github.com/en/rest/reference/issues#list-repository-issues). Ce point de terminaison renvoie une liste d'objets JSON. Chaque objet contenant un grand nombre de champs qui incluent le titre et la description ainsi que des métadonnées sur l'état du problème, etc.
+
+Un moyen pratique de télécharger les problèmes consiste à utiliser la bibliothèque `requests`, qui est la méthode standard pour effectuer des requêtes HTTP en Python. Vous pouvez installer la bibliothèque en exécutant :
+
+```python
+!pip install requests
+```
+
+Une fois la bibliothèque installée, vous pouvez envoyer des requêtes GET au point de terminaison `Issues` en appelant la fonction `requests.get()`. Par exemple, vous pouvez exécuter la commande suivante pour récupérer le premier numéro sur la première page :
+
+```py
+import requests
+
+url = "https://api.github.com/repos/huggingface/datasets/issues?page=1&per_page=1"
+response = requests.get(url)
+```
+
+L'objet `response` contient de nombreuses informations utiles sur la requête, y compris le code d'état HTTP :
+
+```py
+response.status_code
+```
+
+```python out
+200
+```
+
+où un statut `200` signifie que la requête a réussi (vous pouvez trouver une liste des codes de statut HTTP possibles [ici](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)). Ce qui nous intéresse vraiment, cependant, c'est le _payload_, qui peut être consulté dans différents formats comme les octets, les chaînes ou JSON. Comme nous savons que nos problèmes sont au format JSON, examinons la charge utile comme suit :
+
+```py
+response.json()
+```
+
+```python out
+[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
+ 'repository_url': 'https://api.github.com/repos/huggingface/datasets',
+ 'labels_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/labels{/name}',
+ 'comments_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/comments',
+ 'events_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/events',
+ 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
+ 'id': 968650274,
+ 'node_id': 'MDExOlB1bGxSZXF1ZXN0NzEwNzUyMjc0',
+ 'number': 2792,
+ 'title': 'Update GooAQ',
+ 'user': {'login': 'bhavitvyamalik',
+ 'id': 19718818,
+ 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
+ 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
+ 'gravatar_id': '',
+ 'url': 'https://api.github.com/users/bhavitvyamalik',
+ 'html_url': 'https://github.com/bhavitvyamalik',
+ 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
+ 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
+ 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
+ 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
+ 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
+ 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
+ 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
+ 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
+ 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
+ 'type': 'User',
+ 'site_admin': False},
+ 'labels': [],
+ 'state': 'open',
+ 'locked': False,
+ 'assignee': None,
+ 'assignees': [],
+ 'milestone': None,
+ 'comments': 1,
+ 'created_at': '2021-08-12T11:40:18Z',
+ 'updated_at': '2021-08-12T12:31:17Z',
+ 'closed_at': None,
+ 'author_association': 'CONTRIBUTOR',
+ 'active_lock_reason': None,
+ 'pull_request': {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/2792',
+ 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
+ 'diff_url': 'https://github.com/huggingface/datasets/pull/2792.diff',
+ 'patch_url': 'https://github.com/huggingface/datasets/pull/2792.patch'},
+ 'body': '[GooAQ](https://github.com/allenai/gooaq) dataset was recently updated after splits were added for the same. This PR contains new updated GooAQ with train/val/test splits and updated README as well.',
+ 'performed_via_github_app': None}]
+```
+
+Waouh, ça fait beaucoup d'informations ! Nous pouvons voir des champs utiles comme `title`, `body` et `number` qui décrivent le problème, ainsi que des informations sur l'utilisateur GitHub qui a ouvert le problème.
+
+
+
+✏️ **Essayez !** Cliquez sur quelques-unes des URL pour avoir une idée du type d'informations auxquelles chaque problème GitHub est lié.
+
+
+
+Comme décrit dans la [documentation GitHub](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting), les requêtes non authentifiées sont limitées à 60 requêtes par heure. Bien que vous puissiez augmenter le paramètre de requête `per_page` pour réduire le nombre de requêtes que vous effectuez, vous atteindrez toujours la limite de débit sur tout dépôt contenant des milliers de problèmes. Donc, à la place, vous devez suivre les [instructions de GitHub](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) sur la création d'un _jeton d'accès personnel_ afin que vous peut augmenter la limite de débit à 5 000 requêtes par heure. Une fois que vous avez votre *token*, vous pouvez l'inclure dans l'en-tête de la requête :
+
+```py
+GITHUB_TOKEN = xxx # Copy your GitHub token here
+headers = {"Authorization": f"token {GITHUB_TOKEN}"}
+```
+
+
+
+⚠️ Ne partagez pas un *notebook* avec votre `GITHUB_TOKEN` collé dedans. Nous vous recommandons de supprimer la dernière cellule une fois que vous l'avez exécutée pour éviter de divulguer accidentellement ces informations. Mieux encore, stockez le jeton dans un fichier *.env* et utilisez la [bibliothèque `python-dotenv`](https://github.com/theskumar/python-dotenv) pour le charger automatiquement pour vous en tant que variable d'environnement.
+
+
+
+Maintenant que nous avons notre jeton d'accès, créons une fonction qui peut télécharger tous les problèmes depuis un référentiel GitHub :
+
+```py
+import time
+import math
+from pathlib import Path
+import pandas as pd
+from tqdm.notebook import tqdm
+
+
+def fetch_issues(
+ owner="huggingface",
+ repo="datasets",
+ num_issues=10_000,
+ rate_limit=5_000,
+ issues_path=Path("."),
+):
+ if not issues_path.is_dir():
+ issues_path.mkdir(exist_ok=True)
+
+ batch = []
+ all_issues = []
+ per_page = 100 # Number of issues to return per page
+ num_pages = math.ceil(num_issues / per_page)
+ base_url = "https://api.github.com/repos"
+
+ for page in tqdm(range(num_pages)):
+ # Query with state=all to get both open and closed issues
+ query = f"issues?page={page}&per_page={per_page}&state=all"
+ issues = requests.get(f"{base_url}/{owner}/{repo}/{query}", headers=headers)
+ batch.extend(issues.json())
+
+ if len(batch) > rate_limit and len(all_issues) < num_issues:
+ all_issues.extend(batch)
+ batch = [] # Flush batch for next time period
+ print(f"Reached GitHub rate limit. Sleeping for one hour ...")
+ time.sleep(60 * 60 + 1)
+
+ all_issues.extend(batch)
+ df = pd.DataFrame.from_records(all_issues)
+ df.to_json(f"{issues_path}/{repo}-issues.jsonl", orient="records", lines=True)
+ print(
+ f"Downloaded all the issues for {repo}! Dataset stored at {issues_path}/{repo}-issues.jsonl"
+ )
+```
+
+Désormais, lorsque nous appellerons `fetch_issues()`, tous les problèmes seront téléchargés par batchs pour éviter de dépasser la limite de GitHub sur le nombre de requêtes par heure. Le résultat sera stocké dans un fichier _repository_name-issues.jsonl_, où chaque ligne est un objet JSON qui représente un problème. Utilisons cette fonction pour saisir tous les problèmes de 🤗 *Datasets* :
+
+```py
+# En fonction de votre connexion Internet, l'exécution peut prendre plusieurs minutes...
+fetch_issues()
+```
+
+Une fois les problèmes téléchargés, nous pouvons les charger localement en utilisant nos nouvelles compétences de [section 2](/course/fr/chaper5/2) :
+
+```py
+issues_dataset = load_dataset("json", data_files="datasets-issues.jsonl", split="train")
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'timeline_url', 'performed_via_github_app'],
+ num_rows: 3019
+})
+```
+
+Génial, nous avons créé notre premier jeu de données à partir de rien ! Mais pourquoi y a-t-il plusieurs milliers de problèmes alors que l'[onglet « Issues »](https://github.com/huggingface/datasets/issues) de la librairie 🤗 *Datasets* n'affiche qu'environ 1 000 problèmes au total 🤔 ? Comme décrit dans la [documentation GitHub](https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user), c'est parce que nous avons téléchargé toutes les *pull requests* également :
+
+> L'API REST v3 de GitHub considère chaque *pull request* comme un problème, mais chaque problème n'est pas une *pull request*. Pour cette raison, les points de terminaison « Issues » peuvent renvoyer à la fois des problèmes et des *pull requests* dans la réponse. Vous pouvez identifier les *pull requests* par la clé `pull_request`. Sachez que l'identifiant d'une *pull request* renvoyée par les points de terminaison « Issues » sera un identifiant de problème.
+
+Étant donné que le contenu des « Issues » et des *pull request* est assez différent, procédons à un prétraitement mineur pour nous permettre de les distinguer.
+
+## Nettoyer les données
+
+L'extrait ci-dessus de la documentation de GitHub nous indique que la colonne `pull_request` peut être utilisée pour différencier les *issues* et les *pull requests*. Regardons un échantillon aléatoire pour voir quelle est la différence. Comme nous l'avons fait dans [section 3](/course/fr/chapter5/3), nous allons enchaîner `Dataset.shuffle()` et `Dataset.select()` pour créer un échantillon aléatoire, puis compresser `html_url` et ` pull_request` afin que nous puissions comparer les différentes URL :
+
+```py
+sample = issues_dataset.shuffle(seed=666).select(range(3))
+
+# Print out the URL and pull request entries
+for url, pr in zip(sample["html_url"], sample["pull_request"]):
+ print(f">> URL: {url}")
+ print(f">> Pull request: {pr}\n")
+```
+
+```python out
+>> URL: https://github.com/huggingface/datasets/pull/850
+>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/850', 'html_url': 'https://github.com/huggingface/datasets/pull/850', 'diff_url': 'https://github.com/huggingface/datasets/pull/850.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/850.patch'}
+
+>> URL: https://github.com/huggingface/datasets/issues/2773
+>> Pull request: None
+
+>> URL: https://github.com/huggingface/datasets/pull/783
+>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/783', 'html_url': 'https://github.com/huggingface/datasets/pull/783', 'diff_url': 'https://github.com/huggingface/datasets/pull/783.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/783.patch'}
+```
+
+Ici, nous pouvons voir que chaque *pull request* est associée à diverses URL, tandis que les problèmes ordinaires ont une entrée `None`. Nous pouvons utiliser cette distinction pour créer une nouvelle colonne `is_pull_request` qui vérifie si le champ `pull_request` est `None` ou non :
+
+```py
+issues_dataset = issues_dataset.map(
+ lambda x: {"is_pull_request": False if x["pull_request"] is None else True}
+)
+```
+
+
+
+✏️ **Essayez !** Calculez le temps moyen nécessaire pour résoudre les problèmes dans 🤗 *Datasets*. Vous pouvez trouver la fonction `Dataset.filter()` utile pour filtrer les demandes d'extraction et les problèmes ouverts. Vous pouvez utiliser la fonction `Dataset.set_format()` pour convertir le jeu de données en un `DataFrame` afin que vous puissiez facilement manipuler les horodatages `created_at` et `closed_at`. Pour les points bonus, calculez le temps moyen nécessaire pour fermer les *pull_requests*.
+
+
+
+Bien que nous puissions continuer à nettoyer davantage le jeu de données en supprimant ou en renommant certaines colonnes, il est généralement recommandé de le conserver aussi brut que possible à ce stade afin qu'il puisse être facilement utilisé dans plusieurs applications.
+
+Avant de pousser notre jeu de données vers le *Hub* d’Hugging Face, traitons une chose manquante : les commentaires associés à chaque problème et *pull requests*. Nous les ajouterons ensuite avec l'API GitHub REST !
+
+## Enrichir le jeu de données
+
+Comme le montre la capture d'écran suivante, les commentaires associés à un problème ou à une *pull request* fournissent une riche source d'informations, en particulier si nous souhaitons créer un moteur de recherche pour répondre aux requêtes des utilisateurs sur la bibliothèque.
+
+
+
+
+
+L'API REST GitHub fournit un point de terminaison [`Comments`](https://docs.github.com/en/rest/reference/issues#list-issue-comments) qui renvoie tous les commentaires associés à un numéro de problème. Testons le point de terminaison pour voir ce qu'il renvoie :
+
+```py
+issue_number = 2792
+url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
+response = requests.get(url, headers=headers)
+response.json()
+```
+
+```python out
+[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/comments/897594128',
+ 'html_url': 'https://github.com/huggingface/datasets/pull/2792#issuecomment-897594128',
+ 'issue_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
+ 'id': 897594128,
+ 'node_id': 'IC_kwDODunzps41gDMQ',
+ 'user': {'login': 'bhavitvyamalik',
+ 'id': 19718818,
+ 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
+ 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
+ 'gravatar_id': '',
+ 'url': 'https://api.github.com/users/bhavitvyamalik',
+ 'html_url': 'https://github.com/bhavitvyamalik',
+ 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
+ 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
+ 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
+ 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
+ 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
+ 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
+ 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
+ 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
+ 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
+ 'type': 'User',
+ 'site_admin': False},
+ 'created_at': '2021-08-12T12:21:52Z',
+ 'updated_at': '2021-08-12T12:31:17Z',
+ 'author_association': 'CONTRIBUTOR',
+ 'body': "@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?",
+ 'performed_via_github_app': None}]
+```
+
+Nous pouvons voir que le commentaire est stocké dans le champ `body`. Ecrivons donc une fonction simple qui renvoie tous les commentaires associés à un problème en sélectionnant le contenu `body` pour chaque élément dans `response.json()` :
+
+```py
+def get_comments(issue_number):
+ url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
+ response = requests.get(url, headers=headers)
+ return [r["body"] for r in response.json()]
+
+
+# Tester notre fonction fonctionne comme prévu
+get_comments(2792)
+```
+
+```python out
+["@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?"]
+```
+
+Cela a l'air bien. Utilisons `Dataset.map()` pour ajouter une nouvelle colonne `comments` à chaque problème de notre jeu de données :
+
+```py
+# Selon votre connexion internet, cela peut prendre quelques minutes...
+issues_with_comments_dataset = issues_dataset.map(
+ lambda x: {"comments": get_comments(x["number"])}
+)
+```
+
+La dernière étape consiste à enregistrer le jeu de données augmentées avec nos données brutes afin que nous puissions les pousser tous les deux vers le *Hub* :
+
+```py
+issues_with_comments_dataset.to_json("issues-datasets-with-comments.jsonl")
+```
+
+## Téléchargement du jeu de données sur le Hub
+
+
+
+Maintenant que nous avons notre jeu de données augmenté, il est temps de le pousser vers le *Hub* afin que nous puissions le partager avec la communauté ! Pour télécharger le jeu de données, nous utilisons la [bibliothèque 🤗 *Hub*](https://github.com/huggingface/huggingface_hub), qui nous permet d'interagir avec le *Hub* d’Hugging Face via une API Python. 🤗 *Hub* est préinstallé avec 🤗 *Transformers*, nous pouvons donc l'utiliser directement. Par exemple, nous pouvons utiliser la fonction `list_datasets()` pour obtenir des informations sur tous les ensembles de données publics actuellement hébergés sur le *Hub*:
+
+```py
+from huggingface_hub import list_datasets
+
+all_datasets = list_datasets()
+print(f"Number of datasets on Hub: {len(all_datasets)}")
+print(all_datasets[0])
+```
+
+```python out
+Number of datasets on Hub: 1487
+Dataset Name: acronym_identification, Tags: ['annotations_creators:expert-generated', 'language_creators:found', 'languages:en', 'licenses:mit', 'multilinguality:monolingual', 'size_categories:10K
+
+✏️ **Essayez !** Utilisez votre nom d'utilisateur et votre mot de passe Hugging Face pour obtenir un jeton et créer un dépôt vide appelé `github-issues`. N'oubliez pas de **n'enregistrez jamais vos informations d'identification** dans Colab ou tout autre référentiel car ces informations peuvent être exploitées par de mauvais individus.
+
+
+
+Ensuite, clonons le dépôt du Hub sur notre machine locale et copions-y notre fichier jeu de données. 🤗 *Hub* fournit une classe `Repository` pratique qui encapsule de nombreuses commandes Git courantes. Donc pour cloner le dépôt distant, nous devons simplement fournir l'URL et le chemin local vers lesquels nous souhaitons cloner :
+
+```py
+from huggingface_hub import Repository
+
+repo = Repository(local_dir="github-issues", clone_from=repo_url)
+!cp datasets-issues-with-comments.jsonl github-issues/
+```
+
+Par défaut, diverses extensions de fichiers (telles que *.bin*, *.gz* et *.zip*) sont suivies avec Git LFS afin que les fichiers volumineux puissent être versionnés dans le même workflow Git. Vous pouvez trouver une liste des extensions de fichiers suivis dans le fichier *.gitattributes* du référentiel. Pour inclure le format JSON Lines dans la liste, nous pouvons exécuter la commande suivante :
+
+```py
+repo.lfs_track("*.jsonl")
+```
+
+Ensuite, nous pouvons utiliser `Repository.push_to_hub()` pour pousser le jeu de données vers le *Hub* :
+
+```py
+repo.push_to_hub()
+```
+
+Si nous naviguons vers l'URL contenue dans `repo_url`, nous devrions maintenant voir que notre fichier de jeu de données a été téléchargé.
+
+
+
+
+
+À partir de là, n'importe qui peut télécharger le jeu de données en fournissant simplement `load_dataset()` avec l'ID du référentiel comme argument `path` :
+
+```py
+remote_dataset = load_dataset("lewtun/github-issues", split="train")
+remote_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
+ num_rows: 2855
+})
+```
+
+Cool, nous avons poussé notre jeu de données vers le *Hub* et il est disponible pour que d'autres puissent l'utiliser ! Il ne reste plus qu'une chose importante à faire : ajouter une _carte de jeu de données_ qui explique comment le corpus a été créé et fournit d'autres informations utiles à la communauté.
+
+
+
+💡 Vous pouvez également télécharger un jeu de données sur le *Hub* directement depuis le terminal en utilisant `huggingface-cli` et un peu de magie Git. Consultez le [guide de 🤗 *Datasets*](https://huggingface.co/docs/datasets/share.html#add-a-community-dataset) pour savoir comment procéder.
+
+
+
+## Création d'une carte pour un jeu de données
+
+Des jeux de données bien documentés sont plus susceptibles d'être utiles aux autres (y compris à vous-même) car ils fournissent le contexte permettant aux utilisateurs de décider si le jeu de données est pertinent pour leur tâche et d'évaluer les biais potentiels ou les risques associés à l'utilisation du jeu de données.
+
+Sur le *Hub*, ces informations sont stockées dans le fichier *README.md* de chaque dépôt de jeux de données. Il y a deux étapes principales que vous devez suivre avant de créer ce fichier :
+
+1. Utilisez l'[application `datasets-tagging`](https://huggingface.co/datasets/tagging/) pour créer des balises de métadonnées au format YAML. Ces balises sont utilisées pour une variété de fonctionnalités de recherche sur le *Hub* d’Hugging Face et garantissent que votre jeu de données peut être facilement trouvé par les membres de la communauté. Puisque nous avons créé un jeu de données personnalisé ici, vous devrez cloner le référentiel `datasets-tagging` et exécuter l'application localement. Voici à quoi ressemble l'interface :
+
+
+
+
+
+2. Lisez le [guide de 🤗 *Datasets*](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) sur la création des cartes informatives des jeux de données et utilisez-le comme modèle.
+
+Vous pouvez créer le fichier *README.md* directement sur le *Hub* et vous pouvez trouver un modèle de carte dans le dépot `lewtun/github-issues`. Une capture d'écran de la carte remplie est illustrée ci-dessous.
+
+
+
+
+
+
+
+
+✏️ **Essayez !** Utilisez l'application `dataset-tagging` et [le guide de 🤗 *Datasets*](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) pour compléter le fichier *README.md* de votre jeu de données de problèmes GitHub.
+
+
+C’est tout ! Nous avons vu dans cette section que la création d'un bon jeu de données peut être assez complexe, mais heureusement, le télécharger et le partager avec la communauté ne l'est pas. Dans la section suivante, nous utiliserons notre nouveau jeu de données pour créer un moteur de recherche sémantique avec 🤗 *Datasets* qui peut faire correspondre les questions aux problèmes et commentaires les plus pertinents.
+
+
+
+✏️ **Essayez !** Suivez les étapes que nous avons suivies dans cette section pour créer un jeu de données de problèmes GitHub pour votre bibliothèque open source préférée (choisissez autre chose que 🤗 *Datasets*, bien sûr !). Pour obtenir des points bonus, *finetunez* un classifieur multilabel pour prédire les balises présentes dans le champ `labels`.
+
+
diff --git a/chapters/fr/chapter5/6.mdx b/chapters/fr/chapter5/6.mdx
index 68cf7373e..dcb42deb5 100644
--- a/chapters/fr/chapter5/6.mdx
+++ b/chapters/fr/chapter5/6.mdx
@@ -1,530 +1,530 @@
-
-
-# Recherche sémantique avec FAISS
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Dans [section 5](/course/fr/chapter5/5), nous avons créé un jeu de données de problèmes et de commentaires GitHub à partir du dépôt 🤗 *Datasets*. Dans cette section, nous utilisons ces informations pour créer un moteur de recherche qui peut nous aider à trouver des réponses à nos questions les plus urgentes sur la bibliothèque !
-
-
-
-## Utilisation des enchâssements pour la recherche sémantique
-
-Comme nous l'avons vu dans le [Chapitre 1](/course/fr/chapter1), les modèles de langage basés sur les *transformers* représentent chaque *token* dans une étendue de texte sous la forme d'un _enchâssement_. Il s'avère que l'on peut regrouper les enchâssements individuels pour créer une représentation vectorielle pour des phrases entières, des paragraphes ou (dans certains cas) des documents. Ces enchâssements peuvent ensuite être utilisés pour trouver des documents similaires dans le corpus en calculant la similarité du produit scalaire (ou une autre métrique de similarité) entre chaque enchâssement et en renvoyant les documents avec le plus grand chevauchement.
-
-Dans cette section, nous utilisons les enchâssements pour développer un moteur de recherche sémantique. Ces moteurs de recherche offrent plusieurs avantages par rapport aux approches conventionnelles basées sur la correspondance des mots-clés dans une requête avec les documents.
-
-
-
-
-
-
-## Chargement et préparation du jeu de données
-
-La première chose que nous devons faire est de télécharger notre jeu de données de problèmes GitHub. Utilisons la bibliothèque 🤗 *Hub* pour résoudre l'URL où notre fichier est stocké sur le *Hib* d’Hugging Face :
-
-```py
-from huggingface_hub import hf_hub_url
-
-data_files = hf_hub_url(
- repo_id="lewtun/github-issues",
- filename="datasets-issues-with-comments.jsonl",
- repo_type="dataset",
-)
-```
-
-Avec l'URL stocké dans `data_files`, nous pouvons ensuite charger le jeu de données distant en utilisant la méthode introduite dans [section 2](/course/fr/chapter5/2) :
-
-```py
-from datasets import load_dataset
-
-issues_dataset = load_dataset("json", data_files=data_files, split="train")
-issues_dataset
-```
-
-```python out
-Dataset({
- features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
- num_rows: 2855
-})
-```
-
-Ici, nous avons spécifié l’échantillon `train` par défaut dans `load_dataset()`, de sorte que cela renvoie un `Dataset` au lieu d'un `DatasetDict`. La première chose à faire est de filtrer les *pull requests* car celles-ci ont tendance à être rarement utilisées pour répondre aux requêtes des utilisateurs et introduiront du bruit dans notre moteur de recherche. Comme cela devrait être familier maintenant, nous pouvons utiliser la fonction `Dataset.filter()` pour exclure ces lignes de notre jeu de données. Pendant que nous y sommes, filtrons également les lignes sans commentaires, car celles-ci ne fournissent aucune réponse aux requêtes des utilisateurs :
-
-```py
-issues_dataset = issues_dataset.filter(
- lambda x: (x["is_pull_request"] == False and len(x["comments"]) > 0)
-)
-issues_dataset
-```
-
-```python out
-Dataset({
- features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
- num_rows: 771
-})
-```
-
-Nous pouvons voir qu'il y a beaucoup de colonnes dans notre jeu de données, dont la plupart n'ont pas besoin de construire notre moteur de recherche. Du point de vue de la recherche, les colonnes les plus informatives sont `title`, `body` et `comments`, tandis que `html_url` nous fournit un lien vers le problème source. Utilisons la fonction `Dataset.remove_columns()` pour supprimer le reste :
-
-```py
-columns = issues_dataset.column_names
-columns_to_keep = ["title", "body", "html_url", "comments"]
-columns_to_remove = set(columns_to_keep).symmetric_difference(columns)
-issues_dataset = issues_dataset.remove_columns(columns_to_remove)
-issues_dataset
-```
-
-```python out
-Dataset({
- features: ['html_url', 'title', 'comments', 'body'],
- num_rows: 771
-})
-```
-
-Pour créer nos enchâssements, nous ajoutons à chaque commentaire le titre et le corps du problème, car ces champs contiennent des informations contextuelles utiles. Étant donné que notre colonne `comments` est actuellement une liste de commentaires pour chaque problème, nous devons « éclater » la colonne afin que chaque ligne se compose d'un *tuple* `(html_url, title, body, comment)`. Dans Pandas, nous pouvons le faire avec la fonction [`DataFrame.explode()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html), qui crée une nouvelle ligne pour chaque élément dans une colonne de type liste, tout en répliquant toutes les autres valeurs de colonne. Pour voir cela en action, passons d'abord au format `DataFrame` de Pandas :
-
-```py
-issues_dataset.set_format("pandas")
-df = issues_dataset[:]
-```
-
-Si nous inspectons la première ligne de ce `DataFrame`, nous pouvons voir qu'il y a quatre commentaires associés à ce problème :
-
-```py
-df["comments"][0].tolist()
-```
-
-```python out
-['the bug code locate in :\r\n if data_args.task_name is not None:\r\n # Downloading and loading a dataset from the hub.\r\n datasets = load_dataset("glue", data_args.task_name, cache_dir=model_args.cache_dir)',
- 'Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com\r\n\r\nNormally, it should work if you wait a little and then retry.\r\n\r\nCould you please confirm if the problem persists?',
- 'cannot connect,even by Web browser,please check that there is some problems。',
- 'I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...']
-```
-
-Lorsque nous décomposons `df`, nous nous attendons à obtenir une ligne pour chacun de ces commentaires. Vérifions si c'est le cas :
-
-```py
-comments_df = df.explode("comments", ignore_index=True)
-comments_df.head(4)
-```
-
-
-
-
- |
- html_url |
- title |
- comments |
- body |
-
-
-
-
- 0 |
- https://github.com/huggingface/datasets/issues/2787 |
- ConnectionError: Couldn't reach https://raw.githubusercontent.com |
- the bug code locate in :\r\n if data_args.task_name is not None... |
- Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
-
-
- 1 |
- https://github.com/huggingface/datasets/issues/2787 |
- ConnectionError: Couldn't reach https://raw.githubusercontent.com |
- Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com... |
- Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
-
-
- 2 |
- https://github.com/huggingface/datasets/issues/2787 |
- ConnectionError: Couldn't reach https://raw.githubusercontent.com |
- cannot connect,even by Web browser,please check that there is some problems。 |
- Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
-
-
- 3 |
- https://github.com/huggingface/datasets/issues/2787 |
- ConnectionError: Couldn't reach https://raw.githubusercontent.com |
- I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem... |
- Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
-
-
-
-
-Génial, nous pouvons voir que les lignes ont été répliquées, avec la colonne `comments` contenant les commentaires individuels ! Maintenant que nous en avons fini avec Pandas, nous pouvons rapidement revenir à un `Dataset` en chargeant le `DataFrame` en mémoire :
-
-```py
-from datasets import Dataset
-
-comments_dataset = Dataset.from_pandas(comments_df)
-comments_dataset
-```
-
-```python out
-Dataset({
- features: ['html_url', 'title', 'comments', 'body'],
- num_rows: 2842
-})
-```
-
-D'accord, cela nous a donné quelques milliers de commentaires avec lesquels travailler !
-
-
-
-
-✏️ **Essayez !** Voyez si vous pouvez utiliser `Dataset.map()` pour exploser la colonne `comments` de `issues_dataset` _sans_ recourir à l'utilisation de Pandas. C'est un peu délicat. La section [« Batch mapping »](https://huggingface.co/docs/datasets/v1.12.1/about_map_batch.html?batch-mapping#batch-mapping) de la documentation 🤗 *Datasets* peut être utile pour cette tâche.
-
-
-
-Maintenant que nous avons un commentaire par ligne, créons une nouvelle colonne `comments_length` contenant le nombre de mots par commentaire :
-
-```py
-comments_dataset = comments_dataset.map(
- lambda x: {"comment_length": len(x["comments"].split())}
-)
-```
-
-Nous pouvons utiliser cette nouvelle colonne pour filtrer les commentaires courts incluant généralement des éléments tels que « cc @lewtun » ou « Merci ! » qui ne sont pas pertinents pour notre moteur de recherche. Il n'y a pas de nombre précis à sélectionner pour le filtre mais 15 mots semblent être un bon début :
-
-```py
-comments_dataset = comments_dataset.filter(lambda x: x["comment_length"] > 15)
-comments_dataset
-```
-
-```python out
-Dataset({
- features: ['html_url', 'title', 'comments', 'body', 'comment_length'],
- num_rows: 2098
-})
-```
-
-Après avoir un peu nettoyé notre jeu de données, concaténons le titre, la description et les commentaires du problème dans une nouvelle colonne `text`. Comme d'habitude, nous allons écrire une fonction simple que nous pouvons passer à `Dataset.map()` :
-
-```py
-def concatenate_text(examples):
- return {
- "text": examples["title"]
- + " \n "
- + examples["body"]
- + " \n "
- + examples["comments"]
- }
-
-
-comments_dataset = comments_dataset.map(concatenate_text)
-```
-
-Nous sommes enfin prêts à créer des enchâssements ! Jetons un coup d'œil.
-
-## Création d’enchâssements pour les textes
-
-Nous avons vu dans [Chapitre 2](/course/fr/chapter2) que nous pouvons obtenir des enchâssements de *tokens* en utilisant la classe `AutoModel`. Tout ce que nous avons à faire est de choisir un *checkpoint* approprié à partir duquel charger le modèle. Heureusement, il existe une bibliothèque appelée `sentence-transformers` dédiée à la création d’enchâssements. Comme décrit dans la [documentation de la bibliothèque](https://www.sbert.net/examples/applications/semantic-search/README.html#symmetric-vs-asymmetric-semantic-search), notre cas d'utilisation est un exemple de _recherche sémantique asymétrique_. En effet, nous avons une requête courte dont nous aimerions trouver la réponse dans un document plus long, par exemple un commentaire à un problème. Le [tableau de présentation des modèles](https://www.sbert.net/docs/pretrained_models.html#model-overview) de la documentation indique que le *checkpoint* `multi-qa-mpnet-base-dot-v1` a les meilleures performances pour la recherche sémantique. Utilisons donc le pour notre application. Nous allons également charger le *tokenizer* en utilisant le même *checkpoint* :
-
-{#if fw === 'pt'}
-
-```py
-from transformers import AutoTokenizer, AutoModel
-
-model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
-tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
-model = AutoModel.from_pretrained(model_ckpt)
-```
-
-Pour accélérer le processus, il est utile de placer le modèle et les entrées sur un périphérique GPU, alors faisons-le maintenant :
-
-```py
-import torch
-
-device = torch.device("cuda")
-model.to(device)
-```
-
-{:else}
-
-```py
-from transformers import AutoTokenizer, TFAutoModel
-
-model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
-tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
-model = TFAutoModel.from_pretrained(model_ckpt, from_pt=True)
-```
-
-Notez que nous avons défini `from_pt=True` comme argument de la méthode `from_pretrained()`. C'est parce que le point de contrôle `multi-qa-mpnet-base-dot-v1` n'a que des poids PyTorch. Donc définir `from_pt=True` converti automatiquement au format TensorFlow pour nous. Comme vous pouvez le voir, il est très simple de passer d'un *framework* à l'autre dans 🤗 *Transformers* !
-
-{/if}
-
-Comme nous l'avons mentionné précédemment, nous aimerions représenter chaque entrée dans notre corpus de problèmes GitHub comme un vecteur unique. Nous devons donc regrouper ou faire la moyenne de nos enchâssements de *tokens* d'une manière ou d'une autre. Une approche populaire consiste à effectuer un *regroupement CLS* sur les sorties de notre modèle, où nous collectons simplement le dernier état caché pour le *token* spécial `[CLS]`. La fonction suivante fait ça pour nous :
-
-```py
-def cls_pooling(model_output):
- return model_output.last_hidden_state[:, 0]
-```
-
-Ensuite, nous allons créer une fonction utile qui va tokeniser une liste de documents, placer les tenseurs dans le GPU, les donner au modèle et enfin appliquer le regroupement CLS aux sorties :
-
-{#if fw === 'pt'}
-
-```py
-def get_embeddings(text_list):
- encoded_input = tokenizer(
- text_list, padding=True, truncation=True, return_tensors="pt"
- )
- encoded_input = {k: v.to(device) for k, v in encoded_input.items()}
- model_output = model(**encoded_input)
- return cls_pooling(model_output)
-```
-
-Nous pouvons tester le fonctionnement de la fonction en lui donnant la première entrée textuelle de notre corpus et en inspectant la forme de sortie :
-
-```py
-embedding = get_embeddings(comments_dataset["text"][0])
-embedding.shape
-```
-
-```python out
-torch.Size([1, 768])
-```
-
-Super ! Nous avons converti la première entrée de notre corpus en un vecteur à 768 dimensions. Nous pouvons utiliser `Dataset.map()` pour appliquer notre fonction `get_embeddings()` à chaque ligne de notre corpus. Créons donc une nouvelle colonne `embeddings` comme suit :
-
-```py
-embeddings_dataset = comments_dataset.map(
- lambda x: {"embeddings": get_embeddings(x["text"]).detach().cpu().numpy()[0]}
-)
-```
-
-{:else}
-
-```py
-def get_embeddings(text_list):
- encoded_input = tokenizer(
- text_list, padding=True, truncation=True, return_tensors="tf"
- )
- encoded_input = {k: v for k, v in encoded_input.items()}
- model_output = model(**encoded_input)
- return cls_pooling(model_output)
-```
-
-Nous pouvons tester le fonctionnement de la fonction en lui donnant la première entrée textuelle de notre corpus et en inspectant la forme de sortie :
-
-```py
-embedding = get_embeddings(comments_dataset["text"][0])
-embedding.shape
-```
-
-```python out
-TensorShape([1, 768])
-```
-
-Super ! Nous avons converti la première entrée de notre corpus en un vecteur à 768 dimensions. Nous pouvons utiliser `Dataset.map()` pour appliquer notre fonction `get_embeddings()` à chaque ligne de notre corpus. Créons donc une nouvelle colonne `embeddings` comme suit :
-
-```py
-embeddings_dataset = comments_dataset.map(
- lambda x: {"embeddings": get_embeddings(x["text"]).numpy()[0]}
-)
-```
-
-{/if}
-
-
-Notez que nous avons converti les enchâssements en tableaux NumPy. C'est parce que 🤗 *Datasets* nécessite ce format lorsque nous essayons de les indexer avec FAISS, ce que nous ferons ensuite.
-
-## Utilisation de FAISS pour une recherche de similarité efficace
-
-Maintenant que nous avons un jeu de données d'incorporations, nous avons besoin d'un moyen de les rechercher. Pour ce faire, nous utiliserons une structure de données spéciale dans 🤗 *Datasets* appelée _FAISS index_. [FAISS](https://faiss.ai/) (abréviation de Facebook AI Similarity Search) est une bibliothèque qui fournit des algorithmes efficaces pour rechercher et regrouper rapidement des vecteurs d'intégration.
-
-L'idée de base derrière FAISS est de créer une structure de données spéciale appelée un _index_ qui permet de trouver quels plongements sont similaires à un plongement d'entrée. Créer un index FAISS dans 🤗 *Datasets* est simple -- nous utilisons la fonction `Dataset.add_faiss_index()` et spécifions quelle colonne de notre jeu de données nous aimerions indexer :
-
-```py
-embeddings_dataset.add_faiss_index(column="embeddings")
-```
-
-Nous pouvons maintenant effectuer des requêtes sur cet index en effectuant une recherche des voisins les plus proches avec la fonction `Dataset.get_nearest_examples()`. Testons cela en enchâssant d'abord une question comme suit :
-
-{#if fw === 'pt'}
-
-```py
-question = "How can I load a dataset offline?"
-question_embedding = get_embeddings([question]).cpu().detach().numpy()
-question_embedding.shape
-```
-
-```python out
-torch.Size([1, 768])
-```
-
-{:else}
-
-```py
-question = "How can I load a dataset offline?"
-question_embedding = get_embeddings([question]).numpy()
-question_embedding.shape
-```
-
-```python out
-(1, 768)
-```
-
-{/if}
-
-Tout comme avec les documents, nous avons maintenant un vecteur de 768 dimensions représentant la requête. Nous pouvons le comparer à l’ensemble du corpus pour trouver les enchâssements les plus similaires :
-
-```py
-scores, samples = embeddings_dataset.get_nearest_examples(
- "embeddings", question_embedding, k=5
-)
-```
-
-La fonction `Dataset.get_nearest_examples()` renvoie un *tuple* de scores qui classent le chevauchement entre la requête et le document, et un jeu correspondant d'échantillons (ici, les 5 meilleures correspondances). Collectons-les dans un `pandas.DataFrame` afin de pouvoir les trier facilement :
-
-```py
-import pandas as pd
-
-samples_df = pd.DataFrame.from_dict(samples)
-samples_df["scores"] = scores
-samples_df.sort_values("scores", ascending=False, inplace=True)
-```
-
-Nous pouvons maintenant parcourir les premières lignes pour voir dans quelle mesure notre requête correspond aux commentaires disponibles :
-
-```py
-for _, row in samples_df.iterrows():
- print(f"COMMENT: {row.comments}")
- print(f"SCORE: {row.scores}")
- print(f"TITLE: {row.title}")
- print(f"URL: {row.html_url}")
- print("=" * 50)
- print()
-```
-
-```python out
-"""
-COMMENT: Requiring online connection is a deal breaker in some cases unfortunately so it'd be great if offline mode is added similar to how `transformers` loads models offline fine.
-
-@mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
-SCORE: 25.505046844482422
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-
-COMMENT: The local dataset builders (csv, text , json and pandas) are now part of the `datasets` package since #1726 :)
-You can now use them offline
-\`\`\`python
-datasets = load_dataset("text", data_files=data_files)
-\`\`\`
-
-We'll do a new release soon
-SCORE: 24.555509567260742
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-
-COMMENT: I opened a PR that allows to reload modules that have already been loaded once even if there's no internet.
-
-Let me know if you know other ways that can make the offline mode experience better. I'd be happy to add them :)
-
-I already note the "freeze" modules option, to prevent local modules updates. It would be a cool feature.
-
-----------
-
-> @mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
-
-Indeed `load_dataset` allows to load remote dataset script (squad, glue, etc.) but also you own local ones.
-For example if you have a dataset script at `./my_dataset/my_dataset.py` then you can do
-\`\`\`python
-load_dataset("./my_dataset")
-\`\`\`
-and the dataset script will generate your dataset once and for all.
-
-----------
-
-About I'm looking into having `csv`, `json`, `text`, `pandas` dataset builders already included in the `datasets` package, so that they are available offline by default, as opposed to the other datasets that require the script to be downloaded.
-cf #1724
-SCORE: 24.14896583557129
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-
-COMMENT: > here is my way to load a dataset offline, but it **requires** an online machine
->
-> 1. (online machine)
->
-> ```
->
-> import datasets
->
-> data = datasets.load_dataset(...)
->
-> data.save_to_disk(/YOUR/DATASET/DIR)
->
-> ```
->
-> 2. copy the dir from online to the offline machine
->
-> 3. (offline machine)
->
-> ```
->
-> import datasets
->
-> data = datasets.load_from_disk(/SAVED/DATA/DIR)
->
-> ```
->
->
->
-> HTH.
-
-
-SCORE: 22.893993377685547
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-
-COMMENT: here is my way to load a dataset offline, but it **requires** an online machine
-1. (online machine)
-\`\`\`
-import datasets
-data = datasets.load_dataset(...)
-data.save_to_disk(/YOUR/DATASET/DIR)
-\`\`\`
-2. copy the dir from online to the offline machine
-3. (offline machine)
-\`\`\`
-import datasets
-data = datasets.load_from_disk(/SAVED/DATA/DIR)
-\`\`\`
-
-HTH.
-SCORE: 22.406635284423828
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-"""
-```
-
-Pas mal ! Notre deuxième résultat semble correspondre à la requête.
-
-
-
-✏️ **Essayez !** Créez votre propre requête et voyez si vous pouvez trouver une réponse dans les documents récupérés. Vous devrez peut-être augmenter le paramètre `k` dans `Dataset.get_nearest_examples()` pour élargir la recherche.
-
-
+
+
+# Recherche sémantique avec FAISS
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Dans [section 5](/course/fr/chapter5/5), nous avons créé un jeu de données de problèmes et de commentaires GitHub à partir du dépôt 🤗 *Datasets*. Dans cette section, nous utilisons ces informations pour créer un moteur de recherche qui peut nous aider à trouver des réponses à nos questions les plus urgentes sur la bibliothèque !
+
+
+
+## Utilisation des enchâssements pour la recherche sémantique
+
+Comme nous l'avons vu dans le [chapitre 1](/course/fr/chapter1), les modèles de langage basés sur les *transformers* représentent chaque *token* dans une étendue de texte sous la forme d'un _enchâssement_. Il s'avère que l'on peut regrouper les enchâssements individuels pour créer une représentation vectorielle pour des phrases entières, des paragraphes ou (dans certains cas) des documents. Ces enchâssements peuvent ensuite être utilisés pour trouver des documents similaires dans le corpus en calculant la similarité du produit scalaire (ou une autre métrique de similarité) entre chaque enchâssement et en renvoyant les documents avec le plus grand chevauchement.
+
+Dans cette section, nous utilisons les enchâssements pour développer un moteur de recherche sémantique. Ces moteurs de recherche offrent plusieurs avantages par rapport aux approches conventionnelles basées sur la correspondance des mots-clés dans une requête avec les documents.
+
+
+
+
+
+
+## Chargement et préparation du jeu de données
+
+La première chose que nous devons faire est de télécharger notre jeu de données de problèmes GitHub. Utilisons la bibliothèque 🤗 *Hub* pour résoudre l'URL où notre fichier est stocké sur le *Hub* d’Hugging Face :
+
+```py
+from huggingface_hub import hf_hub_url
+
+data_files = hf_hub_url(
+ repo_id="lewtun/github-issues",
+ filename="datasets-issues-with-comments.jsonl",
+ repo_type="dataset",
+)
+```
+
+Avec l'URL stocké dans `data_files`, nous pouvons ensuite charger le jeu de données distant en utilisant la méthode introduite dans [section 2](/course/fr/chapter5/2) :
+
+```py
+from datasets import load_dataset
+
+issues_dataset = load_dataset("json", data_files=data_files, split="train")
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
+ num_rows: 2855
+})
+```
+
+Ici, nous avons spécifié l’échantillon `train` par défaut dans `load_dataset()`, de sorte que cela renvoie un `Dataset` au lieu d'un `DatasetDict`. La première chose à faire est de filtrer les *pull requests* car celles-ci ont tendance à être rarement utilisées pour répondre aux requêtes des utilisateurs et introduiront du bruit dans notre moteur de recherche. Comme cela devrait être familier maintenant, nous pouvons utiliser la fonction `Dataset.filter()` pour exclure ces lignes de notre jeu de données. Pendant que nous y sommes, filtrons également les lignes sans commentaires, car celles-ci ne fournissent aucune réponse aux requêtes des utilisateurs :
+
+```py
+issues_dataset = issues_dataset.filter(
+ lambda x: (x["is_pull_request"] == False and len(x["comments"]) > 0)
+)
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
+ num_rows: 771
+})
+```
+
+Nous pouvons voir qu'il y a beaucoup de colonnes dans notre jeu de données, dont la plupart n'ont pas besoin de construire notre moteur de recherche. Du point de vue de la recherche, les colonnes les plus informatives sont `title`, `body` et `comments`, tandis que `html_url` nous fournit un lien vers le problème source. Utilisons la fonction `Dataset.remove_columns()` pour supprimer le reste :
+
+```py
+columns = issues_dataset.column_names
+columns_to_keep = ["title", "body", "html_url", "comments"]
+columns_to_remove = set(columns_to_keep).symmetric_difference(columns)
+issues_dataset = issues_dataset.remove_columns(columns_to_remove)
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['html_url', 'title', 'comments', 'body'],
+ num_rows: 771
+})
+```
+
+Pour créer nos enchâssements, nous ajoutons à chaque commentaire le titre et le corps du problème, car ces champs contiennent des informations contextuelles utiles. Étant donné que notre colonne `comments` est actuellement une liste de commentaires pour chaque problème, nous devons « éclater » la colonne afin que chaque ligne se compose d'un *tuple* `(html_url, title, body, comment)`. Dans Pandas, nous pouvons le faire avec la fonction [`DataFrame.explode()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html), qui crée une nouvelle ligne pour chaque élément dans une colonne de type liste, tout en répliquant toutes les autres valeurs de colonne. Pour voir cela en action, passons d'abord au format `DataFrame` de Pandas :
+
+```py
+issues_dataset.set_format("pandas")
+df = issues_dataset[:]
+```
+
+Si nous inspectons la première ligne de ce `DataFrame`, nous pouvons voir qu'il y a quatre commentaires associés à ce problème :
+
+```py
+df["comments"][0].tolist()
+```
+
+```python out
+['the bug code locate in :\r\n if data_args.task_name is not None:\r\n # Downloading and loading a dataset from the hub.\r\n datasets = load_dataset("glue", data_args.task_name, cache_dir=model_args.cache_dir)',
+ 'Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com\r\n\r\nNormally, it should work if you wait a little and then retry.\r\n\r\nCould you please confirm if the problem persists?',
+ 'cannot connect,even by Web browser,please check that there is some problems。',
+ 'I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...']
+```
+
+Lorsque nous décomposons `df`, nous nous attendons à obtenir une ligne pour chacun de ces commentaires. Vérifions si c'est le cas :
+
+```py
+comments_df = df.explode("comments", ignore_index=True)
+comments_df.head(4)
+```
+
+
+
+
+ |
+ html_url |
+ title |
+ comments |
+ body |
+
+
+
+
+ 0 |
+ https://github.com/huggingface/datasets/issues/2787 |
+ ConnectionError: Couldn't reach https://raw.githubusercontent.com |
+ the bug code locate in :\r\n if data_args.task_name is not None... |
+ Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
+
+
+ 1 |
+ https://github.com/huggingface/datasets/issues/2787 |
+ ConnectionError: Couldn't reach https://raw.githubusercontent.com |
+ Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com... |
+ Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
+
+
+ 2 |
+ https://github.com/huggingface/datasets/issues/2787 |
+ ConnectionError: Couldn't reach https://raw.githubusercontent.com |
+ cannot connect,even by Web browser,please check that there is some problems。 |
+ Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
+
+
+ 3 |
+ https://github.com/huggingface/datasets/issues/2787 |
+ ConnectionError: Couldn't reach https://raw.githubusercontent.com |
+ I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem... |
+ Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
+
+
+
+
+Génial, nous pouvons voir que les lignes ont été répliquées, avec la colonne `comments` contenant les commentaires individuels ! Maintenant que nous en avons fini avec Pandas, nous pouvons rapidement revenir à un `Dataset` en chargeant le `DataFrame` en mémoire :
+
+```py
+from datasets import Dataset
+
+comments_dataset = Dataset.from_pandas(comments_df)
+comments_dataset
+```
+
+```python out
+Dataset({
+ features: ['html_url', 'title', 'comments', 'body'],
+ num_rows: 2842
+})
+```
+
+D'accord, cela nous a donné quelques milliers de commentaires avec lesquels travailler !
+
+
+
+
+✏️ **Essayez !** Voyez si vous pouvez utiliser `Dataset.map()` pour exploser la colonne `comments` de `issues_dataset` _sans_ recourir à l'utilisation de Pandas. C'est un peu délicat. La section [« Batch mapping »](https://huggingface.co/docs/datasets/v1.12.1/about_map_batch.html?batch-mapping#batch-mapping) de la documentation 🤗 *Datasets* peut être utile pour cette tâche.
+
+
+
+Maintenant que nous avons un commentaire par ligne, créons une nouvelle colonne `comments_length` contenant le nombre de mots par commentaire :
+
+```py
+comments_dataset = comments_dataset.map(
+ lambda x: {"comment_length": len(x["comments"].split())}
+)
+```
+
+Nous pouvons utiliser cette nouvelle colonne pour filtrer les commentaires courts incluant généralement des éléments tels que « cc @lewtun » ou « Merci ! » qui ne sont pas pertinents pour notre moteur de recherche. Il n'y a pas de nombre précis à sélectionner pour le filtre mais 15 mots semblent être un bon début :
+
+```py
+comments_dataset = comments_dataset.filter(lambda x: x["comment_length"] > 15)
+comments_dataset
+```
+
+```python out
+Dataset({
+ features: ['html_url', 'title', 'comments', 'body', 'comment_length'],
+ num_rows: 2098
+})
+```
+
+Après avoir un peu nettoyé notre jeu de données, concaténons le titre, la description et les commentaires du problème dans une nouvelle colonne `text`. Comme d'habitude, nous allons écrire une fonction simple que nous pouvons passer à `Dataset.map()` :
+
+```py
+def concatenate_text(examples):
+ return {
+ "text": examples["title"]
+ + " \n "
+ + examples["body"]
+ + " \n "
+ + examples["comments"]
+ }
+
+
+comments_dataset = comments_dataset.map(concatenate_text)
+```
+
+Nous sommes enfin prêts à créer des enchâssements ! Jetons un coup d'œil.
+
+## Création d’enchâssements pour les textes
+
+Nous avons vu dans [chapitre 2](/course/fr/chapter2) que nous pouvons obtenir des enchâssements de *tokens* en utilisant la classe `AutoModel`. Tout ce que nous avons à faire est de choisir un *checkpoint* approprié à partir duquel charger le modèle. Heureusement, il existe une bibliothèque appelée `sentence-transformers` dédiée à la création d’enchâssements. Comme décrit dans la [documentation de la bibliothèque](https://www.sbert.net/examples/applications/semantic-search/README.html#symmetric-vs-asymmetric-semantic-search), notre cas d'utilisation est un exemple de _recherche sémantique asymétrique_. En effet, nous avons une requête courte dont nous aimerions trouver la réponse dans un document plus long, par exemple un commentaire à un problème. Le [tableau de présentation des modèles](https://www.sbert.net/docs/pretrained_models.html#model-overview) de la documentation indique que le *checkpoint* `multi-qa-mpnet-base-dot-v1` a les meilleures performances pour la recherche sémantique. Utilisons donc le pour notre application. Nous allons également charger le *tokenizer* en utilisant le même *checkpoint* :
+
+{#if fw === 'pt'}
+
+```py
+from transformers import AutoTokenizer, AutoModel
+
+model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
+tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
+model = AutoModel.from_pretrained(model_ckpt)
+```
+
+Pour accélérer le processus, il est utile de placer le modèle et les entrées sur un périphérique GPU, alors faisons-le maintenant :
+
+```py
+import torch
+
+device = torch.device("cuda")
+model.to(device)
+```
+
+{:else}
+
+```py
+from transformers import AutoTokenizer, TFAutoModel
+
+model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
+tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
+model = TFAutoModel.from_pretrained(model_ckpt, from_pt=True)
+```
+
+Notez que nous avons défini `from_pt=True` comme argument de la méthode `from_pretrained()`. C'est parce que le point de contrôle `multi-qa-mpnet-base-dot-v1` n'a que des poids PyTorch. Donc définir `from_pt=True` converti automatiquement au format TensorFlow pour nous. Comme vous pouvez le voir, il est très simple de passer d'un *framework* à l'autre dans 🤗 *Transformers* !
+
+{/if}
+
+Comme nous l'avons mentionné précédemment, nous aimerions représenter chaque entrée dans notre corpus de problèmes GitHub comme un vecteur unique. Nous devons donc regrouper ou faire la moyenne de nos enchâssements de *tokens* d'une manière ou d'une autre. Une approche populaire consiste à effectuer un *regroupement CLS* sur les sorties de notre modèle, où nous collectons simplement le dernier état caché pour le *token* spécial `[CLS]`. La fonction suivante fait ça pour nous :
+
+```py
+def cls_pooling(model_output):
+ return model_output.last_hidden_state[:, 0]
+```
+
+Ensuite, nous allons créer une fonction utile qui va tokeniser une liste de documents, placer les tenseurs dans le GPU, les donner au modèle et enfin appliquer le regroupement CLS aux sorties :
+
+{#if fw === 'pt'}
+
+```py
+def get_embeddings(text_list):
+ encoded_input = tokenizer(
+ text_list, padding=True, truncation=True, return_tensors="pt"
+ )
+ encoded_input = {k: v.to(device) for k, v in encoded_input.items()}
+ model_output = model(**encoded_input)
+ return cls_pooling(model_output)
+```
+
+Nous pouvons tester le fonctionnement de la fonction en lui donnant la première entrée textuelle de notre corpus et en inspectant la forme de sortie :
+
+```py
+embedding = get_embeddings(comments_dataset["text"][0])
+embedding.shape
+```
+
+```python out
+torch.Size([1, 768])
+```
+
+Super ! Nous avons converti la première entrée de notre corpus en un vecteur à 768 dimensions. Nous pouvons utiliser `Dataset.map()` pour appliquer notre fonction `get_embeddings()` à chaque ligne de notre corpus. Créons donc une nouvelle colonne `embeddings` comme suit :
+
+```py
+embeddings_dataset = comments_dataset.map(
+ lambda x: {"embeddings": get_embeddings(x["text"]).detach().cpu().numpy()[0]}
+)
+```
+
+{:else}
+
+```py
+def get_embeddings(text_list):
+ encoded_input = tokenizer(
+ text_list, padding=True, truncation=True, return_tensors="tf"
+ )
+ encoded_input = {k: v for k, v in encoded_input.items()}
+ model_output = model(**encoded_input)
+ return cls_pooling(model_output)
+```
+
+Nous pouvons tester le fonctionnement de la fonction en lui donnant la première entrée textuelle de notre corpus et en inspectant la forme de sortie :
+
+```py
+embedding = get_embeddings(comments_dataset["text"][0])
+embedding.shape
+```
+
+```python out
+TensorShape([1, 768])
+```
+
+Super ! Nous avons converti la première entrée de notre corpus en un vecteur à 768 dimensions. Nous pouvons utiliser `Dataset.map()` pour appliquer notre fonction `get_embeddings()` à chaque ligne de notre corpus. Créons donc une nouvelle colonne `embeddings` comme suit :
+
+```py
+embeddings_dataset = comments_dataset.map(
+ lambda x: {"embeddings": get_embeddings(x["text"]).numpy()[0]}
+)
+```
+
+{/if}
+
+
+Notez que nous avons converti les enchâssements en tableaux NumPy. C'est parce que 🤗 *Datasets* nécessite ce format lorsque nous essayons de les indexer avec FAISS, ce que nous ferons ensuite.
+
+## Utilisation de FAISS pour une recherche de similarité efficace
+
+Maintenant que nous avons un jeu de données d'incorporations, nous avons besoin d'un moyen de les rechercher. Pour ce faire, nous utiliserons une structure de données spéciale dans 🤗 *Datasets* appelée _FAISS index_. [FAISS](https://faiss.ai/) (abréviation de *Facebook AI Similarity Search*) est une bibliothèque qui fournit des algorithmes efficaces pour rechercher et regrouper rapidement des vecteurs d'intégration.
+
+L'idée de base derrière FAISS est de créer une structure de données spéciale appelée un _index_ qui permet de trouver quels plongements sont similaires à un plongement d'entrée. Créer un index FAISS dans 🤗 *Datasets* est simple -- nous utilisons la fonction `Dataset.add_faiss_index()` et spécifions quelle colonne de notre jeu de données nous aimerions indexer :
+
+```py
+embeddings_dataset.add_faiss_index(column="embeddings")
+```
+
+Nous pouvons maintenant effectuer des requêtes sur cet index en effectuant une recherche des voisins les plus proches avec la fonction `Dataset.get_nearest_examples()`. Testons cela en enchâssant d'abord une question comme suit :
+
+{#if fw === 'pt'}
+
+```py
+question = "How can I load a dataset offline?"
+question_embedding = get_embeddings([question]).cpu().detach().numpy()
+question_embedding.shape
+```
+
+```python out
+torch.Size([1, 768])
+```
+
+{:else}
+
+```py
+question = "How can I load a dataset offline?"
+question_embedding = get_embeddings([question]).numpy()
+question_embedding.shape
+```
+
+```python out
+(1, 768)
+```
+
+{/if}
+
+Tout comme avec les documents, nous avons maintenant un vecteur de 768 dimensions représentant la requête. Nous pouvons le comparer à l’ensemble du corpus pour trouver les enchâssements les plus similaires :
+
+```py
+scores, samples = embeddings_dataset.get_nearest_examples(
+ "embeddings", question_embedding, k=5
+)
+```
+
+La fonction `Dataset.get_nearest_examples()` renvoie un *tuple* de scores qui classent le chevauchement entre la requête et le document, et un jeu correspondant d'échantillons (ici, les 5 meilleures correspondances). Collectons-les dans un `pandas.DataFrame` afin de pouvoir les trier facilement :
+
+```py
+import pandas as pd
+
+samples_df = pd.DataFrame.from_dict(samples)
+samples_df["scores"] = scores
+samples_df.sort_values("scores", ascending=False, inplace=True)
+```
+
+Nous pouvons maintenant parcourir les premières lignes pour voir dans quelle mesure notre requête correspond aux commentaires disponibles :
+
+```py
+for _, row in samples_df.iterrows():
+ print(f"COMMENT: {row.comments}")
+ print(f"SCORE: {row.scores}")
+ print(f"TITLE: {row.title}")
+ print(f"URL: {row.html_url}")
+ print("=" * 50)
+ print()
+```
+
+```python out
+"""
+COMMENT: Requiring online connection is a deal breaker in some cases unfortunately so it'd be great if offline mode is added similar to how `transformers` loads models offline fine.
+
+@mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
+SCORE: 25.505046844482422
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+COMMENT: The local dataset builders (csv, text , json and pandas) are now part of the `datasets` package since #1726 :)
+You can now use them offline
+\`\`\`python
+datasets = load_dataset("text", data_files=data_files)
+\`\`\`
+
+We'll do a new release soon
+SCORE: 24.555509567260742
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+COMMENT: I opened a PR that allows to reload modules that have already been loaded once even if there's no internet.
+
+Let me know if you know other ways that can make the offline mode experience better. I'd be happy to add them :)
+
+I already note the "freeze" modules option, to prevent local modules updates. It would be a cool feature.
+
+----------
+
+> @mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
+
+Indeed `load_dataset` allows to load remote dataset script (squad, glue, etc.) but also you own local ones.
+For example if you have a dataset script at `./my_dataset/my_dataset.py` then you can do
+\`\`\`python
+load_dataset("./my_dataset")
+\`\`\`
+and the dataset script will generate your dataset once and for all.
+
+----------
+
+About I'm looking into having `csv`, `json`, `text`, `pandas` dataset builders already included in the `datasets` package, so that they are available offline by default, as opposed to the other datasets that require the script to be downloaded.
+cf #1724
+SCORE: 24.14896583557129
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+COMMENT: > here is my way to load a dataset offline, but it **requires** an online machine
+>
+> 1. (online machine)
+>
+> ```
+>
+> import datasets
+>
+> data = datasets.load_dataset(...)
+>
+> data.save_to_disk(/YOUR/DATASET/DIR)
+>
+> ```
+>
+> 2. copy the dir from online to the offline machine
+>
+> 3. (offline machine)
+>
+> ```
+>
+> import datasets
+>
+> data = datasets.load_from_disk(/SAVED/DATA/DIR)
+>
+> ```
+>
+>
+>
+> HTH.
+
+
+SCORE: 22.893993377685547
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+COMMENT: here is my way to load a dataset offline, but it **requires** an online machine
+1. (online machine)
+\`\`\`
+import datasets
+data = datasets.load_dataset(...)
+data.save_to_disk(/YOUR/DATASET/DIR)
+\`\`\`
+2. copy the dir from online to the offline machine
+3. (offline machine)
+\`\`\`
+import datasets
+data = datasets.load_from_disk(/SAVED/DATA/DIR)
+\`\`\`
+
+HTH.
+SCORE: 22.406635284423828
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+"""
+```
+
+Pas mal ! Notre deuxième résultat semble correspondre à la requête.
+
+
+
+✏️ **Essayez !** Créez votre propre requête et voyez si vous pouvez trouver une réponse dans les documents récupérés. Vous devrez peut-être augmenter le paramètre `k` dans `Dataset.get_nearest_examples()` pour élargir la recherche.
+
+
diff --git a/chapters/fr/chapter5/7.mdx b/chapters/fr/chapter5/7.mdx
index 6e1bc43ce..55083fffa 100644
--- a/chapters/fr/chapter5/7.mdx
+++ b/chapters/fr/chapter5/7.mdx
@@ -1,10 +1,10 @@
-# 🤗 *Datasets*, vérifié !
-
-Eh bien, ce fut une sacrée visite de la bibliothèque 🤗 *Datasets*. Félicitations d’être arrivé jusqu'ici ! Avec les connaissances que vous avez acquises dans ce chapitre, vous devriez être en mesure de :
-- charger des jeux de données depuis n'importe où, que ce soit le *Hub* d’Hugging Face, votre ordinateur portable ou un serveur distant de votre entreprise.
-- manipuler vos données en utilisant un mélange des fonctions Dataset.map() et Dataset.filter().
-- passer rapidement d'un format de données à un autre, comme Pandas et NumPy, en utilisant Dataset.set_format().
-- créer votre propre jeu de données et l’envoyer vers le *Hub*.
-- enchâsser vos documents en utilisant un *transformer* et construire un moteur de recherche sémantique en utilisant FAISS.
-
-Dans le [Chapter 7](/course/fr/chapter7), nous mettrons tout cela à profit en plongeant dans les tâches de traitement du langage naturel de base pour lesquelles les *transformers* sont parfaits. Avant cela mettez vos connaissances sur la librairie 🤗 *Datasets* à l'épreuve avec un petit quiz !
+# 🤗 Datasets, coché !
+
+Eh bien, ce fut une sacrée visite de la bibliothèque 🤗 *Datasets*. Félicitations d’être arrivé jusqu'ici ! Avec les connaissances que vous avez acquises dans ce chapitre, vous devriez être en mesure de :
+- charger des jeux de données depuis n'importe où, que ce soit le *Hub* d’Hugging Face, votre ordinateur portable ou un serveur distant de votre entreprise,
+- manipuler vos données en utilisant un mélange des fonctions Dataset.map() et Dataset.filter(),
+- passer rapidement d'un format de données à un autre, comme Pandas et NumPy, en utilisant Dataset.set_format(),
+- créer votre propre jeu de données et l’envoyer vers le *Hub*,
+- enchâsser vos documents en utilisant un *transformer* et construire un moteur de recherche sémantique en utilisant FAISS.
+
+Dans le [chapitre 7](/course/fr/chapter7), nous mettrons tout cela à profit en plongeant dans les tâches de traitement du langage naturel de base pour lesquelles les *transformers* sont parfaits. Avant cela mettez vos connaissances sur la librairie 🤗 *Datasets* à l'épreuve avec un petit quiz !
diff --git a/chapters/fr/chapter5/8.mdx b/chapters/fr/chapter5/8.mdx
index 5ea12fd8e..54f4e770f 100644
--- a/chapters/fr/chapter5/8.mdx
+++ b/chapters/fr/chapter5/8.mdx
@@ -1,226 +1,226 @@
-
-
-# Quiz de fin de chapitre
-
-Ce chapitre a couvert beaucoup de terrain ! Ne vous inquiétez pas si vous n'avez pas saisi tous les détails, les chapitres suivants vous aideront à comprendre comment les choses fonctionnent sous le capot.
-
-Avant de poursuivre, testons ce que vous avez appris dans ce chapitre.
-
-### 1. La fonction `load_dataset()` dans 🤗 *Datasets* vous permet de charger un jeu de données depuis lequel des emplacements suivants ?
-
-data_files de load_dataset()
pour charger les jeux de données locaux.",
- correct: true
- },
- {
- text: "Le Hub d’Hugging Face.",
- explain: "Correct ! Vous pouvez charger des jeux de données sur le Hub> en fournissant l'ID du jeu de données. Par exemple : load_dataset('emotion')
.",
- correct: true
- },
- {
- text: "Un serveur distant.",
- explain: "Correct ! Vous pouvez passer des URLs à l'argument data_files
de load_dataset()
pour charger des fichiers distants.",
- correct: true
- },
- ]}
-/>
-
-### 2. Supposons que vous chargiez l'une des tâches du jeu de données GLUE comme suit :
-
-```py
-from datasets import load_dataset
-
-dataset = load_dataset("glue", "mrpc", split="train")
-```
-
-Laquelle des commandes suivantes produira un échantillon aléatoire de 50 éléments à partir de `dataset` ?
-
-dataset.sample(50)",
- explain: "Ceci est incorrect, il n'y a pas de méthode Dataset.sample()
."
- },
- {
- text: "dataset.shuffle().select(range(50))
",
- explain: "Correct ! Comme vous l'avez vu dans ce chapitre, vous mélangez d'abord le jeu de données puis sélectionnez les échantillons à partir de celui-ci.",
- correct: true
- },
- {
- text: "dataset.select(range(50)).shuffle()
",
- explain: "Ceci est incorrect. Bien que le code s'exécute, il ne mélange que les 50 premiers éléments du jeu de données."
- }
- ]}
-/>
-
-### 3. Supposons que vous disposiez d'un jeu de données sur les animaux domestiques appelé `pets_dataset` qui comporte une colonne `name` indiquant le nom de chaque animal. Parmi les approches suivantes, laquelle vous permettrait de filtrer le jeu de données pour tous les animaux dont le nom commence par la lettre « L » ?
-
-pets_dataset.filter(lambda x : x['name'].startswith('L'))",
- explain: "Correct ! L'utilisation d'une fonction Python lambda pour ces filtres rapides est une excellente idée. Pouvez-vous penser à une autre solution ?",
- correct: true
- },
- {
- text: "pets_dataset.filter(lambda x['name'].startswith('L'))
",
- explain: "Ceci est incorrect. Une fonction lambda prend la forme générale lambda *arguments* : *expression*
, vous devez donc fournir des arguments dans ce cas."
- },
- {
- text: "Créer une fonction comme def filter_names(x): return x['name'].startswith('L')
et exécuter pets_dataset.filter(filter_names)
.",
- explain: "Correct ! Tout comme avec Dataset.map()
, vous pouvez passer des fonctions explicites à Dataset.filter()
. Ceci est utile lorsque vous avez une logique complexe qui ne convient pas à une fonction lambda courte. Parmi les autres solutions, laquelle fonctionnerait ?",
- correct: true
- }
- ]}
-/>
-
-### 4. Qu'est-ce que le *memory mapping* ?
-
-mapping entre la RAM CPU et GPU.",
- explain: "Ce n'est pas ça, réessayez !",
- },
- {
- text: "Un mappaging entre la RAM et le stockage du système de fichiers.",
- explain: "Correct ! 🤗 Datasets traite chaque jeu de données comme un fichier mappé en mémoire. Cela permet à la bibliothèque d'accéder et d'opérer sur des éléments du jeu de données sans avoir à le charger complètement en mémoire.",
- correct: true
- },
- {
- text: "Un mappaging entre deux fichiers dans le cache 🤗 Datasets.",
- explain: "Ce n'est pas correct, réessayez !"
- }
- ]}
-/>
-
-### 5. Parmi les éléments suivants, lesquels sont les principaux avantages du *memory mapping* ?
-
-Datasets d'être extrêmement rapide. Ce n'est cependant pas le seul avantage.",
- correct: true
- },
- {
- text: "Les applications peuvent accéder à des segments de données dans un fichier extrêmement volumineux sans avoir à lire tout le fichier dans la RAM au préalable.",
- explain: "Correct ! Cela permet à 🤗 Datasets de charger des jeux de données de plusieurs Go sur votre ordinateur portable sans faire exploser votre CPU. Quel autre avantage cette technique offre-t-elle ?",
- correct: true
- },
- {
- text: "Cela consomme moins d'énergie, donc votre batterie dure plus longtemps.",
- explain: "Ce n'est pas correct, réessayez !"
- }
- ]}
-/>
-
-### 6. Pourquoi le code suivant échoue-t-il ?
-
-```py
-from datasets import load_dataset
-
-dataset = load_dataset("allocine", streaming=True, split="train")
-dataset[0]
-```
-
-IterableDataset.",
- explain: "Correct! Un IterableDataset
est un générateur, pas un conteneur. Vous devez donc accéder à ses éléments en utilisant next(iter(dataset))
.",
- correct: true
- },
- {
- text: "Le jeu de données allocine
n'a pas d’échantillon train
.",
- explain: "Ceci est incorrect. Consultez la [fiche d’ allocine
](https://huggingface.co/datasets/allocine) sur le Hub pour voir quels échantillons il contient."
- }
- ]}
-/>
-
-### 7. Parmi les avantages suivants, lesquels sont les principaux pour la création d'une fiche pour les jeux de données ?
-
-
-
-
-### 8. Qu'est-ce que la recherche sémantique ?
-
-
-
-### 9. Pour la recherche sémantique asymétrique, vous avez généralement :
-
-
-
-### 10. Puis-je utiliser 🤗 *Datasets* pour charger des données à utiliser dans d'autres domaines, comme le traitement de la parole ?
-
-Datasets prend actuellement en charge les données tabulaires, l'audio et la vision par ordinateur. Consultez le jeu de donnéesMNIST sur le Hub pour un exemple de vision par ordinateur."
- },
- {
- text: "Oui.",
- explain: "Correct ! Découvrez les développements passionnants concernant la parole et la vision dans la bibliothèque 🤗 Transformers pour voir comment 🤗 Datasets est utilisé dans ces domaines.",
- correct : true
- },
- ]}
-/>
+
+
+# Quiz de fin de chapitre
+
+Ce chapitre a couvert beaucoup de terrain ! Ne vous inquiétez pas si vous n'avez pas saisi tous les détails, les chapitres suivants vous aideront à comprendre comment les choses fonctionnent sous le capot.
+
+Avant de poursuivre, testons ce que vous avez appris dans ce chapitre.
+
+### 1. La fonction `load_dataset()` dans 🤗 *Datasets* vous permet de charger un jeu de données depuis lequel des emplacements suivants ?
+
+data_files de load_dataset()
pour charger les jeux de données locaux.",
+ correct: true
+ },
+ {
+ text: "Le Hub d’Hugging Face.",
+ explain: "Vous pouvez charger des jeux de données sur le Hub en fournissant l'ID du jeu de données. Par exemple : load_dataset('emotion')
.",
+ correct: true
+ },
+ {
+ text: "Un serveur distant.",
+ explain: "Vous pouvez passer des URLs à l'argument data_files
de load_dataset()
pour charger des fichiers distants.",
+ correct: true
+ },
+ ]}
+/>
+
+### 2. Supposons que vous chargiez l'une des tâches du jeu de données GLUE comme suit :
+
+```py
+from datasets import load_dataset
+
+dataset = load_dataset("glue", "mrpc", split="train")
+```
+
+Laquelle des commandes suivantes produira un échantillon aléatoire de 50 éléments à partir de `dataset` ?
+
+dataset.sample(50)",
+ explain: "Il n'y a pas de méthode Dataset.sample()
."
+ },
+ {
+ text: "dataset.shuffle().select(range(50))
",
+ explain: "Comme vous l'avez vu dans ce chapitre, vous mélangez d'abord le jeu de données puis sélectionnez les échantillons à partir de celui-ci.",
+ correct: true
+ },
+ {
+ text: "dataset.select(range(50)).shuffle()
",
+ explain: "Bien que le code s'exécute, il ne mélange que les 50 premiers éléments du jeu de données."
+ }
+ ]}
+/>
+
+### 3. Supposons que vous disposiez d'un jeu de données sur les animaux domestiques appelé `pets_dataset` qui comporte une colonne `name` indiquant le nom de chaque animal. Parmi les approches suivantes, laquelle vous permettrait de filtrer le jeu de données pour tous les animaux dont le nom commence par la lettre « L » ?
+
+pets_dataset.filter(lambda x : x['name'].startswith('L'))",
+ explain: "L'utilisation d'une fonction Python lambda pour ces filtres rapides est une excellente idée. Pouvez-vous penser à une autre solution ?",
+ correct: true
+ },
+ {
+ text: "pets_dataset.filter(lambda x['name'].startswith('L'))
",
+ explain: "Une fonction lambda prend la forme générale lambda *arguments* : *expression*
, vous devez donc fournir des arguments dans ce cas."
+ },
+ {
+ text: "Créer une fonction comme def filter_names(x): return x['name'].startswith('L')
et exécuter pets_dataset.filter(filter_names)
.",
+ explain: "Tout comme avec Dataset.map()
, vous pouvez passer des fonctions explicites à Dataset.filter()
. Ceci est utile lorsque vous avez une logique complexe qui ne convient pas à une fonction lambda courte. Parmi les autres solutions, laquelle fonctionnerait ?",
+ correct: true
+ }
+ ]}
+/>
+
+### 4. Qu'est-ce que le *memory mapping* ?
+
+mapping entre la RAM CPU et GPU.",
+ explain: "Ce n'est pas ça, réessayez !",
+ },
+ {
+ text: "Un mapping entre la RAM et le stockage du système de fichiers.",
+ explain: "🤗 Datasets traite chaque jeu de données comme un fichier mappé en mémoire. Cela permet à la bibliothèque d'accéder et d'opérer sur des éléments du jeu de données sans avoir à le charger complètement en mémoire.",
+ correct: true
+ },
+ {
+ text: "Un mapping entre deux fichiers dans le cache 🤗 Datasets.",
+ explain: "Ce n'est pas ça, réessayez !"
+ }
+ ]}
+/>
+
+### 5. Parmi les éléments suivants, lesquels sont les principaux avantages du *memory mapping* ?
+
+Datasets d'être extrêmement rapide. Ce n'est cependant pas le seul avantage.",
+ correct: true
+ },
+ {
+ text: "Les applications peuvent accéder à des segments de données dans un fichier extrêmement volumineux sans avoir à lire tout le fichier dans la RAM au préalable.",
+ explain: "Cela permet à 🤗 Datasets de charger des jeux de données de plusieurs Go sur votre ordinateur portable sans faire exploser votre CPU. Quel autre avantage cette technique offre-t-elle ?",
+ correct: true
+ },
+ {
+ text: "Cela consomme moins d'énergie, donc votre batterie dure plus longtemps.",
+ explain: "Ce n'est pas ça, réessayez !"
+ }
+ ]}
+/>
+
+### 6. Pourquoi le code suivant échoue-t-il ?
+
+```py
+from datasets import load_dataset
+
+dataset = load_dataset("allocine", streaming=True, split="train")
+dataset[0]
+```
+
+IterableDataset.",
+ explain: "Un IterableDataset
est un générateur, pas un conteneur. Vous devez donc accéder à ses éléments en utilisant next(iter(dataset))
.",
+ correct: true
+ },
+ {
+ text: "Le jeu de données allocine
n'a pas d’échantillon train
.",
+ explain: "Consultez le jeu de données allocine
sur le Hub (https://huggingface.co/datasets/allocine) pour voir quels échantillons il contient."
+ }
+ ]}
+/>
+
+### 7. Parmi les avantages suivants, lesquels sont les principaux pour la création d'une fiche pour les jeux de données ?
+
+
+
+
+### 8. Qu'est-ce que la recherche sémantique ?
+
+recherche lexicale et c'est ce que vous voyez généralement avec les moteurs de recherche traditionnels."
+ },
+ {
+ text: "Un moyen de rechercher des documents correspondants en comprenant la signification contextuelle d'une requête.",
+ explain: "La recherche sémantique utilise des vecteurs d’enchâssement pour représenter les requêtes et les documents. Elle utilise ensuite une métrique de similarité pour mesurer la quantité de chevauchement entre eux. Comment la décrire autrement ?",
+ correct: true
+ },
+ {
+ text: "Un moyen d'améliorer la précision de la recherche.",
+ explain: "Les moteurs de recherche sémantique peuvent capturer l'intention d'une requête bien mieux que la correspondance des mots clés et récupèrent généralement les documents avec une plus grande précision. Mais ce n'est pas la seule bonne réponse. Qu'est-ce que la recherche sémantique apporte d'autre ?",
+ correct: true
+ }
+ ]}
+/>
+
+### 9. Pour la recherche sémantique asymétrique, vous avez généralement :
+
+
+
+### 10. Puis-je utiliser 🤗 *Datasets* pour charger des données à utiliser dans d'autres domaines, comme le traitement de la parole ?
+
+Datasets prend actuellement en charge les données tabulaires, l'audio et la vision par ordinateur. Consultez le jeu de données MNIST sur le Hub pour un exemple de vision par ordinateur."
+ },
+ {
+ text: "Oui.",
+ explain: "Découvrez les développements passionnants concernant la parole et la vision dans la bibliothèque 🤗 Transformers pour voir comment 🤗 Datasets est utilisé dans ces domaines.",
+ correct : true
+ },
+ ]}
+/>
diff --git a/chapters/fr/chapter6/1.mdx b/chapters/fr/chapter6/1.mdx
index 0950598c8..6869cc815 100644
--- a/chapters/fr/chapter6/1.mdx
+++ b/chapters/fr/chapter6/1.mdx
@@ -1,13 +1,13 @@
# Introduction
-Dans le [Chapitre 3](/course/fr/chapter3), nous avons vu comment *finetuner* un modèle sur une tâche donnée. Pour ce faire, nous utilisons le même *tokenizer* que celui avec lequel le modèle a été pré-entraîné. Mais que faisons-nous lorsque nous voulons entraîner un modèle à partir de zéro ? Dans ces cas, l'utilisation d'un *tokenizer* qui a été pré-entraîné sur un corpus d'un autre domaine ou d'une autre langue est généralement sous-optimale. Par exemple, un *tokenizer* entraîné sur un corpus anglais sera peu performant sur un corpus de textes japonais car l'utilisation des espaces et de la ponctuation est très différente entre les deux langues.
+Dans le [chapitre 3](/course/fr/chapter3), nous avons vu comment *finetuner* un modèle sur une tâche donnée. Pour ce faire, nous utilisons le même *tokenizer* que celui avec lequel le modèle a été pré-entraîné. Mais que faisons-nous lorsque nous voulons entraîner un modèle à partir de zéro ? Dans ces cas, l'utilisation d'un *tokenizer* qui a été pré-entraîné sur un corpus d'un autre domaine ou d'une autre langue est généralement sous-optimale. Par exemple, un *tokenizer* entraîné sur un corpus anglais sera peu performant sur un corpus de textes japonais car l'utilisation des espaces et de la ponctuation est très différente entre les deux langues.
Dans ce chapitre, vous apprendrez à entraîner un tout nouveau *tokenizer* sur un corpus de textes afin qu'il puisse ensuite être utilisé pour pré-entraîner un modèle de langue. Tout cela se fera à l'aide de la bibliothèque [🤗 *Tokenizers*](https://github.com/huggingface/tokenizers), qui fournit les *tokenizers* « rapides » de la bibliothèque [🤗 *Transformers*](https://github.com/huggingface/transformers). Nous examinerons de près les fonctionnalités offertes par cette bibliothèque et nous étudierons comment les *tokenizers* rapides diffèrent des versions « lentes ».
Les sujets que nous couvrirons comprennent :
-* comment entraîner un nouveau *tokenizer* similaire à celui utilisé par un *checkpoint* donné sur un nouveau corpus de textes,
+* comment entraîner sur un nouveau corpus de textes, un nouveau *tokenizer* similaire à celui utilisé par un *checkpoint* donné,
* les caractéristiques spéciales des *tokenizers* rapides,
* les différences entre les trois principaux algorithmes de tokénisation utilisés aujourd'hui en NLP,
* comment construire un *tokenizer* à partir de zéro avec la bibliothèque 🤗 *Tokenizers* et l'entraîner sur des données.
-Les techniques présentées dans ce chapitre vous prépareront à la section du [Chapitre 7](/course/fr/chapter7/6) où nous examinons la création d'un modèle de langue pour le langage Python. Commençons par examiner ce que signifie « entraîner » un *tokenizer*.
\ No newline at end of file
+Les techniques présentées dans ce chapitre vous prépareront à la section du [chapitre 7](/course/fr/chapter7/6) où nous verrons comment créer un modèle de langue pour le langage Python. Commençons par examiner ce que signifie « entraîner » un *tokenizer*.
\ No newline at end of file
diff --git a/chapters/fr/chapter6/10.mdx b/chapters/fr/chapter6/10.mdx
index b7ef6c773..b4a6cdc89 100644
--- a/chapters/fr/chapter6/10.mdx
+++ b/chapters/fr/chapter6/10.mdx
@@ -4,31 +4,31 @@
Testons ce que vous avez appris dans ce chapitre !
-### 1. Quand devez-vous entraîner un nouveau *tokenizer* ?
+### 1. Quand devez-vous entraîner un nouveau tokenizer ?
tokenizer que le modèle pré-entraîné et de finetuner ce modèle à la place."
+ text: "Lorsque votre jeu de données est similaire à celui utilisé par un modèle pré-entraîné existant et que vous voulez pré-entraîner un nouveau modèle",
+ explain: "Dans ce cas, pour économiser du temps et des ressources de calcul, il est préférable d'utiliser le même tokenizer que le modèle pré-entraîné et de finetuner ce modèle à la place."
},
{
text: "Lorsque votre jeu de données est similaire à celui utilisé par un modèle pré-entraîné existant et que vous souhaitez finetuner un nouveau modèle en utilisant ce modèle pré-entraîné.",
explain: "Pour finetuner un modèle à partir d'un modèle pré-entraîné, vous devez toujours utiliser le même tokenizer."
},
{
- text: "Lorsque votre jeu de données est différent de celui utilisé par un modèle pré-entraîné existant, et que vous souhaitez pré-entraîner un nouveau modèle.",
- explain: "C'est exact ! Dans ce cas, il n'y a aucun avantage à utiliser le même tokenizer.",
+ text: "Lorsque votre jeu de données est différent de celui utilisé par un modèle pré-entraîné existant et que vous souhaitez pré-entraîner un nouveau modèle.",
+ explain: "Dans ce cas, il n'y a aucun avantage à utiliser le même tokenizer.",
correct: true
},
{
- text: "Lorsque votre jeu de données est différent de celui utilisé par un modèle pré-entraîné existant, mais que vous souhaitez finetuner un nouveau modèle en utilisant ce modèle pré-entraîné.",
+ text: "Lorsque votre jeu de données est différent de celui utilisé par un modèle pré-entraîné existant mais que vous souhaitez finetuner un nouveau modèle en utilisant ce modèle pré-entraîné.",
explain: "Pour finetuner un modèle à partir d'un modèle pré-entraîné, vous devez toujours utiliser le même tokenizer."
}
]}
/>
-### 2. Quel est l'avantage d'utiliser un générateur de listes de textes par rapport à une liste de listes de textes lors de l'utilisation de `train_new_from_iterator()` ?
+### 2. Quel est l'avantage d'utiliser un générateur de listes par rapport à une liste de listes lors de l'utilisation de train_new_from_iterator()
?
Datasets pour stocker vos textes.",
+ explain: "Chaque batch de textes sera libéré de la mémoire lorsque vous itérerez et le gain sera particulièrement visible si vous utilisez des 🤗 Datasets pour stocker vos textes.",
correct: true
},
{
text: "Cela permettra à la bibliothèque 🤗 Tokenizers d'utiliser le multitraitement.",
- explain: "Non, il utilisera le multiprocesseur dans tous les cas."
+ explain: "Il utilisera le multiprocesseur dans tous les cas."
},
{
text: "Le tokenizer que vous entraînez générera de meilleurs textes.",
@@ -52,13 +52,13 @@ Testons ce que vous avez appris dans ce chapitre !
]}
/>
-### 3. Quels sont les avantages d'utiliser un *tokenizer* « rapide » ?
+### 3. Quels sont les avantages d'utiliser un tokenizer « rapide » ?
tokenizer lent lorsque vous faites des batchs d'entrées.",
- explain: "Correct ! Grâce au parallélisme implémenté dans Rust, il sera plus rapide sur les batchs d'entrées. Quel autre avantage pouvez-vous imaginer ?",
+ explain: "Grâce au parallélisme implémenté dans Rust, il sera plus rapide sur les batchs d'entrées. Quel autre avantage pouvez-vous imaginer ?",
correct: true
},
{
@@ -66,18 +66,18 @@ Testons ce que vous avez appris dans ce chapitre !
explain: "Un tokenizer rapide peut en fait être plus lent si vous ne lui donnez qu'un seul ou très peu de textes, car il ne peut pas utiliser le parallélisme."
},
{
- text: "Il peut appliquer le *padding* et la troncature.",
- explain: "C'est vrai, mais les *tokenizers* lents le font aussi."
+ text: "Il peut appliquer le padding et la troncature.",
+ explain: "C'est vrai, mais les tokenizers lents le font aussi."
},
{
text: "Il possède des fonctionnalités supplémentaires qui vous permettent d'associer les tokens à l'extrait de texte qui les a créés.",
- explain: "En effet, c'est ce qu'on appelle des mappages de décalage. Ce n'est pas le seul avantage, cependant.",
+ explain: "En effet, c'est ce qu'on appelle des correspondances d'offset. Ce n'est pas le seul avantage, cependant.",
correct: true
}
]}
/>
-### 4. Comment le pipeline `token-classification` gère-t-il les entités qui s'étendent sur plusieurs *tokens* ?
+### 4. Comment le pipeline `token-classification` gère-t-il les entités qui s'étendent sur plusieurs tokens ?
@@ -132,36 +132,36 @@ Testons ce que vous avez appris dans ce chapitre !
tokenizer effectue sur les textes lors des étapes initiales.",
+ explain: "Par exemple, il peut s'agir de supprimer les accents ou les espaces, ou de mettre les entrées en minuscules.",
correct: true
},
{
- text: "Il s'agit d'une technique d'augmentation des données qui consiste à rendre le texte plus normal en supprimant les mots rares.",
- explain: "C'est incorrect ! Essayez encore."
+ text: "Il s'agit d'une technique d'augmentation de données qui consiste à rendre le texte plus normal en supprimant les mots rares.",
+ explain: "Essayez encore."
},
{
- text: "C'est l'étape finale du post-traitement où le *tokenizer* ajoute les *tokens* spéciaux.",
+ text: "C'est l'étape finale du post-traitement où le tokenizer ajoute les tokens spéciaux.",
explain: "Cette étape est simplement appelée post-traitement."
},
{
- text: "C'est lorsque les incorporations sont faites avec une moyenne de 0 et un écart-type de 1, en soustrayant la moyenne et en divisant par l'écart-type.",
+ text: "C'est lorsque les enchâssements sont faits avec une moyenne nulle et un écart-type de 1, en soustrayant la moyenne et en divisant par l'écart-type.",
explain: "Ce processus est communément appelé normalisation lorsqu'il est appliqué aux valeurs des pixels en vision par ordinateur, mais ce n'est pas ce que signifie la normalisation en NLP."
}
]}
/>
-### 7. Qu'est-ce que la pré-tokénisation pour un *tokenizer* en sous-mots ?
+### 7. Qu'est-ce que la pré-tokénisation pour un tokenizer en sous-mots ?
tokenizer, qui divise l'entrée en tokens.",
+ explain: "La division en tokens est le travail du modèle tokenizer."
}
]}
/>
-### 8. Sélectionnez les phrases qui s'appliquent au *tokenizer* BPE.
+### 8. Sélectionnez les phrases qui s'appliquent au tokenizer BPE.
tokens.",
- explain: "Non, c'est l'approche adoptée par un algorithme de tokénisation différent."
+ text: "BPE est un algorithme de tokénisation en sous-mots qui part d'un grand vocabulaire et en retire progressivement les tokens.",
+ explain: "C'est l'approche adoptée par un algorithme de tokénisation différent."
},
{
text: "Un tokenizer BPE apprend les règles de fusion en fusionnant la paire de tokens la plus fréquente.",
@@ -195,84 +195,84 @@ Testons ce que vous avez appris dans ce chapitre !
},
{
text: "Un tokenizer BPE apprend une règle de fusion en fusionnant la paire de tokens qui maximise un score qui privilégie les paires fréquentes avec des parties individuelles moins fréquentes.",
- explain: "Non, c'est la stratégie appliquée par un autre algorithme de tokenization."
+ explain: "C'est la stratégie appliquée par un autre algorithme de tokenization."
},
{
text: "BPE tokenise les mots en sous-mots en les divisant en caractères, puis en appliquant les règles de fusion.",
- explain: "C'est exact !",
+ explain: " ",
correct: true
},
{
text: "BPE tokenise les mots en sous-mots en trouvant le plus long sous-mot du vocabulaire en commençant par le début, puis en répétant le processus pour le reste du texte.",
- explain: "Non, c'est la façon de faire d'un autre algorithme de tokenization."
+ explain: "C'est la façon de faire d'un autre algorithme de tokenization."
},
]}
/>
-### 9. Sélectionnez les phrases qui s'appliquent au *tokenizer* WordPiece.
+### 9. Sélectionnez les phrases qui s'appliquent au tokenizer WordPiece.
tokens.",
- explain: "Non, c'est l'approche adoptée par un algorithme de tokénisation différent."
+ text: "WordPiece est un algorithme de tokénisation en sous-mots qui part d'un grand vocabulaire et en retire progressivement les tokens.",
+ explain: "C'est la façon de faire d'un autre algorithme de tokenization."
},
{
text: "WordPiece Les tokenizer apprennent les règles de fusion en fusionnant la paire de tokens la plus fréquente.",
- explain: "Non, c'est la stratégie appliquée par un autre algorithme de tokenization."
+ explain: "C'est la façon de faire d'un autre algorithme de tokenization."
},
{
text: "Un tokenizer WordPiece apprend une règle de fusion en fusionnant la paire de tokens qui maximise un score qui privilégie les paires fréquentes avec des parties individuelles moins fréquentes.",
- explain: "C'est exact !",
+ explain: " ",
correct: true
},
{
text: "WordPiece tokenise les mots en sous-mots en trouvant la segmentation en tokens la plus probable, selon le modèle.",
- explain: "Non, c'est le fonctionnement d'un autre algorithme de tokenization."
+ explain: "C'est la façon de faire d'un autre algorithme de tokenization."
},
{
text: "WordPiece tokenise les mots en sous-mots en trouvant le plus long sous-mot du vocabulaire en commençant par le début, puis en répétant le processus pour le reste du texte.",
- explain: "Oui, c'est ainsi que WordPiece procède pour l'encodage.",
+ explain: "C'est ainsi que WordPiece procède pour l'encodage.",
correct: true
},
]}
/>
-### 10. Sélectionnez les phrases qui s'appliquent au *tokenizer* Unigram.
+### 10. Sélectionnez les phrases qui s'appliquent au tokenizer Unigram.
tokens.",
- explain: "C'est exact !",
+ text: "Unigram est un algorithme de tokénisation en sous-mots qui part d'un grand vocabulaire et en retire progressivement les tokens.",
+ explain: " ",
correct: true
},
{
text: "Unigram adapte son vocabulaire en minimisant une perte calculée sur l'ensemble du corpus.",
- explain: "C'est exact !",
+ explain: " ",
correct: true
},
{
text: "Unigram adapte son vocabulaire en conservant les sous-mots les plus fréquents.",
- explain: "Non, cet incorrect."
+ explain: " "
},
{
text: "Unigram segmente les mots en sous-mots en trouvant la segmentation la plus probable en tokens, selon le modèle.",
- explain: "C'est exact !",
+ explain: " ",
correct: true
},
{
text: "Unigram décompose les mots en sous-mots en les divisant en caractères puis en appliquant les règles de fusion.",
- explain: "Non, c'est le fonctionnement d'un autre algorithme de tokenization."
+ explain: "C'est la façon de faire d'un autre algorithme de tokenization."
},
]}
/>
diff --git a/chapters/fr/chapter6/2.mdx b/chapters/fr/chapter6/2.mdx
index 6168820ae..e4a23cc6e 100644
--- a/chapters/fr/chapter6/2.mdx
+++ b/chapters/fr/chapter6/2.mdx
@@ -1,4 +1,4 @@
-# Entraîner un nouveau *tokenizer* à partir d'un ancien
+# Entraîner un nouveau tokenizer à partir d'un ancien
-Si un modèle de langue n'est pas disponible dans la langue qui vous intéresse ou si votre corpus est très différent de celui sur lequel votre modèle de langue a été entraîné, vous voudrez très probablement réentraîner le modèle à partir de zéro en utilisant un *tokenizer* adapté à vos données. Pour ce faire, vous devrez entraîner un nouveau *tokenizer* sur votre ensemble de données. Mais qu'est-ce que cela signifie exactement ? Lorsque nous avons examiné pour la première fois les *tokenizers* dans le [Chapitre 2](/course/fr/chapter2), nous avons vu que la plupart des *transformers* utilisent un _algorithme de tokenisation des sous-mots_. Pour identifier les sous-mots qui sont intéressants et qui apparaissent le plus fréquemment dans le corpus en question, le *tokenizer* doit examiner attentivement tous les textes du corpus -- un processus que nous appelons *entraînement*. Les règles exactes qui régissent cet apprentissage dépendent du type de *tokenizer* utilisé, et nous passerons en revue les trois principaux algorithmes plus loin dans ce chapitre.
+Si un modèle de langue n'est pas disponible dans la langue qui vous intéresse ou si votre corpus est très différent de celui sur lequel votre modèle de langue a été entraîné, vous voudrez très probablement réentraîner le modèle à partir de zéro en utilisant un *tokenizer* adapté à vos données. Pour ce faire, vous devrez entraîner un nouveau *tokenizer* sur votre jeu de données. Mais qu'est-ce que cela signifie exactement ? Lorsque nous avons examiné pour la première fois les *tokenizers* dans le [chapitre 2](/course/fr/chapter2), nous avons vu que la plupart des *transformers* utilisent un _algorithme de tokenisation en sous-mots_. Pour identifier les sous-mots qui sont intéressants et qui apparaissent le plus fréquemment dans un corpus donné, le *tokenizer* doit examiner attentivement tous les textes du corpus. C'est un processus que nous appelons *entraînement*. Les règles exactes qui régissent cet apprentissage dépendent du type de *tokenizer* utilisé. Nous passerons en revue les trois principaux algorithmes plus loin dans ce chapitre.
-⚠️ Entraîner un *tokenizer* n'est pas la même chose qu'entraîner un modèle ! L'entraînement du modèle utilise la descente de gradient stochastique pour réduire un peu plus la perte à chaque batch. Il est aléatoire par nature (ce qui signifie que vous devez définir des graines pour obtenir les mêmes résultats lorsque vous effectuez deux fois le même entraînement). Entraîner un *tokenizer* est un processus statistique qui tente d'identifier les meilleurs sous-mots à choisir pour un corpus donné, et les règles exactes utilisées pour les choisir dépendent de l'algorithme de tokénisation. C'est un processus déterministe, ce qui signifie que vous obtenez toujours les mêmes résultats lorsque vous vous entraînez avec le même algorithme sur le même corpus.
+⚠️ Entraîner un *tokenizer* n'est pas la même chose qu'entraîner un modèle ! L'entraînement du modèle utilise la descente de gradient stochastique pour réduire un peu plus la perte à chaque batch. Il est par nature aléatoire (ce qui signifie que vous devez définir des graines pour obtenir les mêmes résultats lorsque vous effectuez deux fois le même entraînement). Entraîner un *tokenizer* est un processus statistique qui identifie les meilleurs sous-mots à choisir pour un corpus donné. Les règles exactes utilisées pour les choisir dépendent de l'algorithme de tokénisation. Le processus est déterministe, ce qui signifie que vous obtenez toujours les mêmes résultats lorsque vous vous entraînez avec le même algorithme sur le même corpus.
## Assemblage d'un corpus
-Il y a une API très simple dans 🤗 *Transformers* que vous pouvez utiliser pour entraîner un nouveau *tokenizer* avec les mêmes caractéristiques qu'un existant : `AutoTokenizer.train_new_from_iterator()`. Pour voir cela en action, disons que nous voulons entraîner GPT-2 à partir de zéro, mais dans une langue autre que l'anglais. Notre première tâche sera de rassembler des batchs de données dans cette langue dans un corpus d'entraînement. Pour fournir des exemples que tout le monde pourra comprendre, nous n'utiliserons pas ici une langue comme le russe ou le chinois, mais plutôt une langue anglaise spécialisée : le code Python.
+Il y a une API très simple dans 🤗 *Transformers* que vous pouvez utiliser pour entraîner un nouveau *tokenizer* avec les mêmes caractéristiques qu'un déjà existant : `AutoTokenizer.train_new_from_iterator()`. Pour illustrer cela, disons que nous voulons entraîner GPT-2 à partir de zéro mais dans une langue autre que l'anglais. Notre première tâche est de rassembler des batchs de données dans cette langue dans un corpus d'entraînement. Pour avoir des exemples que tout le monde puisse comprendre, nous n'utiliserons pas ici une langue comme le russe ou le chinois mais plutôt une langue anglaise spécialisée : le langage Python.
La bibliothèque [🤗 *Datasets*](https://github.com/huggingface/datasets) peut nous aider à assembler un corpus de code source Python. Nous allons utiliser la fonction habituelle `load_dataset()` pour télécharger et mettre en cache le jeu de données [CodeSearchNet](https://huggingface.co/datasets/code_search_net). Ce jeu de données a été créé pour le [CodeSearchNet challenge](https://wandb.ai/github/CodeSearchNet/benchmark) et contient des millions de fonctions provenant de bibliothèques open source sur GitHub dans plusieurs langages de programmation. Ici, nous allons charger la partie Python de ce jeu de données :
```py
from datasets import load_dataset
-# Le chargement peut prendre quelques minutes, alors prenez un café ou un thé pendant que vous attendez !
+# Cela peut prendre quelques minutes alors prenez un thé ou un café pendant que vous patientez !
raw_datasets = load_dataset("code_search_net", "python")
```
-Nous pouvons jeter un coup d'œil à la répartition dans le jeu d'entraînement pour voir à quelles colonnes nous avons accès :
+Nous pouvons jeter un coup d'œil au jeu d'entraînement pour voir quelles sont les colonnes auxquelles nous avons accès :
```py
raw_datasets["train"]
@@ -47,13 +47,13 @@ Dataset({
})
```
-Nous pouvons voir que le jeu de données sépare les chaînes de documents du code et suggère une tokenization des deux. Ici, nous utiliserons simplement la colonne `whole_func_string` pour entraîner notre *tokenizer*. Nous pouvons regarder un exemple d'une de ces fonctions en indexant dans le split `train` :
+Nous pouvons voir que le jeu de données sépare les chaînes de documents du code et suggère une tokenization des deux. Ici, nous utiliserons simplement la colonne `whole_func_string` pour entraîner notre *tokenizer*. Nous pouvons regarder un exemple de la façon suivante :
```py
print(raw_datasets["train"][123456]["whole_func_string"])
```
-qui devrait afficher ce qui suit :
+qui nous affiche ce qui suit :
```out
def handle_simple_responses(
@@ -70,9 +70,9 @@ def handle_simple_responses(
return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)
```
-La première chose à faire est de transformer l'ensemble de données en un _itérateur_ de listes de textes -- par exemple, une liste de listes de textes. L'utilisation de listes de textes permettra à notre *tokenizer* d'aller plus vite (en s'entraînant sur des lots de textes au lieu de traiter des textes individuels un par un), et il doit s'agir d'un itérateur si nous voulons éviter d'avoir tout en mémoire en même temps. Si votre corpus est énorme, vous voudrez profiter du fait que 🤗 *Datasets* ne charge pas tout en RAM mais stocke les éléments du jeu de données sur le disque.
+La première chose à faire est de transformer le jeu de données en un _itérateur_ de listes de textes. Par exemple, une liste de listes de textes. L'utilisation de listes de textes permet à notre *tokenizer* d'aller plus vite (l'entraînement a alors lieu sur des batchs de textes au lieu de traiter des textes un par un). Et le fait que ce soit un itérateur permet d'éviter d'avoir tout en mémoire en même temps. Si votre corpus est énorme, vous voudrez profiter du fait que 🤗 *Datasets* ne charge pas tout en RAM mais stocke les éléments du jeu de données sur le disque.
-Faire ce qui suit créerait une liste de listes de 1 000 textes chacune, mais chargerait tout en mémoire :
+Faire ce qui suit créerait une liste de listes de 1 000 textes chacune mais chargerait tout en mémoire :
```py
@@ -80,7 +80,7 @@ Faire ce qui suit créerait une liste de listes de 1 000 textes chacune, mais ch
# training_corpus = [raw_datasets["train"][i: i + 1000]["whole_func_string"] for i in range(0, len(raw_datasets["train"]), 1000)]
```
-En utilisant un générateur Python, nous pouvons éviter que Python ne charge quoi que ce soit en mémoire jusqu'à ce que cela soit réellement nécessaire. Pour créer un tel générateur, il suffit de remplacer les crochets par des parenthèses :
+En utilisant un générateur, nous pouvons éviter que Python ne charge quoi que ce soit en mémoire à moins que cela soit réellement nécessaire. Pour créer un tel générateur, il suffit de remplacer les crochets par des parenthèses :
```py
training_corpus = (
@@ -89,7 +89,7 @@ training_corpus = (
)
```
-Cette ligne de code ne récupère aucun élément de l'ensemble de données ; elle crée simplement un objet que vous pouvez utiliser dans une boucle `for` Python. Les textes ne seront chargés que lorsque vous en aurez besoin (c'est-à-dire lorsque vous serez à l'étape de la boucle `for` qui les requiert), et seulement 1 000 textes à la fois seront chargés. De cette façon, vous n'épuiserez pas toute votre mémoire, même si vous traitez un énorme ensemble de données.
+Cette ligne de code ne récupère aucun élément du jeu de données. Elle crée simplement un objet que vous pouvez utiliser dans une boucle `for` Python. Les textes ne seront chargés que lorsque vous en aurez besoin (c'est-à-dire lorsque vous serez à l'étape de la boucle `for` qui les requiert) et seulement 1 000 textes à la fois. De cette façon, vous n'épuiserez pas toute votre mémoire, même si vous traitez un énorme jeu de données.
Le problème avec un objet générateur est qu'il ne peut être utilisé qu'une seule fois. Ainsi, au lieu que cet objet nous donne deux fois la liste des 10 premiers chiffres :
@@ -130,11 +130,12 @@ def get_training_corpus():
yield samples["whole_func_string"]
```
-qui produira exactement le même générateur que précédemment, mais vous permet d'utiliser une logique plus complexe que celle que vous pouvez utiliser dans une compréhension de liste.
+qui produit exactement le même générateur que précédemment mais permet d'utiliser une logique plus complexe que celle que vous pouvez utiliser dans une compréhension de liste.
-## Entraînement d'un nouveau *tokenizer*.
-Maintenant que nous avons notre corpus sous la forme d'un itérateur de lots de textes, nous sommes prêts à entraîner un nouveau *tokenizer*. Pour ce faire, nous devons d'abord charger le *tokenizer* que nous voulons coupler avec notre modèle (ici, GPT-2) :
+## Entraînement d'un nouveau tokenizer
+
+Maintenant que nous avons notre corpus sous la forme d'un itérateur de batchs de textes, nous sommes prêts à entraîner un nouveau *tokenizer*. Pour ce faire, nous devons d'abord charger le *tokenizer* que nous voulons coupler avec notre modèle (ici, le GPT-2) :
```py
@@ -143,7 +144,7 @@ from transformers import AutoTokenizer
old_tokenizer = AutoTokenizer.from_pretrained("gpt2")
```
-Même si nous allons entraîner un nouveau *tokenizer*, c'est une bonne idée de le faire pour éviter de partir entièrement de zéro. De cette façon, nous n'aurons pas à spécifier l'algorithme de tokénisation ou les jetons spéciaux que nous voulons utiliser ; notre nouveau *tokenizer* sera exactement le même que GPT-2, et la seule chose qui changera sera le vocabulaire, qui sera déterminé par l'Entraînement sur notre corpus.
+Même si nous allons entraîner un nouveau *tokenizer*, c'est une bonne idée de faire ça pour éviter de partir entièrement de zéro. De cette façon, nous n'aurons pas à spécifier l'algorithme de tokénisation ou les jetons spéciaux que nous voulons utiliser. Notre nouveau *tokenizer* sera exactement le même que celui du GPT-2. La seule chose qui changera sera le vocabulaire qui sera déterminé lors de l'entraînement sur notre corpus.
Voyons d'abord comment ce *tokenizer* traiterait un exemple de fonction :
@@ -162,7 +163,7 @@ tokens
'Ġnumbers', 'Ġ`', 'a', '`', 'Ġand', 'Ġ`', 'b', '`', '."', '""', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
```
-Ce *tokenizer* possède quelques symboles spéciaux, comme `Ġ` et `Ċ`, qui désignent respectivement les espaces et les retours à la ligne. Comme on peut le voir, ce n'est pas très efficace : le *tokenizer* renvoie des jetons individuels pour chaque espace, alors qu'il pourrait regrouper les niveaux d'indentation (puisqu’avoir des ensembles de quatre ou huit espaces va être très courant dans le code). Il divise également le nom de la fonction de façon un peu bizarre, n'étant pas habitué à voir des mots avec le caractère `_`.
+Ce *tokenizer* possède quelques symboles spéciaux, comme `Ġ` et `Ċ`, qui désignent respectivement les espaces et les retours à la ligne. Comme on peut le voir, ce n'est pas très efficace. Le *tokenizer* renvoie des jetons individuels pour chaque espace alors qu'il pourrait regrouper ceux des indentations (puisqu’avoir des ensembles de quatre ou huit espaces est très courant dans du code). Il divise également le nom de la fonction de façon un peu bizarre car pas habitué à voir des mots avec le caractère `_`.
Entraînons un nouveau *tokenizer* et voyons s'il résout ces problèmes. Pour cela, nous allons utiliser la méthode `train_new_from_iterator()` :
@@ -171,14 +172,13 @@ Entraînons un nouveau *tokenizer* et voyons s'il résout ces problèmes. Pour c
tokenizer = old_tokenizer.train_new_from_iterator(training_corpus, 52000)
```
-Cette commande peut prendre un peu de temps si votre corpus est très grand, mais pour ce jeu de données de 1,6 Go de textes, elle est très rapide (1 minute 16 secondes sur un CPU AMD Ryzen 9 3900X avec 12 cœurs).
-
-Notez que `AutoTokenizer.train_new_from_iterator()` ne fonctionne que si le tokenizer que vous utilisez est un tokenizer « rapide ». Comme vous le verrez dans la section suivante, la bibliothèque 🤗 *Transformers* contient deux types de *tokenizers* : certains sont écrits purement en Python et d'autres (les rapides) sont soutenus par la bibliothèque 🤗 *Tokenizers*, qui est écrite dans le langage de programmation [Rust](https://www.rust-lang.org). Python est le langage le plus souvent utilisé pour les applications de science des données et d'apprentissage profond, mais lorsque quelque chose doit être parallélisé pour être rapide, il doit être écrit dans un autre langage. Par exemple, les multiplications matricielles qui sont au cœur du calcul du modèle sont écrites en CUDA, une bibliothèque C optimisée pour les GPU.
+Cette commande peut prendre un peu de temps si votre corpus est très grand. Pour ce jeu de données de 1,6 Go de textes, elle est très rapide (1 minute 16 secondes sur un CPU AMD Ryzen 9 3900X avec 12 cœurs).
-Entraîner un tout nouveau *tokenizer* en Python pur serait atrocement lent, c'est pourquoi nous avons développé la bibliothèque 🤗 *Tokenizers*. Notez que, tout comme vous n'avez pas eu à apprendre le langage CUDA pour pouvoir exécuter votre modèle sur un lot d'entrées sur un GPU, vous n'aurez pas besoin d'apprendre Rust pour utiliser un *tokenizer* rapide. La bibliothèque 🤗 *Tokenizers* fournit des liaisons Python pour de nombreuses méthodes qui appellent en interne un morceau de code en Rust ; par exemple, pour paralléliser l'entraînement de votre nouveau *tokenizer* ou, comme nous l'avons vu dans le [Chapitre 3](/course/fr/chapter3), la tokenisation d'un lot d'entrées.
+Notez que `AutoTokenizer.train_new_from_iterator()` ne fonctionne que si le *tokenizer* que vous utilisez est un *tokenizer* « rapide ». Comme vous le verrez dans la section suivante, la bibliothèque 🤗 *Transformers* contient deux types de *tokenizers* : certains sont écrits en pur Python et d'autres (les rapides) sont soutenus par la bibliothèque 🤗 *Tokenizers* qui est écrite dans le langage [Rust](https://www.rust-lang.org). Python est le langage le plus souvent utilisé pour les applications de science des données et d'apprentissage profond, mais lorsque quelque chose doit être parallélisé pour être rapide, il faut que cela soit écrit dans un autre langage. Par exemple, les multiplications matricielles qui sont au cœur du calcul du modèle sont écrites en CUDA, une bibliothèque en C optimisée pour les GPUs.
-La plupart des *transformers* ont un *tokenizer* rapide disponible (il y a quelques exceptions que vous pouvez vérifier [ici](https://huggingface.co/transformers/#supported-frameworks)), et l'API `AutoTokenizer` sélectionne toujours le *tokenizer* rapide pour vous s'il est disponible. Dans la prochaine section, nous allons jeter un coup d'oeil à certaines des autres caractéristiques spéciales des *tokenizers* rapides, qui seront vraiment utiles pour des tâches comme la classification des *tokens* et la réponse aux questions. Mais avant cela, essayons notre tout nouveau *tokenizer* sur l'exemple précédent :
+Entraîner un tout nouveau *tokenizer* en Python pur est atrocement lent, c'est pourquoi nous avons développé la bibliothèque 🤗 *Tokenizers*. Notez que, tout comme vous n'avez pas eu à apprendre le langage CUDA pour pouvoir exécuter votre modèle sur un batch d'entrées sur un GPU, vous n'aurez pas besoin d'apprendre Rust pour utiliser un *tokenizer* rapide. La bibliothèque 🤗 *Tokenizers* fournit des liaisons Python pour de nombreuses méthodes qui appellent en interne un morceau de code en Rust. Par exemple, pour paralléliser l'entraînement de votre nouveau *tokenizer* ou, comme nous l'avons vu dans le [Chapitre 3](/course/fr/chapter3), la tokenisation d'un lot d'entrées.
+La plupart des *transformers* ont un *tokenizer* rapide de disponible. Il y a quelques exceptions que vous pouvez vérifier [ici](https://huggingface.co/transformers/#supported-frameworks). S'il est disponible, l'API `AutoTokenizer` sélectionne toujours pour vous le *tokenizer* rapide. Dans la prochaine section, nous allons jeter un coup d'oeil à certaines des autres caractéristiques spéciales des *tokenizers* rapides, qui seront très utiles pour des tâches comme la classification de *tokens* et la réponse aux questions. Mais avant cela, essayons notre tout nouveau *tokenizer* sur l'exemple précédent :
```py
tokens = tokenizer.tokenize(example)
@@ -190,7 +190,7 @@ tokens
'a', '`', 'Ġand', 'Ġ`', 'b', '`."""', 'ĊĠĠĠ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
```
-Ici, nous voyons à nouveau les symboles spéciaux `Ġ` et `Ċ` qui indiquent les espaces et les retours à la ligne, mais nous pouvons également voir que notre *tokenizer* a appris certains *tokens* qui sont très spécifiques à un corpus de fonctions Python : par exemple, il y a un token `ĊĠĠĠ` qui représente une indentation, et un *token* `Ġ"""` qui représente les trois guillemets qui commencent une docstring. Le *tokenizer* divise également correctement le nom de la fonction sur `_`. Il s'agit d'une représentation assez compacte ; en comparaison, l'utilisation du *tokenizer* en anglais simple sur le même exemple nous donnera une phrase plus longue :
+Ici, nous voyons à nouveau les symboles spéciaux `Ġ` et `Ċ` qui indiquent les espaces et les retours à la ligne. Nous pouvons également voir que notre *tokenizer* a appris certains *tokens* qui sont très spécifiques à un corpus de fonctions Python. Par exemple, il y a un token `ĊĠĠĠ` qui représente une indentation et un *token* `Ġ"""` qui représente les trois guillemets qui commencent une *docstring*. Le *tokenizer* divise également correctement le nom de la fonction sur `_`. Il s'agit d'une représentation assez compacte. En comparaison, l'utilisation du *tokenizer* en anglais « simple » sur le même exemple nous donnera une phrase plus longue :
```py
print(len(tokens))
@@ -224,9 +224,9 @@ tokenizer.tokenize(example)
'Ġreturn', 'Ġx', 'Ġ@', 'Ġself', '.', 'weights', 'Ġ+', 'Ġself', '.', 'bias', 'ĊĠĠĠĠ']
```
-En plus du *token* correspondant à une indentation, on peut également voir ici un *token* pour une double indentation : `ĊĠĠĠĠĠĠĠĠĠ`. Les mots spéciaux de Python comme `class`, `init`, `call`, `self`, et `return` sont tous tokenizés comme un seul token, et nous pouvons voir qu'en plus de séparer sur `_` et `.` le tokenizer sépare correctement même les noms en minuscules : `LinearLayer` est tokenizé comme `["ĠLinear", "Layer"]`.
+En plus du *token* correspondant à une indentation, on peut également voir ici un *token* pour une double indentation : `ĊĠĠĠĠĠĠĠĠĠ`. Les mots spéciaux de Python comme `class`, `init`, `call`, `self`, et `return` sont tous tokenizés comme un seul *token*. Nous pouvons voir qu'en plus de séparer sur `_` et `.` le tokenizer sépare correctement même les noms en minuscules. Par exemple `LinearLayer` est tokenisé comme `["ĠLinear", "Layer"]`.
-## Sauvegarde du *tokenizer*.
+## Sauvegarde du tokenizer
Pour être sûr de pouvoir l'utiliser plus tard, nous devons sauvegarder notre nouveau *tokenizer*. Comme pour les modèles, ceci est fait avec la méthode `save_pretrained()` :
@@ -235,7 +235,7 @@ Pour être sûr de pouvoir l'utiliser plus tard, nous devons sauvegarder notre n
tokenizer.save_pretrained("code-search-net-tokenizer")
```
-Cela créera un nouveau dossier nommé *code-search-net-tokenizer*, qui contiendra tous les fichiers dont le *tokenizer* a besoin pour être rechargé. Si vous souhaitez partager ce *tokenizer* avec vos collègues et amis, vous pouvez le télécharger sur le *Hub* en vous connectant à votre compte. Si vous travaillez dans un *notebook*, il existe une fonction pratique pour vous aider à le faire :
+Cela créera un nouveau dossier nommé *code-search-net-tokenizer* contenant tous les fichiers dont le *tokenizer* a besoin pour être rechargé. Si vous souhaitez partager ce *tokenizer* avec vos collègues et amis, vous pouvez le télécharger sur le *Hub* en vous connectant à votre compte. Si vous travaillez dans un *notebook*, il existe une fonction pratique pour vous aider à le faire :
```python
from huggingface_hub import notebook_login
@@ -243,23 +243,23 @@ from huggingface_hub import notebook_login
notebook_login()
```
-Cela affichera un widget où vous pourrez entrer vos identifiants de connexion à Hugging Face. Si vous ne travaillez pas dans un ordinateur portable, tapez simplement la ligne suivante dans votre terminal :
+Cela affichera un *widget* où vous pourrez entrer vos identifiants de connexion à Hugging Face. Si vous ne travaillez pas sur un ordinateur portable, tapez simplement la ligne suivante dans votre terminal :
```bash
huggingface-cli login
```
-Une fois que vous vous êtes connecté, vous pouvez pousser votre *tokenizer* en exécutant la commande suivante :
+Une fois connecté, vous pouvez pousser votre *tokenizer* en exécutant la commande suivante :
```py
tokenizer.push_to_hub("code-search-net-tokenizer")
```
-Cela créera un nouveau dépôt dans votre espace de noms avec le nom `code-search-net-tokenizer`, contenant le fichier *tokenizer*. Vous pouvez ensuite charger le *tokenizer* de n'importe où avec la méthode `from_pretrained()` :
+Cela créera un nouveau dépôt dans votre espace avec le nom `code-search-net-tokenizer` contenant le fichier *tokenizer*. Vous pouvez ensuite charger le *tokenizer* de n'importe où avec la méthode `from_pretrained()` :
```py
-# Remplacez "huggingface-course" ci-dessous par votre espace de nom réel pour utiliser votre propre tokenizer
+# Remplacez "huggingface-course" ci-dessous par votre espace réel pour utiliser votre propre tokenizer
tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")
```
-Vous êtes maintenant prêt à entraîner un modèle de langue à partir de zéro et à le *finetuner* sur votre tâche ! Nous y reviendrons au [Chapitre 7](/course/fr/chapter7), mais d'abord, dans le reste de ce chapitre, nous allons examiner de plus près les *tokenizers* rapides et explorer en détail ce qui se passe réellement lorsque nous appelons la méthode `train_new_from_iterator()`.
\ No newline at end of file
+Vous êtes maintenant prêt à entraîner un modèle de langue à partir de zéro et à le *finetuner* sur votre tâche ! Nous verrons cela dans le [chapitre 7](/course/fr/chapter7), mais d'abord, dans le reste de ce chapitre, nous allons examiner de plus près les *tokenizers* rapides et explorer en détail ce qui se passe lorsque nous appelons la méthode `train_new_from_iterator()`.
\ No newline at end of file
diff --git a/chapters/fr/chapter6/3.mdx b/chapters/fr/chapter6/3.mdx
index 85c5c2d43..c37fc06ff 100644
--- a/chapters/fr/chapter6/3.mdx
+++ b/chapters/fr/chapter6/3.mdx
@@ -1,6 +1,6 @@
-# Pouvoirs spéciaux des *tokenizers* rapides
+# Pouvoirs spéciaux des tokenizers rapides
{#if fw === 'pt'}
@@ -22,12 +22,12 @@
{/if}
-Dans cette section, nous allons examiner de plus près les capacités des tokenizers dans 🤗 *Transformers*.
- Jusqu'à présent, nous ne les avons utilisés que pour *tokeniser* les entrées ou décoder les identifiants pour les retranscrire en texte, mais les *tokenizers*, surtout ceux soutenus par la bibliothèque 🤗 *Tokenizers*, peuvent faire beaucoup plus. Pour illustrer ces fonctionnalités supplémentaires, nous allons explorer comment reproduire les résultats des pipelines `token-classification` (que nous avons appelé `ner`) et `question-answering` que nous avons rencontrés pour la première fois dans [Chapter 1](/course/fr/chapter1).
+Dans cette section, nous allons examiner de plus près les capacités des *tokenizers* dans 🤗 *Transformers*.
+Jusqu'à présent, nous ne les avons utilisés que pour tokeniser les entrées ou décoder les identifiants pour revenir à du texte. Mais les *tokenizers*, et surtout ceux soutenus par la bibliothèque 🤗 *Tokenizers*, peuvent faire beaucoup plus. Pour illustrer ces fonctionnalités supplémentaires, nous allons explorer comment reproduire les résultats des pipelines `token-classification` (que nous avons appelé `ner`) et `question-answering` que nous avons rencontrés pour la première fois dans le [chapitre 1](/course/fr/chapter1).
-Dans la discussion qui suit, nous ferons souvent la distinction entre les *tokenizers* « lents » et « rapides ». Les *tokenizers* lents sont ceux écrits en Python à l'intérieur de la bibliothèque 🤗 *Transformers*, tandis que les versions rapides sont celles fournies par 🤗 *Tokenizers*, qui sont écrites en Rust. Si vous vous souvenez du tableau du [Chapitre 5](/course/fr/chapter5/3) qui indiquait combien de temps il fallait à un *tokenizer* rapide et à un *tokenizer* lent pour tokeniser le jeu de données Drug Review, vous devriez avoir une idée de la raison pour laquelle nous les appelons rapides et lents :
+Dans la discussion qui suit, nous ferons souvent la distinction entre les *tokenizers* « lents » et les « rapides ». Les *tokenizers* lents sont ceux écrits en Python à l'intérieur de la bibliothèque 🤗 *Transformers*, tandis que les rapides sont ceux fournis par 🤗 *Tokenizers* et sont codés en Rust. Si vous vous souvenez du tableau du [chapitre 5](/course/fr/chapter5/3) qui indiquait combien de temps il fallait à un *tokenizer* rapide et à un *tokenizer* lent pour tokeniser le jeu de données *Drug Review*, vous devriez avoir une idée de la raison pour laquelle nous les appelons rapides et lents :
| *Tokenizer* rapide | *Tokenizer* lent
:--------------:|:--------------:|:-------------:
@@ -36,11 +36,11 @@ Dans la discussion qui suit, nous ferons souvent la distinction entre les *token
-⚠️ Lors de la tokenisation d'une seule phrase, vous ne verrez pas toujours une différence de vitesse entre les versions lente et rapide d'un même *tokenizer*. En fait, la version rapide peut même être plus lente ! Ce n'est que lorsque vous tokenisez des batchs de textes en parallèle et en même temps que vous pourrez voir clairement la différence.
+⚠️ Lors de la tokenisation d'une seule phrase, vous ne verrez pas toujours une différence de vitesse entre les versions lente et rapide d'un même *tokenizer*. En fait, la version rapide peut même être plus lente ! Ce n'est que lorsque vous tokenisez beaucoup de textes en parallèle et en même temps que vous pourrez clairement voir la différence.
-## *BatchEncoding*
+## L'objet BatchEncoding
@@ -54,7 +54,8 @@ Prenons un exemple :
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-example = "My name is Sylvain and I work at Hugging Face in Brooklyn." # "Je m'appelle Sylvain et je travaille chez Hugging Face à Brooklyn."
+example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
+# Je m'appelle Sylvain et je travaille chez Hugging Face à Brooklyn.
encoding = tokenizer(example)
print(type(encoding))
```
@@ -65,7 +66,7 @@ Comme mentionné précédemment, nous obtenons un objet `BatchEncoding` dans la
```
-Puisque la classe `AutoTokenizer` choisit un *tokenizer* rapide par défaut, nous pouvons utiliser les méthodes supplémentaires que cet objet `BatchEncoding` fournit. Nous avons deux façons de vérifier si notre *tokenizer* est rapide ou lent. Nous pouvons soit vérifier l'attribut `is_fast` du *tokenizer* :
+Puisque la classe `AutoTokenizer` choisit un *tokenizer* rapide par défaut, nous pouvons utiliser les méthodes supplémentaires que cet objet `BatchEncoding` fournit. Nous avons deux façons de vérifier si notre *tokenizer* est rapide ou lent. Nous pouvons soit vérifier l'attribut `is_fast` du *tokenizer* comme suit :
```python
tokenizer.is_fast
@@ -75,7 +76,7 @@ tokenizer.is_fast
True
```
-ou vérifiez le même attribut de notre `encoding` :
+soit vérifier le même attribut mais avec notre `encoding` :
```python
encoding.is_fast
@@ -85,7 +86,7 @@ encoding.is_fast
True
```
-Voyons ce qu'un *tokenizer* rapide nous permet de faire. Tout d'abord, nous pouvons accéder aux *tokens* sans avoir à reconvertir les ID en *tokens* :
+Voyons ce qu'un *tokenizer* rapide nous permet de faire. Tout d'abord, nous pouvons accéder aux *tokens* sans avoir à reconvertir les identifiants en *tokens* :
```py
encoding.tokens()
@@ -96,7 +97,7 @@ encoding.tokens()
'Brooklyn', '.', '[SEP]']
```
-Dans ce cas, le *token* à l'index 5 est `##yl`, qui fait partie du mot « Sylvain » dans la phrase originale. Nous pouvons également utiliser la méthode `word_ids()` pour obtenir l'index du mot dont provient chaque *token* :
+Dans ce cas, le *token* à l'index 5 est `##yl` et fait partie du mot « Sylvain » dans la phrase originale. Nous pouvons également utiliser la méthode `word_ids()` pour obtenir l'index du mot dont provient chaque *token* :
```py
encoding.word_ids()
@@ -106,19 +107,21 @@ encoding.word_ids()
[None, 0, 1, 2, 3, 3, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, None]
```
-On peut voir que les *tokens* spéciaux du tokenizer `[CLS]` et `[SEP]` sont mis en correspondance avec `None`, puis chaque *token* est mis en correspondance avec le mot dont il provient. Ceci est particulièrement utile pour déterminer si un *token* est au début d'un mot ou si deux *tokens* sont dans le même mot. Nous pourrions nous appuyer sur le préfixe `##` pour cela, mais il ne fonctionne que pour les *tokenizers* de type BERT. Cette méthode fonctionne pour n'importe quel type de *tokenizer*, du moment qu'il est rapide. Dans le chapitre suivant, nous verrons comment utiliser cette capacité pour appliquer correctement les étiquettes que nous avons pour chaque mot aux *tokens* dans des tâches comme la reconnaissance d'entités nommées (NER) et le marquage POS (Part-of-speech). Nous pouvons également l'utiliser pour masquer tous les *tokens* provenant du même mot dans la modélisation du langage masqué (une technique appelée _whole word masking_).
+On peut voir que les *tokens* spéciaux du *tokenizer*, `[CLS]` et `[SEP]`, sont mis en correspondance avec `None` et que chaque *token* est mis en correspondance avec le mot dont il provient. Ceci est particulièrement utile pour déterminer si un *token* est au début d'un mot ou si deux *tokens* sont dans le même mot. Nous pourrions nous appuyer sur le préfixe `##` pour cela, mais il ne fonctionne que pour les *tokenizers* de type BERT. Cette méthode fonctionne pour n'importe quel type de *tokenizer*, du moment qu'il est rapide. Dans le chapitre suivant, nous verrons comment utiliser cette capacité pour appliquer correctement les étiquettes que nous avons pour chaque mot aux *tokens* dans des tâches comme la reconnaissance d'entités nommées et le POS (*Part-of-speech*). Nous pouvons également l'utiliser pour masquer tous les *tokens* provenant du même mot dans la modélisation du langage masqué (une technique appelée _whole word masking_).
+
-
-La notion de ce qu'est un mot est compliquée. Par exemple, est-ce que « I will » (contraction de « I will ») compte pour un ou deux mots ? Cela dépend en fait du *tokenizer* et de l'opération de pré-tokénisation qu'il applique. Certains *tokenizer* se contentent de séparer les espaces et considèrent donc qu'il s'agit d'un seul mot. D'autres utilisent la ponctuation en plus des espaces et considèrent donc qu'il s'agit de deux mots.
+La notion de ce qu'est un mot est compliquée. Par exemple, est-ce que « I'll » (contraction de « I will ») compte pour un ou deux mots ? Cela dépend en fait du *tokenizer* et de l'opération de prétokénisation qu'il applique. Certains *tokenizer* se contentent de séparer les espaces et considèrent donc qu'il s'agit d'un seul mot. D'autres utilisent la ponctuation en plus des espaces et considèrent donc qu'il s'agit de deux mots.
+
+
-✏️ **Essayez !** Créez un *tokenizer* à partir des points de contrôle `bert-base-cased` et `roberta-base` et tokenisez « 81s » avec eux. Qu'observez-vous ? Quels sont les identifiants des mots ?
+✏️ **Essayez !** Créez un *tokenizer* à partir des checkpoints `bert-base-cased` et `roberta-base` et tokenisez « 81s » avec. Qu'observez-vous ? Quels sont les identifiants des mots ?
-De même, il existe une méthode `sentence_ids()` que nous pouvons utiliser pour associer un token à la phrase dont il provient (bien que dans ce cas, le `token_type_ids` retourné par le *tokenizer* peut nous donner la même information).
+De même, il existe une méthode `sentence_ids()` que nous pouvons utiliser pour associer un *token* à la phrase dont il provient (bien que dans ce cas, le `token_type_ids` retourné par le *tokenizer* peut nous donner la même information).
-Enfin, nous pouvons faire correspondre n'importe quel mot ou jeton aux caractères du texte d'origine, et vice versa, grâce aux méthodes `word_to_chars()` ou `token_to_chars()` et `char_to_word()` ou `char_to_token()`. Par exemple, la méthode `word_ids()` nous a dit que `##yl` fait partie du mot à l'indice 3, mais de quel mot s'agit-il dans la phrase ? Nous pouvons le découvrir comme ceci :
+Enfin, nous pouvons faire correspondre n'importe quel mot ou *token* aux caractères du texte d'origine (et vice versa) grâce aux méthodes `word_to_chars()` ou `token_to_chars()` et `char_to_word()` ou `char_to_token()`. Par exemple, la méthode `word_ids()` nous a dit que `##yl` fait partie du mot à l'indice 3, mais de quel mot s'agit-il dans la phrase ? Nous pouvons le découvrir comme ceci :
```py
start, end = encoding.word_to_chars(3)
@@ -129,17 +132,17 @@ example[start:end]
Sylvain
```
-Comme nous l'avons mentionné précédemment, tout ceci est rendu possible par le fait que le tokenizer rapide garde la trace de la partie du texte d'où provient chaque *token dans une liste de *offsets*. Pour illustrer leur utilisation, nous allons maintenant vous montrer comment reproduire manuellement les résultats du pipeline `token-classification`.
+Comme nous l'avons mentionné précédemment, tout ceci est rendu possible par le fait que le *tokenizer* rapide garde la trace de la partie du texte d'où provient chaque *token* dans une liste d'*offsets*. Pour illustrer leur utilisation, nous allons maintenant vous montrer comment reproduire manuellement les résultats du pipeline `token-classification`.
-✏️ **Essayez !** Créez votre propre texte d'exemple et voyez si vous pouvez comprendre quels *tokens* sont associés à l'ID du mot, et aussi comment extraire les portées de caractères pour un seul mot. Pour obtenir des points bonus, essayez d'utiliser deux phrases en entrée et voyez si les identifiants de phrase ont un sens pour vous.
+✏️ **Essayez !** Rédigez votre propre texte et voyez si vous pouvez comprendre quels *tokens* sont associés à l'identifiant du mot et comment extraire les étendues de caractères pour un seul mot. Pour obtenir des points bonus, essayez d'utiliser deux phrases en entrée et voyez si les identifiants ont un sens pour vous.
## A l'intérieur du pipeline `token-classification`
-Dans le [Chapitre 1](/course/fr/chapter1), nous avons eu un premier aperçu de l'application de NER (où la tâche est d'identifier les parties du texte qui correspondent à des entités telles que des personnes, des lieux ou des organisations) avec la fonction 🤗 *Transformers* `pipeline()`. Puis, dans [Chapitre 2](/course/fr/chapter2), nous avons vu comment un pipeline regroupe les trois étapes nécessaires pour obtenir les prédictions à partir d'un texte brut : la tokenisation, le passage des entrées dans le modèle et le post-traitement. Les deux premières étapes du pipeline de `token-classification` sont les mêmes que dans tout autre pipeline, mais le post-traitement est un peu plus complexe. Voyons comment !
+Dans le [chapitre 1](/course/fr/chapter1), nous avons eu un premier aperçu de la NER (où la tâche est d'identifier les parties du texte qui correspondent à des entités telles que des personnes, des lieux ou des organisations) avec la fonction `pipeline()` de 🤗 *Transformers*. Puis, dans le [chapitre 2](/course/fr/chapter2), nous avons vu comment un pipeline regroupe les trois étapes nécessaires pour obtenir les prédictions à partir d'un texte brut : la tokenisation, le passage des entrées dans le modèle et le post-traitement. Les deux premières étapes du pipeline de `token-classification` sont les mêmes que dans tout autre pipeline mais le post-traitement est un peu plus complexe. Voyons comment !
{#if fw === 'pt'}
@@ -153,7 +156,7 @@ Dans le [Chapitre 1](/course/fr/chapter1), nous avons eu un premier aperçu de l
### Obtenir les résultats de base avec le pipeline
-Tout d'abord, prenons un pipeline de classification de tokens afin d'obtenir des résultats à comparer manuellement. Le modèle utilisé par défaut est [`dbmdz/bert-large-cased-finetuned-conll03-english`](https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english). Il effectue un NER sur les phrases :
+Tout d'abord, prenons un pipeline de classification de *tokens* afin d'obtenir des résultats à comparer manuellement. Le modèle utilisé par défaut est [`dbmdz/bert-large-cased-finetuned-conll03-english`](https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english). Il effectue une NER sur les phrases :
```py
from transformers import pipeline
@@ -173,7 +176,7 @@ token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
{'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```
-Le modèle a correctement identifié chaque token généré par « Sylvain » comme une personne, chaque token généré par « Hugging Face » comme une organisation, et le *token* « Brooklyn » comme un lieu. Nous pouvons également demander au pipeline de regrouper les tokens qui correspondent à la même entité :
+Le modèle a correctement identifié chaque *token* généré par « Sylvain » comme une personne, chaque *token* généré par « Hugging Face » comme une organisation, et le *token* « Brooklyn » comme un lieu. Nous pouvons également demander au pipeline de regrouper les *tokens* qui correspondent à la même entité :
```py
from transformers import pipeline
@@ -188,12 +191,11 @@ token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
{'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```
-La `aggregation_strategy` choisie va changer les scores calculés pour chaque entité groupée. Avec `"simple"` le score est juste la moyenne des scores de chaque *token* dans l'entité donnée : par exemple, le score de « Sylvain » est la moyenne des scores que nous avons vu dans l'exemple précédent pour les tokens `S`, `##yl`, `##va`, et `##in`. D'autres stratégies sont disponibles :
+La propriété `aggregation_strategy` choisie va changer les scores calculés pour chaque entité groupée. Avec `"simple"` le score est juste la moyenne des scores de chaque *token* dans l'entité donnée. Par exemple, le score de « Sylvain » est la moyenne des scores que nous avons vu dans l'exemple précédent pour les tokens `S`, `##yl`, `##va`, et `##in`. D'autres stratégies sont disponibles :
-- `"first"`, où le score de chaque entité est le score du premier token de cette entité (donc pour « Sylvain » ce serait 0.993828, le score du token `S`)
-- `"max"`, où le score de chaque entité est le score maximal des tokens de cette entité (ainsi, pour « Hugging Face », le score de « Face » serait de 0,98879766).
-- `"moyenne"`, où le score de chaque entité est la moyenne des scores des mots qui composent cette entité (ainsi, pour « Sylvain »,
-il n'y aurait pas de différence avec la stratégie `"simple"`, mais "Étreinte du visage" aurait un score de 0,9819, la moyenne des scores de « Hugging Face », 0,975, et « Face », 0,98879).
+- `"first"`, où le score de chaque entité est le score du premier *token* de cette entité (donc pour « Sylvain » ce serait 0.993828, le score du token `S`)
+- `"max"`, où le score de chaque entité est le score maximal des *tokens* de cette entité (ainsi, pour « Hugging Face », le score de « Face » serait de 0,98879766).
+- `"average"`, où le score de chaque entité est la moyenne des scores des mots qui composent cette entité (ainsi, pour « Sylvain », il n'y aurait pas de différence avec la stratégie `"simple"`, mais "Hugging Face" aurait un score de 0,9819, la moyenne des scores de « Hugging », 0,975, et « Face », 0,98879).
Voyons maintenant comment obtenir ces résultats sans utiliser la fonction `pipeline()` !
@@ -201,7 +203,7 @@ Voyons maintenant comment obtenir ces résultats sans utiliser la fonction `pipe
{#if fw === 'pt'}
-D'abord, nous devons tokeniser notre entrée et la faire passer dans le modèle. Cela se fait exactement comme dans le [Chapitre 2](/course/frchapter2). Nous instancions le *tokenizer* et le modèle en utilisant les classes `TFAutoXxx` et les utilisons ensuite dans notre exemple :
+D'abord, nous devons tokeniser notre entrée et la faire passer dans le modèle. Cela se fait exactement comme dans le [chapitre 2](/course/fr/chapter2). Nous instancions le *tokenizer* et le modèle en utilisant les classes `TFAutoXxx` et les utilisons ensuite dans notre exemple :
```py
from transformers import AutoTokenizer, AutoModelForTokenClassification
@@ -215,7 +217,7 @@ inputs = tokenizer(example, return_tensors="pt")
outputs = model(**inputs)
```
-Puisque nous utilisons `AutoModelForTokenClassification` ici, nous obtenons un ensemble de logits pour chaque token dans la séquence d'entrée :
+Puisque nous utilisons `AutoModelForTokenClassification`, nous obtenons un ensemble de logits pour chaque *token* dans la séquence d'entrée :
```py
print(inputs["input_ids"].shape)
@@ -229,7 +231,7 @@ torch.Size([1, 19, 9])
{:else}
-D'abord, nous devons tokeniser notre entrée et la faire passer dans le modèle. Cela se fait exactement comme dans le [Chapitre 2](/course/frchapter2). Nous instancions le *tokenizer* et le modèle en utilisant les classes `TFAutoXxx` et les utilisons ensuite dans notre exemple :
+D'abord, nous devons tokeniser notre entrée et la faire passer dans le modèle. Cela se fait exactement comme dans le [chapitre 2](/course/fr/chapter2). Nous instancions le *tokenizer* et le modèle en utilisant les classes `TFAutoXxx` et les utilisons ensuite dans notre exemple :
```py
from transformers import AutoTokenizer, TFAutoModelForTokenClassification
@@ -243,7 +245,7 @@ inputs = tokenizer(example, return_tensors="tf")
outputs = model(**inputs)
```
-Puisque nous utilisons `TFAutoModelForTokenClassification` ici, nous obtenons un ensemble de logits pour chaque *token* dans la séquence d'entrée :
+Puisque nous utilisons `TFAutoModelForTokenClassification`, nous obtenons un ensemble de logits pour chaque *token* dans la séquence d'entrée :
```py
print(inputs["input_ids"].shape)
@@ -257,7 +259,7 @@ print(outputs.logits.shape)
{/if}
-Nous avons un batch avec 1 séquence de 19 *tokens* et le modèle a 9 étiquettes différentes, donc la sortie du modèle a une forme de 1 x 19 x 9. Comme pour le pipeline de classification de texte, nous utilisons une fonction softmax pour convertir ces logits en probabilités, et nous prenons l'argmax pour obtenir des prédictions (notez que nous pouvons prendre l'argmax sur les logits parce que la softmax ne change pas l'ordre) :
+Nous avons un batch avec 1 séquence de 19 *tokens* et le modèle a 9 étiquettes différentes. Ainsi, la sortie du modèle a une forme de 1 x 19 x 9. Comme pour le pipeline de classification de texte, nous utilisons une fonction softmax pour convertir ces logits en probabilités et nous prenons l'argmax pour obtenir des prédictions (notez que nous pouvons prendre l'argmax sur les logits car la fonction softmax ne change pas l'ordre) :
{#if fw === 'pt'}
@@ -305,16 +307,16 @@ model.config.id2label
8: 'I-LOC'}
```
-Comme nous l'avons vu précédemment, il y a 9 étiquettes : `O` est le label pour les *tokens* qui ne sont dans aucune entité nommée (il signifie *outside*) et nous avons ensuite deux labels pour chaque type d'entité (divers, personne, organisation, et lieu). L'étiquette `B-XXX` indique que le *token* est au début d'une entité `XXX` et l'étiquette `I-XXX` indique que le *token* est à l'intérieur de l'entité `XXX`. Par exemple, dans l'exemple actuel, nous nous attendons à ce que notre modèle classe le *token* `S` comme `B-PER` (début d'une entité personne) et les *tokens* `##yl`, `##va` et `##in` comme `I-PER` (à l'intérieur d'une entité personne).
+Comme nous l'avons vu précédemment, il y a 9 étiquettes : `O` est le label pour les *tokens* qui ne sont dans aucune entité nommée (il signifie *outside* (en dehors)) et nous avons ensuite deux labels pour chaque type d'entité (divers, personne, organisation et lieu). L'étiquette `B-XXX` indique que le *token* est au début d'une entité `XXX` et l'étiquette `I-XXX` indique que le *token* est à l'intérieur de l'entité `XXX`. Par exemple, dans l'exemple actuel, nous nous attendons à ce que notre modèle classe le *token* `S` comme `B-PER` (début d'une entité personne) et les *tokens* `##yl`, `##va` et `##in` comme `I-PER` (à l'intérieur d'une entité personne).
-Vous pourriez penser que le modèle s'est trompé dans ce cas, car il a attribué l'étiquette `I-PER` à ces quatre *tokens*, mais ce n'est pas tout à fait vrai. Il existe en fait deux formats pour ces étiquettes `B-` et `I-` : *IOB1* et *IOB2*. Le format IOB2 (en rose ci-dessous) est celui que nous avons introduit alors que dans le format IOB1 (en bleu), les étiquettes commençant par `B-` ne sont jamais utilisées que pour séparer deux entités adjacentes du même type. Le modèle que nous utilisons a été *finetuné* sur un jeu de données utilisant ce format, c'est pourquoi il attribue le label `I-PER` au *token* `S`.
+Vous pourriez penser que le modèle s'est trompé ici car il a attribué l'étiquette `I-PER` à ces quatre *tokens* mais ce n'est pas tout à fait vrai. Il existe en fait deux formats pour ces étiquettes `B-` et `I-` : *IOB1* et *IOB2*. Le format IOB2 (en rose ci-dessous) est celui que nous avons introduit alors que dans le format IOB1 (en bleu), les étiquettes commençant par `B-` ne sont jamais utilisées que pour séparer deux entités adjacentes du même type. Le modèle que nous utilisons a été *finetuné* sur un jeu de données utilisant ce format, c'est pourquoi il attribue le label `I-PER` au *token* `S`.
-Avec cette carte, nous sommes prêts à reproduire (presque entièrement) les résultats du premier pipeline. Nous pouvons simplement récupérer le score et le label de chaque *token* qui n'a pas été classé comme `O` :
+Nous sommes à présent prêts à reproduire (presque entièrement) les résultats du premier pipeline. Nous pouvons simplement récupérer le score et le label de chaque *token* qui n'a pas été classé comme `O` :
```py
results = []
@@ -341,7 +343,7 @@ print(results)
{'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn'}]
```
-C'est très similaire à ce que nous avions avant, à une exception près : le pipeline nous a aussi donné des informations sur le `début` et la `fin` de chaque entité dans la phrase originale. C'est là que notre mappage de décalage va entrer en jeu. Pour obtenir les décalages, il suffit de définir `return_offsets_mapping=True` lorsque nous appliquons le *tokenizer* à nos entrées :
+C'est très similaire à ce que nous avions avant, à une exception près : le pipeline nous a aussi donné des informations sur le `début` et la `fin` de chaque entité dans la phrase originale. C'est là que notre *offset mapping* va entrer en jeu. Pour obtenir les *offsets*, il suffit de définir `return_offsets_mapping=True` lorsque nous appliquons le *tokenizer* à nos entrées :
```py
inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
@@ -353,7 +355,7 @@ inputs_with_offsets["offset_mapping"]
(33, 35), (35, 40), (41, 45), (46, 48), (49, 57), (57, 58), (0, 0)]
```
-Chaque *tuple* est l'empan de texte correspondant à chaque token, où `(0, 0)` est réservé aux *tokens* spéciaux. Nous avons vu précédemment que le token à l'index 5 est `##yl`, qui a `(12, 14)` comme *offsets* ici. Si on prend la tranche correspondante dans notre exemple :
+Chaque *tuple* est l'étendue de texte correspondant à chaque *token* où `(0, 0)` est réservé aux *tokens* spéciaux. Nous avons vu précédemment que le *token* à l'index 5 est `##yl`, qui a `(12, 14)` comme *offsets* ici. Si on prend la tranche correspondante dans notre exemple :
```py
@@ -406,9 +408,9 @@ C'est la même chose que ce que nous avons obtenu avec le premier pipeline !
### Regroupement des entités
-L'utilisation des offsets pour déterminer les clés de début et de fin pour chaque entité est pratique mais cette information n'est pas strictement nécessaire. Cependant, lorsque nous voulons regrouper les entités, les offsets nous épargnent un batch de code compliqué. Par exemple, si nous voulions regrouper les *tokens* `Hu`, `##gging`, et `Face`, nous pourrions établir des règles spéciales disant que les deux premiers devraient être attachés tout en enlevant le `##`, et le `Face` devrait être ajouté avec un espace puisqu'il ne commence pas par `##` mais cela ne fonctionnerait que pour ce type particulier de *tokenizer*. Il faudrait écrire un autre ensemble de règles pour un *tokenizer* de type SentencePiece ou Byte-Pair-Encoding (voir plus loin dans ce chapitre).
+L'utilisation des *offsets* pour déterminer les clés de début et de fin pour chaque entité est pratique mais cette information n'est pas strictement nécessaire. Cependant, lorsque nous voulons regrouper les entités, les *offsets* nous épargnent un batch de code compliqué. Par exemple, si nous voulions regrouper les *tokens* `Hu`, `##gging`, et `Face`, nous pourrions établir des règles spéciales disant que les deux premiers devraient être attachés tout en enlevant le `##`, et le `Face` devrait être ajouté avec un espace puisqu'il ne commence pas par `##` mais cela ne fonctionnerait que pour ce type particulier de *tokenizer*. Il faudrait écrire un autre ensemble de règles pour un *tokenizer* de type SentencePiece ou *Byte-Pair-Encoding* (voir plus loin dans ce chapitre).
-Avec les *offsets*, tout ce code personnalisé disparaît : il suffit de prendre l'intervalle du texte original qui commence par le premier *token* et se termine par le dernier *token*. Ainsi, dans le cas des tokens `Hu`, `##gging`, et `Face`, nous devrions commencer au caractère 33 (le début de `Hu`) et finir avant le caractère 45 (la fin de `Face`) :
+Avec les *offsets*, tout ce code personnalisé disparaît : il suffit de prendre l'intervalle du texte original qui commence par le premier *token* et se termine par le dernier *token*. Ainsi, dans le cas des *tokens* `Hu`, `##gging`, et `Face`, nous devrions commencer au caractère 33 (le début de `Hu`) et finir avant le caractère 45 (la fin de `Face`) :
```py
example[33:45]
@@ -447,7 +449,7 @@ while idx < len(predictions):
_, end = offsets[idx]
idx += 1
- # Le score est la moyenne de tous les scores des tokens dans cette entité groupée.
+ # Le score est la moyenne de tous les scores des tokens dans cette entité groupée
score = np.mean(all_scores).item()
word = example[start:end]
results.append(
@@ -472,4 +474,4 @@ Et nous obtenons les mêmes résultats qu'avec notre deuxième pipeline !
{'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```
-Un autre exemple de tâche où ces décalages sont extrêmement utiles est la réponse aux questions. Plonger dans ce pipeline, ce que nous ferons dans la section suivante, nous permettra également de jeter un coup d'œil à une dernière caractéristique des *tokenizers* de la bibliothèque 🤗 *Transformers* : la gestion des tokens qui débordent lorsque nous tronquons une entrée à une longueur donnée.
+Un autre exemple de tâche où ces *offsets* sont extrêmement utiles est la réponse aux questions. Plonger dans ce pipeline, ce que nous ferons dans la section suivante, nous permettra de jeter un coup d'œil à une dernière caractéristique des *tokenizers* de la bibliothèque 🤗 *Transformers* : la gestion des *tokens* qui débordent lorsque nous tronquons une entrée à une longueur donnée.
diff --git a/chapters/fr/chapter6/3b.mdx b/chapters/fr/chapter6/3b.mdx
index e3c67b105..3e177a9fb 100644
--- a/chapters/fr/chapter6/3b.mdx
+++ b/chapters/fr/chapter6/3b.mdx
@@ -1,6 +1,6 @@
-# *Tokenizer* rapide dans le pipeline de QA
+# Tokenizer rapide dans le pipeline de QA
{#if fw === 'pt'}
@@ -22,7 +22,7 @@
{/if}
-Nous allons maintenant nous plonger dans le pipeline de `question-answering` et voir comment exploiter les *offsets* pour extraire du contexte la réponse à la question posée, un peu comme nous l'avons fait pour les entités groupées dans la section précédente. Nous verrons ensuite comment gérer les contextes très longs qui finissent par être tronqués. Vous pouvez sauter cette section si vous n'êtes pas intéressé par la tâche de réponse aux questions.
+Nous allons maintenant nous plonger dans le pipeline de `question-answering` et voir comment exploiter les *offsets* pour extraire d'u ncontexte la réponse à la question posée. Nous verrons ensuite comment gérer les contextes très longs qui finissent par être tronqués. Vous pouvez sauter cette section si vous n'êtes pas intéressé par la tâche de réponse aux questions.
{#if fw === 'pt'}
@@ -34,20 +34,24 @@ Nous allons maintenant nous plonger dans le pipeline de `question-answering` et
{/if}
-## Utilisation du pipeline de `question-answering`.
+## Utilisation du pipeline de `question-answering`
-Comme nous l'avons vu dans le [Chapitre 1](/course/fr/chapter1), nous pouvons utiliser le pipeline de `question-answering` comme ceci pour obtenir la réponse à une question :
+Comme nous l'avons vu dans le [chapitre 1](/course/fr/chapter1), nous pouvons utiliser le pipeline de `question-answering` comme ceci pour obtenir une réponse à une question :
```py
from transformers import pipeline
question_answerer = pipeline("question-answering")
context = """
-🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch, and TensorFlow — with a seamless integration
-between them. It's straightforward to train your models with one before loading them for inference with the other.
+🤗 Transformers is backed by the three most popular deep learning libraries
+ — Jax, PyTorch, and TensorFlow — with a seamless integration between them.
+It's straightforward to train your models with one before loading them for inference with the other.
"""
-# 🤗 Transformers s'appuie sur les trois bibliothèques d'apprentissage profond les plus populaires (Jax, PyTorch et TensorFlow) avec une intégration transparente entre elles. C'est simple d'entraîner vos modèles avec l'une avant de les charger pour l'inférence avec l'autre.
-question = "Which deep learning libraries back 🤗 Transformers?" # Quelles bibliothèques d'apprentissage profond derrière 🤗 Transformers ?
+# 🤗 Transformers s'appuie sur les trois bibliothèques d'apprentissage profond les plus populaires
+# (Jax, PyTorch et TensorFlow) avec une intégration transparente entre elles.
+# C'est simple d'entraîner vos modèles avec l'une avant de les charger pour l'inférence avec l'autre.
+question = "Which deep learning libraries back 🤗 Transformers?"
+# Quelles bibliothèques d'apprentissage profond derrière 🤗 Transformers ?
question_answerer(question=question, context=context)
```
@@ -102,8 +106,8 @@ between them. It's straightforward to train your models with one before loading
long_context - fr = """
🤗 Transformers : l'état de l'art du NLP
-🤗 Transformers fournit des milliers de modèles pré-entraînés pour effectuer des tâches sur des textes telles que la classification, l'extraction d'informations,
-la réponse à des questions, le résumé de textes, la traduction, la génération de texte et plus encore dans plus de 100 langues.
+🤗 Transformers fournit des milliers de modèles pré-entraînés pour effectuer des tâches sur des textes telles que la classification,
+l'extraction d'informations, la réponse à des questions, le résumé de textes, la traduction, la génération de texte et plus encore dans plus de 100 langues.
Son objectif est de rendre le traitement automatique des langues de pointe plus facile à utiliser pour tout le monde.
🤗 Transformers fournit des API permettant de télécharger et d'utiliser rapidement ces modèles pré-entraînés sur un texte donné, de les affiner sur vos propres ensembles de données et de les partager avec la communauté sur notre site Web.
@@ -150,7 +154,7 @@ Voyons comment il fait tout cela !
### Utilisation d'un modèle pour répondre à des questions
-Comme avec n'importe quel autre pipeline, nous commençons par tokeniser notre entrée et l'envoyons ensuite à travers le modèle. Le point de contrôle utilisé par défaut pour le pipeline de `question-answering` est [`distilbert-base-cased-distilled-squad`](https://huggingface.co/distilbert-base-cased-distilled-squad) (le « squad » dans le nom vient du jeu de données sur lequel le modèle a été *finetuné*, nous parlerons plus du jeu de données SQuAD dans le [Chapitre 7](/course/fr/chapter7/7)) :
+Comme avec n'importe quel autre pipeline, nous commençons par tokeniser notre entrée et l'envoyons ensuite à travers le modèle. Le *checkpoint* utilisé par défaut pour le pipeline de `question-answering` est [`distilbert-base-cased-distilled-squad`](https://huggingface.co/distilbert-base-cased-distilled-squad) (le « squad » dans le nom vient du jeu de données sur lequel le modèle a été *finetuné*, nous parlerons davantage de ce jeu de données dans le [chapitre 7](/course/fr/chapter7/7)) :
{#if fw === 'pt'}
@@ -209,7 +213,7 @@ torch.Size([1, 66]) torch.Size([1, 66])
{/if}
-Pour convertir ces logits en probabilités, nous allons appliquer une fonction softmax. Mais avant cela, nous devons nous assurer de masquer les indices qui ne font pas partie du contexte. Notre entrée est `[CLS] question [SEP] contexte [SEP]`, donc nous devons masquer les *tokens* de la question ainsi que le *token* `[SEP]`. Nous garderons cependant le *token* `[CLS]`, car certains modèles l'utilisent pour indiquer que la réponse n'est pas dans le contexte.
+Pour convertir ces logits en probabilités, nous allons appliquer une fonction softmax. Mais avant cela, nous devons nous assurer de masquer les indices qui ne font pas partie du contexte. Notre entrée est `[CLS] question [SEP] contexte [SEP]` donc nous devons masquer les *tokens* de la question ainsi que le *token* `[SEP]`. Nous garderons cependant le *token* `[CLS]` car certains modèles l'utilisent pour indiquer que la réponse n'est pas dans le contexte.
Puisque nous appliquerons une fonction softmax par la suite, il nous suffit de remplacer les logits que nous voulons masquer par un grand nombre négatif. Ici, nous utilisons `-10000` :
@@ -265,7 +269,7 @@ end_probabilities = tf.math.softmax(end_logits, axis=-1)[0].numpy()
{/if}
-A ce stade, nous pourrions prendre l'argmax des probabilités de début et de fin mais nous pourrions nous retrouver avec un indice de début supérieur à l'indice de fin, nous devons donc prendre quelques précautions supplémentaires. Nous allons calculer les probabilités de chaque `start_index` et `end_index` possible où `start_index<=end_index`, puis nous prendrons le tuple `(start_index, end_index)` avec la plus grande probabilité.
+A ce stade, nous pourrions prendre l'argmax des probabilités de début et de fin mais nous pourrions nous retrouver avec un indice de début supérieur à l'indice de fin. Nous devons donc prendre quelques précautions supplémentaires. Nous allons calculer les probabilités de chaque `start_index` et `end_index` possible où `start_index<=end_index`, puis nous prendrons le *tuple* `(start_index, end_index)` avec la plus grande probabilité.
En supposant que les événements « La réponse commence à `start_index` » et « La réponse se termine à `end_index` » sont indépendants, la probabilité que la réponse commence à `end_index` et se termine à `end_index` est :
@@ -297,7 +301,7 @@ scores = np.triu(scores)
{/if}
-Il ne nous reste plus qu'à obtenir l'indice du maximum. Puisque PyTorch retournera l'index dans le tenseur aplati, nous devons utiliser les opérations division plancher `//` et modulus `%` pour obtenir le `start_index` et le `end_index` :
+Il ne nous reste plus qu'à obtenir l'indice du maximum. Puisque PyTorch retourne l'index dans le tenseur aplati, nous devons utiliser les opérations division `//` et modulo `%` pour obtenir le `start_index` et le `end_index` :
```py
max_index = scores.argmax().item()
@@ -318,7 +322,7 @@ Nous n'avons pas encore tout à fait terminé, mais au moins nous avons déjà l
-Nous avons les `start_index` et `end_index` de la réponse en termes de *tokens*, donc maintenant nous devons juste convertir en indices de caractères dans le contexte. C'est là que les *offsets* seront super utiles. Nous pouvons les saisir et les utiliser comme nous l'avons fait dans la tâche de classification des *tokens* :
+Nous avons les `start_index` et `end_index` de la réponse en termes de *tokens*. Maintenant nous devons juste convertir en indices de caractères dans le contexte. C'est là que les *offsets* seront super utiles. Nous pouvons les saisir et les utiliser comme nous l'avons fait dans la tâche de classification des *tokens* :
```py
inputs_with_offsets = tokenizer(question, context, return_offsets_mapping=True)
@@ -358,7 +362,7 @@ Super ! C'est la même chose que dans notre premier exemple !
## Gestion des contextes longs
-Si nous essayons de tokeniser la question et le long contexte que nous avons utilisé comme exemple précédemment, nous obtiendrons un nombre de *tokens* supérieur à la longueur maximale utilisée dans le pipeline `question-answering` (qui est de 384) :
+Si nous essayons de tokeniser la question et le long contexte que nous avons utilisé dans l'exemple précédemment, nous obtenons un nombre de *tokens* supérieur à la longueur maximale utilisée dans le pipeline `question-answering` (qui est de 384) :
```py
inputs = tokenizer(question, long_context)
@@ -369,7 +373,7 @@ print(len(inputs["input_ids"]))
461
```
-Nous devrons donc tronquer nos entrées à cette longueur maximale. Il y a plusieurs façons de le faire, mais nous ne voulons pas tronquer la question, seulement le contexte. Puisque le contexte est la deuxième phrase, nous utiliserons la stratégie de troncature `"only_second"`. Le problème qui se pose alors est que la réponse à la question peut ne pas se trouver dans le contexte tronqué. Ici, par exemple, nous avons choisi une question dont la réponse se trouve vers la fin du contexte, et lorsque nous la tronquons, cette réponse n'est pas présente :
+Nous devrons donc tronquer nos entrées à cette longueur maximale. Il y a plusieurs façons de le faire mais nous ne voulons pas tronquer la question, seulement le contexte. Puisque le contexte est la deuxième phrase, nous utilisons la stratégie de troncature `"only_second"`. Le problème qui se pose alors est que la réponse à la question peut ne pas se trouver dans le contexte tronqué. Ici, par exemple, nous avons choisi une question dont la réponse se trouve vers la fin du contexte, et lorsque nous la tronquons, cette réponse n'est pas présente :
```py
inputs = tokenizer(question, long_context, max_length=384, truncation="only_second")
@@ -446,12 +450,13 @@ Pourquoi devrais-je utiliser des transformateurs ?
"""
```
-Cela signifie que le modèle aura du mal à trouver la bonne réponse. Pour résoudre ce problème, le pipeline de `question-answering` nous permet de diviser le contexte en morceaux plus petits, en spécifiant la longueur maximale. Pour s'assurer que nous ne divisons pas le contexte exactement au mauvais endroit pour permettre de trouver la réponse, il inclut également un certain chevauchement entre les morceaux.
+Cela signifie que le modèle a du mal à trouver la bonne réponse. Pour résoudre ce problème, le pipeline de `question-answering` nous permet de diviser le contexte en morceaux plus petits, en spécifiant la longueur maximale. Pour s'assurer que nous ne divisons pas le contexte exactement au mauvais endroit pour permettre de trouver la réponse, il inclut également un certain chevauchement entre les morceaux.
Nous pouvons demander au *tokenizer* (rapide ou lent) de le faire pour nous en ajoutant `return_overflowing_tokens=True`, et nous pouvons spécifier le chevauchement que nous voulons avec l'argument `stride`. Voici un exemple, en utilisant une phrase plus petite :
```py
-sentence = "This sentence is not too long but we are going to split it anyway." # "Cette phrase n'est pas trop longue mais nous allons la diviser quand même."
+sentence = "This sentence is not too long but we are going to split it anyway."
+# "Cette phrase n'est pas trop longue mais nous allons la diviser quand même."
inputs = tokenizer(
sentence, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
)
@@ -482,7 +487,7 @@ print(inputs.keys())
dict_keys(['input_ids', 'attention_mask', 'overflow_to_sample_mapping'])
```
-Comme prévu, nous obtenons les ID d'entrée et un masque d'attention. La dernière clé, `overflow_to_sample_mapping`, est une carte qui nous indique à quelle phrase correspond chacun des résultats -- ici nous avons 7 résultats qui proviennent tous de la (seule) phrase que nous avons passée au *tokenizer* :
+Comme prévu, nous obtenons les identifiants d'entrée et un masque d'attention. La dernière clé, `overflow_to_sample_mapping`, est une carte qui nous indique à quelle phrase correspond chacun des résultats. Ici nous avons 7 résultats qui proviennent tous de la (seule) phrase que nous avons passée au *tokenizer* :
```py
print(inputs["overflow_to_sample_mapping"])
@@ -492,12 +497,14 @@ print(inputs["overflow_to_sample_mapping"])
[0, 0, 0, 0, 0, 0, 0]
```
-C'est plus utile lorsque nous tokenisons plusieurs phrases ensemble. Par exemple, ceci :
+C'est plus utile lorsque nous tokenisons plusieurs phrases ensemble. Par exemple :
```py
sentences = [
- "This sentence is not too long but we are going to split it anyway.", # Cette phrase n'est pas trop longue mais nous allons la diviser quand même
- "This sentence is shorter but will still get split.", # Cette phrase est plus courte mais sera quand même divisée
+ "This sentence is not too long but we are going to split it anyway.",
+ # Cette phrase n'est pas trop longue mais nous allons la diviser quand même.
+ "This sentence is shorter but will still get split.",
+ # Cette phrase est plus courte mais sera quand même divisée.
]
inputs = tokenizer(
sentences, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
@@ -512,9 +519,9 @@ nous donne :
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]
```
-ce qui signifie que la première phrase est divisée en 7 morceaux comme précédemment, et que les 4 morceaux suivants proviennent de la deuxième phrase.
+ce qui signifie que la première phrase est divisée en 7 morceaux comme précédemment et que les 4 morceaux suivants proviennent de la deuxième phrase.
-Revenons maintenant à notre contexte long. Par défaut, le pipeline `question-answering` utilise une longueur maximale de 384, comme nous l'avons mentionné plus tôt, et un stride de 128, qui correspondent à la façon dont le modèle a été ajusté (vous pouvez ajuster ces paramètres en passant les arguments `max_seq_len` et `stride` lorsque vous appelez le pipeline). Nous utiliserons donc ces paramètres lors de la tokenisation. Nous ajouterons aussi du *padding* (pour avoir des échantillons de même longueur, afin de pouvoir construire des tenseurs) ainsi que demander les offsets :
+Revenons maintenant à notre contexte long. Par défaut, le pipeline `question-answering` utilise une longueur maximale de 384 et un *stride* de 128, qui correspondent à la façon dont le modèle a été *finetuné* (vous pouvez ajuster ces paramètres en passant les arguments `max_seq_len` et `stride` lorsque vous appelez le pipeline). Nous utiliserons donc ces paramètres lors de la tokenisation. Nous ajouterons aussi du *padding* (pour avoir des échantillons de même longueur afin de pouvoir construire des tenseurs) ainsi que demander les *offsets* :
```py
inputs = tokenizer(
@@ -529,7 +536,7 @@ inputs = tokenizer(
)
```
-Ces `inputs` contiendront les ID d'entrée et les masques d'attention que le modèle attend, ainsi que les offsets et le `overflow_to_sample_mapping` dont on vient de parler. Puisque ces deux éléments ne sont pas des paramètres utilisés par le modèle, nous allons les sortir des `inputs` (et nous ne stockerons pas la carte, puisqu'elle n'est pas utile ici) avant de le convertir en tenseur :
+Ces `inputs` contiendront les identifiants d'entrée, les masques d'attention que le modèle attend, ainsi que les *offsets* et le `overflow_to_sample_mapping` dont on vient de parler. Puisque ces deux éléments ne sont pas des paramètres utilisés par le modèle, nous allons les sortir des `inputs` (et nous ne stockerons pas la correspondance puisqu'elle n'est pas utile ici) avant de le convertir en tenseur :
{#if fw === 'pt'}
@@ -619,7 +626,7 @@ end_logits = tf.where(mask, -10000, end_logits)
{/if}
-Ensuite, nous pouvons utiliser lafonction softmax pour convertir nos logits en probabilités :
+Ensuite, nous pouvons utiliser la fonction softmax pour convertir nos logits en probabilités :
{#if fw === 'pt'}
@@ -637,7 +644,7 @@ end_probabilities = tf.math.softmax(end_logits, axis=-1).numpy()
{/if}
-L'étape suivante est similaire à ce que nous avons fait pour le petit contexte, mais nous la répétons pour chacun de nos deux chunks. Nous attribuons un score à tous les espaces de réponse possibles, puis nous prenons l'espace ayant le meilleur score :
+L'étape suivante est similaire à ce que nous avons fait pour le petit contexte mais nous la répétons pour chacun de nos deux morceaux. Nous attribuons un score à tous les espaces de réponse possibles puis nous prenons l'espace ayant le meilleur score :
{#if fw === 'pt'}
@@ -677,7 +684,7 @@ print(candidates)
[(0, 18, 0.33867), (173, 184, 0.97149)]
```
-Ces deux candidats correspondent aux meilleures réponses que le modèle a pu trouver dans chaque morceau. Le modèle est beaucoup plus confiant que la bonne réponse se trouve dans la deuxième partie (ce qui est bon signe !). Il ne nous reste plus qu'à faire correspondre ces deux espaces de *tokens* à des espaces de caractères dans le contexte (nous n'avons besoin de faire correspondre que le second pour avoir notre réponse, mais il est intéressant de voir ce que le modèle a choisi dans le premier morceau).
+Ces deux candidats correspondent aux meilleures réponses que le modèle a pu trouver dans chaque morceau. Le modèle est beaucoup plus confiant dans le fait que la bonne réponse se trouve dans la deuxième partie (ce qui est bon signe !). Il ne nous reste plus qu'à faire correspondre ces deux espaces de *tokens* à des espaces de caractères dans le contexte (nous n'avons besoin de faire correspondre que le second pour avoir notre réponse, mais il est intéressant de voir ce que le modèle a choisi dans le premier morceau).
@@ -685,7 +692,7 @@ Ces deux candidats correspondent aux meilleures réponses que le modèle a pu tr
-Le `offsets` que nous avons saisi plus tôt est en fait une liste d'*offsets*, avec une liste par morceau de texte :
+Le `offsets` que nous avons saisi plus tôt est en fait une liste d'*offsets* avec une liste par morceau de texte :
```py
for candidate, offset in zip(candidates, offsets):
@@ -702,11 +709,11 @@ for candidate, offset in zip(candidates, offsets):
{'answer': 'Jax, PyTorch and TensorFlow', 'start': 1892, 'end': 1919, 'score': 0.97149}
```
-Si nous ignorons le premier résultat, nous obtenons le même résultat que notre pipeline pour ce long contexte. Yay !
+Si nous ignorons le premier résultat, nous obtenons le même résultat que notre pipeline pour ce long contexte !
-✏️ **Essayez !** Utilisez les meilleurs scores que vous avez calculés auparavant pour montrer les cinq réponses les plus probables (pour l'ensemble du contexte, pas pour chaque morceau). Pour vérifier vos résultats, retournez au premier pipeline et passez `top_k=5` en l'appelant.
+✏️ **Essayez !** Utilisez les meilleurs scores que vous avez calculés auparavant pour montrer les cinq réponses les plus probables (pour l'ensemble du contexte, pas pour chaque morceau). Pour vérifier vos résultats, retournez au premier pipeline et spécifiez `top_k=5` en argument en l'appelant.
diff --git a/chapters/fr/chapter6/4.mdx b/chapters/fr/chapter6/4.mdx
index 314c2d536..0d8ddd4ba 100644
--- a/chapters/fr/chapter6/4.mdx
+++ b/chapters/fr/chapter6/4.mdx
@@ -1,4 +1,4 @@
-# Normalisation et pré-tokenization
+# Normalisation et prétokenization
-Avant de diviser un texte en sous-*tokens* (selon son modèle), le *tokenizer* effectue deux étapes : _normalisation_ et _pré-tokénisation_.
+Avant de diviser un texte en sous-*tokens* (selon le modèle), le *tokenizer* effectue deux étapes : la _normalisation_ et la _prétokénisation_.
## Normalisation
-L'étape de normalisation implique un nettoyage général, comme la suppression des espaces blancs inutiles, la mise en minuscules et/ou la suppression des accents. Si vous êtes familier avec la [normalisation Unicode](http://www.unicode.org/reports/tr15/) (comme NFC ou NFKC), c'est aussi quelque chose que le *tokenizer* peut appliquer.
+L'étape de normalisation implique un nettoyage général, comme la suppression des espaces inutiles, la mise en minuscules et/ou la suppression des accents. Si vous êtes familier avec la [normalisation Unicode](http://www.unicode.org/reports/tr15/) (comme NFC ou NFKC), c'est aussi quelque chose que le *tokenizer* peut appliquer.
Le `tokenizer` de 🤗 *Transformers* possède un attribut appelé `backend_tokenizer` qui donne accès au *tokenizer* sous-jacent de la bibliothèque 🤗 *Tokenizers* :
@@ -45,7 +45,7 @@ print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?
'hello how are u?'
```
-Dans cet exemple, puisque nous avons choisi le point de contrôle `bert-base-uncased`, la normalisation a appliqué la minuscule et supprimé les accents.
+Dans cet exemple, puisque nous avons choisi le *checkpoint* `bert-base-uncased`, la normalisation a mis le texte en minuscule et supprimé les accents.
@@ -53,13 +53,13 @@ Dans cet exemple, puisque nous avons choisi le point de contrôle `bert-base-unc
-## Pré-tokenization
+## Prétokenization
-Comme nous le verrons dans les sections suivantes, un *tokenizer* ne peut pas être entraîné uniquement sur du texte brut. Au lieu de cela, nous devons d'abord diviser les textes en petites entités, comme des mots. C'est là qu'intervient l'étape de pré-tokénisation. Comme nous l'avons vu dans le [Chapitre 2](/course/fr/chapter2), un *tokenizer* basé sur les mots peut simplement diviser un texte brut en mots sur les espaces et la ponctuation. Ces mots constitueront les limites des sous-*tokens* que le *tokenizer* peut apprendre pendant son entraînement.
+Comme nous le verrons dans les sections suivantes, un *tokenizer* ne peut pas être entraîné uniquement sur du texte brut. Au lieu de cela, nous devons d'abord diviser les textes en petites entités, comme des mots. C'est là qu'intervient l'étape de prétokénisation. Comme nous l'avons vu dans le [chapitre 2](/course/fr/chapter2), un *tokenizer* basé sur les mots peut simplement diviser un texte brut en mots sur les espaces et la ponctuation. Ces mots constitueront les limites des sous-*tokens* que le *tokenizer* peut apprendre pendant son entraînement.
-Pour voir comment un *tokenizer* rapide effectue la pré-tokénisation, nous pouvons utiliser la méthode `pre_tokenize_str()`de l'attribut `pre_tokenizer` de l'objet `tokenizer` :
+Pour voir comment un *tokenizer* rapide effectue la prétokénisation, nous pouvons utiliser la méthode `pre_tokenize_str()`de l'attribut `pre_tokenizer` de l'objet `tokenizer` :
```py
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
@@ -69,16 +69,16 @@ tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?
[('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))]
```
-Remarquez que le *tokenizer* garde déjà la trace des décalages, ce qui lui permet de nous donner la correspondance des décalages que nous avons utilisée dans la section précédente. Ici, le *tokenizer* ignore les deux espaces et les remplace par un seul, mais le décalage saute entre `are` et `you` pour en tenir compte.
+Remarquez que le *tokenizer* garde déjà la trace des *offsets*, ce qui lui permet de nous donner la correspondance des décalages que nous avons utilisée dans la section précédente. Ici, le *tokenizer* ignore les deux espaces et les remplace par un seul, mais le décalage saute entre `are` et `you` pour en tenir compte.
-Puisque nous utilisons un *tokenizer* de BERT, la pré-tokénisation implique la séparation des espaces et de la ponctuation. D'autres *tokenizers* peuvent avoir des règles différentes pour cette étape. Par exemple, si nous utilisons le *tokenizer* GPT-2 :
+Puisque nous utilisons le *tokenizer* de BERT, la prétokénisation implique la séparation des espaces et de la ponctuation. D'autres *tokenizers* peuvent avoir des règles différentes pour cette étape. Par exemple, si nous utilisons le *tokenizer* du GPT-2 :
```py
tokenizer = AutoTokenizer.from_pretrained("gpt2")
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
```
-cela séparera aussi sur les espaces et la ponctuation, mais il gardera les espaces et les remplacera par un symbole `Ġ`, ce qui lui permettra de récupérer les espaces originaux si nous décodons les *tokens* :
+Il séparera aussi sur les espaces et la ponctuation mais gardera les espaces et les remplacera par un symbole `Ġ`, ce qui lui permettra de récupérer les espaces originaux si nous décodons les *tokens* :
```python out
[('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)),
@@ -87,7 +87,7 @@ cela séparera aussi sur les espaces et la ponctuation, mais il gardera les espa
Notez également que, contrairement au *tokenizer* de BERT, ce *tokenizer* n'ignore pas les doubles espaces.
-Pour un dernier exemple, regardons le *tokenizer* T5, qui est basé sur l'algorithme SentencePiece :
+Pour un dernier exemple, regardons le *tokenizer* du 5, qui est basé sur l'algorithme SentencePiece :
```py
tokenizer = AutoTokenizer.from_pretrained("t5-small")
@@ -98,26 +98,26 @@ tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?
[('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))]
```
-Comme le *tokenizer* du GPT-2, celui-ci garde les espaces et les remplace par un token spécifique (`_`), mais le tokenizer T5 ne sépare que sur les espaces, pas sur la ponctuation. Notez également qu'il a ajouté un espace par défaut au début de la phrase (avant `Hello`) et a ignoré le double espace entre `are` et `you`.
+Comme le *tokenizer* du GPT-2, celui-ci garde les espaces et les remplace par un *token* spécifique (`_`), mais le *tokenizer* du T5 ne sépare que sur les espaces, pas sur la ponctuation. Notez également qu'il a ajouté un espace par défaut au début de la phrase (avant `Hello`) et il ignore le double espace entre `are` et `you`.
-Maintenant que nous avons vu un peu comment les différents *tokenizers* traitent le texte, nous pouvons commencer à explorer les algorithmes sous-jacents eux-mêmes. Nous commencerons par jeter un coup d'oeil rapide sur le très répandu SentencePiece ; puis, au cours des trois sections suivantes, nous examinerons le fonctionnement des trois principaux algorithmes utilisés pour la tokenisation en sous-mots.
+Maintenant que nous avons vu un peu comment les différents *tokenizers* traitent le texte, nous pouvons commencer à explorer les algorithmes sous-jacents eux-mêmes. Nous commencerons par jeter un coup d'oeil rapide sur le très répandu SentencePiece, puis au cours des trois sections suivantes nous examinerons le fonctionnement des trois principaux algorithmes utilisés pour la tokenisation en sous-mots.
## SentencePiece
-[SentencePiece](https://github.com/google/sentencepiece) est un algorithme de tokenization pour le prétraitement du texte que vous pouvez utiliser avec n'importe lequel des modèles que nous verrons dans les trois prochaines sections. Il considère le texte comme une séquence de caractères Unicode, et remplace les espaces par un caractère spécial, `▁`. Utilisé en conjonction avec l'algorithme Unigram (voir [section 7](/course/fr/chapter7/7)), il ne nécessite même pas d'étape de pré-tokénisation, ce qui est très utile pour les langues où le caractère espace n'est pas utilisé (comme le chinois ou le japonais).
+[SentencePiece](https://github.com/google/sentencepiece) est un algorithme de tokenisation pour le prétraitement du texte que vous pouvez utiliser avec n'importe lequel des modèles que nous verrons dans les trois prochaines sections. Il considère le texte comme une séquence de caractères Unicode et il remplace les espaces par un caractère spécial : `▁`. Utilisé en conjonction avec l'algorithme Unigram (voir la [section 7](/course/fr/chapter7/7)), il ne nécessite même pas d'étape de prétokénisation, ce qui est très utile pour les langues où le caractère espace n'est pas utilisé (comme le chinois ou le japonais).
-L'autre caractéristique principale de SentencePiece est le *tokenizer* réversible : comme il n'y a pas de traitement spécial des espaces, le décodage des *tokens* se fait simplement en les concaténant et en remplaçant les `_` par des espaces, ce qui donne le texte normalisé. Comme nous l'avons vu précédemment, le *tokenizer* de BERT supprime les espaces répétitifs, donc sa tokenisation n'est pas réversible.
+L'autre caractéristique principale de SentencePiece est le *tokenisation réversible* : comme il n'y a pas de traitement spécial des espaces, le décodage des *tokens* se fait simplement en les concaténant et en remplaçant les `_` par des espaces, ce qui donne le texte normalisé. Comme nous l'avons vu précédemment, le *tokenizer* de BERT supprime les espaces répétitifs, donc sa tokenisation n'est pas réversible.
-## Aperçu de l'algorithme
+## Vue d'ensemble des algorithmes
-Dans les sections suivantes, nous allons nous plonger dans les trois principaux algorithmes de tokenisation des sous-mots : BPE (utilisé par GPT-2 et autres), WordPiece (utilisé par exemple par BERT), et Unigram (utilisé par T5 et autres). Avant de commencer, voici un rapide aperçu du fonctionnement de chacun d'entre eux. N'hésitez pas à revenir à ce tableau après avoir lu chacune des sections suivantes si cela n'a pas encore de sens pour vous.
+Dans les sections suivantes, nous allons nous plonger dans les trois principaux algorithmes de tokenisation en sous-mots : BPE (utilisé par GPT-2 et autres), WordPiece (utilisé par exemple par BERT), et Unigram (utilisé par T5 et autres). Avant de commencer, voici un rapide aperçu du fonctionnement de chacun d'entre eux. N'hésitez pas à revenir à ce tableau après avoir lu chacune des sections suivantes si cela n'a pas encore de sens pour vous.
Modèle | BPE | WordPiece | Unigramme
:----:|:---:|:---------:|:------:
-Entraînement | Part d'un petit vocabulaire et apprend des règles pour fusionner les *tokens* | Part d'un petit vocabulaire et apprend des règles pour fusionner les *tokens* | Part d'un grand vocabulaire et apprend des règles pour supprimer les *tokens*.
-Étape d'Entraînement | Fusionne les *tokens* correspondant à la paire la plus commune | Fusionne les *tokens* correspondant à la paire ayant le meilleur score basé sur la fréquence de la paire, en privilégiant les paires où chaque *token* individuel est moins fréquent | Supprime tous les *tokens* du vocabulaire qui minimiseront la perte calculée sur le corpus entier
-Apprendre | Fusionner des règles et un vocabulaire | Juste un vocabulaire | Un vocabulaire avec un score pour chaque *token*
-Encodage | Découpe un mot en caractères et applique les fusions apprises pendant l'entraînement | Trouve le plus long sous-mot depuis le début qui est dans le vocabulaire, puis fait de même pour le reste du mot | Trouve la division la plus probable en tokens, en utilisant les scores appris pendant l'entraînement
+Entraînement | Part d'un petit vocabulaire et apprend des règles pour fusionner les *tokens* | Part d'un petit vocabulaire et apprend des règles pour fusionner les *tokens* | Part d'un grand vocabulaire et apprend des règles pour supprimer les *tokens*
+Étape d'entraînement | Fusionne les *tokens* correspondant à la paire la plus commune | Fusionne les *tokens* correspondant à la paire ayant le meilleur score basé sur la fréquence de la paire, en privilégiant les paires où chaque *token* individuel est moins fréquent | Supprime tous les *tokens* du vocabulaire qui minimiseront la perte calculée sur le corpus entier
+Apprend | A fusionner des règles et un vocabulaire | Juste un vocabulaire | Un vocabulaire avec un score pour chaque *token*
+Encodage | Découpe un mot en caractères et applique les fusions apprises pendant l'entraînement | Trouve le plus long sous-mot depuis le début qui est dans le vocabulaire puis fait de même pour le reste du mot | Trouve la division la plus probable en tokens, en utilisant les scores appris pendant l'entraînement
-Maintenant, plongeons dans BPE !
+Maintenant, plongeons dans le BPE !
diff --git a/chapters/fr/chapter6/5.mdx b/chapters/fr/chapter6/5.mdx
index 8f899884a..a20f96555 100644
--- a/chapters/fr/chapter6/5.mdx
+++ b/chapters/fr/chapter6/5.mdx
@@ -1,4 +1,4 @@
-# Tokénisation *Byte-Pair Encoding*
+# Tokénisation Byte-Pair Encoding
-*Byte-Pair Encoding* (BPE) a été initialement développé en tant qu'algorithme de compression de textes, puis utilisé par OpenAI pour la tokenisation lors du pré-entraînement du modèle GPT. Il est utilisé par de nombreux modèles Transformer, dont GPT, GPT-2, RoBERTa, BART et DeBERTa.
+Le *Byte-Pair Encoding* (BPE) a été initialement développé en tant qu'algorithme de compression de textes puis utilisé par OpenAI pour la tokenisation du pré-entraînement du modèle GPT. Il est utilisé par de nombreux *transformers* dont GPT, GPT-2, RoBERTa, BART et DeBERTa.
@@ -19,23 +19,23 @@
## Algorithme d'entraînement
-L'entraînement du BPE commence par le calcul de l'ensemble unique de mots utilisés dans le corpus (après les étapes de normalisation et de pré-tokénisation), puis la construction du vocabulaire en prenant tous les symboles utilisés pour écrire ces mots. A titre d'exemple très simple, disons que notre corpus utilise ces cinq mots :
+L'entraînement du BPE commence par le calcul de l'unique ensemble de mots utilisés dans le corpus (après les étapes de normalisation et de prétokénisation), puis la construction du vocabulaire en prenant tous les symboles utilisés pour écrire ces mots. A titre d'exemple, disons que notre corpus utilise ces cinq mots :
```
-"hug", "pug", "pun", "bun", "hugs" # "câlin", "carlin", "jeu de mots", "brioche", "câlins"...
+"hug", "pug", "pun", "bun", "hugs" # "câlin", "carlin", "jeu de mots", "brioche", "câlins"
```
-Le vocabulaire de base sera alors `["b", "g", "h", "n", "p", "s", "u"]`. Dans le monde réel, ce vocabulaire de base contiendra au moins tous les caractères ASCII, et probablement aussi quelques caractères Unicode. Si un exemple que vous tokenisez utilise un caractère qui n'est pas dans le corpus d'entraînement, ce caractère sera converti en *token* inconnu. C'est l'une des raisons pour lesquelles de nombreux modèles de NLP sont très mauvais dans l'analyse de contenus contenant des emojis, par exemple.
+Le vocabulaire de base sera alors `["b", "g", "h", "n", "p", "s", "u"]`. Dans le monde réel, le vocabulaire de base contient au moins tous les caractères ASCII et probablement aussi quelques caractères Unicode. Si un exemple que vous tokenisez utilise un caractère qui n'est pas dans le corpus d'entraînement, ce caractère est converti en *token* inconnu. C'est l'une des raisons pour lesquelles de nombreux modèles de NLP sont par exemple très mauvais dans l'analyse de contenus contenant des emojis.
-Les *tokenizer* de GPT-2 et de RoBERTa (qui sont assez similaires) ont une façon intelligente de gérer ce problème : ils ne considèrent pas les mots comme étant écrits avec des caractères Unicode, mais avec des octets. De cette façon, le vocabulaire de base a une petite taille (256), mais tous les caractères auxquels vous pouvez penser seront inclus et ne finiront pas par être convertis en un token inconnu. Cette astuce est appelée *byte-level BPE*.
+Les *tokenizers* du GPT-2 et de RoBERTa (qui sont assez similaires) ont une façon intelligente de gérer ce problème : ils ne considèrent pas les mots comme étant écrits avec des caractères Unicode mais avec des octets. De cette façon, le vocabulaire de base a une petite taille (256) et tous les caractères auxquels vous pouvez penser seront inclus dedans et ne finiront pas par être convertis en un *token* inconnu. Cette astuce est appelée *byte-level BPE*.
-Après avoir obtenu ce vocabulaire de base, nous ajoutons de nouveaux *tokens* jusqu'à ce que la taille souhaitée du vocabulaire soit atteinte en apprenant les *merges*, qui sont des règles permettant de fusionner deux éléments du vocabulaire existant pour en créer un nouveau. Ainsi, au début, ces fusions créeront des *tokens* de deux caractères, puis, au fur et à mesure de l'entraînement, des sous-mots plus longs.
+Après avoir obtenu ce vocabulaire de base, nous ajoutons de nouveaux *tokens* jusqu'à ce que la taille souhaitée du vocabulaire soit atteinte en apprenant les fusions qui sont des règles permettant de fusionner deux éléments du vocabulaire existant pour en créer un nouveau. Ainsi, au début, ces fusions créeront des *tokens* de deux caractères, puis au fur et à mesure de l'entraînement, des sous-mots plus longs.
-À chaque étape de l'entraînement du *tokenizer*, l'algorithme BPE recherche la paire la plus fréquente de *tokens* existants (par "paire", nous entendons ici deux *tokens* consécutifs dans un mot). Cette paire la plus fréquente est celle qui sera fusionnée, et nous rinçons et répétons pour l'étape suivante.
+À chaque étape de l'entraînement du *tokenizer*, l'algorithme BPE recherche la paire la plus fréquente de *tokens* existants (par « paire », nous entendons ici deux *tokens* consécutifs dans un mot). Cette paire la plus fréquente est celle qui sera fusionnée. Nous rinçons et répétons pour l'étape suivante.
Pour revenir à notre exemple précédent, supposons que les mots ont les fréquences suivantes :
@@ -43,29 +43,29 @@ Pour revenir à notre exemple précédent, supposons que les mots ont les fréqu
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```
-ce qui veut dire que `"hug"` était présent 10 fois dans le corpus, `"pug"` 5 fois, `"pun"` 12 fois, `"bun"` 4 fois, et `"hugs"`" 5 fois. Nous commençons l'entraînement en divisant chaque mot en caractères (ceux qui forment notre vocabulaire initial) afin de voir chaque mot comme une liste de *tokens* :
+ce qui veut dire que `"hug"` était présent 10 fois dans le corpus, `"pug"` 5 fois, `"pun"` 12 fois, `"bun"` 4 fois et `"hugs"`" 5 fois. Nous commençons l'entraînement en divisant chaque mot en caractères (ceux qui forment notre vocabulaire initial) afin de voir chaque mot comme une liste de *tokens* :
```
("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)
```
-Ensuite, nous regardons les paires. La paire `("h", "u")` est présente dans les mots `"hug"` et `"hugs"`, donc 15 fois au total dans le corpus. Ce n'est pas la paire la plus fréquente, cependant : cet honneur revient à `("u", "g")`, qui est présent dans `"hug"`, `"pug"`, et `"hugs"`, pour un grand total de 20 fois dans le vocabulaire.
+Ensuite, nous regardons les paires. La paire `("h", "u")` est présente dans les mots `"hug"` et `"hugs"`, donc 15 fois au total dans le corpus. Ce n'est cependant pas la paire la plus fréquente. Cet honneur revient à `("u", "g")` qui est présent dans `"hug"`, `"pug"`, et `"hugs"`, pour un total de 20 fois dans le vocabulaire.
-Ainsi, la première règle de fusion apprise par le *tokenizer* est `("u", "g") -> "ug"`, ce qui signifie que `"ug"` sera ajouté au vocabulaire, et que la paire devra être fusionnée dans tous les mots du corpus. A la fin de cette étape, le vocabulaire et le corpus ressemblent à ceci :
+Ainsi, la première règle de fusion apprise par le *tokenizer* est `("u", "g") -> "ug"`, ce qui signifie que `"ug"` est ajouté au vocabulaire et que la paire doit être fusionnée dans tous les mots du corpus. A la fin de cette étape, le vocabulaire et le corpus ressemblent à ceci :
```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug"]
Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)
```
-Nous avons maintenant quelques paires qui aboutissent à un token de plus de deux caractères : la paire `("h", "ug")`, par exemple (présente 15 fois dans le corpus). La paire la plus fréquente à ce stade est `("u", "n")`, cependant, présente 16 fois dans le corpus, donc la deuxième règle de fusion apprise est `("u", "n") -> "un"`. Ajouter cela au vocabulaire et fusionner toutes les occurrences existantes nous conduit à :
+Nous avons maintenant quelques paires qui aboutissent à un *token* de plus de deux caractères. Par exemple la paire `("h", "ug")` présente 15 fois dans le corpus. La paire la plus fréquente à ce stade est `("u", "n")`, présente 16 fois dans le corpus, donc la deuxième règle de fusion apprise est `("u", "n") -> "un"`. En ajoutant cela au vocabulaire et en fusionnant toutes les occurrences existantes, nous obtenons :
```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un"]
Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("h" "ug" "s", 5)
```
-Maintenant la paire la plus fréquente est `("h", "ug")`, donc nous apprenons la règle de fusion `("h", "ug") -> "hug"`, ce qui nous donne notre premier *token* de trois lettres. Après la fusion, le corpus ressemble à ceci :
+Maintenant la paire la plus fréquente est `("h", "ug")` donc nous apprenons la règle de fusion `("h", "ug") -> "hug"`. Cela nous donne donc notre premier *token* de trois lettres. Après la fusion, le corpus ressemble à ceci :
```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]
@@ -85,7 +85,7 @@ Et nous continuons ainsi jusqu'à ce que nous atteignions la taille de vocabulai
La tokenisation suit de près le processus d'entraînement, dans le sens où les nouvelles entrées sont tokenisées en appliquant les étapes suivantes :
1. Normalisation
-2. Pré-tokénisation.
+2. Prétokénisation
3. Découpage des mots en caractères individuels
4. Application des règles de fusion apprises dans l'ordre sur ces divisions.
@@ -97,30 +97,34 @@ Prenons l'exemple que nous avons utilisé pendant l'entraînement, avec les troi
("h", "ug") -> "hug"
```
-Le mot " bug " sera traduit par " ["b", "ug"]` ". Par contre, le mot " mug " sera traduit par " ["[UNK]", "ug"]` " puisque la lettre " m " ne fait pas partie du vocabulaire de base. De la même façon, le mot " thug "` sera tokenisé comme "["[UNK]", "hug"]` : la lettre `"t"` n'est pas dans le vocabulaire de base, et l'application des règles de fusion résulte d'abord en la fusion de `"u"` et `"g"` et ensuite en la fusion de `"hu"` et `"g"`.
+Le mot « bug » sera traduit par « ["b", "ug"] ». Par contre, le mot « mug » (tasse en français) sera traduit par « ["[UNK]", "ug"] » puisque la lettre « m » ne fait pas partie du vocabulaire de base. De la même façon, le mot « thug » (voyou en français) sera tokenisé en « ["[UNK]", "hug"] » car la lettre « t » n'est pas dans le vocabulaire de base et l'application des règles de fusion résulte d'abord en la fusion de « u » et « g » et ensuite en la fusion de « hu » et « g ».
-✏️ **A votre tour !** Comment pensez-vous que le mot "unhug"`` sera tokenized ?
+✏️ **A votre tour !** Comment pensez-vous que le mot « unhug » (détacher en français) sera tokenized ?
-## Mise en œuvre du BPE
+## Implémentation du BPE
-Voyons maintenant une implémentation de l'algorithme BPE. Il ne s'agira pas d'une version optimisée que vous pourrez utiliser sur un grand corpus ; nous voulons simplement vous montrer le code afin que vous puissiez comprendre un peu mieux l'algorithme.
+Voyons maintenant une implémentation de l'algorithme BPE. Il ne s'agira pas d'une version optimisée que vous pourrez utiliser sur un grand corpus. Nous voulons simplement vous montrer le code afin que vous puissiez comprendre un peu mieux l'algorithme.
Tout d'abord, nous avons besoin d'un corpus, alors créons un corpus simple avec quelques phrases :
```python
corpus = [
- "This is the Hugging Face course.", # C'est le cours d'Hugging Face.
- "This chapter is about tokenization.", # This chapter is about tokenization
- "This section shows several tokenizer algorithms.", # Cette section présente plusieurs algorithmes de *tokenizer*.
- "Hopefully, you will be able to understand how they are trained and generate tokens.", # Avec un peu de chance, vous serez en mesure de comprendre comment ils sont entraînés et génèrent des *tokens*.
+ "This is the Hugging Face Course.",
+ # C'est le cours d'Hugging Face.
+ "This chapter is about tokenization.",
+ # Ce chapitre traite de la tokenisation.
+ "This section shows several tokenizer algorithms.",
+ # Cette section présente plusieurs algorithmes de *tokenizer*.
+ "Hopefully, you will be able to understand how they are trained and generate tokens.",
+ # Avec un peu de chance, vous serez en mesure de comprendre comment ils sont entraînés et génèrent des *tokens*.
]
```
-Ensuite, nous devons pré-tokeniser ce corpus en mots. Puisque nous répliquons un *tokenizer* BPE (comme GPT-2), nous utiliserons le *tokenizer* `gpt2` pour la pré-tokénisation :
+Ensuite, nous devons prétokeniser ce corpus en mots. Puisque nous répliquons un *tokenizer* BPE (comme celui du GPT-2), nous utiliserons le *tokenizer* `gpt2` pour la prétokénisation :
```python
from transformers import AutoTokenizer
@@ -128,7 +132,7 @@ from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
```
-Ensuite, nous calculons les fréquences de chaque mot dans le corpus comme nous le faisons pour la pré-tokénisation :
+Ensuite, nous calculons les fréquences de chaque mot dans le corpus comme nous le faisons pour la prétokénisation :
```python
from collections import defaultdict
@@ -170,13 +174,13 @@ print(alphabet)
't', 'u', 'v', 'w', 'y', 'z', 'Ġ']
```
-Nous ajoutons également les *tokens* spéciaux utilisés par le modèle au début de ce vocabulaire. Dans le cas de GPT-2, le seul token spécial est `"<|endoftext|>"` :
+Nous ajoutons également les *tokens* spéciaux utilisés par le modèle au début de ce vocabulaire. Dans le cas du GPT-2, le seul *token* spécial est `"<|endoftext|>"` :
```python
vocab = ["<|endoftext|>"] + alphabet.copy()
```
-Nous devons maintenant diviser chaque mot en caractères individuels, pour pouvoir commencer l'entraînement :
+Nous devons maintenant diviser chaque mot en caractères individuels pour pouvoir commencer l'entraînement :
```python
splits = {word: [c for c in word] for word in word_freqs.keys()}
@@ -290,7 +294,7 @@ while len(vocab) < vocab_size:
vocab.append(best_pair[0] + best_pair[1])
```
-En conséquence, nous avons appris 19 règles de fusion (le vocabulaire initial avait une taille de 31 : 30 caractères dans l'alphabet, plus le *token* spécial) :
+En conséquence, nous avons appris 19 règles de fusion (le vocabulaire initial avait une taille de 31 : 30 caractères dans l'alphabet plus le *token* spécial) :
```py
print(merges)
@@ -321,7 +325,7 @@ print(vocab)
-Pour tokeniser un nouveau texte, on le pré-tokenise, on le divise, puis on applique toutes les règles de fusion apprises :
+Pour tokeniser un nouveau texte, on le prétokenise, on le divise, puis on applique toutes les règles de fusion apprises :
```python
def tokenize(text):
@@ -353,8 +357,8 @@ tokenize("This is not a token.")
-⚠️ Notre implémentation lancera une erreur s'il y a un caractère inconnu puisque nous n'avons rien fait pour les gérer. GPT-2 n'a pas réellement de jeton inconnu (il est impossible d'obtenir un caractère inconnu en utilisant le BPE au niveau de l'octet), mais cela pourrait arriver ici parce que nous n'avons pas inclus tous les octets possibles dans le vocabulaire initial. Cet aspect du BPE dépasse le cadre de cette section, nous avons donc laissé les détails de côté.
+⚠️ Notre implémentation lancera une erreur s'il y a un caractère inconnu puisque nous n'avons rien fait pour les gérer. GPT-2 n'a pas réellement de token inconnu (il est impossible d'obtenir un caractère inconnu en utilisant le BPE au niveau de l'octet) mais cela pourrait arriver ici car nous n'avons pas inclus tous les octets possibles dans le vocabulaire initial. Cet aspect du BPE dépasse le cadre de cette section, nous avons donc laissé ces détails de côté.
-C'est tout pour l'algorithme BPE ! Ensuite, nous allons nous intéresser à WordPiece.
\ No newline at end of file
+C'est tout pour l'algorithme BPE ! Nous allons nous intéresser à WordPiece dans la suite.
\ No newline at end of file
diff --git a/chapters/fr/chapter6/6.mdx b/chapters/fr/chapter6/6.mdx
index 701e4d7f8..115588c63 100644
--- a/chapters/fr/chapter6/6.mdx
+++ b/chapters/fr/chapter6/6.mdx
@@ -1,4 +1,4 @@
-# Tokénisation *WordPiece*
+# Tokénisation WordPiece
-*WordPiece* est l'algorithme de tokénisation développé par Google pour prétraîner BERT. Il a depuis été réutilisé dans un grand nombre de modèles de transformateurs basés sur BERT, tels que DistilBERT, MobileBERT, Funnel Transformers et MPNET. Il est très similaire à BPE en termes d'entraînement, mais la tokenisation réelle est effectuée différemment.
+*WordPiece* est l'algorithme de tokénisation développé par Google pour prétraîner BERT. Il a depuis été réutilisé dans un grand nombre de modèles de *transformers* basés sur BERT tels que DistilBERT, MobileBERT, Funnel Transformers et MPNET. Il est très similaire au BPE en termes d'entraînement mais la tokenisation réelle est effectuée différemment.
-💡 Cette section couvre le *WordPiece* en profondeur, allant jusqu'à montrer une implémentation complète. Vous pouvez passer directement à la fin si vous souhaitez simplement avoir un aperçu général de l'algorithme de tokénisation.
+💡 Cette section couvre le WordPiece en profondeur, allant jusqu'à montrer une implémentation complète. Vous pouvez passer directement à la fin si vous souhaitez simplement avoir un aperçu général de l'algorithme de tokénisation.
@@ -21,11 +21,11 @@
-⚠️ Google n'a jamais mis en ligne son implémentation de l'algorithme d'entraînement de *WordPiece*. Ce qui suit est donc notre meilleure estimation basée sur la littérature publiée. Il se peut qu'elle ne soit pas exacte à 100 %.
+⚠️ Google n'a jamais mis en ligne son implémentation de l'algorithme d'entraînement du WordPiece. Ce qui suit est donc notre meilleure estimation basée sur la littérature publiée. Il se peut qu'elle ne soit pas exacte à 100 %.
-Comme le BPE, *WordPiece* part d'un petit vocabulaire comprenant les *tokens* spéciaux utilisés par le modèle et l'alphabet initial. Puisqu'il identifie les sous-mots en ajoutant un préfixe (comme `##` pour BERT), chaque mot est initialement découpé en ajoutant ce préfixe à tous les caractères du mot. Ainsi, par exemple, `"mot"` est divisé comme ceci :
+Comme le BPE, *WordPiece* part d'un petit vocabulaire comprenant les *tokens* spéciaux utilisés par le modèle et l'alphabet initial. Puisqu'il identifie les sous-mots en ajoutant un préfixe (comme `##` pour BERT), chaque mot est initialement découpé en ajoutant ce préfixe à tous les caractères du mot. Ainsi par exemple, `"word"` est divisé comme ceci :
```
w ##o ##r ##d
@@ -33,11 +33,11 @@ w ##o ##r ##d
Ainsi, l'alphabet initial contient tous les caractères présents au début d'un mot et les caractères présents à l'intérieur d'un mot précédé du préfixe de *WordPiece*.
-Ensuite, toujours comme le BPE, *WordPiece* apprend des règles de fusion. La principale différence réside dans la manière dont la paire à fusionner est sélectionnée. Au lieu de sélectionner la paire la plus fréquente, *WordPiece* calcule un score pour chaque paire, en utilisant la formule suivante :
+Ensuite, toujours comme le BPE, *WordPiece* apprend des règles de fusion. La principale différence réside dans la manière dont la paire à fusionner est sélectionnée. Au lieu de sélectionner la paire la plus fréquente, *WordPiece* calcule un score pour chaque paire en utilisant la formule suivante :
$$\mathrm{score} = (\mathrm{freq\_of\_pair}) / (\mathrm{freq\_of\_first\_element} \times \mathrm{freq\_of\_second\_element})$$
-En divisant la fréquence de la paire par le produit des fréquences de chacune de ses parties, l'algorithme donne la priorité à la fusion des paires dont les parties individuelles sont moins fréquentes dans le vocabulaire. Par exemple, il ne fusionnera pas nécessairement `("un", "##able")` même si cette paire apparaît très fréquemment dans le vocabulaire, car les deux paires `"un"`" et `"##able"` apparaîtront probablement chacune dans un lot d'autres mots et auront une fréquence élevée. En revanche, une paire comme `("hu", "##gging")` sera probablement fusionnée plus rapidement (en supposant que le mot "hugging" apparaisse souvent dans le vocabulaire) puisque `"hu"` et `"##gging"` sont probablement moins fréquents individuellement.
+En divisant la fréquence de la paire par le produit des fréquences de chacune de ses parties, l'algorithme donne la priorité à la fusion des paires dont les parties individuelles sont moins fréquentes dans le vocabulaire. Par exemple, il ne fusionnera pas nécessairement `("un", "##able")` même si cette paire apparaît très fréquemment dans le vocabulaire car les deux paires `"un"`" et `"##able"` apparaîtront probablement chacune dans un batch d'autres mots et auront une fréquence élevée. En revanche, une paire comme `("hu", "##gging")` sera probablement fusionnée plus rapidement (en supposant que le mot `"hugging"` apparaisse souvent dans le vocabulaire) puisque `"hu"` et `"##gging"` sont probablement moins fréquents individuellement.
Examinons le même vocabulaire que celui utilisé dans l'exemple d'entraînement du BPE :
@@ -51,7 +51,7 @@ Les divisions ici seront :
("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5)
```
-Le vocabulaire initial sera donc `["b", "h", "p", "##g", "##n", "##s", "##u"]` (si on oublie les *tokens* spéciaux pour l'instant). La paire la plus fréquente est `("##u", "##g")` (présente 20 fois), mais la fréquence individuelle de `"##u"` est très élevée, donc son score n'est pas le plus élevé (il est de 1 / 36). Toutes les paires avec un `"##u"` ont en fait le même score (1 / 36), donc le meilleur score va à la paire `("##g", "##s")` -- la seule sans un `"##u"` -- à 1 / 20, et la première fusion apprise est `("##g", "##s") -> ("##gs")`.
+Si on oublie les *tokens* spéciaux pour l'instant, le vocabulaire initial sera donc `["b", "h", "p", "##g", "##n", "##s", "##u"]`. La paire la plus fréquente est `("##u", "##g")` (présente 20 fois), mais la fréquence individuelle de `"##u"` est très élevée, donc son score n'est pas le plus élevé (il est de 1 / 36). Toutes les paires avec un `"##u"` ont en fait le même score (1 / 36). Ainsi le meilleur score va à la paire `("##g", "##s")` qui est la seule sans un `"##u"` avec un score 1 / 20. Et la première fusion apprise est `("##g", "##s") -> ("##gs")`.
Notez que lorsque nous fusionnons, nous enlevons le `##` entre les deux *tokens*, donc nous ajoutons `"##gs"` au vocabulaire et appliquons la fusion dans les mots du corpus :
@@ -67,7 +67,7 @@ Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu"]
Corpus: ("hu" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
```
-Ensuite, le meilleur score suivant est partagé par `("hu", "##g")` et `("hu", "##gs")` (avec 1/15, comparé à 1/21 pour toutes les autres paires), donc la première paire avec le plus grand score est fusionnée :
+Ensuite, le meilleur score suivant est partagé par `("hu", "##g")` et `("hu", "##gs")` (avec 1/15, comparé à 1/21 pour toutes les autres paires). Ainsi la première paire avec le plus grand score est fusionnée :
```
Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]
@@ -84,13 +84,13 @@ et nous continuons ainsi jusqu'à ce que nous atteignions la taille de vocabulai
## Algorithme de tokenisation
-La tokénisation diffère dans *WordPiece* et BPE en ce que *WordPiece* ne sauvegarde que le vocabulaire final, pas les règles de fusion apprises. En partant du mot à tokeniser, *WordPiece* trouve le sous-mot le plus long qui se trouve dans le vocabulaire, puis se sépare sur celui-ci. Par exemple, si nous utilisons le vocabulaire appris dans l'exemple ci-dessus, pour le mot `"hugs"` le plus long sous-mot en partant du début qui est dans le vocabulaire est `"hug"`, donc nous le divisons et obtenons `["hug", "##s"]`. On continue avec `"##s"`, qui est dans le vocabulaire, donc la tokenisation de `"hugs"` est `["hug", "##s"]`.
+La tokénisation diffère dans *WordPiece* et BPE en ce que *WordPiece* ne sauvegarde que le vocabulaire final et non pas les règles de fusion apprises. En partant du mot à tokeniser, *WordPiece* trouve le sous-mot le plus long qui se trouve dans le vocabulaire, puis se sépare sur celui-ci. Par exemple, si nous utilisons le vocabulaire appris dans l'exemple ci-dessus, pour le mot `"hugs"` le plus long sous-mot en partant du début qui est dans le vocabulaire est `"hug"`. Donc nous le divisons et obtenons `["hug", "##s"]`. On continue avec `"##s"`, qui est dans le vocabulaire, donc la tokenisation de `"hugs"` est `["hug", "##s"]`.
Avec BPE, nous aurions appliqué les fusions apprises dans l'ordre et la tokénisation aurait été `["hu", "##gs"]`, l'encodage est donc différent.
-Comme autre exemple, voyons comment le mot `"bugs"` serait tokenisé. `"b"` est le plus long sous-mot commençant au début du mot qui est dans le vocabulaire, donc on le divise et on obtient `["b", "##ugs"]`. Ensuite, `"##u"` est le plus long sous-mot commençant au début de `"##ugs"` qui est dans le vocabulaire, donc on le sépare et on obtient `["b", "##u, "##gs"]`. Enfin, `"##gs"` est dans le vocabulaire, donc cette dernière liste est la tokenization de `"bugs"`.
+Comme autre exemple, voyons comment le mot `"bugs"` serait tokenisé. `"b"` est le plus long sous-mot commençant au début du mot qui est dans le vocabulaire donc on le divise et on obtient `["b", "##ugs"]`. Ensuite, `"##u"` est le plus long sous-mot commençant au début de `"##ugs"` qui est dans le vocabulaire, donc on le sépare et on obtient `["b", "##u, "##gs"]`. Enfin, `"##gs"` est dans le vocabulaire, donc cette dernière liste est la tokenization de `"bugs"`.
-Lorsque la tokenisation arrive à un stade où il n'est pas possible de trouver un sous-mot dans le vocabulaire, le mot entier est tokenisé comme inconnu -- donc, par exemple, `"mug"` serait tokenisé comme `["[UNK]"]`, tout comme " bum " (même si on peut commencer par " b " et " ##u ", " ##m " ne fait pas partie du vocabulaire, et le *tokenizer* résultant sera simplement `["[UNK]"]` ", et non `["b", "##u", "[UNK]"]` "). C'est une autre différence avec BPE, qui classerait seulement les caractères individuels qui ne sont pas dans le vocabulaire comme inconnus.
+Lorsque la tokenisation arrive à un stade où il n'est pas possible de trouver un sous-mot dans le vocabulaire, le mot entier est tokenisé comme inconnu. Par exemple, `"mug"` serait tokenisé comme `["[UNK]"]`, tout comme `"bum"` (même si on peut commencer par " b " et " ##u ", " ##m " ne fait pas partie du vocabulaire, et le *tokenizer* résultant sera simplement `["[UNK]"]` " et non `["b", "##u", "[UNK]"]` "). C'est une autre différence avec le BPE qui classerait seulement les caractères individuels qui ne sont pas dans le vocabulaire comme inconnus.
@@ -98,22 +98,26 @@ Lorsque la tokenisation arrive à un stade où il n'est pas possible de trouver
-## Mise en œuvre de *WordPiece*
+## Implémentation de WordPiece
-Voyons maintenant une implémentation de l'algorithme *WordPiece*. Comme pour le BPE, il s'agit d'un exemple pédagogique, et vous ne pourrez pas l'utiliser sur un grand corpus.
+Voyons maintenant une implémentation de l'algorithme *WordPiece*. Comme pour le BPE, il s'agit d'un exemple pédagogique et vous ne pourrez pas l'utiliser sur un grand corpus.
Nous utiliserons le même corpus que dans l'exemple BPE :
```python
corpus = [
- "This is the Hugging Face course.", # C'est le cours d'Hugging Face.
- "This chapter is about tokenization.", # This chapter is about tokenization
- "This section shows several tokenizer algorithms.", # Cette section présente plusieurs algorithmes de *tokenizer*.
- "Hopefully, you will be able to understand how they are trained and generate tokens.", # Avec un peu de chance, vous serez en mesure de comprendre comment ils sont entraînés et génèrent des *tokens*.
+ "This is the Hugging Face Course.",
+ # C'est le cours d'Hugging Face.
+ "This chapter is about tokenization.",
+ # This chapter is about tokenization
+ "This section shows several tokenizer algorithms.",
+ # Cette section présente plusieurs algorithmes de *tokenizer*.
+ "Hopefully, you will be able to understand how they are trained and generate tokens.",
+ # Avec un peu de chance, vous serez en mesure de comprendre comment ils sont entraînés et génèrent des *tokens*.
]
```
-Tout d'abord, nous devons pré-tokéniser le corpus en mots. Puisque nous répliquons un *tokenizer WordPiece* (comme BERT), nous utiliserons le *tokenizer* `bert-base-cased` pour la pré-tokénisation :
+Tout d'abord, nous devons prétokéniser le corpus en mots. Puisque nous répliquons un *tokenizer WordPiece* (comme BERT), nous utiliserons le *tokenizer* `bert-base-cased` pour la prétokénisation :
```python
from transformers import AutoTokenizer
@@ -121,7 +125,7 @@ from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
```
-Ensuite, nous calculons les fréquences de chaque mot dans le corpus comme nous le faisons pour la pré-tokénisation :
+Ensuite, nous calculons les fréquences de chaque mot dans le corpus comme nous le faisons pour la prétokénisation :
```python
from collections import defaultdict
@@ -144,7 +148,7 @@ defaultdict(
'trained': 1, 'and': 1, 'generate': 1, 'tokens': 1})
```
-Comme nous l'avons vu précédemment, l'alphabet est l'ensemble unique composé de toutes les premières lettres des mots, et de toutes les autres lettres qui apparaissent dans les mots préfixés par `##` :
+Comme nous l'avons vu précédemment, l'alphabet est l'unique ensemble composé de toutes les premières lettres des mots, et de toutes les autres lettres qui apparaissent dans les mots préfixés par `##` :
```python
alphabet = []
@@ -167,7 +171,7 @@ print(alphabet)
'w', 'y']
```
-Nous ajoutons également les tokens spéciaux utilisés par le modèle au début de ce vocabulaire. Dans le cas de BERT, il s'agit de la liste `["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]` :
+Nous ajoutons également les *tokens* spéciaux utilisés par le modèle au début de ce vocabulaire. Dans le cas de BERT, il s'agit de la liste `["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]` :
```python
vocab = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + alphabet.copy()
@@ -242,7 +246,7 @@ print(best_pair, max_score)
('a', '##b') 0.2
```
-Ainsi, la première fusion à apprendre est `('a', '##b') -> 'ab'`, et nous ajoutons `'ab'` au vocabulaire :
+Ainsi, la première fusion à apprendre est `('a', '##b') -> 'ab'` et nous ajoutons `'ab'` au vocabulaire :
```python
vocab.append("ab")
@@ -316,11 +320,11 @@ Comme nous pouvons le voir, comparé à BPE, ce *tokenizer* apprend les parties
-💡 Utiliser `train_new_from_iterator()` sur le même corpus ne donnera pas exactement le même vocabulaire. C'est parce que la bibliothèque 🤗 *Tokenizers* n'implémente pas *WordPiece* pour l'entraînement (puisque nous ne sommes pas complètement sûrs de ses internes), mais utilise le BPE à la place.
+💡 Utiliser `train_new_from_iterator()` sur le même corpus ne donnera pas exactement le même vocabulaire. C'est parce que la bibliothèque 🤗 *Tokenizers* n'implémente pas *WordPiece* pour l'entraînement (puisque nous ne sommes pas complètement sûrs de son fonctionnement interne), mais utilise le BPE à la place.
-Pour tokeniser un nouveau texte, on le pré-tokenise, on le divise, puis on applique l'algorithme de tokenisation sur chaque mot. En d'autres termes, nous recherchons le plus grand sous-mot commençant au début du premier mot et le divisons, puis nous répétons le processus sur la deuxième partie, et ainsi de suite pour le reste de ce mot et les mots suivants dans le texte :
+Pour tokeniser un nouveau texte, on le prétokenise, on le divise, puis on applique l'algorithme de tokenisation sur chaque mot. En d'autres termes, nous recherchons le plus grand sous-mot commençant au début du premier mot et le divisons. Puis nous répétons le processus sur la deuxième partie et ainsi de suite pour le reste de ce mot et les mots suivants dans le texte :
```python
def encode_word(word):
@@ -363,7 +367,7 @@ def tokenize(text):
On peut l'essayer sur n'importe quel texte :
```python
-tokenize("This is the Hugging Face course!") # C'est le cours d'Hugging Face
+tokenize("This is the Hugging Face Course!") # C'est le cours d'Hugging Face
```
```python out
diff --git a/chapters/fr/chapter6/7.mdx b/chapters/fr/chapter6/7.mdx
index ba32b85e7..bf5c970dc 100644
--- a/chapters/fr/chapter6/7.mdx
+++ b/chapters/fr/chapter6/7.mdx
@@ -1,4 +1,4 @@
-# Tokenisation *Unigram*
+# Tokenisation Unigram
-The Unigram algorithm is often used in SentencePiece, which is the tokenization algorithm used by models like AlBERT, T5, mBART, Big Bird, and XLNet.
+L'algorithme *Unigram* est souvent utilisé dans *SentencePiece*, qui est l'algorithme de tokenization utilisé par des modèles comme ALBERT, T5, mBART, Big Bird et XLNet.
@@ -19,20 +19,20 @@ The Unigram algorithm is often used in SentencePiece, which is the tokenization
## Algorithme d'entraînement
-Comparé à BPE et *WordPiece*, *Unigram* fonctionne dans l'autre sens : il part d'un grand vocabulaire et enlève des *tokens* jusqu'à atteindre la taille de vocabulaire désirée. Il existe plusieurs options pour construire ce vocabulaire de base : nous pouvons prendre les sous-chaînes les plus courantes dans les mots pré-tokénisés, par exemple, ou appliquer BPE sur le corpus initial avec une grande taille de vocabulaire.
+Comparé au BPE et *WordPiece*, *Unigram* fonctionne dans l'autre sens : il part d'un grand vocabulaire et enlève des *tokens* jusqu'à atteindre la taille de vocabulaire désirée. Il existe plusieurs options pour construire ce vocabulaire de base. Nous pouvons par exemple prendre les sous-chaînes les plus courantes dans les mots prétokénisés ou appliquer le BPE sur le corpus initial avec une grande taille de vocabulaire.
-À chaque étape de l'entraînement, l'algorithme *Unigram* calcule une perte sur le corpus compte tenu du vocabulaire actuel. Ensuite, pour chaque symbole du vocabulaire, l'algorithme calcule de combien la perte globale augmenterait si le symbole était supprimé, et recherche les symboles qui l'augmenteraient le moins. Ces symboles ont un effet moindre sur la perte globale du corpus, ils sont donc en quelque sorte "moins nécessaires" et sont les meilleurs candidats à la suppression.
+À chaque étape de l'entraînement, l'algorithme *Unigram* calcule une perte sur le corpus compte tenu du vocabulaire actuel. Ensuite, pour chaque symbole du vocabulaire, l'algorithme calcule de combien la perte globale augmenterait si le symbole était supprimé et recherche les symboles qui l'augmenteraient le moins. Ces symboles ont un effet moindre sur la perte globale du corpus, ils sont donc en quelque sorte « moins nécessaires » et sont les meilleurs candidats à la suppression.
-Comme il s'agit d'une opération très coûteuse, nous ne nous contentons pas de supprimer le symbole unique associé à la plus faible augmentation de la perte, mais le \\(p\\) (\(p\\) étant un hyperparamètre que vous pouvez contrôler, généralement 10 ou 20) pour cent des symboles associés à la plus faible augmentation de la perte. Ce processus est ensuite répété jusqu'à ce que le vocabulaire ait atteint la taille souhaitée.
+Comme il s'agit d'une opération très coûteuse, nous ne nous contentons pas de supprimer le symbole unique associé à la plus faible augmentation de la perte mais le \\(p\\) pourcent des symboles associés à la plus faible augmentation de la perte. \(p\\) est un hyperparamètre que vous pouvez contrôler, valant généralement 10 ou 20. Ce processus est ensuite répété jusqu'à ce que le vocabulaire ait atteint la taille souhaitée.
Notez que nous ne supprimons jamais les caractères de base, afin de nous assurer que tout mot peut être tokenisé.
-Maintenant, c'est encore un peu vague : la partie principale de l'algorithme est de calculer une perte sur le corpus et de voir comment elle change lorsque nous supprimons certains *tokens* du vocabulaire, mais nous n'avons pas encore expliqué comment le faire. Cette étape repose sur l'algorithme de tokénisation d'un modèle *Unigram*, nous allons donc l'aborder maintenant.
+Tout ceci peut paraître encore un peu vague. En effet, la partie principale de l'algorithme est de calculer une perte sur le corpus et de voir comment elle change lorsque nous supprimons certains *tokens* du vocabulaire mais nous n'avons pas encore expliqué comment le faire. Cette étape repose sur l'algorithme de tokénisation *Unigram*, nous allons donc l'aborder à présent.
Nous allons réutiliser le corpus des exemples précédents :
```
-("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5) # "câlin", "carlin", "jeu de mots", "brioche", "câlins"...
```
et pour cet exemple, nous prendrons toutes les sous-chaînes strictes pour le vocabulaire initial :
@@ -45,7 +45,7 @@ et pour cet exemple, nous prendrons toutes les sous-chaînes strictes pour le vo
Un modèle *Unigram* est un type de modèle de langage qui considère que chaque *token* est indépendant des *tokens* qui le précèdent. Il s'agit du modèle de langage le plus simple, dans le sens où la probabilité du *token* X compte tenu du contexte précédent est simplement la probabilité du *token* X. Ainsi, si nous utilisions un modèle de langage *Unigram* pour générer du texte, nous prédirions toujours le *token* le plus courant.
-La probabilité d'un *token* donné est sa fréquence (le nombre de fois que nous le trouvons) dans le corpus original, divisée par la somme de toutes les fréquences de tous les *tokens* dans le vocabulaire (pour s'assurer que la somme des probabilités est égale à 1). Par exemple, `"ug"` est présent dans `"hug"`, `"pug"`, et `"hugs"`, il a donc une fréquence de 20 dans notre corpus.
+La probabilité d'un *token* donné est sa fréquence (le nombre de fois que nous le trouvons) dans le corpus original, divisée par la somme de toutes les fréquences de tous les *tokens* dans le vocabulaire (pour s'assurer que la somme des probabilités est égale à 1). Par exemple, `"ug"` est présent dans `"hug"`, `"pug"`, et `"hugs"`. Il a donc une fréquence de 20 dans notre corpus.
Voici les fréquences de tous les sous-mots possibles dans le vocabulaire :
@@ -54,11 +54,11 @@ Voici les fréquences de tous les sous-mots possibles dans le vocabulaire :
("un", 16) ("b", 4) ("bu", 4) ("s", 5) ("hug", 15) ("gs", 5) ("ugs", 5)
```
-Ainsi, la somme de toutes les fréquences est de 210, et la probabilité du sous-mot `"ug"` est donc de 20/210.
+Ainsi, la somme de toutes les fréquences est de 210 et la probabilité du sous-mot `"ug"` est donc de 20/210.
-✏️ **A votre tour !** Ecrivez le code pour calculer les fréquences ci-dessus et vérifiez que les résultats affichés sont corrects, ainsi que la somme totale.
+✏️ **A votre tour !** Ecrivez le code permettant de calculer les fréquences ci-dessus et vérifiez que les résultats affichés sont corrects, de même que la somme totale.
@@ -82,9 +82,9 @@ La tokenisation d'un mot avec le modèle *Unigram* est donc la tokenisation avec
Ainsi, `"pug"` sera tokenisé comme `["p", "ug"]` ou `["pu", "g"]`, selon la segmentation rencontrée en premier (notez que dans un corpus plus large, les cas d'égalité comme celui-ci seront rares).
-Dans ce cas, il était facile de trouver toutes les segmentations possibles et de calculer leurs probabilités, mais en général, ce sera un peu plus difficile. Il existe un algorithme classique utilisé pour cela, appelé *algorithme de Viterbi*. Essentiellement, on peut construire un graphe pour détecter les segmentations possibles d'un mot donné en disant qu'il existe une branche du caractère _a_ au caractère _b_ si le sous-mot de _a_ à _b_ est dans le vocabulaire, et attribuer à cette branche la probabilité du sous-mot.
+Dans ce cas-ci, cela a été facile de trouver toutes les segmentations possibles et de calculer leurs probabilités, mais en général ce sera un peu plus difficile. Il existe un algorithme classique utilisé pour cela, appelé *algorithme de Viterbi*. Essentiellement, on peut construire un graphe pour détecter les segmentations possibles d'un mot donné en disant qu'il existe une branche du caractère _a_ au caractère _b_ si le sous-mot de _a_ à _b_ est dans le vocabulaire, et attribuer à cette branche la probabilité du sous-mot.
-Pour trouver le chemin dans ce graphe qui va avoir le meilleur score, l'algorithme de Viterbi détermine, pour chaque position dans le mot, la segmentation avec le meilleur score qui se termine à cette position. Puisque nous allons du début à la fin, ce meilleur score peut être trouvé en parcourant en boucle tous les sous-mots se terminant à la position actuelle, puis en utilisant le meilleur score de tokenization de la position à laquelle ce sous-mot commence. Ensuite, il suffit de dérouler le chemin emprunté pour arriver à la fin.
+Pour trouver le chemin qui va avoir le meilleur score dans ce graphe, l'algorithme de Viterbi détermine, pour chaque position dans le mot, la segmentation avec le meilleur score qui se termine à cette position. Puisque nous allons du début à la fin, ce meilleur score peut être trouvé en parcourant en boucle tous les sous-mots se terminant à la position actuelle, puis en utilisant le meilleur score de tokenization de la position à laquelle ce sous-mot commence. Ensuite, il suffit de dérouler le chemin emprunté pour arriver à la fin.
Prenons un exemple en utilisant notre vocabulaire et le mot `"unhug"`. Pour chaque position, les sous-mots avec les meilleurs scores se terminant là sont les suivants :
@@ -108,7 +108,7 @@ Ainsi, `"unhug"` serait tokenisé comme `["un", "hug"]`.
Maintenant que nous avons vu comment fonctionne la tokenisation, nous pouvons nous plonger un peu plus profondément dans la perte utilisée pendant l'entraînement. À n'importe quelle étape, cette perte est calculée en tokenisant chaque mot du corpus, en utilisant le vocabulaire courant et le modèle *Unigram* déterminé par les fréquences de chaque *token* dans le corpus (comme vu précédemment).
-Chaque mot du corpus a un score, et la perte est le logarithme négatif de ces scores : c'est-à-dire la somme pour tous les mots du corpus de tous les `-log(P(word))`.
+Chaque mot du corpus a un score, et la perte est le négatif du logarithme de ces scores, c'est-à-dire la somme pour tous les mots du corpus de tous les `-log(P(word))`.
Revenons à notre exemple avec le corpus suivant :
@@ -132,7 +132,7 @@ Donc la perte est :
10 * (-log(0.071428)) + 5 * (-log(0.007710)) + 12 * (-log(0.006168)) + 4 * (-log(0.001451)) + 5 * (-log(0.001701)) = 169.8
```
-Maintenant, nous devons calculer comment la suppression de chaque token affecte la perte. C'est plutôt fastidieux, donc nous allons le faire pour deux *tokens* ici et garder tout le processus pour quand nous aurons du code pour nous aider. Dans ce cas (très) particulier, nous avions deux tokenizations équivalentes de tous les mots : comme nous l'avons vu précédemment, par exemple, `"pug"` pourrait être tokenisé `["p", "ug"]` avec le même score. Ainsi, enlever le token `"pu"` du vocabulaire donnera exactement la même perte.
+Maintenant, nous devons calculer comment la suppression de chaque token affecte la perte. C'est plutôt fastidieux, donc nous allons le faire pour deux *tokens* ici et garder tout le processus pour quand nous aurons du code pour nous aider. Dans ce cas (très) particulier, nous avions deux tokenizations équivalentes de tous les mots. Par exmeple, comme nous l'avons vu précédemment, `"pug"` pourrait être tokenisé en `["p", "ug"]` avec le même score. Ainsi, enlever le token `"pu"` du vocabulaire donnera exactement la même perte.
D'un autre côté, supprimer le mot `"hug"` aggravera la perte, car la tokenisation de `"hug"` et `"hugs"` deviendra :
@@ -149,18 +149,22 @@ Ces changements entraîneront une augmentation de la perte de :
Par conséquent, le token `"pu"` sera probablement retiré du vocabulaire, mais pas `"hug"`.
-## Implémentation d'*Unigram*
+## Implémentation d'Unigram
-Maintenant, implémentons tout ce que nous avons vu jusqu'à présent dans le code. Comme pour BPE et *WordPiece*, ce n'est pas une implémentation efficace de l'algorithme *Unigram* (bien au contraire), mais cela devrait vous aider à le comprendre un peu mieux.
+Maintenant, implémentons tout ce que nous avons vu jusqu'à présent dans le code. Comme pour le BPE et *WordPiece*, ce n'est pas une implémentation efficace de l'algorithme *Unigram* (bien au contraire), mais elle devrait vous aider à le comprendre un peu mieux.
Nous allons utiliser le même corpus que précédemment comme exemple :
```python
corpus = [
- "This is the Hugging Face course.", # C'est le cours d'Hugging Face.
- "This chapter is about tokenization.", # This chapter is about tokenization
- "This section shows several tokenizer algorithms.", # Cette section présente plusieurs algorithmes de *tokenizer*.
- "Hopefully, you will be able to understand how they are trained and generate tokens.", # Avec un peu de chance, vous serez en mesure de comprendre comment ils sont entraînés et génèrent des *tokens*.
+ "This is the Hugging Face Course.",
+ # C'est le cours d'Hugging Face.
+ "This chapter is about tokenization.",
+ # Ce chapitre traite de la tokenisation.
+ "This section shows several tokenizer algorithms.",
+ # Cette section présente plusieurs algorithmes de *tokenizer*.
+ "Hopefully, you will be able to understand how they are trained and generate tokens.",
+ # Avec un peu de chance, vous serez en mesure de comprendre comment ils sont entraînés et génèrent des *tokens*.
]
```
@@ -172,7 +176,7 @@ from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("xlnet-base-cased")
```
-Comme pour BPE et *WordPiece*, nous commençons par compter le nombre d'occurrences de chaque mot dans le corpus :
+Comme pour le BPE et *WordPiece*, nous commençons par compter le nombre d'occurrences de chaque mot dans le corpus :
```python
from collections import defaultdict
@@ -187,7 +191,7 @@ for text in corpus:
word_freqs
```
-Ensuite, nous devons initialiser notre vocabulaire à quelque chose de plus grand que la taille du vocabulaire que nous voudrons à la fin. Nous devons inclure tous les caractères de base (sinon nous ne serons pas en mesure de tokeniser chaque mot), mais pour les sous-chaînes plus grandes, nous ne garderons que les plus communs, donc nous les trions par fréquence :
+Ensuite, nous devons initialiser notre vocabulaire à une taille plus grande que celle du vocabulaire que nous voudrons à la fin. Nous devons inclure tous les caractères de base (sinon nous ne serons pas en mesure de tokeniser chaque mot), mais pour les sous-chaînes plus grandes, nous ne garderons que les plus communs. AInsi nous les trions par fréquence :
```python
char_freqs = defaultdict(int)
@@ -195,11 +199,11 @@ subwords_freqs = defaultdict(int)
for word, freq in word_freqs.items():
for i in range(len(word)):
char_freqs[word[i]] += freq
- # Loop through the subwords of length at least 2
+ # Boucle à travers les sous-mots de longueur au moins égale à 2
for j in range(i + 2, len(word) + 1):
subwords_freqs[word[i:j]] += freq
-# Sort subwords by frequency
+# Trier les sous-mots par fréquence
sorted_subwords = sorted(subwords_freqs.items(), key=lambda x: x[1], reverse=True)
sorted_subwords[:10]
```
@@ -221,7 +225,7 @@ token_freqs = {token: freq for token, freq in token_freqs}
-Ensuite, nous calculons la somme de toutes les fréquences, pour convertir les fréquences en probabilités. Pour notre modèle, nous allons stocker les logarithmes des probabilités, car il est plus stable numériquement d'additionner des logarithmes que de multiplier des petits nombres, et cela simplifiera le calcul de la perte du modèle :
+Ensuite, nous calculons la somme de toutes les fréquences, pour convertir les fréquences en probabilités. Pour notre modèle, nous allons stocker les logarithmes des probabilités, car c'est plus stable numériquement d'additionner des logarithmes que de multiplier des petits nombres. Cela simplifiera aussi le calcul de la perte du modèle :
```python
from math import log
@@ -230,9 +234,9 @@ total_sum = sum([freq for token, freq in token_freqs.items()])
model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
```
-Maintenant la fonction principale est celle qui tokenise les mots en utilisant l'algorithme de Viterbi. Comme nous l'avons vu précédemment, cet algorithme calcule la meilleure segmentation de chaque sous-chaîne du mot, que nous allons stocker dans une variable nommée `best_segmentations`. Nous allons stocker un dictionnaire par position dans le mot (de 0 à sa longueur totale), avec deux clés : l'index du début du dernier *token* dans la meilleure segmentation, et le score de la meilleure segmentation. Avec l'index du début du dernier *token*, nous serons en mesure de récupérer la segmentation complète une fois que la liste est complètement remplie.
+Maintenant la fonction principale est celle qui tokenise les mots en utilisant l'algorithme de Viterbi. Comme nous l'avons vu précédemment, cet algorithme calcule la meilleure segmentation de chaque sous-chaîne du mot que nous allons stocker dans une variable nommée `best_segmentations`. Nous allons stocker un dictionnaire par position dans le mot (de 0 à sa longueur totale), avec deux clés : l'index du début du dernier *token* dans la meilleure segmentation et le score de la meilleure segmentation. Avec l'index du début du dernier *token*, nous serons en mesure de récupérer la segmentation complète une fois que la liste est complètement remplie.
-Le remplissage de la liste se fait à l'aide de deux boucles seulement : la boucle principale passe en revue chaque position de départ, et la seconde boucle essaie toutes les sous-chaînes commençant à cette position de départ. Si la sous-chaîne est dans le vocabulaire, nous avons une nouvelle segmentation du mot jusqu'à cette position finale, que nous comparons à ce qui est dans `best_segmentations`.
+Le remplissage de la liste se fait à l'aide de deux boucles seulement : la boucle principale passe en revue chaque position de départ et la seconde boucle essaie toutes les sous-chaînes commençant à cette position de départ. Si la sous-chaîne est dans le vocabulaire, nous avons une nouvelle segmentation du mot jusqu'à cette position finale que nous comparons à ce qui est dans `best_segmentations`.
Une fois que la boucle principale est terminée, nous commençons juste à la fin et sautons d'une position de départ à une autre, en enregistrant les *tokens* au fur et à mesure, jusqu'à ce que nous atteignions le début du mot :
@@ -242,13 +246,13 @@ def encode_word(word, model):
{"start": None, "score": None} for _ in range(len(word))
]
for start_idx in range(len(word)):
- # This should be properly filled by the previous steps of the loop
+ # Doit être correctement rempli par les étapes précédentes de la boucle
best_score_at_start = best_segmentations[start_idx]["score"]
for end_idx in range(start_idx + 1, len(word) + 1):
token = word[start_idx:end_idx]
if token in model and best_score_at_start is not None:
score = model[token] + best_score_at_start
- # If we have found a better segmentation ending at end_idx, we update
+ # Si nous avons trouvé une meilleure segmentation se terminant à end_idx, nous mettons à jour
if (
best_segmentations[end_idx]["score"] is None
or best_segmentations[end_idx]["score"] > score
@@ -257,7 +261,7 @@ def encode_word(word, model):
segmentation = best_segmentations[-1]
if segmentation["score"] is None:
- # We did not find a tokenization of the word -> unknown
+ # Nous n'avons pas trouvé de tokenization du mot -> inconnu ()
return [""], None
score = segmentation["score"]
@@ -306,7 +310,7 @@ compute_loss(model)
413.10377642940875
```
-Le calcul des scores pour chaque *token* n'est pas très difficile non plus ; il suffit de calculer la perte pour les modèles obtenus en supprimant chaque *token* :
+Le calcul des scores pour chaque *token* n'est pas très difficile non plus. Il suffit de calculer la perte pour les modèles obtenus en supprimant chaque *token* :
```python
import copy
@@ -316,7 +320,7 @@ def compute_scores(model):
scores = {}
model_loss = compute_loss(model)
for token, score in model.items():
- # We always keep tokens of length 1
+ # Nous gardons toujours les tokens de longueur 1.
if len(token) == 1:
continue
model_without_token = copy.deepcopy(model)
@@ -342,7 +346,7 @@ Puisque `"ll"` est utilisé dans la tokenisation de `"Hopefully"`, et que le sup
-💡 Cette approche est très inefficace, c'est pourquoi *SentencePiece* utilise une approximation de la perte du modèle sans le *token* X : au lieu de partir de zéro, il remplace simplement le *token* X par sa segmentation dans le vocabulaire restant. De cette façon, tous les scores peuvent être calculés en une seule fois, en même temps que la perte du modèle.
+💡 Cette approche est très inefficace, c'est pourquoi *SentencePiece* utilise une approximation de la perte du modèle sans le *token* X. Au lieu de partir de zéro, il remplace simplement le *token* X par sa segmentation dans le vocabulaire restant. De cette façon, tous les scores peuvent être calculés en une seule fois, en même temps que la perte du modèle.
@@ -353,7 +357,7 @@ percent_to_remove = 0.1
while len(model) > 100:
scores = compute_scores(model)
sorted_scores = sorted(scores.items(), key=lambda x: x[1])
- # Remove percent_to_remove tokens with the lowest scores.
+ # Supprime les tokens percent_to_remove ayant les scores les plus bas
for i in range(int(len(model) * percent_to_remove)):
_ = token_freqs.pop(sorted_scores[i][0])
@@ -361,7 +365,7 @@ while len(model) > 100:
model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
```
-Ensuite, pour tokeniser un texte, il suffit d'appliquer la pré-tokénisation et d'utiliser la fonction `encode_word()` :
+Ensuite, pour tokeniser un texte, il suffit d'appliquer la prétokénisation et d'utiliser la fonction `encode_word()` :
```python
def tokenize(text, model):
@@ -378,4 +382,4 @@ tokenize("This is the Hugging Face course.", model)
['▁This', '▁is', '▁the', '▁Hugging', '▁Face', '▁', 'c', 'ou', 'r', 's', 'e', '.']
```
-C'est tout pour *Unigram* ! Avec un peu de chance, vous vous sentez maintenant comme un expert en tout ce qui concerne les *tokenizers*. Dans la prochaine section, nous allons nous plonger dans les blocs de construction de la bibliothèque 🤗 *Tokenizers*, et vous montrer comment vous pouvez les utiliser pour construire votre propre *tokenizer*.
+C'est tout pour *Unigram* ! Avec un peu de chance, vous vous sentez à présent être un expert des *tokenizers*. Dans la prochaine section, nous allons nous plonger dans les blocs de construction de la bibliothèque 🤗 *Tokenizers* et allons vous montrer comment vous pouvez les utiliser pour construire votre propre *tokenizer*.
diff --git a/chapters/fr/chapter6/8.mdx b/chapters/fr/chapter6/8.mdx
index 8c8af3b5b..46440deb7 100644
--- a/chapters/fr/chapter6/8.mdx
+++ b/chapters/fr/chapter6/8.mdx
@@ -1,566 +1,566 @@
-# Construction d'un *tokenizer*, bloc par bloc
-
-
-
-Comme nous l'avons vu dans les sections précédentes, la tokenisation comprend plusieurs étapes :
-
-- normalisation (tout nettoyage du texte jugé nécessaire, comme la suppression des espaces ou des accents, la normalisation Unicode, etc.)
-- pré-tokénisation (division de l'entrée en mots)
-- passage de l'entrée dans le modèle (utilisation des mots prétokénisés pour produire une séquence de *tokens*)
-- post-traitement (ajout des tokens spéciaux du *tokenizer*, génération du masque d'attention et des identifiants du type de *token*).
-
-Pour mémoire, voici un autre aperçu du processus global :
-
-
-
-
-
-
-La bibliothèque 🤗 *Tokenizers* a été construite pour fournir plusieurs options pour chacune de ces étapes, que vous pouvez mélanger et assortir ensemble. Dans cette section, nous verrons comment nous pouvons construire un *tokenizer* à partir de zéro, par opposition à entraîner un nouveau *tokenizer* à partir d'un ancien, comme nous l'avons fait dans [section 2](/course/fr/chapter6/2). Vous serez alors en mesure de construire n'importe quel type de *tokenizer* auquel vous pouvez penser !
-
-
-
-Plus précisément, la bibliothèque est construite autour d'une classe centrale `Tokenizer` avec les blocs de construction regroupés en sous-modules :
-
-- `normalizers` contient tous les types de `Normalizer` que vous pouvez utiliser (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.normalizers)),
-- `pre_tokenizers` contient tous les types de `PreTokenizer` que vous pouvez utiliser (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.pre_tokenizers)),
-- `models` contient les différents types de `Model` que vous pouvez utiliser, comme `BPE`, `WordPiece`, et `Unigram` (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.models)),
-- `trainers` contient tous les différents types de `Trainer` que vous pouvez utiliser pour entraîner votre modèle sur un corpus (un par type de modèle ; liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.trainers)),
-- `post_processors` contient les différents types de `PostProcessor` que vous pouvez utiliser (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.processors)),
-- `decoders` contient les différents types de `Decoder` que vous pouvez utiliser pour décoder les sorties de tokenization (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/components.html#decoders)).
-
-Vous pouvez trouver la liste complète des blocs de construction [ici](https://huggingface.co/docs/tokenizers/python/latest/components.html).
-
-## Acquisition d'un corpus
-
-Pour entraîner notre nouveau *tokenizer*, nous utiliserons un petit corpus de texte (pour que les exemples soient rapides). Les étapes pour acquérir le corpus sont similaires à celles que nous avons suivies au [début de ce chapitre](/course/fr/chapter6/2), mais cette fois nous utiliserons le jeu de données [WikiText-2](https://huggingface.co/datasets/wikitext) :
-
-
-```python
-from datasets import load_dataset
-
-dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train")
-
-
-def get_training_corpus():
- for i in range(0, len(dataset), 1000):
- yield dataset[i : i + 1000]["text"]
-```
-
-La fonction `get_training_corpus()` est un générateur qui donnera des batchs de 1 000 textes, que nous utiliserons pour entraîner le *tokenizer*.
-
-🤗 *Tokenizers* peuvent aussi être entraînés directement sur des fichiers texte. Voici comment nous pouvons générer un fichier texte contenant tous les textes/entrées de WikiText-2 que nous pouvons utiliser localement :
-
-```python
-with open("wikitext-2.txt", "w", encoding="utf-8") as f:
- for i in range(len(dataset)):
- f.write(dataset[i]["text"] + "\n")
-```
-
-Ensuite, nous vous montrerons comment construire vos propres *tokenizers* BERT, GPT-2 et XLNet, bloc par bloc. Cela nous donnera un exemple de chacun des trois principaux algorithmes de tokenisation : *WordPiece*, BPE et *Unigram*. Commençons par BERT !
-
-## Construire un tokenizer *WordPiece* à partir de zéro
-
-Pour construire un *tokenizer* avec la bibliothèque 🤗 *Tokenizers*, nous commençons par instancier un objet `Tokenizer` avec un `model`, puis nous définissons ses attributs `normalizer`, `pre_tokenizer`, `post_processor`, et `decoder` aux valeurs que nous voulons.
-
-Pour cet exemple, nous allons créer un `Tokenizer` avec un modèle *WordPiece* :
-
-```python
-from tokenizers import (
- decoders,
- models,
- normalizers,
- pre_tokenizers,
- processors,
- trainers,
- Tokenizer,
-)
-
-tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))
-```
-
-Nous devons spécifier le `unk_token` pour que le modèle sache quoi retourner lorsqu'il rencontre des caractères qu'il n'a pas vu auparavant. D'autres arguments que nous pouvons définir ici incluent le `vocab` de notre modèle (nous allons entraîner le modèle, donc nous n'avons pas besoin de le définir) et `max_input_chars_per_word`, qui spécifie une longueur maximale pour chaque mot (les mots plus longs que la valeur passée seront séparés).
-
-La première étape de la tokénisation est la normalisation, donc commençons par cela. Puisque BERT est largement utilisé, il y a un `BertNormalizer` avec les options classiques que nous pouvons définir pour BERT : `lowercase` et `strip_accents`, qui sont auto-explicatifs ; `clean_text` pour enlever tous les caractères de contrôle et remplacer les espaces répétés par un seul ; et `handle_chinese_chars`, qui place des espaces autour des caractères chinois. Pour reproduire le *tokenizer* `bert-base-uncased`, nous pouvons simplement définir ce *normalizer* :
-
-```python
-tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True)
-```
-
-En général, cependant, lorsque vous construisez un nouveau *tokenizer*, vous n'aurez pas accès à un normalisateur aussi pratique déjà implémenté dans la bibliothèque 🤗 *Tokenizers*. Donc voyons comment créer le normalisateur BERT manuellement. La bibliothèque fournit un normaliseur `Lowercase` et un normaliseur `StripAccents`, et vous pouvez composer plusieurs normaliseurs en utilisant une `Sequence` :
-
-```python
-tokenizer.normalizer = normalizers.Sequence(
- [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()]
-)
-```
-
-Nous utilisons également un normaliseur Unicode `NFD`, car sinon le normalisateur `StripAccents` ne reconnaîtra pas correctement les caractères accentués et ne les supprimera donc pas.
-
-Comme nous l'avons vu précédemment, nous pouvons utiliser la méthode `normalize_str()` du `normalizer` pour vérifier les effets qu'il a sur un texte donné :
-
-```python
-print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
-```
-
-```python out
-hello how are u?
-```
-
-
-
-**Pour aller plus loin** Si vous testez les deux versions des normalisateurs précédents sur une chaîne contenant le caractère unicode `u"\u0085"` vous remarquerez sûrement que ces deux normalisateurs ne sont pas exactement équivalents.
-Pour ne pas trop compliquer la version avec `normalizers.Sequence`, nous n'avons pas inclus les remplacements Regex que le `BertNormalizer` requiert quand l'argument `clean_text` est mis à `True` ce qui est le comportement par défaut. Mais ne vous inquiétez pas : il est possible d'obtenir exactement la même normalisation sans utiliser le très pratique `BertNormalizer` en ajoutant deux `normalizers.Replace` à la séquence de normalisation.
-
-
-
-L'étape suivante est la pré-tokenalisation. Encore une fois, il y a un `BertPreTokenizer` préconstruit que nous pouvons utiliser :
-
-```python
-tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer()
-```
-
-Ou nous pouvons le construire à partir de zéro :
-
-```python
-tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
-```
-
-Notez que le pré-tokenizer `Whitespace` divise sur les espaces et tous les caractères qui ne sont pas des lettres, des chiffres ou le caractère de soulignement, donc techniquement il divise sur les espaces et la ponctuation :
-
-```python
-tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
-```
-
-```python out
-[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
- ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
-```
-
-Si vous voulez seulement séparer sur les espaces, vous devriez utiliser le pré-tokenizer `WhitespaceSplit` à la place :
-
-```python
-pre_tokenizer = pre_tokenizers.WhitespaceSplit()
-pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
-```
-
-```python out
-[("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))]
-```
-
-Comme pour les normaliseurs, vous pouvez utiliser une `Sequence` pour composer plusieurs pré-tokenizers :
-
-```python
-pre_tokenizer = pre_tokenizers.Sequence(
- [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()]
-)
-pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
-```
-
-```python out
-[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
- ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
-```
-
-L'étape suivante dans le pipeline de tokénisation est de faire passer les entrées par le modèle. Nous avons déjà spécifié notre modèle dans l'initialisation, mais nous devons encore l'entraîner, ce qui nécessitera un `WordPieceTrainer`. La principale chose à retenir lors de l'instanciation d'un entraîneur dans 🤗 *Tokenizers* est que vous devez lui passer tous les *tokens* spéciaux que vous avez l'intention d'utiliser. Sinon il ne les ajoutera pas au vocabulaire, puisqu'ils ne sont pas dans le corpus d'entraînement :
-
-```python
-special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"]
-trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens)
-```
-
-En plus de spécifier la `vocab_size` et les `special_tokens`, nous pouvons définir la `min_frequency` (le nombre de fois qu'un *token* doit apparaître pour être inclus dans le vocabulaire) ou changer le `continuing_subword_prefix` (si nous voulons utiliser quelque chose de différent de `##`).
-
-Pour entraîner notre modèle en utilisant l'itérateur que nous avons défini plus tôt, il suffit d'exécuter cette commande :
-
-```python
-tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
-```
-
-Nous pouvons également utiliser des fichiers texte pour entraîner notre *tokenizer*, qui ressemblerait à ceci (nous réinitialisons le modèle avec un `WordPiece` vide au préalable) :
-
-```python
-tokenizer.model = models.WordPiece(unk_token="[UNK]")
-tokenizer.train(["wikitext-2.txt"], trainer=trainer)
-```
-
-Dans les deux cas, nous pouvons ensuite tester le *tokenizer* sur un texte en appelant la méthode `encode()` :
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer.")
-print(encoding.tokens)
-```
-
-```python out
-['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.']
-```
-
-Le `encodage` obtenu est un `Encoding`, qui contient toutes les sorties nécessaires du *tokenizer* dans ses différents attributs : `ids`, `type_ids`, `tokens`, `offsets`, `attention_mask`, `special_tokens_mask`, et `overflowing`.
-
-La dernière étape du pipeline de tokénisation est le post-traitement. Nous devons ajouter le *token* `[CLS]` au début et le *token* `[SEP]` à la fin (ou après chaque phrase, si nous avons une paire de phrases). Nous utiliserons un `TemplateProcessor` pour cela, mais d'abord nous devons connaître les ID des *tokens* `[CLS]` et `[SEP]` dans le vocabulaire :
-
-```python
-cls_token_id = tokenizer.token_to_id("[CLS]")
-sep_token_id = tokenizer.token_to_id("[SEP]")
-print(cls_token_id, sep_token_id)
-```
-
-```python out
-(2, 3)
-```
-
-Pour écrire le modèle pour le `TemplateProcessor`, nous devons spécifier comment traiter une seule phrase et une paire de phrases. Pour les deux, nous écrivons les *tokens* spéciaux que nous voulons utiliser ; la première (ou unique) phrase est représentée par `$A`, alors que la deuxième phrase (si on code une paire) est représentée par `$B`. Pour chacun de ces éléments (*tokens* spéciaux et phrases), nous spécifions également l'ID du type de *token* correspondant après un deux-points.
-
-Le *template* classique de BERT est donc défini comme suit :
-
-```python
-tokenizer.post_processor = processors.TemplateProcessing(
- single=f"[CLS]:0 $A:0 [SEP]:0",
- pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1",
- special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)],
-)
-```
-
-Notez que nous devons transmettre les ID des jetons spéciaux, afin que le *tokenizer* puisse les convertir correctement en leurs ID.
-
-Une fois que cela est ajouté, revenir à notre exemple précédent donnera :
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer.")
-print(encoding.tokens)
-```
-
-```python out
-['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]']
-```
-
-Et sur une paire de phrases, on obtient le bon résultat :
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.")
-print(encoding.tokens)
-print(encoding.type_ids)
-```
-
-```python out
-['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]']
-[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
-```
-
-Nous avons presque fini de construire ce *tokenizer* à partir de zéro. La dernière étape consiste à inclure un décodeur :
-
-```python
-tokenizer.decoder = decoders.WordPiece(prefix="##")
-```
-
-Testons-le sur notre précédent `encoding` :
-
-```python
-tokenizer.decode(encoding.ids)
-```
-
-```python out
-"let's test this tokenizer... on a pair of sentences." # Testons ce tokenizer... sur une paire de phrases.
-```
-
-Génial ! Nous pouvons enregistrer notre *tokenizer* dans un seul fichier JSON comme ceci :
-
-```python
-tokenizer.save("tokenizer.json")
-```
-
-Nous pouvons alors recharger ce fichier dans un objet `Tokenizer` avec la méthode `from_file()` :
-
-```python
-new_tokenizer = Tokenizer.from_file("tokenizer.json")
-```
-
-Pour utiliser ce *tokenizer* dans 🤗 *Transformers*, nous devons l'envelopper dans un `PreTrainedTokenizerFast`. Nous pouvons soit utiliser la classe générique, soit, si notre *tokenizer* correspond à un modèle existant, utiliser cette classe (ici, `BertTokenizerFast`). Si vous appliquez cette leçon pour construire un tout nouveau *tokenizer*, vous devrez utiliser la première option.
-
-Pour envelopper le *tokenizer* dans un `PreTrainedTokenizerFast`, nous pouvons soit passer le *tokenizer*que nous avons construit comme un `tokenizer_object`, soit passer le fichier de *tokenizer* que nous avons sauvegardé comme `tokenizer_file`. Ce qu'il faut retenir, c'est que nous devons définir manuellement tous les *tokens* spéciaux, car cette classe ne peut pas déduire de l'objet `tokenizer` quel *token* est le *token* de masque, le *token*`[CLS]`, etc :
-
-```python
-from transformers import PreTrainedTokenizerFast
-
-wrapped_tokenizer = PreTrainedTokenizerFast(
- tokenizer_object=tokenizer,
- # tokenizer_file="tokenizer.json", # You can load from the tokenizer file, alternatively
- unk_token="[UNK]",
- pad_token="[PAD]",
- cls_token="[CLS]",
- sep_token="[SEP]",
- mask_token="[MASK]",
-)
-```
-
-Si vous utilisez une classe de *tokenizer* spécifique (comme `BertTokenizerFast`), vous aurez seulement besoin de spécifier les *tokens* spéciaux qui sont différents de ceux par défaut (ici, aucun) :
-
-```python
-from transformers import BertTokenizerFast
-
-wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer)
-```
-
-Vous pouvez ensuite utiliser ce *tokenizer* comme n'importe quel autre *tokenizer* de 🤗 *Transformers*. Vous pouvez le sauvegarder avec la méthode `save_pretrained()`, ou le télécharger sur le *Hub* avec la méthode `push_to_hub()`.
-
-Maintenant que nous avons vu comment construire un *tokenizer WordPiece*, faisons de même pour un *tokenizer* BPE. Nous irons un peu plus vite puisque vous connaissez toutes les étapes, et nous ne soulignerons que les différences.
-
-## Construire un *tokenizer* BPE à partir de zéro
-
-Construisons maintenant un *tokenizer* BPE. Comme pour le *tokenizer* BERT, nous commençons par initialiser un `Tokenizer` avec un modèle BPE :
-
-```python
-tokenizer = Tokenizer(models.BPE())
-```
-
-Comme pour BERT, nous pourrions initialiser ce modèle avec un vocabulaire si nous en avions un (nous aurions besoin de passer le `vocab` et le `merges` dans ce cas), mais puisque nous allons nous entraîner à partir de zéro, nous n'avons pas besoin de le faire. Nous n'avons pas non plus besoin de spécifier un `unk_token` parce que GPT-2 utilise un BPE au niveau de l'octet, ce qui ne le nécessite pas.
-
-GPT-2 n'utilise pas de normaliseur, donc nous sautons cette étape et allons directement à la pré-tokénisation :
-
-```python
-tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
-```
-
-L'option que nous avons ajoutée à `ByteLevel` ici est de ne pas ajouter d'espace en début de phrase (ce qui est le cas par défaut). Nous pouvons jeter un coup d'oeil à la pré-tokénisation d'un texte d'exemple comme avant :
-
-```python
-tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!")
-```
-
-```python out
-[('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)),
- ('tokenization', (15, 27)), ('!', (27, 28))]
-```
-
-Vient ensuite le modèle, qui doit être entraîné. Pour GPT-2, le seul *token* spécial est le *token* de fin de texte :
-
-```python
-trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
-tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
-```
-
-Comme avec le `WordPieceTrainer`, ainsi que le `vocab_size` et le `special_tokens`, nous pouvons spécifier la `min_frequency` si nous le voulons, ou si nous avons un suffixe de fin de mot (comme ``), nous pouvons le définir avec `end_of_word_suffix`.
-
-Ce *tokenizer* peut aussi être entraîné sur des fichiers texte :
-
-```python
-tokenizer.model = models.BPE()
-tokenizer.train(["wikitext-2.txt"], trainer=trainer)
-```
-
-Regardons la tokenisation d'un exemple de texte :
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer.")
-print(encoding.tokens)
-```
-
-```python out
-['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']
-```
-
-Nous appliquons le post-traitement au niveau de l'octet pour le *tokenizer* du GPT-2 comme suit :
-
-```python
-tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
-```
-
-L'option `trim_offsets = False` indique au post-processeur que nous devons laisser les *offsets* des *tokens* qui commencent par 'Ġ' tels quels : de cette façon, le début des *offsets* pointera sur l'espace avant le mot, et non sur le premier caractère du mot (puisque l'espace fait techniquement partie du token). Regardons le résultat avec le texte que nous venons de coder, où `'Ġtest'` est le token à l'index 4 :
-
-```python
-sentence = "Let's test this tokenizer."
-encoding = tokenizer.encode(sentence)
-start, end = encoding.offsets[4]
-sentence[start:end]
-```
-
-```python out
-' test'
-```
-
-Enfin, nous ajoutons un décodeur de niveau octet :
-
-```python
-tokenizer.decoder = decoders.ByteLevel()
-```
-
-et nous pourrons vérifier qu'il fonctionne correctement :
-
-```python
-tokenizer.decode(encoding.ids)
-```
-
-```python out
-"Let's test this tokenizer." # Testons ce tokenizer
-```
-
-Super ! Maintenant que nous avons terminé, nous pouvons sauvegarder le tokenizer comme avant, et l'envelopper dans un `PreTrainedTokenizerFast` ou un `GPT2TokenizerFast` si nous voulons l'utiliser dans 🤗 *Transformers* :
-
-```python
-from transformers import PreTrainedTokenizerFast
-
-wrapped_tokenizer = PreTrainedTokenizerFast(
- tokenizer_object=tokenizer,
- bos_token="<|endoftext|>",
- eos_token="<|endoftext|>",
-)
-```
-
-ou :
-
-```python
-from transformers import GPT2TokenizerFast
-
-wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)
-```
-
-Comme dernier exemple, nous allons vous montrer comment construire un *tokenizer* *Unigram* à partir de zéro.
-
-## Construire un *tokenizer* *Unigram* à partir de rien.
-
-Construisons maintenant un *tokenizer* XLNet. Comme pour les *tokenizers* précédents, nous commençons par initialiser un `Tokenizer` avec un modèle *Unigram* :
-
-```python
-tokenizer = Tokenizer(models.Unigram())
-```
-
-Encore une fois, nous pourrions initialiser ce modèle avec un vocabulaire si nous en avions un.
-
-Pour la normalisation, XLNet utilise quelques remplacements (qui proviennent de *SentencePiece*) :
-
-```python
-from tokenizers import Regex
-
-tokenizer.normalizer = normalizers.Sequence(
- [
- normalizers.Replace("``", '"'),
- normalizers.Replace("''", '"'),
- normalizers.NFKD(),
- normalizers.StripAccents(),
- normalizers.Replace(Regex(" {2,}"), " "),
- ]
-)
-```
-
-Cela remplace ``
et ''
avec "
et toute séquence de deux espaces ou plus par un seul espace, ainsi que la suppression des accents dans les textes à catégoriser.
-
-Le pré-*tokenizer* à utiliser pour tout *tokenizer SentencePiece* est `Metaspace` :
-
-```python
-tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()
-```
-
-Nous pouvons jeter un coup d'oeil à la pré-tokénisation d'un exemple de texte comme précédemment :
-
-```python
-tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!")
-```
-
-```python out
-[("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))]
-```
-
-Vient ensuite le modèle, qui doit être entraîné. XLNet possède un certain nombre de *tokens* spéciaux :
-
-```python
-special_tokens = ["", "", "", "", "", "", ""]
-trainer = trainers.UnigramTrainer(
- vocab_size=25000, special_tokens=special_tokens, unk_token=""
-)
-tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
-```
-
-Un argument très important à ne pas oublier pour le `UnigramTrainer` est le `unk_token`. Nous pouvons aussi passer d'autres arguments spécifiques à l'algorithme *Unigram*, comme le `shrinking_factor` pour chaque étape où nous enlevons des *tokens* (par défaut 0.75) ou le `max_piece_length` pour spécifier la longueur maximale d'un token donné (par défaut 16).
-
-Ce *tokenizer* peut aussi être entraîné sur des fichiers texte :
-
-```python
-tokenizer.model = models.Unigram()
-tokenizer.train(["wikitext-2.txt"], trainer=trainer)
-```
-
-Regardons la tokenisation d'un exemple de texte :
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer.")
-print(encoding.tokens)
-```
-
-```python out
-['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']
-```
-
-Une particularité de XLNet est qu'il place le *token* `` à la fin de la phrase, avec un type ID de 2 (pour le distinguer des autres *tokens*). Le résultat est un remplissage à gauche. Nous pouvons traiter tous les *tokens* spéciaux et les IDs de type de *token* avec un modèle, comme pour BERT, mais d'abord nous devons obtenir les IDs des *tokens* `` et `` :
-
-```python
-cls_token_id = tokenizer.token_to_id("")
-sep_token_id = tokenizer.token_to_id("")
-print(cls_token_id, sep_token_id)
-```
-
-```python out
-0 1
-```
-
-Le modèle ressemble à ceci :
-
-```python
-tokenizer.post_processor = processors.TemplateProcessing(
- single="$A:0 :0 :2",
- pair="$A:0 :0 $B:1 :1 :2",
- special_tokens=[("", sep_token_id), ("", cls_token_id)],
-)
-```
-
-Et nous pouvons tester son fonctionnement en codant une paire de phrases :
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!")
-print(encoding.tokens)
-print(encoding.type_ids)
-```
-
-```python out
-['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '', '▁', 'on', '▁', 'a', '▁pair',
- '▁of', '▁sentence', 's', '!', '', '']
-[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]
-```
-
-Enfin, nous ajoutons un décodeur `Metaspace` :
-
-```python
-tokenizer.decoder = decoders.Metaspace()
-```
-
-et on en a fini avec ce *tokenizer* ! On peut sauvegarder le *tokenizer* comme avant, et l'envelopper dans un `PreTrainedTokenizerFast` ou `XLNetTokenizerFast` si on veut l'utiliser dans 🤗 *Transformers*. Une chose à noter lors de l'utilisation de `PreTrainedTokenizerFast` est qu'en plus des *tokens* spéciaux, nous devons dire à la bibliothèque 🤗 *Transformers* de remplir à gauche :
-
-```python
-from transformers import PreTrainedTokenizerFast
-
-wrapped_tokenizer = PreTrainedTokenizerFast(
- tokenizer_object=tokenizer,
- bos_token="",
- eos_token="",
- unk_token="",
- pad_token="",
- cls_token="",
- sep_token="",
- mask_token="",
- padding_side="left",
-)
-```
-
-Ou alternativement :
-
-```python
-from transformers import XLNetTokenizerFast
-
-wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)
-```
-
-Maintenant que vous avez vu comment les différentes briques sont utilisées pour construire des *tokenizers* existants, vous devriez être capable d'écrire n'importe quel *tokenizer* que vous voulez avec la bibliothèque 🤗 *Tokenizers* et pouvoir l'utiliser dans 🤗 *Transformers*.
+# Construction d'un tokenizer, bloc par bloc
+
+
+
+Comme nous l'avons vu dans les sections précédentes, la tokenisation comprend plusieurs étapes :
+
+- normalisation (tout nettoyage du texte jugé nécessaire, comme la suppression des espaces ou des accents, la normalisation Unicode, etc.),
+- prétokénisation (division de l'entrée en mots),
+- passage de l'entrée dans le modèle (utilisation des mots prétokénisés pour produire une séquence de *tokens*),
+- post-traitement (ajout des *tokens* spéciaux du *tokenizer*, génération du masque d'attention et des identifiants du type de *token*).
+
+Pour mémoire, voici un autre aperçu du processus global :
+
+
+
+
+
+
+La bibliothèque 🤗 *Tokenizers* a été construite pour fournir plusieurs options pour chacune de ces étapes. Vous pouvez les mélanger et assortir ensemble. Dans cette section, nous verrons comment nous pouvons construire un *tokenizer* à partir de zéro, par opposition à entraîner un nouveau *tokenizer* à partir d'un ancien, comme nous l'avons fait dans [section 2](/course/fr/chapter6/2). Vous serez alors en mesure de construire n'importe quel type de *tokenizer* auquel vous pouvez penser !
+
+
+
+Plus précisément, la bibliothèque est construite autour d'une classe centrale `Tokenizer` avec les blocs de construction regroupés en sous-modules :
+
+- `normalizers` contient tous les types de `Normalizer` que vous pouvez utiliser (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.normalizers)),
+- `pre_tokenizers` contient tous les types de `PreTokenizer` que vous pouvez utiliser (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.pre_tokenizers)),
+- `models` contient les différents types de `Model` que vous pouvez utiliser, comme `BPE`, `WordPiece`, et `Unigram` (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.models)),
+- `trainers` contient tous les différents types de `Trainer` que vous pouvez utiliser pour entraîner votre modèle sur un corpus (un par type de modèle ; liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.trainers)),
+- `post_processors` contient les différents types de `PostProcessor` que vous pouvez utiliser (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.processors)),
+- `decoders` contient les différents types de `Decoder` que vous pouvez utiliser pour décoder les sorties de tokenization (liste complète [ici](https://huggingface.co/docs/tokenizers/python/latest/components.html#decoders)).
+
+Vous pouvez trouver la liste complète des blocs de construction [ici](https://huggingface.co/docs/tokenizers/python/latest/components.html).
+
+## Acquisition d'un corpus
+
+Pour entraîner notre nouveau *tokenizer*, nous utiliserons un petit corpus de texte (pour que les exemples soient rapides). Les étapes pour acquérir ce corpus sont similaires à celles que nous avons suivies au [début du chapitre](/course/fr/chapter6/2), mais cette fois nous utiliserons le jeu de données [WikiText-2](https://huggingface.co/datasets/wikitext) :
+
+
+```python
+from datasets import load_dataset
+
+dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train")
+
+
+def get_training_corpus():
+ for i in range(0, len(dataset), 1000):
+ yield dataset[i : i + 1000]["text"]
+```
+
+La fonction `get_training_corpus()` est un générateur qui donne des batchs de 1 000 textes, que nous utiliserons pour entraîner le *tokenizer*.
+
+🤗 *Tokenizers* peut aussi être entraîné directement sur des fichiers texte. Voici comment nous pouvons générer un fichier texte contenant tous les textes de WikiText-2 que nous pourrons ensuite utilisé en local :
+
+```python
+with open("wikitext-2.txt", "w", encoding="utf-8") as f:
+ for i in range(len(dataset)):
+ f.write(dataset[i]["text"] + "\n")
+```
+
+Ensuite, nous vous montrerons comment construire vos propres *tokenizers* pour BERT, GPT-2 et XLNet, bloc par bloc. Cela vous donnera un exemple de chacun des trois principaux algorithmes de tokenisation : *WordPiece*, BPE et *Unigram*. Commençons par BERT !
+
+## Construire un tokenizer WordPiece à partir de zéro
+
+Pour construire un *tokenizer* avec la bibliothèque 🤗 *Tokenizers*, nous commençons par instancier un objet `Tokenizer` avec un `model`. Puis nous définissons ses attributs `normalizer`, `pre_tokenizer`, `post_processor` et `decoder` aux valeurs que nous voulons.
+
+Pour cet exemple, nous allons créer un `Tokenizer` avec un modèle *WordPiece* :
+
+```python
+from tokenizers import (
+ decoders,
+ models,
+ normalizers,
+ pre_tokenizers,
+ processors,
+ trainers,
+ Tokenizer,
+)
+
+tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))
+```
+
+Nous devons spécifier le `unk_token` pour que le modèle sache quoi retourner lorsqu'il rencontre des caractères qu'il n'a pas vu auparavant. D'autres arguments que nous pouvons définir ici incluent le `vocab` de notre modèle (nous allons entraîner le modèle, donc nous n'avons pas besoin de le définir) et `max_input_chars_per_word`, qui spécifie une longueur maximale pour chaque mot (les mots plus longs que la valeur passée seront séparés).
+
+La première étape de la tokénisation est la normalisation. Puisque BERT est largement utilisé, une fonction `BertNormalizer` a été créée avec les options classiques que nous pouvons définir pour BERT : `lowercase` pour mettre le texte en minuscule, `strip_accents` qui enlève les accents, `clean_text` pour enlever tous les caractères de contrôle et fusionner des espaces répétés par un seul, et `handle_chinese_chars` qui place des espaces autour des caractères chinois. Pour reproduire le *tokenizer* `bert-base-uncased`, nous pouvons simplement définir ce *normalizer* :
+
+```python
+tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True)
+```
+
+Cependant, généralement, lorsque vous construisez un nouveau *tokenizer*, vous n'avez pas accès à un normaliseur aussi pratique déjà implémenté dans la bibliothèque 🤗 *Tokenizers*. Donc voyons comment créer le normaliseur de BERT manuellement. La bibliothèque fournit un normaliseur `Lowercase` et un normaliseur `StripAccents`. Il est possible de composer plusieurs normaliseurs en utilisant une `Sequence` :
+
+```python
+tokenizer.normalizer = normalizers.Sequence(
+ [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()]
+)
+```
+
+Nous utilisons également un normaliseur Unicode `NFD`, car sinon `StripAccents` ne reconnaîtra pas correctement les caractères accentués et ne les supprimera donc pas.
+
+Comme nous l'avons vu précédemment, nous pouvons utiliser la méthode `normalize_str()` du `normalizer` pour vérifier les effets qu'il a sur un texte donné :
+
+```python
+print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
+```
+
+```python out
+hello how are u?
+```
+
+
+
+**Pour aller plus loin** Si vous testez les deux versions des normaliseurs précédents sur une chaîne contenant le caractère unicode `u"\u0085"` vous remarquerez sûrement qu'ils ne sont pas exactement équivalents.
+Pour ne pas trop compliquer la version avec `normalizers.Sequence`, nous n'avons pas inclus les Regex que le `BertNormalizer` requiert quand l'argument `clean_text` est mis à `True` ce qui est le comportement par défaut. Mais ne vous inquiétez pas : il est possible d'obtenir exactement la même normalisation sans utiliser le très pratique `BertNormalizer` en ajoutant deux `normalizers.Replace` à la séquence de normalisation.
+
+
+
+L'étape suivante est la prétokenisation. Encore une fois, il y a un `BertPreTokenizer` préconstruit que nous pouvons utiliser :
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer()
+```
+
+Ou nous pouvons le construire à partir de zéro :
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
+```
+
+Notez que le `Whitespace` divise sur les espaces et tous les caractères qui ne sont pas des lettres, des chiffres ou le caractère de soulignement. Donc techniquement il divise sur les espaces et la ponctuation :
+
+```python
+tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
+```
+
+```python out
+[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
+ ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
+```
+
+Si vous voulez seulement séparer sur les espaces, vous devez utiliser `WhitespaceSplit` à la place :
+
+```python
+pre_tokenizer = pre_tokenizers.WhitespaceSplit()
+pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
+```
+
+```python out
+[("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))]
+```
+
+Comme pour les normaliseurs, vous pouvez utiliser une `Sequence` pour composer plusieurs prétokenizers :
+
+```python
+pre_tokenizer = pre_tokenizers.Sequence(
+ [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()]
+)
+pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
+```
+
+```python out
+[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
+ ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
+```
+
+L'étape suivante dans le pipeline de tokénisation est de faire passer les entrées par le modèle. Nous avons déjà spécifié notre modèle dans l'initialisation, mais nous devons encore l'entraîner, ce qui nécessitera un `WordPieceTrainer`. La principale chose à retenir lors de l'instanciation d'un entraîneur dans 🤗 *Tokenizers* est que vous devez lui passer tous les *tokens* spéciaux que vous avez l'intention d'utiliser. Sinon il ne les ajoutera pas au vocabulaire puisqu'ils ne sont pas dans le corpus d'entraînement :
+
+```python
+special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"]
+trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens)
+```
+
+En plus de spécifier la `vocab_size` et les `special_tokens`, nous pouvons définir la `min_frequency` (le nombre de fois qu'un *token* doit apparaître pour être inclus dans le vocabulaire) ou changer le `continuing_subword_prefix` (si nous voulons utiliser quelque chose de différent de `##`).
+
+Pour entraîner notre modèle en utilisant l'itérateur que nous avons défini plus tôt, il suffit d'exécuter cette commande :
+
+```python
+tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
+```
+
+Nous pouvons également utiliser des fichiers texte pour entraîner notre *tokenizer* qui ressemblerait alors à ceci (nous réinitialisons le modèle avec un `WordPiece` vide au préalable) :
+
+```python
+tokenizer.model = models.WordPiece(unk_token="[UNK]")
+tokenizer.train(["wikitext-2.txt"], trainer=trainer)
+```
+
+Dans les deux cas, nous pouvons ensuite tester le *tokenizer* sur un texte en appelant la méthode `encode()` :
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.']
+```
+
+L'encodage obtenu est un `Encoding` contenant toutes les sorties nécessaires du *tokenizer* dans ses différents attributs : `ids`, `type_ids`, `tokens`, `offsets`, `attention_mask`, `special_tokens_mask` et `overflowing`.
+
+La dernière étape du pipeline de tokénisation est le post-traitement. Nous devons ajouter le *token* `[CLS]` au début et le *token* `[SEP]` à la fin (ou après chaque phrase si nous avons une paire de phrases). Nous utiliserons `TemplateProcessor` pour cela, mais d'abord nous devons connaître les identifiants des *tokens* `[CLS]` et `[SEP]` dans le vocabulaire :
+
+```python
+cls_token_id = tokenizer.token_to_id("[CLS]")
+sep_token_id = tokenizer.token_to_id("[SEP]")
+print(cls_token_id, sep_token_id)
+```
+
+```python out
+(2, 3)
+```
+
+Pour écrire le gabarit pour `TemplateProcessor`, nous devons spécifier comment traiter une seule phrase et une paire de phrases. Pour les deux, nous écrivons les *tokens* spéciaux que nous voulons utiliser. La première (ou unique) phrase est représentée par `$A`, alors que la deuxième phrase (si on code une paire) est représentée par `$B`. Pour chacun de ces éléments (*tokens* spéciaux et phrases), nous spécifions également l'identifiant du *token* correspondant après un deux-points.
+
+Le gabarit classique de BERT est donc défini comme suit :
+
+```python
+tokenizer.post_processor = processors.TemplateProcessing(
+ single=f"[CLS]:0 $A:0 [SEP]:0",
+ pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1",
+ special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)],
+)
+```
+
+Notez que nous devons transmettre les identifiants des *tokens* spéciaux afin que le *tokenizer* puisse les convertir correctement.
+
+Une fois cela ajouté, revenons à notre exemple précédent donnera :
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]']
+```
+
+Et sur une paire de phrases, on obtient le bon résultat :
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.")
+print(encoding.tokens)
+print(encoding.type_ids)
+```
+
+```python out
+['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]']
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
+```
+
+Nous avons presque fini de construire ce *tokenizer* à partir de zéro. La dernière étape consiste à inclure un décodeur :
+
+```python
+tokenizer.decoder = decoders.WordPiece(prefix="##")
+```
+
+Testons-le sur notre précédent `encoding` :
+
+```python
+tokenizer.decode(encoding.ids)
+```
+
+```python out
+"let's test this tokenizer... on a pair of sentences." # Testons ce tokenizer... sur une paire de phrases.
+```
+
+Génial ! Nous pouvons enregistrer notre *tokenizer* dans un seul fichier JSON comme ceci :
+
+```python
+tokenizer.save("tokenizer.json")
+```
+
+Nous pouvons alors recharger ce fichier dans un objet `Tokenizer` avec la méthode `from_file()` :
+
+```python
+new_tokenizer = Tokenizer.from_file("tokenizer.json")
+```
+
+Pour utiliser ce *tokenizer* dans 🤗 *Transformers*, nous devons l'envelopper dans un `PreTrainedTokenizerFast`. Nous pouvons soit utiliser la classe générique, soit, si notre *tokenizer* correspond à un modèle existant, utiliser cette classe (ici, `BertTokenizerFast`). Si vous appliquez cette logique pour construire un tout nouveau *tokenizer*, vous devrez utiliser la première option.
+
+Pour envelopper le *tokenizer* dans un `PreTrainedTokenizerFast`, nous pouvons soit passer le *tokenizer* que nous avons construit comme un `tokenizer_object`, soit passer le fichier de *tokenizer* que nous avons sauvegardé comme `tokenizer_file`. Ce qu'il faut retenir, c'est que nous devons définir manuellement tous les *tokens* spéciaux car cette classe ne peut pas déduire de l'objet `tokenizer` quel *token* est le *token* de masque, quel est le *token*`[CLS]`, etc :
+
+```python
+from transformers import PreTrainedTokenizerFast
+
+wrapped_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ # tokenizer_file="tokenizer.json", # Vous pouvez charger à partir du fichier du tokenizer, alternativement
+ unk_token="[UNK]",
+ pad_token="[PAD]",
+ cls_token="[CLS]",
+ sep_token="[SEP]",
+ mask_token="[MASK]",
+)
+```
+
+Si vous utilisez une classe de *tokenizer* spécifique (comme `BertTokenizerFast`), vous aurez seulement besoin de spécifier les *tokens* spéciaux qui sont différents de ceux par défaut (ici, aucun) :
+
+```python
+from transformers import BertTokenizerFast
+
+wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer)
+```
+
+Vous pouvez ensuite utiliser ce *tokenizer* comme n'importe quel autre *tokenizer* de 🤗 *Transformers*. Vous pouvez le sauvegarder avec la méthode `save_pretrained()` ou le télécharger sur le *Hub* avec la méthode `push_to_hub()`.
+
+Maintenant que nous avons vu comment construire un *tokenizer WordPiece*, faisons de même pour un *tokenizer* BPE. Nous irons un peu plus vite puisque vous connaissez toutes les étapes. Nous ne soulignerons que les différences.
+
+## Construire un tokenizer BPE à partir de zéro
+
+Construisons maintenant un *tokenizer* BPE. Comme pour le *tokenizer* BERT, nous commençons par initialiser un `Tokenizer` avec un modèle BPE :
+
+```python
+tokenizer = Tokenizer(models.BPE())
+```
+
+Comme pour BERT, nous pourrions initialiser ce modèle avec un vocabulaire si nous en avions un (nous aurions besoin de passer le `vocab` et le `merges` dans ce cas), mais puisque nous allons nous entraîner à partir de zéro, nous n'avons pas besoin de le faire. Nous n'avons pas non plus besoin de spécifier un `unk_token` parce que le GPT-2 utilise un BPE au niveau de l'octet.
+
+GPT-2 n'utilise pas de normaliseur, donc nous sautons cette étape et allons directement à la prétokénisation :
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
+```
+
+L'option que nous avons ajoutée à `ByteLevel` ici est de ne pas ajouter d'espace en début de phrase (ce qui est le cas par défaut). Nous pouvons jeter un coup d'oeil à la prétokénisation d'un texte d'exemple comme avant :
+
+```python
+tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!")
+```
+
+```python out
+[('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)),
+ ('tokenization', (15, 27)), ('!', (27, 28))]
+```
+
+Vient ensuite le modèle, qui doit être entraîné. Pour le GPT-2, le seul *token* spécial est le *token* de fin de texte :
+
+```python
+trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
+tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
+```
+
+Comme avec le `WordPieceTrainer`, ainsi que le `vocab_size` et le `special_tokens`, nous pouvons spécifier la `min_frequency` si nous le voulons, ou si nous avons un suffixe de fin de mot (comme ``), nous pouvons le définir avec `end_of_word_suffix`.
+
+Ce *tokenizer* peut aussi être entraîné sur des fichiers texte :
+
+```python
+tokenizer.model = models.BPE()
+tokenizer.train(["wikitext-2.txt"], trainer=trainer)
+```
+
+Regardons la tokenisation d'un exemple de texte :
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']
+```
+
+Nous appliquons le post-traitement au niveau de l'octet pour le *tokenizer* du GPT-2 comme suit :
+
+```python
+tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
+```
+
+L'option `trim_offsets = False` indique au post-processeur que nous devons laisser les *offsets* des *tokens* qui commencent par 'Ġ' tels quels : de cette façon, le début des *offsets* pointera sur l'espace avant le mot, et non sur le premier caractère du mot (puisque l'espace fait techniquement partie du *token*). Regardons le résultat avec le texte que nous venons de coder, où `'Ġtest'` est le *token* à l'index 4 :
+
+```python
+sentence = "Let's test this tokenizer."
+encoding = tokenizer.encode(sentence)
+start, end = encoding.offsets[4]
+sentence[start:end]
+```
+
+```python out
+' test'
+```
+
+Enfin, nous ajoutons un décodeur au niveau de l'octet :
+
+```python
+tokenizer.decoder = decoders.ByteLevel()
+```
+
+et nous pouvons vérifier qu'il fonctionne correctement :
+
+```python
+tokenizer.decode(encoding.ids)
+```
+
+```python out
+"Let's test this tokenizer." # Testons ce tokenizer
+```
+
+Super ! Maintenant que nous avons terminé, nous pouvons sauvegarder le tokenizer comme avant, et l'envelopper dans un `PreTrainedTokenizerFast` ou un `GPT2TokenizerFast` si nous voulons l'utiliser dans 🤗 *Transformers* :
+
+```python
+from transformers import PreTrainedTokenizerFast
+
+wrapped_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ bos_token="<|endoftext|>",
+ eos_token="<|endoftext|>",
+)
+```
+
+ou :
+
+```python
+from transformers import GPT2TokenizerFast
+
+wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)
+```
+
+Comme dernier exemple, nous allons vous montrer comment construire un *tokenizer* *Unigram* à partir de zéro.
+
+## Construire un tokenizer Unigram à partir de zéro
+
+Construisons maintenant un *tokenizer* XLNet. Comme pour les *tokenizers* précédents, nous commençons par initialiser un `Tokenizer` avec un modèle *Unigram* :
+
+```python
+tokenizer = Tokenizer(models.Unigram())
+```
+
+Encore une fois, nous pourrions initialiser ce modèle avec un vocabulaire si nous en avions un.
+
+Pour la normalisation, XLNet utilise quelques remplacements (qui proviennent de *SentencePiece*) :
+
+```python
+from tokenizers import Regex
+
+tokenizer.normalizer = normalizers.Sequence(
+ [
+ normalizers.Replace("``", '"'),
+ normalizers.Replace("''", '"'),
+ normalizers.NFKD(),
+ normalizers.StripAccents(),
+ normalizers.Replace(Regex(" {2,}"), " "),
+ ]
+)
+```
+
+Il remplace ``
et ''
par "
et toute séquence de deux espaces ou plus par un seul espace, de plus il supprime les accents.
+
+Le prétokenizer à utiliser pour tout *tokenizer SentencePiece* est `Metaspace` :
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()
+```
+
+Nous pouvons jeter un coup d'oeil à la prétokénisation sur le même exemple de texte que précédemment :
+
+```python
+tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!")
+```
+
+```python out
+[("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))]
+```
+
+Vient ensuite le modèle, qui doit être entraîné. XLNet possède un certain nombre de *tokens* spéciaux :
+
+```python
+special_tokens = ["", "", "", "", "", "", ""]
+trainer = trainers.UnigramTrainer(
+ vocab_size=25000, special_tokens=special_tokens, unk_token=""
+)
+tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
+```
+
+Un argument très important à ne pas oublier pour le `UnigramTrainer` est le `unk_token`. Nous pouvons aussi passer d'autres arguments spécifiques à l'algorithme *Unigram*, comme le `shrinking_factor` pour chaque étape où nous enlevons des *tokens* (par défaut 0.75) ou le `max_piece_length` pour spécifier la longueur maximale d'un *token* donné (par défaut 16).
+
+Ce *tokenizer* peut aussi être entraîné sur des fichiers texte :
+
+```python
+tokenizer.model = models.Unigram()
+tokenizer.train(["wikitext-2.txt"], trainer=trainer)
+```
+
+Regardons la tokenisation de notre exemple :
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']
+```
+
+Une particularité de XLNet est qu'il place le *token* `` à la fin de la phrase, avec un identifiant de 2 (pour le distinguer des autres *tokens*). Le résultat est un remplissage à gauche. Nous pouvons traiter tous les *tokens* spéciaux et les types d'identifiant de *token* avec un modèle, comme pour BERT. Mais d'abord nous devons obtenir les identifiants des *tokens* `` et `` :
+
+```python
+cls_token_id = tokenizer.token_to_id("")
+sep_token_id = tokenizer.token_to_id("")
+print(cls_token_id, sep_token_id)
+```
+
+```python out
+0 1
+```
+
+Le modèle ressemble à ceci :
+
+```python
+tokenizer.post_processor = processors.TemplateProcessing(
+ single="$A:0 :0 :2",
+ pair="$A:0 :0 $B:1 :1 :2",
+ special_tokens=[("", sep_token_id), ("", cls_token_id)],
+)
+```
+
+Et nous pouvons tester son fonctionnement en codant une paire de phrases :
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!")
+print(encoding.tokens)
+print(encoding.type_ids)
+```
+
+```python out
+['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '', '▁', 'on', '▁', 'a', '▁pair',
+ '▁of', '▁sentence', 's', '!', '', '']
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]
+```
+
+Enfin, nous ajoutons un décodeur `Metaspace` :
+
+```python
+tokenizer.decoder = decoders.Metaspace()
+```
+
+et on en a fini avec ce *tokenizer* ! On peut le sauvegarder et l'envelopper dans un `PreTrainedTokenizerFast` ou `XLNetTokenizerFast` si on veut l'utiliser dans 🤗 *Transformers*. Une chose à noter lors de l'utilisation de `PreTrainedTokenizerFast` est qu'en plus des *tokens* spéciaux, nous devons dire à la bibliothèque 🤗 *Transformers* de rembourrer à gauche :
+
+```python
+from transformers import PreTrainedTokenizerFast
+
+wrapped_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ bos_token="",
+ eos_token="",
+ unk_token="",
+ pad_token="",
+ cls_token="",
+ sep_token="",
+ mask_token="",
+ padding_side="left",
+)
+```
+
+Ou alternativement :
+
+```python
+from transformers import XLNetTokenizerFast
+
+wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)
+```
+
+Maintenant que vous avez vu comment les différentes briques sont utilisées pour construire des *tokenizers* existants, vous devriez être capable d'écrire n'importe quel *tokenizer* que vous voulez avec la bibliothèque 🤗 *Tokenizers* et pouvoir l'utiliser dans 🤗 *Transformers*.
diff --git a/chapters/fr/chapter6/9.mdx b/chapters/fr/chapter6/9.mdx
index 7e59acd48..11e4beeab 100644
--- a/chapters/fr/chapter6/9.mdx
+++ b/chapters/fr/chapter6/9.mdx
@@ -1,11 +1,11 @@
-# *Tokenizer*, vérifié !
+# Tokenizer, coché !
Bon travail pour finir ce chapitre !
Après cette plongée en profondeur dans les *tokenizers*, vous devriez :
- être capable d'entraîner un nouveau tokenizer en utilisant un ancien tokenizer comme modèle,
-- comprendre comment utiliser les offsets pour faire correspondre la position des tokens à l'étendue du texte d'origine,
+- comprendre comment utiliser les *offsets* pour faire correspondre la position des *tokens* à l'étendue de texte d'origine,
- connaître les différences entre BPE, *WordPiece* et *Unigram*,
- être capable de combiner les blocs fournis par la bibliothèque 🤗 *Tokenizers* pour construire votre propre *tokenizer*,
- être capable d'utiliser ce *tokenizer* dans la bibliothèque 🤗 *Transformers*.
diff --git a/chapters/fr/chapter7/2.mdx b/chapters/fr/chapter7/2.mdx
index 110c5e1ab..7fb7fae81 100644
--- a/chapters/fr/chapter7/2.mdx
+++ b/chapters/fr/chapter7/2.mdx
@@ -1,981 +1,981 @@
-
-
-# Classification de *tokens*
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-La première application que nous allons explorer est la classification de *tokens*. Cette tâche générique englobe tous les problèmes qui peuvent être formulés comme "l'attribution d'une étiquette à chaque *token* dans une phrase", tels que :
-
-- **reconnaissance d'entités nommées (NER)** : trouver les entités (telles que des personnes, des lieux ou des organisations) dans une phrase. Cela peut être formulé comme l'attribution d'une étiquette à chaque *token* en ayant une classe par entité et une classe pour "aucune entité".
-- **part-of-speech tagging (POS)** : marquer chaque mot dans une phrase comme correspondant à une partie particulière du discours (comme un nom, un verbe, un adjectif, etc.).
-- ***chunking*** : trouver les *tokens* qui appartiennent à la même entité. Cette tâche (qui peut être combinée avec le POS ou la NER) peut être formulée comme l'attribution d'une étiquette (habituellement `B-`) à tous les *tokens* qui sont au début d'un morceau, une autre étiquette (habituellement `I-`) aux *tokens* qui sont à l'intérieur d'un morceau, et une troisième étiquette (habituellement `O`) aux *tokens* qui n'appartiennent à aucun morceau.
-
-
-
-Bien sûr, il existe de nombreux autres types de problèmes de classification de *tokens* ; ce ne sont là que quelques exemples représentatifs. Dans cette section, nous allons affiner un modèle (BERT) sur une tâche NER, qui sera alors capable de calculer des prédictions comme celle-ci :
-
-
-
-
-
-
-
-
-
-Vous pouvez trouver le modèle que nous allons entraîner et télécharger sur le *Hub* et vérifier ses prédictions [ici](https://huggingface.co/huggingface-course/bert-finetuned-ner?text=My+nom+est+Sylvain+et+je+travaille+à+Hugging+Face+in+Brooklyn).
-
-## Préparation des données
-
-Tout d'abord, nous avons besoin d'un jeu de données adapté à la classification des *tokens*. Dans cette section, nous utiliserons le jeu de données [CoNLL-2003](https://huggingface.co/datasets/conll2003), qui contient des articles de presse de Reuters.
-
-
-
-💡 Tant que votre jeu de données consiste en des textes divisés en mots avec leurs étiquettes correspondantes, vous pourrez adapter les procédures de traitement des données décrites ici à votre propre jeu de données. Reportez-vous au [Chapitre 5](/course/fr/chapter5) si vous avez besoin d'un rafraîchissement sur la façon de charger vos propres données personnalisées dans un `Dataset`.
-
-
-
-### Le jeu de données CoNLL-2003
-
-Pour charger le jeu de données CoNLL-2003, nous utilisons la méthode `load_dataset()` de la bibliothèque 🤗 *Datasets* :
-
-```py
-from datasets import load_dataset
-
-raw_datasets = load_dataset("conll2003")
-```
-
-Cela va télécharger et mettre en cache le jeu de données, comme nous l'avons vu dans [Chapitre 3](/course/fr/chapter3) pour le jeu de données GLUE MRPC. L'inspection de cet objet nous montre les colonnes présentes et la répartition entre les ensembles d'entraînement, de validation et de test :
-
-```py
-raw_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
- num_rows: 14041
- })
- validation: Dataset({
- features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
- num_rows: 3250
- })
- test: Dataset({
- features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
- num_rows: 3453
- })
-})
-```
-
-En particulier, nous pouvons voir que le jeu de données contient des étiquettes pour les trois tâches que nous avons mentionnées précédemment : NER, POS, et *chunking*. Une grande différence avec les autres jeux de données est que les textes d'entrée ne sont pas présentés comme des phrases ou des documents, mais comme des listes de mots (la dernière colonne est appelée `tokens`, mais elle contient des mots dans le sens où ce sont des entrées pré-tokénisées qui doivent encore passer par le *tokenizer* pour la tokenisation des sous-mots).
-
-Regardons le premier élément de l'ensemble d'entraînement :
-
-```py
-raw_datasets["train"][0]["tokens"]
-```
-
-```python out
-['EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'lamb', '.']
-```
-
-Puisque nous voulons effectuer la reconnaissance des entités nommées, nous allons examiner les balises NER :
-
-```py
-raw_datasets["train"][0]["ner_tags"]
-```
-
-```python out
-[3, 0, 7, 0, 0, 0, 7, 0, 0]
-```
-
-Ce sont les étiquettes sous forme d'entiers prêts pour l'entraînement, mais ils ne sont pas nécessairement utiles lorsque nous voulons inspecter les données. Comme pour la classification de texte, nous pouvons accéder à la correspondance entre ces entiers et les noms des étiquettes en regardant l'attribut `features` de notre jeu de données :
-
-```py
-ner_feature = raw_datasets["train"].features["ner_tags"]
-ner_feature
-```
-
-```python out
-Sequence(feature=ClassLabel(num_classes=9, names=['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC'], names_file=None, id=None), length=-1, id=None)
-```
-
-Cette colonne contient donc des éléments qui sont des séquences de `ClassLabel`s. Le type des éléments de la séquence se trouve dans l'attribut `feature` de cette `ner_feature`, et nous pouvons accéder à la liste des noms en regardant l'attribut `names` de cette `feature` :
-
-```py
-label_names = ner_feature.feature.names
-label_names
-```
-
-```python out
-['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC']
-```
-
-Nous avons déjà vu ces étiquettes en creusant dans le pipeline `token-classification` au [Chapitre 6](/course/fr/chapter6/3), mais pour un rapide rappel :
-
-- `O` signifie que le mot ne correspond à aucune entité.
-- `B-PER`/`I-PER` signifie que le mot correspond au début de/est à l'intérieur d'une entité *personne*.
-- `B-ORG`/`I-ORG` signifie que le mot correspond au début/à l'intérieur d'une entité *organisation*.
-- `B-LOC`/`I-LOC` signifie que le mot correspond au début/à l'intérieur d'une entité *location*.
-- `B-MISC`/`I-MISC` signifie que le mot correspond au début/à l'intérieur d'une entité *divers*.
-
-Maintenant, le décodage des étiquettes que nous avons vues précédemment nous donne ceci :
-
-```python
-words = raw_datasets["train"][0]["tokens"]
-labels = raw_datasets["train"][0]["ner_tags"]
-line1 = ""
-line2 = ""
-for word, label in zip(words, labels):
- full_label = label_names[label]
- max_length = max(len(word), len(full_label))
- line1 += word + " " * (max_length - len(word) + 1)
- line2 += full_label + " " * (max_length - len(full_label) + 1)
-
-print(line1)
-print(line2)
-```
-
-```python out
-'EU rejects German call to boycott British lamb .'
-'B-ORG O B-MISC O O O B-MISC O O'
-```
-
-Et pour un exemple mélangeant les étiquettes `B-` et `I-`, voici ce que le même code nous donne sur l'élément de l'ensemble d'entraînement à l'indice 4 :
-
-```python out
-'Germany \'s representative to the European Union \'s veterinary committee Werner Zwingmann said on Wednesday consumers should buy sheepmeat from countries other than Britain until the scientific advice was clearer .'
-'B-LOC O O O O B-ORG I-ORG O O O B-PER I-PER O O O O O O O O O O O B-LOC O O O O O O O'
-```
-
-Comme on peut le voir, les entités couvrant deux mots, comme "Union européenne" et "Werner Zwingmann", se voient attribuer une étiquette "B-" pour le premier mot et une étiquette "I-" pour le second.
-
-
-
-✏️ *Votre tour !* Affichez les deux mêmes phrases avec leurs étiquettes POS ou *chunking*.
-
-
-
-### Traitement des données
-
-
-
-Comme d'habitude, nos textes doivent être convertis en identifiants de *tokens* avant que le modèle puisse leur donner un sens. Comme nous l'avons vu dans le [Chapitre 6](/course/fr/chapter6/), une grande différence dans le cas des tâches de classification de *tokens* est que nous avons des entrées pré-tokénisées. Heureusement, l'API tokenizer peut gérer cela assez facilement ; nous devons juste avertir le `tokenizer` avec un drapeau spécial.
-
-Pour commencer, nous allons créer notre objet `tokenizer`. Comme nous l'avons dit précédemment, nous allons utiliser un modèle pré-entraîné BERT, donc nous allons commencer par télécharger et mettre en cache le tokenizer associé :
-
-```python
-from transformers import AutoTokenizer
-
-model_checkpoint = "bert-base-cased"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-```
-
-Vous pouvez remplacer le `model_checkpoint` par tout autre modèle que vous préférez à partir du [*Hub*]https://huggingface.co/models), ou par un dossier local dans lequel vous avez sauvegardé un modèle pré-entraîné et un *tokenizer*. La seule contrainte est que le *tokenizer* doit être soutenu par la bibliothèque 🤗 *Tokenizers*, il y a donc une version "rapide" disponible. Vous pouvez voir toutes les architectures qui ont une version rapide dans [ce grand tableau](https://huggingface.co/transformers/#supported-frameworks), et pour vérifier que l'objet `tokenizer` que vous utilisez est bien soutenu par 🤗 *Tokenizers* vous pouvez regarder son attribut `is_fast` :
-
-```py
-tokenizer.is_fast
-```
-
-```python out
-True
-```
-
-Pour tokeniser une entrée pré-tokenisée, nous pouvons utiliser notre `tokenizer` comme d'habitude et juste ajouter `is_split_into_words=True` :
-
-```py
-inputs = tokenizer(raw_datasets["train"][0]["tokens"], is_split_into_words=True)
-inputs.tokens()
-```
-
-```python out
-['[CLS]', 'EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'la', '##mb', '.', '[SEP]']
-```
-
-Comme on peut le voir, le *tokenizer* a ajouté les *tokens* spéciaux utilisés par le modèle (`[CLS]` au début et `[SEP]` à la fin) et n'a pas touché à la plupart des mots. Le mot `lamb`, cependant, a été tokenisé en deux sous-mots, `la` et `##mb`. Cela introduit un décalage entre nos entrées et les étiquettes : la liste des étiquettes n'a que 9 éléments, alors que notre entrée a maintenant 12 *tokens*. Il est facile de tenir compte des *tokens* spéciaux (nous savons qu'ils sont au début et à la fin), mais nous devons également nous assurer que nous alignons toutes les étiquettes avec les mots appropriés.
-
-Heureusement, comme nous utilisons un *tokenizer* rapide, nous avons accès aux superpouvoirs des 🤗 *Tokenizers*, ce qui signifie que nous pouvons facilement faire correspondre chaque *token* au mot correspondant (comme on le voit au [Chapitre 6](/course/fr/chapter6/3)) :
-
-```py
-inputs.word_ids()
-```
-
-```python out
-[None, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, None]
-```
-
-Avec un peu de travail, nous pouvons alors étendre notre liste d'étiquettes pour qu'elle corresponde aux *tokens*. La première règle que nous allons appliquer est que les *tokens* spéciaux reçoivent une étiquette de `-100`. En effet, par défaut, `-100` est un indice qui est ignoré dans la fonction de perte que nous allons utiliser (entropie croisée). Ensuite, chaque *token* reçoit la même étiquette que le *token* qui a commencé le mot dans lequel il se trouve, puisqu'ils font partie de la même entité. Pour les *tokens* à l'intérieur d'un mot mais pas au début, nous remplaçons le `B-` par `I-` (puisque le *token* ne commence pas l'entité) :
-
-```python
-def align_labels_with_tokens(labels, word_ids):
- new_labels = []
- current_word = None
- for word_id in word_ids:
- if word_id != current_word:
- # Start of a new word!
- current_word = word_id
- label = -100 if word_id is None else labels[word_id]
- new_labels.append(label)
- elif word_id is None:
- # Special token
- new_labels.append(-100)
- else:
- # Same word as previous token
- label = labels[word_id]
- # If the label is B-XXX we change it to I-XXX
- if label % 2 == 1:
- label += 1
- new_labels.append(label)
-
- return new_labels
-```
-
-Essayons-le sur notre première phrase :
-
-```py
-labels = raw_datasets["train"][0]["ner_tags"]
-word_ids = inputs.word_ids()
-print(labels)
-print(align_labels_with_tokens(labels, word_ids))
-```
-
-```python out
-[3, 0, 7, 0, 0, 0, 7, 0, 0]
-[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100]
-```
-
-Comme nous pouvons le voir, notre fonction a ajouté le `-100` pour les deux *tokens* spéciaux au début et à la fin, et un nouveau `0` pour notre mot qui a été divisé en deux *tokens*.
-
-
-
-✏️ *Votre tour !* Certains chercheurs préfèrent n'attribuer qu'un seul label par mot, et attribuer `-100` aux autres sous-*tokens* dans un mot donné. Ceci afin d'éviter que les longs mots qui se divisent en plusieurs batchs ne contribuent fortement à la perte. Changez la fonction précédente pour aligner les étiquettes avec les ID d'entrée en suivant cette règle.
-
-
-Pour prétraiter notre ensemble de données, nous devons tokeniser toutes les entrées et appliquer `align_labels_with_tokens()` sur toutes les étiquettes. Pour profiter de la vitesse de notre *tokenizer* rapide, il est préférable de tokeniser beaucoup de textes en même temps, donc nous allons écrire une fonction qui traite une liste d'exemples et utiliser la méthode `Dataset.map()` avec l'option `batched=True`. La seule chose qui diffère de notre exemple précédent est que la fonction `word_ids()` a besoin de récupérer l'index de l'exemple dont nous voulons les IDs de mots lorsque les entrées du *tokenizer* sont des listes de textes (ou dans notre cas, des listes de mots), donc nous l'ajoutons aussi :
-
-```py
-def tokenize_and_align_labels(examples):
- tokenized_inputs = tokenizer(
- examples["tokens"], truncation=True, is_split_into_words=True
- )
- all_labels = examples["ner_tags"]
- new_labels = []
- for i, labels in enumerate(all_labels):
- word_ids = tokenized_inputs.word_ids(i)
- new_labels.append(align_labels_with_tokens(labels, word_ids))
-
- tokenized_inputs["labels"] = new_labels
- return tokenized_inputs
-```
-
-Notez que nous n'avons pas encore paddé nos entrées ; nous le ferons plus tard, lors de la création des lots avec un collateur de données.
-
-Nous pouvons maintenant appliquer tout ce prétraitement en une seule fois sur les autres divisions de notre jeu de données :
-
-```py
-tokenized_datasets = raw_datasets.map(
- tokenize_and_align_labels,
- batched=True,
- remove_columns=raw_datasets["train"].column_names,
-)
-```
-
-Nous avons fait la partie la plus difficile ! Maintenant que les données ont été prétraitées, l'entraînement réel ressemblera beaucoup à ce que nous avons fait dans le [Chapitre 3](/course/fr/chapter3).
-
-{#if fw === 'pt'}
-
-## *Finetuning* du modèle avec l'API `Trainer`.
-
-Le code actuel utilisant le `Trainer` sera le même que précédemment ; les seuls changements sont la façon dont les données sont rassemblées dans un batch et la fonction de calcul de la métrique.
-
-{:else}
-
-## *Finetuning* fin du modèle avec Keras
-
-Le code réel utilisant Keras sera très similaire au précédent ; les seuls changements sont la façon dont les données sont rassemblées dans un batch et la fonction de calcul de la métrique.
-
-{/if}
-
-
-### Collation des données
-
-Nous ne pouvons pas simplement utiliser un `DataCollatorWithPadding` comme dans [Chapter 3](/course/fr/chapter3) parce que cela ne fait que rembourrer les entrées (IDs d'entrée, masque d'attention, et IDs de type de *token*). Ici, nos étiquettes doivent être remplies exactement de la même manière que les entrées afin qu'elles gardent la même taille, en utilisant `-100` comme valeur afin que les prédictions correspondantes soient ignorées dans le calcul de la perte.
-
-Tout ceci est fait par un [`DataCollatorForTokenClassification`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorfortokenclassification). Comme le `DataCollatorWithPadding`, il prend le `tokenizer` utilisé pour prétraiter les entrées :
-
-{#if fw === 'pt'}
-
-```py
-from transformers import DataCollatorForTokenClassification
-
-data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)
-```
-
-{:else}
-
-```py
-from transformers import DataCollatorForTokenClassification
-
-data_collator = DataCollatorForTokenClassification(
- tokenizer=tokenizer, return_tensors="tf"
-)
-```
-
-{/if}
-
-Pour tester cette fonction sur quelques échantillons, nous pouvons simplement l'appeler sur une liste d'exemples provenant de notre jeu d'entraînement tokénisé :
-
-```py
-batch = data_collator([tokenized_datasets["train"][i] for i in range(2)])
-batch["labels"]
-```
-
-```python out
-tensor([[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100],
- [-100, 1, 2, -100, -100, -100, -100, -100, -100, -100, -100, -100]])
-```
-
-Comparons cela aux étiquettes des premier et deuxième éléments de notre jeu de données :
-
-```py
-for i in range(2):
- print(tokenized_datasets["train"][i]["labels"])
-```
-
-```python out
-[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100]
-[-100, 1, 2, -100]
-```
-
-{#if fw === 'pt'}
-
-Comme nous pouvons le voir, le deuxième jeu d'étiquettes a été complété à la longueur du premier en utilisant `-100`s.
-
-{:else}
-
-Notre collateur de données est prêt à fonctionner ! Maintenant, utilisons-le pour créer un `tf.data.Dataset` avec la méthode `to_tf_dataset()`.
-
-```py
-tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
- columns=["attention_mask", "input_ids", "labels", "token_type_ids"],
- collate_fn=data_collator,
- shuffle=True,
- batch_size=16,
-)
-
-tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
- columns=["attention_mask", "input_ids", "labels", "token_type_ids"],
- collate_fn=data_collator,
- shuffle=False,
- batch_size=16,
-)
-```
-
-
- Prochain arrêt : le modèle lui-même.
-
-{/if}
-
-{#if fw === 'tf'}
-
-### Définir le modèle
-
-Puisque nous travaillons sur un problème de classification de *tokens*, nous allons utiliser la classe `TFAutoModelForTokenClassification`. La principale chose à retenir lors de la définition de ce modèle est de transmettre des informations sur le nombre de labels que nous avons. La façon la plus simple de le faire est de passer ce nombre avec l'argument `num_labels`, mais si nous voulons un joli *widget* d'inférence fonctionnant comme celui que nous avons vu au début de cette section, il est préférable de définir les correspondances correctes des étiquettes à la place.
-
-Elles devraient être définies par deux dictionnaires, `id2label` et `label2id`, qui contiennent la correspondance de l'ID au label et vice versa :
-
-```py
-id2label = {str(i): label for i, label in enumerate(label_names)}
-label2id = {v: k for k, v in id2label.items()}
-```
-
-Maintenant, nous pouvons simplement les passer à la méthode `TFAutoModelForTokenClassification.from_pretrained()`, et ils seront définis dans la configuration du modèle, puis correctement enregistrés et téléchargés vers le *Hub* :
-
-```py
-from transformers import TFAutoModelForTokenClassification
-
-model = TFAutoModelForTokenClassification.from_pretrained(
- model_checkpoint,
- id2label=id2label,
- label2id=label2id,
-)
-```
-
-Comme lorsque nous avons défini notre `TFAutoModelForSequenceClassification` au [Chapitre 3](/course/fr/chapter3), la création du modèle émet un avertissement indiquant que certains poids n'ont pas été utilisés (ceux de la tête de pré-entraînement) et que d'autres poids ont été initialisés de manière aléatoire (ceux de la tête de classification des nouveaux *tokens*), et que ce modèle doit être entraîné. Nous ferons cela dans une minute, mais vérifions d'abord que notre modèle a le bon nombre d'étiquettes :
-
-```python
-model.config.num_labels
-```
-
-```python out
-9
-```
-
-
-
-⚠️ Si vous avez un modèle avec le mauvais nombre de labels, vous obtiendrez une erreur obscure en appelant `model.fit()` plus tard. Cela peut être ennuyeux à déboguer, donc assurez-vous de faire cette vérification pour confirmer que vous avez le nombre de labels attendu.
-
-
-
-### *Finetuning* du modèle
-
-Nous sommes maintenant prêts à entraîner notre modèle ! Mais nous devons d'abord faire un peu de ménage : nous devons nous connecter à Hugging Face et définir nos hyperparamètres d'entraînement. Si vous travaillez dans un *notebook*, il y a une fonction pratique pour vous aider à le faire :
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-Cela affichera un *widget* où vous pourrez entrer vos identifiants de connexion à Hugging Face.
-
-Si vous ne travaillez pas dans un *notebook*, tapez simplement la ligne suivante dans votre terminal :
-
-```bash
-huggingface-cli login
-```
-
-Après s'être connecté, nous pouvons préparer tout ce dont nous avons besoin pour compiler notre modèle. 🤗 *Transformers* fournit une fonction pratique `create_optimizer()` qui vous donnera un optimiseur `AdamW` avec des paramètres appropriés pour la décroissance du taux des poids et la décroissance du taux d'apprentissage, les deux améliorant les performances de votre modèle par rapport à l'optimiseur `Adam` intégré :
-
-```python
-from transformers import create_optimizer
-import tensorflow as tf
-
-# Entraîner en mixed-precision float16
-# Commentez cette ligne si vous utilisez un GPU qui ne bénéficiera pas de cette fonction
-tf.keras.mixed_precision.set_global_policy("mixed_float16")
-
-# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans l'ensemble de données, divisé par la taille du batch puis multiplié par le nombre total d'époques
-# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un batchtf.data.Dataset,
-# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size
-num_epochs = 3
-num_train_steps = len(tf_train_dataset) * num_epochs
-
-optimizer, schedule = create_optimizer(
- init_lr=2e-5,
- num_warmup_steps=0,
- num_train_steps=num_train_steps,
- weight_decay_rate=0.01,
-)
-model.compile(optimizer=optimizer)
-```
-
-Notez également que nous ne fournissons pas d'argument `loss` à `compile()`. C'est parce que les modèles peuvent en fait calculer la perte en interne. Si vous compilez sans perte et fournissez vos étiquettes dans le dictionnaire d'entrée (comme nous le faisons dans nos jeux de données), alors le modèle s'entraînera en utilisant cette perte interne, qui sera appropriée pour la tâche et le type de modèle que vous avez choisi.
-
-Ensuite, nous définissons un `PushToHubCallback` pour télécharger notre modèle vers le *Hub* pendant l'entraînement, et nous ajustons le modèle avec ce *callback* :
-
-```python
-from transformers.keras_callbacks import PushToHubCallback
-
-callback = PushToHubCallback(output_dir="bert-finetuned-ner", tokenizer=tokenizer)
-
-model.fit(
- tf_train_dataset,
- validation_data=tf_eval_dataset,
- callbacks=[callback],
- epochs=num_epochs,
-)
-```
-
-Vous pouvez spécifier le nom complet du référentiel vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/bert-finetuned-ner"`. Par défaut, le dépôt utilisé sera dans votre espace de noms et nommé après le répertoire de sortie que vous avez défini, par exemple `"cool_huggingface_user/bert-finetuned-ner"`.
-
-
-
-💡 Si le répertoire de sortie que vous utilisez existe déjà, il doit être un clone local du dépôt vers lequel vous voulez pousser. S'il ne l'est pas, vous obtiendrez une erreur lors de l'appel de `model.fit()` et devrez définir un nouveau nom.
-
-
-
-Notez que pendant l'entraînement, chaque fois que le modèle est sauvegardé (ici, à chaque époque), il est téléchargé sur le *Hub* en arrière-plan. De cette façon, vous pourrez reprendre votre entraînement sur une autre machine si nécessaire.
-
-A ce stade, vous pouvez utiliser le *widget* d'inférence sur le *Hub* pour tester votre modèle et le partager avec vos amis. Vous avez réussi à *finetuner* un modèle sur une tâche de classification de *tokens*. Félicitations ! Mais quelle est la qualité réelle de notre modèle ? Nous devons évaluer certaines métriques pour le découvrir.
-
-{/if}
-
-
-### Métriques
-
-{#if fw === 'pt'}
-
-Pour que le `Trainer` calcule une métrique à chaque époque, nous devrons définir une fonction `compute_metrics()` qui prend les tableaux de prédictions et de labels, et retourne un dictionnaire avec les noms et les valeurs des métriques.
-
-Le cadre traditionnel utilisé pour évaluer la prédiction de la classification des *tokens* est [*seqeval*](https://github.com/chakki-works/seqeval). Pour utiliser cette métrique, nous devons d'abord installer la bibliothèque *seqeval* :
-
-```py
-!pip install seqeval
-```
-
-Nous pouvons ensuite le charger via la fonction `load_metric()` comme nous l'avons fait dans le [Chapitre 3](/course/fr/chapter3) :
-
-{:else}
-
-Le cadre traditionnel utilisé pour évaluer la prédiction de la classification des *tokens* est [*seqeval*](https://github.com/chakki-works/seqeval). Pour utiliser cette métrique, nous devons d'abord installer la bibliothèque *seqeval* :
-
-```py
-!pip install seqeval
-```
-
-Nous pouvons ensuite le charger via la fonction `load_metric()` comme nous l'avons fait dans le [Chapitre 3](/course/fr/chapter3) :
-
-{/if}
-
-```py
-from datasets import load_metric
-
-metric = load_metric("seqeval")
-```
-
-Cette métrique ne se comporte pas comme la précision standard : elle prend les listes d'étiquettes comme des chaînes de caractères et non comme des entiers. Nous devrons donc décoder complètement les prédictions et les étiquettes avant de les transmettre à la métrique. Voyons comment cela fonctionne. Tout d'abord, nous allons obtenir les étiquettes pour notre premier exemple d'entraînement :
-
-```py
-labels = raw_datasets["train"][0]["ner_tags"]
-labels = [label_names[i] for i in labels]
-labels
-```
-
-```python out
-['B-ORG', 'O', 'B-MISC', 'O', 'O', 'O', 'B-MISC', 'O', 'O']
-```
-
-Nous pouvons alors créer de fausses prédictions pour celles-ci en changeant simplement la valeur de l'indice 2 :
-
-```py
-predictions = labels.copy()
-predictions[2] = "O"
-metric.compute(predictions=[predictions], references=[labels])
-```
-
-Notez que la métrique prend une liste de prédictions (pas seulement une) et une liste d'étiquettes. Voici la sortie :
-
-```python out
-{'MISC': {'precision': 1.0, 'recall': 0.5, 'f1': 0.67, 'number': 2},
- 'ORG': {'precision': 1.0, 'recall': 1.0, 'f1': 1.0, 'number': 1},
- 'overall_precision': 1.0,
- 'overall_recall': 0.67,
- 'overall_f1': 0.8,
- 'overall_accuracy': 0.89}
-```
-
-{#if fw === 'pt'}
-
-Cela renvoie un batch d'informations ! Nous obtenons la précision, le rappel, et le score F1 pour chaque entité séparée, ainsi que le score global. Pour notre calcul de métrique, nous ne garderons que le score global, mais n'hésitez pas à modifier la fonction `compute_metrics()` pour retourner toutes les métriques que vous souhaitez.
-
-Cette fonction `compute_metrics()` prend d'abord l'argmax des logits pour les convertir en prédictions (comme d'habitude, les logits et les probabilités sont dans le même ordre, donc nous n'avons pas besoin d'appliquer le softmax). Ensuite, nous devons convertir les étiquettes et les prédictions des entiers en chaînes de caractères. Nous supprimons toutes les valeurs dont l'étiquette est `-100`, puis nous passons les résultats à la méthode `metric.compute()` :
-
-```py
-import numpy as np
-
-
-def compute_metrics(eval_preds):
- logits, labels = eval_preds
- predictions = np.argmax(logits, axis=-1)
-
- # Suppression de l'index ignoré (tokens spéciaux) et conversion en étiquettes
- true_labels = [[label_names[l] for l in label if l != -100] for label in labels]
- true_predictions = [
- [label_names[p] for (p, l) in zip(prediction, label) if l != -100]
- for prediction, label in zip(predictions, labels)
- ]
- all_metrics = metric.compute(predictions=true_predictions, references=true_labels)
- return {
- "precision": all_metrics["overall_precision"],
- "recall": all_metrics["overall_recall"],
- "f1": all_metrics["overall_f1"],
- "accuracy": all_metrics["overall_accuracy"],
- }
-```
-
-Maintenant que ceci est fait, nous sommes presque prêts à définir notre `Trainer`. Nous avons juste besoin d'un `modèle` pour *finetuner* !
-
-{:else}
-
-Cela renvoie un batch d'informations ! Nous obtenons la précision, le rappel et le score F1 pour chaque entité séparée, ainsi que pour l'ensemble. Voyons maintenant ce qui se passe si nous essayons d'utiliser les prédictions de notre modèle pour calculer des scores réels.
-
-TensorFlow n'aime pas concaténer nos prédictions ensemble, car elles ont des longueurs de séquence variables. Cela signifie que nous ne pouvons pas simplement utiliser `model.predict()`. Mais cela ne va pas nous arrêter. Nous obtiendrons des prédictions un batch à la fois et les concaténerons en une grande liste longue au fur et à mesure, en laissant tomber les *tokens* `-100` qui indiquent le masquage/le remplissage, puis nous calculerons les métriques sur la liste à la fin :
-
-```py
-import numpy as np
-
-all_predictions = []
-all_labels = []
-for batch in tf_eval_dataset:
- logits = model.predict(batch)["logits"]
- labels = batch["labels"]
- predictions = np.argmax(logits, axis=-1)
- for prediction, label in zip(predictions, labels):
- for predicted_idx, label_idx in zip(prediction, label):
- if label_idx == -100:
- continue
- all_predictions.append(label_names[predicted_idx])
- all_labels.append(label_names[label_idx])
-metric.compute(predictions=[all_predictions], references=[all_labels])
-```
-
-
-```python out
-{'LOC': {'precision': 0.91, 'recall': 0.92, 'f1': 0.91, 'number': 1668},
- 'MISC': {'precision': 0.70, 'recall': 0.79, 'f1': 0.74, 'number': 702},
- 'ORG': {'precision': 0.85, 'recall': 0.90, 'f1': 0.88, 'number': 1661},
- 'PER': {'precision': 0.95, 'recall': 0.95, 'f1': 0.95, 'number': 1617},
- 'overall_precision': 0.87,
- 'overall_recall': 0.91,
- 'overall_f1': 0.89,
- 'overall_accuracy': 0.97}
-```
-
-Comment s'est comporté votre modèle, comparé au nôtre ? Si vous avez obtenu des chiffres similaires, votre entraînement a été un succès !
-
-{/if}
-
-{#if fw === 'pt'}
-
-### Définir le modèle
-
-Puisque nous travaillons sur un problème de classification de *tokens*, nous allons utiliser la classe `AutoModelForTokenClassification`. La principale chose à retenir lors de la définition de ce modèle est de transmettre des informations sur le nombre de labels que nous avons. La façon la plus simple de le faire est de passer ce nombre avec l'argument `num_labels`, mais si nous voulons un joli *widget* d'inférence fonctionnant comme celui que nous avons vu au début de cette section, il est préférable de définir les correspondances correctes des étiquettes à la place.
-
-Elles devraient être définies par deux dictionnaires, `id2label` et `label2id`, qui contiennent les correspondances entre ID et label et vice versa :
-
-```py
-id2label = {str(i): label for i, label in enumerate(label_names)}
-label2id = {v: k for k, v in id2label.items()}
-```
-
-Maintenant nous pouvons simplement les passer à la méthode `AutoModelForTokenClassification.from_pretrained()`, et ils seront définis dans la configuration du modèle et ensuite correctement sauvegardés et téléchargés vers le *Hub* :
-
-```py
-from transformers import AutoModelForTokenClassification
-
-model = AutoModelForTokenClassification.from_pretrained(
- model_checkpoint,
- id2label=id2label,
- label2id=label2id,
-)
-```
-
-Comme lorsque nous avons défini notre `AutoModelForSequenceClassification` au [Chapitre 3](/course/fr/chapter3), la création du modèle émet un avertissement indiquant que certains poids n'ont pas été utilisés (ceux de la tête de pré-entraînement) et que d'autres poids ont été initialisés de manière aléatoire (ceux de la tête de classification des nouveaux *tokens*), et que ce modèle doit être entraîné. Nous ferons cela dans une minute, mais vérifions d'abord que notre modèle a le bon nombre d'étiquettes :
-
-```python
-model.config.num_labels
-```
-
-```python out
-9
-```
-
-
-
-⚠️ Si vous avez un modèle avec le mauvais nombre d'étiquettes, vous obtiendrez une erreur obscure lors de l'appel de la méthode `Trainer.train()` plus tard (quelque chose comme "CUDA error : device-side assert triggered"). C'est la première cause de bogues signalés par les utilisateurs pour de telles erreurs, donc assurez-vous de faire cette vérification pour confirmer que vous avez le nombre d'étiquettes attendu.
-
-
-
-### *Finetuning* du modèle
-
-Nous sommes maintenant prêts à entraîner notre modèle ! Nous devons juste faire deux dernières choses avant de définir notre `Trainer` : se connecter à Hugging Face et définir nos arguments d'entraînement. Si vous travaillez dans un *notebook*, il y a une fonction pratique pour vous aider à le faire :
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-Cela affichera un *widget* où vous pourrez entrer vos identifiants de connexion à Hugging Face.
-
-Si vous ne travaillez pas dans un *notebook*, tapez simplement la ligne suivante dans votre terminal :
-
-```bash
-huggingface-cli login
-```
-
-Une fois ceci fait, nous pouvons définir nos `TrainingArguments` :
-
-```python
-from transformers import TrainingArguments
-
-args = TrainingArguments(
- "bert-finetuned-ner",
- evaluation_strategy="epoch",
- save_strategy="epoch",
- learning_rate=2e-5,
- num_train_epochs=3,
- weight_decay=0.01,
- push_to_hub=True,
-)
-```
-
-Vous avez déjà vu la plupart d'entre eux : nous définissons quelques hyperparamètres (comme le taux d'apprentissage, le nombre d'époques à entraîner, et la décroissance du poids), et nous spécifions `push_to_hub=True` pour indiquer que nous voulons sauvegarder le modèle et l'évaluer à la fin de chaque époque, et que nous voulons télécharger nos résultats vers le *Hub*. Notez que vous pouvez spécifier le nom du référentiel vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/bert-finetuned-ner"``TrainingArguments`. Par défaut, le référentiel utilisé sera dans votre espace de noms et nommé d'après le répertoire de sortie que vous avez défini, donc dans notre cas ce sera `"sgugger/bert-finetuned-ner"`.
-
-
-
-💡 Si le répertoire de sortie que vous utilisez existe déjà, il doit être un clone local du dépôt vers lequel vous voulez pousser. S'il ne l'est pas, vous obtiendrez une erreur lors de la définition de votre `Trainer` et devrez définir un nouveau nom.
-
-
-
-Enfin, nous passons tout au `Trainer` et lançons l'entraînement :
-
-```python
-from transformers import Trainer
-
-trainer = Trainer(
- model=model,
- args=args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- compute_metrics=compute_metrics,
- tokenizer=tokenizer,
-)
-trainer.train()
-```
-
-Notez que pendant l'entraînement, chaque fois que le modèle est sauvegardé (ici, à chaque époque), il est téléchargé sur le *Hub* en arrière-plan. De cette façon, vous serez en mesure de reprendre votre entraînement sur une autre machine si nécessaire.
-
-Une fois l'entraînement terminé, nous utilisons la méthode `push_to_hub()` pour nous assurer que nous téléchargeons la version la plus récente du modèle :
-
-```py
-trainer.push_to_hub(commit_message="Training complete")
-```
-
-Cette commande renvoie l'URL du commit qu'elle vient de faire, si vous voulez l'inspecter :
-
-```python out
-'https://huggingface.co/sgugger/bert-finetuned-ner/commit/26ab21e5b1568f9afeccdaed2d8715f571d786ed'
-```
-
-Le `Trainer` rédige également une carte modèle avec tous les résultats de l'évaluation et la télécharge. A ce stade, vous pouvez utiliser le *widget* d'inférence sur le *Hub* pour tester votre modèle et le partager avec vos amis. Vous avez réussi à affiner un modèle sur une tâche de classification de *tokens*. Félicitations !
-
-Si vous voulez plonger un peu plus profondément dans la boucle d'entraînement, nous allons maintenant vous montrer comment faire la même chose en utilisant 🤗 *Accelerate*.
-
-## Une boucle d'entraînement personnalisée
-
-Jetons maintenant un coup d'œil à la boucle d'entraînement complète, afin que vous puissiez facilement personnaliser les parties dont vous avez besoin. Elle ressemblera beaucoup à ce que nous avons fait dans le [Chapitre 3](/course/fr/chapter3/4), avec quelques changements pour l'évaluation.
-
-### Préparer tout pour l'entraînement
-
-D'abord nous devons construire le `DataLoader`s à partir de nos jeux de données. Nous allons réutiliser notre `data_collator` comme un `collate_fn` et mélanger l'ensemble d'entraînement, mais pas l'ensemble de validation :
-
-```py
-from torch.utils.data import DataLoader
-
-train_dataloader = DataLoader(
- tokenized_datasets["train"],
- shuffle=True,
- collate_fn=data_collator,
- batch_size=8,
-)
-eval_dataloader = DataLoader(
- tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
-)
-```
-
-Ensuite, nous réinstantifions notre modèle, pour nous assurer que nous ne continuons pas le réglage fin d'avant, mais que nous repartons du modèle pré-entraîné de BERT :
-
-```py
-model = AutoModelForTokenClassification.from_pretrained(
- model_checkpoint,
- id2label=id2label,
- label2id=label2id,
-)
-```
-
-Ensuite, nous aurons besoin d'un optimiseur. Nous allons utiliser le classique `AdamW`, qui est comme `Adam`, mais avec un correctif dans la façon dont la décroissance du taux des poids est appliquée :
-
-```py
-from torch.optim import AdamW
-
-optimizer = AdamW(model.parameters(), lr=2e-5)
-```
-
-Une fois que nous avons tous ces objets, nous pouvons les envoyer à la méthode `accelerator.prepare()` :
-
-```py
-from accelerate import Accelerator
-
-accelerator = Accelerator()
-model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
- model, optimizer, train_dataloader, eval_dataloader
-)
-```
-
-
-
-🚨 Si vous vous entraînez sur un TPU, vous devrez déplacer tout le code à partir de la cellule ci-dessus dans une fonction d'entraînement dédiée. Voir le [Chapitre 3](/course/fr/chapter3) pour plus de détails.
-
-
-
-Maintenant que nous avons envoyé notre `train_dataloader` à `accelerator.prepare()`, nous pouvons utiliser sa longueur pour calculer le nombre d'étapes d'entraînement. Rappelez-vous que nous devrions toujours faire cela après avoir préparé le *dataloader*, car cette méthode modifiera sa longueur. Nous utilisons un programme linéaire classique du taux d'apprentissage à 0 :
-
-```py
-from transformers import get_scheduler
-
-num_train_epochs = 3
-num_update_steps_per_epoch = len(train_dataloader)
-num_training_steps = num_train_epochs * num_update_steps_per_epoch
-
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-```
-
-Enfin, pour pousser notre modèle vers le Hub, nous aurons besoin de créer un objet `Repository` dans un dossier de travail. Tout d'abord, connectez-vous à Hugging Face, si vous n'êtes pas déjà connecté. Nous déterminerons le nom du dépôt à partir de l'ID du modèle que nous voulons donner à notre modèle (n'hésitez pas à remplacer le `repo_name` par votre propre choix ; il doit juste contenir votre nom d'utilisateur, ce que fait la fonction `get_full_repo_name()`) :
-
-```py
-from huggingface_hub import Repository, get_full_repo_name
-
-model_name = "bert-finetuned-ner-accelerate"
-repo_name = get_full_repo_name(model_name)
-repo_name
-```
-
-```python out
-'sgugger/bert-finetuned-ner-accelerate'
-```
-
-Ensuite, nous pouvons cloner ce référentiel dans un dossier local. S'il existe déjà, ce dossier local doit être un clone existant du référentiel avec lequel nous travaillons :
-
-```py
-output_dir = "bert-finetuned-ner-accelerate"
-repo = Repository(output_dir, clone_from=repo_name)
-```
-
-Nous pouvons maintenant télécharger tout ce que nous sauvegardons dans `output_dir` en appelant la méthode `repo.push_to_hub()`. Cela nous aidera à télécharger les modèles intermédiaires à la fin de chaque époque.
-
-### Boucle d'entraînement
-
-Nous sommes maintenant prêts à écrire la boucle d'entraînement complète. Pour simplifier sa partie évaluation, nous définissons cette fonction `postprocess()` qui prend les prédictions et les étiquettes et les convertit en listes de chaînes de caractères, comme notre objet `metric` l'attend :
-
-```py
-def postprocess(predictions, labels):
- predictions = predictions.detach().cpu().clone().numpy()
- labels = labels.detach().cpu().clone().numpy()
-
- # Remove ignored index (special tokens) and convert to labels
- true_labels = [[label_names[l] for l in label if l != -100] for label in labels]
- true_predictions = [
- [label_names[p] for (p, l) in zip(prediction, label) if l != -100]
- for prediction, label in zip(predictions, labels)
- ]
- return true_labels, true_predictions
-```
-
-Ensuite, nous pouvons écrire la boucle d'entraînement. Après avoir défini une barre de progression pour suivre l'évolution de l'entraînement, la boucle comporte trois parties :
-
-- l'entraînement proprement dit, qui est l'itération classique sur le `train_dataloader`, passage en avant du modèle, puis passage en arrière et étape d'optimisation,
-- l'évaluation, dans laquelle il y a une nouveauté après avoir obtenu les sorties de notre modèle sur un lot : puisque deux processus peuvent avoir paddé les entrées et les étiquettes à des formes différentes, nous devons utiliser `accelerator.pad_across_processes()` pour rendre les prédictions et les étiquettes de la même forme avant d'appeler la méthode `gather()`. Si nous ne le faisons pas, l'évaluation va soit se tromper, soit se bloquer pour toujours. Ensuite, nous envoyons les résultats à `metric.add_batch()` et appelons `metric.compute()` une fois que la boucle d'évaluation est terminée,
-- sauvegarde et téléchargement, où nous sauvegardons d'abord le modèle et le tokenizer, puis appelons `repo.push_to_hub()`. Remarquez que nous utilisons l'argument `blocking=False` pour indiquer à la bibliothèque 🤗 *Hub* de pousser dans un processus asynchrone. De cette façon, l'entraînement continue normalement et cette (longue) instruction est exécutée en arrière-plan.
-
-Voici le code complet de la boucle d'entraînement :
-
-```py
-from tqdm.auto import tqdm
-import torch
-
-progress_bar = tqdm(range(num_training_steps))
-
-for epoch in range(num_train_epochs):
- # Entraînement
- model.train()
- for batch in train_dataloader:
- outputs = model(**batch)
- loss = outputs.loss
- accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-
- # Evaluation
- model.eval()
- for batch in eval_dataloader:
- with torch.no_grad():
- outputs = model(**batch)
-
- predictions = outputs.logits.argmax(dim=-1)
- labels = batch["labels"]
-
- # Nécessaire pour rembourrer les prédictions et les étiquettes à rassembler
- predictions = accelerator.pad_across_processes(predictions, dim=1, pad_index=-100)
- labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)
-
- predictions_gathered = accelerator.gather(predictions)
- labels_gathered = accelerator.gather(labels)
-
- true_predictions, true_labels = postprocess(predictions_gathered, labels_gathered)
- metric.add_batch(predictions=true_predictions, references=true_labels)
-
- results = metric.compute()
- print(
- f"epoch {epoch}:",
- {
- key: results[f"overall_{key}"]
- for key in ["precision", "recall", "f1", "accuracy"]
- },
- )
-
- # Sauvegarder et télécharger
- accelerator.wait_for_everyone()
- unwrapped_model = accelerator.unwrap_model(model)
- unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
- if accelerator.is_main_process:
- tokenizer.save_pretrained(output_dir)
- repo.push_to_hub(
- commit_message=f"Training in progress epoch {epoch}", blocking=False
- )
-```
-
-Au cas où ce serait la première fois que vous verriez un modèle enregistré avec 🤗 *Accelerate*, prenons un moment pour inspecter les trois lignes de code qui l'accompagnent :
-
-```py
-accelerator.wait_for_everyone()
-unwrapped_model = accelerator.unwrap_model(model)
-unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
-```
-
-La première ligne est explicite : elle indique à tous les processus d'attendre que tout le monde soit à ce stade avant de continuer. C'est pour s'assurer que nous avons le même modèle dans chaque processus avant de sauvegarder. Ensuite, nous prenons le `unwrapped_model`, qui est le modèle de base que nous avons défini. La méthode `accelerator.prepare()` modifie le modèle pour qu'il fonctionne dans l'entraînement distribué, donc il n'aura plus la méthode `save_pretrained()` ; la méthode `accelerator.unwrap_model()` annule cette étape. Enfin, nous appelons `save_pretrained()` mais nous disons à cette méthode d'utiliser `accelerator.save()` au lieu de `torch.save()`.
-
-Une fois ceci fait, vous devriez avoir un modèle qui produit des résultats assez similaires à celui entraîné avec le `Trainer`. Vous pouvez vérifier le modèle que nous avons formé en utilisant ce code à [*huggingface-course/bert-finetuned-ner-accelerate*(https://huggingface.co/huggingface-course/bert-finetuned-ner-accelerate). Et si vous voulez tester des modifications de la boucle d'entraînement, vous pouvez les implémenter directement en modifiant le code ci-dessus !
-
-{/if}
-
-### Utilisation du modèle *finetuné*
-
-Nous vous avons déjà montré comment vous pouvez utiliser le modèle que nous avons affiné sur le *Hub* avec le *widget* d'inférence. Pour l'utiliser localement dans un `pipeline`, vous devez juste spécifier l'identifiant de modèle approprié :
-
-```py
-from transformers import pipeline
-
-# Remplacez ceci par votre propre checkpoint
-model_checkpoint = "huggingface-course/bert-finetuned-ner"
-token_classifier = pipeline(
- "token-classification", model=model_checkpoint, aggregation_strategy="simple"
-)
-token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
-```
-
-```python out
-[{'entity_group': 'PER', 'score': 0.9988506, 'word': 'Sylvain', 'start': 11, 'end': 18},
- {'entity_group': 'ORG', 'score': 0.9647625, 'word': 'Hugging Face', 'start': 33, 'end': 45},
- {'entity_group': 'LOC', 'score': 0.9986118, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
-```
-
-Super ! Notre modèle fonctionne aussi bien que le modèle par défaut pour ce pipeline !
+
+
+# Classification de *tokens*
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+La première application que nous allons explorer est la classification de *tokens*. Cette tâche générique englobe tous les problèmes qui peuvent être formulés comme "l'attribution d'une étiquette à chaque *token* dans une phrase", tels que :
+
+- **reconnaissance d'entités nommées (NER)** : trouver les entités (telles que des personnes, des lieux ou des organisations) dans une phrase. Cela peut être formulé comme l'attribution d'une étiquette à chaque *token* en ayant une classe par entité et une classe pour "aucune entité".
+- **part-of-speech tagging (POS)** : marquer chaque mot dans une phrase comme correspondant à une partie particulière du discours (comme un nom, un verbe, un adjectif, etc.).
+- ***chunking*** : trouver les *tokens* qui appartiennent à la même entité. Cette tâche (qui peut être combinée avec le POS ou la NER) peut être formulée comme l'attribution d'une étiquette (habituellement `B-`) à tous les *tokens* qui sont au début d'un morceau, une autre étiquette (habituellement `I-`) aux *tokens* qui sont à l'intérieur d'un morceau, et une troisième étiquette (habituellement `O`) aux *tokens* qui n'appartiennent à aucun morceau.
+
+
+
+Bien sûr, il existe de nombreux autres types de problèmes de classification de *tokens* ; ce ne sont là que quelques exemples représentatifs. Dans cette section, nous allons affiner un modèle (BERT) sur une tâche NER, qui sera alors capable de calculer des prédictions comme celle-ci :
+
+
+
+
+
+
+
+
+
+Vous pouvez trouver le modèle que nous allons entraîner et télécharger sur le *Hub* et vérifier ses prédictions [ici](https://huggingface.co/huggingface-course/bert-finetuned-ner?text=My+nom+est+Sylvain+et+je+travaille+à+Hugging+Face+in+Brooklyn).
+
+## Préparation des données
+
+Tout d'abord, nous avons besoin d'un jeu de données adapté à la classification des *tokens*. Dans cette section, nous utiliserons le jeu de données [CoNLL-2003](https://huggingface.co/datasets/conll2003), qui contient des articles de presse de Reuters.
+
+
+
+💡 Tant que votre jeu de données consiste en des textes divisés en mots avec leurs étiquettes correspondantes, vous pourrez adapter les procédures de traitement des données décrites ici à votre propre jeu de données. Reportez-vous au [Chapitre 5](/course/fr/chapter5) si vous avez besoin d'un rafraîchissement sur la façon de charger vos propres données personnalisées dans un `Dataset`.
+
+
+
+### Le jeu de données CoNLL-2003
+
+Pour charger le jeu de données CoNLL-2003, nous utilisons la méthode `load_dataset()` de la bibliothèque 🤗 *Datasets* :
+
+```py
+from datasets import load_dataset
+
+raw_datasets = load_dataset("conll2003")
+```
+
+Cela va télécharger et mettre en cache le jeu de données, comme nous l'avons vu dans [Chapitre 3](/course/fr/chapter3) pour le jeu de données GLUE MRPC. L'inspection de cet objet nous montre les colonnes présentes et la répartition entre les ensembles d'entraînement, de validation et de test :
+
+```py
+raw_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
+ num_rows: 14041
+ })
+ validation: Dataset({
+ features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
+ num_rows: 3250
+ })
+ test: Dataset({
+ features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
+ num_rows: 3453
+ })
+})
+```
+
+En particulier, nous pouvons voir que le jeu de données contient des étiquettes pour les trois tâches que nous avons mentionnées précédemment : NER, POS, et *chunking*. Une grande différence avec les autres jeux de données est que les textes d'entrée ne sont pas présentés comme des phrases ou des documents, mais comme des listes de mots (la dernière colonne est appelée `tokens`, mais elle contient des mots dans le sens où ce sont des entrées pré-tokénisées qui doivent encore passer par le *tokenizer* pour la tokenisation des sous-mots).
+
+Regardons le premier élément de l'ensemble d'entraînement :
+
+```py
+raw_datasets["train"][0]["tokens"]
+```
+
+```python out
+['EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'lamb', '.']
+```
+
+Puisque nous voulons effectuer la reconnaissance des entités nommées, nous allons examiner les balises NER :
+
+```py
+raw_datasets["train"][0]["ner_tags"]
+```
+
+```python out
+[3, 0, 7, 0, 0, 0, 7, 0, 0]
+```
+
+Ce sont les étiquettes sous forme d'entiers prêts pour l'entraînement, mais ils ne sont pas nécessairement utiles lorsque nous voulons inspecter les données. Comme pour la classification de texte, nous pouvons accéder à la correspondance entre ces entiers et les noms des étiquettes en regardant l'attribut `features` de notre jeu de données :
+
+```py
+ner_feature = raw_datasets["train"].features["ner_tags"]
+ner_feature
+```
+
+```python out
+Sequence(feature=ClassLabel(num_classes=9, names=['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC'], names_file=None, id=None), length=-1, id=None)
+```
+
+Cette colonne contient donc des éléments qui sont des séquences de `ClassLabel`s. Le type des éléments de la séquence se trouve dans l'attribut `feature` de cette `ner_feature`, et nous pouvons accéder à la liste des noms en regardant l'attribut `names` de cette `feature` :
+
+```py
+label_names = ner_feature.feature.names
+label_names
+```
+
+```python out
+['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC']
+```
+
+Nous avons déjà vu ces étiquettes en creusant dans le pipeline `token-classification` au [Chapitre 6](/course/fr/chapter6/3), mais pour un rapide rappel :
+
+- `O` signifie que le mot ne correspond à aucune entité.
+- `B-PER`/`I-PER` signifie que le mot correspond au début de/est à l'intérieur d'une entité *personne*.
+- `B-ORG`/`I-ORG` signifie que le mot correspond au début/à l'intérieur d'une entité *organisation*.
+- `B-LOC`/`I-LOC` signifie que le mot correspond au début/à l'intérieur d'une entité *location*.
+- `B-MISC`/`I-MISC` signifie que le mot correspond au début/à l'intérieur d'une entité *divers*.
+
+Maintenant, le décodage des étiquettes que nous avons vues précédemment nous donne ceci :
+
+```python
+words = raw_datasets["train"][0]["tokens"]
+labels = raw_datasets["train"][0]["ner_tags"]
+line1 = ""
+line2 = ""
+for word, label in zip(words, labels):
+ full_label = label_names[label]
+ max_length = max(len(word), len(full_label))
+ line1 += word + " " * (max_length - len(word) + 1)
+ line2 += full_label + " " * (max_length - len(full_label) + 1)
+
+print(line1)
+print(line2)
+```
+
+```python out
+'EU rejects German call to boycott British lamb .'
+'B-ORG O B-MISC O O O B-MISC O O'
+```
+
+Et pour un exemple mélangeant les étiquettes `B-` et `I-`, voici ce que le même code nous donne sur l'élément de l'ensemble d'entraînement à l'indice 4 :
+
+```python out
+'Germany \'s representative to the European Union \'s veterinary committee Werner Zwingmann said on Wednesday consumers should buy sheepmeat from countries other than Britain until the scientific advice was clearer .'
+'B-LOC O O O O B-ORG I-ORG O O O B-PER I-PER O O O O O O O O O O O B-LOC O O O O O O O'
+```
+
+Comme on peut le voir, les entités couvrant deux mots, comme "Union européenne" et "Werner Zwingmann", se voient attribuer une étiquette "B-" pour le premier mot et une étiquette "I-" pour le second.
+
+
+
+✏️ *Votre tour !* Affichez les deux mêmes phrases avec leurs étiquettes POS ou *chunking*.
+
+
+
+### Traitement des données
+
+
+
+Comme d'habitude, nos textes doivent être convertis en identifiants de *tokens* avant que le modèle puisse leur donner un sens. Comme nous l'avons vu dans le [Chapitre 6](/course/fr/chapter6/), une grande différence dans le cas des tâches de classification de *tokens* est que nous avons des entrées pré-tokénisées. Heureusement, l'API tokenizer peut gérer cela assez facilement ; nous devons juste avertir le `tokenizer` avec un drapeau spécial.
+
+Pour commencer, nous allons créer notre objet `tokenizer`. Comme nous l'avons dit précédemment, nous allons utiliser un modèle pré-entraîné BERT, donc nous allons commencer par télécharger et mettre en cache le tokenizer associé :
+
+```python
+from transformers import AutoTokenizer
+
+model_checkpoint = "bert-base-cased"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+```
+
+Vous pouvez remplacer le `model_checkpoint` par tout autre modèle que vous préférez à partir du [*Hub*]https://huggingface.co/models), ou par un dossier local dans lequel vous avez sauvegardé un modèle pré-entraîné et un *tokenizer*. La seule contrainte est que le *tokenizer* doit être soutenu par la bibliothèque 🤗 *Tokenizers*, il y a donc une version "rapide" disponible. Vous pouvez voir toutes les architectures qui ont une version rapide dans [ce grand tableau](https://huggingface.co/transformers/#supported-frameworks), et pour vérifier que l'objet `tokenizer` que vous utilisez est bien soutenu par 🤗 *Tokenizers* vous pouvez regarder son attribut `is_fast` :
+
+```py
+tokenizer.is_fast
+```
+
+```python out
+True
+```
+
+Pour tokeniser une entrée pré-tokenisée, nous pouvons utiliser notre `tokenizer` comme d'habitude et juste ajouter `is_split_into_words=True` :
+
+```py
+inputs = tokenizer(raw_datasets["train"][0]["tokens"], is_split_into_words=True)
+inputs.tokens()
+```
+
+```python out
+['[CLS]', 'EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'la', '##mb', '.', '[SEP]']
+```
+
+Comme on peut le voir, le *tokenizer* a ajouté les *tokens* spéciaux utilisés par le modèle (`[CLS]` au début et `[SEP]` à la fin) et n'a pas touché à la plupart des mots. Le mot `lamb`, cependant, a été tokenisé en deux sous-mots, `la` et `##mb`. Cela introduit un décalage entre nos entrées et les étiquettes : la liste des étiquettes n'a que 9 éléments, alors que notre entrée a maintenant 12 *tokens*. Il est facile de tenir compte des *tokens* spéciaux (nous savons qu'ils sont au début et à la fin), mais nous devons également nous assurer que nous alignons toutes les étiquettes avec les mots appropriés.
+
+Heureusement, comme nous utilisons un *tokenizer* rapide, nous avons accès aux superpouvoirs des 🤗 *Tokenizers*, ce qui signifie que nous pouvons facilement faire correspondre chaque *token* au mot correspondant (comme on le voit au [Chapitre 6](/course/fr/chapter6/3)) :
+
+```py
+inputs.word_ids()
+```
+
+```python out
+[None, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, None]
+```
+
+Avec un peu de travail, nous pouvons alors étendre notre liste d'étiquettes pour qu'elle corresponde aux *tokens*. La première règle que nous allons appliquer est que les *tokens* spéciaux reçoivent une étiquette de `-100`. En effet, par défaut, `-100` est un indice qui est ignoré dans la fonction de perte que nous allons utiliser (entropie croisée). Ensuite, chaque *token* reçoit la même étiquette que le *token* qui a commencé le mot dans lequel il se trouve, puisqu'ils font partie de la même entité. Pour les *tokens* à l'intérieur d'un mot mais pas au début, nous remplaçons le `B-` par `I-` (puisque le *token* ne commence pas l'entité) :
+
+```python
+def align_labels_with_tokens(labels, word_ids):
+ new_labels = []
+ current_word = None
+ for word_id in word_ids:
+ if word_id != current_word:
+ # Start of a new word!
+ current_word = word_id
+ label = -100 if word_id is None else labels[word_id]
+ new_labels.append(label)
+ elif word_id is None:
+ # Special token
+ new_labels.append(-100)
+ else:
+ # Same word as previous token
+ label = labels[word_id]
+ # If the label is B-XXX we change it to I-XXX
+ if label % 2 == 1:
+ label += 1
+ new_labels.append(label)
+
+ return new_labels
+```
+
+Essayons-le sur notre première phrase :
+
+```py
+labels = raw_datasets["train"][0]["ner_tags"]
+word_ids = inputs.word_ids()
+print(labels)
+print(align_labels_with_tokens(labels, word_ids))
+```
+
+```python out
+[3, 0, 7, 0, 0, 0, 7, 0, 0]
+[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100]
+```
+
+Comme nous pouvons le voir, notre fonction a ajouté le `-100` pour les deux *tokens* spéciaux au début et à la fin, et un nouveau `0` pour notre mot qui a été divisé en deux *tokens*.
+
+
+
+✏️ *Votre tour !* Certains chercheurs préfèrent n'attribuer qu'un seul label par mot, et attribuer `-100` aux autres sous-*tokens* dans un mot donné. Ceci afin d'éviter que les longs mots qui se divisent en plusieurs batchs ne contribuent fortement à la perte. Changez la fonction précédente pour aligner les étiquettes avec les ID d'entrée en suivant cette règle.
+
+
+Pour prétraiter notre ensemble de données, nous devons tokeniser toutes les entrées et appliquer `align_labels_with_tokens()` sur toutes les étiquettes. Pour profiter de la vitesse de notre *tokenizer* rapide, il est préférable de tokeniser beaucoup de textes en même temps, donc nous allons écrire une fonction qui traite une liste d'exemples et utiliser la méthode `Dataset.map()` avec l'option `batched=True`. La seule chose qui diffère de notre exemple précédent est que la fonction `word_ids()` a besoin de récupérer l'index de l'exemple dont nous voulons les IDs de mots lorsque les entrées du *tokenizer* sont des listes de textes (ou dans notre cas, des listes de mots), donc nous l'ajoutons aussi :
+
+```py
+def tokenize_and_align_labels(examples):
+ tokenized_inputs = tokenizer(
+ examples["tokens"], truncation=True, is_split_into_words=True
+ )
+ all_labels = examples["ner_tags"]
+ new_labels = []
+ for i, labels in enumerate(all_labels):
+ word_ids = tokenized_inputs.word_ids(i)
+ new_labels.append(align_labels_with_tokens(labels, word_ids))
+
+ tokenized_inputs["labels"] = new_labels
+ return tokenized_inputs
+```
+
+Notez que nous n'avons pas encore paddé nos entrées ; nous le ferons plus tard, lors de la création des lots avec un collateur de données.
+
+Nous pouvons maintenant appliquer tout ce prétraitement en une seule fois sur les autres divisions de notre jeu de données :
+
+```py
+tokenized_datasets = raw_datasets.map(
+ tokenize_and_align_labels,
+ batched=True,
+ remove_columns=raw_datasets["train"].column_names,
+)
+```
+
+Nous avons fait la partie la plus difficile ! Maintenant que les données ont été prétraitées, l'entraînement réel ressemblera beaucoup à ce que nous avons fait dans le [Chapitre 3](/course/fr/chapter3).
+
+{#if fw === 'pt'}
+
+## *Finetuning* du modèle avec l'API `Trainer`.
+
+Le code actuel utilisant le `Trainer` sera le même que précédemment ; les seuls changements sont la façon dont les données sont rassemblées dans un batch et la fonction de calcul de la métrique.
+
+{:else}
+
+## *Finetuning* fin du modèle avec Keras
+
+Le code réel utilisant Keras sera très similaire au précédent ; les seuls changements sont la façon dont les données sont rassemblées dans un batch et la fonction de calcul de la métrique.
+
+{/if}
+
+
+### Collation des données
+
+Nous ne pouvons pas simplement utiliser un `DataCollatorWithPadding` comme dans [Chapter 3](/course/fr/chapter3) parce que cela ne fait que rembourrer les entrées (IDs d'entrée, masque d'attention, et IDs de type de *token*). Ici, nos étiquettes doivent être remplies exactement de la même manière que les entrées afin qu'elles gardent la même taille, en utilisant `-100` comme valeur afin que les prédictions correspondantes soient ignorées dans le calcul de la perte.
+
+Tout ceci est fait par un [`DataCollatorForTokenClassification`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorfortokenclassification). Comme le `DataCollatorWithPadding`, il prend le `tokenizer` utilisé pour prétraiter les entrées :
+
+{#if fw === 'pt'}
+
+```py
+from transformers import DataCollatorForTokenClassification
+
+data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)
+```
+
+{:else}
+
+```py
+from transformers import DataCollatorForTokenClassification
+
+data_collator = DataCollatorForTokenClassification(
+ tokenizer=tokenizer, return_tensors="tf"
+)
+```
+
+{/if}
+
+Pour tester cette fonction sur quelques échantillons, nous pouvons simplement l'appeler sur une liste d'exemples provenant de notre jeu d'entraînement tokénisé :
+
+```py
+batch = data_collator([tokenized_datasets["train"][i] for i in range(2)])
+batch["labels"]
+```
+
+```python out
+tensor([[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100],
+ [-100, 1, 2, -100, -100, -100, -100, -100, -100, -100, -100, -100]])
+```
+
+Comparons cela aux étiquettes des premier et deuxième éléments de notre jeu de données :
+
+```py
+for i in range(2):
+ print(tokenized_datasets["train"][i]["labels"])
+```
+
+```python out
+[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100]
+[-100, 1, 2, -100]
+```
+
+{#if fw === 'pt'}
+
+Comme nous pouvons le voir, le deuxième jeu d'étiquettes a été complété à la longueur du premier en utilisant `-100`s.
+
+{:else}
+
+Notre collateur de données est prêt à fonctionner ! Maintenant, utilisons-le pour créer un `tf.data.Dataset` avec la méthode `to_tf_dataset()`.
+
+```py
+tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
+ columns=["attention_mask", "input_ids", "labels", "token_type_ids"],
+ collate_fn=data_collator,
+ shuffle=True,
+ batch_size=16,
+)
+
+tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
+ columns=["attention_mask", "input_ids", "labels", "token_type_ids"],
+ collate_fn=data_collator,
+ shuffle=False,
+ batch_size=16,
+)
+```
+
+
+ Prochain arrêt : le modèle lui-même.
+
+{/if}
+
+{#if fw === 'tf'}
+
+### Définir le modèle
+
+Puisque nous travaillons sur un problème de classification de *tokens*, nous allons utiliser la classe `TFAutoModelForTokenClassification`. La principale chose à retenir lors de la définition de ce modèle est de transmettre des informations sur le nombre de labels que nous avons. La façon la plus simple de le faire est de passer ce nombre avec l'argument `num_labels`, mais si nous voulons un joli *widget* d'inférence fonctionnant comme celui que nous avons vu au début de cette section, il est préférable de définir les correspondances correctes des étiquettes à la place.
+
+Elles devraient être définies par deux dictionnaires, `id2label` et `label2id`, qui contiennent la correspondance de l'ID au label et vice versa :
+
+```py
+id2label = {str(i): label for i, label in enumerate(label_names)}
+label2id = {v: k for k, v in id2label.items()}
+```
+
+Maintenant, nous pouvons simplement les passer à la méthode `TFAutoModelForTokenClassification.from_pretrained()`, et ils seront définis dans la configuration du modèle, puis correctement enregistrés et téléchargés vers le *Hub* :
+
+```py
+from transformers import TFAutoModelForTokenClassification
+
+model = TFAutoModelForTokenClassification.from_pretrained(
+ model_checkpoint,
+ id2label=id2label,
+ label2id=label2id,
+)
+```
+
+Comme lorsque nous avons défini notre `TFAutoModelForSequenceClassification` au [Chapitre 3](/course/fr/chapter3), la création du modèle émet un avertissement indiquant que certains poids n'ont pas été utilisés (ceux de la tête de pré-entraînement) et que d'autres poids ont été initialisés de manière aléatoire (ceux de la tête de classification des nouveaux *tokens*), et que ce modèle doit être entraîné. Nous ferons cela dans une minute, mais vérifions d'abord que notre modèle a le bon nombre d'étiquettes :
+
+```python
+model.config.num_labels
+```
+
+```python out
+9
+```
+
+
+
+⚠️ Si vous avez un modèle avec le mauvais nombre de labels, vous obtiendrez une erreur obscure en appelant `model.fit()` plus tard. Cela peut être ennuyeux à déboguer, donc assurez-vous de faire cette vérification pour confirmer que vous avez le nombre de labels attendu.
+
+
+
+### *Finetuning* du modèle
+
+Nous sommes maintenant prêts à entraîner notre modèle ! Mais nous devons d'abord faire un peu de ménage : nous devons nous connecter à Hugging Face et définir nos hyperparamètres d'entraînement. Si vous travaillez dans un *notebook*, il y a une fonction pratique pour vous aider à le faire :
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Cela affichera un *widget* où vous pourrez entrer vos identifiants de connexion à Hugging Face.
+
+Si vous ne travaillez pas dans un *notebook*, tapez simplement la ligne suivante dans votre terminal :
+
+```bash
+huggingface-cli login
+```
+
+Après s'être connecté, nous pouvons préparer tout ce dont nous avons besoin pour compiler notre modèle. 🤗 *Transformers* fournit une fonction pratique `create_optimizer()` qui vous donnera un optimiseur `AdamW` avec des paramètres appropriés pour la décroissance du taux des poids et la décroissance du taux d'apprentissage, les deux améliorant les performances de votre modèle par rapport à l'optimiseur `Adam` intégré :
+
+```python
+from transformers import create_optimizer
+import tensorflow as tf
+
+# Entraîner en mixed-precision float16
+# Commentez cette ligne si vous utilisez un GPU qui ne bénéficiera pas de cette fonction
+tf.keras.mixed_precision.set_global_policy("mixed_float16")
+
+# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans l'ensemble de données, divisé par la taille du batch puis multiplié par le nombre total d'époques
+# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un batchtf.data.Dataset,
+# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size
+num_epochs = 3
+num_train_steps = len(tf_train_dataset) * num_epochs
+
+optimizer, schedule = create_optimizer(
+ init_lr=2e-5,
+ num_warmup_steps=0,
+ num_train_steps=num_train_steps,
+ weight_decay_rate=0.01,
+)
+model.compile(optimizer=optimizer)
+```
+
+Notez également que nous ne fournissons pas d'argument `loss` à `compile()`. C'est parce que les modèles peuvent en fait calculer la perte en interne. Si vous compilez sans perte et fournissez vos étiquettes dans le dictionnaire d'entrée (comme nous le faisons dans nos jeux de données), alors le modèle s'entraînera en utilisant cette perte interne, qui sera appropriée pour la tâche et le type de modèle que vous avez choisi.
+
+Ensuite, nous définissons un `PushToHubCallback` pour télécharger notre modèle vers le *Hub* pendant l'entraînement, et nous ajustons le modèle avec ce *callback* :
+
+```python
+from transformers.keras_callbacks import PushToHubCallback
+
+callback = PushToHubCallback(output_dir="bert-finetuned-ner", tokenizer=tokenizer)
+
+model.fit(
+ tf_train_dataset,
+ validation_data=tf_eval_dataset,
+ callbacks=[callback],
+ epochs=num_epochs,
+)
+```
+
+Vous pouvez spécifier le nom complet du référentiel vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/bert-finetuned-ner"`. Par défaut, le dépôt utilisé sera dans votre espace de noms et nommé après le répertoire de sortie que vous avez défini, par exemple `"cool_huggingface_user/bert-finetuned-ner"`.
+
+
+
+💡 Si le répertoire de sortie que vous utilisez existe déjà, il doit être un clone local du dépôt vers lequel vous voulez pousser. S'il ne l'est pas, vous obtiendrez une erreur lors de l'appel de `model.fit()` et devrez définir un nouveau nom.
+
+
+
+Notez que pendant l'entraînement, chaque fois que le modèle est sauvegardé (ici, à chaque époque), il est téléchargé sur le *Hub* en arrière-plan. De cette façon, vous pourrez reprendre votre entraînement sur une autre machine si nécessaire.
+
+A ce stade, vous pouvez utiliser le *widget* d'inférence sur le *Hub* pour tester votre modèle et le partager avec vos amis. Vous avez réussi à *finetuner* un modèle sur une tâche de classification de *tokens*. Félicitations ! Mais quelle est la qualité réelle de notre modèle ? Nous devons évaluer certaines métriques pour le découvrir.
+
+{/if}
+
+
+### Métriques
+
+{#if fw === 'pt'}
+
+Pour que le `Trainer` calcule une métrique à chaque époque, nous devrons définir une fonction `compute_metrics()` qui prend les tableaux de prédictions et de labels, et retourne un dictionnaire avec les noms et les valeurs des métriques.
+
+Le cadre traditionnel utilisé pour évaluer la prédiction de la classification des *tokens* est [*seqeval*](https://github.com/chakki-works/seqeval). Pour utiliser cette métrique, nous devons d'abord installer la bibliothèque *seqeval* :
+
+```py
+!pip install seqeval
+```
+
+Nous pouvons ensuite le charger via la fonction `load_metric()` comme nous l'avons fait dans le [Chapitre 3](/course/fr/chapter3) :
+
+{:else}
+
+Le cadre traditionnel utilisé pour évaluer la prédiction de la classification des *tokens* est [*seqeval*](https://github.com/chakki-works/seqeval). Pour utiliser cette métrique, nous devons d'abord installer la bibliothèque *seqeval* :
+
+```py
+!pip install seqeval
+```
+
+Nous pouvons ensuite le charger via la fonction `load_metric()` comme nous l'avons fait dans le [Chapitre 3](/course/fr/chapter3) :
+
+{/if}
+
+```py
+from datasets import load_metric
+
+metric = load_metric("seqeval")
+```
+
+Cette métrique ne se comporte pas comme la précision standard : elle prend les listes d'étiquettes comme des chaînes de caractères et non comme des entiers. Nous devrons donc décoder complètement les prédictions et les étiquettes avant de les transmettre à la métrique. Voyons comment cela fonctionne. Tout d'abord, nous allons obtenir les étiquettes pour notre premier exemple d'entraînement :
+
+```py
+labels = raw_datasets["train"][0]["ner_tags"]
+labels = [label_names[i] for i in labels]
+labels
+```
+
+```python out
+['B-ORG', 'O', 'B-MISC', 'O', 'O', 'O', 'B-MISC', 'O', 'O']
+```
+
+Nous pouvons alors créer de fausses prédictions pour celles-ci en changeant simplement la valeur de l'indice 2 :
+
+```py
+predictions = labels.copy()
+predictions[2] = "O"
+metric.compute(predictions=[predictions], references=[labels])
+```
+
+Notez que la métrique prend une liste de prédictions (pas seulement une) et une liste d'étiquettes. Voici la sortie :
+
+```python out
+{'MISC': {'precision': 1.0, 'recall': 0.5, 'f1': 0.67, 'number': 2},
+ 'ORG': {'precision': 1.0, 'recall': 1.0, 'f1': 1.0, 'number': 1},
+ 'overall_precision': 1.0,
+ 'overall_recall': 0.67,
+ 'overall_f1': 0.8,
+ 'overall_accuracy': 0.89}
+```
+
+{#if fw === 'pt'}
+
+Cela renvoie un batch d'informations ! Nous obtenons la précision, le rappel, et le score F1 pour chaque entité séparée, ainsi que le score global. Pour notre calcul de métrique, nous ne garderons que le score global, mais n'hésitez pas à modifier la fonction `compute_metrics()` pour retourner toutes les métriques que vous souhaitez.
+
+Cette fonction `compute_metrics()` prend d'abord l'argmax des logits pour les convertir en prédictions (comme d'habitude, les logits et les probabilités sont dans le même ordre, donc nous n'avons pas besoin d'appliquer le softmax). Ensuite, nous devons convertir les étiquettes et les prédictions des entiers en chaînes de caractères. Nous supprimons toutes les valeurs dont l'étiquette est `-100`, puis nous passons les résultats à la méthode `metric.compute()` :
+
+```py
+import numpy as np
+
+
+def compute_metrics(eval_preds):
+ logits, labels = eval_preds
+ predictions = np.argmax(logits, axis=-1)
+
+ # Suppression de l'index ignoré (tokens spéciaux) et conversion en étiquettes
+ true_labels = [[label_names[l] for l in label if l != -100] for label in labels]
+ true_predictions = [
+ [label_names[p] for (p, l) in zip(prediction, label) if l != -100]
+ for prediction, label in zip(predictions, labels)
+ ]
+ all_metrics = metric.compute(predictions=true_predictions, references=true_labels)
+ return {
+ "precision": all_metrics["overall_precision"],
+ "recall": all_metrics["overall_recall"],
+ "f1": all_metrics["overall_f1"],
+ "accuracy": all_metrics["overall_accuracy"],
+ }
+```
+
+Maintenant que ceci est fait, nous sommes presque prêts à définir notre `Trainer`. Nous avons juste besoin d'un `modèle` pour *finetuner* !
+
+{:else}
+
+Cela renvoie un batch d'informations ! Nous obtenons la précision, le rappel et le score F1 pour chaque entité séparée, ainsi que pour l'ensemble. Voyons maintenant ce qui se passe si nous essayons d'utiliser les prédictions de notre modèle pour calculer des scores réels.
+
+TensorFlow n'aime pas concaténer nos prédictions ensemble, car elles ont des longueurs de séquence variables. Cela signifie que nous ne pouvons pas simplement utiliser `model.predict()`. Mais cela ne va pas nous arrêter. Nous obtiendrons des prédictions un batch à la fois et les concaténerons en une grande liste longue au fur et à mesure, en laissant tomber les *tokens* `-100` qui indiquent le masquage/le remplissage, puis nous calculerons les métriques sur la liste à la fin :
+
+```py
+import numpy as np
+
+all_predictions = []
+all_labels = []
+for batch in tf_eval_dataset:
+ logits = model.predict(batch)["logits"]
+ labels = batch["labels"]
+ predictions = np.argmax(logits, axis=-1)
+ for prediction, label in zip(predictions, labels):
+ for predicted_idx, label_idx in zip(prediction, label):
+ if label_idx == -100:
+ continue
+ all_predictions.append(label_names[predicted_idx])
+ all_labels.append(label_names[label_idx])
+metric.compute(predictions=[all_predictions], references=[all_labels])
+```
+
+
+```python out
+{'LOC': {'precision': 0.91, 'recall': 0.92, 'f1': 0.91, 'number': 1668},
+ 'MISC': {'precision': 0.70, 'recall': 0.79, 'f1': 0.74, 'number': 702},
+ 'ORG': {'precision': 0.85, 'recall': 0.90, 'f1': 0.88, 'number': 1661},
+ 'PER': {'precision': 0.95, 'recall': 0.95, 'f1': 0.95, 'number': 1617},
+ 'overall_precision': 0.87,
+ 'overall_recall': 0.91,
+ 'overall_f1': 0.89,
+ 'overall_accuracy': 0.97}
+```
+
+Comment s'est comporté votre modèle, comparé au nôtre ? Si vous avez obtenu des chiffres similaires, votre entraînement a été un succès !
+
+{/if}
+
+{#if fw === 'pt'}
+
+### Définir le modèle
+
+Puisque nous travaillons sur un problème de classification de *tokens*, nous allons utiliser la classe `AutoModelForTokenClassification`. La principale chose à retenir lors de la définition de ce modèle est de transmettre des informations sur le nombre de labels que nous avons. La façon la plus simple de le faire est de passer ce nombre avec l'argument `num_labels`, mais si nous voulons un joli *widget* d'inférence fonctionnant comme celui que nous avons vu au début de cette section, il est préférable de définir les correspondances correctes des étiquettes à la place.
+
+Elles devraient être définies par deux dictionnaires, `id2label` et `label2id`, qui contiennent les correspondances entre ID et label et vice versa :
+
+```py
+id2label = {str(i): label for i, label in enumerate(label_names)}
+label2id = {v: k for k, v in id2label.items()}
+```
+
+Maintenant nous pouvons simplement les passer à la méthode `AutoModelForTokenClassification.from_pretrained()`, et ils seront définis dans la configuration du modèle et ensuite correctement sauvegardés et téléchargés vers le *Hub* :
+
+```py
+from transformers import AutoModelForTokenClassification
+
+model = AutoModelForTokenClassification.from_pretrained(
+ model_checkpoint,
+ id2label=id2label,
+ label2id=label2id,
+)
+```
+
+Comme lorsque nous avons défini notre `AutoModelForSequenceClassification` au [Chapitre 3](/course/fr/chapter3), la création du modèle émet un avertissement indiquant que certains poids n'ont pas été utilisés (ceux de la tête de pré-entraînement) et que d'autres poids ont été initialisés de manière aléatoire (ceux de la tête de classification des nouveaux *tokens*), et que ce modèle doit être entraîné. Nous ferons cela dans une minute, mais vérifions d'abord que notre modèle a le bon nombre d'étiquettes :
+
+```python
+model.config.num_labels
+```
+
+```python out
+9
+```
+
+
+
+⚠️ Si vous avez un modèle avec le mauvais nombre d'étiquettes, vous obtiendrez une erreur obscure lors de l'appel de la méthode `Trainer.train()` plus tard (quelque chose comme "CUDA error : device-side assert triggered"). C'est la première cause de bogues signalés par les utilisateurs pour de telles erreurs, donc assurez-vous de faire cette vérification pour confirmer que vous avez le nombre d'étiquettes attendu.
+
+
+
+### *Finetuning* du modèle
+
+Nous sommes maintenant prêts à entraîner notre modèle ! Nous devons juste faire deux dernières choses avant de définir notre `Trainer` : se connecter à Hugging Face et définir nos arguments d'entraînement. Si vous travaillez dans un *notebook*, il y a une fonction pratique pour vous aider à le faire :
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Cela affichera un *widget* où vous pourrez entrer vos identifiants de connexion à Hugging Face.
+
+Si vous ne travaillez pas dans un *notebook*, tapez simplement la ligne suivante dans votre terminal :
+
+```bash
+huggingface-cli login
+```
+
+Une fois ceci fait, nous pouvons définir nos `TrainingArguments` :
+
+```python
+from transformers import TrainingArguments
+
+args = TrainingArguments(
+ "bert-finetuned-ner",
+ evaluation_strategy="epoch",
+ save_strategy="epoch",
+ learning_rate=2e-5,
+ num_train_epochs=3,
+ weight_decay=0.01,
+ push_to_hub=True,
+)
+```
+
+Vous avez déjà vu la plupart d'entre eux : nous définissons quelques hyperparamètres (comme le taux d'apprentissage, le nombre d'époques à entraîner, et la décroissance du poids), et nous spécifions `push_to_hub=True` pour indiquer que nous voulons sauvegarder le modèle et l'évaluer à la fin de chaque époque, et que nous voulons télécharger nos résultats vers le *Hub*. Notez que vous pouvez spécifier le nom du référentiel vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/bert-finetuned-ner"``TrainingArguments`. Par défaut, le référentiel utilisé sera dans votre espace de noms et nommé d'après le répertoire de sortie que vous avez défini, donc dans notre cas ce sera `"sgugger/bert-finetuned-ner"`.
+
+
+
+💡 Si le répertoire de sortie que vous utilisez existe déjà, il doit être un clone local du dépôt vers lequel vous voulez pousser. S'il ne l'est pas, vous obtiendrez une erreur lors de la définition de votre `Trainer` et devrez définir un nouveau nom.
+
+
+
+Enfin, nous passons tout au `Trainer` et lançons l'entraînement :
+
+```python
+from transformers import Trainer
+
+trainer = Trainer(
+ model=model,
+ args=args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ compute_metrics=compute_metrics,
+ tokenizer=tokenizer,
+)
+trainer.train()
+```
+
+Notez que pendant l'entraînement, chaque fois que le modèle est sauvegardé (ici, à chaque époque), il est téléchargé sur le *Hub* en arrière-plan. De cette façon, vous serez en mesure de reprendre votre entraînement sur une autre machine si nécessaire.
+
+Une fois l'entraînement terminé, nous utilisons la méthode `push_to_hub()` pour nous assurer que nous téléchargeons la version la plus récente du modèle :
+
+```py
+trainer.push_to_hub(commit_message="Training complete")
+```
+
+Cette commande renvoie l'URL du commit qu'elle vient de faire, si vous voulez l'inspecter :
+
+```python out
+'https://huggingface.co/sgugger/bert-finetuned-ner/commit/26ab21e5b1568f9afeccdaed2d8715f571d786ed'
+```
+
+Le `Trainer` rédige également une carte modèle avec tous les résultats de l'évaluation et la télécharge. A ce stade, vous pouvez utiliser le *widget* d'inférence sur le *Hub* pour tester votre modèle et le partager avec vos amis. Vous avez réussi à affiner un modèle sur une tâche de classification de *tokens*. Félicitations !
+
+Si vous voulez plonger un peu plus profondément dans la boucle d'entraînement, nous allons maintenant vous montrer comment faire la même chose en utilisant 🤗 *Accelerate*.
+
+## Une boucle d'entraînement personnalisée
+
+Jetons maintenant un coup d'œil à la boucle d'entraînement complète, afin que vous puissiez facilement personnaliser les parties dont vous avez besoin. Elle ressemblera beaucoup à ce que nous avons fait dans le [Chapitre 3](/course/fr/chapter3/4), avec quelques changements pour l'évaluation.
+
+### Préparer tout pour l'entraînement
+
+D'abord nous devons construire le `DataLoader`s à partir de nos jeux de données. Nous allons réutiliser notre `data_collator` comme un `collate_fn` et mélanger l'ensemble d'entraînement, mais pas l'ensemble de validation :
+
+```py
+from torch.utils.data import DataLoader
+
+train_dataloader = DataLoader(
+ tokenized_datasets["train"],
+ shuffle=True,
+ collate_fn=data_collator,
+ batch_size=8,
+)
+eval_dataloader = DataLoader(
+ tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
+)
+```
+
+Ensuite, nous réinstantifions notre modèle, pour nous assurer que nous ne continuons pas le réglage fin d'avant, mais que nous repartons du modèle pré-entraîné de BERT :
+
+```py
+model = AutoModelForTokenClassification.from_pretrained(
+ model_checkpoint,
+ id2label=id2label,
+ label2id=label2id,
+)
+```
+
+Ensuite, nous aurons besoin d'un optimiseur. Nous allons utiliser le classique `AdamW`, qui est comme `Adam`, mais avec un correctif dans la façon dont la décroissance du taux des poids est appliquée :
+
+```py
+from torch.optim import AdamW
+
+optimizer = AdamW(model.parameters(), lr=2e-5)
+```
+
+Une fois que nous avons tous ces objets, nous pouvons les envoyer à la méthode `accelerator.prepare()` :
+
+```py
+from accelerate import Accelerator
+
+accelerator = Accelerator()
+model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
+ model, optimizer, train_dataloader, eval_dataloader
+)
+```
+
+
+
+🚨 Si vous vous entraînez sur un TPU, vous devrez déplacer tout le code à partir de la cellule ci-dessus dans une fonction d'entraînement dédiée. Voir le [Chapitre 3](/course/fr/chapter3) pour plus de détails.
+
+
+
+Maintenant que nous avons envoyé notre `train_dataloader` à `accelerator.prepare()`, nous pouvons utiliser sa longueur pour calculer le nombre d'étapes d'entraînement. Rappelez-vous que nous devrions toujours faire cela après avoir préparé le *dataloader*, car cette méthode modifiera sa longueur. Nous utilisons un programme linéaire classique du taux d'apprentissage à 0 :
+
+```py
+from transformers import get_scheduler
+
+num_train_epochs = 3
+num_update_steps_per_epoch = len(train_dataloader)
+num_training_steps = num_train_epochs * num_update_steps_per_epoch
+
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+```
+
+Enfin, pour pousser notre modèle vers le Hub, nous aurons besoin de créer un objet `Repository` dans un dossier de travail. Tout d'abord, connectez-vous à Hugging Face, si vous n'êtes pas déjà connecté. Nous déterminerons le nom du dépôt à partir de l'ID du modèle que nous voulons donner à notre modèle (n'hésitez pas à remplacer le `repo_name` par votre propre choix ; il doit juste contenir votre nom d'utilisateur, ce que fait la fonction `get_full_repo_name()`) :
+
+```py
+from huggingface_hub import Repository, get_full_repo_name
+
+model_name = "bert-finetuned-ner-accelerate"
+repo_name = get_full_repo_name(model_name)
+repo_name
+```
+
+```python out
+'sgugger/bert-finetuned-ner-accelerate'
+```
+
+Ensuite, nous pouvons cloner ce référentiel dans un dossier local. S'il existe déjà, ce dossier local doit être un clone existant du référentiel avec lequel nous travaillons :
+
+```py
+output_dir = "bert-finetuned-ner-accelerate"
+repo = Repository(output_dir, clone_from=repo_name)
+```
+
+Nous pouvons maintenant télécharger tout ce que nous sauvegardons dans `output_dir` en appelant la méthode `repo.push_to_hub()`. Cela nous aidera à télécharger les modèles intermédiaires à la fin de chaque époque.
+
+### Boucle d'entraînement
+
+Nous sommes maintenant prêts à écrire la boucle d'entraînement complète. Pour simplifier sa partie évaluation, nous définissons cette fonction `postprocess()` qui prend les prédictions et les étiquettes et les convertit en listes de chaînes de caractères, comme notre objet `metric` l'attend :
+
+```py
+def postprocess(predictions, labels):
+ predictions = predictions.detach().cpu().clone().numpy()
+ labels = labels.detach().cpu().clone().numpy()
+
+ # Remove ignored index (special tokens) and convert to labels
+ true_labels = [[label_names[l] for l in label if l != -100] for label in labels]
+ true_predictions = [
+ [label_names[p] for (p, l) in zip(prediction, label) if l != -100]
+ for prediction, label in zip(predictions, labels)
+ ]
+ return true_labels, true_predictions
+```
+
+Ensuite, nous pouvons écrire la boucle d'entraînement. Après avoir défini une barre de progression pour suivre l'évolution de l'entraînement, la boucle comporte trois parties :
+
+- l'entraînement proprement dit, qui est l'itération classique sur le `train_dataloader`, passage en avant du modèle, puis passage en arrière et étape d'optimisation,
+- l'évaluation, dans laquelle il y a une nouveauté après avoir obtenu les sorties de notre modèle sur un lot : puisque deux processus peuvent avoir paddé les entrées et les étiquettes à des formes différentes, nous devons utiliser `accelerator.pad_across_processes()` pour rendre les prédictions et les étiquettes de la même forme avant d'appeler la méthode `gather()`. Si nous ne le faisons pas, l'évaluation va soit se tromper, soit se bloquer pour toujours. Ensuite, nous envoyons les résultats à `metric.add_batch()` et appelons `metric.compute()` une fois que la boucle d'évaluation est terminée,
+- sauvegarde et téléchargement, où nous sauvegardons d'abord le modèle et le tokenizer, puis appelons `repo.push_to_hub()`. Remarquez que nous utilisons l'argument `blocking=False` pour indiquer à la bibliothèque 🤗 *Hub* de pousser dans un processus asynchrone. De cette façon, l'entraînement continue normalement et cette (longue) instruction est exécutée en arrière-plan.
+
+Voici le code complet de la boucle d'entraînement :
+
+```py
+from tqdm.auto import tqdm
+import torch
+
+progress_bar = tqdm(range(num_training_steps))
+
+for epoch in range(num_train_epochs):
+ # Entraînement
+ model.train()
+ for batch in train_dataloader:
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+
+ # Evaluation
+ model.eval()
+ for batch in eval_dataloader:
+ with torch.no_grad():
+ outputs = model(**batch)
+
+ predictions = outputs.logits.argmax(dim=-1)
+ labels = batch["labels"]
+
+ # Nécessaire pour rembourrer les prédictions et les étiquettes à rassembler
+ predictions = accelerator.pad_across_processes(predictions, dim=1, pad_index=-100)
+ labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)
+
+ predictions_gathered = accelerator.gather(predictions)
+ labels_gathered = accelerator.gather(labels)
+
+ true_predictions, true_labels = postprocess(predictions_gathered, labels_gathered)
+ metric.add_batch(predictions=true_predictions, references=true_labels)
+
+ results = metric.compute()
+ print(
+ f"epoch {epoch}:",
+ {
+ key: results[f"overall_{key}"]
+ for key in ["precision", "recall", "f1", "accuracy"]
+ },
+ )
+
+ # Sauvegarder et télécharger
+ accelerator.wait_for_everyone()
+ unwrapped_model = accelerator.unwrap_model(model)
+ unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
+ if accelerator.is_main_process:
+ tokenizer.save_pretrained(output_dir)
+ repo.push_to_hub(
+ commit_message=f"Training in progress epoch {epoch}", blocking=False
+ )
+```
+
+Au cas où ce serait la première fois que vous verriez un modèle enregistré avec 🤗 *Accelerate*, prenons un moment pour inspecter les trois lignes de code qui l'accompagnent :
+
+```py
+accelerator.wait_for_everyone()
+unwrapped_model = accelerator.unwrap_model(model)
+unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
+```
+
+La première ligne est explicite : elle indique à tous les processus d'attendre que tout le monde soit à ce stade avant de continuer. C'est pour s'assurer que nous avons le même modèle dans chaque processus avant de sauvegarder. Ensuite, nous prenons le `unwrapped_model`, qui est le modèle de base que nous avons défini. La méthode `accelerator.prepare()` modifie le modèle pour qu'il fonctionne dans l'entraînement distribué, donc il n'aura plus la méthode `save_pretrained()` ; la méthode `accelerator.unwrap_model()` annule cette étape. Enfin, nous appelons `save_pretrained()` mais nous disons à cette méthode d'utiliser `accelerator.save()` au lieu de `torch.save()`.
+
+Une fois ceci fait, vous devriez avoir un modèle qui produit des résultats assez similaires à celui entraîné avec le `Trainer`. Vous pouvez vérifier le modèle que nous avons formé en utilisant ce code à [*huggingface-course/bert-finetuned-ner-accelerate*(https://huggingface.co/huggingface-course/bert-finetuned-ner-accelerate). Et si vous voulez tester des modifications de la boucle d'entraînement, vous pouvez les implémenter directement en modifiant le code ci-dessus !
+
+{/if}
+
+### Utilisation du modèle *finetuné*
+
+Nous vous avons déjà montré comment vous pouvez utiliser le modèle que nous avons affiné sur le *Hub* avec le *widget* d'inférence. Pour l'utiliser localement dans un `pipeline`, vous devez juste spécifier l'identifiant de modèle approprié :
+
+```py
+from transformers import pipeline
+
+# Remplacez ceci par votre propre checkpoint
+model_checkpoint = "huggingface-course/bert-finetuned-ner"
+token_classifier = pipeline(
+ "token-classification", model=model_checkpoint, aggregation_strategy="simple"
+)
+token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+```python out
+[{'entity_group': 'PER', 'score': 0.9988506, 'word': 'Sylvain', 'start': 11, 'end': 18},
+ {'entity_group': 'ORG', 'score': 0.9647625, 'word': 'Hugging Face', 'start': 33, 'end': 45},
+ {'entity_group': 'LOC', 'score': 0.9986118, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+Super ! Notre modèle fonctionne aussi bien que le modèle par défaut pour ce pipeline !
diff --git a/chapters/fr/chapter7/4.mdx b/chapters/fr/chapter7/4.mdx
index cfc6af14e..48d83a6da 100644
--- a/chapters/fr/chapter7/4.mdx
+++ b/chapters/fr/chapter7/4.mdx
@@ -1,999 +1,999 @@
-
-
-# Traduction
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Plongeons maintenant dans la traduction. Il s'agit d'une autre [tâche de séquence à séquence](/cours/fr/chapitre1/7), ce qui signifie que c'est un problème qui peut être formulé comme le passage d'une séquence à une autre. En ce sens, le problème est assez proche de la tâche de [résumé](/cours/fr/chapitre7/6) et vous pouvez adapter ce que nous allons voir ici à d'autres problèmes de séquence à séquence tels que :
-
-- le **transfert de style** : créer un modèle qui *traduit* des textes écrits dans un certain style vers un autre (par exemple, du formel au décontracté ou de l'anglais shakespearien à l'anglais moderne).
-- la **génération de réponse à des questions** : Création d'un modèle qui génère des réponses à des questions, compte tenu d'un contexte.
-
-
-
-Si vous disposez d'un corpus suffisamment important de textes en deux langues (ou plus), vous pouvez entraîner un nouveau modèle de traduction à partir de zéro, comme nous le ferons dans la section sur la [modélisation causale du langage](/cours/fr/chapitre7/6). Il est toutefois plus rapide de *finetuner* un modèle de traduction existant, qu'il s'agisse d'un modèle multilingue comme mT5 ou mBART que vous souhaitez adapter à une paire de langues spécifique, ou même d'un modèle spécialisé dans la traduction d'une langue vers une autre que vous souhaitez adapter à votre corpus spécifique.
-
-Dans cette section, nous allons *finetuner* un modèle Marian pré-entraîné pour traduire de l'anglais au français (puisque de nombreux employés de Hugging Face parlent ces deux langues) sur le [KDE4 dataset](https://huggingface.co/datasets/kde4), qui est un jeu de données de fichiers localisés pour les [KDE apps](https://apps.kde.org/). Le modèle que nous utiliserons a été pré-entraîné sur un large corpus de textes français et anglais provenant du [jeu de données Opus](https://opus.nlpl.eu/), qui contient en fait le jeu de données KDE4. Mais même si le modèle pré-entraîné que nous utilisons a vu ces données pendant son pré-entraînement, nous verrons que nous pouvons obtenir une meilleure version de ce modèle après un *finetuning*.
-
-Une fois que nous aurons terminé, nous aurons un modèle capable de faire des prédictions comme celle-ci :
-
-
-
-
-
-
-
-
-
-Comme dans les sections précédentes, vous pouvez trouver le modèle réel que nous allons entraîner et télécharger sur le *Hub* en utilisant le code ci-dessous et vérifier ses prédictions [ici](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr?text=This+plugin+allows+you+to+automatically+translate+web+pages+between+several+languages.).
-
-## Préparation des données
-
-Pour affiner ou entraîner un modèle de traduction à partir de zéro, nous avons besoin d'un jeu de données adapté à cette tâche. Comme mentionné précédemment, nous utiliserons le jeu de données [KDE4](https://huggingface.co/datasets/kde4) dans cette section, mais vous pouvez adapter le code pour utiliser vos propres données assez facilement, tant que vous avez des paires de phrases dans les deux langues que vous voulez traduire de et vers. Reportez-vous au [Chapitre 5](/course/fr/chapter5) si vous avez besoin d'un rappel sur la façon de charger vos données personnalisées dans un `Dataset`.
-
-### Le jeu de données KDE4
-
-Comme d'habitude, nous téléchargeons notre jeu de données en utilisant la fonction `load_dataset()` :
-
-```py
-from datasets import load_dataset, load_metric
-
-raw_datasets = load_dataset("kde4", lang1="en", lang2="fr")
-```
-
-Si vous souhaitez travailler avec une autre paire de langues, vous pouvez les spécifier par leurs codes. Au total, 92 langues sont disponibles pour cet ensemble de données ; vous pouvez les voir toutes en développant les étiquettes de langue sur sa [fiche](https://huggingface.co/datasets/kde4).
-
-
-
-Jetons un coup d'œil au jeu de données :
-
-```py
-raw_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['id', 'translation'],
- num_rows: 210173
- })
-})
-```
-
-Nous avons 210 173 paires de phrases, mais dans un seul split, donc nous devrons créer notre propre ensemble de validation. Comme nous l'avons vu dans le [Chapitre 5](/course/fr/chapter5), un `Dataset` possède une méthode `train_test_split()` qui peut nous aider. Nous allons fournir une graine pour la reproductibilité :
-
-```py
-split_datasets = raw_datasets["train"].train_test_split(train_size=0.9, seed=20)
-split_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['id', 'translation'],
- num_rows: 189155
- })
- test: Dataset({
- features: ['id', 'translation'],
- num_rows: 21018
- })
-})
-```
-
-Nous pouvons renommer la clé "test" en "validation" comme ceci :
-
-```py
-split_datasets["validation"] = split_datasets.pop("test")
-```
-
-Examinons maintenant un élément de ce jeu de données :
-
-```py
-split_datasets["train"][1]["translation"]
-```
-
-```python out
-{'en': 'Default to expanded threads',
- 'fr': 'Par défaut, développer les fils de discussion'}
-```
-
-Nous obtenons un dictionnaire contenant deux phrases dans la paire de langues demandée.
-Une particularité de ce jeu de données rempli de termes techniques informatiques est qu'ils sont tous entièrement traduits en français. Cependant, les ingénieurs français sont souvent paresseux et laissent la plupart des mots spécifiques à l'informatique en anglais lorsqu'ils parlent. Ici, par exemple, le mot "threads" pourrait très bien apparaître dans une phrase française, surtout dans une conversation technique. Mais dans ce jeu de données, il a été traduit par le plus correct "fils de discussion". Le modèle pré-entraîné que nous utilisons, qui a été pré-entraîné sur un plus grand corpus de phrases françaises et anglaises, prend l'option la plus facile de laisser le mot tel quel :
-
-```py
-from transformers import pipeline
-
-model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
-translator = pipeline("translation", model=model_checkpoint)
-translator("Default to expanded threads")
-```
-
-```python out
-[{'translation_text': 'Par défaut pour les threads élargis'}]
-```
-
-Un autre exemple de ce comportement peut être observé avec le mot "*plugin*", qui n'est pas officiellement un mot français mais que la plupart des locuteurs natifs comprendront et ne prendront pas la peine de traduire.
-Dans le jeu de données KDE4, ce mot a été traduit en français par le plus officiel "module d'extension" :
-
-```py
-split_datasets["train"][172]["translation"]
-```
-
-```python out
-{'en': 'Unable to import %1 using the OFX importer plugin. This file is not the correct format.',
- 'fr': "Impossible d'importer %1 en utilisant le module d'extension d'importation OFX. Ce fichier n'a pas un format correct."}
-```
-
-Notre modèle pré-entraîné, cependant, s'en tient au mot anglais compact et familier :
-
-```py
-translator(
- "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
-)
-```
-
-```python out
-[{'translation_text': "Impossible d'importer %1 en utilisant le plugin d'importateur OFX. Ce fichier n'est pas le bon format."}]
-```
-
-Il sera intéressant de voir si notre modèle *finetuné* tient compte de ces particularités de l'ensemble de données (alerte *spoiler* : il le fera).
-
-
-
-
-
-✏️ **Votre tour !** Un autre mot anglais souvent utilisé en français est "email". Trouvez le premier échantillon dans l'ensemble de données d'entraînement qui utilise ce mot. Comment est-il traduit ? Comment le modèle pré-entraîné traduit-il la même phrase anglaise ?
-
-
-
-### Traitement des données
-
-
-
-Vous devriez maintenant connaître le principe : les textes doivent tous être convertis en ensembles d'ID de *tokens* pour que le modèle puisse leur donner un sens. Pour cette tâche, nous aurons besoin de tokeniser les entrées et les cibles. Notre première tâche est de créer notre objet `tokenizer`. Comme indiqué précédemment, nous utiliserons un modèle pré-entraîné Marian English to French. Si vous essayez ce code avec une autre paire de langues, assurez-vous d'adapter le *checkpoint* du modèle. L'organisation [Helsinki-NLP](https://huggingface.co/Helsinki-NLP) fournit plus de mille modèles dans plusieurs langues.
-
-```python
-from transformers import AutoTokenizer
-
-model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="tf")
-```
-
-Vous pouvez également remplacer le `model_checkpoint` par tout autre modèle que vous préférez à partir du [*Hub*](https://huggingface.co/models), ou un dossier local où vous avez sauvegardé un modèle pré-entraîné et un *tokenizer*.
-
-
-
-💡 Si vous utilisez un *tokenizer* multilingue tel que mBART, mBART-50, ou M2M100, vous devrez définir les codes de langue de vos entrées et cibles dans le *tokenizer* en définissant `tokenizer.src_lang` et `tokenizer.tgt_lang` aux bonnes valeurs.
-
-
-
-La préparation de nos données est assez simple. Il y a juste une chose à retenir : vous traitez les entrées comme d'habitude, mais pour les cibles, vous devez envelopper le *tokenizer* dans le gestionnaire de contexte `as_target_tokenizer()`.
-
-Un gestionnaire de contexte en Python est introduit avec l'instruction `with` et est utile lorsque vous avez deux opérations liées à exécuter en paire. L'exemple le plus courant est lorsque vous écrivez ou lisez un fichier, ce qui est souvent fait dans une instruction comme :
-
-```
-with open(file_path) as f:
- content = f.read()
-```
-
-Ici, les deux opérations connexes qui sont exécutées en paire sont les actions d'ouverture et de fermeture du fichier. L'objet correspondant au fichier ouvert `f` n'existe qu'à l'intérieur du bloc indenté sous le `with` ; l'ouverture se produit avant ce bloc et la fermeture à la fin du bloc.
-
-Dans le cas présent, le gestionnaire de contexte `as_target_tokenizer()` va définir le *tokenizer* dans la langue de sortie (ici, le français) avant l'exécution du bloc indenté, puis le redéfinir dans la langue d'entrée (ici, l'anglais).
-
-Ainsi, le prétraitement d'un échantillon ressemble à ceci :
-
-```python
-en_sentence = split_datasets["train"][1]["translation"]["en"]
-fr_sentence = split_datasets["train"][1]["translation"]["fr"]
-
-inputs = tokenizer(en_sentence)
-with tokenizer.as_target_tokenizer():
- targets = tokenizer(fr_sentence)
-```
-
-Si nous oublions de tokeniser les cibles dans le gestionnaire de contexte, elles seront tokenisées par le *tokenizer* d'entrée, ce qui, dans le cas d'un modèle marial, ne va pas du tout bien se passer :
-
-```python
-wrong_targets = tokenizer(fr_sentence)
-print(tokenizer.convert_ids_to_tokens(wrong_targets["input_ids"]))
-print(tokenizer.convert_ids_to_tokens(targets["input_ids"]))
-```
-
-```python out
-['▁Par', '▁dé', 'f', 'aut', ',', '▁dé', 've', 'lop', 'per', '▁les', '▁fil', 's', '▁de', '▁discussion', '']
-['▁Par', '▁défaut', ',', '▁développer', '▁les', '▁fils', '▁de', '▁discussion', '']
-```
-
-Comme on peut le voir, utiliser le *tokenizer* anglais pour prétraiter une phrase française donne un batch de *tokens* plus important, puisque le *tokenizer* ne connaît aucun mot français (sauf ceux qui apparaissent aussi en anglais, comme "discussion").
-
-Les `inputs` et les `targets` sont des dictionnaires avec nos clés habituelles (identifiants d'entrée, masque d'attention, etc.), donc la dernière étape est de définir une clé `"labels"` dans les entrées. Nous faisons cela dans la fonction de prétraitement que nous allons appliquer sur les jeux de données :
-
-```python
-max_input_length = 128
-max_target_length = 128
-
-
-def preprocess_function(examples):
- inputs = [ex["en"] for ex in examples["translation"]]
- targets = [ex["fr"] for ex in examples["translation"]]
- model_inputs = tokenizer(inputs, max_length=max_input_length, truncation=True)
-
- # Set up the tokenizer for targets
- with tokenizer.as_target_tokenizer():
- labels = tokenizer(targets, max_length=max_target_length, truncation=True)
-
- model_inputs["labels"] = labels["input_ids"]
- return model_inputs
-```
-
-Notez que nous avons fixé des longueurs maximales similaires pour nos entrées et nos sorties. Comme les textes que nous traitons semblent assez courts, nous utilisons 128.
-
-
-
-💡 Si vous utilisez un modèle T5 (plus précisément, un des points de contrôle `t5-xxx`), le modèle s'attendra à ce que les entrées de texte aient un préfixe indiquant la tâche à accomplir, comme Si vous utilisez un modèle T5 (plus précisément, un des points de contrôle `t5-xxx`), le modèle s'attendra à ce que les entrées de texte aient un préfixe indiquant la tâche à accomplir, comme `translate : Anglais vers Français:`..
-
-
-
-
-
-⚠️ Nous ne faisons pas attention au masque d'attention des cibles, car le modèle ne s'y attend pas. Au lieu de cela, les étiquettes correspondant à un *token* de *padding* doivent être mises à `-100` afin qu'elles soient ignorées dans le calcul de la perte. Cela sera fait par notre collateur de données plus tard puisque nous appliquons le *padding* dynamique, mais si vous utilisez le *padding* ici, vous devriez adapter la fonction de prétraitement pour mettre tous les labels qui correspondent au *token* de *padding* à `-100`.
-
-
-
-Nous pouvons maintenant appliquer ce prétraitement en une seule fois sur toutes les divisions de notre jeu de données :
-
-```py
-tokenized_datasets = split_datasets.map(
- preprocess_function,
- batched=True,
- remove_columns=split_datasets["train"].column_names,
-)
-```
-
-Maintenant que les données ont été prétraitées, nous sommes prêts à *finetuner* notre modèle pré-entraîné !
-
-{#if fw === 'pt'}
-
-## *Finetuner* le modèle avec l'API `Trainer`.
-
-Le code actuel utilisant le `Trainer` sera le même que précédemment, avec juste un petit changement : nous utilisons ici un [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer), qui est une sous-classe de `Trainer` qui nous permettra de traiter correctement l'évaluation, en utilisant la méthode `generate()` pour prédire les sorties à partir des entrées. Nous y reviendrons plus en détail lorsque nous parlerons du calcul de la métrique.
-
-Tout d'abord, nous avons besoin d'un modèle réel à affiner. Nous allons utiliser l'API habituelle `AutoModel` :
-
-```py
-from transformers import AutoModelForSeq2SeqLM
-
-model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
-```
-
-{:else}
-
-## *Finetuning* du modèle avec Keras
-
-Tout d'abord, nous avons besoin d'un modèle réel à *finetuner*. Nous allons utiliser l'API habituelle `AutoModel` :
-
-```py
-from transformers import TFAutoModelForSeq2SeqLM
-
-model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint, from_pt=True)
-```
-
-
-
-💡 Le *checkpoint* `Helsinki-NLP/opus-mt-en-fr` ne dispose que de poids PyTorch, donc vous aurez une erreur si vous essayez de charger le modèle sans utiliser l'argument
-`from_pt=True` dans la méthode `from_pretrained()`. Lorsque vous spécifiez `from_pt=True`, la bibliothèque téléchargera et convertira automatiquement les poids PyTorch pour vous. Comme vous pouvez le constater, il est très simple de passer d'un framework à l'autre dans 🤗 *Transformers* !
-
-
-
-{/if}
-
-Notez que cette fois-ci, nous utilisons un modèle qui a été entraîné sur une tâche de traduction et qui peut déjà être utilisé, donc il n'y a pas d'avertissement concernant les poids manquants ou ceux nouvellement initialisés.
-
-### Collecte des données
-
-Nous aurons besoin d'un assembleur de données pour gérer le rembourrage pour la mise en lots dynamique. Nous ne pouvons pas simplement utiliser un `DataCollatorWithPadding` comme dans [Chapter 3](/course/fr/chapter3) dans ce cas, parce que cela ne rembourre que les entrées (ID d'entrée, masque d'attention, et ID de type de jeton). Nos étiquettes doivent également être rembourrées à la longueur maximale rencontrée dans les étiquettes. Et, comme mentionné précédemment, la valeur de remplissage utilisée pour remplir les étiquettes doit être `-100` et non le jeton de remplissage du *tokenizer*, pour s'assurer que ces valeurs remplies sont ignorées dans le calcul de la perte.
-
-Tout ceci est réalisé par un [`DataCollatorForSeq2Seq`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorforseq2seq). Comme le `DataCollatorWithPadding`, il prend le `tokenizer` utilisé pour prétraiter les entrées, mais il prend aussi le `model`. C'est parce que ce collateur de données sera également responsable de la préparation des ID d'entrée du décodeur, qui sont des versions décalées des étiquettes avec un jeton spécial au début. Comme ce décalage est effectué de manière légèrement différente selon les architectures, le `DataCollatorForSeq2Seq` a besoin de connaître l'objet `model` :
-
-{#if fw === 'pt'}
-
-```py
-from transformers import DataCollatorForSeq2Seq
-
-data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
-```
-
-{:else}
-
-```py
-from transformers import DataCollatorForSeq2Seq
-
-data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
-```
-
-{/if}
-
-Pour le tester sur quelques échantillons, nous l'appelons simplement sur une liste d'exemples de notre ensemble d'entrainement tokénisé :
-
-```py
-batch = data_collator([tokenized_datasets["train"][i] for i in range(1, 3)])
-batch.keys()
-```
-
-```python out
-dict_keys(['attention_mask', 'input_ids', 'labels', 'decoder_input_ids'])
-```
-
-Nous pouvons vérifier que nos étiquettes ont été paddées à la longueur maximale du lot, en utilisant `-100` :
-
-```py
-batch["labels"]
-```
-
-```python out
-tensor([[ 577, 5891, 2, 3184, 16, 2542, 5, 1710, 0, -100,
- -100, -100, -100, -100, -100, -100],
- [ 1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817,
- 550, 7032, 5821, 7907, 12649, 0]])
-```
-
-Et nous pouvons également jeter un coup d'œil aux ID d'entrée du décodeur, pour voir qu'il s'agit de versions décalées des étiquettes :
-
-```py
-batch["decoder_input_ids"]
-```
-
-```python out
-tensor([[59513, 577, 5891, 2, 3184, 16, 2542, 5, 1710, 0,
- 59513, 59513, 59513, 59513, 59513, 59513],
- [59513, 1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124,
- 817, 550, 7032, 5821, 7907, 12649]])
-```
-
-Voici les étiquettes des premier et deuxième éléments de notre jeu de données :
-
-```py
-for i in range(1, 3):
- print(tokenized_datasets["train"][i]["labels"])
-```
-
-```python out
-[577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]
-[1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817, 550, 7032, 5821, 7907, 12649, 0]
-```
-
-{#if fw === 'pt'}
-
-Nous allons transmettre ce `data_collator` au `Seq2SeqTrainer`. Ensuite, jetons un coup d'oeil à la métrique.
-
-{:else}
-
-Nous pouvons maintenant utiliser ce `data_collator` pour convertir chacun de nos jeux de données en un `tf.data.Dataset`, prêt pour l'entraînement :
-
-```python
-tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
- columns=["input_ids", "attention_mask", "labels"],
- collate_fn=data_collator,
- shuffle=True,
- batch_size=32,
-)
-tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
- columns=["input_ids", "attention_mask", "labels"],
- collate_fn=data_collator,
- shuffle=False,
- batch_size=16,
-)
-```
-
-{/if}
-
-
-### Métriques
-
-
-
-{#if fw === 'pt'}
-
-La fonctionnalité que `Seq2SeqTrainer` ajoute à sa superclasse `Trainer` est la possibilité d'utiliser la méthode `generate()` pendant l'évaluation ou la prédiction. Pendant l'entraînement, le modèle utilisera les `decoder_input_ids` avec un masque d'attention assurant qu'il n'utilise pas les *tokens* après le *token* qu'il essaie de prédire, pour accélérer l'entraînement. Pendant l'inférence, nous ne pourrons pas les utiliser puisque nous n'aurons pas d'étiquettes, donc c'est une bonne idée d'évaluer notre modèle avec la même configuration.
-
-Comme nous l'avons vu dans le [Chapitre 1](/course/fr/chapter1/6), le décodeur effectue l'inférence en prédisant les *tokens* un par un. Quelque chose qui est implémenté en coulisses dans les 🤗 Transformers par la méthode `generate()`. Le `Seq2SeqTrainer` nous laissera utiliser cette méthode pour l'évaluation si nous définissons `predict_with_generate=True`.
-
-{/if}
-
-La métrique traditionnelle utilisée pour la traduction est le [score BLEU](https://en.wikipedia.org/wiki/BLEU), introduit dans [un article de 2002](https://aclanthology.org/P02-1040.pdf) par Kishore Papineni et al. Le score BLEU évalue dans quelle mesure les traductions sont proches de leurs étiquettes. Il ne mesure pas l'intelligibilité ou l'exactitude grammaticale des résultats générés par le modèle, mais utilise des règles statistiques pour garantir que tous les mots des résultats générés apparaissent également dans les cibles. En outre, il existe des règles qui pénalisent les répétitions des mêmes mots s'ils ne sont pas également répétés dans les cibles (pour éviter que le modèle ne produise des phrases telles que "the the the the the the the") et les phrases produites qui sont plus courtes que celles des cibles (pour éviter que le modèle ne produise des phrases telles que "the").
-
-L'une des faiblesses de BLEU est qu'il s'attend à ce que le texte soit déjà tokenisé, ce qui rend difficile la comparaison des scores entre les modèles qui utilisent différents *tokenizers*. Par conséquent, la mesure la plus couramment utilisée aujourd'hui pour évaluer les modèles de traduction est [SacreBLEU](https://github.com/mjpost/sacrebleu), qui remédie à cette faiblesse (et à d'autres) en standardisant l'étape de tokenisation. Pour utiliser cette métrique, nous devons d'abord installer la bibliothèque SacreBLEU :
-
-```py
-!pip install sacrebleu
-```
-
-Nous pouvons ensuite le charger via `load_metric()` comme nous l'avons fait dans le [Chapitre 3](/course/fr/chapter3) :
-
-```py
-from datasets import load_metric
-
-metric = load_metric("sacrebleu")
-```
-
-Cette métrique prend des textes comme entrées et cibles. Elle est conçue pour accepter plusieurs cibles acceptables, car il y a souvent plusieurs traductions acceptables de la même phrase. Le jeu de données que nous utilisons n'en fournit qu'une seule, mais il n'est pas rare en NLP de trouver des jeux de données qui donnent plusieurs phrases comme étiquettes. Ainsi, les prédictions doivent être une liste de phrases, mais les références doivent être une liste de listes de phrases.
-
-Essayons un exemple :
-
-```py
-predictions = [
- "This plugin lets you translate web pages between several languages automatically."
-]
-references = [
- [
- "This plugin allows you to automatically translate web pages between several languages."
- ]
-]
-metric.compute(predictions=predictions, references=references)
-```
-
-```python out
-{'score': 46.750469682990165,
- 'counts': [11, 6, 4, 3],
- 'totals': [12, 11, 10, 9],
- 'precisions': [91.67, 54.54, 40.0, 33.33],
- 'bp': 0.9200444146293233,
- 'sys_len': 12,
- 'ref_len': 13}
-```
-
-Cela donne un score BLEU de 46.75, ce qui est plutôt bon. Pour référence, le Transformer original dans l'article ["Attention Is All You Need"](https://arxiv.org/pdf/1706.03762.pdf) a obtenu un score BLEU de 41.8 sur une tâche de traduction similaire entre l'anglais et le français ! (Pour plus d'informations sur les métriques individuelles, comme `counts` et `bp`, voir le [Dépôt SacreBLEU](https://github.com/mjpost/sacrebleu/blob/078c440168c6adc89ba75fe6d63f0d922d42bcfe/sacrebleu/metrics/bleu.py#L74).) D'autre part, si nous essayons avec les deux mauvais types de prédictions (batchs de répétitions ou trop courts) qui sortent souvent des modèles de traduction, nous obtiendrons des scores BLEU plutôt mauvais :
-
-```py
-predictions = ["This This This This"]
-references = [
- [
- "This plugin allows you to automatically translate web pages between several languages."
- ]
-]
-metric.compute(predictions=predictions, references=references)
-```
-
-```python out
-{'score': 1.683602693167689,
- 'counts': [1, 0, 0, 0],
- 'totals': [4, 3, 2, 1],
- 'precisions': [25.0, 16.67, 12.5, 12.5],
- 'bp': 0.10539922456186433,
- 'sys_len': 4,
- 'ref_len': 13}
-```
-
-```py
-predictions = ["This plugin"]
-references = [
- [
- "This plugin allows you to automatically translate web pages between several languages."
- ]
-]
-metric.compute(predictions=predictions, references=references)
-```
-
-```python out
-{'score': 0.0,
- 'counts': [2, 1, 0, 0],
- 'totals': [2, 1, 0, 0],
- 'precisions': [100.0, 100.0, 0.0, 0.0],
- 'bp': 0.004086771438464067,
- 'sys_len': 2,
- 'ref_len': 13}
-```
-
-Le score peut aller de 0 à 100, et plus il est élevé, mieux c'est.
-
-{#if fw === 'tf'}
-
-Pour passer des sorties du modèle aux textes que la métrique peut utiliser, nous allons utiliser la méthode `tokenizer.batch_decode()`. Nous devons juste nettoyer tous les `-100` dans les étiquettes ; le *tokenizer* fera automatiquement la même chose pour le *token* de *padding*. Définissons une fonction qui prend notre modèle et un jeu de données et calcule des métriques sur ceux-ci. Comme la génération de longues séquences peut être lente, nous sous-échantillonnons l'ensemble de validation pour nous assurer que cela ne prend pas une éternité :
-
-```py
-import numpy as np
-
-
-def compute_metrics():
- all_preds = []
- all_labels = []
- sampled_dataset = tokenized_datasets["validation"].shuffle().select(range(200))
- tf_generate_dataset = sampled_dataset.to_tf_dataset(
- columns=["input_ids", "attention_mask", "labels"],
- collate_fn=data_collator,
- shuffle=False,
- batch_size=4,
- )
- for batch in tf_generate_dataset:
- predictions = model.generate(
- input_ids=batch["input_ids"], attention_mask=batch["attention_mask"]
- )
- decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
- labels = batch["labels"].numpy()
- labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
- decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
- decoded_preds = [pred.strip() for pred in decoded_preds]
- decoded_labels = [[label.strip()] for label in decoded_labels]
- all_preds.extend(decoded_preds)
- all_labels.extend(decoded_labels)
-
- result = metric.compute(predictions=all_preds, references=all_labels)
- return {"bleu": result["score"]}
-```
-
-{:else}
-
-Pour passer des sorties du modèle aux textes utilisables par la métrique, nous allons utiliser la méthode `tokenizer.batch_decode()`. Nous devons juste nettoyer tous les `-100`s dans les étiquettes (le tokenizer fera automatiquement la même chose pour le token de remplissage) :
-
-```py
-import numpy as np
-
-
-def compute_metrics(eval_preds):
- preds, labels = eval_preds
- # In case the model returns more than the prediction logits
- if isinstance(preds, tuple):
- preds = preds[0]
-
- decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)
-
- # Replace -100s in the labels as we can't decode them
- labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
- decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
-
- # Some simple post-processing
- decoded_preds = [pred.strip() for pred in decoded_preds]
- decoded_labels = [[label.strip()] for label in decoded_labels]
-
- result = metric.compute(predictions=decoded_preds, references=decoded_labels)
- return {"bleu": result["score"]}
-```
-
-{/if}
-
-Maintenant que c'est fait, nous sommes prêts à affiner notre modèle !
-
-
-### *Finetuner* le modèle
-
-La première étape consiste à se connecter à Hugging Face, afin de pouvoir télécharger vos résultats sur le *Hub*. Il y a une fonction pratique pour vous aider à le faire dans un *notebook* :
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-Cela affichera un widget où vous pourrez entrer vos identifiants de connexion à Hugging Face.
-
-Si vous ne travaillez pas dans un *notebook*, tapez simplement la ligne suivante dans votre terminal :
-
-```bash
-huggingface-cli login
-```
-
-{#if fw === 'tf'}
-
-Avant de commencer, voyons quel type de résultats nous obtenons avec notre modèle sans entraînement :
-
-```py
-print(compute_metrics())
-```
-
-```
-{'bleu': 33.26983701454733}
-```
-
-Une fois ceci fait, nous pouvons préparer tout ce dont nous avons besoin pour compiler et entraîner notre modèle. Notez l'utilisation de `tf.keras.mixed_precision.set_global_policy("mixed_float16")`. Ceci indiquera à Keras de s'entraîner en utilisant float16, ce qui peut donner un gain de vitesse significatif sur les GPUs qui le supportent (Nvidia 20xx/V100 ou plus récent).
-
-```python
-from transformers import create_optimizer
-from transformers.keras_callbacks import PushToHubCallback
-import tensorflow as tf
-
-# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans l'ensemble de données, divisé par la taille du lot, puis multiplié par le nombre total d'époques.
-# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un lot tf.data.Dataset,
-# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size.
-num_epochs = 3
-num_train_steps = len(tf_train_dataset) * num_epochs
-
-optimizer, schedule = create_optimizer(
- init_lr=5e-5,
- num_warmup_steps=0,
- num_train_steps=num_train_steps,
- weight_decay_rate=0.01,
-)
-model.compile(optimizer=optimizer)
-
-# Entraîner en mixed-precision float16
-tf.keras.mixed_precision.set_global_policy("mixed_float16")
-```
-
-Ensuite, nous définissons un `PushToHubCallback` pour télécharger notre modèle sur le *Hub* pendant l'entraînement, comme nous l'avons vu dans [section 2]((/course/fr/chapter7/2)), et ensuite nous ajustons simplement le modèle avec ce callback :
-
-```python
-from transformers.keras_callbacks import PushToHubCallback
-
-callback = PushToHubCallback(
- output_dir="marian-finetuned-kde4-en-to-fr", tokenizer=tokenizer
-)
-
-model.fit(
- tf_train_dataset,
- validation_data=tf_eval_dataset,
- callbacks=[callback],
- epochs=num_epochs,
-)
-```
-
-Notez que vous pouvez spécifier le nom du référentiel vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"``Seq2SeqTrainingArguments`. Par défaut, le référentiel utilisé sera dans votre espace de noms et nommé après le répertoire de sortie que vous avez défini, donc ici ce sera `"sgugger/marian-finetuned-kde4-en-to-fr"` (qui est le modèle que nous avons lié au début de cette section).
-
-
-
-💡 Si le répertoire de sortie que vous utilisez existe déjà, il doit être un clone local du dépôt vers lequel vous voulez pousser. S'il ne l'est pas, vous obtiendrez une erreur lors de l'appel de `model.fit()` et devrez définir un nouveau nom.
-
-
-
-Enfin, voyons à quoi ressemblent nos mesures maintenant que l'entraînement est terminé :
-
-```py
-print(compute_metrics())
-```
-
-```
-{'bleu': 57.334066271545865}
-```
-
-À ce stade, vous pouvez utiliser le widget d'inférence sur le *Hub* pour tester votre modèle et le partager avec vos amis. Vous avez réussi à *finetuner* un modèle sur une tâche de traduction. Félicitations !
-
-{:else}
-
-Une fois ceci fait, nous pouvons définir notre `Seq2SeqTrainingArguments`. Comme pour le `Trainer`, nous utilisons une sous-classe de `TrainingArguments` qui contient quelques champs supplémentaires :
-
-```python
-from transformers import Seq2SeqTrainingArguments
-
-args = Seq2SeqTrainingArguments(
- f"marian-finetuned-kde4-en-to-fr",
- evaluation_strategy="no",
- save_strategy="epoch",
- learning_rate=2e-5,
- per_device_train_batch_size=32,
- per_device_eval_batch_size=64,
- weight_decay=0.01,
- save_total_limit=3,
- num_train_epochs=3,
- predict_with_generate=True,
- fp16=True,
- push_to_hub=True,
-)
-```
-
-En dehors des hyperparamètres habituels (comme le taux d'apprentissage, le nombre d'époques, la taille du lot et une certaine décroissance des poids), voici quelques changements par rapport à ce que nous avons vu dans les sections précédentes :
-
-- nous ne définissons pas d'évaluation régulière, car l'évaluation prend du temps ; nous allons juste évaluer notre modèle une fois avant l'entraînement et après,
-- nous avons mis `fp16=True`, ce qui accélère l'entraînement sur les GPUs modernes,
-- nous définissons `predict_with_generate=True`, comme discuté ci-dessus,
-- nous utilisons `push_to_hub=True` pour télécharger le modèle sur le *Hub* à la fin de chaque époque.
-
-Notez que vous pouvez spécifier le nom complet du référentiel vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` `Seq2SeqTrainingArguments`. Par défaut, le référentiel utilisé sera dans votre espace de noms et nommé d'après le répertoire de sortie que vous avez défini, donc dans notre cas ce sera `"sgugger/marian-finetuned-kde4-en-to-fr"` (qui est le modèle que nous avons lié au début de cette section).
-
-
-
-💡 Si le répertoire de sortie que vous utilisez existe déjà, il doit être un clone local du dépôt vers lequel vous voulez pousser. S'il ne l'est pas, vous obtiendrez une erreur lors de la définition de votre `Seq2SeqTrainer` et devrez définir un nouveau nom.
-
-
-
-
-Enfin, nous passons tout au `Seq2SeqTrainer` :
-
-```python
-from transformers import Seq2SeqTrainer
-
-trainer = Seq2SeqTrainer(
- model,
- args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- tokenizer=tokenizer,
- compute_metrics=compute_metrics,
-)
-```
-
-Avant d'entraîner, nous allons d'abord regarder le score obtenu par notre modèle, pour vérifier que nous n'aggravons pas les choses avec notre *finetuning*. Cette commande va prendre un peu de temps, vous pouvez donc prendre un café pendant qu'elle s'exécute :
-
-```python
-trainer.evaluate(max_length=max_target_length)
-```
-
-```python out
-{'eval_loss': 1.6964408159255981,
- 'eval_bleu': 39.26865061007616,
- 'eval_runtime': 965.8884,
- 'eval_samples_per_second': 21.76,
- 'eval_steps_per_second': 0.341}
-```
-
-A BLEU score of 39 is not too bad, which reflects the fact that our model is already good at translating English sentences to French ones.
-
-Next is the training, which will also take a bit of time:
-
-```python
-trainer.train()
-```
-
-Notez que pendant l'entraînement, chaque fois que le modèle est sauvegardé (ici, à chaque époque), il est téléchargé sur le *Hub* en arrière-plan. De cette façon, vous serez en mesure de reprendre votre entraînement sur une autre machine si nécessaire.
-
-Une fois l'entraînement terminé, nous évaluons à nouveau notre modèle - avec un peu de chance, nous verrons une amélioration du score BLEU !
-
-```py
-trainer.evaluate(max_length=max_target_length)
-```
-
-```python out
-{'eval_loss': 0.8558505773544312,
- 'eval_bleu': 52.94161337775576,
- 'eval_runtime': 714.2576,
- 'eval_samples_per_second': 29.426,
- 'eval_steps_per_second': 0.461,
- 'epoch': 3.0}
-```
-
-C'est une amélioration de près de 14 points, ce qui est formidable.
-
-Enfin, nous utilisons la méthode `push_to_hub()` pour nous assurer que nous téléchargeons la dernière version du modèle. Le `Trainer` rédige également une carte modèle avec tous les résultats de l'évaluation et la télécharge. Cette carte de modèle contient des métadonnées qui aident le *Hub* à choisir le widget pour la démo d'inférence. Habituellement, il n'y a pas besoin de dire quoi que ce soit car il peut inférer le bon *widget* à partir de la classe du modèle, mais dans ce cas, la même classe de modèle peut être utilisée pour toutes sortes de problèmes de séquence à séquence, donc nous spécifions que c'est un modèle de traduction :
-
-```py
-trainer.push_to_hub(tags="translation", commit_message="Training complete")
-```
-
-Cette commande renvoie l'URL du commit qu'elle vient de faire, si vous voulez l'inspecter :
-
-```python out
-'https://huggingface.co/sgugger/marian-finetuned-kde4-en-to-fr/commit/3601d621e3baae2bc63d3311452535f8f58f6ef3'
-```
-
-À ce stade, vous pouvez utiliser le widget d'inférence sur le Hub du modèle pour tester votre modèle et le partager avec vos amis. Vous avez réussi à *finetuner* un modèle sur une tâche de traduction. Félicitations !
-
-Si vous souhaitez vous plonger un peu plus profondément dans la boucle d'entraînement, nous allons maintenant vous montrer comment faire la même chose en utilisant 🤗 *Accelerate*.
-
-{/if}
-
-{#if fw === 'pt'}
-
-## Une boucle d'entraînement personnalisée
-
-Jetons maintenant un coup d'œil à la boucle d'entraînement complète, afin que vous puissiez facilement personnaliser les parties dont vous avez besoin. Elle ressemblera beaucoup à ce que nous avons fait dans la [section 2](/course/fr/chapter7/2) et le [chapitre 3](/course/fr/chapter3/4).
-
-### Préparer le tout pour l'entraînement
-
-Vous avez vu tout cela plusieurs fois maintenant, donc nous allons passer en revue le code assez rapidement. D'abord, nous allons construire le `DataLoader` à partir de nos jeux de données, après avoir configuré les jeux de données au format `"torch"` pour obtenir les tenseurs PyTorch :
-
-```py
-from torch.utils.data import DataLoader
-
-tokenized_datasets.set_format("torch")
-train_dataloader = DataLoader(
- tokenized_datasets["train"],
- shuffle=True,
- collate_fn=data_collator,
- batch_size=8,
-)
-eval_dataloader = DataLoader(
- tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
-)
-```
-
-Ensuite, nous réinstantifions notre modèle, pour nous assurer que nous ne poursuivons pas l'affinage précédent, mais que nous repartons du modèle pré-entraîné :
-
-```py
-model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
-```
-
-Nous aurons alors besoin d'un optimiseur :
-
-```py
-from transformers import AdamW
-
-optimizer = AdamW(model.parameters(), lr=2e-5)
-```
-
-Une fois que nous avons tous ces objets, nous pouvons les envoyer à la méthode `accelerator.prepare()`. Rappelez-vous que si vous voulez vous entraîner sur des TPUs dans un *notebook* de Colab, vous devrez déplacer tout ce code dans une fonction d'entraînement, et qui ne devrait pas exécuter une cellule qui instancie un `Accelerator`.
-
-```py
-from accelerate import Accelerator
-
-accelerator = Accelerator()
-model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
- model, optimizer, train_dataloader, eval_dataloader
-)
-```
-
-Maintenant que nous avons envoyé notre `train_dataloader` à `accelerator.prepare()`, nous pouvons utiliser sa longueur pour calculer le nombre d'étapes d'entraînement. Rappelez-vous que nous devrions toujours faire cela après avoir préparé le dataloader, car cette méthode va changer la longueur du `DataLoader`. Nous utilisons un programme linéaire classique du taux d'apprentissage à 0 :
-
-```py
-from transformers import get_scheduler
-
-num_train_epochs = 3
-num_update_steps_per_epoch = len(train_dataloader)
-num_training_steps = num_train_epochs * num_update_steps_per_epoch
-
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-```
-
-Enfin, pour pousser notre modèle vers le Hub, nous aurons besoin de créer un objet `Repository` dans un dossier de travail. Tout d'abord, connectez-vous au *Hub*, si vous n'êtes pas déjà connecté. Nous déterminerons le nom du dépôt à partir de l'ID du modèle que nous voulons donner à notre modèle (n'hésitez pas à remplacer le `repo_name` par votre propre choix ; il doit juste contenir votre nom d'utilisateur, ce que fait la fonction `get_full_repo_name()`) :
-
-```py
-from huggingface_hub import Repository, get_full_repo_name
-
-model_name = "marian-finetuned-kde4-en-to-fr-accelerate"
-repo_name = get_full_repo_name(model_name)
-repo_name
-```
-
-```python out
-'sgugger/marian-finetuned-kde4-en-to-fr-accelerate'
-```
-
-Ensuite, nous pouvons cloner ce référentiel dans un dossier local. S'il existe déjà, ce dossier local doit être un clone du référentiel avec lequel nous travaillons :
-
-```py
-output_dir = "marian-finetuned-kde4-en-to-fr-accelerate"
-repo = Repository(output_dir, clone_from=repo_name)
-```
-
-Nous pouvons maintenant télécharger tout ce que nous sauvegardons dans `output_dir` en appelant la méthode `repo.push_to_hub()`. Cela nous aidera à télécharger les modèles intermédiaires à la fin de chaque époque.
-
-### Boucle d'entraînement
-
-Nous sommes maintenant prêts à écrire la boucle d'entraînement complète. Pour simplifier sa partie évaluation, nous définissons cette fonction `postprocess()` qui prend les prédictions et les étiquettes et les convertit en listes de chaînes de caractères que notre objet `metric` attend :
-
-```py
-def postprocess(predictions, labels):
- predictions = predictions.cpu().numpy()
- labels = labels.cpu().numpy()
-
- decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
-
- # Remplacer -100 dans les étiquettes car nous ne pouvons pas les décoder.
- labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
- decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
-
- # Quelques post-traitements simples
- decoded_preds = [pred.strip() for pred in decoded_preds]
- decoded_labels = [[label.strip()] for label in decoded_labels]
- return decoded_preds, decoded_labels
-```
-
-La boucle d'entraînement ressemble beaucoup à celles de [section 2](/course/fr/chapter7/2) et [chapitre 3](/course/fr/chapter3), avec quelques différences dans la partie évaluation -- alors concentrons-nous sur cela !
-
-La première chose à noter est que nous utilisons la méthode `generate()` pour calculer les prédictions, mais c'est une méthode sur notre modèle de base, pas le modèle enveloppé 🤗 Accelerate créé dans la méthode `prepare()`. C'est pourquoi nous déballons d'abord le modèle, puis nous appelons cette méthode.
-
-La deuxième chose est que, comme avec [token classification](/course/fr/chapter7/2), deux processus peuvent avoir paddé les entrées et les étiquettes à des formes différentes, donc nous utilisons `accelerator.pad_across_processes()` pour rendre les prédictions et les étiquettes de la même forme avant d'appeler la méthode `gather()`. Si nous ne faisons pas cela, l'évaluation va soit se tromper, soit se bloquer pour toujours.
-
-```py
-from tqdm.auto import tqdm
-import torch
-
-progress_bar = tqdm(range(num_training_steps))
-
-for epoch in range(num_train_epochs):
- # Entraînement
- model.train()
- for batch in train_dataloader:
- outputs = model(**batch)
- loss = outputs.loss
- accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-
- # Evaluation
- model.eval()
- for batch in tqdm(eval_dataloader):
- with torch.no_grad():
- generated_tokens = accelerator.unwrap_model(model).generate(
- batch["input_ids"],
- attention_mask=batch["attention_mask"],
- max_length=128,
- )
- labels = batch["labels"]
-
- # Nécessaire pour rembourrer les prédictions et les étiquettes à rassembler
- generated_tokens = accelerator.pad_across_processes(
- generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
- )
- labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)
-
- predictions_gathered = accelerator.gather(generated_tokens)
- labels_gathered = accelerator.gather(labels)
-
- decoded_preds, decoded_labels = postprocess(predictions_gathered, labels_gathered)
- metric.add_batch(predictions=decoded_preds, references=decoded_labels)
-
- results = metric.compute()
- print(f"epoch {epoch}, BLEU score: {results['score']:.2f}")
-
- # Sauvegarder et télécharger
- accelerator.wait_for_everyone()
- unwrapped_model = accelerator.unwrap_model(model)
- unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
- if accelerator.is_main_process:
- tokenizer.save_pretrained(output_dir)
- repo.push_to_hub(
- commit_message=f"Training in progress epoch {epoch}", blocking=False
- )
-```
-
-```python out
-epoch 0, BLEU score: 53.47
-epoch 1, BLEU score: 54.24
-epoch 2, BLEU score: 54.44
-```
-
-Une fois que c'est fait, vous devriez avoir un modèle qui a des résultats assez similaires à celui entraîné avec le `Seq2SeqTrainer`. Vous pouvez vérifier celui que nous avons formé en utilisant ce code à [*huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate*](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate). Et si vous voulez tester des modifications de la boucle d'entraînement, vous pouvez les mettre en œuvre directement en modifiant le code ci-dessus !
-
-{/if}
-
-### Utilisation du modèle *finetuné*.
-
-Nous vous avons déjà montré comment vous pouvez utiliser le modèle que nous avons *finetuné* sur le *Hub* avec le *widget* d'inférence. Pour l'utiliser localement dans un `pipeline`, nous devons juste spécifier l'identifiant de modèle approprié :
-
-```py
-from transformers import pipeline
-
-# Remplacez ceci par votre propre checkpoint
-model_checkpoint = "huggingface-course/marian-finetuned-kde4-en-to-fr"
-translator = pipeline("translation", model=model_checkpoint)
-translator("Default to expanded threads")
-```
-
-```python out
-[{'translation_text': 'Par défaut, développer les fils de discussion'}]
-```
-
-Comme prévu, notre modèle pré-entraîné a adapté ses connaissances au corpus sur lequel nous l'avons affiné, et au lieu de laisser le mot anglais "threads" seul, il le traduit maintenant par la version officielle française. Il en va de même pour "plugin" :
-
-```py
-translator(
- "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
-)
-```
-
-```python out
-[{'translation_text': "Impossible d'importer %1 en utilisant le module externe d'importation OFX. Ce fichier n'est pas le bon format."}]
-```
-
-Un autre excellent exemple d'adaptation au domaine !
-
-
-
-✏️ **Votre tour !** Que retourne le modèle sur l'échantillon avec le mot "email" que vous avez identifié plus tôt ?
-
-
+
+
+# Traduction
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Plongeons maintenant dans la traduction. Il s'agit d'une autre [tâche de séquence à séquence](/cours/fr/chapitre1/7), ce qui signifie que c'est un problème qui peut être formulé comme le passage d'une séquence à une autre. En ce sens, le problème est assez proche de la tâche de [résumé](/cours/fr/chapitre7/6) et vous pouvez adapter ce que nous allons voir ici à d'autres problèmes de séquence à séquence tels que :
+
+- le **transfert de style** : créer un modèle qui *traduit* des textes écrits dans un certain style vers un autre (par exemple, du formel au décontracté ou de l'anglais shakespearien à l'anglais moderne).
+- la **génération de réponse à des questions** : Création d'un modèle qui génère des réponses à des questions, compte tenu d'un contexte.
+
+
+
+Si vous disposez d'un corpus suffisamment important de textes en deux langues (ou plus), vous pouvez entraîner un nouveau modèle de traduction à partir de zéro, comme nous le ferons dans la section sur la [modélisation causale du langage](/cours/fr/chapitre7/6). Il est toutefois plus rapide de *finetuner* un modèle de traduction existant, qu'il s'agisse d'un modèle multilingue comme mT5 ou mBART que vous souhaitez adapter à une paire de langues spécifique, ou même d'un modèle spécialisé dans la traduction d'une langue vers une autre que vous souhaitez adapter à votre corpus spécifique.
+
+Dans cette section, nous allons *finetuner* un modèle Marian pré-entraîné pour traduire de l'anglais au français (puisque de nombreux employés de Hugging Face parlent ces deux langues) sur le [KDE4 dataset](https://huggingface.co/datasets/kde4), qui est un jeu de données de fichiers localisés pour les [KDE apps](https://apps.kde.org/). Le modèle que nous utiliserons a été pré-entraîné sur un large corpus de textes français et anglais provenant du [jeu de données Opus](https://opus.nlpl.eu/), qui contient en fait le jeu de données KDE4. Mais même si le modèle pré-entraîné que nous utilisons a vu ces données pendant son pré-entraînement, nous verrons que nous pouvons obtenir une meilleure version de ce modèle après un *finetuning*.
+
+Une fois que nous aurons terminé, nous aurons un modèle capable de faire des prédictions comme celle-ci :
+
+
+
+
+
+
+
+
+
+Comme dans les sections précédentes, vous pouvez trouver le modèle réel que nous allons entraîner et télécharger sur le *Hub* en utilisant le code ci-dessous et vérifier ses prédictions [ici](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr?text=This+plugin+allows+you+to+automatically+translate+web+pages+between+several+languages.).
+
+## Préparation des données
+
+Pour affiner ou entraîner un modèle de traduction à partir de zéro, nous avons besoin d'un jeu de données adapté à cette tâche. Comme mentionné précédemment, nous utiliserons le jeu de données [KDE4](https://huggingface.co/datasets/kde4) dans cette section, mais vous pouvez adapter le code pour utiliser vos propres données assez facilement, tant que vous avez des paires de phrases dans les deux langues que vous voulez traduire de et vers. Reportez-vous au [Chapitre 5](/course/fr/chapter5) si vous avez besoin d'un rappel sur la façon de charger vos données personnalisées dans un `Dataset`.
+
+### Le jeu de données KDE4
+
+Comme d'habitude, nous téléchargeons notre jeu de données en utilisant la fonction `load_dataset()` :
+
+```py
+from datasets import load_dataset, load_metric
+
+raw_datasets = load_dataset("kde4", lang1="en", lang2="fr")
+```
+
+Si vous souhaitez travailler avec une autre paire de langues, vous pouvez les spécifier par leurs codes. Au total, 92 langues sont disponibles pour cet ensemble de données ; vous pouvez les voir toutes en développant les étiquettes de langue sur sa [fiche](https://huggingface.co/datasets/kde4).
+
+
+
+Jetons un coup d'œil au jeu de données :
+
+```py
+raw_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['id', 'translation'],
+ num_rows: 210173
+ })
+})
+```
+
+Nous avons 210 173 paires de phrases, mais dans un seul split, donc nous devrons créer notre propre ensemble de validation. Comme nous l'avons vu dans le [Chapitre 5](/course/fr/chapter5), un `Dataset` possède une méthode `train_test_split()` qui peut nous aider. Nous allons fournir une graine pour la reproductibilité :
+
+```py
+split_datasets = raw_datasets["train"].train_test_split(train_size=0.9, seed=20)
+split_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['id', 'translation'],
+ num_rows: 189155
+ })
+ test: Dataset({
+ features: ['id', 'translation'],
+ num_rows: 21018
+ })
+})
+```
+
+Nous pouvons renommer la clé "test" en "validation" comme ceci :
+
+```py
+split_datasets["validation"] = split_datasets.pop("test")
+```
+
+Examinons maintenant un élément de ce jeu de données :
+
+```py
+split_datasets["train"][1]["translation"]
+```
+
+```python out
+{'en': 'Default to expanded threads',
+ 'fr': 'Par défaut, développer les fils de discussion'}
+```
+
+Nous obtenons un dictionnaire contenant deux phrases dans la paire de langues demandée.
+Une particularité de ce jeu de données rempli de termes techniques informatiques est qu'ils sont tous entièrement traduits en français. Cependant, les ingénieurs français sont souvent paresseux et laissent la plupart des mots spécifiques à l'informatique en anglais lorsqu'ils parlent. Ici, par exemple, le mot "threads" pourrait très bien apparaître dans une phrase française, surtout dans une conversation technique. Mais dans ce jeu de données, il a été traduit par le plus correct "fils de discussion". Le modèle pré-entraîné que nous utilisons, qui a été pré-entraîné sur un plus grand corpus de phrases françaises et anglaises, prend l'option la plus facile de laisser le mot tel quel :
+
+```py
+from transformers import pipeline
+
+model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
+translator = pipeline("translation", model=model_checkpoint)
+translator("Default to expanded threads")
+```
+
+```python out
+[{'translation_text': 'Par défaut pour les threads élargis'}]
+```
+
+Un autre exemple de ce comportement peut être observé avec le mot "*plugin*", qui n'est pas officiellement un mot français mais que la plupart des locuteurs natifs comprendront et ne prendront pas la peine de traduire.
+Dans le jeu de données KDE4, ce mot a été traduit en français par le plus officiel "module d'extension" :
+
+```py
+split_datasets["train"][172]["translation"]
+```
+
+```python out
+{'en': 'Unable to import %1 using the OFX importer plugin. This file is not the correct format.',
+ 'fr': "Impossible d'importer %1 en utilisant le module d'extension d'importation OFX. Ce fichier n'a pas un format correct."}
+```
+
+Notre modèle pré-entraîné, cependant, s'en tient au mot anglais compact et familier :
+
+```py
+translator(
+ "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
+)
+```
+
+```python out
+[{'translation_text': "Impossible d'importer %1 en utilisant le plugin d'importateur OFX. Ce fichier n'est pas le bon format."}]
+```
+
+Il sera intéressant de voir si notre modèle *finetuné* tient compte de ces particularités de l'ensemble de données (alerte *spoiler* : il le fera).
+
+
+
+
+
+✏️ **Votre tour !** Un autre mot anglais souvent utilisé en français est "email". Trouvez le premier échantillon dans l'ensemble de données d'entraînement qui utilise ce mot. Comment est-il traduit ? Comment le modèle pré-entraîné traduit-il la même phrase anglaise ?
+
+
+
+### Traitement des données
+
+
+
+Vous devriez maintenant connaître le principe : les textes doivent tous être convertis en ensembles d'ID de *tokens* pour que le modèle puisse leur donner un sens. Pour cette tâche, nous aurons besoin de tokeniser les entrées et les cibles. Notre première tâche est de créer notre objet `tokenizer`. Comme indiqué précédemment, nous utiliserons un modèle pré-entraîné Marian English to French. Si vous essayez ce code avec une autre paire de langues, assurez-vous d'adapter le *checkpoint* du modèle. L'organisation [Helsinki-NLP](https://huggingface.co/Helsinki-NLP) fournit plus de mille modèles dans plusieurs langues.
+
+```python
+from transformers import AutoTokenizer
+
+model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="tf")
+```
+
+Vous pouvez également remplacer le `model_checkpoint` par tout autre modèle que vous préférez à partir du [*Hub*](https://huggingface.co/models), ou un dossier local où vous avez sauvegardé un modèle pré-entraîné et un *tokenizer*.
+
+
+
+💡 Si vous utilisez un *tokenizer* multilingue tel que mBART, mBART-50, ou M2M100, vous devrez définir les codes de langue de vos entrées et cibles dans le *tokenizer* en définissant `tokenizer.src_lang` et `tokenizer.tgt_lang` aux bonnes valeurs.
+
+
+
+La préparation de nos données est assez simple. Il y a juste une chose à retenir : vous traitez les entrées comme d'habitude, mais pour les cibles, vous devez envelopper le *tokenizer* dans le gestionnaire de contexte `as_target_tokenizer()`.
+
+Un gestionnaire de contexte en Python est introduit avec l'instruction `with` et est utile lorsque vous avez deux opérations liées à exécuter en paire. L'exemple le plus courant est lorsque vous écrivez ou lisez un fichier, ce qui est souvent fait dans une instruction comme :
+
+```
+with open(file_path) as f:
+ content = f.read()
+```
+
+Ici, les deux opérations connexes qui sont exécutées en paire sont les actions d'ouverture et de fermeture du fichier. L'objet correspondant au fichier ouvert `f` n'existe qu'à l'intérieur du bloc indenté sous le `with` ; l'ouverture se produit avant ce bloc et la fermeture à la fin du bloc.
+
+Dans le cas présent, le gestionnaire de contexte `as_target_tokenizer()` va définir le *tokenizer* dans la langue de sortie (ici, le français) avant l'exécution du bloc indenté, puis le redéfinir dans la langue d'entrée (ici, l'anglais).
+
+Ainsi, le prétraitement d'un échantillon ressemble à ceci :
+
+```python
+en_sentence = split_datasets["train"][1]["translation"]["en"]
+fr_sentence = split_datasets["train"][1]["translation"]["fr"]
+
+inputs = tokenizer(en_sentence)
+with tokenizer.as_target_tokenizer():
+ targets = tokenizer(fr_sentence)
+```
+
+Si nous oublions de tokeniser les cibles dans le gestionnaire de contexte, elles seront tokenisées par le *tokenizer* d'entrée, ce qui, dans le cas d'un modèle marial, ne va pas du tout bien se passer :
+
+```python
+wrong_targets = tokenizer(fr_sentence)
+print(tokenizer.convert_ids_to_tokens(wrong_targets["input_ids"]))
+print(tokenizer.convert_ids_to_tokens(targets["input_ids"]))
+```
+
+```python out
+['▁Par', '▁dé', 'f', 'aut', ',', '▁dé', 've', 'lop', 'per', '▁les', '▁fil', 's', '▁de', '▁discussion', '']
+['▁Par', '▁défaut', ',', '▁développer', '▁les', '▁fils', '▁de', '▁discussion', '']
+```
+
+Comme on peut le voir, utiliser le *tokenizer* anglais pour prétraiter une phrase française donne un batch de *tokens* plus important, puisque le *tokenizer* ne connaît aucun mot français (sauf ceux qui apparaissent aussi en anglais, comme "discussion").
+
+Les `inputs` et les `targets` sont des dictionnaires avec nos clés habituelles (identifiants d'entrée, masque d'attention, etc.), donc la dernière étape est de définir une clé `"labels"` dans les entrées. Nous faisons cela dans la fonction de prétraitement que nous allons appliquer sur les jeux de données :
+
+```python
+max_input_length = 128
+max_target_length = 128
+
+
+def preprocess_function(examples):
+ inputs = [ex["en"] for ex in examples["translation"]]
+ targets = [ex["fr"] for ex in examples["translation"]]
+ model_inputs = tokenizer(inputs, max_length=max_input_length, truncation=True)
+
+ # Set up the tokenizer for targets
+ with tokenizer.as_target_tokenizer():
+ labels = tokenizer(targets, max_length=max_target_length, truncation=True)
+
+ model_inputs["labels"] = labels["input_ids"]
+ return model_inputs
+```
+
+Notez que nous avons fixé des longueurs maximales similaires pour nos entrées et nos sorties. Comme les textes que nous traitons semblent assez courts, nous utilisons 128.
+
+
+
+💡 Si vous utilisez un modèle T5 (plus précisément, un des points de contrôle `t5-xxx`), le modèle s'attendra à ce que les entrées de texte aient un préfixe indiquant la tâche à accomplir, comme Si vous utilisez un modèle T5 (plus précisément, un des points de contrôle `t5-xxx`), le modèle s'attendra à ce que les entrées de texte aient un préfixe indiquant la tâche à accomplir, comme `translate : Anglais vers Français:`..
+
+
+
+
+
+⚠️ Nous ne faisons pas attention au masque d'attention des cibles, car le modèle ne s'y attend pas. Au lieu de cela, les étiquettes correspondant à un *token* de *padding* doivent être mises à `-100` afin qu'elles soient ignorées dans le calcul de la perte. Cela sera fait par notre collateur de données plus tard puisque nous appliquons le *padding* dynamique, mais si vous utilisez le *padding* ici, vous devriez adapter la fonction de prétraitement pour mettre tous les labels qui correspondent au *token* de *padding* à `-100`.
+
+
+
+Nous pouvons maintenant appliquer ce prétraitement en une seule fois sur toutes les divisions de notre jeu de données :
+
+```py
+tokenized_datasets = split_datasets.map(
+ preprocess_function,
+ batched=True,
+ remove_columns=split_datasets["train"].column_names,
+)
+```
+
+Maintenant que les données ont été prétraitées, nous sommes prêts à *finetuner* notre modèle pré-entraîné !
+
+{#if fw === 'pt'}
+
+## *Finetuner* le modèle avec l'API `Trainer`.
+
+Le code actuel utilisant le `Trainer` sera le même que précédemment, avec juste un petit changement : nous utilisons ici un [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer), qui est une sous-classe de `Trainer` qui nous permettra de traiter correctement l'évaluation, en utilisant la méthode `generate()` pour prédire les sorties à partir des entrées. Nous y reviendrons plus en détail lorsque nous parlerons du calcul de la métrique.
+
+Tout d'abord, nous avons besoin d'un modèle réel à affiner. Nous allons utiliser l'API habituelle `AutoModel` :
+
+```py
+from transformers import AutoModelForSeq2SeqLM
+
+model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
+```
+
+{:else}
+
+## *Finetuning* du modèle avec Keras
+
+Tout d'abord, nous avons besoin d'un modèle réel à *finetuner*. Nous allons utiliser l'API habituelle `AutoModel` :
+
+```py
+from transformers import TFAutoModelForSeq2SeqLM
+
+model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint, from_pt=True)
+```
+
+
+
+💡 Le *checkpoint* `Helsinki-NLP/opus-mt-en-fr` ne dispose que de poids PyTorch, donc vous aurez une erreur si vous essayez de charger le modèle sans utiliser l'argument
+`from_pt=True` dans la méthode `from_pretrained()`. Lorsque vous spécifiez `from_pt=True`, la bibliothèque téléchargera et convertira automatiquement les poids PyTorch pour vous. Comme vous pouvez le constater, il est très simple de passer d'un framework à l'autre dans 🤗 *Transformers* !
+
+
+
+{/if}
+
+Notez que cette fois-ci, nous utilisons un modèle qui a été entraîné sur une tâche de traduction et qui peut déjà être utilisé, donc il n'y a pas d'avertissement concernant les poids manquants ou ceux nouvellement initialisés.
+
+### Collecte des données
+
+Nous aurons besoin d'un assembleur de données pour gérer le rembourrage pour la mise en lots dynamique. Nous ne pouvons pas simplement utiliser un `DataCollatorWithPadding` comme dans [Chapter 3](/course/fr/chapter3) dans ce cas, parce que cela ne rembourre que les entrées (ID d'entrée, masque d'attention, et ID de type de jeton). Nos étiquettes doivent également être rembourrées à la longueur maximale rencontrée dans les étiquettes. Et, comme mentionné précédemment, la valeur de remplissage utilisée pour remplir les étiquettes doit être `-100` et non le jeton de remplissage du *tokenizer*, pour s'assurer que ces valeurs remplies sont ignorées dans le calcul de la perte.
+
+Tout ceci est réalisé par un [`DataCollatorForSeq2Seq`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorforseq2seq). Comme le `DataCollatorWithPadding`, il prend le `tokenizer` utilisé pour prétraiter les entrées, mais il prend aussi le `model`. C'est parce que ce collateur de données sera également responsable de la préparation des ID d'entrée du décodeur, qui sont des versions décalées des étiquettes avec un jeton spécial au début. Comme ce décalage est effectué de manière légèrement différente selon les architectures, le `DataCollatorForSeq2Seq` a besoin de connaître l'objet `model` :
+
+{#if fw === 'pt'}
+
+```py
+from transformers import DataCollatorForSeq2Seq
+
+data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
+```
+
+{:else}
+
+```py
+from transformers import DataCollatorForSeq2Seq
+
+data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
+```
+
+{/if}
+
+Pour le tester sur quelques échantillons, nous l'appelons simplement sur une liste d'exemples de notre ensemble d'entrainement tokénisé :
+
+```py
+batch = data_collator([tokenized_datasets["train"][i] for i in range(1, 3)])
+batch.keys()
+```
+
+```python out
+dict_keys(['attention_mask', 'input_ids', 'labels', 'decoder_input_ids'])
+```
+
+Nous pouvons vérifier que nos étiquettes ont été paddées à la longueur maximale du lot, en utilisant `-100` :
+
+```py
+batch["labels"]
+```
+
+```python out
+tensor([[ 577, 5891, 2, 3184, 16, 2542, 5, 1710, 0, -100,
+ -100, -100, -100, -100, -100, -100],
+ [ 1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817,
+ 550, 7032, 5821, 7907, 12649, 0]])
+```
+
+Et nous pouvons également jeter un coup d'œil aux ID d'entrée du décodeur, pour voir qu'il s'agit de versions décalées des étiquettes :
+
+```py
+batch["decoder_input_ids"]
+```
+
+```python out
+tensor([[59513, 577, 5891, 2, 3184, 16, 2542, 5, 1710, 0,
+ 59513, 59513, 59513, 59513, 59513, 59513],
+ [59513, 1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124,
+ 817, 550, 7032, 5821, 7907, 12649]])
+```
+
+Voici les étiquettes des premier et deuxième éléments de notre jeu de données :
+
+```py
+for i in range(1, 3):
+ print(tokenized_datasets["train"][i]["labels"])
+```
+
+```python out
+[577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]
+[1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817, 550, 7032, 5821, 7907, 12649, 0]
+```
+
+{#if fw === 'pt'}
+
+Nous allons transmettre ce `data_collator` au `Seq2SeqTrainer`. Ensuite, jetons un coup d'oeil à la métrique.
+
+{:else}
+
+Nous pouvons maintenant utiliser ce `data_collator` pour convertir chacun de nos jeux de données en un `tf.data.Dataset`, prêt pour l'entraînement :
+
+```python
+tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
+ columns=["input_ids", "attention_mask", "labels"],
+ collate_fn=data_collator,
+ shuffle=True,
+ batch_size=32,
+)
+tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
+ columns=["input_ids", "attention_mask", "labels"],
+ collate_fn=data_collator,
+ shuffle=False,
+ batch_size=16,
+)
+```
+
+{/if}
+
+
+### Métriques
+
+
+
+{#if fw === 'pt'}
+
+La fonctionnalité que `Seq2SeqTrainer` ajoute à sa superclasse `Trainer` est la possibilité d'utiliser la méthode `generate()` pendant l'évaluation ou la prédiction. Pendant l'entraînement, le modèle utilisera les `decoder_input_ids` avec un masque d'attention assurant qu'il n'utilise pas les *tokens* après le *token* qu'il essaie de prédire, pour accélérer l'entraînement. Pendant l'inférence, nous ne pourrons pas les utiliser puisque nous n'aurons pas d'étiquettes, donc c'est une bonne idée d'évaluer notre modèle avec la même configuration.
+
+Comme nous l'avons vu dans le [Chapitre 1](/course/fr/chapter1/6), le décodeur effectue l'inférence en prédisant les *tokens* un par un. Quelque chose qui est implémenté en coulisses dans les 🤗 Transformers par la méthode `generate()`. Le `Seq2SeqTrainer` nous laissera utiliser cette méthode pour l'évaluation si nous définissons `predict_with_generate=True`.
+
+{/if}
+
+La métrique traditionnelle utilisée pour la traduction est le [score BLEU](https://en.wikipedia.org/wiki/BLEU), introduit dans [un article de 2002](https://aclanthology.org/P02-1040.pdf) par Kishore Papineni et al. Le score BLEU évalue dans quelle mesure les traductions sont proches de leurs étiquettes. Il ne mesure pas l'intelligibilité ou l'exactitude grammaticale des résultats générés par le modèle, mais utilise des règles statistiques pour garantir que tous les mots des résultats générés apparaissent également dans les cibles. En outre, il existe des règles qui pénalisent les répétitions des mêmes mots s'ils ne sont pas également répétés dans les cibles (pour éviter que le modèle ne produise des phrases telles que "the the the the the the the") et les phrases produites qui sont plus courtes que celles des cibles (pour éviter que le modèle ne produise des phrases telles que "the").
+
+L'une des faiblesses de BLEU est qu'il s'attend à ce que le texte soit déjà tokenisé, ce qui rend difficile la comparaison des scores entre les modèles qui utilisent différents *tokenizers*. Par conséquent, la mesure la plus couramment utilisée aujourd'hui pour évaluer les modèles de traduction est [SacreBLEU](https://github.com/mjpost/sacrebleu), qui remédie à cette faiblesse (et à d'autres) en standardisant l'étape de tokenisation. Pour utiliser cette métrique, nous devons d'abord installer la bibliothèque SacreBLEU :
+
+```py
+!pip install sacrebleu
+```
+
+Nous pouvons ensuite le charger via `load_metric()` comme nous l'avons fait dans le [Chapitre 3](/course/fr/chapter3) :
+
+```py
+from datasets import load_metric
+
+metric = load_metric("sacrebleu")
+```
+
+Cette métrique prend des textes comme entrées et cibles. Elle est conçue pour accepter plusieurs cibles acceptables, car il y a souvent plusieurs traductions acceptables de la même phrase. Le jeu de données que nous utilisons n'en fournit qu'une seule, mais il n'est pas rare en NLP de trouver des jeux de données qui donnent plusieurs phrases comme étiquettes. Ainsi, les prédictions doivent être une liste de phrases, mais les références doivent être une liste de listes de phrases.
+
+Essayons un exemple :
+
+```py
+predictions = [
+ "This plugin lets you translate web pages between several languages automatically."
+]
+references = [
+ [
+ "This plugin allows you to automatically translate web pages between several languages."
+ ]
+]
+metric.compute(predictions=predictions, references=references)
+```
+
+```python out
+{'score': 46.750469682990165,
+ 'counts': [11, 6, 4, 3],
+ 'totals': [12, 11, 10, 9],
+ 'precisions': [91.67, 54.54, 40.0, 33.33],
+ 'bp': 0.9200444146293233,
+ 'sys_len': 12,
+ 'ref_len': 13}
+```
+
+Cela donne un score BLEU de 46.75, ce qui est plutôt bon. Pour référence, le Transformer original dans l'article ["Attention Is All You Need"](https://arxiv.org/pdf/1706.03762.pdf) a obtenu un score BLEU de 41.8 sur une tâche de traduction similaire entre l'anglais et le français ! (Pour plus d'informations sur les métriques individuelles, comme `counts` et `bp`, voir le [Dépôt SacreBLEU](https://github.com/mjpost/sacrebleu/blob/078c440168c6adc89ba75fe6d63f0d922d42bcfe/sacrebleu/metrics/bleu.py#L74).) D'autre part, si nous essayons avec les deux mauvais types de prédictions (batchs de répétitions ou trop courts) qui sortent souvent des modèles de traduction, nous obtiendrons des scores BLEU plutôt mauvais :
+
+```py
+predictions = ["This This This This"]
+references = [
+ [
+ "This plugin allows you to automatically translate web pages between several languages."
+ ]
+]
+metric.compute(predictions=predictions, references=references)
+```
+
+```python out
+{'score': 1.683602693167689,
+ 'counts': [1, 0, 0, 0],
+ 'totals': [4, 3, 2, 1],
+ 'precisions': [25.0, 16.67, 12.5, 12.5],
+ 'bp': 0.10539922456186433,
+ 'sys_len': 4,
+ 'ref_len': 13}
+```
+
+```py
+predictions = ["This plugin"]
+references = [
+ [
+ "This plugin allows you to automatically translate web pages between several languages."
+ ]
+]
+metric.compute(predictions=predictions, references=references)
+```
+
+```python out
+{'score': 0.0,
+ 'counts': [2, 1, 0, 0],
+ 'totals': [2, 1, 0, 0],
+ 'precisions': [100.0, 100.0, 0.0, 0.0],
+ 'bp': 0.004086771438464067,
+ 'sys_len': 2,
+ 'ref_len': 13}
+```
+
+Le score peut aller de 0 à 100, et plus il est élevé, mieux c'est.
+
+{#if fw === 'tf'}
+
+Pour passer des sorties du modèle aux textes que la métrique peut utiliser, nous allons utiliser la méthode `tokenizer.batch_decode()`. Nous devons juste nettoyer tous les `-100` dans les étiquettes ; le *tokenizer* fera automatiquement la même chose pour le *token* de *padding*. Définissons une fonction qui prend notre modèle et un jeu de données et calcule des métriques sur ceux-ci. Comme la génération de longues séquences peut être lente, nous sous-échantillonnons l'ensemble de validation pour nous assurer que cela ne prend pas une éternité :
+
+```py
+import numpy as np
+
+
+def compute_metrics():
+ all_preds = []
+ all_labels = []
+ sampled_dataset = tokenized_datasets["validation"].shuffle().select(range(200))
+ tf_generate_dataset = sampled_dataset.to_tf_dataset(
+ columns=["input_ids", "attention_mask", "labels"],
+ collate_fn=data_collator,
+ shuffle=False,
+ batch_size=4,
+ )
+ for batch in tf_generate_dataset:
+ predictions = model.generate(
+ input_ids=batch["input_ids"], attention_mask=batch["attention_mask"]
+ )
+ decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
+ labels = batch["labels"].numpy()
+ labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
+ decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
+ decoded_preds = [pred.strip() for pred in decoded_preds]
+ decoded_labels = [[label.strip()] for label in decoded_labels]
+ all_preds.extend(decoded_preds)
+ all_labels.extend(decoded_labels)
+
+ result = metric.compute(predictions=all_preds, references=all_labels)
+ return {"bleu": result["score"]}
+```
+
+{:else}
+
+Pour passer des sorties du modèle aux textes utilisables par la métrique, nous allons utiliser la méthode `tokenizer.batch_decode()`. Nous devons juste nettoyer tous les `-100`s dans les étiquettes (le tokenizer fera automatiquement la même chose pour le token de remplissage) :
+
+```py
+import numpy as np
+
+
+def compute_metrics(eval_preds):
+ preds, labels = eval_preds
+ # In case the model returns more than the prediction logits
+ if isinstance(preds, tuple):
+ preds = preds[0]
+
+ decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)
+
+ # Replace -100s in the labels as we can't decode them
+ labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
+ decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
+
+ # Some simple post-processing
+ decoded_preds = [pred.strip() for pred in decoded_preds]
+ decoded_labels = [[label.strip()] for label in decoded_labels]
+
+ result = metric.compute(predictions=decoded_preds, references=decoded_labels)
+ return {"bleu": result["score"]}
+```
+
+{/if}
+
+Maintenant que c'est fait, nous sommes prêts à affiner notre modèle !
+
+
+### *Finetuner* le modèle
+
+La première étape consiste à se connecter à Hugging Face, afin de pouvoir télécharger vos résultats sur le *Hub*. Il y a une fonction pratique pour vous aider à le faire dans un *notebook* :
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Cela affichera un widget où vous pourrez entrer vos identifiants de connexion à Hugging Face.
+
+Si vous ne travaillez pas dans un *notebook*, tapez simplement la ligne suivante dans votre terminal :
+
+```bash
+huggingface-cli login
+```
+
+{#if fw === 'tf'}
+
+Avant de commencer, voyons quel type de résultats nous obtenons avec notre modèle sans entraînement :
+
+```py
+print(compute_metrics())
+```
+
+```
+{'bleu': 33.26983701454733}
+```
+
+Une fois ceci fait, nous pouvons préparer tout ce dont nous avons besoin pour compiler et entraîner notre modèle. Notez l'utilisation de `tf.keras.mixed_precision.set_global_policy("mixed_float16")`. Ceci indiquera à Keras de s'entraîner en utilisant float16, ce qui peut donner un gain de vitesse significatif sur les GPUs qui le supportent (Nvidia 20xx/V100 ou plus récent).
+
+```python
+from transformers import create_optimizer
+from transformers.keras_callbacks import PushToHubCallback
+import tensorflow as tf
+
+# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans l'ensemble de données, divisé par la taille du lot, puis multiplié par le nombre total d'époques.
+# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un lot tf.data.Dataset,
+# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size.
+num_epochs = 3
+num_train_steps = len(tf_train_dataset) * num_epochs
+
+optimizer, schedule = create_optimizer(
+ init_lr=5e-5,
+ num_warmup_steps=0,
+ num_train_steps=num_train_steps,
+ weight_decay_rate=0.01,
+)
+model.compile(optimizer=optimizer)
+
+# Entraîner en mixed-precision float16
+tf.keras.mixed_precision.set_global_policy("mixed_float16")
+```
+
+Ensuite, nous définissons un `PushToHubCallback` pour télécharger notre modèle sur le *Hub* pendant l'entraînement, comme nous l'avons vu dans [section 2]((/course/fr/chapter7/2)), et ensuite nous ajustons simplement le modèle avec ce callback :
+
+```python
+from transformers.keras_callbacks import PushToHubCallback
+
+callback = PushToHubCallback(
+ output_dir="marian-finetuned-kde4-en-to-fr", tokenizer=tokenizer
+)
+
+model.fit(
+ tf_train_dataset,
+ validation_data=tf_eval_dataset,
+ callbacks=[callback],
+ epochs=num_epochs,
+)
+```
+
+Notez que vous pouvez spécifier le nom du référentiel vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"``Seq2SeqTrainingArguments`. Par défaut, le référentiel utilisé sera dans votre espace de noms et nommé après le répertoire de sortie que vous avez défini, donc ici ce sera `"sgugger/marian-finetuned-kde4-en-to-fr"` (qui est le modèle que nous avons lié au début de cette section).
+
+
+
+💡 Si le répertoire de sortie que vous utilisez existe déjà, il doit être un clone local du dépôt vers lequel vous voulez pousser. S'il ne l'est pas, vous obtiendrez une erreur lors de l'appel de `model.fit()` et devrez définir un nouveau nom.
+
+
+
+Enfin, voyons à quoi ressemblent nos mesures maintenant que l'entraînement est terminé :
+
+```py
+print(compute_metrics())
+```
+
+```
+{'bleu': 57.334066271545865}
+```
+
+À ce stade, vous pouvez utiliser le widget d'inférence sur le *Hub* pour tester votre modèle et le partager avec vos amis. Vous avez réussi à *finetuner* un modèle sur une tâche de traduction. Félicitations !
+
+{:else}
+
+Une fois ceci fait, nous pouvons définir notre `Seq2SeqTrainingArguments`. Comme pour le `Trainer`, nous utilisons une sous-classe de `TrainingArguments` qui contient quelques champs supplémentaires :
+
+```python
+from transformers import Seq2SeqTrainingArguments
+
+args = Seq2SeqTrainingArguments(
+ f"marian-finetuned-kde4-en-to-fr",
+ evaluation_strategy="no",
+ save_strategy="epoch",
+ learning_rate=2e-5,
+ per_device_train_batch_size=32,
+ per_device_eval_batch_size=64,
+ weight_decay=0.01,
+ save_total_limit=3,
+ num_train_epochs=3,
+ predict_with_generate=True,
+ fp16=True,
+ push_to_hub=True,
+)
+```
+
+En dehors des hyperparamètres habituels (comme le taux d'apprentissage, le nombre d'époques, la taille du lot et une certaine décroissance des poids), voici quelques changements par rapport à ce que nous avons vu dans les sections précédentes :
+
+- nous ne définissons pas d'évaluation régulière, car l'évaluation prend du temps ; nous allons juste évaluer notre modèle une fois avant l'entraînement et après,
+- nous avons mis `fp16=True`, ce qui accélère l'entraînement sur les GPUs modernes,
+- nous définissons `predict_with_generate=True`, comme discuté ci-dessus,
+- nous utilisons `push_to_hub=True` pour télécharger le modèle sur le *Hub* à la fin de chaque époque.
+
+Notez que vous pouvez spécifier le nom complet du référentiel vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` `Seq2SeqTrainingArguments`. Par défaut, le référentiel utilisé sera dans votre espace de noms et nommé d'après le répertoire de sortie que vous avez défini, donc dans notre cas ce sera `"sgugger/marian-finetuned-kde4-en-to-fr"` (qui est le modèle que nous avons lié au début de cette section).
+
+
+
+💡 Si le répertoire de sortie que vous utilisez existe déjà, il doit être un clone local du dépôt vers lequel vous voulez pousser. S'il ne l'est pas, vous obtiendrez une erreur lors de la définition de votre `Seq2SeqTrainer` et devrez définir un nouveau nom.
+
+
+
+
+Enfin, nous passons tout au `Seq2SeqTrainer` :
+
+```python
+from transformers import Seq2SeqTrainer
+
+trainer = Seq2SeqTrainer(
+ model,
+ args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ tokenizer=tokenizer,
+ compute_metrics=compute_metrics,
+)
+```
+
+Avant d'entraîner, nous allons d'abord regarder le score obtenu par notre modèle, pour vérifier que nous n'aggravons pas les choses avec notre *finetuning*. Cette commande va prendre un peu de temps, vous pouvez donc prendre un café pendant qu'elle s'exécute :
+
+```python
+trainer.evaluate(max_length=max_target_length)
+```
+
+```python out
+{'eval_loss': 1.6964408159255981,
+ 'eval_bleu': 39.26865061007616,
+ 'eval_runtime': 965.8884,
+ 'eval_samples_per_second': 21.76,
+ 'eval_steps_per_second': 0.341}
+```
+
+A BLEU score of 39 is not too bad, which reflects the fact that our model is already good at translating English sentences to French ones.
+
+Next is the training, which will also take a bit of time:
+
+```python
+trainer.train()
+```
+
+Notez que pendant l'entraînement, chaque fois que le modèle est sauvegardé (ici, à chaque époque), il est téléchargé sur le *Hub* en arrière-plan. De cette façon, vous serez en mesure de reprendre votre entraînement sur une autre machine si nécessaire.
+
+Une fois l'entraînement terminé, nous évaluons à nouveau notre modèle - avec un peu de chance, nous verrons une amélioration du score BLEU !
+
+```py
+trainer.evaluate(max_length=max_target_length)
+```
+
+```python out
+{'eval_loss': 0.8558505773544312,
+ 'eval_bleu': 52.94161337775576,
+ 'eval_runtime': 714.2576,
+ 'eval_samples_per_second': 29.426,
+ 'eval_steps_per_second': 0.461,
+ 'epoch': 3.0}
+```
+
+C'est une amélioration de près de 14 points, ce qui est formidable.
+
+Enfin, nous utilisons la méthode `push_to_hub()` pour nous assurer que nous téléchargeons la dernière version du modèle. Le `Trainer` rédige également une carte modèle avec tous les résultats de l'évaluation et la télécharge. Cette carte de modèle contient des métadonnées qui aident le *Hub* à choisir le widget pour la démo d'inférence. Habituellement, il n'y a pas besoin de dire quoi que ce soit car il peut inférer le bon *widget* à partir de la classe du modèle, mais dans ce cas, la même classe de modèle peut être utilisée pour toutes sortes de problèmes de séquence à séquence, donc nous spécifions que c'est un modèle de traduction :
+
+```py
+trainer.push_to_hub(tags="translation", commit_message="Training complete")
+```
+
+Cette commande renvoie l'URL du commit qu'elle vient de faire, si vous voulez l'inspecter :
+
+```python out
+'https://huggingface.co/sgugger/marian-finetuned-kde4-en-to-fr/commit/3601d621e3baae2bc63d3311452535f8f58f6ef3'
+```
+
+À ce stade, vous pouvez utiliser le widget d'inférence sur le Hub du modèle pour tester votre modèle et le partager avec vos amis. Vous avez réussi à *finetuner* un modèle sur une tâche de traduction. Félicitations !
+
+Si vous souhaitez vous plonger un peu plus profondément dans la boucle d'entraînement, nous allons maintenant vous montrer comment faire la même chose en utilisant 🤗 *Accelerate*.
+
+{/if}
+
+{#if fw === 'pt'}
+
+## Une boucle d'entraînement personnalisée
+
+Jetons maintenant un coup d'œil à la boucle d'entraînement complète, afin que vous puissiez facilement personnaliser les parties dont vous avez besoin. Elle ressemblera beaucoup à ce que nous avons fait dans la [section 2](/course/fr/chapter7/2) et le [chapitre 3](/course/fr/chapter3/4).
+
+### Préparer le tout pour l'entraînement
+
+Vous avez vu tout cela plusieurs fois maintenant, donc nous allons passer en revue le code assez rapidement. D'abord, nous allons construire le `DataLoader` à partir de nos jeux de données, après avoir configuré les jeux de données au format `"torch"` pour obtenir les tenseurs PyTorch :
+
+```py
+from torch.utils.data import DataLoader
+
+tokenized_datasets.set_format("torch")
+train_dataloader = DataLoader(
+ tokenized_datasets["train"],
+ shuffle=True,
+ collate_fn=data_collator,
+ batch_size=8,
+)
+eval_dataloader = DataLoader(
+ tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
+)
+```
+
+Ensuite, nous réinstantifions notre modèle, pour nous assurer que nous ne poursuivons pas l'affinage précédent, mais que nous repartons du modèle pré-entraîné :
+
+```py
+model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
+```
+
+Nous aurons alors besoin d'un optimiseur :
+
+```py
+from transformers import AdamW
+
+optimizer = AdamW(model.parameters(), lr=2e-5)
+```
+
+Une fois que nous avons tous ces objets, nous pouvons les envoyer à la méthode `accelerator.prepare()`. Rappelez-vous que si vous voulez vous entraîner sur des TPUs dans un *notebook* de Colab, vous devrez déplacer tout ce code dans une fonction d'entraînement, et qui ne devrait pas exécuter une cellule qui instancie un `Accelerator`.
+
+```py
+from accelerate import Accelerator
+
+accelerator = Accelerator()
+model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
+ model, optimizer, train_dataloader, eval_dataloader
+)
+```
+
+Maintenant que nous avons envoyé notre `train_dataloader` à `accelerator.prepare()`, nous pouvons utiliser sa longueur pour calculer le nombre d'étapes d'entraînement. Rappelez-vous que nous devrions toujours faire cela après avoir préparé le dataloader, car cette méthode va changer la longueur du `DataLoader`. Nous utilisons un programme linéaire classique du taux d'apprentissage à 0 :
+
+```py
+from transformers import get_scheduler
+
+num_train_epochs = 3
+num_update_steps_per_epoch = len(train_dataloader)
+num_training_steps = num_train_epochs * num_update_steps_per_epoch
+
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+```
+
+Enfin, pour pousser notre modèle vers le Hub, nous aurons besoin de créer un objet `Repository` dans un dossier de travail. Tout d'abord, connectez-vous au *Hub*, si vous n'êtes pas déjà connecté. Nous déterminerons le nom du dépôt à partir de l'ID du modèle que nous voulons donner à notre modèle (n'hésitez pas à remplacer le `repo_name` par votre propre choix ; il doit juste contenir votre nom d'utilisateur, ce que fait la fonction `get_full_repo_name()`) :
+
+```py
+from huggingface_hub import Repository, get_full_repo_name
+
+model_name = "marian-finetuned-kde4-en-to-fr-accelerate"
+repo_name = get_full_repo_name(model_name)
+repo_name
+```
+
+```python out
+'sgugger/marian-finetuned-kde4-en-to-fr-accelerate'
+```
+
+Ensuite, nous pouvons cloner ce référentiel dans un dossier local. S'il existe déjà, ce dossier local doit être un clone du référentiel avec lequel nous travaillons :
+
+```py
+output_dir = "marian-finetuned-kde4-en-to-fr-accelerate"
+repo = Repository(output_dir, clone_from=repo_name)
+```
+
+Nous pouvons maintenant télécharger tout ce que nous sauvegardons dans `output_dir` en appelant la méthode `repo.push_to_hub()`. Cela nous aidera à télécharger les modèles intermédiaires à la fin de chaque époque.
+
+### Boucle d'entraînement
+
+Nous sommes maintenant prêts à écrire la boucle d'entraînement complète. Pour simplifier sa partie évaluation, nous définissons cette fonction `postprocess()` qui prend les prédictions et les étiquettes et les convertit en listes de chaînes de caractères que notre objet `metric` attend :
+
+```py
+def postprocess(predictions, labels):
+ predictions = predictions.cpu().numpy()
+ labels = labels.cpu().numpy()
+
+ decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
+
+ # Remplacer -100 dans les étiquettes car nous ne pouvons pas les décoder.
+ labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
+ decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
+
+ # Quelques post-traitements simples
+ decoded_preds = [pred.strip() for pred in decoded_preds]
+ decoded_labels = [[label.strip()] for label in decoded_labels]
+ return decoded_preds, decoded_labels
+```
+
+La boucle d'entraînement ressemble beaucoup à celles de [section 2](/course/fr/chapter7/2) et [chapitre 3](/course/fr/chapter3), avec quelques différences dans la partie évaluation -- alors concentrons-nous sur cela !
+
+La première chose à noter est que nous utilisons la méthode `generate()` pour calculer les prédictions, mais c'est une méthode sur notre modèle de base, pas le modèle enveloppé 🤗 Accelerate créé dans la méthode `prepare()`. C'est pourquoi nous déballons d'abord le modèle, puis nous appelons cette méthode.
+
+La deuxième chose est que, comme avec [token classification](/course/fr/chapter7/2), deux processus peuvent avoir paddé les entrées et les étiquettes à des formes différentes, donc nous utilisons `accelerator.pad_across_processes()` pour rendre les prédictions et les étiquettes de la même forme avant d'appeler la méthode `gather()`. Si nous ne faisons pas cela, l'évaluation va soit se tromper, soit se bloquer pour toujours.
+
+```py
+from tqdm.auto import tqdm
+import torch
+
+progress_bar = tqdm(range(num_training_steps))
+
+for epoch in range(num_train_epochs):
+ # Entraînement
+ model.train()
+ for batch in train_dataloader:
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+
+ # Evaluation
+ model.eval()
+ for batch in tqdm(eval_dataloader):
+ with torch.no_grad():
+ generated_tokens = accelerator.unwrap_model(model).generate(
+ batch["input_ids"],
+ attention_mask=batch["attention_mask"],
+ max_length=128,
+ )
+ labels = batch["labels"]
+
+ # Nécessaire pour rembourrer les prédictions et les étiquettes à rassembler
+ generated_tokens = accelerator.pad_across_processes(
+ generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
+ )
+ labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)
+
+ predictions_gathered = accelerator.gather(generated_tokens)
+ labels_gathered = accelerator.gather(labels)
+
+ decoded_preds, decoded_labels = postprocess(predictions_gathered, labels_gathered)
+ metric.add_batch(predictions=decoded_preds, references=decoded_labels)
+
+ results = metric.compute()
+ print(f"epoch {epoch}, BLEU score: {results['score']:.2f}")
+
+ # Sauvegarder et télécharger
+ accelerator.wait_for_everyone()
+ unwrapped_model = accelerator.unwrap_model(model)
+ unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
+ if accelerator.is_main_process:
+ tokenizer.save_pretrained(output_dir)
+ repo.push_to_hub(
+ commit_message=f"Training in progress epoch {epoch}", blocking=False
+ )
+```
+
+```python out
+epoch 0, BLEU score: 53.47
+epoch 1, BLEU score: 54.24
+epoch 2, BLEU score: 54.44
+```
+
+Une fois que c'est fait, vous devriez avoir un modèle qui a des résultats assez similaires à celui entraîné avec le `Seq2SeqTrainer`. Vous pouvez vérifier celui que nous avons formé en utilisant ce code à [*huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate*](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate). Et si vous voulez tester des modifications de la boucle d'entraînement, vous pouvez les mettre en œuvre directement en modifiant le code ci-dessus !
+
+{/if}
+
+### Utilisation du modèle *finetuné*.
+
+Nous vous avons déjà montré comment vous pouvez utiliser le modèle que nous avons *finetuné* sur le *Hub* avec le *widget* d'inférence. Pour l'utiliser localement dans un `pipeline`, nous devons juste spécifier l'identifiant de modèle approprié :
+
+```py
+from transformers import pipeline
+
+# Remplacez ceci par votre propre checkpoint
+model_checkpoint = "huggingface-course/marian-finetuned-kde4-en-to-fr"
+translator = pipeline("translation", model=model_checkpoint)
+translator("Default to expanded threads")
+```
+
+```python out
+[{'translation_text': 'Par défaut, développer les fils de discussion'}]
+```
+
+Comme prévu, notre modèle pré-entraîné a adapté ses connaissances au corpus sur lequel nous l'avons affiné, et au lieu de laisser le mot anglais "threads" seul, il le traduit maintenant par la version officielle française. Il en va de même pour "plugin" :
+
+```py
+translator(
+ "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
+)
+```
+
+```python out
+[{'translation_text': "Impossible d'importer %1 en utilisant le module externe d'importation OFX. Ce fichier n'est pas le bon format."}]
+```
+
+Un autre excellent exemple d'adaptation au domaine !
+
+
+
+✏️ **Votre tour !** Que retourne le modèle sur l'échantillon avec le mot "email" que vous avez identifié plus tôt ?
+
+
diff --git a/chapters/fr/chapter7/5.mdx b/chapters/fr/chapter7/5.mdx
index cfac55b89..0ba896f6d 100644
--- a/chapters/fr/chapter7/5.mdx
+++ b/chapters/fr/chapter7/5.mdx
@@ -1,1065 +1,1065 @@
-
-
-# Résumé de textes
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-
-Dans cette section, nous allons voir comment les *transformers* peuvent être utilisés pour condenser de longs documents en résumés, une tâche connue sous le nom de _résumé de texte_. Il s'agit de l'une des tâches de NLP les plus difficiles car elle requiert une série de capacités, telles que la compréhension de longs passages et la génération d'un texte cohérent qui capture les sujets principaux d'un document. Cependant, lorsqu'il est bien fait, le résumé de texte est un outil puissant qui peut accélérer divers processus commerciaux en soulageant les experts du domaine de la lecture détaillée de longs documents.
-
-
-
-Bien qu'il existe déjà plusieurs modèles *finetunés* pour le résumé sur le [Hugging Face Hub](https://huggingface.co/models?pipeline_tag=summarization&sort=downloads), la plupart d'entre eux ne sont adaptés qu'aux documents en anglais. Ainsi, pour ajouter une touche d'originalité à cette section, nous allons entraîner un modèle bilingue pour l'anglais et l'espagnol. À la fin de cette section, vous disposerez d'un [modèle](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es) capable de résumer les commentaires des clients comme celui présenté ici :
-
-
-
-
-Comme nous allons le voir, ces résumés sont concis car ils sont appris à partir des titres que les clients fournissent dans leurs commentaires sur les produits. Commençons par constituer un corpus bilingue approprié pour cette tâche.
-
-## Préparation d'un corpus multilingue
-
-Nous allons utiliser le [Multilingual Amazon Reviews Corpus](https://huggingface.co/datasets/amazon_reviews_multi) pour créer notre résumeur bilingue. Ce corpus est constitué d'évaluations de produits Amazon en six langues et est généralement utilisé pour évaluer les classificateurs multilingues. Cependant, comme chaque critique est accompagnée d'un titre court, nous pouvons utiliser les titres comme résumés cibles pour l'apprentissage de notre modèle ! Pour commencer, téléchargeons les sous-ensembles anglais et espagnols depuis le *Hub* :
-
-```python
-from datasets import load_dataset
-
-spanish_dataset = load_dataset("amazon_reviews_multi", "es")
-english_dataset = load_dataset("amazon_reviews_multi", "en")
-english_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
- num_rows: 200000
- })
- validation: Dataset({
- features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
- num_rows: 5000
- })
- test: Dataset({
- features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
- num_rows: 5000
- })
-})
-```
-
-Comme vous pouvez le voir, pour chaque langue, il y a 200 000 évaluations pour la partie "entraînement", et 5 000 évaluations pour chacune des parties "validation" et "test". Les informations qui nous intéressent sont contenues dans les colonnes `review_body` et `review_title`. Voyons quelques exemples en créant une fonction simple qui prend un échantillon aléatoire de l'ensemble d'entraînement avec les techniques apprises au [Chapitre 5](/course/fr/chapter5) :
-
-```python
-def show_samples(dataset, num_samples=3, seed=42):
- sample = dataset["train"].shuffle(seed=seed).select(range(num_samples))
- for example in sample:
- print(f"\n'>> Title: {example['review_title']}'")
- print(f"'>> Review: {example['review_body']}'")
-
-
-show_samples(english_dataset)
-```
-
-```python out
-'>> Title: Worked in front position, not rear' # Travaillé en position avant, pas arrière
-'>> Review: 3 stars because these are not rear brakes as stated in the item description. At least the mount adapter only worked on the front fork of the bike that I got it for.'
-# 3 étoiles car ce ne sont pas des freins arrière comme indiqué dans la description de l'article. Au moins, l'adaptateur de montage ne fonctionnait que sur la fourche avant du vélo pour lequel je l'ai acheté.''
-
-'>> Title: meh'
-'>> Review: Does it’s job and it’s gorgeous but mine is falling apart, I had to basically put it together again with hot glue'
-# Il fait son travail et il est magnifique mais le mien est en train de tomber en morceaux, j'ai dû le recoller avec de la colle chaude.
-
-'>> Title: Can\'t beat these for the money' # On ne peut pas faire mieux pour le prix
-'>> Review: Bought this for handling miscellaneous aircraft parts and hanger "stuff" that I needed to organize; it really fit the bill. The unit arrived quickly, was well packaged and arrived intact (always a good sign). There are five wall mounts-- three on the top and two on the bottom. I wanted to mount it on the wall, so all I had to do was to remove the top two layers of plastic drawers, as well as the bottom corner drawers, place it when I wanted and mark it; I then used some of the new plastic screw in wall anchors (the 50 pound variety) and it easily mounted to the wall. Some have remarked that they wanted dividers for the drawers, and that they made those. Good idea. My application was that I needed something that I can see the contents at about eye level, so I wanted the fuller-sized drawers. I also like that these are the new plastic that doesn\'t get brittle and split like my older plastic drawers did. I like the all-plastic construction. It\'s heavy duty enough to hold metal parts, but being made of plastic it\'s not as heavy as a metal frame, so you can easily mount it to the wall and still load it up with heavy stuff, or light stuff. No problem there. For the money, you can\'t beat it. Best one of these I\'ve bought to date-- and I\'ve been using some version of these for over forty years.'
-# Je l'ai acheté pour manipuler diverses pièces d'avion et des "trucs" de hangar que je devais organiser ; il a vraiment fait l'affaire. L'unité est arrivée rapidement, était bien emballée et est arrivée intacte (toujours un bon signe). Il y a cinq supports muraux - trois sur le dessus et deux sur le dessous. Je voulais le monter sur le mur, alors tout ce que j'ai eu à faire était d'enlever les deux couches supérieures de tiroirs en plastique, ainsi que les tiroirs d'angle inférieurs, de le placer où je voulais et de le marquer ; j'ai ensuite utilisé quelques-uns des nouveaux ancrages muraux à vis en plastique (la variété de 50 livres) et il s'est facilement monté sur le mur. Certains ont fait remarquer qu'ils voulaient des séparateurs pour les tiroirs, et qu'ils les ont fabriqués. Bonne idée. Pour ma part, j'avais besoin de quelque chose dont je pouvais voir le contenu à hauteur des yeux, et je voulais donc des tiroirs plus grands. J'aime aussi le fait qu'il s'agisse du nouveau plastique qui ne se fragilise pas et ne se fend pas comme mes anciens tiroirs en plastique. J'aime la construction entièrement en plastique. Elle est suffisamment résistante pour contenir des pièces métalliques, mais étant en plastique, elle n'est pas aussi lourde qu'un cadre métallique, ce qui permet de la fixer facilement au mur et de la charger d'objets lourds ou légers. Aucun problème. Pour le prix, c'est imbattable. C'est le meilleur que j'ai acheté à ce jour, et j'utilise des versions de ce type depuis plus de quarante ans.
-
-```
-
-
-
-✏️ **Essayez !** Changez la graine aléatoire dans la commande `Dataset.shuffle()` pour explorer d'autres critiques dans le corpus. Si vous parlez espagnol, jetez un coup d'œil à certaines des critiques dans `spanish_dataset` pour voir si les titres semblent aussi être des résumés raisonnables.
-
-
-
-Cet échantillon montre la diversité des critiques que l'on trouve généralement en ligne, allant du positif au négatif (et tout ce qui se trouve entre les deux !). Bien que l'exemple avec le titre "meh" ne soit pas très informatif, les autres titres semblent être des résumés décents des critiques elles-mêmes. Entraîner un modèle de résumé sur l'ensemble des 400 000 avis prendrait beaucoup trop de temps sur un seul GPU, nous allons donc nous concentrer sur la génération de résumés pour un seul domaine de produits. Pour avoir une idée des domaines parmi lesquels nous pouvons choisir, convertissons `english_dataset` en `pandas.DataFrame` et calculons le nombre d'avis par catégorie de produits :
-
-```python
-english_dataset.set_format("pandas")
-english_df = english_dataset["train"][:]
-# Afficher les comptes des 20 premiers produits
-english_df["product_category"].value_counts()[:20]
-```
-
-```python out
-home 17679
-apparel 15951
-wireless 15717
-other 13418
-beauty 12091
-drugstore 11730
-kitchen 10382
-toy 8745
-sports 8277
-automotive 7506
-lawn_and_garden 7327
-home_improvement 7136
-pet_products 7082
-digital_ebook_purchase 6749
-pc 6401
-electronics 6186
-office_product 5521
-shoes 5197
-grocery 4730
-book 3756
-Name: product_category, dtype: int64
-```
-
-Les produits les plus populaires dans l'ensemble de données anglaises concernent les articles ménagers, les vêtements et l'électronique sans fil. Pour rester dans le thème d'Amazon, nous allons nous concentrer sur le résumé des critiques de livres. Après tout, c'est la raison d'être de l'entreprise ! Nous pouvons voir deux catégories de produits qui correspondent à nos besoins (`book` et `digital_ebook_purchase`), nous allons donc filtrer les ensembles de données dans les deux langues pour ces produits uniquement. Comme nous l'avons vu dans le [Chapitre 5](/course/fr/chapter5), la fonction `Dataset.filter()` nous permet de découper un jeu de données de manière très efficace, nous pouvons donc définir une fonction simple pour le faire :
-
-```python
-def filter_books(example):
- return (
- example["product_category"] == "book"
- or example["product_category"] == "digital_ebook_purchase"
- )
-```
-
-Maintenant, lorsque nous appliquons cette fonction à `english_dataset` et `spanish_dataset`, le résultat ne contiendra que les lignes impliquant les catégories de livres. Avant d'appliquer le filtre, changeons le format de `english_dataset` de `"pandas"` à `"arrow"` :
-
-```python
-english_dataset.reset_format()
-```
-
-Nous pouvons ensuite appliquer la fonction de filtrage et, à titre de vérification, inspecter un échantillon de critiques pour voir si elles portent bien sur des livres :
-
-```python
-spanish_books = spanish_dataset.filter(filter_books)
-english_books = english_dataset.filter(filter_books)
-show_samples(english_books)
-```
-
-```python out
-'>> Title: I\'m dissapointed.' # Je suis déçu
-'>> Review: I guess I had higher expectations for this book from the reviews. I really thought I\'d at least like it. The plot idea was great. I loved Ash but, it just didnt go anywhere. Most of the book was about their radio show and talking to callers. I wanted the author to dig deeper so we could really get to know the characters. All we know about Grace is that she is attractive looking, Latino and is kind of a brat. I\'m dissapointed.'
-# Je suppose que j'avais de plus grandes attentes pour ce livre d'après les critiques. Je pensais vraiment que j'allais au moins l'aimer. L'idée de l'intrigue était géniale. J'aimais Ash, mais ça n'allait nulle part. La plus grande partie du livre était consacrée à leur émission de radio et aux conversations avec les auditeurs. Je voulais que l'auteur creuse plus profondément pour que nous puissions vraiment connaître les personnages. Tout ce que nous savons de Grace, c'est qu'elle est séduisante, qu'elle est latino et qu'elle est une sorte de garce. Je suis déçue.
-
-'>> Title: Good art, good price, poor design' # Un bon art, un bon prix, un mauvais design
-'>> Review: I had gotten the DC Vintage calendar the past two years, but it was on backorder forever this year and I saw they had shrunk the dimensions for no good reason. This one has good art choices but the design has the fold going through the picture, so it\'s less aesthetically pleasing, especially if you want to keep a picture to hang. For the price, a good calendar'
-# J'ai eu le calendrier DC Vintage ces deux dernières années, mais il était en rupture de stock pour toujours cette année et j'ai vu qu'ils avaient réduit les dimensions sans raison valable. Celui-ci a de bons choix artistiques mais le design a le pli qui traverse l'image, donc c'est moins esthétique, surtout si vous voulez garder une image à accrocher. Pour le prix, c'est un bon calendrier.
-
-'>> Title: Helpful' Utile
-'>> Review: Nearly all the tips useful and. I consider myself an intermediate to advanced user of OneNote. I would highly recommend.'
-# Presque tous les conseils sont utiles et. Je me considère comme un utilisateur intermédiaire à avancé de OneNote. Je le recommande vivement.
-```
-
-D'accord, nous pouvons voir que les critiques ne concernent pas strictement les livres et peuvent se référer à des choses comme des calendriers et des applications électroniques telles que OneNote. Néanmoins, le domaine semble approprié pour entraîner un modèle de résumé. Avant de regarder les différents modèles qui conviennent à cette tâche, nous avons une dernière préparation de données à faire : combiner les critiques anglaises et espagnoles en un seul objet `DatasetDict`. 🤗 *Datasets* fournit une fonction pratique `concatenate_datasets()` qui (comme son nom l'indique) va empiler deux objets `Dataset` l'un sur l'autre. Ainsi, pour créer notre jeu de données bilingue, nous allons boucler sur chaque division, concaténer les jeux de données pour cette division, et mélanger le résultat pour s'assurer que notre modèle ne s'adapte pas trop à une seule langue :
-
-```python
-from datasets import concatenate_datasets, DatasetDict
-
-books_dataset = DatasetDict()
-
-for split in english_books.keys():
- books_dataset[split] = concatenate_datasets(
- [english_books[split], spanish_books[split]]
- )
- books_dataset[split] = books_dataset[split].shuffle(seed=42)
-
-# Quelques exemples
-show_samples(books_dataset)
-```
-
-```python out
-'>> Title: Easy to follow!!!!' # Facile à suivre!!!!
-'>> Review: I loved The dash diet weight loss Solution. Never hungry. I would recommend this diet. Also the menus are well rounded. Try it. Has lots of the information need thanks.'
-# J'ai adoré The dash diet weight loss Solution. Jamais faim. Je recommande ce régime. Les menus sont également bien arrondis. Essayez-le. Il contient beaucoup d'informations, merci.
-
-'>> Title: PARCIALMENTE DAÑADO' # PARTIELLEMENT ENDOMMAGÉ
-'>> Review: Me llegó el día que tocaba, junto a otros libros que pedí, pero la caja llegó en mal estado lo cual dañó las esquinas de los libros porque venían sin protección (forro).'
-# Il est arrivé le jour prévu, avec d'autres livres que j'avais commandés, mais la boîte est arrivée en mauvais état, ce qui a endommagé les coins des livres car ils étaient livrés sans protection (doublure).
-
-'>> Title: no lo he podido descargar' # Je n'ai pas pu le télécharger
-'>> Review: igual que el anterior' # même chose que ci-dessus
-```
-
-Cela ressemble certainement à un mélange de critiques anglaises et espagnoles ! Maintenant que nous avons un corpus d'entraînement, une dernière chose à vérifier est la distribution des mots dans les critiques et leurs titres. Ceci est particulièrement important pour les tâches de résumé, où les résumés de référence courts dans les données peuvent biaiser le modèle pour qu'il ne produise qu'un ou deux mots dans les résumés générés. Les graphiques ci-dessous montrent les distributions de mots, et nous pouvons voir que les titres sont fortement biaisés vers seulement 1 ou 2 mots :
-
-
-
-
-
-
-Pour y remédier, nous allons filtrer les exemples avec des titres très courts afin que notre modèle puisse produire des résumés plus intéressants. Puisque nous avons affaire à des textes anglais et espagnols, nous pouvons utiliser une heuristique grossière pour séparer les titres sur les espaces blancs, puis utiliser notre fidèle méthode `Dataset.filter()` comme suit :
-
-```python
-books_dataset = books_dataset.filter(lambda x: len(x["review_title"].split()) > 2)
-```
-
-Maintenant que nous avons préparé notre corpus, voyons quelques *transformers* possibles que l'on pourrait *finetuné* dessus !
-
-## Modèles pour le résumé de texte
-
-Si vous y pensez, le résumé de texte est une tâche similaire à la traduction automatique : nous avons un corps de texte, comme une critique, que nous aimerions "traduire" en une version plus courte qui capture les caractéristiques saillantes de l'entrée. En conséquence, la plupart des modèles Transformer pour le résumé adoptent l'architecture encodeur-décodeur que nous avons rencontrée pour la première fois dans le [Chapitre 1](/course/fr/chapter1), bien qu'il y ait quelques exceptions comme la famille de modèles GPT qui peut également être utilisée pour le résumé dans des contextes peu complexes. Le tableau suivant présente quelques modèles pré-entraînés populaires qui peuvent être *finetunés* pour le résumé.
-
-| *Transformers* | Description | Multilingue ? |
-| :---------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------: |
-| [GPT-2](https://huggingface.co/gpt2-xl) | Bien qu'il soit entraîné comme un modèle de langage autorégressif, vous pouvez faire en sorte que GPT-2 génère des résumés en ajoutant "TL;DR" à la fin du texte d'entrée. | ❌ |
-| [PEGASUS](https://huggingface.co/google/pegasus-large) | Utilise un objectif de pré-entraînement pour prédire les phrases masquées dans les textes à plusieurs phrases. Cet objectif de pré-entraînement est plus proche du résumé que de la modélisation du langage standard et obtient des scores élevés sur des benchmarks populaires. | ❌ |
-| [T5](https://huggingface.co/t5-base) | Une architecture universelle de *transformer* qui formule toutes les tâches dans un cadre texte à texte ; par exemple, le format d'entrée du modèle pour résumer un document est `summarize : ARTICLE`. | ❌ |
-| [mT5](https://huggingface.co/google/mt5-base) | Une version multilingue de T5, pré-entraînée sur le corpus multilingue Common Crawl (mC4), couvrant 101 langues. | ✅ |
-| [BART](https://huggingface.co/facebook/bart-base) | Une architecture de *transformer* avec une pile d'encodeurs et de décodeurs entraînés pour reconstruire l'entrée corrompue qui combine les schémas de pré-entraînement de BERT et GPT-2. | ❌ |
-| [mBART-50](https://huggingface.co/facebook/mbart-large-50) | Une version multilingue de BART, pré-entraînée sur 50 langues. | ✅ |
-
-Comme vous pouvez le voir dans ce tableau, la majorité des modèles Transformer pour le résumé (et en fait la plupart des tâches de NLP) sont monolingues. C'est une bonne chose si votre tâche se déroule dans une langue "à haute ressource" comme l'anglais ou l'allemand, mais moins pour les milliers d'autres langues utilisées dans le monde. Heureusement, il existe une catégorie de modèles Transformer multilingues, comme mT5 et mBART, qui viennent à la rescousse. Ces modèles sont pré-entraînés en utilisant la modélisation du langage, mais avec une particularité : au lieu de s'entraîner sur un corpus d'une seule langue, ils sont entraînés conjointement sur des textes dans plus de 50 langues à la fois !
-
-Nous allons nous concentrer sur mT5, une architecture intéressante basée sur T5 qui a été pré-entraînée dans un cadre texte à texte. Dans T5, chaque tâche NLP est formulée en termes d'un préfixe d'invite comme `summarize:` qui conditionne le modèle à adapter le texte généré à l'invite. Comme le montre la figure ci-dessous, cela rend T5 extrêmement polyvalent, car vous pouvez résoudre de nombreuses tâches avec un seul modèle !
-
-
-
-
-
-
-mT5 n'utilise pas de préfixes, mais partage une grande partie de la polyvalence de T5 et a l'avantage d'être multilingue. Maintenant que nous avons choisi un modèle, voyons comment préparer nos données pour l'entraînement.
-
-
-
-
-✏️ **Essayez** Une fois que vous avez travaillé sur cette section, voyez comment mT5 se compare à mBART en affinant ce dernier avec les mêmes techniques. Pour des points bonus, vous pouvez aussi essayer de *finetuner* le T5 uniquement sur les critiques anglaises. Puisque le T5 a un préfixe spécial, vous devrez ajouter `summarize:` aux exemples d'entrée dans les étapes de prétraitement ci-dessous.
-
-
-
-## Prétraitement des données
-
-
-
-Notre prochaine tâche est de tokeniser et d'encoder nos critiques et leurs titres. Comme d'habitude, nous commençons par charger le *tokenizer* associé au point de contrôle du modèle pré-entraîné. Nous utiliserons `mt5-small` comme point de contrôle afin de pouvoir affiner le modèle en un temps raisonnable :
-
-```python
-from transformers import AutoTokenizer
-
-model_checkpoint = "google/mt5-small"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-```
-
-
-
-💡 Aux premiers stades de vos projets de NLP, une bonne pratique consiste à entraîner une classe de "petits" modèles sur un petit échantillon de données. Cela vous permet de déboguer et d'itérer plus rapidement vers un flux de travail de bout en bout. Une fois que vous avez confiance dans les résultats, vous pouvez toujours faire évoluer le modèle en changeant simplement le point de contrôle du modèle !
-
-
-
-Testons le *tokenizer* de mT5 sur un petit exemple :
-
-```python
-inputs = tokenizer(
- "I loved reading the Hunger Games!"
-) # J'ai adoré lire les Hunger Games !
-inputs
-```
-
-```python out
-{'input_ids': [336, 259, 28387, 11807, 287, 62893, 295, 12507, 1], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
-```
-
-Ici nous pouvons voir les familiers `input_ids` et `attention_mask` que nous avons rencontrés dans nos premières expériences de fine-tuning au [Chapitre 3](/course/fr/chapter3). Décodons ces identifiants d'entrée avec la fonction `convert_ids_to_tokens()` du tokenizer pour voir à quel type de tokenizer nous avons affaire :
-
-```python
-tokenizer.convert_ids_to_tokens(inputs.input_ids)
-```
-
-```python out
-['▁I', '▁', 'loved', '▁reading', '▁the', '▁Hung', 'er', '▁Games', '']
-```
-
-Le caractère spécial Unicode `▁` et le *token* de fin de séquence `` indiquent que nous avons affaire au *tokenizer* de SentencePiece, qui est basé sur l'algorithme de segmentation Unigram discuté dans le [Chapitre 6](/course/chapter6). Unigram est particulièrement utile pour les corpus multilingues car il permet à SentencePiece d'être agnostique vis-à-vis des accents, de la ponctuation et du fait que de nombreuses langues, comme le japonais, n'ont pas de caractères d'espacement.
-
-Pour tokeniser notre corpus, nous devons faire face à une subtilité associée au résumé : comme nos étiquettes sont également du texte, il est possible qu'elles dépassent la taille maximale du contexte du modèle. Cela signifie que nous devons appliquer une troncature à la fois aux critiques et à leurs titres pour nous assurer de ne pas transmettre des entrées trop longues à notre modèle. Les tokenizers de 🤗 *Transformers* fournissent une fonction très pratique `as_target_tokenizer()` qui vous permet de tokeniser les étiquettes en parallèle avec les entrées. Ceci est typiquement fait en utilisant un gestionnaire de contexte à l'intérieur d'une fonction de prétraitement qui encode d'abord les entrées, et ensuite encode les étiquettes comme une colonne séparée. Voici un exemple d'une telle fonction pour mT5 :
-
-```python
-max_input_length = 512
-max_target_length = 30
-
-
-def preprocess_function(examples):
- model_inputs = tokenizer(
- examples["review_body"], max_length=max_input_length, truncation=True
- )
- # Configurer le *tokenizer* pour les cibles.
- with tokenizer.as_target_tokenizer():
- labels = tokenizer(
- examples["review_title"], max_length=max_target_length, truncation=True
- )
-
- model_inputs["labels"] = labels["input_ids"]
- return model_inputs
-```
-
-Parcourons ce code pour comprendre ce qui se passe. La première chose que nous avons faite est de définir des valeurs pour `max_input_length` et `max_target_length`, qui fixent les limites supérieures de la longueur des commentaires et des titres. Comme le corps de la critique est généralement beaucoup plus long que le titre, nous avons mis ces valeurs à l'échelle en conséquence. Ensuite, dans la `preprocess_function()` elle-même, nous pouvons voir que les commentaires sont d'abord tokenizés, suivis par les titres avec `as_target_tokenizer()`.
-
-Avec la fonction `preprocess_function()`, il est alors simple de tokeniser l'ensemble du corpus en utilisant la fonction pratique `Dataset.map()` que nous avons largement utilisée dans ce cours :
-
-```python
-tokenized_datasets = books_dataset.map(preprocess_function, batched=True)
-```
-
-Maintenant que le corpus a été prétraité, examinons certaines métriques couramment utilisées pour le résumé. Comme nous allons le voir, il n'existe pas de solution miracle pour mesurer la qualité d'un texte généré par une machine.
-
-
-
-💡 Vous avez peut-être remarqué que nous avons utilisé `batched=True` dans notre fonction `Dataset.map()` ci-dessus. Cela permet de coder les exemples par lots de 1 000 (par défaut) et d'utiliser les capacités de multithreading des *tokenizers* rapides de 🤗 *Transformers*. Lorsque cela est possible, essayez d'utiliser `batched=True` pour tirer le meilleur parti de votre prétraitement !
-
-
-
-
-## Métriques pour le résumé de texte
-
-
-
-Par rapport à la plupart des autres tâches que nous avons abordées dans ce cours, la mesure des performances des tâches de génération de texte comme le résumé ou la traduction n'est pas aussi simple. Par exemple, pour une critique telle que "J'ai adoré lire les Hunger Games", il existe plusieurs résumés valides, comme "J'ai adoré Hunger Games" ou "Hunger Games est une excellente lecture". Il est clair que l'application d'une sorte de correspondance exacte entre le résumé généré et l'étiquette n'est pas une bonne solution - même les humains auraient de mauvais résultats avec une telle mesure, car nous avons tous notre propre style d'écriture.
-
-Pour le résumé, l'une des métriques les plus couramment utilisées est le [score ROUGE](https://en.wikipedia.org/wiki/ROUGE_(metric)) (abréviation de Recall-Oriented Understudy for Gisting Evaluation). L'idée de base de cette métrique est de comparer un résumé généré avec un ensemble de résumés de référence qui sont généralement créés par des humains. Pour être plus précis, supposons que nous voulions comparer les deux résumés suivants :
-
-```python
-generated_summary = "I absolutely loved reading the Hunger Games"
-reference_summary = "I loved reading the Hunger Games"
-```
-
-Une façon de les comparer pourrait être de compter le nombre de mots qui se chevauchent, qui dans ce cas serait de 6. Cependant, cette méthode est un peu grossière, c'est pourquoi ROUGE se base sur le calcul des scores de _précision_ et de _rappel_ pour le chevauchement.
-
-
-
-🙋 Ne vous inquiétez pas si c'est la première fois que vous entendez parler de précision et de rappel - nous allons parcourir ensemble quelques exemples explicites pour que tout soit clair. Ces métriques sont généralement rencontrées dans les tâches de classification, donc si vous voulez comprendre comment la précision et le rappel sont définis dans ce contexte, nous vous recommandons de consulter les [guides de `scikit-learn`](https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html).
-
-
-
-Pour ROUGE, le rappel mesure la proportion du résumé de référence qui est capturée par le résumé généré. Si nous ne faisons que comparer des mots, le rappel peut être calculé selon la formule suivante :
-
-$$ \mathrm{Recall} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, reference\, summary}} $$
-
-Pour notre exemple simple ci-dessus, cette formule donne un rappel parfait de 6/6 = 1 ; c'est-à-dire que tous les mots du résumé de référence ont été produits par le modèle. Cela peut sembler génial, mais imaginez que le résumé généré ait été "J'ai vraiment aimé lire les Hunger Games toute la nuit". Le rappel serait également parfait, mais le résumé serait sans doute moins bon puisqu'il serait verbeux. Pour traiter ces scénarios, nous calculons également la précision, qui, dans le contexte de ROUGE, mesure la proportion du résumé généré qui était pertinente :
-
-$$ \mathrm{Precision} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, generated\, summary}} $$
-
-En appliquant cela à notre résumé verbeux, on obtient une précision de 6/10 = 0,6, ce qui est considérablement moins bon que la précision de 6/7 = 0,86 obtenue par notre résumé plus court. En pratique, la précision et le rappel sont généralement calculés, puis le score F1 (la moyenne harmonique de la précision et du rappel) est indiqué. Nous pouvons le faire facilement dans 🤗 *Datasets* en installant d'abord le paquet `rouge_score` :
-
-```py
-!pip install rouge_score
-```
-
-et ensuite charger la métrique ROUGE comme suit :
-
-```python
-from datasets import load_metric
-
-rouge_score = load_metric("rouge")
-```
-
-Ensuite, nous pouvons utiliser la fonction `rouge_score.compute()` pour calculer toutes les métriques en une seule fois :
-
-```python
-scores = rouge_score.compute(
- predictions=[generated_summary], references=[reference_summary]
-)
-scores
-```
-
-```python out
-{'rouge1': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
- 'rouge2': AggregateScore(low=Score(precision=0.67, recall=0.8, fmeasure=0.73), mid=Score(precision=0.67, recall=0.8, fmeasure=0.73), high=Score(precision=0.67, recall=0.8, fmeasure=0.73)),
- 'rougeL': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
- 'rougeLsum': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92))}
-```
-
-Whoa, il y a un batch d'informations dans cette sortie. Qu'est-ce que ça veut dire ? Tout d'abord, 🤗 *Datasets* calcule en fait des intervalles de confiance pour la précision, le rappel et le score F1 ; ce sont les attributs `low`, `mid`, et `high` que vous pouvez voir ici. De plus, 🤗 *Datasets* calcule une variété de scores ROUGE qui sont basés sur différents types de granularité du texte lors de la comparaison des résumés générés et de référence. La variante `rouge1` est le chevauchement des unigrammes. C'est juste une façon fantaisiste de dire le chevauchement des mots et c'est exactement la métrique dont nous avons discuté ci-dessus. Pour vérifier cela, nous allons extraire la valeur "moyenne" de nos scores :
-
-```python
-scores["rouge1"].mid
-```
-
-```python out
-Score(precision=0.86, recall=1.0, fmeasure=0.92)
-```
-
-Super, les chiffres de précision et de rappel correspondent ! Maintenant, qu'en est-il des autres scores ROUGE ? `rouge2` mesure le chevauchement entre les bigrammes (pensez au chevauchement des paires de mots), tandis que `rougeL` et `rougeLsum` mesurent les plus longues séquences de mots correspondants en recherchant les plus longues sous-souches communes dans les résumés générés et de référence. La "somme" dans `rougeLsum` fait référence au fait que cette métrique est calculée sur un résumé entier, alors que `rougeL` est calculée comme une moyenne sur des phrases individuelles.
-
-
-
-✏️ **Essayez** Créez votre propre exemple de résumé généré et de référence et voyez si les scores ROUGE obtenus correspondent à un calcul manuel basé sur les formules de précision et de rappel. Pour des points bonus, divisez le texte en bigrammes et comparez la précision et le rappel pour la métrique `rouge2`.
-
-
-
-Nous utiliserons ces scores ROUGE pour suivre les performances de notre modèle, mais avant cela, faisons ce que tout bon praticien de NLP devrait faire : créer une base de référence solide, mais simple !
-
-### Création d'une base de référence solide
-
-Une base de référence commune pour le résumé de texte consiste à prendre simplement les trois premières phrases d'un article, souvent appelée la base de référence _lead-3_. Nous pourrions utiliser des points pour suivre les limites de la phrase, mais cela échouera avec des acronymes comme "U.S." ou "U.N.". -- Nous allons donc utiliser la bibliothèque `nltk`, qui inclut un meilleur algorithme pour gérer ces cas. Vous pouvez installer le paquetage en utilisant `pip` comme suit :
-
-```python
-!pip install nltk
-```
-
-puis téléchargez les règles de ponctuation :
-
-```python
-import nltk
-
-nltk.download("punkt")
-```
-
-Ensuite, nous importons le *tokenizer* de `nltk` et créons une fonction simple pour extraire les trois premières phrases d'une critique. La convention dans le résumé de texte est de séparer chaque résumé avec une nouvelle ligne, donc nous allons également inclure ceci et le tester sur un exemple d'entraînement :
-
-```python
-from nltk.tokenize import sent_tokenize
-
-
-def three_sentence_summary(text):
- return "\n".join(sent_tokenize(text)[:3])
-
-
-print(three_sentence_summary(books_dataset["train"][1]["review_body"]))
-```
-
-```python out
-'I grew up reading Koontz, and years ago, I stopped,convinced i had "outgrown" him.' # J'ai grandi en lisant Koontz, et il y a des années, j'ai arrêté, convaincu que je l'avais "dépassé"
-'Still,when a friend was looking for something suspenseful too read, I suggested Koontz.' " Pourtant, quand une amie cherchait un livre à suspense, je lui ai suggéré Koontz.
-'She found Strangers.' # Elle a trouvé Strangers.
-```
-
-Cela semble fonctionner, alors implémentons maintenant une fonction qui extrait ces "résumés" d'un ensemble de données et calcule les scores ROUGE pour la ligne de base :
-
-```python
-def evaluate_baseline(dataset, metric):
- summaries = [three_sentence_summary(text) for text in dataset["review_body"]]
- return metric.compute(predictions=summaries, references=dataset["review_title"])
-```
-
-Nous pouvons ensuite utiliser cette fonction pour calculer les scores ROUGE sur l'ensemble de validation et les embellir un peu en utilisant Pandas :
-
-```python
-import pandas as pd
-
-score = evaluate_baseline(books_dataset["validation"], rouge_score)
-rouge_names = ["rouge1", "rouge2", "rougeL", "rougeLsum"]
-rouge_dict = dict((rn, round(score[rn].mid.fmeasure * 100, 2)) for rn in rouge_names)
-rouge_dict
-```
-
-```python out
-{'rouge1': 16.74, 'rouge2': 8.83, 'rougeL': 15.6, 'rougeLsum': 15.96}
-```
-
-Nous pouvons voir que le score de `rouge2` est significativement plus bas que le reste ; ceci reflète probablement le fait que les titres des revues sont typiquement concis et donc que la ligne de base de lead-3 est trop verbeuse. Maintenant que nous disposons d'une bonne base de travail, concentrons-nous sur le réglage fin de mT5 !
-
-{#if fw === 'pt'}
-
-## *Finetuning* de mT5 avec l'API `Trainer`.
-
-Le *finetuning* d'un modèle pour le résumé est très similaire aux autres tâches que nous avons couvertes dans ce chapitre. La première chose à faire est de charger le modèle pré-entraîné depuis le checkpoint `mt5-small`. Puisque la compression est une tâche de séquence à séquence, nous pouvons charger le modèle avec la classe `AutoModelForSeq2SeqLM`, qui téléchargera automatiquement et mettra en cache les poids :
-
-```python
-from transformers import AutoModelForSeq2SeqLM
-
-model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
-```
-
-{:else}
-
-## *Finetuning* de mT5 avec Keras
-
-Le *finetuning* d'un modèle pour le résumé est très similaire aux autres tâches que nous avons couvertes dans ce chapitre. La première chose à faire est de charger le modèle pré-entraîné à partir du point de contrôle `mt5-small`. Puisque la compression est une tâche de séquence à séquence, nous pouvons charger le modèle avec la classe `AutoModelForSeq2SeqLM`, qui téléchargera automatiquement et mettra en cache les poids :
-
-```python
-from transformers import TFAutoModelForSeq2SeqLM
-
-model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
-```
-
-{/if}
-
-
-
-💡 Si vous vous demandez pourquoi vous ne voyez aucun avertissement concernant l'affinement du modèle sur une tâche en aval, c'est parce que pour les tâches de séquence à séquence, nous conservons tous les poids du réseau. Comparez cela à notre modèle de classification de texte dans [Chapitre 3](/course/fr/chapter3), où la tête du modèle pré-entraîné a été remplacée par un réseau initialisé de manière aléatoire.
-
-
-
-La prochaine chose que nous devons faire est de nous connecter au *Hub*. Si vous exécutez ce code dans un *notebook*, vous pouvez le faire avec la fonction utilitaire suivante :
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-qui affichera un *widget* où vous pourrez saisir vos informations d'identification. Vous pouvez également exécuter cette commande dans votre terminal et vous connecter à partir de là :
-
-```
-huggingface-cli login
-```
-
-{#if fw === 'pt'}
-
-Nous aurons besoin de générer des résumés afin de calculer les scores ROUGE pendant l'entraînement. Heureusement, 🤗 *Transformers* fournit des classes dédiées `Seq2SeqTrainingArguments` et `Seq2SeqTrainer` qui peuvent faire cela pour nous automatiquement ! Pour voir comment cela fonctionne, définissons d'abord les hyperparamètres et autres arguments pour nos expériences :
-
-```python
-from transformers import Seq2SeqTrainingArguments
-
-batch_size = 8
-num_train_epochs = 8
-# La perte d'entraînement à chaque époque
-logging_steps = len(tokenized_datasets["train"]) // batch_size
-model_name = model_checkpoint.split("/")[-1]
-
-args = Seq2SeqTrainingArguments(
- output_dir=f"{model_name}-finetuned-amazon-en-es",
- evaluation_strategy="epoch",
- learning_rate=5.6e-5,
- per_device_train_batch_size=batch_size,
- per_device_eval_batch_size=batch_size,
- weight_decay=0.01,
- save_total_limit=3,
- num_train_epochs=num_train_epochs,
- predict_with_generate=True,
- logging_steps=logging_steps,
- push_to_hub=True,
-)
-```
-
-Ici, l'argument `predict_with_generate` a été défini pour indiquer que nous devons générer des résumés pendant l'évaluation afin de pouvoir calculer les scores ROUGE pour chaque époque. Comme discuté dans [Chapter 1](/course/fr/chapter1), le décodeur effectue l'inférence en prédisant les tokens un par un, et ceci est implémenté par la méthode `generate()` du modèle. Définir `predict_with_generate=True` indique au `Seq2SeqTrainer` d'utiliser cette méthode pour l'évaluation. Nous avons également ajusté certains des hyperparamètres par défaut, comme le taux d'apprentissage, le nombre d'époques, et le taux de décroissance des poids, et nous avons réglé l'option `save_total_limit` pour ne sauvegarder que jusqu'à 3 *checkpoints* pendant l'entraînement. C'est parce que même la "petite" version de mT5 utilise environ un Go d'espace disque, et nous pouvons gagner un peu de place en limitant le nombre de copies que nous sauvegardons.
-
-L'argument `push_to_hub=True` nous permettra de pousser le modèle vers le Hub après l'entraînement ; vous trouverez le dépôt sous votre profil utilisateur dans l'emplacement défini par `output_dir`. Notez que vous pouvez spécifier le nom du dépôt vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/mt5-finetuned-amazon-en-es"`à `Seq2SeqTrainingArguments`.
-
-La prochaine chose que nous devons faire est de fournir à l'entraîneur une fonction `compute_metrics()` afin que nous puissions évaluer notre modèle pendant l'entraînement. Pour le résumé, c'est un peu plus compliqué que de simplement appeler `rouge_score.compute()` sur les prédictions du modèle, puisque nous devons _décoder_ les sorties et les étiquettes en texte avant de pouvoir calculer les scores ROUGE. La fonction suivante fait exactement cela, et utilise également la fonction `sent_tokenize()` de `nltk` pour séparer les phrases du résumé avec des nouvelles lignes :
-
-
-```python
-import numpy as np
-
-
-def compute_metrics(eval_pred):
- predictions, labels = eval_pred
- # Décoder les résumés générés en texte
- decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
- # Remplacer -100 dans les étiquettes car nous ne pouvons pas les décoder
- labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
- # Décoder les résumés de référence en texte
- decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
- # ROUGE attend une nouvelle ligne après chaque phrase
- decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
- decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
- # Calcul des scores ROUGE
- result = rouge_score.compute(
- predictions=decoded_preds, references=decoded_labels, use_stemmer=True
- )
- # Extract the median scores
- result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
- return {k: round(v, 4) for k, v in result.items()}
-```
-
-{/if}
-
-Ensuite, nous devons définir un collateur de données pour notre tâche de séquence à séquence. Comme mT5 est un modèle Transformer encodeur-décodeur, une des subtilités de la préparation de nos lots est que, pendant le décodage, nous devons décaler les étiquettes d'une unité vers la droite. Ceci est nécessaire pour garantir que le décodeur ne voit que les étiquettes de vérité terrain précédentes et non les étiquettes actuelles ou futures, qui seraient faciles à mémoriser pour le modèle. Cela ressemble à la façon dont l'auto-attention masquée est appliquée aux entrées dans une tâche comme [la modélisation causale du langage](/course/fr/chapter7/6).
-
-Heureusement, 🤗 *Transformers* fournit un collateur `DataCollatorForSeq2Seq` qui rembourrera dynamiquement les entrées et les étiquettes pour nous. Pour instancier ce collateur, nous devons simplement fournir le *tokenizer* et le `model` :
-
-{#if fw === 'pt'}
-
-```python
-from transformers import DataCollatorForSeq2Seq
-
-data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
-```
-
-{:else}
-
-```python
-from transformers import DataCollatorForSeq2Seq
-
-data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
-```
-
-{/if}
-
-Voyons ce que produit ce collateur lorsqu'on lui donne un petit lot d'exemples. Tout d'abord, nous devons supprimer les colonnes contenant des chaînes de caractères, car le collateur ne saura pas comment remplir ces éléments :
-
-```python
-tokenized_datasets = tokenized_datasets.remove_columns(
- books_dataset["train"].column_names
-)
-```
-
-Comme le collateur attend une liste de `dict`s, où chaque `dict` représente un seul exemple dans l'ensemble de données, nous devons également mettre les données dans le format attendu avant de les transmettre au collateur de données :
-
-```python
-features = [tokenized_datasets["train"][i] for i in range(2)]
-data_collator(features)
-```
-
-```python out
-{'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]), 'input_ids': tensor([[ 1494, 259, 8622, 390, 259, 262, 2316, 3435, 955,
- 772, 281, 772, 1617, 263, 305, 14701, 260, 1385,
- 3031, 259, 24146, 332, 1037, 259, 43906, 305, 336,
- 260, 1, 0, 0, 0, 0, 0, 0],
- [ 259, 27531, 13483, 259, 7505, 260, 112240, 15192, 305,
- 53198, 276, 259, 74060, 263, 260, 459, 25640, 776,
- 2119, 336, 259, 2220, 259, 18896, 288, 4906, 288,
- 1037, 3931, 260, 7083, 101476, 1143, 260, 1]]), 'labels': tensor([[ 7483, 259, 2364, 15695, 1, -100],
- [ 259, 27531, 13483, 259, 7505, 1]]), 'decoder_input_ids': tensor([[ 0, 7483, 259, 2364, 15695, 1],
- [ 0, 259, 27531, 13483, 259, 7505]])}
-```
-
-La principale chose à remarquer ici est que le premier exemple est plus long que le second, donc les `input_ids` et `attention_mask` du second exemple ont été complétés sur la droite avec un jeton `[PAD]` (dont l'ID est `0`). De même, nous pouvons voir que les `labels` ont été complétés par des `-100`s, pour s'assurer que les *tokens* de remplissage sont ignorés par la fonction de perte. Et enfin, nous pouvons voir un nouveau `decoder_input_ids` qui a déplacé les étiquettes vers la droite en insérant un jeton `[PAD]` dans la première entrée.
-
-{#if fw === 'pt'}
-
-Nous avons enfin tous les ingrédients dont nous avons besoin pour nous entraîner ! Nous devons maintenant simplement instancier le formateur avec les arguments standards :
-
-```python
-from transformers import Seq2SeqTrainer
-
-trainer = Seq2SeqTrainer(
- model,
- args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- tokenizer=tokenizer,
- compute_metrics=compute_metrics,
-)
-```
-
-et lancer notre course d'entraînement :
-
-```python
-trainer.train()
-```
-
-Pendant l'entraînement, vous devriez voir la perte d'entraînement diminuer et les scores ROUGE augmenter à chaque époque. Une fois l'entraînement terminé, vous pouvez voir les scores ROUGE finaux en exécutant `Trainer.evaluate()` :
-
-```python
-trainer.evaluate()
-```
-
-```python out
-{'eval_loss': 3.028524398803711,
- 'eval_rouge1': 16.9728,
- 'eval_rouge2': 8.2969,
- 'eval_rougeL': 16.8366,
- 'eval_rougeLsum': 16.851,
- 'eval_gen_len': 10.1597,
- 'eval_runtime': 6.1054,
- 'eval_samples_per_second': 38.982,
- 'eval_steps_per_second': 4.914}
-```
-
-D'après les scores, nous pouvons voir que notre modèle a largement surpassé notre ligne de base lead-3. Bien ! La dernière chose à faire est de pousser les poids du modèle vers le *Hub*, comme suit :
-
-```
-trainer.push_to_hub(commit_message="Training complete", tags="summarization")
-```
-
-```python out
-'https://huggingface.co/huggingface-course/mt5-finetuned-amazon-en-es/commit/aa0536b829b28e73e1e4b94b8a5aacec420d40e0'
-```
-
-Ceci sauvegardera le point de contrôle et les fichiers de configuration dans `output_dir`, avant de télécharger tous les fichiers sur le *Hub*. En spécifiant l'argument `tags`, nous nous assurons également que le widget sur le Hub sera celui d'un pipeline de résumé au lieu de celui de la génération de texte par défaut associé à l'architecture mT5 (pour plus d'informations sur les balises de modèle, voir la [🤗 documentation du *Hub*](https://huggingface.co/docs/hub/main#how-is-a-models-type-of-inference-api-and-widget-determined)). La sortie de `trainer.push_to_hub()` est une URL vers le hash du commit Git, donc vous pouvez facilement voir les changements qui ont été faits au dépôt de modèle !
-
-Pour conclure cette section, voyons comment nous pouvons également affiner mT5 en utilisant les fonctionnalités de bas niveau fournies par 🤗 *Accelerate*.
-
-{:else}
-
-Nous sommes presque prêts à nous entraîner ! Nous devons juste convertir nos jeux de données en `tf.data.Dataset`s en utilisant le collateur de données que nous avons défini ci-dessus, et ensuite `compile()` et `fit()` le modèle. D'abord, les jeux de données :
-
-```python
-tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
- columns=["input_ids", "attention_mask", "labels"],
- collate_fn=data_collator,
- shuffle=True,
- batch_size=8,
-)
-tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
- columns=["input_ids", "attention_mask", "labels"],
- collate_fn=data_collator,
- shuffle=False,
- batch_size=8,
-)
-```
-
-Maintenant, nous définissons nos hyperparamètres d'entraînement et nous compilons :
-
-```python
-from transformers import create_optimizer
-import tensorflow as tf
-
-# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans l'ensemble de données, divisé par la taille du batch, puis multiplié par le nombre total d'époques.
-# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un batch tf.data.Dataset,
-# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size.
-num_train_epochs = 8
-num_train_steps = len(tf_train_dataset) * num_train_epochs
-model_name = model_checkpoint.split("/")[-1]
-
-optimizer, schedule = create_optimizer(
- init_lr=5.6e-5,
- num_warmup_steps=0,
- num_train_steps=num_train_steps,
- weight_decay_rate=0.01,
-)
-
-model.compile(optimizer=optimizer)
-
-# Entraîner en mixed-precision float16
-tf.keras.mixed_precision.set_global_policy("mixed_float16")
-```
-
-Et enfin, nous ajustons le modèle. Nous utilisons un `PushToHubCallback` pour sauvegarder le modèle sur le *Hub* après chaque époque, ce qui nous permettra de l'utiliser pour l'inférence plus tard :
-
-```python
-from transformers.keras_callbacks import PushToHubCallback
-
-callback = PushToHubCallback(
- output_dir=f"{model_name}-finetuned-amazon-en-es", tokenizer=tokenizer
-)
-
-model.fit(
- tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback], epochs=8
-)
-```
-
-Nous avons obtenu quelques valeurs de perte pendant l'entraînement, mais nous aimerions vraiment voir les métriques ROUGE que nous avons calculées plus tôt. Pour obtenir ces métriques, nous devons générer les sorties du modèle et les convertir en chaînes de caractères. Construisons quelques listes d'étiquettes et de prédictions pour comparer la métrique ROUGE (notez que si vous obtenez des erreurs d'importation pour cette section, vous pouvez avoir besoin de "pip install tqdm") :
-
-```python
-from tqdm import tqdm
-import numpy as np
-
-all_preds = []
-all_labels = []
-for batch in tqdm(tf_eval_dataset):
- predictions = model.generate(**batch)
- decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
- labels = batch["labels"].numpy()
- labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
- decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
- decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
- decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
- all_preds.extend(decoded_preds)
- all_labels.extend(decoded_labels)
-```
-
-Une fois que nous avons nos listes d'étiquettes et de chaînes de prédiction, le calcul du score ROUGE est facile :
-
-```python
-result = rouge_score.compute(
- predictions=decoded_preds, references=decoded_labels, use_stemmer=True
-)
-result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
-{k: round(v, 4) for k, v in result.items()}
-```
-
-```
-{'rouge1': 31.4815, 'rouge2': 25.4386, 'rougeL': 31.4815, 'rougeLsum': 31.4815}
-```
-
-
-{/if}
-
-{#if fw === 'pt'}
-
-## *Finetuning* de mT5 avec 🤗 *Accelerate*
-
-Le *finetuning* de notre modèle avec 🤗 *Accelerate* est très similaire à l'exemple de classification de texte que nous avons rencontré dans [Chapitre 3](/course/fr/chapter3). Les principales différences seront la nécessité de générer explicitement nos résumés pendant l'Entraînement et de définir comment nous calculons les scores ROUGE (rappelons que le `Seq2SeqTrainer` s'est occupé de la génération pour nous). Voyons comment nous pouvons mettre en œuvre ces deux exigences dans 🤗 *Accelerate* !
-
-### Préparer tout pour l'entraînement
-
-La première chose que nous devons faire est de créer un `DataLoader` pour chacun de nos splits. Puisque les chargeurs de données PyTorch attendent des batchs de tenseurs, nous devons définir le format à `"torch"` dans nos jeux de données :
-
-```python
-tokenized_datasets.set_format("torch")
-```
-
-Maintenant que nous avons des jeux de données constitués uniquement de tenseurs, la prochaine chose à faire est d'instancier à nouveau le `DataCollatorForSeq2Seq`. Pour cela, nous devons fournir une nouvelle version du modèle, donc chargeons-le à nouveau depuis notre cache :
-
-```python
-model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
-```
-
-Nous pouvons ensuite instancier le collateur de données et l'utiliser pour définir nos chargeurs de données :
-
-```python
-from torch.utils.data import DataLoader
-
-batch_size = 8
-train_dataloader = DataLoader(
- tokenized_datasets["train"],
- shuffle=True,
- collate_fn=data_collator,
- batch_size=batch_size,
-)
-eval_dataloader = DataLoader(
- tokenized_datasets["validation"], collate_fn=data_collator, batch_size=batch_size
-)
-```
-
-La prochaine chose à faire est de définir l'optimiseur que nous voulons utiliser. Comme dans nos autres exemples, nous allons utiliser `AdamW`, qui fonctionne bien pour la plupart des problèmes :
-
-```python
-from torch.optim import AdamW
-
-optimizer = AdamW(model.parameters(), lr=2e-5)
-```
-
-Enfin, nous introduisons notre modèle, notre optimiseur et nos chargeurs de données dans la méthode `accelerator.prepare()` :
-
-```python
-from accelerate import Accelerator
-
-accelerator = Accelerator()
-model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
- model, optimizer, train_dataloader, eval_dataloader
-)
-```
-
-
-
-🚨 Si vous vous entraînez sur un TPU, vous devrez déplacer tout le code ci-dessus dans une fonction d'entraînement dédiée. Voir le [Chapitre 3](/course/fr/chapter3) pour plus de détails.
-
-
-
-Maintenant que nous avons préparé nos objets, il reste trois choses à faire :
-
-* définir le programme du taux d'apprentissage,
-* implémenter une fonction pour post-traiter les résumés pour l'évaluation,
-* créer un référentiel sur le *Hub* vers lequel nous pouvons pousser notre modèle.
-
-Pour le programme de taux d'apprentissage, nous utiliserons le programme linéaire standard des sections précédentes :
-
-```python
-from transformers import get_scheduler
-
-num_train_epochs = 10
-num_update_steps_per_epoch = len(train_dataloader)
-num_training_steps = num_train_epochs * num_update_steps_per_epoch
-
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-```
-
-Pour le post-traitement, nous avons besoin d'une fonction qui divise les résumés générés en phrases séparées par des nouvelles lignes. C'est le format attendu par la métrique ROUGE, et nous pouvons y parvenir avec le bout de code suivant :
-
-```python
-def postprocess_text(preds, labels):
- preds = [pred.strip() for pred in preds]
- labels = [label.strip() for label in labels]
-
- # ROUGE attend une nouvelle ligne après chaque phrase
- preds = ["\n".join(nltk.sent_tokenize(pred)) for pred in preds]
- labels = ["\n".join(nltk.sent_tokenize(label)) for label in labels]
-
- return preds, labels
-```
-
-Cela devrait vous sembler familier si vous vous rappelez comment nous avons défini la fonction `compute_metrics()` du `Seq2SeqTrainer`.
-
-Enfin, nous devons créer un dépôt de modèles sur le *Hub*. Pour cela, nous pouvons utiliser la bibliothèque 🤗 *Hub*, qui porte le nom approprié. Nous avons juste besoin de définir un nom pour notre référentiel, et la bibliothèque a une fonction utilitaire pour combiner l'ID du référentiel avec le profil de l'utilisateur :
-
-```python
-from huggingface_hub import get_full_repo_name
-
-model_name = "test-bert-finetuned-squad-accelerate"
-repo_name = get_full_repo_name(model_name)
-repo_name
-```
-
-```python out
-'lewtun/mt5-finetuned-amazon-en-es-accelerate'
-```
-
-Nous pouvons maintenant utiliser ce nom de référentiel pour cloner une version locale dans notre répertoire de résultats qui stockera les artefacts d'entraînement :
-
-```python
-from huggingface_hub import Repository
-
-output_dir = "results-mt5-finetuned-squad-accelerate"
-repo = Repository(output_dir, clone_from=repo_name)
-```
-
-This will allow us to push the artifacts back to the Hub by calling the `repo.push_to_hub()` method during training! Let's now wrap up our analysis by writing out the training loop.
-
-### Boucle d'entraînement
-
-La boucle d'entraînement pour le résumé est assez similaire aux autres exemples 🤗 *Accelerate* que nous avons rencontrés et est grossièrement divisée en quatre étapes principales :
-
-1. entraîner le modèle en itérant sur tous les exemples dans `train_dataloader` pour chaque époque,
-2. générer les résumés du modèle à la fin de chaque époque, en générant d'abord les *tokens* puis en les décodant (ainsi que les résumés de référence) en texte,
-3. calculer les scores ROUGE en utilisant les mêmes techniques que nous avons vues précédemment,
-4. sauvegarder les points de contrôle et pousser le tout vers le *Hub*. Ici, nous nous appuyons sur l'argument `blocking=False` de l'objet `Repository` afin de pouvoir pousser les points de contrôle par époque de manière _asynchrone_. Cela nous permet de poursuivre l'entraînement sans avoir à attendre le téléchargement quelque peu lent associé à un modèle de la taille d'un Go !
-
-Ces étapes peuvent être vues dans le bloc de code suivant :
-
-```python
-from tqdm.auto import tqdm
-import torch
-import numpy as np
-
-progress_bar = tqdm(range(num_training_steps))
-
-for epoch in range(num_train_epochs):
- # Entraînement
- model.train()
- for step, batch in enumerate(train_dataloader):
- outputs = model(**batch)
- loss = outputs.loss
- accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-
- # Evaluation
- model.eval()
- for step, batch in enumerate(eval_dataloader):
- with torch.no_grad():
- generated_tokens = accelerator.unwrap_model(model).generate(
- batch["input_ids"],
- attention_mask=batch["attention_mask"],
- )
-
- generated_tokens = accelerator.pad_across_processes(
- generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
- )
- labels = batch["labels"]
-
- # Si nous n'avons pas rempli la longueur maximale, nous devons également remplir les étiquettes.
- labels = accelerator.pad_across_processes(
- batch["labels"], dim=1, pad_index=tokenizer.pad_token_id
- )
-
- generated_tokens = accelerator.gather(generated_tokens).cpu().numpy()
- labels = accelerator.gather(labels).cpu().numpy()
-
- # Remplacer -100 dans les étiquettes car nous ne pouvons pas les décoder.
- labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
- if isinstance(generated_tokens, tuple):
- generated_tokens = generated_tokens[0]
- decoded_preds = tokenizer.batch_decode(
- generated_tokens, skip_special_tokens=True
- )
- decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
-
- decoded_preds, decoded_labels = postprocess_text(
- decoded_preds, decoded_labels
- )
-
- rouge_score.add_batch(predictions=decoded_preds, references=decoded_labels)
-
- # Calculer les métriques
- result = rouge_score.compute()
- # Extract the median ROUGE scores
- result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
- result = {k: round(v, 4) for k, v in result.items()}
- print(f"Epoch {epoch}:", result)
-
- # Sauvegarder et télécharger
- accelerator.wait_for_everyone()
- unwrapped_model = accelerator.unwrap_model(model)
- unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
- if accelerator.is_main_process:
- tokenizer.save_pretrained(output_dir)
- repo.push_to_hub(
- commit_message=f"Training in progress epoch {epoch}", blocking=False
- )
-```
-
-```python out
-Epoch 0: {'rouge1': 5.6351, 'rouge2': 1.1625, 'rougeL': 5.4866, 'rougeLsum': 5.5005}
-Epoch 1: {'rouge1': 9.8646, 'rouge2': 3.4106, 'rougeL': 9.9439, 'rougeLsum': 9.9306}
-Epoch 2: {'rouge1': 11.0872, 'rouge2': 3.3273, 'rougeL': 11.0508, 'rougeLsum': 10.9468}
-Epoch 3: {'rouge1': 11.8587, 'rouge2': 4.8167, 'rougeL': 11.7986, 'rougeLsum': 11.7518}
-Epoch 4: {'rouge1': 12.9842, 'rouge2': 5.5887, 'rougeL': 12.7546, 'rougeLsum': 12.7029}
-Epoch 5: {'rouge1': 13.4628, 'rouge2': 6.4598, 'rougeL': 13.312, 'rougeLsum': 13.2913}
-Epoch 6: {'rouge1': 12.9131, 'rouge2': 5.8914, 'rougeL': 12.6896, 'rougeLsum': 12.5701}
-Epoch 7: {'rouge1': 13.3079, 'rouge2': 6.2994, 'rougeL': 13.1536, 'rougeLsum': 13.1194}
-Epoch 8: {'rouge1': 13.96, 'rouge2': 6.5998, 'rougeL': 13.9123, 'rougeLsum': 13.7744}
-Epoch 9: {'rouge1': 14.1192, 'rouge2': 7.0059, 'rougeL': 14.1172, 'rougeLsum': 13.9509}
-```
-
-Et c'est tout ! Une fois que vous l'aurez exécuté, vous aurez un modèle et des résultats assez similaires à ceux que nous avons obtenus avec le `Trainer`.
-
-{/if}
-
-## Utilisation de votre modèle *finetuné*
-
-Une fois que vous avez poussé le modèle vers le *Hub*, vous pouvez jouer avec lui soit via le widget d'inférence, soit avec un objet `pipeline`, comme suit :
-
-```python
-from transformers import pipeline
-
-hub_model_id = "huggingface-course/mt5-small-finetuned-amazon-en-es"
-summarizer = pipeline("summarization", model=hub_model_id)
-```
-
-Nous pouvons alimenter notre pipeline avec quelques exemples de l'ensemble de test (que le modèle n'a pas vu) pour avoir une idée de la qualité des résumés. Tout d'abord, implémentons une fonction simple pour afficher ensemble la critique, le titre et le résumé généré :
-
-```python
-def print_summary(idx):
- review = books_dataset["test"][idx]["review_body"]
- title = books_dataset["test"][idx]["review_title"]
- summary = summarizer(books_dataset["test"][idx]["review_body"])[0]["summary_text"]
- print(f"'>>> Review: {review}'")
- print(f"\n'>>> Title: {title}'")
- print(f"\n'>>> Summary: {summary}'")
-```
-
-Examinons l'un des exemples anglais que nous recevons :
-
-```python
-print_summary(100)
-```
-
-```python out
-'>>> Review: Nothing special at all about this product... the book is too small and stiff and hard to write in. The huge sticker on the back doesn’t come off and looks super tacky. I would not purchase this again. I could have just bought a journal from the dollar store and it would be basically the same thing. It’s also really expensive for what it is.'
-# Ce produit n'a rien de spécial... le livre est trop petit et rigide et il est difficile d'y écrire. L'énorme autocollant au dos ne se détache pas et a l'air super collant. Je n'achèterai plus jamais ce produit. J'aurais pu simplement acheter un journal dans un magasin à un dollar et ce serait à peu près la même chose. Il est également très cher pour ce qu'il est.
-
-'>>> Title: Not impressed at all... buy something else' # Pas du tout impressionné... achetez autre chose.
-
-'>>> Summary: Nothing special at all about this product' # Rien de spécial à propos de ce produit
-```
-
-Ce n'est pas si mal ! Nous pouvons voir que notre modèle a été capable d'effectuer un résumé _abstractif_ en augmentant certaines parties de la critique avec de nouveaux mots. Et peut-être que l'aspect le plus cool de notre modèle est qu'il est bilingue, donc nous pouvons également générer des résumés de critiques en espagnol :
-
-```python
-print_summary(0)
-```
-
-```python out
-'>>> Review: Es una trilogia que se hace muy facil de leer. Me ha gustado, no me esperaba el final para nada' # C'est une trilogie qui se lit très facilement. J'ai aimé, je ne m'attendais pas du tout à la fin.
-
-'>>> Title: Buena literatura para adolescentes' # Bonne littérature pour les adolescents
-
-'>>> Summary: Muy facil de leer' # Très facile à lire
-```
-
-Le résumé se traduit par "Très facile à lire", ce qui, comme nous pouvons le constater, a été extrait directement de la critique. Néanmoins, cela montre la polyvalence du modèle mT5 et vous a donné un aperçu de ce que c'est que de traiter un corpus multilingue !
-
-Ensuite, nous allons nous intéresser à une tâche un peu plus complexe : entraîner un modèle de langue à partir de zéro.
+
+
+# Résumé de textes
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+
+Dans cette section, nous allons voir comment les *transformers* peuvent être utilisés pour condenser de longs documents en résumés, une tâche connue sous le nom de _résumé de texte_. Il s'agit de l'une des tâches de NLP les plus difficiles car elle requiert une série de capacités, telles que la compréhension de longs passages et la génération d'un texte cohérent qui capture les sujets principaux d'un document. Cependant, lorsqu'il est bien fait, le résumé de texte est un outil puissant qui peut accélérer divers processus commerciaux en soulageant les experts du domaine de la lecture détaillée de longs documents.
+
+
+
+Bien qu'il existe déjà plusieurs modèles *finetunés* pour le résumé sur le [Hugging Face Hub](https://huggingface.co/models?pipeline_tag=summarization&sort=downloads), la plupart d'entre eux ne sont adaptés qu'aux documents en anglais. Ainsi, pour ajouter une touche d'originalité à cette section, nous allons entraîner un modèle bilingue pour l'anglais et l'espagnol. À la fin de cette section, vous disposerez d'un [modèle](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es) capable de résumer les commentaires des clients comme celui présenté ici :
+
+
+
+
+Comme nous allons le voir, ces résumés sont concis car ils sont appris à partir des titres que les clients fournissent dans leurs commentaires sur les produits. Commençons par constituer un corpus bilingue approprié pour cette tâche.
+
+## Préparation d'un corpus multilingue
+
+Nous allons utiliser le [Multilingual Amazon Reviews Corpus](https://huggingface.co/datasets/amazon_reviews_multi) pour créer notre résumeur bilingue. Ce corpus est constitué d'évaluations de produits Amazon en six langues et est généralement utilisé pour évaluer les classificateurs multilingues. Cependant, comme chaque critique est accompagnée d'un titre court, nous pouvons utiliser les titres comme résumés cibles pour l'apprentissage de notre modèle ! Pour commencer, téléchargeons les sous-ensembles anglais et espagnols depuis le *Hub* :
+
+```python
+from datasets import load_dataset
+
+spanish_dataset = load_dataset("amazon_reviews_multi", "es")
+english_dataset = load_dataset("amazon_reviews_multi", "en")
+english_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
+ num_rows: 200000
+ })
+ validation: Dataset({
+ features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
+ num_rows: 5000
+ })
+ test: Dataset({
+ features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
+ num_rows: 5000
+ })
+})
+```
+
+Comme vous pouvez le voir, pour chaque langue, il y a 200 000 évaluations pour la partie "entraînement", et 5 000 évaluations pour chacune des parties "validation" et "test". Les informations qui nous intéressent sont contenues dans les colonnes `review_body` et `review_title`. Voyons quelques exemples en créant une fonction simple qui prend un échantillon aléatoire de l'ensemble d'entraînement avec les techniques apprises au [Chapitre 5](/course/fr/chapter5) :
+
+```python
+def show_samples(dataset, num_samples=3, seed=42):
+ sample = dataset["train"].shuffle(seed=seed).select(range(num_samples))
+ for example in sample:
+ print(f"\n'>> Title: {example['review_title']}'")
+ print(f"'>> Review: {example['review_body']}'")
+
+
+show_samples(english_dataset)
+```
+
+```python out
+'>> Title: Worked in front position, not rear' # Travaillé en position avant, pas arrière
+'>> Review: 3 stars because these are not rear brakes as stated in the item description. At least the mount adapter only worked on the front fork of the bike that I got it for.'
+# 3 étoiles car ce ne sont pas des freins arrière comme indiqué dans la description de l'article. Au moins, l'adaptateur de montage ne fonctionnait que sur la fourche avant du vélo pour lequel je l'ai acheté.''
+
+'>> Title: meh'
+'>> Review: Does it’s job and it’s gorgeous but mine is falling apart, I had to basically put it together again with hot glue'
+# Il fait son travail et il est magnifique mais le mien est en train de tomber en morceaux, j'ai dû le recoller avec de la colle chaude.
+
+'>> Title: Can\'t beat these for the money' # On ne peut pas faire mieux pour le prix
+'>> Review: Bought this for handling miscellaneous aircraft parts and hanger "stuff" that I needed to organize; it really fit the bill. The unit arrived quickly, was well packaged and arrived intact (always a good sign). There are five wall mounts-- three on the top and two on the bottom. I wanted to mount it on the wall, so all I had to do was to remove the top two layers of plastic drawers, as well as the bottom corner drawers, place it when I wanted and mark it; I then used some of the new plastic screw in wall anchors (the 50 pound variety) and it easily mounted to the wall. Some have remarked that they wanted dividers for the drawers, and that they made those. Good idea. My application was that I needed something that I can see the contents at about eye level, so I wanted the fuller-sized drawers. I also like that these are the new plastic that doesn\'t get brittle and split like my older plastic drawers did. I like the all-plastic construction. It\'s heavy duty enough to hold metal parts, but being made of plastic it\'s not as heavy as a metal frame, so you can easily mount it to the wall and still load it up with heavy stuff, or light stuff. No problem there. For the money, you can\'t beat it. Best one of these I\'ve bought to date-- and I\'ve been using some version of these for over forty years.'
+# Je l'ai acheté pour manipuler diverses pièces d'avion et des "trucs" de hangar que je devais organiser ; il a vraiment fait l'affaire. L'unité est arrivée rapidement, était bien emballée et est arrivée intacte (toujours un bon signe). Il y a cinq supports muraux - trois sur le dessus et deux sur le dessous. Je voulais le monter sur le mur, alors tout ce que j'ai eu à faire était d'enlever les deux couches supérieures de tiroirs en plastique, ainsi que les tiroirs d'angle inférieurs, de le placer où je voulais et de le marquer ; j'ai ensuite utilisé quelques-uns des nouveaux ancrages muraux à vis en plastique (la variété de 50 livres) et il s'est facilement monté sur le mur. Certains ont fait remarquer qu'ils voulaient des séparateurs pour les tiroirs, et qu'ils les ont fabriqués. Bonne idée. Pour ma part, j'avais besoin de quelque chose dont je pouvais voir le contenu à hauteur des yeux, et je voulais donc des tiroirs plus grands. J'aime aussi le fait qu'il s'agisse du nouveau plastique qui ne se fragilise pas et ne se fend pas comme mes anciens tiroirs en plastique. J'aime la construction entièrement en plastique. Elle est suffisamment résistante pour contenir des pièces métalliques, mais étant en plastique, elle n'est pas aussi lourde qu'un cadre métallique, ce qui permet de la fixer facilement au mur et de la charger d'objets lourds ou légers. Aucun problème. Pour le prix, c'est imbattable. C'est le meilleur que j'ai acheté à ce jour, et j'utilise des versions de ce type depuis plus de quarante ans.
+
+```
+
+
+
+✏️ **Essayez !** Changez la graine aléatoire dans la commande `Dataset.shuffle()` pour explorer d'autres critiques dans le corpus. Si vous parlez espagnol, jetez un coup d'œil à certaines des critiques dans `spanish_dataset` pour voir si les titres semblent aussi être des résumés raisonnables.
+
+
+
+Cet échantillon montre la diversité des critiques que l'on trouve généralement en ligne, allant du positif au négatif (et tout ce qui se trouve entre les deux !). Bien que l'exemple avec le titre "meh" ne soit pas très informatif, les autres titres semblent être des résumés décents des critiques elles-mêmes. Entraîner un modèle de résumé sur l'ensemble des 400 000 avis prendrait beaucoup trop de temps sur un seul GPU, nous allons donc nous concentrer sur la génération de résumés pour un seul domaine de produits. Pour avoir une idée des domaines parmi lesquels nous pouvons choisir, convertissons `english_dataset` en `pandas.DataFrame` et calculons le nombre d'avis par catégorie de produits :
+
+```python
+english_dataset.set_format("pandas")
+english_df = english_dataset["train"][:]
+# Afficher les comptes des 20 premiers produits
+english_df["product_category"].value_counts()[:20]
+```
+
+```python out
+home 17679
+apparel 15951
+wireless 15717
+other 13418
+beauty 12091
+drugstore 11730
+kitchen 10382
+toy 8745
+sports 8277
+automotive 7506
+lawn_and_garden 7327
+home_improvement 7136
+pet_products 7082
+digital_ebook_purchase 6749
+pc 6401
+electronics 6186
+office_product 5521
+shoes 5197
+grocery 4730
+book 3756
+Name: product_category, dtype: int64
+```
+
+Les produits les plus populaires dans l'ensemble de données anglaises concernent les articles ménagers, les vêtements et l'électronique sans fil. Pour rester dans le thème d'Amazon, nous allons nous concentrer sur le résumé des critiques de livres. Après tout, c'est la raison d'être de l'entreprise ! Nous pouvons voir deux catégories de produits qui correspondent à nos besoins (`book` et `digital_ebook_purchase`), nous allons donc filtrer les ensembles de données dans les deux langues pour ces produits uniquement. Comme nous l'avons vu dans le [Chapitre 5](/course/fr/chapter5), la fonction `Dataset.filter()` nous permet de découper un jeu de données de manière très efficace, nous pouvons donc définir une fonction simple pour le faire :
+
+```python
+def filter_books(example):
+ return (
+ example["product_category"] == "book"
+ or example["product_category"] == "digital_ebook_purchase"
+ )
+```
+
+Maintenant, lorsque nous appliquons cette fonction à `english_dataset` et `spanish_dataset`, le résultat ne contiendra que les lignes impliquant les catégories de livres. Avant d'appliquer le filtre, changeons le format de `english_dataset` de `"pandas"` à `"arrow"` :
+
+```python
+english_dataset.reset_format()
+```
+
+Nous pouvons ensuite appliquer la fonction de filtrage et, à titre de vérification, inspecter un échantillon de critiques pour voir si elles portent bien sur des livres :
+
+```python
+spanish_books = spanish_dataset.filter(filter_books)
+english_books = english_dataset.filter(filter_books)
+show_samples(english_books)
+```
+
+```python out
+'>> Title: I\'m dissapointed.' # Je suis déçu
+'>> Review: I guess I had higher expectations for this book from the reviews. I really thought I\'d at least like it. The plot idea was great. I loved Ash but, it just didnt go anywhere. Most of the book was about their radio show and talking to callers. I wanted the author to dig deeper so we could really get to know the characters. All we know about Grace is that she is attractive looking, Latino and is kind of a brat. I\'m dissapointed.'
+# Je suppose que j'avais de plus grandes attentes pour ce livre d'après les critiques. Je pensais vraiment que j'allais au moins l'aimer. L'idée de l'intrigue était géniale. J'aimais Ash, mais ça n'allait nulle part. La plus grande partie du livre était consacrée à leur émission de radio et aux conversations avec les auditeurs. Je voulais que l'auteur creuse plus profondément pour que nous puissions vraiment connaître les personnages. Tout ce que nous savons de Grace, c'est qu'elle est séduisante, qu'elle est latino et qu'elle est une sorte de garce. Je suis déçue.
+
+'>> Title: Good art, good price, poor design' # Un bon art, un bon prix, un mauvais design
+'>> Review: I had gotten the DC Vintage calendar the past two years, but it was on backorder forever this year and I saw they had shrunk the dimensions for no good reason. This one has good art choices but the design has the fold going through the picture, so it\'s less aesthetically pleasing, especially if you want to keep a picture to hang. For the price, a good calendar'
+# J'ai eu le calendrier DC Vintage ces deux dernières années, mais il était en rupture de stock pour toujours cette année et j'ai vu qu'ils avaient réduit les dimensions sans raison valable. Celui-ci a de bons choix artistiques mais le design a le pli qui traverse l'image, donc c'est moins esthétique, surtout si vous voulez garder une image à accrocher. Pour le prix, c'est un bon calendrier.
+
+'>> Title: Helpful' Utile
+'>> Review: Nearly all the tips useful and. I consider myself an intermediate to advanced user of OneNote. I would highly recommend.'
+# Presque tous les conseils sont utiles et. Je me considère comme un utilisateur intermédiaire à avancé de OneNote. Je le recommande vivement.
+```
+
+D'accord, nous pouvons voir que les critiques ne concernent pas strictement les livres et peuvent se référer à des choses comme des calendriers et des applications électroniques telles que OneNote. Néanmoins, le domaine semble approprié pour entraîner un modèle de résumé. Avant de regarder les différents modèles qui conviennent à cette tâche, nous avons une dernière préparation de données à faire : combiner les critiques anglaises et espagnoles en un seul objet `DatasetDict`. 🤗 *Datasets* fournit une fonction pratique `concatenate_datasets()` qui (comme son nom l'indique) va empiler deux objets `Dataset` l'un sur l'autre. Ainsi, pour créer notre jeu de données bilingue, nous allons boucler sur chaque division, concaténer les jeux de données pour cette division, et mélanger le résultat pour s'assurer que notre modèle ne s'adapte pas trop à une seule langue :
+
+```python
+from datasets import concatenate_datasets, DatasetDict
+
+books_dataset = DatasetDict()
+
+for split in english_books.keys():
+ books_dataset[split] = concatenate_datasets(
+ [english_books[split], spanish_books[split]]
+ )
+ books_dataset[split] = books_dataset[split].shuffle(seed=42)
+
+# Quelques exemples
+show_samples(books_dataset)
+```
+
+```python out
+'>> Title: Easy to follow!!!!' # Facile à suivre!!!!
+'>> Review: I loved The dash diet weight loss Solution. Never hungry. I would recommend this diet. Also the menus are well rounded. Try it. Has lots of the information need thanks.'
+# J'ai adoré The dash diet weight loss Solution. Jamais faim. Je recommande ce régime. Les menus sont également bien arrondis. Essayez-le. Il contient beaucoup d'informations, merci.
+
+'>> Title: PARCIALMENTE DAÑADO' # PARTIELLEMENT ENDOMMAGÉ
+'>> Review: Me llegó el día que tocaba, junto a otros libros que pedí, pero la caja llegó en mal estado lo cual dañó las esquinas de los libros porque venían sin protección (forro).'
+# Il est arrivé le jour prévu, avec d'autres livres que j'avais commandés, mais la boîte est arrivée en mauvais état, ce qui a endommagé les coins des livres car ils étaient livrés sans protection (doublure).
+
+'>> Title: no lo he podido descargar' # Je n'ai pas pu le télécharger
+'>> Review: igual que el anterior' # même chose que ci-dessus
+```
+
+Cela ressemble certainement à un mélange de critiques anglaises et espagnoles ! Maintenant que nous avons un corpus d'entraînement, une dernière chose à vérifier est la distribution des mots dans les critiques et leurs titres. Ceci est particulièrement important pour les tâches de résumé, où les résumés de référence courts dans les données peuvent biaiser le modèle pour qu'il ne produise qu'un ou deux mots dans les résumés générés. Les graphiques ci-dessous montrent les distributions de mots, et nous pouvons voir que les titres sont fortement biaisés vers seulement 1 ou 2 mots :
+
+
+
+
+
+
+Pour y remédier, nous allons filtrer les exemples avec des titres très courts afin que notre modèle puisse produire des résumés plus intéressants. Puisque nous avons affaire à des textes anglais et espagnols, nous pouvons utiliser une heuristique grossière pour séparer les titres sur les espaces blancs, puis utiliser notre fidèle méthode `Dataset.filter()` comme suit :
+
+```python
+books_dataset = books_dataset.filter(lambda x: len(x["review_title"].split()) > 2)
+```
+
+Maintenant que nous avons préparé notre corpus, voyons quelques *transformers* possibles que l'on pourrait *finetuné* dessus !
+
+## Modèles pour le résumé de texte
+
+Si vous y pensez, le résumé de texte est une tâche similaire à la traduction automatique : nous avons un corps de texte, comme une critique, que nous aimerions "traduire" en une version plus courte qui capture les caractéristiques saillantes de l'entrée. En conséquence, la plupart des modèles Transformer pour le résumé adoptent l'architecture encodeur-décodeur que nous avons rencontrée pour la première fois dans le [Chapitre 1](/course/fr/chapter1), bien qu'il y ait quelques exceptions comme la famille de modèles GPT qui peut également être utilisée pour le résumé dans des contextes peu complexes. Le tableau suivant présente quelques modèles pré-entraînés populaires qui peuvent être *finetunés* pour le résumé.
+
+| *Transformers* | Description | Multilingue ? |
+| :---------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------: |
+| [GPT-2](https://huggingface.co/gpt2-xl) | Bien qu'il soit entraîné comme un modèle de langage autorégressif, vous pouvez faire en sorte que GPT-2 génère des résumés en ajoutant "TL;DR" à la fin du texte d'entrée. | ❌ |
+| [PEGASUS](https://huggingface.co/google/pegasus-large) | Utilise un objectif de pré-entraînement pour prédire les phrases masquées dans les textes à plusieurs phrases. Cet objectif de pré-entraînement est plus proche du résumé que de la modélisation du langage standard et obtient des scores élevés sur des benchmarks populaires. | ❌ |
+| [T5](https://huggingface.co/t5-base) | Une architecture universelle de *transformer* qui formule toutes les tâches dans un cadre texte à texte ; par exemple, le format d'entrée du modèle pour résumer un document est `summarize : ARTICLE`. | ❌ |
+| [mT5](https://huggingface.co/google/mt5-base) | Une version multilingue de T5, pré-entraînée sur le corpus multilingue Common Crawl (mC4), couvrant 101 langues. | ✅ |
+| [BART](https://huggingface.co/facebook/bart-base) | Une architecture de *transformer* avec une pile d'encodeurs et de décodeurs entraînés pour reconstruire l'entrée corrompue qui combine les schémas de pré-entraînement de BERT et GPT-2. | ❌ |
+| [mBART-50](https://huggingface.co/facebook/mbart-large-50) | Une version multilingue de BART, pré-entraînée sur 50 langues. | ✅ |
+
+Comme vous pouvez le voir dans ce tableau, la majorité des modèles Transformer pour le résumé (et en fait la plupart des tâches de NLP) sont monolingues. C'est une bonne chose si votre tâche se déroule dans une langue "à haute ressource" comme l'anglais ou l'allemand, mais moins pour les milliers d'autres langues utilisées dans le monde. Heureusement, il existe une catégorie de modèles Transformer multilingues, comme mT5 et mBART, qui viennent à la rescousse. Ces modèles sont pré-entraînés en utilisant la modélisation du langage, mais avec une particularité : au lieu de s'entraîner sur un corpus d'une seule langue, ils sont entraînés conjointement sur des textes dans plus de 50 langues à la fois !
+
+Nous allons nous concentrer sur mT5, une architecture intéressante basée sur T5 qui a été pré-entraînée dans un cadre texte à texte. Dans T5, chaque tâche NLP est formulée en termes d'un préfixe d'invite comme `summarize:` qui conditionne le modèle à adapter le texte généré à l'invite. Comme le montre la figure ci-dessous, cela rend T5 extrêmement polyvalent, car vous pouvez résoudre de nombreuses tâches avec un seul modèle !
+
+
+
+
+
+
+mT5 n'utilise pas de préfixes, mais partage une grande partie de la polyvalence de T5 et a l'avantage d'être multilingue. Maintenant que nous avons choisi un modèle, voyons comment préparer nos données pour l'entraînement.
+
+
+
+
+✏️ **Essayez** Une fois que vous avez travaillé sur cette section, voyez comment mT5 se compare à mBART en affinant ce dernier avec les mêmes techniques. Pour des points bonus, vous pouvez aussi essayer de *finetuner* le T5 uniquement sur les critiques anglaises. Puisque le T5 a un préfixe spécial, vous devrez ajouter `summarize:` aux exemples d'entrée dans les étapes de prétraitement ci-dessous.
+
+
+
+## Prétraitement des données
+
+
+
+Notre prochaine tâche est de tokeniser et d'encoder nos critiques et leurs titres. Comme d'habitude, nous commençons par charger le *tokenizer* associé au point de contrôle du modèle pré-entraîné. Nous utiliserons `mt5-small` comme point de contrôle afin de pouvoir affiner le modèle en un temps raisonnable :
+
+```python
+from transformers import AutoTokenizer
+
+model_checkpoint = "google/mt5-small"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+```
+
+
+
+💡 Aux premiers stades de vos projets de NLP, une bonne pratique consiste à entraîner une classe de "petits" modèles sur un petit échantillon de données. Cela vous permet de déboguer et d'itérer plus rapidement vers un flux de travail de bout en bout. Une fois que vous avez confiance dans les résultats, vous pouvez toujours faire évoluer le modèle en changeant simplement le point de contrôle du modèle !
+
+
+
+Testons le *tokenizer* de mT5 sur un petit exemple :
+
+```python
+inputs = tokenizer(
+ "I loved reading the Hunger Games!"
+) # J'ai adoré lire les Hunger Games !
+inputs
+```
+
+```python out
+{'input_ids': [336, 259, 28387, 11807, 287, 62893, 295, 12507, 1], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
+```
+
+Ici nous pouvons voir les familiers `input_ids` et `attention_mask` que nous avons rencontrés dans nos premières expériences de fine-tuning au [Chapitre 3](/course/fr/chapter3). Décodons ces identifiants d'entrée avec la fonction `convert_ids_to_tokens()` du tokenizer pour voir à quel type de tokenizer nous avons affaire :
+
+```python
+tokenizer.convert_ids_to_tokens(inputs.input_ids)
+```
+
+```python out
+['▁I', '▁', 'loved', '▁reading', '▁the', '▁Hung', 'er', '▁Games', '']
+```
+
+Le caractère spécial Unicode `▁` et le *token* de fin de séquence `` indiquent que nous avons affaire au *tokenizer* de SentencePiece, qui est basé sur l'algorithme de segmentation Unigram discuté dans le [Chapitre 6](/course/chapter6). Unigram est particulièrement utile pour les corpus multilingues car il permet à SentencePiece d'être agnostique vis-à-vis des accents, de la ponctuation et du fait que de nombreuses langues, comme le japonais, n'ont pas de caractères d'espacement.
+
+Pour tokeniser notre corpus, nous devons faire face à une subtilité associée au résumé : comme nos étiquettes sont également du texte, il est possible qu'elles dépassent la taille maximale du contexte du modèle. Cela signifie que nous devons appliquer une troncature à la fois aux critiques et à leurs titres pour nous assurer de ne pas transmettre des entrées trop longues à notre modèle. Les tokenizers de 🤗 *Transformers* fournissent une fonction très pratique `as_target_tokenizer()` qui vous permet de tokeniser les étiquettes en parallèle avec les entrées. Ceci est typiquement fait en utilisant un gestionnaire de contexte à l'intérieur d'une fonction de prétraitement qui encode d'abord les entrées, et ensuite encode les étiquettes comme une colonne séparée. Voici un exemple d'une telle fonction pour mT5 :
+
+```python
+max_input_length = 512
+max_target_length = 30
+
+
+def preprocess_function(examples):
+ model_inputs = tokenizer(
+ examples["review_body"], max_length=max_input_length, truncation=True
+ )
+ # Configurer le *tokenizer* pour les cibles.
+ with tokenizer.as_target_tokenizer():
+ labels = tokenizer(
+ examples["review_title"], max_length=max_target_length, truncation=True
+ )
+
+ model_inputs["labels"] = labels["input_ids"]
+ return model_inputs
+```
+
+Parcourons ce code pour comprendre ce qui se passe. La première chose que nous avons faite est de définir des valeurs pour `max_input_length` et `max_target_length`, qui fixent les limites supérieures de la longueur des commentaires et des titres. Comme le corps de la critique est généralement beaucoup plus long que le titre, nous avons mis ces valeurs à l'échelle en conséquence. Ensuite, dans la `preprocess_function()` elle-même, nous pouvons voir que les commentaires sont d'abord tokenizés, suivis par les titres avec `as_target_tokenizer()`.
+
+Avec la fonction `preprocess_function()`, il est alors simple de tokeniser l'ensemble du corpus en utilisant la fonction pratique `Dataset.map()` que nous avons largement utilisée dans ce cours :
+
+```python
+tokenized_datasets = books_dataset.map(preprocess_function, batched=True)
+```
+
+Maintenant que le corpus a été prétraité, examinons certaines métriques couramment utilisées pour le résumé. Comme nous allons le voir, il n'existe pas de solution miracle pour mesurer la qualité d'un texte généré par une machine.
+
+
+
+💡 Vous avez peut-être remarqué que nous avons utilisé `batched=True` dans notre fonction `Dataset.map()` ci-dessus. Cela permet de coder les exemples par lots de 1 000 (par défaut) et d'utiliser les capacités de multithreading des *tokenizers* rapides de 🤗 *Transformers*. Lorsque cela est possible, essayez d'utiliser `batched=True` pour tirer le meilleur parti de votre prétraitement !
+
+
+
+
+## Métriques pour le résumé de texte
+
+
+
+Par rapport à la plupart des autres tâches que nous avons abordées dans ce cours, la mesure des performances des tâches de génération de texte comme le résumé ou la traduction n'est pas aussi simple. Par exemple, pour une critique telle que "J'ai adoré lire les Hunger Games", il existe plusieurs résumés valides, comme "J'ai adoré Hunger Games" ou "Hunger Games est une excellente lecture". Il est clair que l'application d'une sorte de correspondance exacte entre le résumé généré et l'étiquette n'est pas une bonne solution - même les humains auraient de mauvais résultats avec une telle mesure, car nous avons tous notre propre style d'écriture.
+
+Pour le résumé, l'une des métriques les plus couramment utilisées est le [score ROUGE](https://en.wikipedia.org/wiki/ROUGE_(metric)) (abréviation de Recall-Oriented Understudy for Gisting Evaluation). L'idée de base de cette métrique est de comparer un résumé généré avec un ensemble de résumés de référence qui sont généralement créés par des humains. Pour être plus précis, supposons que nous voulions comparer les deux résumés suivants :
+
+```python
+generated_summary = "I absolutely loved reading the Hunger Games"
+reference_summary = "I loved reading the Hunger Games"
+```
+
+Une façon de les comparer pourrait être de compter le nombre de mots qui se chevauchent, qui dans ce cas serait de 6. Cependant, cette méthode est un peu grossière, c'est pourquoi ROUGE se base sur le calcul des scores de _précision_ et de _rappel_ pour le chevauchement.
+
+
+
+🙋 Ne vous inquiétez pas si c'est la première fois que vous entendez parler de précision et de rappel - nous allons parcourir ensemble quelques exemples explicites pour que tout soit clair. Ces métriques sont généralement rencontrées dans les tâches de classification, donc si vous voulez comprendre comment la précision et le rappel sont définis dans ce contexte, nous vous recommandons de consulter les [guides de `scikit-learn`](https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html).
+
+
+
+Pour ROUGE, le rappel mesure la proportion du résumé de référence qui est capturée par le résumé généré. Si nous ne faisons que comparer des mots, le rappel peut être calculé selon la formule suivante :
+
+$$ \mathrm{Recall} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, reference\, summary}} $$
+
+Pour notre exemple simple ci-dessus, cette formule donne un rappel parfait de 6/6 = 1 ; c'est-à-dire que tous les mots du résumé de référence ont été produits par le modèle. Cela peut sembler génial, mais imaginez que le résumé généré ait été "J'ai vraiment aimé lire les Hunger Games toute la nuit". Le rappel serait également parfait, mais le résumé serait sans doute moins bon puisqu'il serait verbeux. Pour traiter ces scénarios, nous calculons également la précision, qui, dans le contexte de ROUGE, mesure la proportion du résumé généré qui était pertinente :
+
+$$ \mathrm{Precision} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, generated\, summary}} $$
+
+En appliquant cela à notre résumé verbeux, on obtient une précision de 6/10 = 0,6, ce qui est considérablement moins bon que la précision de 6/7 = 0,86 obtenue par notre résumé plus court. En pratique, la précision et le rappel sont généralement calculés, puis le score F1 (la moyenne harmonique de la précision et du rappel) est indiqué. Nous pouvons le faire facilement dans 🤗 *Datasets* en installant d'abord le paquet `rouge_score` :
+
+```py
+!pip install rouge_score
+```
+
+et ensuite charger la métrique ROUGE comme suit :
+
+```python
+from datasets import load_metric
+
+rouge_score = load_metric("rouge")
+```
+
+Ensuite, nous pouvons utiliser la fonction `rouge_score.compute()` pour calculer toutes les métriques en une seule fois :
+
+```python
+scores = rouge_score.compute(
+ predictions=[generated_summary], references=[reference_summary]
+)
+scores
+```
+
+```python out
+{'rouge1': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
+ 'rouge2': AggregateScore(low=Score(precision=0.67, recall=0.8, fmeasure=0.73), mid=Score(precision=0.67, recall=0.8, fmeasure=0.73), high=Score(precision=0.67, recall=0.8, fmeasure=0.73)),
+ 'rougeL': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
+ 'rougeLsum': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92))}
+```
+
+Whoa, il y a un batch d'informations dans cette sortie. Qu'est-ce que ça veut dire ? Tout d'abord, 🤗 *Datasets* calcule en fait des intervalles de confiance pour la précision, le rappel et le score F1 ; ce sont les attributs `low`, `mid`, et `high` que vous pouvez voir ici. De plus, 🤗 *Datasets* calcule une variété de scores ROUGE qui sont basés sur différents types de granularité du texte lors de la comparaison des résumés générés et de référence. La variante `rouge1` est le chevauchement des unigrammes. C'est juste une façon fantaisiste de dire le chevauchement des mots et c'est exactement la métrique dont nous avons discuté ci-dessus. Pour vérifier cela, nous allons extraire la valeur "moyenne" de nos scores :
+
+```python
+scores["rouge1"].mid
+```
+
+```python out
+Score(precision=0.86, recall=1.0, fmeasure=0.92)
+```
+
+Super, les chiffres de précision et de rappel correspondent ! Maintenant, qu'en est-il des autres scores ROUGE ? `rouge2` mesure le chevauchement entre les bigrammes (pensez au chevauchement des paires de mots), tandis que `rougeL` et `rougeLsum` mesurent les plus longues séquences de mots correspondants en recherchant les plus longues sous-souches communes dans les résumés générés et de référence. La "somme" dans `rougeLsum` fait référence au fait que cette métrique est calculée sur un résumé entier, alors que `rougeL` est calculée comme une moyenne sur des phrases individuelles.
+
+
+
+✏️ **Essayez** Créez votre propre exemple de résumé généré et de référence et voyez si les scores ROUGE obtenus correspondent à un calcul manuel basé sur les formules de précision et de rappel. Pour des points bonus, divisez le texte en bigrammes et comparez la précision et le rappel pour la métrique `rouge2`.
+
+
+
+Nous utiliserons ces scores ROUGE pour suivre les performances de notre modèle, mais avant cela, faisons ce que tout bon praticien de NLP devrait faire : créer une base de référence solide, mais simple !
+
+### Création d'une base de référence solide
+
+Une base de référence commune pour le résumé de texte consiste à prendre simplement les trois premières phrases d'un article, souvent appelée la base de référence _lead-3_. Nous pourrions utiliser des points pour suivre les limites de la phrase, mais cela échouera avec des acronymes comme "U.S." ou "U.N.". -- Nous allons donc utiliser la bibliothèque `nltk`, qui inclut un meilleur algorithme pour gérer ces cas. Vous pouvez installer le paquetage en utilisant `pip` comme suit :
+
+```python
+!pip install nltk
+```
+
+puis téléchargez les règles de ponctuation :
+
+```python
+import nltk
+
+nltk.download("punkt")
+```
+
+Ensuite, nous importons le *tokenizer* de `nltk` et créons une fonction simple pour extraire les trois premières phrases d'une critique. La convention dans le résumé de texte est de séparer chaque résumé avec une nouvelle ligne, donc nous allons également inclure ceci et le tester sur un exemple d'entraînement :
+
+```python
+from nltk.tokenize import sent_tokenize
+
+
+def three_sentence_summary(text):
+ return "\n".join(sent_tokenize(text)[:3])
+
+
+print(three_sentence_summary(books_dataset["train"][1]["review_body"]))
+```
+
+```python out
+'I grew up reading Koontz, and years ago, I stopped,convinced i had "outgrown" him.' # J'ai grandi en lisant Koontz, et il y a des années, j'ai arrêté, convaincu que je l'avais "dépassé"
+'Still,when a friend was looking for something suspenseful too read, I suggested Koontz.' " Pourtant, quand une amie cherchait un livre à suspense, je lui ai suggéré Koontz.
+'She found Strangers.' # Elle a trouvé Strangers.
+```
+
+Cela semble fonctionner, alors implémentons maintenant une fonction qui extrait ces "résumés" d'un ensemble de données et calcule les scores ROUGE pour la ligne de base :
+
+```python
+def evaluate_baseline(dataset, metric):
+ summaries = [three_sentence_summary(text) for text in dataset["review_body"]]
+ return metric.compute(predictions=summaries, references=dataset["review_title"])
+```
+
+Nous pouvons ensuite utiliser cette fonction pour calculer les scores ROUGE sur l'ensemble de validation et les embellir un peu en utilisant Pandas :
+
+```python
+import pandas as pd
+
+score = evaluate_baseline(books_dataset["validation"], rouge_score)
+rouge_names = ["rouge1", "rouge2", "rougeL", "rougeLsum"]
+rouge_dict = dict((rn, round(score[rn].mid.fmeasure * 100, 2)) for rn in rouge_names)
+rouge_dict
+```
+
+```python out
+{'rouge1': 16.74, 'rouge2': 8.83, 'rougeL': 15.6, 'rougeLsum': 15.96}
+```
+
+Nous pouvons voir que le score de `rouge2` est significativement plus bas que le reste ; ceci reflète probablement le fait que les titres des revues sont typiquement concis et donc que la ligne de base de lead-3 est trop verbeuse. Maintenant que nous disposons d'une bonne base de travail, concentrons-nous sur le réglage fin de mT5 !
+
+{#if fw === 'pt'}
+
+## *Finetuning* de mT5 avec l'API `Trainer`.
+
+Le *finetuning* d'un modèle pour le résumé est très similaire aux autres tâches que nous avons couvertes dans ce chapitre. La première chose à faire est de charger le modèle pré-entraîné depuis le checkpoint `mt5-small`. Puisque la compression est une tâche de séquence à séquence, nous pouvons charger le modèle avec la classe `AutoModelForSeq2SeqLM`, qui téléchargera automatiquement et mettra en cache les poids :
+
+```python
+from transformers import AutoModelForSeq2SeqLM
+
+model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
+```
+
+{:else}
+
+## *Finetuning* de mT5 avec Keras
+
+Le *finetuning* d'un modèle pour le résumé est très similaire aux autres tâches que nous avons couvertes dans ce chapitre. La première chose à faire est de charger le modèle pré-entraîné à partir du point de contrôle `mt5-small`. Puisque la compression est une tâche de séquence à séquence, nous pouvons charger le modèle avec la classe `AutoModelForSeq2SeqLM`, qui téléchargera automatiquement et mettra en cache les poids :
+
+```python
+from transformers import TFAutoModelForSeq2SeqLM
+
+model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
+```
+
+{/if}
+
+
+
+💡 Si vous vous demandez pourquoi vous ne voyez aucun avertissement concernant l'affinement du modèle sur une tâche en aval, c'est parce que pour les tâches de séquence à séquence, nous conservons tous les poids du réseau. Comparez cela à notre modèle de classification de texte dans [Chapitre 3](/course/fr/chapter3), où la tête du modèle pré-entraîné a été remplacée par un réseau initialisé de manière aléatoire.
+
+
+
+La prochaine chose que nous devons faire est de nous connecter au *Hub*. Si vous exécutez ce code dans un *notebook*, vous pouvez le faire avec la fonction utilitaire suivante :
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+qui affichera un *widget* où vous pourrez saisir vos informations d'identification. Vous pouvez également exécuter cette commande dans votre terminal et vous connecter à partir de là :
+
+```
+huggingface-cli login
+```
+
+{#if fw === 'pt'}
+
+Nous aurons besoin de générer des résumés afin de calculer les scores ROUGE pendant l'entraînement. Heureusement, 🤗 *Transformers* fournit des classes dédiées `Seq2SeqTrainingArguments` et `Seq2SeqTrainer` qui peuvent faire cela pour nous automatiquement ! Pour voir comment cela fonctionne, définissons d'abord les hyperparamètres et autres arguments pour nos expériences :
+
+```python
+from transformers import Seq2SeqTrainingArguments
+
+batch_size = 8
+num_train_epochs = 8
+# La perte d'entraînement à chaque époque
+logging_steps = len(tokenized_datasets["train"]) // batch_size
+model_name = model_checkpoint.split("/")[-1]
+
+args = Seq2SeqTrainingArguments(
+ output_dir=f"{model_name}-finetuned-amazon-en-es",
+ evaluation_strategy="epoch",
+ learning_rate=5.6e-5,
+ per_device_train_batch_size=batch_size,
+ per_device_eval_batch_size=batch_size,
+ weight_decay=0.01,
+ save_total_limit=3,
+ num_train_epochs=num_train_epochs,
+ predict_with_generate=True,
+ logging_steps=logging_steps,
+ push_to_hub=True,
+)
+```
+
+Ici, l'argument `predict_with_generate` a été défini pour indiquer que nous devons générer des résumés pendant l'évaluation afin de pouvoir calculer les scores ROUGE pour chaque époque. Comme discuté dans [Chapter 1](/course/fr/chapter1), le décodeur effectue l'inférence en prédisant les tokens un par un, et ceci est implémenté par la méthode `generate()` du modèle. Définir `predict_with_generate=True` indique au `Seq2SeqTrainer` d'utiliser cette méthode pour l'évaluation. Nous avons également ajusté certains des hyperparamètres par défaut, comme le taux d'apprentissage, le nombre d'époques, et le taux de décroissance des poids, et nous avons réglé l'option `save_total_limit` pour ne sauvegarder que jusqu'à 3 *checkpoints* pendant l'entraînement. C'est parce que même la "petite" version de mT5 utilise environ un Go d'espace disque, et nous pouvons gagner un peu de place en limitant le nombre de copies que nous sauvegardons.
+
+L'argument `push_to_hub=True` nous permettra de pousser le modèle vers le Hub après l'entraînement ; vous trouverez le dépôt sous votre profil utilisateur dans l'emplacement défini par `output_dir`. Notez que vous pouvez spécifier le nom du dépôt vers lequel vous voulez pousser avec l'argument `hub_model_id` (en particulier, vous devrez utiliser cet argument pour pousser vers une organisation). Par exemple, lorsque nous avons poussé le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/mt5-finetuned-amazon-en-es"`à `Seq2SeqTrainingArguments`.
+
+La prochaine chose que nous devons faire est de fournir à l'entraîneur une fonction `compute_metrics()` afin que nous puissions évaluer notre modèle pendant l'entraînement. Pour le résumé, c'est un peu plus compliqué que de simplement appeler `rouge_score.compute()` sur les prédictions du modèle, puisque nous devons _décoder_ les sorties et les étiquettes en texte avant de pouvoir calculer les scores ROUGE. La fonction suivante fait exactement cela, et utilise également la fonction `sent_tokenize()` de `nltk` pour séparer les phrases du résumé avec des nouvelles lignes :
+
+
+```python
+import numpy as np
+
+
+def compute_metrics(eval_pred):
+ predictions, labels = eval_pred
+ # Décoder les résumés générés en texte
+ decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
+ # Remplacer -100 dans les étiquettes car nous ne pouvons pas les décoder
+ labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
+ # Décoder les résumés de référence en texte
+ decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
+ # ROUGE attend une nouvelle ligne après chaque phrase
+ decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
+ decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
+ # Calcul des scores ROUGE
+ result = rouge_score.compute(
+ predictions=decoded_preds, references=decoded_labels, use_stemmer=True
+ )
+ # Extract the median scores
+ result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
+ return {k: round(v, 4) for k, v in result.items()}
+```
+
+{/if}
+
+Ensuite, nous devons définir un collateur de données pour notre tâche de séquence à séquence. Comme mT5 est un modèle Transformer encodeur-décodeur, une des subtilités de la préparation de nos lots est que, pendant le décodage, nous devons décaler les étiquettes d'une unité vers la droite. Ceci est nécessaire pour garantir que le décodeur ne voit que les étiquettes de vérité terrain précédentes et non les étiquettes actuelles ou futures, qui seraient faciles à mémoriser pour le modèle. Cela ressemble à la façon dont l'auto-attention masquée est appliquée aux entrées dans une tâche comme [la modélisation causale du langage](/course/fr/chapter7/6).
+
+Heureusement, 🤗 *Transformers* fournit un collateur `DataCollatorForSeq2Seq` qui rembourrera dynamiquement les entrées et les étiquettes pour nous. Pour instancier ce collateur, nous devons simplement fournir le *tokenizer* et le `model` :
+
+{#if fw === 'pt'}
+
+```python
+from transformers import DataCollatorForSeq2Seq
+
+data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
+```
+
+{:else}
+
+```python
+from transformers import DataCollatorForSeq2Seq
+
+data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
+```
+
+{/if}
+
+Voyons ce que produit ce collateur lorsqu'on lui donne un petit lot d'exemples. Tout d'abord, nous devons supprimer les colonnes contenant des chaînes de caractères, car le collateur ne saura pas comment remplir ces éléments :
+
+```python
+tokenized_datasets = tokenized_datasets.remove_columns(
+ books_dataset["train"].column_names
+)
+```
+
+Comme le collateur attend une liste de `dict`s, où chaque `dict` représente un seul exemple dans l'ensemble de données, nous devons également mettre les données dans le format attendu avant de les transmettre au collateur de données :
+
+```python
+features = [tokenized_datasets["train"][i] for i in range(2)]
+data_collator(features)
+```
+
+```python out
+{'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]), 'input_ids': tensor([[ 1494, 259, 8622, 390, 259, 262, 2316, 3435, 955,
+ 772, 281, 772, 1617, 263, 305, 14701, 260, 1385,
+ 3031, 259, 24146, 332, 1037, 259, 43906, 305, 336,
+ 260, 1, 0, 0, 0, 0, 0, 0],
+ [ 259, 27531, 13483, 259, 7505, 260, 112240, 15192, 305,
+ 53198, 276, 259, 74060, 263, 260, 459, 25640, 776,
+ 2119, 336, 259, 2220, 259, 18896, 288, 4906, 288,
+ 1037, 3931, 260, 7083, 101476, 1143, 260, 1]]), 'labels': tensor([[ 7483, 259, 2364, 15695, 1, -100],
+ [ 259, 27531, 13483, 259, 7505, 1]]), 'decoder_input_ids': tensor([[ 0, 7483, 259, 2364, 15695, 1],
+ [ 0, 259, 27531, 13483, 259, 7505]])}
+```
+
+La principale chose à remarquer ici est que le premier exemple est plus long que le second, donc les `input_ids` et `attention_mask` du second exemple ont été complétés sur la droite avec un jeton `[PAD]` (dont l'ID est `0`). De même, nous pouvons voir que les `labels` ont été complétés par des `-100`s, pour s'assurer que les *tokens* de remplissage sont ignorés par la fonction de perte. Et enfin, nous pouvons voir un nouveau `decoder_input_ids` qui a déplacé les étiquettes vers la droite en insérant un jeton `[PAD]` dans la première entrée.
+
+{#if fw === 'pt'}
+
+Nous avons enfin tous les ingrédients dont nous avons besoin pour nous entraîner ! Nous devons maintenant simplement instancier le formateur avec les arguments standards :
+
+```python
+from transformers import Seq2SeqTrainer
+
+trainer = Seq2SeqTrainer(
+ model,
+ args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ tokenizer=tokenizer,
+ compute_metrics=compute_metrics,
+)
+```
+
+et lancer notre course d'entraînement :
+
+```python
+trainer.train()
+```
+
+Pendant l'entraînement, vous devriez voir la perte d'entraînement diminuer et les scores ROUGE augmenter à chaque époque. Une fois l'entraînement terminé, vous pouvez voir les scores ROUGE finaux en exécutant `Trainer.evaluate()` :
+
+```python
+trainer.evaluate()
+```
+
+```python out
+{'eval_loss': 3.028524398803711,
+ 'eval_rouge1': 16.9728,
+ 'eval_rouge2': 8.2969,
+ 'eval_rougeL': 16.8366,
+ 'eval_rougeLsum': 16.851,
+ 'eval_gen_len': 10.1597,
+ 'eval_runtime': 6.1054,
+ 'eval_samples_per_second': 38.982,
+ 'eval_steps_per_second': 4.914}
+```
+
+D'après les scores, nous pouvons voir que notre modèle a largement surpassé notre ligne de base lead-3. Bien ! La dernière chose à faire est de pousser les poids du modèle vers le *Hub*, comme suit :
+
+```
+trainer.push_to_hub(commit_message="Training complete", tags="summarization")
+```
+
+```python out
+'https://huggingface.co/huggingface-course/mt5-finetuned-amazon-en-es/commit/aa0536b829b28e73e1e4b94b8a5aacec420d40e0'
+```
+
+Ceci sauvegardera le point de contrôle et les fichiers de configuration dans `output_dir`, avant de télécharger tous les fichiers sur le *Hub*. En spécifiant l'argument `tags`, nous nous assurons également que le widget sur le Hub sera celui d'un pipeline de résumé au lieu de celui de la génération de texte par défaut associé à l'architecture mT5 (pour plus d'informations sur les balises de modèle, voir la [🤗 documentation du *Hub*](https://huggingface.co/docs/hub/main#how-is-a-models-type-of-inference-api-and-widget-determined)). La sortie de `trainer.push_to_hub()` est une URL vers le hash du commit Git, donc vous pouvez facilement voir les changements qui ont été faits au dépôt de modèle !
+
+Pour conclure cette section, voyons comment nous pouvons également affiner mT5 en utilisant les fonctionnalités de bas niveau fournies par 🤗 *Accelerate*.
+
+{:else}
+
+Nous sommes presque prêts à nous entraîner ! Nous devons juste convertir nos jeux de données en `tf.data.Dataset`s en utilisant le collateur de données que nous avons défini ci-dessus, et ensuite `compile()` et `fit()` le modèle. D'abord, les jeux de données :
+
+```python
+tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
+ columns=["input_ids", "attention_mask", "labels"],
+ collate_fn=data_collator,
+ shuffle=True,
+ batch_size=8,
+)
+tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
+ columns=["input_ids", "attention_mask", "labels"],
+ collate_fn=data_collator,
+ shuffle=False,
+ batch_size=8,
+)
+```
+
+Maintenant, nous définissons nos hyperparamètres d'entraînement et nous compilons :
+
+```python
+from transformers import create_optimizer
+import tensorflow as tf
+
+# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans l'ensemble de données, divisé par la taille du batch, puis multiplié par le nombre total d'époques.
+# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un batch tf.data.Dataset,
+# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size.
+num_train_epochs = 8
+num_train_steps = len(tf_train_dataset) * num_train_epochs
+model_name = model_checkpoint.split("/")[-1]
+
+optimizer, schedule = create_optimizer(
+ init_lr=5.6e-5,
+ num_warmup_steps=0,
+ num_train_steps=num_train_steps,
+ weight_decay_rate=0.01,
+)
+
+model.compile(optimizer=optimizer)
+
+# Entraîner en mixed-precision float16
+tf.keras.mixed_precision.set_global_policy("mixed_float16")
+```
+
+Et enfin, nous ajustons le modèle. Nous utilisons un `PushToHubCallback` pour sauvegarder le modèle sur le *Hub* après chaque époque, ce qui nous permettra de l'utiliser pour l'inférence plus tard :
+
+```python
+from transformers.keras_callbacks import PushToHubCallback
+
+callback = PushToHubCallback(
+ output_dir=f"{model_name}-finetuned-amazon-en-es", tokenizer=tokenizer
+)
+
+model.fit(
+ tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback], epochs=8
+)
+```
+
+Nous avons obtenu quelques valeurs de perte pendant l'entraînement, mais nous aimerions vraiment voir les métriques ROUGE que nous avons calculées plus tôt. Pour obtenir ces métriques, nous devons générer les sorties du modèle et les convertir en chaînes de caractères. Construisons quelques listes d'étiquettes et de prédictions pour comparer la métrique ROUGE (notez que si vous obtenez des erreurs d'importation pour cette section, vous pouvez avoir besoin de "pip install tqdm") :
+
+```python
+from tqdm import tqdm
+import numpy as np
+
+all_preds = []
+all_labels = []
+for batch in tqdm(tf_eval_dataset):
+ predictions = model.generate(**batch)
+ decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
+ labels = batch["labels"].numpy()
+ labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
+ decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
+ decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
+ decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
+ all_preds.extend(decoded_preds)
+ all_labels.extend(decoded_labels)
+```
+
+Une fois que nous avons nos listes d'étiquettes et de chaînes de prédiction, le calcul du score ROUGE est facile :
+
+```python
+result = rouge_score.compute(
+ predictions=decoded_preds, references=decoded_labels, use_stemmer=True
+)
+result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
+{k: round(v, 4) for k, v in result.items()}
+```
+
+```
+{'rouge1': 31.4815, 'rouge2': 25.4386, 'rougeL': 31.4815, 'rougeLsum': 31.4815}
+```
+
+
+{/if}
+
+{#if fw === 'pt'}
+
+## *Finetuning* de mT5 avec 🤗 *Accelerate*
+
+Le *finetuning* de notre modèle avec 🤗 *Accelerate* est très similaire à l'exemple de classification de texte que nous avons rencontré dans [Chapitre 3](/course/fr/chapter3). Les principales différences seront la nécessité de générer explicitement nos résumés pendant l'Entraînement et de définir comment nous calculons les scores ROUGE (rappelons que le `Seq2SeqTrainer` s'est occupé de la génération pour nous). Voyons comment nous pouvons mettre en œuvre ces deux exigences dans 🤗 *Accelerate* !
+
+### Préparer tout pour l'entraînement
+
+La première chose que nous devons faire est de créer un `DataLoader` pour chacun de nos splits. Puisque les chargeurs de données PyTorch attendent des batchs de tenseurs, nous devons définir le format à `"torch"` dans nos jeux de données :
+
+```python
+tokenized_datasets.set_format("torch")
+```
+
+Maintenant que nous avons des jeux de données constitués uniquement de tenseurs, la prochaine chose à faire est d'instancier à nouveau le `DataCollatorForSeq2Seq`. Pour cela, nous devons fournir une nouvelle version du modèle, donc chargeons-le à nouveau depuis notre cache :
+
+```python
+model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
+```
+
+Nous pouvons ensuite instancier le collateur de données et l'utiliser pour définir nos chargeurs de données :
+
+```python
+from torch.utils.data import DataLoader
+
+batch_size = 8
+train_dataloader = DataLoader(
+ tokenized_datasets["train"],
+ shuffle=True,
+ collate_fn=data_collator,
+ batch_size=batch_size,
+)
+eval_dataloader = DataLoader(
+ tokenized_datasets["validation"], collate_fn=data_collator, batch_size=batch_size
+)
+```
+
+La prochaine chose à faire est de définir l'optimiseur que nous voulons utiliser. Comme dans nos autres exemples, nous allons utiliser `AdamW`, qui fonctionne bien pour la plupart des problèmes :
+
+```python
+from torch.optim import AdamW
+
+optimizer = AdamW(model.parameters(), lr=2e-5)
+```
+
+Enfin, nous introduisons notre modèle, notre optimiseur et nos chargeurs de données dans la méthode `accelerator.prepare()` :
+
+```python
+from accelerate import Accelerator
+
+accelerator = Accelerator()
+model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
+ model, optimizer, train_dataloader, eval_dataloader
+)
+```
+
+
+
+🚨 Si vous vous entraînez sur un TPU, vous devrez déplacer tout le code ci-dessus dans une fonction d'entraînement dédiée. Voir le [Chapitre 3](/course/fr/chapter3) pour plus de détails.
+
+
+
+Maintenant que nous avons préparé nos objets, il reste trois choses à faire :
+
+* définir le programme du taux d'apprentissage,
+* implémenter une fonction pour post-traiter les résumés pour l'évaluation,
+* créer un référentiel sur le *Hub* vers lequel nous pouvons pousser notre modèle.
+
+Pour le programme de taux d'apprentissage, nous utiliserons le programme linéaire standard des sections précédentes :
+
+```python
+from transformers import get_scheduler
+
+num_train_epochs = 10
+num_update_steps_per_epoch = len(train_dataloader)
+num_training_steps = num_train_epochs * num_update_steps_per_epoch
+
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+```
+
+Pour le post-traitement, nous avons besoin d'une fonction qui divise les résumés générés en phrases séparées par des nouvelles lignes. C'est le format attendu par la métrique ROUGE, et nous pouvons y parvenir avec le bout de code suivant :
+
+```python
+def postprocess_text(preds, labels):
+ preds = [pred.strip() for pred in preds]
+ labels = [label.strip() for label in labels]
+
+ # ROUGE attend une nouvelle ligne après chaque phrase
+ preds = ["\n".join(nltk.sent_tokenize(pred)) for pred in preds]
+ labels = ["\n".join(nltk.sent_tokenize(label)) for label in labels]
+
+ return preds, labels
+```
+
+Cela devrait vous sembler familier si vous vous rappelez comment nous avons défini la fonction `compute_metrics()` du `Seq2SeqTrainer`.
+
+Enfin, nous devons créer un dépôt de modèles sur le *Hub*. Pour cela, nous pouvons utiliser la bibliothèque 🤗 *Hub*, qui porte le nom approprié. Nous avons juste besoin de définir un nom pour notre référentiel, et la bibliothèque a une fonction utilitaire pour combiner l'ID du référentiel avec le profil de l'utilisateur :
+
+```python
+from huggingface_hub import get_full_repo_name
+
+model_name = "test-bert-finetuned-squad-accelerate"
+repo_name = get_full_repo_name(model_name)
+repo_name
+```
+
+```python out
+'lewtun/mt5-finetuned-amazon-en-es-accelerate'
+```
+
+Nous pouvons maintenant utiliser ce nom de référentiel pour cloner une version locale dans notre répertoire de résultats qui stockera les artefacts d'entraînement :
+
+```python
+from huggingface_hub import Repository
+
+output_dir = "results-mt5-finetuned-squad-accelerate"
+repo = Repository(output_dir, clone_from=repo_name)
+```
+
+This will allow us to push the artifacts back to the Hub by calling the `repo.push_to_hub()` method during training! Let's now wrap up our analysis by writing out the training loop.
+
+### Boucle d'entraînement
+
+La boucle d'entraînement pour le résumé est assez similaire aux autres exemples 🤗 *Accelerate* que nous avons rencontrés et est grossièrement divisée en quatre étapes principales :
+
+1. entraîner le modèle en itérant sur tous les exemples dans `train_dataloader` pour chaque époque,
+2. générer les résumés du modèle à la fin de chaque époque, en générant d'abord les *tokens* puis en les décodant (ainsi que les résumés de référence) en texte,
+3. calculer les scores ROUGE en utilisant les mêmes techniques que nous avons vues précédemment,
+4. sauvegarder les points de contrôle et pousser le tout vers le *Hub*. Ici, nous nous appuyons sur l'argument `blocking=False` de l'objet `Repository` afin de pouvoir pousser les points de contrôle par époque de manière _asynchrone_. Cela nous permet de poursuivre l'entraînement sans avoir à attendre le téléchargement quelque peu lent associé à un modèle de la taille d'un Go !
+
+Ces étapes peuvent être vues dans le bloc de code suivant :
+
+```python
+from tqdm.auto import tqdm
+import torch
+import numpy as np
+
+progress_bar = tqdm(range(num_training_steps))
+
+for epoch in range(num_train_epochs):
+ # Entraînement
+ model.train()
+ for step, batch in enumerate(train_dataloader):
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+
+ # Evaluation
+ model.eval()
+ for step, batch in enumerate(eval_dataloader):
+ with torch.no_grad():
+ generated_tokens = accelerator.unwrap_model(model).generate(
+ batch["input_ids"],
+ attention_mask=batch["attention_mask"],
+ )
+
+ generated_tokens = accelerator.pad_across_processes(
+ generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
+ )
+ labels = batch["labels"]
+
+ # Si nous n'avons pas rempli la longueur maximale, nous devons également remplir les étiquettes.
+ labels = accelerator.pad_across_processes(
+ batch["labels"], dim=1, pad_index=tokenizer.pad_token_id
+ )
+
+ generated_tokens = accelerator.gather(generated_tokens).cpu().numpy()
+ labels = accelerator.gather(labels).cpu().numpy()
+
+ # Remplacer -100 dans les étiquettes car nous ne pouvons pas les décoder.
+ labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
+ if isinstance(generated_tokens, tuple):
+ generated_tokens = generated_tokens[0]
+ decoded_preds = tokenizer.batch_decode(
+ generated_tokens, skip_special_tokens=True
+ )
+ decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
+
+ decoded_preds, decoded_labels = postprocess_text(
+ decoded_preds, decoded_labels
+ )
+
+ rouge_score.add_batch(predictions=decoded_preds, references=decoded_labels)
+
+ # Calculer les métriques
+ result = rouge_score.compute()
+ # Extract the median ROUGE scores
+ result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
+ result = {k: round(v, 4) for k, v in result.items()}
+ print(f"Epoch {epoch}:", result)
+
+ # Sauvegarder et télécharger
+ accelerator.wait_for_everyone()
+ unwrapped_model = accelerator.unwrap_model(model)
+ unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
+ if accelerator.is_main_process:
+ tokenizer.save_pretrained(output_dir)
+ repo.push_to_hub(
+ commit_message=f"Training in progress epoch {epoch}", blocking=False
+ )
+```
+
+```python out
+Epoch 0: {'rouge1': 5.6351, 'rouge2': 1.1625, 'rougeL': 5.4866, 'rougeLsum': 5.5005}
+Epoch 1: {'rouge1': 9.8646, 'rouge2': 3.4106, 'rougeL': 9.9439, 'rougeLsum': 9.9306}
+Epoch 2: {'rouge1': 11.0872, 'rouge2': 3.3273, 'rougeL': 11.0508, 'rougeLsum': 10.9468}
+Epoch 3: {'rouge1': 11.8587, 'rouge2': 4.8167, 'rougeL': 11.7986, 'rougeLsum': 11.7518}
+Epoch 4: {'rouge1': 12.9842, 'rouge2': 5.5887, 'rougeL': 12.7546, 'rougeLsum': 12.7029}
+Epoch 5: {'rouge1': 13.4628, 'rouge2': 6.4598, 'rougeL': 13.312, 'rougeLsum': 13.2913}
+Epoch 6: {'rouge1': 12.9131, 'rouge2': 5.8914, 'rougeL': 12.6896, 'rougeLsum': 12.5701}
+Epoch 7: {'rouge1': 13.3079, 'rouge2': 6.2994, 'rougeL': 13.1536, 'rougeLsum': 13.1194}
+Epoch 8: {'rouge1': 13.96, 'rouge2': 6.5998, 'rougeL': 13.9123, 'rougeLsum': 13.7744}
+Epoch 9: {'rouge1': 14.1192, 'rouge2': 7.0059, 'rougeL': 14.1172, 'rougeLsum': 13.9509}
+```
+
+Et c'est tout ! Une fois que vous l'aurez exécuté, vous aurez un modèle et des résultats assez similaires à ceux que nous avons obtenus avec le `Trainer`.
+
+{/if}
+
+## Utilisation de votre modèle *finetuné*
+
+Une fois que vous avez poussé le modèle vers le *Hub*, vous pouvez jouer avec lui soit via le widget d'inférence, soit avec un objet `pipeline`, comme suit :
+
+```python
+from transformers import pipeline
+
+hub_model_id = "huggingface-course/mt5-small-finetuned-amazon-en-es"
+summarizer = pipeline("summarization", model=hub_model_id)
+```
+
+Nous pouvons alimenter notre pipeline avec quelques exemples de l'ensemble de test (que le modèle n'a pas vu) pour avoir une idée de la qualité des résumés. Tout d'abord, implémentons une fonction simple pour afficher ensemble la critique, le titre et le résumé généré :
+
+```python
+def print_summary(idx):
+ review = books_dataset["test"][idx]["review_body"]
+ title = books_dataset["test"][idx]["review_title"]
+ summary = summarizer(books_dataset["test"][idx]["review_body"])[0]["summary_text"]
+ print(f"'>>> Review: {review}'")
+ print(f"\n'>>> Title: {title}'")
+ print(f"\n'>>> Summary: {summary}'")
+```
+
+Examinons l'un des exemples anglais que nous recevons :
+
+```python
+print_summary(100)
+```
+
+```python out
+'>>> Review: Nothing special at all about this product... the book is too small and stiff and hard to write in. The huge sticker on the back doesn’t come off and looks super tacky. I would not purchase this again. I could have just bought a journal from the dollar store and it would be basically the same thing. It’s also really expensive for what it is.'
+# Ce produit n'a rien de spécial... le livre est trop petit et rigide et il est difficile d'y écrire. L'énorme autocollant au dos ne se détache pas et a l'air super collant. Je n'achèterai plus jamais ce produit. J'aurais pu simplement acheter un journal dans un magasin à un dollar et ce serait à peu près la même chose. Il est également très cher pour ce qu'il est.
+
+'>>> Title: Not impressed at all... buy something else' # Pas du tout impressionné... achetez autre chose.
+
+'>>> Summary: Nothing special at all about this product' # Rien de spécial à propos de ce produit
+```
+
+Ce n'est pas si mal ! Nous pouvons voir que notre modèle a été capable d'effectuer un résumé _abstractif_ en augmentant certaines parties de la critique avec de nouveaux mots. Et peut-être que l'aspect le plus cool de notre modèle est qu'il est bilingue, donc nous pouvons également générer des résumés de critiques en espagnol :
+
+```python
+print_summary(0)
+```
+
+```python out
+'>>> Review: Es una trilogia que se hace muy facil de leer. Me ha gustado, no me esperaba el final para nada' # C'est une trilogie qui se lit très facilement. J'ai aimé, je ne m'attendais pas du tout à la fin.
+
+'>>> Title: Buena literatura para adolescentes' # Bonne littérature pour les adolescents
+
+'>>> Summary: Muy facil de leer' # Très facile à lire
+```
+
+Le résumé se traduit par "Très facile à lire", ce qui, comme nous pouvons le constater, a été extrait directement de la critique. Néanmoins, cela montre la polyvalence du modèle mT5 et vous a donné un aperçu de ce que c'est que de traiter un corpus multilingue !
+
+Ensuite, nous allons nous intéresser à une tâche un peu plus complexe : entraîner un modèle de langue à partir de zéro.
diff --git a/chapters/fr/chapter7/7.mdx b/chapters/fr/chapter7/7.mdx
index e2074354a..e301b1bac 100644
--- a/chapters/fr/chapter7/7.mdx
+++ b/chapters/fr/chapter7/7.mdx
@@ -1,1228 +1,1228 @@
-
-
-# Réponse aux questions
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Il est temps de s'intéresser à la réponse aux questions ! Cette tâche peut prendre plusieurs formes, mais celle sur laquelle nous allons nous concentrer dans cette section est appelée réponse aux questions *extractives*. Il s'agit de poser des questions sur un document et d'identifier les réponses sous forme de "morceaux de texte" dans le document lui-même.
-
-
-
-Nous allons affiner un modèle BERT sur le [jeu de données SQuAD](https://rajpurkar.github.io/SQuAD-explorer/), qui consiste en des questions posées par des *crowdworkers* sur un ensemble d'articles de Wikipedia. Cela nous donnera un modèle capable de calculer des prédictions comme celle-ci :
-
-
-
-
-Il s'agit en fait de la présentation du modèle qui a été entraîné et téléchargé sur le *Hub* à l'aide du code présenté dans cette section. Vous pouvez le trouver et vérifier les prédictions [ici](https://huggingface.co/huggingface-course/bert-finetuned-squad?context=%F0%9F%A4%97+Transformers+is+backed+by+the+three+most+popular+deep+learning+libraries+%E2%80%94+Jax%2C+PyTorch+and+TensorFlow+%E2%80%94+with+a+seamless+integration+between+them.+It%27s+straightforward+to+train+your+models+with+one+before+loading+them+for+inference+with+the+other.&question=Which+deep+learning+libraries+back+%F0%9F%A4%97+Transformers%3F)
-
-
-
-💡 Les modèles à codeur unique comme BERT ont tendance à être excellents pour extraire les réponses à des questions factuelles comme "Qui a inventé l'architecture Transformer ?", mais ne sont pas très performants lorsqu'on leur pose des questions ouvertes comme "Pourquoi le ciel est-il bleu ?". Dans ces cas plus difficiles, les modèles encodeurs-décodeurs comme T5 et BART sont généralement utilisés pour synthétiser les informations d'une manière assez similaire au [résumé de texte](/cours/fr/chapter7/5). Si vous êtes intéressé par ce type de réponse aux questions *génératives*, nous vous recommandons de consulter notre [démo](https://yjernite.github.io/lfqa.html) basée sur le [jeu de données ELI5](https://huggingface.co/datasets/eli5).
-
-
-
-## Préparation des données
-
-Le jeu de données le plus utilisé comme référence académique pour la réponse extractive aux questions est [SQuAD](https://rajpurkar.github.io/SQuAD-explorer/), c'est donc celui que nous utiliserons ici. Il existe également une référence plus difficile [SQuAD v2](https://huggingface.co/datasets/squad_v2), qui comprend des questions sans réponse. Tant que votre propre jeu de données contient une colonne pour les contextes, une colonne pour les questions et une colonne pour les réponses, vous devriez être en mesure d'adapter les étapes ci-dessous.
-
-### Le jeu de données SQuAD
-
-Comme d'habitude, nous pouvons télécharger et mettre en cache le jeu de données en une seule étape grâce à `load_dataset()` :
-
-```py
-from datasets import load_dataset
-
-raw_datasets = load_dataset("squad")
-```
-
-Nous pouvons alors jeter un coup d'œil à cet objet pour en savoir plus sur le jeu de données SQuAD :
-
-```py
-raw_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['id', 'title', 'context', 'question', 'answers'],
- num_rows: 87599
- })
- validation: Dataset({
- features: ['id', 'title', 'context', 'question', 'answers'],
- num_rows: 10570
- })
-})
-```
-
-On dirait que nous avons tout ce dont nous avons besoin avec les champs `context`, `question` et `answers`, alors imprimons-les pour le premier élément de notre ensemble d'entraînement :
-
-```py
-print("Context: ", raw_datasets["train"][0]["context"])
-print("Question: ", raw_datasets["train"][0]["question"])
-print("Answer: ", raw_datasets["train"][0]["answers"])
-```
-
-```python out
-Context: 'Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend "Venite Ad Me Omnes". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.'
-# Sur le plan architectural, l'école a un caractère catholique. Au sommet du dôme doré du bâtiment principal se trouve une statue dorée de la Vierge Marie. Immédiatement devant le bâtiment principal et face à lui, se trouve une statue en cuivre du Christ, les bras levés, avec la légende "Venite Ad Me Omnes". À côté du bâtiment principal se trouve la basilique du Sacré-Cœur. Immédiatement derrière la basilique se trouve la Grotte, un lieu marial de prière et de réflexion. Il s'agit d'une réplique de la grotte de Lourdes, en France, où la Vierge Marie serait apparue à Sainte Bernadette Soubirous en 1858. Au bout de l'allée principale (et dans une ligne directe qui passe par 3 statues et le Dôme d'or), se trouve une statue de pierre simple et moderne de Marie'.
-Question: 'To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?' # A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes, en France ?
-Answer: {'text': ['Saint Bernadette Soubirous'], 'answer_start': [515]}
-```
-
-Les champs `context` et `question` sont très simples à utiliser. Le champ `answers` est un peu plus délicat car il compile un dictionnaire avec deux champs qui sont tous deux des listes. C'est le format qui sera attendu par la métrique `squad` lors de l'évaluation ; si vous utilisez vos propres données, vous n'avez pas nécessairement besoin de vous soucier de mettre les réponses dans le même format. Le champ `text` est assez évident, et le champ `answer_start` contient l'indice du caractère de départ de chaque réponse dans le contexte.
-
-Pendant l'entraînement, il n'y a qu'une seule réponse possible. Nous pouvons vérifier cela en utilisant la méthode `Dataset.filter()` :
-
-```py
-raw_datasets["train"].filter(lambda x: len(x["answers"]["text"]) != 1)
-```
-
-```python out
-Dataset({
- features: ['id', 'title', 'context', 'question', 'answers'],
- num_rows: 0
-})
-```
-
-Pour l'évaluation, cependant, il existe plusieurs réponses possibles pour chaque échantillon, qui peuvent être identiques ou différentes :
-
-```py
-print(raw_datasets["validation"][0]["answers"])
-print(raw_datasets["validation"][2]["answers"])
-```
-
-```python out
-{'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]}
-{'text': ['Santa Clara, California', "Levi's Stadium", "Levi's Stadium in the San Francisco Bay Area at Santa Clara, California."], 'answer_start': [403, 355, 355]}
-```
-
-Nous ne nous plongerons pas dans le script d'évaluation car tout sera enveloppé par une métrique 🤗 *Datasets* pour nous, mais la version courte est que certaines des questions ont plusieurs réponses possibles, et ce script va comparer une réponse prédite à toutes les réponses acceptables et prendre le meilleur score. Si nous regardons l'échantillon de l'indice 2, par exemple :
-
-```py
-print(raw_datasets["validation"][2]["context"])
-print(raw_datasets["validation"][2]["question"])
-```
-
-```python out
-'Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi\'s Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the "golden anniversary" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as "Super Bowl L"), so that the logo could prominently feature the Arabic numerals 50.'
-# Le Super Bowl 50 était un match de football américain visant à déterminer le champion de la National Football League (NFL) pour la saison 2015. Les Denver Broncos, champions de la Conférence de football américain (AFC), ont battu les Carolina Panthers, champions de la Conférence nationale de football (NFC), 24 à 10, pour remporter leur troisième titre de Super Bowl. Le match s'est déroulé le 7 février 2016 au Levi\'s Stadium, dans la baie de San Francisco, à Santa Clara, en Californie. Comme il s'agissait du 50e Super Bowl, la ligue a mis l'accent sur l'" anniversaire doré " avec diverses initiatives sur le thème de l'or, ainsi qu'en suspendant temporairement la tradition de nommer chaque match du Super Bowl avec des chiffres romains (en vertu de laquelle le match aurait été appelé " Super Bowl L "), afin que le logo puisse mettre en évidence les chiffres arabes 50.''
-'Where did Super Bowl 50 take place?' # Où a eu lieu le Super Bowl 50 ?
-```
-
-nous pouvons voir que la réponse peut effectivement être l'une des trois possibilités que nous avons vues précédemment.
-
-### Traitement des données d'entraînement
-
-
-
-Commençons par le prétraitement des données d'entraînement. La partie la plus difficile sera de générer des étiquettes pour la réponse à la question, qui seront les positions de début et de fin des *tokens* correspondant à la réponse dans le contexte.
-
-Mais ne nous emballons pas. Tout d'abord, nous devons convertir le texte de l'entrée en identifiants que le modèle peut comprendre, en utilisant un *tokenizer* :
-
-```py
-from transformers import AutoTokenizer
-
-model_checkpoint = "bert-base-cased"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-```
-
-Comme mentionné précédemment, nous allons *finetuner* un modèle BERT, mais vous pouvez utiliser n'importe quel autre type de modèle tant qu'il a un *tokenizer* rapide implémenté. Vous pouvez voir toutes les architectures qui sont livrées avec une version rapide dans [ce grand tableau](https://huggingface.co/transformers/#supported-frameworks), et pour vérifier que l'objet `tokenizer` que vous utilisez est bien soutenu par des 🤗 *Tokenizers* vous pouvez regarder son attribut `is_fast` :
-
-```py
-tokenizer.is_fast
-```
-
-```python out
-True
-```
-
-Nous pouvons transmettre à notre *tokenizer* la question et le contexte ensemble, et il insérera correctement les *tokens* spéciaux pour former une phrase comme celle-ci :
-
-```
-[CLS] question [SEP] context [SEP]
-```
-
-Vérifions à nouveau :
-
-```py
-context = raw_datasets["train"][0]["context"]
-question = raw_datasets["train"][0]["question"]
-
-inputs = tokenizer(question, context)
-tokenizer.decode(inputs["input_ids"])
-```
-
-```python out
-'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, '
-'the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin '
-'Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms '
-'upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred '
-'Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a '
-'replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette '
-'Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues '
-'and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]'
-
-'[CLS] A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes en France ? [SEP] Architecturalement, '
-l'école a un caractère catholique. Au sommet du dôme doré du bâtiment principal se trouve une statue dorée de la Vierge '
-Marie. Immédiatement devant le bâtiment principal et face à lui, se trouve une statue en cuivre du Christ, les bras ''levés''.
-'levés avec la légende " Venite Ad Me Omnes ". A côté du bâtiment principal se trouve la basilique du Sacré-Cœur.
-'Cœur. Immédiatement derrière la basilique se trouve la Grotte, un lieu marial de prière et de réflexion. Il s'agit d'une '
-'réplique de la grotte de Lourdes, en France, où la Vierge Marie serait apparue à Sainte Bernadette '
-Soubirous en 1858. Au bout de l'allée principale ( et en ligne directe qui passe par 3 statues '
-'et le Dôme d'or), se trouve une statue de Marie en pierre, simple et moderne. [SEP]''
-```
-
-Les étiquettes seront alors l'index des *tokens* de début et de fin de la réponse, et le modèle sera chargé de prédire un logit de début et de fin par *token* dans l'entrée, les étiquettes théoriques étant les suivantes :
-
-
-
-
-
-
-Dans ce cas, le contexte n'est pas trop long, mais certains des exemples de l'ensemble de données ont des contextes très longs qui dépasseront la longueur maximale que nous avons fixée (qui est de 384 dans ce cas). Comme nous l'avons vu dans le [Chapitre 6](/course/fr/chapter6/4) lorsque nous avons exploré les internes du pipeline `question-answering`, nous allons traiter les contextes longs en créant plusieurs caractéristiques d'entraînement à partir d'un échantillon de notre jeu de données, avec une fenêtre glissante entre eux.
-
-Pour voir comment cela fonctionne en utilisant l'exemple actuel, nous pouvons limiter la longueur à 100 et utiliser une fenêtre glissante de 50 *tokens*. Pour rappel, nous utilisons
-
-- `max_length` pour définir la longueur maximale (ici 100)
-- `truncation="only_second"` pour tronquer le contexte (qui est en deuxième position) quand la question avec son contexte est trop longue
-- `stride` pour fixer le nombre de *tokens* se chevauchant entre deux morceaux successifs (ici 50)
-- `return_overflowing_tokens=True` pour indiquer au tokenizer que l'on veut les *tokens* qui débordent
-
-```py
-inputs = tokenizer(
- question,
- context,
- max_length=100,
- truncation="only_second",
- stride=50,
- return_overflowing_tokens=True,
-)
-
-for ids in inputs["input_ids"]:
- print(tokenizer.decode(ids))
-```
-
-```python out
-'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basi [SEP]'
-'[CLS] A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes en France ? [SEP] Sur le plan architectural, l'école a un caractère catholique. Au sommet du dôme doré du bâtiment principal se trouve une statue dorée de la Vierge Marie. Immédiatement devant le bâtiment principal et face à lui, se trouve une statue en cuivre du Christ, les bras levés, avec la légende " Venite Ad Me Omnes ". À côté du bâtiment principal se trouve la basilique du Sacré-Cœur. Immédiatement derrière la basi [SEP]''.
-
-'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin [SEP]'
-'[CLS] A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes en France ? [le bâtiment principal et face à lui, une statue en cuivre du Christ aux bras levés avec la légende " Venite Ad Me Omnes ". À côté du bâtiment principal se trouve la basilique du Sacré-Cœur. Immédiatement derrière la basilique se trouve la Grotte, un lieu marial de prière et de réflexion. Il s'agit d'une réplique de la grotte de Lourdes, en France, où la Vierge [SEP]''.
-
-'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 [SEP]'
-'[CLS] A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes en France ? [A côté du bâtiment principal se trouve la basilique du Sacré-Cœur. Immédiatement derrière la basilique se trouve la Grotte, un lieu marial de prière et de réflexion. Il s'agit d'une réplique de la grotte de Lourdes, en France, où la Vierge Marie serait apparue à Sainte Bernadette Soubirous en 1858. Au bout de l'allée principale ( et dans une ligne directe qui relie par 3 [SEP]''.
-
-'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP]. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]'
-'[CLS] A qui la Vierge Marie est-elle prétendument apparue en 1858 à Lourdes France ? [SEP]. Il s'agit d'une réplique de la grotte de Lourdes, en France, où la Vierge Marie serait apparue à Sainte Bernadette Soubirous en 1858. Au bout de l'allée principale (et dans une ligne directe qui passe par 3 statues et le Dôme d'or), se trouve une simple statue de pierre moderne de Marie. [SEP]'
-```
-
-Comme nous pouvons le voir, notre exemple a été divisé en quatre entrées, chacune d'entre elles contenant la question et une partie du contexte. Notez que la réponse à la question ("Bernadette Soubirous") n'apparaît que dans la troisième et dernière entrée, donc en traitant les longs contextes de cette façon, nous allons créer quelques exemples d'entraînement où la réponse n'est pas incluse dans le contexte. Pour ces exemples, les étiquettes seront `start_position = end_position = 0` (donc nous prédisons le *token* `[CLS]`). Nous définirons également ces étiquettes dans le cas malheureux où la réponse a été tronquée de sorte que nous n'avons que le début (ou la fin) de celle-ci. Pour les exemples où la réponse est entièrement dans le contexte, les étiquettes seront l'index du *token* où la réponse commence et l'index du *token* où la réponse se termine.
-
-L'ensemble de données nous fournit le caractère de début de la réponse dans le contexte, et en ajoutant la longueur de la réponse, nous pouvons trouver le caractère de fin dans le contexte. Pour faire correspondre ces indices aux *tokens*, nous devrons utiliser les mappages d'offset que nous avons étudiés au [Chapitre 6](/course/chapter6/4). Nous pouvons faire en sorte que notre *tokenizer* renvoie ces index en passant `return_offsets_mapping=True` :
-
-```py
-inputs = tokenizer(
- question,
- context,
- max_length=100,
- truncation="only_second",
- stride=50,
- return_overflowing_tokens=True,
- return_offsets_mapping=True,
-)
-inputs.keys()
-```
-
-```python out
-dict_keys(['input_ids', 'token_type_ids', 'attention_mask', 'offset_mapping', 'overflow_to_sample_mapping'])
-```
-
-Comme nous pouvons le voir, nous récupérons les habituels ID d'entrée, ID de type de jeton, et masque d'attention, ainsi que le mappage d'offset dont nous avions besoin et une clé supplémentaire, `overflow_to_sample_mapping`. La valeur correspondante nous sera utile lorsque nous tokeniserons plusieurs textes en même temps (ce que nous devrions faire pour bénéficier du fait que notre *tokenizer* est soutenu par Rust). Puisqu'un échantillon peut donner plusieurs caractéristiques, il fait correspondre chaque caractéristique à l'exemple d'où elle provient. Parce qu'ici nous avons seulement tokenisé un exemple, nous obtenons une liste de `0`s :
-
-```py
-inputs["overflow_to_sample_mapping"]
-```
-
-```python out
-[0, 0, 0, 0]
-```
-
-Mais si nous tokenisons plus d'exemples, cela deviendra plus utile :
-
-```py
-inputs = tokenizer(
- raw_datasets["train"][2:6]["question"],
- raw_datasets["train"][2:6]["context"],
- max_length=100,
- truncation="only_second",
- stride=50,
- return_overflowing_tokens=True,
- return_offsets_mapping=True,
-)
-
-print(f"The 4 examples gave {len(inputs['input_ids'])} features.")
-print(f"Here is where each comes from: {inputs['overflow_to_sample_mapping']}.")
-```
-
-```python out
-'The 4 examples gave 19 features.'
-'Here is where each comes from: [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3].'
-```
-
-Comme nous pouvons le voir, les trois premiers exemples (aux indices 2, 3 et 4 de l'ensemble d'entraînement) ont chacun donné quatre caractéristiques et le dernier exemple (à l'indice 5 de l'ensemble d'entraînement) a donné 7 caractéristiques.
-
-Ces informations seront utiles pour associer chaque caractéristique obtenue à son étiquette correspondante. Comme mentionné précédemment, ces étiquettes sont :
-
-- `(0, 0)` si la réponse n'est pas dans l'espace correspondant du contexte.
-- `(start_position, end_position)` si la réponse est dans l'espace correspondant du contexte, avec `start_position` étant l'index du *token* (dans les IDs d'entrée) au début de la réponse et `end_position` étant l'index du *token* (dans les IDs d'entrée) où la réponse se termine.
-
-Pour déterminer ce qui est le cas et, le cas échéant, les positions des *tokens*, nous trouvons d'abord les indices qui commencent et finissent le contexte dans les IDs d'entrée. Nous pourrions utiliser les IDs du type de *token* pour le faire, mais puisque ceux-ci n'existent pas nécessairement pour tous les modèles (DistilBERT ne les requiert pas, par exemple), nous allons plutôt utiliser la méthode `sequence_ids()` du `BatchEncoding` que notre tokenizer retourne.
-
-Une fois que nous avons ces indices de *tokens*, nous regardons les offsets correspondants, qui sont des tuples de deux entiers représentant l'étendue des caractères dans le contexte original. Nous pouvons ainsi détecter si le *chunk* du contexte dans cette fonctionnalité commence après la réponse ou se termine avant que la réponse ne commence (dans ce cas, l'étiquette est `(0, 0)`). Si ce n'est pas le cas, nous bouclons pour trouver le premier et le dernier *token* de la réponse :
-
-```py
-answers = raw_datasets["train"][2:6]["answers"]
-start_positions = []
-end_positions = []
-
-for i, offset in enumerate(inputs["offset_mapping"]):
- sample_idx = inputs["overflow_to_sample_mapping"][i]
- answer = answers[sample_idx]
- start_char = answer["answer_start"][0]
- end_char = answer["answer_start"][0] + len(answer["text"][0])
- sequence_ids = inputs.sequence_ids(i)
-
- # Trouver le début et la fin du contexte
- idx = 0
- while sequence_ids[idx] != 1:
- idx += 1
- context_start = idx
- while sequence_ids[idx] == 1:
- idx += 1
- context_end = idx - 1
-
- # Si la réponse n'est pas entièrement dans le contexte, l'étiquette est (0, 0).
- if offset[context_start][0] > start_char or offset[context_end][1] < end_char:
- start_positions.append(0)
- end_positions.append(0)
- else:
- # Otherwise it's the start and end token positions
- idx = context_start
- while idx <= context_end and offset[idx][0] <= start_char:
- idx += 1
- start_positions.append(idx - 1)
-
- idx = context_end
- while idx >= context_start and offset[idx][1] >= end_char:
- idx -= 1
- end_positions.append(idx + 1)
-
-start_positions, end_positions
-```
-
-```python out
-([83, 51, 19, 0, 0, 64, 27, 0, 34, 0, 0, 0, 67, 34, 0, 0, 0, 0, 0],
- [85, 53, 21, 0, 0, 70, 33, 0, 40, 0, 0, 0, 68, 35, 0, 0, 0, 0, 0])
-```
-
-Jetons un coup d'œil à quelques résultats pour vérifier que notre approche est correcte. Pour la première caractéristique, nous trouvons `(83, 85)` comme étiquettes, alors comparons la réponse théorique avec l'étendue décodée des *tokens* de 83 à 85 (inclus) :
-
-```py
-idx = 0
-sample_idx = inputs["overflow_to_sample_mapping"][idx]
-answer = answers[sample_idx]["text"][0]
-
-start = start_positions[idx]
-end = end_positions[idx]
-labeled_answer = tokenizer.decode(inputs["input_ids"][idx][start : end + 1])
-
-print(f"Theoretical answer: {answer}, labels give: {labeled_answer}")
-```
-
-```python out
-'Theoretical answer: the Main Building, labels give: the Main Building'
-```
-
-Donc, c'est une correspondance ! Maintenant, vérifions l'index 4, où nous avons mis les étiquettes à `(0, 0)`, ce qui signifie que la réponse n'est pas dans le *chunk* de contexte de cette caractéristique :
-
-```py
-idx = 4
-sample_idx = inputs["overflow_to_sample_mapping"][idx]
-answer = answers[sample_idx]["text"][0]
-
-decoded_example = tokenizer.decode(inputs["input_ids"][idx])
-print(f"Theoretical answer: {answer}, decoded example: {decoded_example}")
-```
-
-```python out
-'Theoretical answer: a Marian place of prayer and reflection, decoded example: [CLS] What is the Grotto at Notre Dame? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grot [SEP]'
-```
-
-En effet, nous ne voyons pas la réponse dans le contexte.
-
-
-
-✏️ **A votre tour !** En utilisant l'architecture XLNet, le *padding* est appliqué à gauche et la question et le contexte sont intervertis. Adaptez tout le code que nous venons de voir à l'architecture XLNet (et ajoutez `padding=True`). Soyez conscient que le token `[CLS]` peut ne pas être à la position 0 avec le *padding* appliqué.
-
-
-
-Maintenant que nous avons vu étape par étape comment prétraiter nos données d'entraînement, nous pouvons les regrouper dans une fonction que nous appliquerons à l'ensemble des données d'entraînement. Nous allons rembourrer chaque caractéristique à la longueur maximale que nous avons définie, car la plupart des contextes seront longs (et les échantillons correspondants seront divisés en plusieurs caractéristiques), il n'y a donc pas de réel avantage à appliquer un rembourrage dynamique ici :
-
-```py
-max_length = 384
-stride = 128
-
-
-def preprocess_training_examples(examples):
- questions = [q.strip() for q in examples["question"]]
- inputs = tokenizer(
- questions,
- examples["context"],
- max_length=max_length,
- truncation="only_second",
- stride=stride,
- return_overflowing_tokens=True,
- return_offsets_mapping=True,
- padding="max_length",
- )
-
- offset_mapping = inputs.pop("offset_mapping")
- sample_map = inputs.pop("overflow_to_sample_mapping")
- answers = examples["answers"]
- start_positions = []
- end_positions = []
-
- for i, offset in enumerate(offset_mapping):
- sample_idx = sample_map[i]
- answer = answers[sample_idx]
- start_char = answer["answer_start"][0]
- end_char = answer["answer_start"][0] + len(answer["text"][0])
- sequence_ids = inputs.sequence_ids(i)
-
- # Trouver le début et la fin du contexte
- idx = 0
- while sequence_ids[idx] != 1:
- idx += 1
- context_start = idx
- while sequence_ids[idx] == 1:
- idx += 1
- context_end = idx - 1
-
- # Si la réponse n'est pas entièrement dans le contexte, l'étiquette est (0, 0).
- if offset[context_start][0] > start_char or offset[context_end][1] < end_char:
- start_positions.append(0)
- end_positions.append(0)
- else:
- # Otherwise it's the start and end token positions
- idx = context_start
- while idx <= context_end and offset[idx][0] <= start_char:
- idx += 1
- start_positions.append(idx - 1)
-
- idx = context_end
- while idx >= context_start and offset[idx][1] >= end_char:
- idx -= 1
- end_positions.append(idx + 1)
-
- inputs["start_positions"] = start_positions
- inputs["end_positions"] = end_positions
- return inputs
-```
-
-Notez que nous avons défini deux constantes pour déterminer la longueur maximale utilisée ainsi que la longueur de la fenêtre glissante, et que nous avons ajouté un petit nettoyage avant la tokénisation : certaines des questions dans le jeu de données SQuAD ont des espaces supplémentaires au début et à la fin qui n'ajoutent rien (et prennent de la place lors de la tokénisation si vous utilisez un modèle comme RoBERTa), donc nous avons supprimé ces espaces supplémentaires.
-
-Pour appliquer cette fonction à l'ensemble de l'entraînement, nous utilisons la méthode `Dataset.map()` avec le flag `batched=True`. C'est nécessaire ici car nous changeons la longueur de l'ensemble de données (puisqu'un exemple peut donner plusieurs caractéristiques d'entraînement) :
-
-```py
-train_dataset = raw_datasets["train"].map(
- preprocess_training_examples,
- batched=True,
- remove_columns=raw_datasets["train"].column_names,
-)
-len(raw_datasets["train"]), len(train_dataset)
-```
-
-```python out
-(87599, 88729)
-```
-
-Comme nous pouvons le voir, le prétraitement a ajouté environ 1 000 caractéristiques. Notre ensemble d'entraînement est maintenant prêt à être utilisé - passons au prétraitement de l'ensemble de validation !
-
-### Traitement des données de validation
-
-Le prétraitement des données de validation sera légèrement plus facile car nous n'avons pas besoin de générer des étiquettes (sauf si nous voulons calculer une perte de validation, mais ce nombre ne nous aidera pas vraiment à comprendre la qualité du modèle). La vraie joie sera d'interpréter les prédictions du modèle dans des étendues du contexte original. Pour cela, il nous suffit de stocker les mappages de décalage et un moyen de faire correspondre chaque caractéristique créée à l'exemple original dont elle provient. Puisqu'il y a une colonne ID dans l'ensemble de données original, nous utiliserons cet ID.
-
-La seule chose que nous allons ajouter ici est un petit nettoyage des mappages de décalage. Ils contiendront les offsets pour la question et le contexte, mais une fois que nous serons dans la phase de post-traitement, nous n'aurons aucun moyen de savoir quelle partie des IDs d'entrée correspondait au contexte et quelle partie était la question (la méthode `sequence_ids()` que nous avons utilisée n'est disponible que pour la sortie du *tokenizer*). Donc, nous allons mettre les offsets correspondant à la question à `None` :
-
-```py
-def preprocess_validation_examples(examples):
- questions = [q.strip() for q in examples["question"]]
- inputs = tokenizer(
- questions,
- examples["context"],
- max_length=max_length,
- truncation="only_second",
- stride=stride,
- return_overflowing_tokens=True,
- return_offsets_mapping=True,
- padding="max_length",
- )
-
- sample_map = inputs.pop("overflow_to_sample_mapping")
- example_ids = []
-
- for i in range(len(inputs["input_ids"])):
- sample_idx = sample_map[i]
- example_ids.append(examples["id"][sample_idx])
-
- sequence_ids = inputs.sequence_ids(i)
- offset = inputs["offset_mapping"][i]
- inputs["offset_mapping"][i] = [
- o if sequence_ids[k] == 1 else None for k, o in enumerate(offset)
- ]
-
- inputs["example_id"] = example_ids
- return inputs
-```
-
-Nous pouvons appliquer cette fonction sur l'ensemble des données de validation comme précédemment :
-
-```py
-validation_dataset = raw_datasets["validation"].map(
- preprocess_validation_examples,
- batched=True,
- remove_columns=raw_datasets["validation"].column_names,
-)
-len(raw_datasets["validation"]), len(validation_dataset)
-```
-
-```python out
-(10570, 10822)
-```
-
-Dans ce cas, nous n'avons ajouté que quelques centaines d'échantillons, il semble donc que les contextes dans l'ensemble de données de validation soient un peu plus courts.
-
-Maintenant que nous avons prétraité toutes les données, nous pouvons passer à l'entraînement.
-
-{#if fw === 'pt'}
-
-## *Finetuner* le modèle avec l'API `Trainer`
-
-Le code d'entraînement pour cet exemple ressemblera beaucoup au code des sections précédentes -- la chose la plus difficile sera d'écrire la fonction `compute_metrics()`. Puisque nous avons capitonné tous les échantillons à la longueur maximale que nous avons fixée, il n'y a pas de collateur de données à définir, donc ce calcul de métrique est vraiment la seule chose dont nous devons nous soucier. La partie la plus difficile sera de post-traiter les prédictions du modèle en travées de texte dans les exemples originaux ; une fois que nous aurons fait cela, la métrique de la bibliothèque 🤗 *Datasets* fera le gros du travail pour nous.
-
-{:else}
-
-## *Finetuner* fin du modèle avec Keras
-
-Le code d'entraînement de cet exemple ressemblera beaucoup au code des sections précédentes, mais le calcul des métriques sera un défi unique. Puisque nous avons capitonné tous les échantillons à la longueur maximale que nous avons définie, il n'y a pas de collateur de données à définir, donc le calcul de la métrique est vraiment la seule chose dont nous devons nous soucier. La partie la plus difficile sera de post-traiter les prédictions du modèle en travées de texte dans les exemples originaux ; une fois que nous aurons fait cela, la métrique de la bibliothèque 🤗 *Datasets* fera le gros du travail pour nous.
-
-{/if}
-
-### Post-traitement
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Le modèle produira des logits pour les positions de début et de fin de la réponse dans les IDs d'entrée, comme nous l'avons vu lors de notre exploration du [`question-answering` pipeline](/course/chapter6/4). L'étape de post-traitement sera similaire à ce que nous avons fait là-bas, donc voici un rappel rapide des actions que nous avons prises :
-
-- nous avons masqué les logits de début et de fin correspondant aux *tokens* en dehors du contexte,
-- nous avons ensuite converti les logits de début et de fin en probabilités en utilisant un softmax,
-- nous avons attribué un score à chaque paire `(start_token, end_token)` en prenant le produit des deux probabilités correspondantes,
-- nous avons cherché la paire avec le score maximum qui donnait une réponse valide (par exemple, un `start_token` inférieur au `end_token`).
-
-Ici, nous allons modifier légèrement ce processus car nous n'avons pas besoin de calculer les scores réels (juste la réponse prédite). Cela signifie que nous pouvons sauter l'étape du softmax. Pour aller plus vite, nous ne noterons pas non plus toutes les paires `(start_token, end_token)` possibles, mais seulement celles correspondant aux logits `n_best` les plus élevés (avec `n_best=20`). Puisque nous sauterons le softmax, ces scores seront des scores logit, et seront obtenus en prenant la somme des logits de début et de fin (au lieu du produit, à cause de la règle \\(\log(ab) = \log(a) + \log(b)\)).
-
-Pour démontrer tout cela, nous aurons besoin d'un certain type de prédictions. Puisque nous n'avons pas encore entraîné notre modèle, nous allons utiliser le modèle par défaut du pipeline d'assurance qualité pour générer quelques prédictions sur une petite partie de l'ensemble de validation. Nous pouvons utiliser la même fonction de traitement que précédemment ; parce qu'elle repose sur la constante globale `tokenizer`, nous devons juste changer cet objet pour le tokenizer du modèle que nous voulons utiliser temporairement :
-
-```python
-small_eval_set = raw_datasets["validation"].select(range(100))
-trained_checkpoint = "distilbert-base-cased-distilled-squad"
-
-tokenizer = AutoTokenizer.from_pretrained(trained_checkpoint)
-eval_set = small_eval_set.map(
- preprocess_validation_examples,
- batched=True,
- remove_columns=raw_datasets["validation"].column_names,
-)
-```
-
-Maintenant que le prétraitement est terminé, nous changeons le *tokenizer* pour celui que nous avons choisi à l'origine :
-
-```python
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-```
-
-Nous supprimons ensuite les colonnes de notre `eval_set` qui ne sont pas attendues par le modèle, nous construisons un lot avec l'ensemble de ce petit ensemble de validation, et nous le passons au modèle. Si un GPU est disponible, nous l'utilisons pour aller plus vite :
-
-{#if fw === 'pt'}
-
-```python
-import torch
-from transformers import AutoModelForQuestionAnswering
-
-eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"])
-eval_set_for_model.set_format("torch")
-
-device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-batch = {k: eval_set_for_model[k].to(device) for k in eval_set_for_model.column_names}
-trained_model = AutoModelForQuestionAnswering.from_pretrained(trained_checkpoint).to(
- device
-)
-
-with torch.no_grad():
- outputs = trained_model(**batch)
-```
-
-Puisque le `Trainer` nous donnera les prédictions sous forme de tableaux NumPy, nous récupérons les logits de début et de fin et les convertissons dans ce format :
-
-```python
-start_logits = outputs.start_logits.cpu().numpy()
-end_logits = outputs.end_logits.cpu().numpy()
-```
-
-{:else}
-
-```python
-import tensorflow as tf
-from transformers import TFAutoModelForQuestionAnswering
-
-eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"])
-eval_set_for_model.set_format("numpy")
-
-batch = {k: eval_set_for_model[k] for k in eval_set_for_model.column_names}
-trained_model = TFAutoModelForQuestionAnswering.from_pretrained(trained_checkpoint)
-
-outputs = trained_model(**batch)
-```
-
-Pour faciliter l'expérimentation, nous allons convertir ces sorties en tableaux NumPy :
-
-```python
-start_logits = outputs.start_logits.numpy()
-end_logits = outputs.end_logits.numpy()
-```
-
-{/if}
-
-Maintenant, nous devons trouver la réponse prédite pour chaque exemple dans notre `small_eval_set`. Un exemple peut avoir été divisé en plusieurs caractéristiques dans `eval_set`, donc la première étape est de faire correspondre chaque exemple dans `small_eval_set` aux caractéristiques correspondantes dans `eval_set` :
-
-```python
-import collections
-
-example_to_features = collections.defaultdict(list)
-for idx, feature in enumerate(eval_set):
- example_to_features[feature["example_id"]].append(idx)
-```
-
-Avec cela en main, nous pouvons vraiment nous mettre au travail en parcourant en boucle tous les exemples et, pour chaque exemple, toutes les caractéristiques associées. Comme nous l'avons dit précédemment, nous allons regarder les scores logit pour les `n_meilleurs` logits de début et logits de fin, en excluant les positions qui donnent :
-
-- une réponse qui ne serait pas dans le contexte.
-- une réponse avec une longueur négative
-- une réponse qui est trop longue (nous limitons les possibilités à `max_answer_length=30`)
-
-Une fois que nous avons toutes les réponses possibles notées pour un exemple, nous choisissons simplement celle qui a le meilleur score logit :
-
-```python
-import numpy as np
-
-n_best = 20
-max_answer_length = 30
-predicted_answers = []
-
-for example in small_eval_set:
- example_id = example["id"]
- context = example["context"]
- answers = []
-
- for feature_index in example_to_features[example_id]:
- start_logit = start_logits[feature_index]
- end_logit = end_logits[feature_index]
- offsets = eval_set["offset_mapping"][feature_index]
-
- start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist()
- end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist()
- for start_index in start_indexes:
- for end_index in end_indexes:
- # Ignore les réponses qui ne sont pas entièrement dans le contexte
- if offsets[start_index] is None or offsets[end_index] is None:
- continue
- # Ignorer les réponses dont la longueur est soit < 0 soit > max_answer_length.
- if (
- end_index < start_index
- or end_index - start_index + 1 > max_answer_length
- ):
- continue
-
- answers.append(
- {
- "text": context[offsets[start_index][0] : offsets[end_index][1]],
- "logit_score": start_logit[start_index] + end_logit[end_index],
- }
- )
-
- best_answer = max(answers, key=lambda x: x["logit_score"])
- predicted_answers.append({"id": example_id, "prediction_text": best_answer["text"]})
-```
-
-Le format final des réponses prédites est celui qui sera attendu par la métrique que nous allons utiliser. Comme d'habitude, nous pouvons le charger à l'aide de la bibliothèque 🤗 *Datasets* :
-
-```python
-from datasets import load_metric
-
-metric = load_metric("squad")
-```
-
-Cette métrique attend les réponses prédites dans le format que nous avons vu ci-dessus (une liste de dictionnaires avec une clé pour l'ID de l'exemple et une clé pour le texte prédit) et les réponses théoriques dans le format ci-dessous (une liste de dictionnaires avec une clé pour l'ID de l'exemple et une clé pour les réponses possibles) :
-
-```python
-theoretical_answers = [
- {"id": ex["id"], "answers": ex["answers"]} for ex in small_eval_set
-]
-```
-
-Nous pouvons maintenant vérifier que nous obtenons des résultats raisonnables en examinant le premier élément des deux listes :
-
-```python
-print(predicted_answers[0])
-print(theoretical_answers[0])
-```
-
-```python out
-{'id': '56be4db0acb8001400a502ec', 'prediction_text': 'Denver Broncos'}
-{'id': '56be4db0acb8001400a502ec', 'answers': {'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]}}
-```
-
-Pas trop mal ! Voyons maintenant le score que la métrique nous donne :
-
-```python
-metric.compute(predictions=predicted_answers, references=theoretical_answers)
-```
-
-```python out
-{'exact_match': 83.0, 'f1': 88.25}
-```
-
-Encore une fois, c'est plutôt bon si l'on considère que, selon [son article](https://arxiv.org/abs/1910.01108v2), DistilBERT *finetuné* sur SQuAD obtient 79,1 et 86,9 pour ces scores sur l'ensemble des données.
-
-{#if fw === 'pt'}
-
-Maintenant, mettons tout ce que nous venons de faire dans une fonction `compute_metrics()` que nous utiliserons dans le `Trainer`. Normalement, cette fonction `compute_metrics()` reçoit seulement un tuple `eval_preds` avec les logits et les labels. Ici, nous aurons besoin d'un peu plus, car nous devons chercher dans le jeu de données des caractéristiques pour le décalage et dans le jeu de données des exemples pour les contextes originaux, donc nous ne serons pas en mesure d'utiliser cette fonction pour obtenir des résultats d'évaluation réguliers pendant l'entraînement. Nous ne l'utiliserons qu'à la fin de l'entraînement pour vérifier les résultats.
-
-La fonction `compute_metrics()` regroupe les mêmes étapes que précédemment ; nous ajoutons juste une petite vérification au cas où nous ne trouverions aucune réponse valide (dans ce cas nous prédisons une chaîne vide).
-
-{:else}
-
-Maintenant, mettons tout ce que nous venons de faire dans une fonction `compute_metrics()` que nous utiliserons après avoir entraîné notre modèle. Nous aurons besoin de passer un peu plus que juste les logits de sortie, car nous devons chercher dans le jeu de données des caractéristiques pour le décalage et dans le jeu de données des exemples pour les contextes originaux :
-
-{/if}
-
-```python
-from tqdm.auto import tqdm
-
-
-def compute_metrics(start_logits, end_logits, features, examples):
- example_to_features = collections.defaultdict(list)
- for idx, feature in enumerate(features):
- example_to_features[feature["example_id"]].append(idx)
-
- predicted_answers = []
- for example in tqdm(examples):
- example_id = example["id"]
- context = example["context"]
- answers = []
-
- # Parcourir en boucle toutes les fonctionnalités associées à cet exemple
- for feature_index in example_to_features[example_id]:
- start_logit = start_logits[feature_index]
- end_logit = end_logits[feature_index]
- offsets = features[feature_index]["offset_mapping"]
-
- start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist()
- end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist()
- for start_index in start_indexes:
- for end_index in end_indexes:
- # Ignore les réponses qui ne sont pas entièrement dans le contexte
- if offsets[start_index] is None or offsets[end_index] is None:
- continue
- # Ignore les réponses dont la longueur est soit < 0, soit > max_answer_length.
- if (
- end_index < start_index
- or end_index - start_index + 1 > max_answer_length
- ):
- continue
-
- answer = {
- "text": context[offsets[start_index][0] : offsets[end_index][1]],
- "logit_score": start_logit[start_index] + end_logit[end_index],
- }
- answers.append(answer)
-
- # Sélectionne la réponse avec le meilleur score
- if len(answers) > 0:
- best_answer = max(answers, key=lambda x: x["logit_score"])
- predicted_answers.append(
- {"id": example_id, "prediction_text": best_answer["text"]}
- )
- else:
- predicted_answers.append({"id": example_id, "prediction_text": ""})
-
- theoretical_answers = [{"id": ex["id"], "answers": ex["answers"]} for ex in examples]
- return metric.compute(predictions=predicted_answers, references=theoretical_answers)
-```
-
-Nous pouvons vérifier que cela fonctionne sur nos prédictions :
-
-```python
-compute_metrics(start_logits, end_logits, eval_set, small_eval_set)
-```
-
-```python out
-{'exact_match': 83.0, 'f1': 88.25}
-```
-
-C'est bien ! Maintenant, utilisons ceci pour affiner notre modèle.
-
-### *Finetuning* du modèle
-
-{#if fw === 'pt'}
-
-Nous sommes maintenant prêts à entraîner notre modèle. Créons-le d'abord, en utilisant la classe `AutoModelForQuestionAnswering` comme précédemment :
-
-```python
-model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
-```
-
-{:else}
-
-Nous sommes maintenant prêts à entraîner notre modèle. Créons-le d'abord, en utilisant la classe `TFAutoModelForQuestionAnswering` comme précédemment :
-
-```python
-model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
-```
-
-{/if}
-
-Comme d'habitude, nous recevons un avertissement indiquant que certains poids ne sont pas utilisés (ceux de la tête de pré-entraînement) et que d'autres sont initialisés de manière aléatoire (ceux de la tête de réponse aux questions). Vous devriez être habitué à cela maintenant, mais cela signifie que ce modèle n'est pas encore prêt à être utilisé et qu'il a besoin d'être *finetuné*. Une bonne chose que nous soyons sur le point de le faire !
-
-Pour pouvoir pousser notre modèle vers le *Hub*, nous devons nous connecter à Hugging Face. Si vous exécutez ce code dans un *notebook*, vous pouvez le faire avec la fonction utilitaire suivante, qui affiche un widget où vous pouvez entrer vos identifiants de connexion :
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-Si vous ne travaillez pas dans un *notebook*, tapez simplement la ligne suivante dans votre terminal :
-
-```bash
-huggingface-cli login
-```
-
-{#if fw === 'pt'}
-
-Une fois ceci fait, nous pouvons définir nos `TrainingArguments`. Comme nous l'avons dit lorsque nous avons défini notre fonction pour calculer la métrique, nous ne serons pas en mesure d'avoir une boucle d'évaluation régulière à cause de la signature de la fonction `compute_metrics()`. Nous pourrions écrire notre propre sous-classe de `Trainer` pour faire cela (une approche que vous pouvez trouver dans le [script d'exemple de réponse aux questions](https://github.com/huggingface/transformers/blob/master/examples/pytorch/question-answering/trainer_qa.py)), mais c'est un peu trop long pour cette section. A la place, nous n'évaluerons le modèle qu'à la fin de l'entraînement et nous vous montrerons comment faire une évaluation régulière dans "Une boucle d'entraînement personnalisée" ci-dessous.
-
-C'est vraiment là que l'API `Trainer` montre ses limites et que la bibliothèque 🤗 *Accelerate* brille : personnaliser la classe pour un cas d'utilisation spécifique peut être pénible, mais modifier une boucle d'entraînement entièrement exposée est facile.
-
-Jetons un coup d'œil à notre `TrainingArguments` :
-
-```python
-from transformers import TrainingArguments
-
-args = TrainingArguments(
- "bert-finetuned-squad",
- evaluation_strategy="no",
- save_strategy="epoch",
- learning_rate=2e-5,
- num_train_epochs=3,
- weight_decay=0.01,
- fp16=True,
- push_to_hub=True,
-)
-```
-
-Nous avons déjà vu la plupart d'entre eux : nous définissons quelques hyperparamètres (comme le taux d'apprentissage, le nombre d'époques pour lesquelles nous nous entraînons, et une certaine décroissance de poids) et nous indiquons que nous voulons sauvegarder le modèle à la fin de chaque époque, sauter l'évaluation, et télécharger nos résultats vers le Model Hub. Nous activons également l'entraînement en précision mixte avec `fp16=True`, car cela peut accélérer l'entraînement sur un GPU récent.
-
-{:else}
-
-Maintenant que c'est fait, nous pouvons créer nos ensembles de données TF. Nous pouvons utiliser le simple collateur de données par défaut cette fois-ci :
-
-```python
-from transformers import DefaultDataCollator
-
-data_collator = DefaultDataCollator(return_tensors="tf")
-```
-
-Et maintenant nous créons les jeux de données comme d'habitude.
-
-```python
-tf_train_dataset = train_dataset.to_tf_dataset(
- columns=[
- "input_ids",
- "start_positions",
- "end_positions",
- "attention_mask",
- "token_type_ids",
- ],
- collate_fn=data_collator,
- shuffle=True,
- batch_size=16,
-)
-tf_eval_dataset = validation_dataset.to_tf_dataset(
- columns=["input_ids", "attention_mask", "token_type_ids"],
- collate_fn=data_collator,
- shuffle=False,
- batch_size=16,
-)
-```
-
-Ensuite, nous configurons nos hyperparamètres d'entraînement et compilons notre modèle :
-
-```python
-from transformers import create_optimizer
-from transformers.keras_callbacks import PushToHubCallback
-import tensorflow as tf
-
-# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans le jeu de données, divisé par la taille du batch, puis multiplié par le nombre total d'époques.
-# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un lot tf.data.Dataset,
-# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size.
-num_train_epochs = 3
-num_train_steps = len(tf_train_dataset) * num_train_epochs
-optimizer, schedule = create_optimizer(
- init_lr=2e-5,
- num_warmup_steps=0,
- num_train_steps=num_train_steps,
- weight_decay_rate=0.01,
-)
-model.compile(optimizer=optimizer)
-
-# Entraîner en mixed-precision float16
-tf.keras.mixed_precision.set_global_policy("mixed_float16")
-```
-
-Enfin, nous sommes prêts à nous entraîner avec `model.fit()`. Nous utilisons un `PushToHubCallback` pour télécharger le modèle sur le *Hub* après chaque époque.
-
-{/if}
-
-Par défaut, le dépôt utilisé sera dans votre espace de noms et nommé après le répertoire de sortie que vous avez défini, donc dans notre cas il sera dans `"sgugger/bert-finetuned-squad"`. Nous pouvons passer outre en passant un `hub_model_id` ; par exemple, pour pousser le modèle dans l'organisation `huggingface_course` nous avons utilisé `hub_model_id= "huggingface_course/bert-finetuned-squad"` (qui est le modèle que nous avons lié au début de cette section).
-
-{#if fw === 'pt'}
-
-
-
-💡 Si le répertoire de sortie que vous utilisez existe, il doit être un clone local du dépôt vers lequel vous voulez pousser (donc définissez un nouveau nom si vous obtenez une erreur lors de la définition de votre `Trainer`).
-
-
-
-Enfin, nous passons tout à la classe `Trainer` et lançons l'entraînement :
-
-```python
-from transformers import Trainer
-
-trainer = Trainer(
- model=model,
- args=args,
- train_dataset=train_dataset,
- eval_dataset=validation_dataset,
- tokenizer=tokenizer,
-)
-trainer.train()
-```
-
-{:else}
-
-```python
-from transformers.keras_callbacks import PushToHubCallback
-
-callback = PushToHubCallback(output_dir="bert-finetuned-squad", tokenizer=tokenizer)
-
-# Nous allons faire la validation après, donc pas de validation au milieu de l'entraînement.
-model.fit(tf_train_dataset, callbacks=[callback], epochs=num_train_epochs)
-```
-
-{/if}
-
-Notez que pendant l'entraînement, chaque fois que le modèle est sauvegardé (ici, à chaque époque), il est téléchargé sur le Hub en arrière-plan. Ainsi, vous pourrez reprendre votre entraînement sur une autre machine si nécessaire. L'ensemble de l'entraînement prend un certain temps (un peu plus d'une heure sur une Titan RTX), vous pouvez donc prendre un café ou relire les parties du cours qui vous ont semblé plus difficiles pendant qu'il se déroule. Notez également que dès que la première époque est terminée, vous verrez des poids téléchargés sur le Hub et vous pourrez commencer à jouer avec votre modèle sur sa page.
-
-{#if fw === 'pt'}
-
-Une fois l'entraînement terminé, nous pouvons enfin évaluer notre modèle (et prier pour ne pas avoir dépensé tout ce temps de calcul pour rien). La méthode `predict()` du `Trainer` retournera un tuple où les premiers éléments seront les prédictions du modèle (ici une paire avec les logits de début et de fin). Nous envoyons ceci à notre fonction `compute_metrics()` :
-
-```python
-predictions, _ = trainer.predict(validation_dataset)
-start_logits, end_logits = predictions
-compute_metrics(start_logits, end_logits, validation_dataset, raw_datasets["validation"])
-```
-
-{:else}
-
-Une fois l'entraînement terminé, nous pouvons enfin évaluer notre modèle (et prier pour ne pas avoir dépensé tout ce temps de calcul pour rien). La méthode `predict()` de notre `model` se chargera d'obtenir les prédictions, et puisque nous avons fait tout le travail difficile de définir une fonction `compute_metrics()` plus tôt, nous pouvons obtenir nos résultats en une seule ligne :
-
-```python
-predictions = model.predict(tf_eval_dataset)
-compute_metrics(
- predictions["start_logits"],
- predictions["end_logits"],
- validation_dataset,
- raw_datasets["validation"],
-)
-```
-
-{/if}
-
-```python out
-{'exact_match': 81.18259224219489, 'f1': 88.67381321905516}
-```
-
-Super ! À titre de comparaison, les scores de base indiqués dans l'article du BERT pour ce modèle sont de 80,8 et 88,5, donc nous sommes exactement là où nous devrions être.
-
-{#if fw === 'pt'}
-
-Enfin, nous utilisons la méthode `push_to_hub()` pour nous assurer que nous téléchargeons la dernière version du modèle :
-
-```py
-trainer.push_to_hub(commit_message="Training complete")
-```
-
-Cela renvoie l'URL du commit qu'il vient de faire, si vous voulez l'inspecter :
-
-```python out
-'https://huggingface.co/sgugger/bert-finetuned-squad/commit/9dcee1fbc25946a6ed4bb32efb1bd71d5fa90b68'
-```
-
-Le `Trainer` rédige également une fiche modèle avec tous les résultats de l'évaluation et la télécharge.
-
-{/if}
-
-À ce stade, vous pouvez utiliser le widget d'inférence sur le *Hub* du modèle pour tester le modèle et le partager avec vos amis, votre famille et vos animaux préférés. Vous avez réussi à *finetuner* un modèle sur une tâche de réponse à une question - félicitations !
-
-
-
-✏️ **Votre tour** Essayez un autre modèle d'architecture pour voir s'il est plus performant dans cette tâche !
-
-
-
-{#if fw === 'pt'}
-
-Si vous voulez plonger un peu plus profondément dans la boucle d'entraînement, nous allons maintenant vous montrer comment faire la même chose en utilisant 🤗 *Accelerate*.
-
-## Une boucle d'entraînement personnalisée
-
-Jetons maintenant un coup d'œil à la boucle d'entraînement complète, afin que vous puissiez facilement personnaliser les parties dont vous avez besoin. Elle ressemblera beaucoup à la boucle d'entraînement du [Chapitre 3](/course/fr/chapter3/4), à l'exception de la boucle d'évaluation. Nous serons en mesure d'évaluer le modèle régulièrement puisque nous ne sommes plus contraints par la classe `Trainer`.
-
-### Préparer tout pour l'entraînement
-
-Tout d'abord, nous devons construire le `DataLoader`s à partir de nos jeux de données. Nous définissons le format de ces jeux de données à `"torch"`, et supprimons les colonnes dans le jeu de validation qui ne sont pas utilisées par le modèle. Ensuite, nous pouvons utiliser le `default_data_collator` fourni par Transformers comme `collate_fn` et mélanger l'ensemble d'entraînement, mais pas l'ensemble de validation :
-
-```py
-from torch.utils.data import DataLoader
-from transformers import default_data_collator
-
-train_dataset.set_format("torch")
-validation_set = validation_dataset.remove_columns(["example_id", "offset_mapping"])
-validation_set.set_format("torch")
-
-train_dataloader = DataLoader(
- train_dataset,
- shuffle=True,
- collate_fn=default_data_collator,
- batch_size=8,
-)
-eval_dataloader = DataLoader(
- validation_set, collate_fn=default_data_collator, batch_size=8
-)
-```
-
-Ensuite, nous réinstantifions notre modèle, afin de nous assurer que nous ne poursuivons pas les réglages fins précédents mais que nous repartons du modèle pré-entraîné de BERT :
-
-```py
-model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
-```
-
-Ensuite, nous aurons besoin d'un optimiseur. Comme d'habitude, nous utilisons le classique `AdamW`, qui est comme Adam, mais avec une correction dans la façon dont la décroissance du poids est appliquée :
-
-```py
-from torch.optim import AdamW
-
-optimizer = AdamW(model.parameters(), lr=2e-5)
-```
-
-Une fois que nous avons tous ces objets, nous pouvons les envoyer à la méthode `accelerator.prepare()`. Rappelez-vous que si vous voulez vous entraîner sur des TPUs dans un *notebook* de Colab, vous devrez déplacer tout ce code dans une fonction d'entraînement, et qui ne devrait pas exécuter une cellule qui instancie un `Accelerator`. Nous pouvons forcer l'entraînement en précision mixte en passant `fp16=True` à l'`Accelerator` (ou, si vous exécutez le code comme un script, assurez-vous de remplir la 🤗 *Accelerate* `config` de manière appropriée).
-
-```py
-from accelerate import Accelerator
-
-accelerator = Accelerator(fp16=True)
-model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
- model, optimizer, train_dataloader, eval_dataloader
-)
-```
-
-Comme vous devez le savoir depuis les sections précédentes, nous ne pouvons utiliser la longueur de `train_dataloader` pour calculer le nombre d'étapes d'entraînement qu'après qu'il soit passé par la méthode `accelerator.prepare()`. Nous utilisons le même programme linéaire que dans les sections précédentes :
-
-```py
-from transformers import get_scheduler
-
-num_train_epochs = 3
-num_update_steps_per_epoch = len(train_dataloader)
-num_training_steps = num_train_epochs * num_update_steps_per_epoch
-
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-```
-
-Pour pousser notre modèle vers le Hub, nous aurons besoin de créer un objet `Repository` dans un dossier de travail. Tout d'abord, connectez-vous au Hugging Face Hub, si vous n'êtes pas déjà connecté. Nous déterminerons le nom du dépôt à partir de l'ID du modèle que nous voulons donner à notre modèle (n'hésitez pas à remplacer le `repo_name` par votre propre choix ; il doit juste contenir votre nom d'utilisateur, ce que fait la fonction `get_full_repo_name()`) :
-
-```py
-from huggingface_hub import Repository, get_full_repo_name
-
-model_name = "bert-finetuned-squad-accelerate"
-repo_name = get_full_repo_name(model_name)
-repo_name
-```
-
-```python out
-'sgugger/bert-finetuned-squad-accelerate'
-```
-
-Ensuite, nous pouvons cloner ce référentiel dans un dossier local. S'il existe déjà, ce dossier local doit être un clone du référentiel avec lequel nous travaillons :
-
-```py
-output_dir = "bert-finetuned-squad-accelerate"
-repo = Repository(output_dir, clone_from=repo_name)
-```
-
-Nous pouvons maintenant télécharger tout ce que nous sauvegardons dans `output_dir` en appelant la méthode `repo.push_to_hub()`. Cela nous aidera à télécharger les modèles intermédiaires à la fin de chaque époque.
-
-## Boucle d'entraînement
-
-Nous sommes maintenant prêts à écrire la boucle d'entraînement complète. Après avoir défini une barre de progression pour suivre l'évolution de l'entraînement, la boucle comporte trois parties :
-
-- l'entraînement proprement dit, qui est l'itération classique sur le `train_dataloader`, passage en avant du modèle, puis passage en arrière et étape d'optimisation.
-- l'évaluation, dans laquelle nous rassemblons toutes les valeurs pour `start_logits` et `end_logits` avant de les convertir en tableaux NumPy. Une fois la boucle d'évaluation terminée, nous concaténons tous les résultats. Notez que nous devons tronquer parce que l' `Accelerator` peut avoir ajouté quelques échantillons à la fin pour s'assurer que nous avons le même nombre d'exemples dans chaque processus.
-- sauvegarde et téléchargement, où nous sauvegardons d'abord le modèle et le *tokenizer*, puis appelons `repo.push_to_hub()`. Comme nous l'avons fait auparavant, nous utilisons l'argument `blocking=False` pour dire à la bibliothèque 🤗 *Hub* de pousser dans un processus asynchrone. De cette façon, l'entraînement continue normalement et cette (longue) instruction est exécutée en arrière-plan.
-
-Voici le code complet de la boucle d'entraînement :
-
-```py
-from tqdm.auto import tqdm
-import torch
-
-progress_bar = tqdm(range(num_training_steps))
-
-for epoch in range(num_train_epochs):
- # Entraînement
- model.train()
- for step, batch in enumerate(train_dataloader):
- outputs = model(**batch)
- loss = outputs.loss
- accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-
- # Evaluation
- model.eval()
- start_logits = []
- end_logits = []
- accelerator.print("Evaluation!")
- for batch in tqdm(eval_dataloader):
- with torch.no_grad():
- outputs = model(**batch)
-
- start_logits.append(accelerator.gather(outputs.start_logits).cpu().numpy())
- end_logits.append(accelerator.gather(outputs.end_logits).cpu().numpy())
-
- start_logits = np.concatenate(start_logits)
- end_logits = np.concatenate(end_logits)
- start_logits = start_logits[: len(validation_dataset)]
- end_logits = end_logits[: len(validation_dataset)]
-
- metrics = compute_metrics(
- start_logits, end_logits, validation_dataset, raw_datasets["validation"]
- )
- print(f"epoch {epoch}:", metrics)
-
- # Sauvegarder et télécharger
- accelerator.wait_for_everyone()
- unwrapped_model = accelerator.unwrap_model(model)
- unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
- if accelerator.is_main_process:
- tokenizer.save_pretrained(output_dir)
- repo.push_to_hub(
- commit_message=f"Training in progress epoch {epoch}", blocking=False
- )
-```
-
-Au cas où ce serait la première fois que vous verriez un modèle enregistré avec 🤗 *Accelerate*, prenons un moment pour inspecter les trois lignes de code qui l'accompagnent :
-
-```py
-accelerator.wait_for_everyone()
-unwrapped_model = accelerator.unwrap_model(model)
-unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
-```
-
-La première ligne est explicite : elle indique à tous les processus d'attendre que tout le monde soit à ce stade avant de continuer. C'est pour s'assurer que nous avons le même modèle dans chaque processus avant de sauvegarder. Ensuite, nous prenons le `unwrapped_model`, qui est le modèle de base que nous avons défini. La méthode `accelerator.prepare()` modifie le modèle pour qu'il fonctionne dans l'entraînement distribué, donc il n'aura plus la méthode `save_pretrained()` ; la méthode `accelerator.unwrap_model()` annule cette étape. Enfin, nous appelons `save_pretrained()` mais nous disons à cette méthode d'utiliser `accelerator.save()` au lieu de `torch.save()`.
-
-Une fois ceci fait, vous devriez avoir un modèle qui produit des résultats assez similaires à celui entraîné avec le `Trainer`. Vous pouvez vérifier le modèle que nous avons entraîné en utilisant ce code à [*huggingface-course/bert-finetuned-squad-accelerate*](https://huggingface.co/huggingface-course/bert-finetuned-squad-accelerate). Et si vous voulez tester des modifications de la boucle d'entraînement, vous pouvez les implémenter directement en modifiant le code ci-dessus !
-
-{/if}
-
-### Utilisation du modèle *finetuné*
-
-Nous vous avons déjà montré comment vous pouvez utiliser le modèle que nous avons *finetuné* sur le *Hub* avec le widget d'inférence. Pour l'utiliser localement dans un `pipeline`, il suffit de spécifier l'identifiant du modèle :
-
-```py
-from transformers import pipeline
-
-# Replace this with your own checkpoint
-model_checkpoint = "huggingface-course/bert-finetuned-squad"
-question_answerer = pipeline("question-answering", model=model_checkpoint)
-
-context = """
-🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration
-between them. It's straightforward to train your models with one before loading them for inference with the other.
-"""
-question = "Which deep learning libraries back 🤗 Transformers?"
-question_answerer(question=question, context=context)
-```
-
-```python out
-{'score': 0.9979003071784973,
- 'start': 78,
- 'end': 105,
- 'answer': 'Jax, PyTorch and TensorFlow'}
-```
-
-Super ! Notre modèle fonctionne aussi bien que le modèle par défaut pour ce pipeline !
+
+
+# Réponse aux questions
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Il est temps de s'intéresser à la réponse aux questions ! Cette tâche peut prendre plusieurs formes, mais celle sur laquelle nous allons nous concentrer dans cette section est appelée réponse aux questions *extractives*. Il s'agit de poser des questions sur un document et d'identifier les réponses sous forme de "morceaux de texte" dans le document lui-même.
+
+
+
+Nous allons affiner un modèle BERT sur le [jeu de données SQuAD](https://rajpurkar.github.io/SQuAD-explorer/), qui consiste en des questions posées par des *crowdworkers* sur un ensemble d'articles de Wikipedia. Cela nous donnera un modèle capable de calculer des prédictions comme celle-ci :
+
+
+
+
+Il s'agit en fait de la présentation du modèle qui a été entraîné et téléchargé sur le *Hub* à l'aide du code présenté dans cette section. Vous pouvez le trouver et vérifier les prédictions [ici](https://huggingface.co/huggingface-course/bert-finetuned-squad?context=%F0%9F%A4%97+Transformers+is+backed+by+the+three+most+popular+deep+learning+libraries+%E2%80%94+Jax%2C+PyTorch+and+TensorFlow+%E2%80%94+with+a+seamless+integration+between+them.+It%27s+straightforward+to+train+your+models+with+one+before+loading+them+for+inference+with+the+other.&question=Which+deep+learning+libraries+back+%F0%9F%A4%97+Transformers%3F)
+
+
+
+💡 Les modèles à codeur unique comme BERT ont tendance à être excellents pour extraire les réponses à des questions factuelles comme "Qui a inventé l'architecture Transformer ?", mais ne sont pas très performants lorsqu'on leur pose des questions ouvertes comme "Pourquoi le ciel est-il bleu ?". Dans ces cas plus difficiles, les modèles encodeurs-décodeurs comme T5 et BART sont généralement utilisés pour synthétiser les informations d'une manière assez similaire au [résumé de texte](/cours/fr/chapter7/5). Si vous êtes intéressé par ce type de réponse aux questions *génératives*, nous vous recommandons de consulter notre [démo](https://yjernite.github.io/lfqa.html) basée sur le [jeu de données ELI5](https://huggingface.co/datasets/eli5).
+
+
+
+## Préparation des données
+
+Le jeu de données le plus utilisé comme référence académique pour la réponse extractive aux questions est [SQuAD](https://rajpurkar.github.io/SQuAD-explorer/), c'est donc celui que nous utiliserons ici. Il existe également une référence plus difficile [SQuAD v2](https://huggingface.co/datasets/squad_v2), qui comprend des questions sans réponse. Tant que votre propre jeu de données contient une colonne pour les contextes, une colonne pour les questions et une colonne pour les réponses, vous devriez être en mesure d'adapter les étapes ci-dessous.
+
+### Le jeu de données SQuAD
+
+Comme d'habitude, nous pouvons télécharger et mettre en cache le jeu de données en une seule étape grâce à `load_dataset()` :
+
+```py
+from datasets import load_dataset
+
+raw_datasets = load_dataset("squad")
+```
+
+Nous pouvons alors jeter un coup d'œil à cet objet pour en savoir plus sur le jeu de données SQuAD :
+
+```py
+raw_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['id', 'title', 'context', 'question', 'answers'],
+ num_rows: 87599
+ })
+ validation: Dataset({
+ features: ['id', 'title', 'context', 'question', 'answers'],
+ num_rows: 10570
+ })
+})
+```
+
+On dirait que nous avons tout ce dont nous avons besoin avec les champs `context`, `question` et `answers`, alors imprimons-les pour le premier élément de notre ensemble d'entraînement :
+
+```py
+print("Context: ", raw_datasets["train"][0]["context"])
+print("Question: ", raw_datasets["train"][0]["question"])
+print("Answer: ", raw_datasets["train"][0]["answers"])
+```
+
+```python out
+Context: 'Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend "Venite Ad Me Omnes". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.'
+# Sur le plan architectural, l'école a un caractère catholique. Au sommet du dôme doré du bâtiment principal se trouve une statue dorée de la Vierge Marie. Immédiatement devant le bâtiment principal et face à lui, se trouve une statue en cuivre du Christ, les bras levés, avec la légende "Venite Ad Me Omnes". À côté du bâtiment principal se trouve la basilique du Sacré-Cœur. Immédiatement derrière la basilique se trouve la Grotte, un lieu marial de prière et de réflexion. Il s'agit d'une réplique de la grotte de Lourdes, en France, où la Vierge Marie serait apparue à Sainte Bernadette Soubirous en 1858. Au bout de l'allée principale (et dans une ligne directe qui passe par 3 statues et le Dôme d'or), se trouve une statue de pierre simple et moderne de Marie'.
+Question: 'To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?' # A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes, en France ?
+Answer: {'text': ['Saint Bernadette Soubirous'], 'answer_start': [515]}
+```
+
+Les champs `context` et `question` sont très simples à utiliser. Le champ `answers` est un peu plus délicat car il compile un dictionnaire avec deux champs qui sont tous deux des listes. C'est le format qui sera attendu par la métrique `squad` lors de l'évaluation ; si vous utilisez vos propres données, vous n'avez pas nécessairement besoin de vous soucier de mettre les réponses dans le même format. Le champ `text` est assez évident, et le champ `answer_start` contient l'indice du caractère de départ de chaque réponse dans le contexte.
+
+Pendant l'entraînement, il n'y a qu'une seule réponse possible. Nous pouvons vérifier cela en utilisant la méthode `Dataset.filter()` :
+
+```py
+raw_datasets["train"].filter(lambda x: len(x["answers"]["text"]) != 1)
+```
+
+```python out
+Dataset({
+ features: ['id', 'title', 'context', 'question', 'answers'],
+ num_rows: 0
+})
+```
+
+Pour l'évaluation, cependant, il existe plusieurs réponses possibles pour chaque échantillon, qui peuvent être identiques ou différentes :
+
+```py
+print(raw_datasets["validation"][0]["answers"])
+print(raw_datasets["validation"][2]["answers"])
+```
+
+```python out
+{'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]}
+{'text': ['Santa Clara, California', "Levi's Stadium", "Levi's Stadium in the San Francisco Bay Area at Santa Clara, California."], 'answer_start': [403, 355, 355]}
+```
+
+Nous ne nous plongerons pas dans le script d'évaluation car tout sera enveloppé par une métrique 🤗 *Datasets* pour nous, mais la version courte est que certaines des questions ont plusieurs réponses possibles, et ce script va comparer une réponse prédite à toutes les réponses acceptables et prendre le meilleur score. Si nous regardons l'échantillon de l'indice 2, par exemple :
+
+```py
+print(raw_datasets["validation"][2]["context"])
+print(raw_datasets["validation"][2]["question"])
+```
+
+```python out
+'Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi\'s Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the "golden anniversary" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as "Super Bowl L"), so that the logo could prominently feature the Arabic numerals 50.'
+# Le Super Bowl 50 était un match de football américain visant à déterminer le champion de la National Football League (NFL) pour la saison 2015. Les Denver Broncos, champions de la Conférence de football américain (AFC), ont battu les Carolina Panthers, champions de la Conférence nationale de football (NFC), 24 à 10, pour remporter leur troisième titre de Super Bowl. Le match s'est déroulé le 7 février 2016 au Levi\'s Stadium, dans la baie de San Francisco, à Santa Clara, en Californie. Comme il s'agissait du 50e Super Bowl, la ligue a mis l'accent sur l'" anniversaire doré " avec diverses initiatives sur le thème de l'or, ainsi qu'en suspendant temporairement la tradition de nommer chaque match du Super Bowl avec des chiffres romains (en vertu de laquelle le match aurait été appelé " Super Bowl L "), afin que le logo puisse mettre en évidence les chiffres arabes 50.''
+'Where did Super Bowl 50 take place?' # Où a eu lieu le Super Bowl 50 ?
+```
+
+nous pouvons voir que la réponse peut effectivement être l'une des trois possibilités que nous avons vues précédemment.
+
+### Traitement des données d'entraînement
+
+
+
+Commençons par le prétraitement des données d'entraînement. La partie la plus difficile sera de générer des étiquettes pour la réponse à la question, qui seront les positions de début et de fin des *tokens* correspondant à la réponse dans le contexte.
+
+Mais ne nous emballons pas. Tout d'abord, nous devons convertir le texte de l'entrée en identifiants que le modèle peut comprendre, en utilisant un *tokenizer* :
+
+```py
+from transformers import AutoTokenizer
+
+model_checkpoint = "bert-base-cased"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+```
+
+Comme mentionné précédemment, nous allons *finetuner* un modèle BERT, mais vous pouvez utiliser n'importe quel autre type de modèle tant qu'il a un *tokenizer* rapide implémenté. Vous pouvez voir toutes les architectures qui sont livrées avec une version rapide dans [ce grand tableau](https://huggingface.co/transformers/#supported-frameworks), et pour vérifier que l'objet `tokenizer` que vous utilisez est bien soutenu par des 🤗 *Tokenizers* vous pouvez regarder son attribut `is_fast` :
+
+```py
+tokenizer.is_fast
+```
+
+```python out
+True
+```
+
+Nous pouvons transmettre à notre *tokenizer* la question et le contexte ensemble, et il insérera correctement les *tokens* spéciaux pour former une phrase comme celle-ci :
+
+```
+[CLS] question [SEP] context [SEP]
+```
+
+Vérifions à nouveau :
+
+```py
+context = raw_datasets["train"][0]["context"]
+question = raw_datasets["train"][0]["question"]
+
+inputs = tokenizer(question, context)
+tokenizer.decode(inputs["input_ids"])
+```
+
+```python out
+'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, '
+'the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin '
+'Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms '
+'upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred '
+'Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a '
+'replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette '
+'Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues '
+'and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]'
+
+'[CLS] A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes en France ? [SEP] Architecturalement, '
+l'école a un caractère catholique. Au sommet du dôme doré du bâtiment principal se trouve une statue dorée de la Vierge '
+Marie. Immédiatement devant le bâtiment principal et face à lui, se trouve une statue en cuivre du Christ, les bras ''levés''.
+'levés avec la légende " Venite Ad Me Omnes ". A côté du bâtiment principal se trouve la basilique du Sacré-Cœur.
+'Cœur. Immédiatement derrière la basilique se trouve la Grotte, un lieu marial de prière et de réflexion. Il s'agit d'une '
+'réplique de la grotte de Lourdes, en France, où la Vierge Marie serait apparue à Sainte Bernadette '
+Soubirous en 1858. Au bout de l'allée principale ( et en ligne directe qui passe par 3 statues '
+'et le Dôme d'or), se trouve une statue de Marie en pierre, simple et moderne. [SEP]''
+```
+
+Les étiquettes seront alors l'index des *tokens* de début et de fin de la réponse, et le modèle sera chargé de prédire un logit de début et de fin par *token* dans l'entrée, les étiquettes théoriques étant les suivantes :
+
+
+
+
+
+
+Dans ce cas, le contexte n'est pas trop long, mais certains des exemples de l'ensemble de données ont des contextes très longs qui dépasseront la longueur maximale que nous avons fixée (qui est de 384 dans ce cas). Comme nous l'avons vu dans le [Chapitre 6](/course/fr/chapter6/4) lorsque nous avons exploré les internes du pipeline `question-answering`, nous allons traiter les contextes longs en créant plusieurs caractéristiques d'entraînement à partir d'un échantillon de notre jeu de données, avec une fenêtre glissante entre eux.
+
+Pour voir comment cela fonctionne en utilisant l'exemple actuel, nous pouvons limiter la longueur à 100 et utiliser une fenêtre glissante de 50 *tokens*. Pour rappel, nous utilisons
+
+- `max_length` pour définir la longueur maximale (ici 100)
+- `truncation="only_second"` pour tronquer le contexte (qui est en deuxième position) quand la question avec son contexte est trop longue
+- `stride` pour fixer le nombre de *tokens* se chevauchant entre deux morceaux successifs (ici 50)
+- `return_overflowing_tokens=True` pour indiquer au tokenizer que l'on veut les *tokens* qui débordent
+
+```py
+inputs = tokenizer(
+ question,
+ context,
+ max_length=100,
+ truncation="only_second",
+ stride=50,
+ return_overflowing_tokens=True,
+)
+
+for ids in inputs["input_ids"]:
+ print(tokenizer.decode(ids))
+```
+
+```python out
+'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basi [SEP]'
+'[CLS] A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes en France ? [SEP] Sur le plan architectural, l'école a un caractère catholique. Au sommet du dôme doré du bâtiment principal se trouve une statue dorée de la Vierge Marie. Immédiatement devant le bâtiment principal et face à lui, se trouve une statue en cuivre du Christ, les bras levés, avec la légende " Venite Ad Me Omnes ". À côté du bâtiment principal se trouve la basilique du Sacré-Cœur. Immédiatement derrière la basi [SEP]''.
+
+'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin [SEP]'
+'[CLS] A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes en France ? [le bâtiment principal et face à lui, une statue en cuivre du Christ aux bras levés avec la légende " Venite Ad Me Omnes ". À côté du bâtiment principal se trouve la basilique du Sacré-Cœur. Immédiatement derrière la basilique se trouve la Grotte, un lieu marial de prière et de réflexion. Il s'agit d'une réplique de la grotte de Lourdes, en France, où la Vierge [SEP]''.
+
+'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 [SEP]'
+'[CLS] A qui la Vierge Marie serait-elle apparue en 1858 à Lourdes en France ? [A côté du bâtiment principal se trouve la basilique du Sacré-Cœur. Immédiatement derrière la basilique se trouve la Grotte, un lieu marial de prière et de réflexion. Il s'agit d'une réplique de la grotte de Lourdes, en France, où la Vierge Marie serait apparue à Sainte Bernadette Soubirous en 1858. Au bout de l'allée principale ( et dans une ligne directe qui relie par 3 [SEP]''.
+
+'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP]. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]'
+'[CLS] A qui la Vierge Marie est-elle prétendument apparue en 1858 à Lourdes France ? [SEP]. Il s'agit d'une réplique de la grotte de Lourdes, en France, où la Vierge Marie serait apparue à Sainte Bernadette Soubirous en 1858. Au bout de l'allée principale (et dans une ligne directe qui passe par 3 statues et le Dôme d'or), se trouve une simple statue de pierre moderne de Marie. [SEP]'
+```
+
+Comme nous pouvons le voir, notre exemple a été divisé en quatre entrées, chacune d'entre elles contenant la question et une partie du contexte. Notez que la réponse à la question ("Bernadette Soubirous") n'apparaît que dans la troisième et dernière entrée, donc en traitant les longs contextes de cette façon, nous allons créer quelques exemples d'entraînement où la réponse n'est pas incluse dans le contexte. Pour ces exemples, les étiquettes seront `start_position = end_position = 0` (donc nous prédisons le *token* `[CLS]`). Nous définirons également ces étiquettes dans le cas malheureux où la réponse a été tronquée de sorte que nous n'avons que le début (ou la fin) de celle-ci. Pour les exemples où la réponse est entièrement dans le contexte, les étiquettes seront l'index du *token* où la réponse commence et l'index du *token* où la réponse se termine.
+
+L'ensemble de données nous fournit le caractère de début de la réponse dans le contexte, et en ajoutant la longueur de la réponse, nous pouvons trouver le caractère de fin dans le contexte. Pour faire correspondre ces indices aux *tokens*, nous devrons utiliser les mappages d'offset que nous avons étudiés au [Chapitre 6](/course/chapter6/4). Nous pouvons faire en sorte que notre *tokenizer* renvoie ces index en passant `return_offsets_mapping=True` :
+
+```py
+inputs = tokenizer(
+ question,
+ context,
+ max_length=100,
+ truncation="only_second",
+ stride=50,
+ return_overflowing_tokens=True,
+ return_offsets_mapping=True,
+)
+inputs.keys()
+```
+
+```python out
+dict_keys(['input_ids', 'token_type_ids', 'attention_mask', 'offset_mapping', 'overflow_to_sample_mapping'])
+```
+
+Comme nous pouvons le voir, nous récupérons les habituels ID d'entrée, ID de type de jeton, et masque d'attention, ainsi que le mappage d'offset dont nous avions besoin et une clé supplémentaire, `overflow_to_sample_mapping`. La valeur correspondante nous sera utile lorsque nous tokeniserons plusieurs textes en même temps (ce que nous devrions faire pour bénéficier du fait que notre *tokenizer* est soutenu par Rust). Puisqu'un échantillon peut donner plusieurs caractéristiques, il fait correspondre chaque caractéristique à l'exemple d'où elle provient. Parce qu'ici nous avons seulement tokenisé un exemple, nous obtenons une liste de `0`s :
+
+```py
+inputs["overflow_to_sample_mapping"]
+```
+
+```python out
+[0, 0, 0, 0]
+```
+
+Mais si nous tokenisons plus d'exemples, cela deviendra plus utile :
+
+```py
+inputs = tokenizer(
+ raw_datasets["train"][2:6]["question"],
+ raw_datasets["train"][2:6]["context"],
+ max_length=100,
+ truncation="only_second",
+ stride=50,
+ return_overflowing_tokens=True,
+ return_offsets_mapping=True,
+)
+
+print(f"The 4 examples gave {len(inputs['input_ids'])} features.")
+print(f"Here is where each comes from: {inputs['overflow_to_sample_mapping']}.")
+```
+
+```python out
+'The 4 examples gave 19 features.'
+'Here is where each comes from: [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3].'
+```
+
+Comme nous pouvons le voir, les trois premiers exemples (aux indices 2, 3 et 4 de l'ensemble d'entraînement) ont chacun donné quatre caractéristiques et le dernier exemple (à l'indice 5 de l'ensemble d'entraînement) a donné 7 caractéristiques.
+
+Ces informations seront utiles pour associer chaque caractéristique obtenue à son étiquette correspondante. Comme mentionné précédemment, ces étiquettes sont :
+
+- `(0, 0)` si la réponse n'est pas dans l'espace correspondant du contexte.
+- `(start_position, end_position)` si la réponse est dans l'espace correspondant du contexte, avec `start_position` étant l'index du *token* (dans les IDs d'entrée) au début de la réponse et `end_position` étant l'index du *token* (dans les IDs d'entrée) où la réponse se termine.
+
+Pour déterminer ce qui est le cas et, le cas échéant, les positions des *tokens*, nous trouvons d'abord les indices qui commencent et finissent le contexte dans les IDs d'entrée. Nous pourrions utiliser les IDs du type de *token* pour le faire, mais puisque ceux-ci n'existent pas nécessairement pour tous les modèles (DistilBERT ne les requiert pas, par exemple), nous allons plutôt utiliser la méthode `sequence_ids()` du `BatchEncoding` que notre tokenizer retourne.
+
+Une fois que nous avons ces indices de *tokens*, nous regardons les offsets correspondants, qui sont des tuples de deux entiers représentant l'étendue des caractères dans le contexte original. Nous pouvons ainsi détecter si le *chunk* du contexte dans cette fonctionnalité commence après la réponse ou se termine avant que la réponse ne commence (dans ce cas, l'étiquette est `(0, 0)`). Si ce n'est pas le cas, nous bouclons pour trouver le premier et le dernier *token* de la réponse :
+
+```py
+answers = raw_datasets["train"][2:6]["answers"]
+start_positions = []
+end_positions = []
+
+for i, offset in enumerate(inputs["offset_mapping"]):
+ sample_idx = inputs["overflow_to_sample_mapping"][i]
+ answer = answers[sample_idx]
+ start_char = answer["answer_start"][0]
+ end_char = answer["answer_start"][0] + len(answer["text"][0])
+ sequence_ids = inputs.sequence_ids(i)
+
+ # Trouver le début et la fin du contexte
+ idx = 0
+ while sequence_ids[idx] != 1:
+ idx += 1
+ context_start = idx
+ while sequence_ids[idx] == 1:
+ idx += 1
+ context_end = idx - 1
+
+ # Si la réponse n'est pas entièrement dans le contexte, l'étiquette est (0, 0).
+ if offset[context_start][0] > start_char or offset[context_end][1] < end_char:
+ start_positions.append(0)
+ end_positions.append(0)
+ else:
+ # Otherwise it's the start and end token positions
+ idx = context_start
+ while idx <= context_end and offset[idx][0] <= start_char:
+ idx += 1
+ start_positions.append(idx - 1)
+
+ idx = context_end
+ while idx >= context_start and offset[idx][1] >= end_char:
+ idx -= 1
+ end_positions.append(idx + 1)
+
+start_positions, end_positions
+```
+
+```python out
+([83, 51, 19, 0, 0, 64, 27, 0, 34, 0, 0, 0, 67, 34, 0, 0, 0, 0, 0],
+ [85, 53, 21, 0, 0, 70, 33, 0, 40, 0, 0, 0, 68, 35, 0, 0, 0, 0, 0])
+```
+
+Jetons un coup d'œil à quelques résultats pour vérifier que notre approche est correcte. Pour la première caractéristique, nous trouvons `(83, 85)` comme étiquettes, alors comparons la réponse théorique avec l'étendue décodée des *tokens* de 83 à 85 (inclus) :
+
+```py
+idx = 0
+sample_idx = inputs["overflow_to_sample_mapping"][idx]
+answer = answers[sample_idx]["text"][0]
+
+start = start_positions[idx]
+end = end_positions[idx]
+labeled_answer = tokenizer.decode(inputs["input_ids"][idx][start : end + 1])
+
+print(f"Theoretical answer: {answer}, labels give: {labeled_answer}")
+```
+
+```python out
+'Theoretical answer: the Main Building, labels give: the Main Building'
+```
+
+Donc, c'est une correspondance ! Maintenant, vérifions l'index 4, où nous avons mis les étiquettes à `(0, 0)`, ce qui signifie que la réponse n'est pas dans le *chunk* de contexte de cette caractéristique :
+
+```py
+idx = 4
+sample_idx = inputs["overflow_to_sample_mapping"][idx]
+answer = answers[sample_idx]["text"][0]
+
+decoded_example = tokenizer.decode(inputs["input_ids"][idx])
+print(f"Theoretical answer: {answer}, decoded example: {decoded_example}")
+```
+
+```python out
+'Theoretical answer: a Marian place of prayer and reflection, decoded example: [CLS] What is the Grotto at Notre Dame? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grot [SEP]'
+```
+
+En effet, nous ne voyons pas la réponse dans le contexte.
+
+
+
+✏️ **A votre tour !** En utilisant l'architecture XLNet, le *padding* est appliqué à gauche et la question et le contexte sont intervertis. Adaptez tout le code que nous venons de voir à l'architecture XLNet (et ajoutez `padding=True`). Soyez conscient que le token `[CLS]` peut ne pas être à la position 0 avec le *padding* appliqué.
+
+
+
+Maintenant que nous avons vu étape par étape comment prétraiter nos données d'entraînement, nous pouvons les regrouper dans une fonction que nous appliquerons à l'ensemble des données d'entraînement. Nous allons rembourrer chaque caractéristique à la longueur maximale que nous avons définie, car la plupart des contextes seront longs (et les échantillons correspondants seront divisés en plusieurs caractéristiques), il n'y a donc pas de réel avantage à appliquer un rembourrage dynamique ici :
+
+```py
+max_length = 384
+stride = 128
+
+
+def preprocess_training_examples(examples):
+ questions = [q.strip() for q in examples["question"]]
+ inputs = tokenizer(
+ questions,
+ examples["context"],
+ max_length=max_length,
+ truncation="only_second",
+ stride=stride,
+ return_overflowing_tokens=True,
+ return_offsets_mapping=True,
+ padding="max_length",
+ )
+
+ offset_mapping = inputs.pop("offset_mapping")
+ sample_map = inputs.pop("overflow_to_sample_mapping")
+ answers = examples["answers"]
+ start_positions = []
+ end_positions = []
+
+ for i, offset in enumerate(offset_mapping):
+ sample_idx = sample_map[i]
+ answer = answers[sample_idx]
+ start_char = answer["answer_start"][0]
+ end_char = answer["answer_start"][0] + len(answer["text"][0])
+ sequence_ids = inputs.sequence_ids(i)
+
+ # Trouver le début et la fin du contexte
+ idx = 0
+ while sequence_ids[idx] != 1:
+ idx += 1
+ context_start = idx
+ while sequence_ids[idx] == 1:
+ idx += 1
+ context_end = idx - 1
+
+ # Si la réponse n'est pas entièrement dans le contexte, l'étiquette est (0, 0).
+ if offset[context_start][0] > start_char or offset[context_end][1] < end_char:
+ start_positions.append(0)
+ end_positions.append(0)
+ else:
+ # Otherwise it's the start and end token positions
+ idx = context_start
+ while idx <= context_end and offset[idx][0] <= start_char:
+ idx += 1
+ start_positions.append(idx - 1)
+
+ idx = context_end
+ while idx >= context_start and offset[idx][1] >= end_char:
+ idx -= 1
+ end_positions.append(idx + 1)
+
+ inputs["start_positions"] = start_positions
+ inputs["end_positions"] = end_positions
+ return inputs
+```
+
+Notez que nous avons défini deux constantes pour déterminer la longueur maximale utilisée ainsi que la longueur de la fenêtre glissante, et que nous avons ajouté un petit nettoyage avant la tokénisation : certaines des questions dans le jeu de données SQuAD ont des espaces supplémentaires au début et à la fin qui n'ajoutent rien (et prennent de la place lors de la tokénisation si vous utilisez un modèle comme RoBERTa), donc nous avons supprimé ces espaces supplémentaires.
+
+Pour appliquer cette fonction à l'ensemble de l'entraînement, nous utilisons la méthode `Dataset.map()` avec le flag `batched=True`. C'est nécessaire ici car nous changeons la longueur de l'ensemble de données (puisqu'un exemple peut donner plusieurs caractéristiques d'entraînement) :
+
+```py
+train_dataset = raw_datasets["train"].map(
+ preprocess_training_examples,
+ batched=True,
+ remove_columns=raw_datasets["train"].column_names,
+)
+len(raw_datasets["train"]), len(train_dataset)
+```
+
+```python out
+(87599, 88729)
+```
+
+Comme nous pouvons le voir, le prétraitement a ajouté environ 1 000 caractéristiques. Notre ensemble d'entraînement est maintenant prêt à être utilisé - passons au prétraitement de l'ensemble de validation !
+
+### Traitement des données de validation
+
+Le prétraitement des données de validation sera légèrement plus facile car nous n'avons pas besoin de générer des étiquettes (sauf si nous voulons calculer une perte de validation, mais ce nombre ne nous aidera pas vraiment à comprendre la qualité du modèle). La vraie joie sera d'interpréter les prédictions du modèle dans des étendues du contexte original. Pour cela, il nous suffit de stocker les mappages de décalage et un moyen de faire correspondre chaque caractéristique créée à l'exemple original dont elle provient. Puisqu'il y a une colonne ID dans l'ensemble de données original, nous utiliserons cet ID.
+
+La seule chose que nous allons ajouter ici est un petit nettoyage des mappages de décalage. Ils contiendront les offsets pour la question et le contexte, mais une fois que nous serons dans la phase de post-traitement, nous n'aurons aucun moyen de savoir quelle partie des IDs d'entrée correspondait au contexte et quelle partie était la question (la méthode `sequence_ids()` que nous avons utilisée n'est disponible que pour la sortie du *tokenizer*). Donc, nous allons mettre les offsets correspondant à la question à `None` :
+
+```py
+def preprocess_validation_examples(examples):
+ questions = [q.strip() for q in examples["question"]]
+ inputs = tokenizer(
+ questions,
+ examples["context"],
+ max_length=max_length,
+ truncation="only_second",
+ stride=stride,
+ return_overflowing_tokens=True,
+ return_offsets_mapping=True,
+ padding="max_length",
+ )
+
+ sample_map = inputs.pop("overflow_to_sample_mapping")
+ example_ids = []
+
+ for i in range(len(inputs["input_ids"])):
+ sample_idx = sample_map[i]
+ example_ids.append(examples["id"][sample_idx])
+
+ sequence_ids = inputs.sequence_ids(i)
+ offset = inputs["offset_mapping"][i]
+ inputs["offset_mapping"][i] = [
+ o if sequence_ids[k] == 1 else None for k, o in enumerate(offset)
+ ]
+
+ inputs["example_id"] = example_ids
+ return inputs
+```
+
+Nous pouvons appliquer cette fonction sur l'ensemble des données de validation comme précédemment :
+
+```py
+validation_dataset = raw_datasets["validation"].map(
+ preprocess_validation_examples,
+ batched=True,
+ remove_columns=raw_datasets["validation"].column_names,
+)
+len(raw_datasets["validation"]), len(validation_dataset)
+```
+
+```python out
+(10570, 10822)
+```
+
+Dans ce cas, nous n'avons ajouté que quelques centaines d'échantillons, il semble donc que les contextes dans l'ensemble de données de validation soient un peu plus courts.
+
+Maintenant que nous avons prétraité toutes les données, nous pouvons passer à l'entraînement.
+
+{#if fw === 'pt'}
+
+## *Finetuner* le modèle avec l'API `Trainer`
+
+Le code d'entraînement pour cet exemple ressemblera beaucoup au code des sections précédentes -- la chose la plus difficile sera d'écrire la fonction `compute_metrics()`. Puisque nous avons capitonné tous les échantillons à la longueur maximale que nous avons fixée, il n'y a pas de collateur de données à définir, donc ce calcul de métrique est vraiment la seule chose dont nous devons nous soucier. La partie la plus difficile sera de post-traiter les prédictions du modèle en travées de texte dans les exemples originaux ; une fois que nous aurons fait cela, la métrique de la bibliothèque 🤗 *Datasets* fera le gros du travail pour nous.
+
+{:else}
+
+## *Finetuner* fin du modèle avec Keras
+
+Le code d'entraînement de cet exemple ressemblera beaucoup au code des sections précédentes, mais le calcul des métriques sera un défi unique. Puisque nous avons capitonné tous les échantillons à la longueur maximale que nous avons définie, il n'y a pas de collateur de données à définir, donc le calcul de la métrique est vraiment la seule chose dont nous devons nous soucier. La partie la plus difficile sera de post-traiter les prédictions du modèle en travées de texte dans les exemples originaux ; une fois que nous aurons fait cela, la métrique de la bibliothèque 🤗 *Datasets* fera le gros du travail pour nous.
+
+{/if}
+
+### Post-traitement
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Le modèle produira des logits pour les positions de début et de fin de la réponse dans les IDs d'entrée, comme nous l'avons vu lors de notre exploration du [`question-answering` pipeline](/course/chapter6/4). L'étape de post-traitement sera similaire à ce que nous avons fait là-bas, donc voici un rappel rapide des actions que nous avons prises :
+
+- nous avons masqué les logits de début et de fin correspondant aux *tokens* en dehors du contexte,
+- nous avons ensuite converti les logits de début et de fin en probabilités en utilisant un softmax,
+- nous avons attribué un score à chaque paire `(start_token, end_token)` en prenant le produit des deux probabilités correspondantes,
+- nous avons cherché la paire avec le score maximum qui donnait une réponse valide (par exemple, un `start_token` inférieur au `end_token`).
+
+Ici, nous allons modifier légèrement ce processus car nous n'avons pas besoin de calculer les scores réels (juste la réponse prédite). Cela signifie que nous pouvons sauter l'étape du softmax. Pour aller plus vite, nous ne noterons pas non plus toutes les paires `(start_token, end_token)` possibles, mais seulement celles correspondant aux logits `n_best` les plus élevés (avec `n_best=20`). Puisque nous sauterons le softmax, ces scores seront des scores logit, et seront obtenus en prenant la somme des logits de début et de fin (au lieu du produit, à cause de la règle \\(\log(ab) = \log(a) + \log(b)\)).
+
+Pour démontrer tout cela, nous aurons besoin d'un certain type de prédictions. Puisque nous n'avons pas encore entraîné notre modèle, nous allons utiliser le modèle par défaut du pipeline d'assurance qualité pour générer quelques prédictions sur une petite partie de l'ensemble de validation. Nous pouvons utiliser la même fonction de traitement que précédemment ; parce qu'elle repose sur la constante globale `tokenizer`, nous devons juste changer cet objet pour le tokenizer du modèle que nous voulons utiliser temporairement :
+
+```python
+small_eval_set = raw_datasets["validation"].select(range(100))
+trained_checkpoint = "distilbert-base-cased-distilled-squad"
+
+tokenizer = AutoTokenizer.from_pretrained(trained_checkpoint)
+eval_set = small_eval_set.map(
+ preprocess_validation_examples,
+ batched=True,
+ remove_columns=raw_datasets["validation"].column_names,
+)
+```
+
+Maintenant que le prétraitement est terminé, nous changeons le *tokenizer* pour celui que nous avons choisi à l'origine :
+
+```python
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+```
+
+Nous supprimons ensuite les colonnes de notre `eval_set` qui ne sont pas attendues par le modèle, nous construisons un lot avec l'ensemble de ce petit ensemble de validation, et nous le passons au modèle. Si un GPU est disponible, nous l'utilisons pour aller plus vite :
+
+{#if fw === 'pt'}
+
+```python
+import torch
+from transformers import AutoModelForQuestionAnswering
+
+eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"])
+eval_set_for_model.set_format("torch")
+
+device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+batch = {k: eval_set_for_model[k].to(device) for k in eval_set_for_model.column_names}
+trained_model = AutoModelForQuestionAnswering.from_pretrained(trained_checkpoint).to(
+ device
+)
+
+with torch.no_grad():
+ outputs = trained_model(**batch)
+```
+
+Puisque le `Trainer` nous donnera les prédictions sous forme de tableaux NumPy, nous récupérons les logits de début et de fin et les convertissons dans ce format :
+
+```python
+start_logits = outputs.start_logits.cpu().numpy()
+end_logits = outputs.end_logits.cpu().numpy()
+```
+
+{:else}
+
+```python
+import tensorflow as tf
+from transformers import TFAutoModelForQuestionAnswering
+
+eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"])
+eval_set_for_model.set_format("numpy")
+
+batch = {k: eval_set_for_model[k] for k in eval_set_for_model.column_names}
+trained_model = TFAutoModelForQuestionAnswering.from_pretrained(trained_checkpoint)
+
+outputs = trained_model(**batch)
+```
+
+Pour faciliter l'expérimentation, nous allons convertir ces sorties en tableaux NumPy :
+
+```python
+start_logits = outputs.start_logits.numpy()
+end_logits = outputs.end_logits.numpy()
+```
+
+{/if}
+
+Maintenant, nous devons trouver la réponse prédite pour chaque exemple dans notre `small_eval_set`. Un exemple peut avoir été divisé en plusieurs caractéristiques dans `eval_set`, donc la première étape est de faire correspondre chaque exemple dans `small_eval_set` aux caractéristiques correspondantes dans `eval_set` :
+
+```python
+import collections
+
+example_to_features = collections.defaultdict(list)
+for idx, feature in enumerate(eval_set):
+ example_to_features[feature["example_id"]].append(idx)
+```
+
+Avec cela en main, nous pouvons vraiment nous mettre au travail en parcourant en boucle tous les exemples et, pour chaque exemple, toutes les caractéristiques associées. Comme nous l'avons dit précédemment, nous allons regarder les scores logit pour les `n_meilleurs` logits de début et logits de fin, en excluant les positions qui donnent :
+
+- une réponse qui ne serait pas dans le contexte.
+- une réponse avec une longueur négative
+- une réponse qui est trop longue (nous limitons les possibilités à `max_answer_length=30`)
+
+Une fois que nous avons toutes les réponses possibles notées pour un exemple, nous choisissons simplement celle qui a le meilleur score logit :
+
+```python
+import numpy as np
+
+n_best = 20
+max_answer_length = 30
+predicted_answers = []
+
+for example in small_eval_set:
+ example_id = example["id"]
+ context = example["context"]
+ answers = []
+
+ for feature_index in example_to_features[example_id]:
+ start_logit = start_logits[feature_index]
+ end_logit = end_logits[feature_index]
+ offsets = eval_set["offset_mapping"][feature_index]
+
+ start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist()
+ end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist()
+ for start_index in start_indexes:
+ for end_index in end_indexes:
+ # Ignore les réponses qui ne sont pas entièrement dans le contexte
+ if offsets[start_index] is None or offsets[end_index] is None:
+ continue
+ # Ignorer les réponses dont la longueur est soit < 0 soit > max_answer_length.
+ if (
+ end_index < start_index
+ or end_index - start_index + 1 > max_answer_length
+ ):
+ continue
+
+ answers.append(
+ {
+ "text": context[offsets[start_index][0] : offsets[end_index][1]],
+ "logit_score": start_logit[start_index] + end_logit[end_index],
+ }
+ )
+
+ best_answer = max(answers, key=lambda x: x["logit_score"])
+ predicted_answers.append({"id": example_id, "prediction_text": best_answer["text"]})
+```
+
+Le format final des réponses prédites est celui qui sera attendu par la métrique que nous allons utiliser. Comme d'habitude, nous pouvons le charger à l'aide de la bibliothèque 🤗 *Datasets* :
+
+```python
+from datasets import load_metric
+
+metric = load_metric("squad")
+```
+
+Cette métrique attend les réponses prédites dans le format que nous avons vu ci-dessus (une liste de dictionnaires avec une clé pour l'ID de l'exemple et une clé pour le texte prédit) et les réponses théoriques dans le format ci-dessous (une liste de dictionnaires avec une clé pour l'ID de l'exemple et une clé pour les réponses possibles) :
+
+```python
+theoretical_answers = [
+ {"id": ex["id"], "answers": ex["answers"]} for ex in small_eval_set
+]
+```
+
+Nous pouvons maintenant vérifier que nous obtenons des résultats raisonnables en examinant le premier élément des deux listes :
+
+```python
+print(predicted_answers[0])
+print(theoretical_answers[0])
+```
+
+```python out
+{'id': '56be4db0acb8001400a502ec', 'prediction_text': 'Denver Broncos'}
+{'id': '56be4db0acb8001400a502ec', 'answers': {'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]}}
+```
+
+Pas trop mal ! Voyons maintenant le score que la métrique nous donne :
+
+```python
+metric.compute(predictions=predicted_answers, references=theoretical_answers)
+```
+
+```python out
+{'exact_match': 83.0, 'f1': 88.25}
+```
+
+Encore une fois, c'est plutôt bon si l'on considère que, selon [son article](https://arxiv.org/abs/1910.01108v2), DistilBERT *finetuné* sur SQuAD obtient 79,1 et 86,9 pour ces scores sur l'ensemble des données.
+
+{#if fw === 'pt'}
+
+Maintenant, mettons tout ce que nous venons de faire dans une fonction `compute_metrics()` que nous utiliserons dans le `Trainer`. Normalement, cette fonction `compute_metrics()` reçoit seulement un tuple `eval_preds` avec les logits et les labels. Ici, nous aurons besoin d'un peu plus, car nous devons chercher dans le jeu de données des caractéristiques pour le décalage et dans le jeu de données des exemples pour les contextes originaux, donc nous ne serons pas en mesure d'utiliser cette fonction pour obtenir des résultats d'évaluation réguliers pendant l'entraînement. Nous ne l'utiliserons qu'à la fin de l'entraînement pour vérifier les résultats.
+
+La fonction `compute_metrics()` regroupe les mêmes étapes que précédemment ; nous ajoutons juste une petite vérification au cas où nous ne trouverions aucune réponse valide (dans ce cas nous prédisons une chaîne vide).
+
+{:else}
+
+Maintenant, mettons tout ce que nous venons de faire dans une fonction `compute_metrics()` que nous utiliserons après avoir entraîné notre modèle. Nous aurons besoin de passer un peu plus que juste les logits de sortie, car nous devons chercher dans le jeu de données des caractéristiques pour le décalage et dans le jeu de données des exemples pour les contextes originaux :
+
+{/if}
+
+```python
+from tqdm.auto import tqdm
+
+
+def compute_metrics(start_logits, end_logits, features, examples):
+ example_to_features = collections.defaultdict(list)
+ for idx, feature in enumerate(features):
+ example_to_features[feature["example_id"]].append(idx)
+
+ predicted_answers = []
+ for example in tqdm(examples):
+ example_id = example["id"]
+ context = example["context"]
+ answers = []
+
+ # Parcourir en boucle toutes les fonctionnalités associées à cet exemple
+ for feature_index in example_to_features[example_id]:
+ start_logit = start_logits[feature_index]
+ end_logit = end_logits[feature_index]
+ offsets = features[feature_index]["offset_mapping"]
+
+ start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist()
+ end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist()
+ for start_index in start_indexes:
+ for end_index in end_indexes:
+ # Ignore les réponses qui ne sont pas entièrement dans le contexte
+ if offsets[start_index] is None or offsets[end_index] is None:
+ continue
+ # Ignore les réponses dont la longueur est soit < 0, soit > max_answer_length.
+ if (
+ end_index < start_index
+ or end_index - start_index + 1 > max_answer_length
+ ):
+ continue
+
+ answer = {
+ "text": context[offsets[start_index][0] : offsets[end_index][1]],
+ "logit_score": start_logit[start_index] + end_logit[end_index],
+ }
+ answers.append(answer)
+
+ # Sélectionne la réponse avec le meilleur score
+ if len(answers) > 0:
+ best_answer = max(answers, key=lambda x: x["logit_score"])
+ predicted_answers.append(
+ {"id": example_id, "prediction_text": best_answer["text"]}
+ )
+ else:
+ predicted_answers.append({"id": example_id, "prediction_text": ""})
+
+ theoretical_answers = [{"id": ex["id"], "answers": ex["answers"]} for ex in examples]
+ return metric.compute(predictions=predicted_answers, references=theoretical_answers)
+```
+
+Nous pouvons vérifier que cela fonctionne sur nos prédictions :
+
+```python
+compute_metrics(start_logits, end_logits, eval_set, small_eval_set)
+```
+
+```python out
+{'exact_match': 83.0, 'f1': 88.25}
+```
+
+C'est bien ! Maintenant, utilisons ceci pour affiner notre modèle.
+
+### *Finetuning* du modèle
+
+{#if fw === 'pt'}
+
+Nous sommes maintenant prêts à entraîner notre modèle. Créons-le d'abord, en utilisant la classe `AutoModelForQuestionAnswering` comme précédemment :
+
+```python
+model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
+```
+
+{:else}
+
+Nous sommes maintenant prêts à entraîner notre modèle. Créons-le d'abord, en utilisant la classe `TFAutoModelForQuestionAnswering` comme précédemment :
+
+```python
+model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
+```
+
+{/if}
+
+Comme d'habitude, nous recevons un avertissement indiquant que certains poids ne sont pas utilisés (ceux de la tête de pré-entraînement) et que d'autres sont initialisés de manière aléatoire (ceux de la tête de réponse aux questions). Vous devriez être habitué à cela maintenant, mais cela signifie que ce modèle n'est pas encore prêt à être utilisé et qu'il a besoin d'être *finetuné*. Une bonne chose que nous soyons sur le point de le faire !
+
+Pour pouvoir pousser notre modèle vers le *Hub*, nous devons nous connecter à Hugging Face. Si vous exécutez ce code dans un *notebook*, vous pouvez le faire avec la fonction utilitaire suivante, qui affiche un widget où vous pouvez entrer vos identifiants de connexion :
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Si vous ne travaillez pas dans un *notebook*, tapez simplement la ligne suivante dans votre terminal :
+
+```bash
+huggingface-cli login
+```
+
+{#if fw === 'pt'}
+
+Une fois ceci fait, nous pouvons définir nos `TrainingArguments`. Comme nous l'avons dit lorsque nous avons défini notre fonction pour calculer la métrique, nous ne serons pas en mesure d'avoir une boucle d'évaluation régulière à cause de la signature de la fonction `compute_metrics()`. Nous pourrions écrire notre propre sous-classe de `Trainer` pour faire cela (une approche que vous pouvez trouver dans le [script d'exemple de réponse aux questions](https://github.com/huggingface/transformers/blob/master/examples/pytorch/question-answering/trainer_qa.py)), mais c'est un peu trop long pour cette section. A la place, nous n'évaluerons le modèle qu'à la fin de l'entraînement et nous vous montrerons comment faire une évaluation régulière dans "Une boucle d'entraînement personnalisée" ci-dessous.
+
+C'est vraiment là que l'API `Trainer` montre ses limites et que la bibliothèque 🤗 *Accelerate* brille : personnaliser la classe pour un cas d'utilisation spécifique peut être pénible, mais modifier une boucle d'entraînement entièrement exposée est facile.
+
+Jetons un coup d'œil à notre `TrainingArguments` :
+
+```python
+from transformers import TrainingArguments
+
+args = TrainingArguments(
+ "bert-finetuned-squad",
+ evaluation_strategy="no",
+ save_strategy="epoch",
+ learning_rate=2e-5,
+ num_train_epochs=3,
+ weight_decay=0.01,
+ fp16=True,
+ push_to_hub=True,
+)
+```
+
+Nous avons déjà vu la plupart d'entre eux : nous définissons quelques hyperparamètres (comme le taux d'apprentissage, le nombre d'époques pour lesquelles nous nous entraînons, et une certaine décroissance de poids) et nous indiquons que nous voulons sauvegarder le modèle à la fin de chaque époque, sauter l'évaluation, et télécharger nos résultats vers le Model Hub. Nous activons également l'entraînement en précision mixte avec `fp16=True`, car cela peut accélérer l'entraînement sur un GPU récent.
+
+{:else}
+
+Maintenant que c'est fait, nous pouvons créer nos ensembles de données TF. Nous pouvons utiliser le simple collateur de données par défaut cette fois-ci :
+
+```python
+from transformers import DefaultDataCollator
+
+data_collator = DefaultDataCollator(return_tensors="tf")
+```
+
+Et maintenant nous créons les jeux de données comme d'habitude.
+
+```python
+tf_train_dataset = train_dataset.to_tf_dataset(
+ columns=[
+ "input_ids",
+ "start_positions",
+ "end_positions",
+ "attention_mask",
+ "token_type_ids",
+ ],
+ collate_fn=data_collator,
+ shuffle=True,
+ batch_size=16,
+)
+tf_eval_dataset = validation_dataset.to_tf_dataset(
+ columns=["input_ids", "attention_mask", "token_type_ids"],
+ collate_fn=data_collator,
+ shuffle=False,
+ batch_size=16,
+)
+```
+
+Ensuite, nous configurons nos hyperparamètres d'entraînement et compilons notre modèle :
+
+```python
+from transformers import create_optimizer
+from transformers.keras_callbacks import PushToHubCallback
+import tensorflow as tf
+
+# Le nombre d'étapes d'entraînement est le nombre d'échantillons dans le jeu de données, divisé par la taille du batch, puis multiplié par le nombre total d'époques.
+# par le nombre total d'époques. Notez que le jeu de données tf_train_dataset est ici un lot tf.data.Dataset,
+# et non le jeu de données original Hugging Face Dataset, donc son len() est déjà num_samples // batch_size.
+num_train_epochs = 3
+num_train_steps = len(tf_train_dataset) * num_train_epochs
+optimizer, schedule = create_optimizer(
+ init_lr=2e-5,
+ num_warmup_steps=0,
+ num_train_steps=num_train_steps,
+ weight_decay_rate=0.01,
+)
+model.compile(optimizer=optimizer)
+
+# Entraîner en mixed-precision float16
+tf.keras.mixed_precision.set_global_policy("mixed_float16")
+```
+
+Enfin, nous sommes prêts à nous entraîner avec `model.fit()`. Nous utilisons un `PushToHubCallback` pour télécharger le modèle sur le *Hub* après chaque époque.
+
+{/if}
+
+Par défaut, le dépôt utilisé sera dans votre espace de noms et nommé après le répertoire de sortie que vous avez défini, donc dans notre cas il sera dans `"sgugger/bert-finetuned-squad"`. Nous pouvons passer outre en passant un `hub_model_id` ; par exemple, pour pousser le modèle dans l'organisation `huggingface_course` nous avons utilisé `hub_model_id= "huggingface_course/bert-finetuned-squad"` (qui est le modèle que nous avons lié au début de cette section).
+
+{#if fw === 'pt'}
+
+
+
+💡 Si le répertoire de sortie que vous utilisez existe, il doit être un clone local du dépôt vers lequel vous voulez pousser (donc définissez un nouveau nom si vous obtenez une erreur lors de la définition de votre `Trainer`).
+
+
+
+Enfin, nous passons tout à la classe `Trainer` et lançons l'entraînement :
+
+```python
+from transformers import Trainer
+
+trainer = Trainer(
+ model=model,
+ args=args,
+ train_dataset=train_dataset,
+ eval_dataset=validation_dataset,
+ tokenizer=tokenizer,
+)
+trainer.train()
+```
+
+{:else}
+
+```python
+from transformers.keras_callbacks import PushToHubCallback
+
+callback = PushToHubCallback(output_dir="bert-finetuned-squad", tokenizer=tokenizer)
+
+# Nous allons faire la validation après, donc pas de validation au milieu de l'entraînement.
+model.fit(tf_train_dataset, callbacks=[callback], epochs=num_train_epochs)
+```
+
+{/if}
+
+Notez que pendant l'entraînement, chaque fois que le modèle est sauvegardé (ici, à chaque époque), il est téléchargé sur le Hub en arrière-plan. Ainsi, vous pourrez reprendre votre entraînement sur une autre machine si nécessaire. L'ensemble de l'entraînement prend un certain temps (un peu plus d'une heure sur une Titan RTX), vous pouvez donc prendre un café ou relire les parties du cours qui vous ont semblé plus difficiles pendant qu'il se déroule. Notez également que dès que la première époque est terminée, vous verrez des poids téléchargés sur le Hub et vous pourrez commencer à jouer avec votre modèle sur sa page.
+
+{#if fw === 'pt'}
+
+Une fois l'entraînement terminé, nous pouvons enfin évaluer notre modèle (et prier pour ne pas avoir dépensé tout ce temps de calcul pour rien). La méthode `predict()` du `Trainer` retournera un tuple où les premiers éléments seront les prédictions du modèle (ici une paire avec les logits de début et de fin). Nous envoyons ceci à notre fonction `compute_metrics()` :
+
+```python
+predictions, _ = trainer.predict(validation_dataset)
+start_logits, end_logits = predictions
+compute_metrics(start_logits, end_logits, validation_dataset, raw_datasets["validation"])
+```
+
+{:else}
+
+Une fois l'entraînement terminé, nous pouvons enfin évaluer notre modèle (et prier pour ne pas avoir dépensé tout ce temps de calcul pour rien). La méthode `predict()` de notre `model` se chargera d'obtenir les prédictions, et puisque nous avons fait tout le travail difficile de définir une fonction `compute_metrics()` plus tôt, nous pouvons obtenir nos résultats en une seule ligne :
+
+```python
+predictions = model.predict(tf_eval_dataset)
+compute_metrics(
+ predictions["start_logits"],
+ predictions["end_logits"],
+ validation_dataset,
+ raw_datasets["validation"],
+)
+```
+
+{/if}
+
+```python out
+{'exact_match': 81.18259224219489, 'f1': 88.67381321905516}
+```
+
+Super ! À titre de comparaison, les scores de base indiqués dans l'article du BERT pour ce modèle sont de 80,8 et 88,5, donc nous sommes exactement là où nous devrions être.
+
+{#if fw === 'pt'}
+
+Enfin, nous utilisons la méthode `push_to_hub()` pour nous assurer que nous téléchargeons la dernière version du modèle :
+
+```py
+trainer.push_to_hub(commit_message="Training complete")
+```
+
+Cela renvoie l'URL du commit qu'il vient de faire, si vous voulez l'inspecter :
+
+```python out
+'https://huggingface.co/sgugger/bert-finetuned-squad/commit/9dcee1fbc25946a6ed4bb32efb1bd71d5fa90b68'
+```
+
+Le `Trainer` rédige également une fiche modèle avec tous les résultats de l'évaluation et la télécharge.
+
+{/if}
+
+À ce stade, vous pouvez utiliser le widget d'inférence sur le *Hub* du modèle pour tester le modèle et le partager avec vos amis, votre famille et vos animaux préférés. Vous avez réussi à *finetuner* un modèle sur une tâche de réponse à une question - félicitations !
+
+
+
+✏️ **Votre tour** Essayez un autre modèle d'architecture pour voir s'il est plus performant dans cette tâche !
+
+
+
+{#if fw === 'pt'}
+
+Si vous voulez plonger un peu plus profondément dans la boucle d'entraînement, nous allons maintenant vous montrer comment faire la même chose en utilisant 🤗 *Accelerate*.
+
+## Une boucle d'entraînement personnalisée
+
+Jetons maintenant un coup d'œil à la boucle d'entraînement complète, afin que vous puissiez facilement personnaliser les parties dont vous avez besoin. Elle ressemblera beaucoup à la boucle d'entraînement du [Chapitre 3](/course/fr/chapter3/4), à l'exception de la boucle d'évaluation. Nous serons en mesure d'évaluer le modèle régulièrement puisque nous ne sommes plus contraints par la classe `Trainer`.
+
+### Préparer tout pour l'entraînement
+
+Tout d'abord, nous devons construire le `DataLoader`s à partir de nos jeux de données. Nous définissons le format de ces jeux de données à `"torch"`, et supprimons les colonnes dans le jeu de validation qui ne sont pas utilisées par le modèle. Ensuite, nous pouvons utiliser le `default_data_collator` fourni par Transformers comme `collate_fn` et mélanger l'ensemble d'entraînement, mais pas l'ensemble de validation :
+
+```py
+from torch.utils.data import DataLoader
+from transformers import default_data_collator
+
+train_dataset.set_format("torch")
+validation_set = validation_dataset.remove_columns(["example_id", "offset_mapping"])
+validation_set.set_format("torch")
+
+train_dataloader = DataLoader(
+ train_dataset,
+ shuffle=True,
+ collate_fn=default_data_collator,
+ batch_size=8,
+)
+eval_dataloader = DataLoader(
+ validation_set, collate_fn=default_data_collator, batch_size=8
+)
+```
+
+Ensuite, nous réinstantifions notre modèle, afin de nous assurer que nous ne poursuivons pas les réglages fins précédents mais que nous repartons du modèle pré-entraîné de BERT :
+
+```py
+model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
+```
+
+Ensuite, nous aurons besoin d'un optimiseur. Comme d'habitude, nous utilisons le classique `AdamW`, qui est comme Adam, mais avec une correction dans la façon dont la décroissance du poids est appliquée :
+
+```py
+from torch.optim import AdamW
+
+optimizer = AdamW(model.parameters(), lr=2e-5)
+```
+
+Une fois que nous avons tous ces objets, nous pouvons les envoyer à la méthode `accelerator.prepare()`. Rappelez-vous que si vous voulez vous entraîner sur des TPUs dans un *notebook* de Colab, vous devrez déplacer tout ce code dans une fonction d'entraînement, et qui ne devrait pas exécuter une cellule qui instancie un `Accelerator`. Nous pouvons forcer l'entraînement en précision mixte en passant `fp16=True` à l'`Accelerator` (ou, si vous exécutez le code comme un script, assurez-vous de remplir la 🤗 *Accelerate* `config` de manière appropriée).
+
+```py
+from accelerate import Accelerator
+
+accelerator = Accelerator(fp16=True)
+model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
+ model, optimizer, train_dataloader, eval_dataloader
+)
+```
+
+Comme vous devez le savoir depuis les sections précédentes, nous ne pouvons utiliser la longueur de `train_dataloader` pour calculer le nombre d'étapes d'entraînement qu'après qu'il soit passé par la méthode `accelerator.prepare()`. Nous utilisons le même programme linéaire que dans les sections précédentes :
+
+```py
+from transformers import get_scheduler
+
+num_train_epochs = 3
+num_update_steps_per_epoch = len(train_dataloader)
+num_training_steps = num_train_epochs * num_update_steps_per_epoch
+
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+```
+
+Pour pousser notre modèle vers le Hub, nous aurons besoin de créer un objet `Repository` dans un dossier de travail. Tout d'abord, connectez-vous au Hugging Face Hub, si vous n'êtes pas déjà connecté. Nous déterminerons le nom du dépôt à partir de l'ID du modèle que nous voulons donner à notre modèle (n'hésitez pas à remplacer le `repo_name` par votre propre choix ; il doit juste contenir votre nom d'utilisateur, ce que fait la fonction `get_full_repo_name()`) :
+
+```py
+from huggingface_hub import Repository, get_full_repo_name
+
+model_name = "bert-finetuned-squad-accelerate"
+repo_name = get_full_repo_name(model_name)
+repo_name
+```
+
+```python out
+'sgugger/bert-finetuned-squad-accelerate'
+```
+
+Ensuite, nous pouvons cloner ce référentiel dans un dossier local. S'il existe déjà, ce dossier local doit être un clone du référentiel avec lequel nous travaillons :
+
+```py
+output_dir = "bert-finetuned-squad-accelerate"
+repo = Repository(output_dir, clone_from=repo_name)
+```
+
+Nous pouvons maintenant télécharger tout ce que nous sauvegardons dans `output_dir` en appelant la méthode `repo.push_to_hub()`. Cela nous aidera à télécharger les modèles intermédiaires à la fin de chaque époque.
+
+## Boucle d'entraînement
+
+Nous sommes maintenant prêts à écrire la boucle d'entraînement complète. Après avoir défini une barre de progression pour suivre l'évolution de l'entraînement, la boucle comporte trois parties :
+
+- l'entraînement proprement dit, qui est l'itération classique sur le `train_dataloader`, passage en avant du modèle, puis passage en arrière et étape d'optimisation.
+- l'évaluation, dans laquelle nous rassemblons toutes les valeurs pour `start_logits` et `end_logits` avant de les convertir en tableaux NumPy. Une fois la boucle d'évaluation terminée, nous concaténons tous les résultats. Notez que nous devons tronquer parce que l' `Accelerator` peut avoir ajouté quelques échantillons à la fin pour s'assurer que nous avons le même nombre d'exemples dans chaque processus.
+- sauvegarde et téléchargement, où nous sauvegardons d'abord le modèle et le *tokenizer*, puis appelons `repo.push_to_hub()`. Comme nous l'avons fait auparavant, nous utilisons l'argument `blocking=False` pour dire à la bibliothèque 🤗 *Hub* de pousser dans un processus asynchrone. De cette façon, l'entraînement continue normalement et cette (longue) instruction est exécutée en arrière-plan.
+
+Voici le code complet de la boucle d'entraînement :
+
+```py
+from tqdm.auto import tqdm
+import torch
+
+progress_bar = tqdm(range(num_training_steps))
+
+for epoch in range(num_train_epochs):
+ # Entraînement
+ model.train()
+ for step, batch in enumerate(train_dataloader):
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+
+ # Evaluation
+ model.eval()
+ start_logits = []
+ end_logits = []
+ accelerator.print("Evaluation!")
+ for batch in tqdm(eval_dataloader):
+ with torch.no_grad():
+ outputs = model(**batch)
+
+ start_logits.append(accelerator.gather(outputs.start_logits).cpu().numpy())
+ end_logits.append(accelerator.gather(outputs.end_logits).cpu().numpy())
+
+ start_logits = np.concatenate(start_logits)
+ end_logits = np.concatenate(end_logits)
+ start_logits = start_logits[: len(validation_dataset)]
+ end_logits = end_logits[: len(validation_dataset)]
+
+ metrics = compute_metrics(
+ start_logits, end_logits, validation_dataset, raw_datasets["validation"]
+ )
+ print(f"epoch {epoch}:", metrics)
+
+ # Sauvegarder et télécharger
+ accelerator.wait_for_everyone()
+ unwrapped_model = accelerator.unwrap_model(model)
+ unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
+ if accelerator.is_main_process:
+ tokenizer.save_pretrained(output_dir)
+ repo.push_to_hub(
+ commit_message=f"Training in progress epoch {epoch}", blocking=False
+ )
+```
+
+Au cas où ce serait la première fois que vous verriez un modèle enregistré avec 🤗 *Accelerate*, prenons un moment pour inspecter les trois lignes de code qui l'accompagnent :
+
+```py
+accelerator.wait_for_everyone()
+unwrapped_model = accelerator.unwrap_model(model)
+unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
+```
+
+La première ligne est explicite : elle indique à tous les processus d'attendre que tout le monde soit à ce stade avant de continuer. C'est pour s'assurer que nous avons le même modèle dans chaque processus avant de sauvegarder. Ensuite, nous prenons le `unwrapped_model`, qui est le modèle de base que nous avons défini. La méthode `accelerator.prepare()` modifie le modèle pour qu'il fonctionne dans l'entraînement distribué, donc il n'aura plus la méthode `save_pretrained()` ; la méthode `accelerator.unwrap_model()` annule cette étape. Enfin, nous appelons `save_pretrained()` mais nous disons à cette méthode d'utiliser `accelerator.save()` au lieu de `torch.save()`.
+
+Une fois ceci fait, vous devriez avoir un modèle qui produit des résultats assez similaires à celui entraîné avec le `Trainer`. Vous pouvez vérifier le modèle que nous avons entraîné en utilisant ce code à [*huggingface-course/bert-finetuned-squad-accelerate*](https://huggingface.co/huggingface-course/bert-finetuned-squad-accelerate). Et si vous voulez tester des modifications de la boucle d'entraînement, vous pouvez les implémenter directement en modifiant le code ci-dessus !
+
+{/if}
+
+### Utilisation du modèle *finetuné*
+
+Nous vous avons déjà montré comment vous pouvez utiliser le modèle que nous avons *finetuné* sur le *Hub* avec le widget d'inférence. Pour l'utiliser localement dans un `pipeline`, il suffit de spécifier l'identifiant du modèle :
+
+```py
+from transformers import pipeline
+
+# Replace this with your own checkpoint
+model_checkpoint = "huggingface-course/bert-finetuned-squad"
+question_answerer = pipeline("question-answering", model=model_checkpoint)
+
+context = """
+🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration
+between them. It's straightforward to train your models with one before loading them for inference with the other.
+"""
+question = "Which deep learning libraries back 🤗 Transformers?"
+question_answerer(question=question, context=context)
+```
+
+```python out
+{'score': 0.9979003071784973,
+ 'start': 78,
+ 'end': 105,
+ 'answer': 'Jax, PyTorch and TensorFlow'}
+```
+
+Super ! Notre modèle fonctionne aussi bien que le modèle par défaut pour ce pipeline !
diff --git a/chapters/fr/chapter8/1.mdx b/chapters/fr/chapter8/1.mdx
index 0c32596a0..441ea1969 100644
--- a/chapters/fr/chapter8/1.mdx
+++ b/chapters/fr/chapter8/1.mdx
@@ -4,9 +4,9 @@ Maintenant que vous savez comment aborder les tâches de NLP les plus courantes
Plus précisément, dans ce chapitre vous allez apprendre :
-- la première chose à faire lorsque vous obtenez une erreur.
-- comment demander de l'aide sur le [forum](https://discuss.huggingface.co/)
-- comment déboguer votre pipeline d'entraînement
-- comment rédiger une bonne *issue*
+- la première chose à faire lorsque vous obtenez une erreur,
+- comment demander de l'aide sur le [forum](https://discuss.huggingface.co/),
+- comment déboguer votre pipeline d'entraînement,
+- comment rédiger une bonne *issue*.
Rien de tout cela n'est spécifiquement lié à 🤗 *Transformers* ou à l'écosystème Hugging Face. Les leçons de ce chapitre sont applicables à la plupart des projets open source !
diff --git a/chapters/fr/chapter8/2.mdx b/chapters/fr/chapter8/2.mdx
index 39f70542d..76abf7488 100644
--- a/chapters/fr/chapter8/2.mdx
+++ b/chapters/fr/chapter8/2.mdx
@@ -7,11 +7,11 @@
{label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/chapter8/section2.ipynb"},
]} />
-Dans cette section, nous allons examiner certaines erreurs courantes qui peuvent se produire lorsque vous essayez de générer des prédictions à partir de votre *transformer* fraîchement *tuné*. Cela vous préparera pour la [section 4](/course/chapter8/fr/section4), où nous explorerons comment déboguer la phase d'entraînement elle-même.
+Dans cette section, nous allons examiner certaines erreurs courantes qui peuvent se produire lorsque vous essayez de générer des prédictions à partir de votre *transformer* fraîchement *finetuné*. Cela vous préparera pour la [section 4](/course/chapter8/fr/section4) de ce chapitre où nous explorerons comment déboguer la phase d'entraînement elle-même.
-Nous avons préparé un [dépôt de modèles](https://huggingface.co/lewtun/distilbert-base-uncased-finetuned-squad-d5716d28) pour cette section, et si vous voulez exécuter le code de ce chapitre, vous devrez d'abord copier le modèle dans votre compte sur le [*Hub* d'Hugging Face](https://huggingface.co). Pour ce faire, connectez-vous d'abord en exécutant l'une ou l'autre des commandes suivantes dans un *notebook* Jupyter :
+Nous avons préparé un gabarit de [dépôt de modèles](https://huggingface.co/lewtun/distilbert-base-uncased-finetuned-squad-d5716d28) pour cette section et si vous voulez exécuter le code de ce chapitre, vous devrez d'abord copier le modèle dans votre compte sur le [*Hub* d'Hugging Face](https://huggingface.co). Pour ce faire, connectez-vous d'abord en exécutant l'une ou l'autre des commandes suivantes dans un *notebook* Jupyter :
```python
from huggingface_hub import notebook_login
@@ -25,7 +25,7 @@ ou ce qui suit dans votre terminal préféré :
huggingface-cli login
```
-Cela vous demandera d'entrer votre nom d'utilisateur et votre mot de passe, et enregistrera un jeton sous *~/.cache/huggingface/*. Une fois que vous vous êtes connecté, vous pouvez copier le dépôt de modèles avec la fonction suivante :
+Cela vous demandera d'entrer votre nom d'utilisateur et votre mot de passe, et enregistrera un jeton sous *~/.cache/huggingface/*. Une fois que vous vous êtes connecté, vous pouvez copier le gabarit du dépôt avec la fonction suivante :
```python
from distutils.dir_util import copy_tree
@@ -50,15 +50,15 @@ def copy_repository_template():
repo.push_to_hub()
```
-Maintenant, lorsque vous appelez `copy_repository_template()`, cela va créer une copie du dépôt de modèles sous votre compte.
+Maintenant, lorsque vous appelez `copy_repository_template()`, cela va créer une copie du gabarit du dépôt sous votre compte.
-## Déboguer le pipeline à partir de 🤗 *Transformers*
+## Déboguer le pipeline à partir de 🤗 Transformers
-Pour donner le coup d'envoi de notre voyage dans le monde merveilleux du débogage des *transformers*, considérez le scénario suivant : vous travaillez avec un collègue sur un projet de réponse à des questions pour aider les clients d'un site de commerce électronique à trouver des réponses sur des produits de consommation. Votre collègue vous envoie un message du genre :
+Pour donner le coup d'envoi de notre voyage dans le monde merveilleux du débogage de *transformers*, considérez le scénario suivant : vous travaillez avec un collègue sur un projet de réponse à des questions pour aider les clients d'un site de commerce en ligne à trouver des réponses à des produits. Votre collègue vous envoie un message du genre :
-> Bonjour ! Je viens de réaliser une expérience en utilisant les techniques du [chapitre 7](/course/fr/chapiter7/7) du cours d'Hugging Face et j'ai obtenu d'excellents résultats sur SQuAD ! Je pense que nous pouvons utiliser ce modèle comme point de départ pour notre projet. L'ID du modèle sur le Hub est "lewtun/distillbert-base-uncased-finetuned-squad-d5716d28". N'hésitez pas à le tester :)
+> Bonjour ! Je viens de réaliser une expérience en utilisant les techniques du [chapitre 7](/course/fr/chapiter7/7) du cours d'Hugging Face et j'ai obtenu d'excellents résultats sur SQuAD ! Je pense que nous pouvons utiliser ce modèle comme point de départ pour notre projet. L'identifiant du modèle sur le *Hub* est "lewtun/distillbert-base-uncased-finetuned-squad-d5716d28". N'hésite pas à le tester :)
-et la première chose à laquelle on pense est de charger le modèle en utilisant la `pipeline` de 🤗 *Transformers* :
+et la première chose à laquelle on pense est de charger le modèle en utilisant le `pipeline` de 🤗 *Transformers* :
```python
from transformers import pipeline
@@ -77,21 +77,21 @@ OSError: Can't load config for 'lewtun/distillbert-base-uncased-finetuned-squad-
"""
```
-Oh non, quelque chose semble avoir mal tourné ! Si vous êtes novice en programmation, ce genre d'erreurs peut sembler un peu cryptique au début (qu'est-ce qu'un `OSError` ?!). L'erreur affichée ici n'est que la dernière partie d'un rapport d'erreur beaucoup plus large appelé _Python traceback_ (alias *stack trace*). Par exemple, si vous exécutez ce code sur Google Colab, vous devriez voir quelque chose comme la capture d'écran suivante :
+Oh non, quelque chose semble s'être mal passée ! Si vous êtes novice en programmation, ce genre d'erreurs peut sembler un peu cryptique au début (qu'est-ce qu'une `OSError` ?!). L'erreur affichée ici n'est que la dernière partie d'un rapport d'erreur beaucoup plus long appelé _Python traceback_ (alias *stack trace*). Par exemple, si vous exécutez ce code sur Google Colab, vous devriez voir quelque chose comme la capture d'écran suivante :
-Il y a beaucoup d'informations dans ces rapports, nous allons donc en parcourir ensemble les éléments clés. La première chose à noter est que les rapports de traçage doivent être lus _de bas en haut_. Cela peut sembler bizarre si vous avez l'habitude de lire du texte anglais de haut en bas, mais cela reflète le fait que le *traceback* montre la séquence d'appels de fonction que la `pipeline` fait lors du téléchargement du modèle et du *tokenizer*. Consultez le [Chapitre 2](/course/fr/chapter2) pour plus de détails sur la façon dont le `pipeline` fonctionne sous le capot.
+Il y a beaucoup d'informations dans ces rapports, nous allons donc en parcourir ensemble les éléments clés. La première chose à noter est que les *tracebacks* doivent être lus _de bas en haut_. Cela peut sembler bizarre si vous avez l'habitude de lire du texte français de haut en bas mais cela reflète le fait que le *traceback* montre la séquence d'appels de fonction que le `pipeline` fait lors du téléchargement du modèle et du *tokenizer*. Consultez le [chapitre 2](/course/fr/chapter2) pour plus de détails sur la façon dont le `pipeline` fonctionne sous le capot.
-🚨 Vous voyez le cadre bleu autour de "6 frames" dans lle *traceback* de Google Colab ? Il s'agit d'une fonctionnalité spéciale de Colab, qui compresse la trace en "frames". Si vous ne parvenez pas à trouver la source d'une erreur, veillez à développer la trace complète en cliquant sur ces deux petites flèches.
+🚨 Vous voyez le cadre bleu autour de « 6 frames » dans le traceback de Google Colab ? Il s'agit d'une fonctionnalité spéciale de Colab qui compresse le traceback en frames. Si vous ne parvenez pas à trouver la source d'une erreur, déroulez le traceback en cliquant sur ces deux petites flèches.
-Cela signifie que la dernière ligne de la trace indique le dernier message d'erreur et donne le nom de l'exception qui a été levée. Dans ce cas, le type d'exception est `OSError`, ce qui indique une erreur liée au système. Si nous lisons le message d'erreur qui l'accompagne, nous pouvons voir qu'il semble y avoir un problème avec le fichier *config.json* du modèle, et deux suggestions nous sont données pour le résoudre :
+Cela signifie que la dernière ligne du traceback indique le dernier message d'erreur et donne le nom de l'exception qui a été levée. Dans ce cas, le type d'exception est `OSError`, ce qui indique une erreur liée au système. Si nous lisons le message d'erreur qui l'accompagne, nous pouvons voir qu'il semble y avoir un problème avec le fichier *config.json* du modèle et deux suggestions nous sont données pour le résoudre :
```python out
"""
@@ -105,23 +105,23 @@ Make sure that:
-💡 Si vous rencontrez un message d'erreur difficile à comprendre, copiez et collez le message dans Google ou sur [Stack Overflow](https://stackoverflow.com/) (oui, vraiment !). Il y a de fortes chances que vous ne soyez pas la première personne à rencontrer cette erreur, et c'est un bon moyen de trouver des solutions que d'autres membres de la communauté ont publiées. Par exemple, en recherchant `OSError : Can't load config for` sur Stack Overflow donne plusieurs [réponses](https://stackoverflow.com/search?q=OSError%3A+Can%27t+load+config+for+) qui peuvent être utilisées comme point de départ pour résoudre le problème.
+💡 Si vous rencontrez un message d'erreur difficile à comprendre, copiez et collez le message dans Google ou sur [Stack Overflow](https://stackoverflow.com/) (oui, vraiment !). Il y a de fortes chances que vous ne soyez pas la première personne à rencontrer cette erreur et c'est un bon moyen de trouver des solutions que d'autres membres de la communauté ont publiées. Par exemple, en recherchant `OSError : Can't load config for` sur Stack Overflow donne plusieurs [réponses](https://stackoverflow.com/search?q=OSError%3A+Can%27t+load+config+for+) qui peuvent être utilisées comme point de départ pour résoudre le problème.
-La première suggestion nous demande de vérifier si l'identifiant du modèle est effectivement correct, la première chose à faire est donc de copier l'identifiant et de le coller dans la barre de recherche du Hub :
+La première suggestion nous demande de vérifier si l'identifiant du modèle est effectivement correct, la première chose à faire est donc de copier l'identifiant et de le coller dans la barre de recherche du *Hub* :
-Hmm, il semble en effet que le modèle de notre collègue ne soit pas sur le Hub... Mais il y a une faute de frappe dans le nom du modèle ! DistilBERT n'a qu'un seul "l" dans son nom, alors corrigeons cela et cherchons "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28" à la place :
+Hmm, il semble en effet que le modèle de notre collègue ne soit pas sur le *Hub*... Mais il y a une faute de frappe dans le nom du modèle ! DistilBERT n'a qu'un seul « l » dans son nom alors corrigeons cela et cherchons « lewtun/distilbert-base-uncased-finetuned-squad-d5716d28 » à la place :
-Ok, ça a marché. Maintenant essayons de télécharger à nouveau le modèle avec l'ID correct du modèle :
+Ok, ça a marché. Maintenant essayons de télécharger à nouveau le modèle avec le bon identifiant :
```python
model_checkpoint = get_full_repo_name("distilbert-base-uncased-finetuned-squad-d5716d28")
@@ -138,7 +138,7 @@ OSError: Can't load config for 'lewtun/distilbert-base-uncased-finetuned-squad-d
"""
```
-Argh, encore un échec. Bienvenue dans la vie quotidienne d'un ingénieur en apprentissage machine ! Puisque nous avons corrigé l'ID du modèle, le problème doit se situer dans le référentiel lui-même. Une façon rapide d'accéder au contenu d'un dépôt sur le 🤗 *Hub* est via la fonction `list_repo_files()` de la bibliothèque `huggingface_hub` :
+Argh, encore un échec. Bienvenue dans la vie quotidienne d'un ingénieur en apprentissage machine ! Puisque nous avons corrigé l'identifiant du modèle, le problème doit se situer dans le dépôt lui-même. Une façon rapide d'accéder au contenu d'un dépôt sur le 🤗 *Hub* est via la fonction `list_repo_files()` de la bibliothèque `huggingface_hub` :
```python
from huggingface_hub import list_repo_files
@@ -150,7 +150,7 @@ list_repo_files(repo_id=model_checkpoint)
['.gitattributes', 'README.md', 'pytorch_model.bin', 'special_tokens_map.json', 'tokenizer_config.json', 'training_args.bin', 'vocab.txt']
```
-Intéressant. Il ne semble pas y avoir de fichier *config.json* dans le référentiel ! Pas étonnant que notre `pipeline` n'ait pas pu charger le modèle ; notre collègue a dû oublier de pousser ce fichier vers le *Hub* après l'avoir mis au point. Dans ce cas, le problème semble assez simple à résoudre : nous pouvons lui demander d'ajouter le fichier, ou, puisque nous pouvons voir à partir de l'ID du modèle que le modèle pré-entraîné utilisé est [`distilbert-base-uncased`](https://huggingface.co/distilbert-base-uncased), nous pouvons télécharger la configuration de ce modèle et la pousser dans notre dépôt pour voir si cela résout le problème. Essayons cela. En utilisant les techniques apprises dans le [Chapitre 2](/course/fr/chapter2), nous pouvons télécharger la configuration du modèle avec la classe `AutoConfig` :
+Intéressant. Il ne semble pas y avoir de fichier *config.json* dans le dépôt ! Pas étonnant que notre `pipeline` n'ait pas pu charger le modèle. Notre collègue a dû oublier de pousser ce fichier vers le *Hub* après l'avoir *finetuné*. Dans ce cas, le problème semble assez simple à résoudre : nous pouvons lui demander d'ajouter le fichier, ou, puisque nous pouvons voir à partir de l'identifiant du modèle que le modèle pré-entraîné utilisé est [`distilbert-base-uncased`](https://huggingface.co/distilbert-base-uncased), nous pouvons télécharger la configuration de ce modèle et la pousser dans notre dépôt pour voir si cela résout le problème. Essayons cela. En utilisant les techniques apprises dans le [chapitre 2](/course/fr/chapter2), nous pouvons télécharger la configuration du modèle avec la classe `AutoConfig` :
```python
from transformers import AutoConfig
@@ -161,7 +161,7 @@ config = AutoConfig.from_pretrained(pretrained_checkpoint)
-🚨 L'approche que nous adoptons ici n'est pas infaillible, puisque notre collègue peut avoir modifié la configuration de `distilbert-base-uncased` avant d'affiner le modèle. Dans la vie réelle, nous voudrions vérifier avec lui d'abord, mais pour les besoins de cette section, nous supposerons qu'il a utilisé la configuration par défaut.
+🚨 L'approche que nous adoptons ici n'est pas infaillible puisque notre collègue peut avoir modifié la configuration de `distilbert-base-uncased` avant de finetuner le modèle. Dans la vie réelle, nous voudrions vérifier avec lui d'abord, mais pour les besoins de cette section nous supposerons qu'il a utilisé la configuration par défaut.
@@ -171,7 +171,7 @@ Nous pouvons ensuite le pousser vers notre dépôt de modèles avec la fonction
config.push_to_hub(model_checkpoint, commit_message="Add config.json")
```
-Maintenant, nous pouvons tester si cela a fonctionné en chargeant le modèle depuis le dernier commit de la branche `main` :
+Maintenant, nous pouvons tester si cela a fonctionné en chargeant le modèle depuis le dernier *commit* de la branche `main` :
```python
reader = pipeline("question-answering", model=model_checkpoint, revision="main")
@@ -189,16 +189,17 @@ frameworks, so you can use your favourite tools for a wide variety of tasks!
context_fr = r"""
La réponse à des questions consiste à extraire une réponse d'un texte
-à partir d'une question. Un exemple de jeu de données de réponse aux questions est le jeu de données SQuAD
-qui est entièrement basé sur cette tâche. Si vous souhaitez affiner un modèle
-modèle sur une tâche SQuAD, vous pouvez utiliser le fichier
+à partir d'une question. Un exemple de jeu de données de réponse aux questions est le
+jeu de données SQuAD qui est entièrement basé sur cette tâche. Si vous souhaitez finetuner
+un modèle sur une tâche SQuAD, vous pouvez utiliser le fichier
exemples/pytorch/question-answering/run_squad.py.
🤗 Transformers est interopérable avec les frameworks PyTorch, TensorFlow et JAX.
de sorte que vous pouvez utiliser vos outils préférés pour une grande variété de tâches !
"""
-question = "What is extractive question answering?" # Qu'est-ce que la réponse extractive aux questions ?
+question = "What is extractive question answering?"
+# Qu'est-ce que la réponse extractive aux questions ?
reader(question=question, context=context)
```
@@ -206,23 +207,22 @@ reader(question=question, context=context)
{'score': 0.38669535517692566,
'start': 34,
'end': 95,
- 'answer': 'the task of extracting an answer from a text given a question'} # la tâche consistant à extraire une réponse d'un texte à partir d'une question.
+ 'answer': 'the task of extracting an answer from a text given a question'}
+ # la tâche consistant à extraire une réponse d'un texte à partir d'une question.
```
Woohoo, ça a marché ! Récapitulons ce que vous venez d'apprendre :
- les messages d'erreur en Python sont appelés _tracebacks_ et sont lus de bas en haut. La dernière ligne du message d'erreur contient généralement les informations dont vous avez besoin pour localiser la source du problème,
-- si la dernière ligne ne contient pas suffisamment d'informations, remontez dans la traceback et voyez si vous pouvez identifier où l'erreur se produit dans le code source,
-- si aucun des messages d'erreur ne peut vous aider à déboguer le problème, essayez de rechercher en ligne une solution à un problème similaire.
-- l'`huggingface_hub`,
-// 🤗 *Hub* ?
-fournit une suite d'outils que vous pouvez utiliser pour interagir avec et déboguer les dépôts sur le *Hub*.
+- si la dernière ligne ne contient pas suffisamment d'informations, remontez dans le *traceback* et voyez si vous pouvez identifier où l'erreur se produit dans le code source,
+- si aucun des messages d'erreur ne peut vous aider à déboguer le problème, essayez de rechercher en ligne une solution à un problème similaire,
+- l'`huggingface_hub` fournit une suite d'outils que vous pouvez utiliser pour interagir avec et déboguer les dépôts sur le *Hub*.
Maintenant que vous savez comment déboguer un pipeline, examinons un exemple plus délicat dans la passe avant du modèle lui-même.
## Déboguer la passe avant de votre modèle
-Bien que le `pipeline` soit parfait pour la plupart des applications où vous devez générer rapidement des prédictions, vous aurez parfois besoin d'accéder aux logits du modèle (par exemple, si vous avez un post-traitement personnalisé que vous souhaitez appliquer). Pour voir ce qui peut mal tourner dans ce cas, commençons par récupérer le modèle et le *tokenizer* de notre `pipeline` :
+Bien que le `pipeline` soit parfait pour la plupart des applications où vous devez générer rapidement des prédictions, vous aurez parfois besoin d'accéder aux logits du modèle (par exemple si vous avez un post-traitement personnalisé que vous souhaitez appliquer). Pour voir ce qui peut mal tourner dans ce cas, commençons par récupérer le modèle et le *tokenizer* de notre `pipeline` :
```python
tokenizer = reader.tokenizer
@@ -232,10 +232,10 @@ model = reader.model
Ensuite, nous avons besoin d'une question, alors voyons si nos *frameworks* préférés sont supportés :
```python
-question = "Which frameworks can I use?"
+question = "Which frameworks can I use?" # Quel frameworks puis-je utiliser ?
```
-Comme nous l'avons vu dans le [Chapitre 7](/course/fr/chapter7), les étapes habituelles que nous devons suivre sont la tokénisation des entrées, l'extraction des logits des *tokens* de début et de fin, puis le décodage de l'empan de réponse :
+Comme nous l'avons vu dans le [chapitre 7](/course/fr/chapter7), les étapes habituelles que nous devons suivre sont la tokénisation des entrées, l'extraction des logits des *tokens* de début et de fin, puis le décodage de l'étendue de la réponse :
```python
import torch
@@ -245,9 +245,9 @@ input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
-# Obtenez le début de réponse le plus probable avec l'argmax du score.
+# Pour obtenir le début de réponse le plus probable avec l'argmax du score
answer_start = torch.argmax(answer_start_scores)
-# Obtenir la fin de réponse la plus probable avec l'argmax du score
+# Pour obtenir la fin de réponse la plus probable avec l'argmax du score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
@@ -299,7 +299,7 @@ AttributeError: 'list' object has no attribute 'size'
"""
```
-Oh là là, il semble que nous ayons un bug dans notre code ! Mais nous n'avons pas peur d'un petit débogage. Vous pouvez utiliser le débogueur Python dans un *notebook* :
+Il semble que nous ayons un *bug* dans notre code ! Mais il ne nous fait pas peur. Nous pouvons utiliser le débogueur Python dans un *notebook* :
@@ -317,7 +317,7 @@ inputs["input_ids"][:5]
[101, 2029, 7705, 2015, 2064]
```
-Cela ressemble certainement à une `list` ordinaire de Python, mais vérifions le type :
+Cela ressemble certainement à une `list` ordinaire en Python mais vérifions le type :
```python
type(inputs["input_ids"])
@@ -327,7 +327,7 @@ type(inputs["input_ids"])
list
```
-Oui, c'est bien une `list` Python. Alors, qu'est-ce qui a mal tourné ? Rappelez-vous du [Chapitre 2](/course/fr/chapter2) que les classes `AutoModelForXxx` dans 🤗 Transformers opèrent sur des _tenseurs_ (soit dans PyTorch ou TensorFlow), et une opération commune est d'extraire les dimensions d'un tenseur en utilisant `Tensor.size()` dans, disons, PyTorch. Jetons un autre coup d'oeil à la *traceback*, pour voir quelle ligne a déclenché l'exception :
+Oui, c'est bien une `list` Python. Alors, qu'est-ce qui a mal tourné ? Rappelez-vous que dans le [chapitre 2](/course/fr/chapter2) nous avons vu que les classes `AutoModelForXxx` opèrent sur des _tenseurs_ (soit dans PyTorch ou TensorFlow) et qu'une opération commune est d'extraire les dimensions d'un tenseur en utilisant `Tensor.size()`. Jetons un autre coup d'oeil au *traceback* pour voir quelle ligne a déclenché l'exception :
```
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
@@ -340,7 +340,7 @@ Oui, c'est bien une `list` Python. Alors, qu'est-ce qui a mal tourné ? Rappelez
AttributeError: 'list' object has no attribute 'size'
```
-Il semble que notre code ait essayé d'appeler `input_ids.size()`, mais cela ne fonctionne clairement pas pour une `liste` Python, qui est juste un conteneur. Comment pouvons-nous résoudre ce problème ? La recherche du message d'erreur sur Stack Overflow donne quelques [réponses](https://stackoverflow.com/search?q=AttributeError%3A+%27list%27+object+has+no+attribute+%27size%27&s=c15ec54c-63cb-481d-a749-408920073e8f) pertinentes. En cliquant sur la première, une question similaire à la nôtre s'affiche, avec la réponse indiquée dans la capture d'écran ci-dessous :
+Il semble que notre code ait essayé d'appeler `input_ids.size()`, mais cela ne fonctionne clairement pas pour une `list` Python qui est juste un conteneur. Comment pouvons-nous résoudre ce problème ? La recherche du message d'erreur sur Stack Overflow donne quelques [réponses](https://stackoverflow.com/search?q=AttributeError%3A+%27list%27+object+has+no+attribute+%27size%27&s=c15ec54c-63cb-481d-a749-408920073e8f) pertinentes. En cliquant sur la première, une question similaire à la nôtre s'affiche, avec la réponse indiquée dans la capture d'écran ci-dessous :
@@ -354,9 +354,9 @@ input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
-# Obtenez le début de réponse le plus probable avec l'argmax du score.
+# Pour obtenir le début de réponse le plus probable avec l'argmax du score
answer_start = torch.argmax(answer_start_scores)
-# Obtenir la fin de réponse la plus probable avec l'argmax du score
+# Pour obtenir la fin de réponse la plus probable avec l'argmax du score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
@@ -372,4 +372,4 @@ Answer: pytorch, tensorflow, and jax # pytorch, tensorflow et jax
"""
```
-Super, ça a marché ! Voilà un excellent exemple de l'utilité de Stack Overflow : en identifiant un problème similaire, nous avons pu bénéficier de l'expérience d'autres membres de la communauté. Cependant, une recherche de ce type ne donne pas toujours une réponse pertinente, alors que faire dans ce cas ? Heureusement, il existe une communauté accueillante de développeurs sur le [forum d'Hugging Face](https://discuss.huggingface.co/) qui peut vous aider ! Dans la prochaine section, nous verrons comment rédiger de bonnes questions de forum qui ont des chances d'obtenir une réponse.
+Super, ça a marché ! Voilà un excellent exemple de l'utilité de Stack Overflow : en identifiant un problème similaire, nous avons pu bénéficier de l'expérience d'autres membres de la communauté. Cependant, une recherche de ce type ne donne pas toujours une réponse pertinente. Que faire alors dans ce cas ? Heureusement, il existe une communauté accueillante de développeurs sur le [forum d'Hugging Face](https://discuss.huggingface.co/) qui peut vous aider ! Dans la prochaine section, nous verrons comment rédiger de bonnes questions sur les forums pour avoir de bonnes chances d'obtenir une réponse.
diff --git a/chapters/fr/chapter8/3.mdx b/chapters/fr/chapter8/3.mdx
index 526fe482b..a75b49f95 100644
--- a/chapters/fr/chapter8/3.mdx
+++ b/chapters/fr/chapter8/3.mdx
@@ -9,23 +9,23 @@
-Le [forum d'Hugging Face](https://discuss.huggingface.co) est un endroit idéal pour obtenir de l'aide de l'équipe open source et de la communauté Hugging Face au sens large. Voici à quoi ressemble la page principale tous les jours :
+Le [forum d'Hugging Face](https://discuss.huggingface.co) est un endroit idéal pour obtenir de l'aide de l'équipe open source d'Hugging Face et de la communauté au sens large. Voici à quoi ressemble la page principale :
-ODans la partie gauche, vous pouvez voir toutes les catégories dans lesquelles les différents sujets sont regroupés, tandis que la partie droite montre les sujets les plus récents. Un sujet est un message qui contient un titre, une catégorie et une description ; il est assez similaire au format des *issues* GitHub que nous avons vu lors de la création de notre propre jeu de données dans [Chapitre 5](/course/fr/chapter5). Comme son nom l'indique, la catégorie [*Beginners*](https://discuss.huggingface.co/c/beginners/5) est principalement destinée aux personnes qui débutent avec les bibliothèques et l'écosystème Hugging Face. Toute question sur l'une des bibliothèques est la bienvenue ici, que ce soit pour déboguer du code ou pour demander de l'aide sur la façon de faire quelque chose. (Cela dit, si votre question concerne une bibliothèque en particulier, vous devriez probablement vous diriger vers la catégorie de bibliothèque correspondante sur le forum).
+Dans la partie gauche, vous pouvez voir toutes les catégories dans lesquelles les différents sujets sont regroupés, tandis que la partie droite montre les sujets les plus récents. Un sujet est un message qui contient un titre, une catégorie et une description. C'est assez similaire au format des *issues* GitHub que nous avons vu lors de la création de notre propre jeu de données dans le [chapitre 5](/course/fr/chapter5). Comme son nom l'indique, la catégorie [*Beginners*](https://discuss.huggingface.co/c/beginners/5) est principalement destinée aux personnes qui débutent avec les bibliothèques et l'écosystème d'Hugging Face. Toute question sur l'une des bibliothèques est la bienvenue ici, que ce soit pour déboguer du code ou pour demander de l'aide sur la façon de faire quelque chose. (Cela dit, si votre question concerne une bibliothèque en particulier, vous devriez probablement vous diriger vers la catégorie de bibliothèque correspondante sur le forum).
-De même, les catégories [*Intermediate*](https://discuss.huggingface.co/c/intermediate/6) et [*Research*](https://discuss.huggingface.co/c/research/7) sont destinées aux questions plus avancées, par exemple sur les bibliothèques ou sur une avancée en recherche en NLP dont vous aimeriez discuter.
+De même, les catégories [*Intermediate*](https://discuss.huggingface.co/c/intermediate/6) et [*Research*](https://discuss.huggingface.co/c/research/7) sont destinées aux questions plus avancées. Par exemple sur les bibliothèques ou sur une avancée en recherche en NLP dont vous aimeriez discuter.
-Et naturellement, nous devrions aussi mentionner la catégorie [*Course*](https://discuss.huggingface.co/c/course/20), où vous pouvez poser toutes les questions que vous avez en rapport avec le cours d'Hugging Face !
+Et naturellement, nous devrions aussi mentionner la catégorie [*Course*](https://discuss.huggingface.co/c/course/20) où vous pouvez poser toutes les questions que vous avez en rapport avec le cours d'Hugging Face !
-Une fois que vous aurez choisi une catégorie, vous serez prêt à rédiger votre premier sujet. Vous pouvez trouver quelques [directives](https://discuss.huggingface.co/t/how-to-request-support/3128) dans le forum sur la façon de le faire, et dans cette section, nous allons jeter un coup d'oeil à certaines caractéristiques d'un bon sujet.
+Une fois une catégorie choisie, vous êtes prêt à rédiger votre premier sujet. Vous pouvez trouver quelques [indications](https://discuss.huggingface.co/t/how-to-request-support/3128) dans le forum sur la façon de le faire. Dans cette section, nous allons jeter un coup d'oeil à certaines caractéristiques d'un bon sujet.
## Rédiger un bon message sur le forum
-A titre d'exemple, supposons que nous essayons de générer des embeddings à partir d'articles Wikipédia pour créer un moteur de recherche personnalisé. Comme d'habitude, nous chargeons le *tokenizer* et le modèle comme suit :
+A titre d'exemple, supposons que nous essayons de générer des enchâssements à partir d'articles Wikipédia pour créer un moteur de recherche personnalisé. Comme d'habitude, nous chargeons le *tokenizer* et le modèle comme suit :
```python
from transformers import AutoTokenizer, AutoModel
@@ -35,7 +35,7 @@ tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = AutoModel.from_pretrained(model_checkpoint)
```
-Supposons maintenant que nous essayons d'intégrer une section entière de l'[article Wikipedia](https://en.wikipedia.org/wiki/Transformers) sur Transformers (la franchise de films, pas la bibliothèque !):
+Supposons maintenant que nous essayons d'enchâsser une section entière de l'[article Wikipedia](https://en.wikipedia.org/wiki/Transformers) sur Transformers (la franchise de films, pas la bibliothèque !):
```python
text = """
@@ -82,44 +82,46 @@ Creation Matrix (giving life to Transformers), and its guardian Alpha Trion.
text_fr = """
Génération 1 est un terme rétroactif pour les personnages de Transformers qui sont apparus
-sont apparus entre 1984 et 1993. Les Transformers ont commencé avec les lignes de jouets japonaises des années 1980
-japonaises des années 1980, Micro Change et Diaclone. Elles présentaient des robots capables de se transformer
-en véhicules de tous les jours, en objets électroniques ou en armes. Hasbro a acheté les jouets Micro
-Change et Diaclone, et s'est associé à Takara. Marvel Comics est engagé par
-Hasbro pour créer l'histoire de fond ; le rédacteur en chef Jim Shooter a écrit une histoire générale
-et confie la tâche de créer les personnages au scénariste Dennis O'Neil.
-Mécontent du travail d'O'Neil (bien que ce dernier ait créé le nom "Optimus Prime"),
-Shooter choisit Bob Budiansky pour créer les personnages.
-
-Les mecha de Transformers ont été en grande partie conçus par Shōji Kawamori, le créateur de
-de l'anime japonais Macross (qui a été adapté en Robotech en Amérique du Nord).
-en Amérique du Nord). Kawamori a eu l'idée de transformer des
-mechas transformables alors qu'il travaillait sur les franchises Diaclone et Macross au début des années 1980.
-(comme le VF-1 Valkyrie dans Macross et Robotech), et ses méchas Diaclone
-ont plus tard servi de base à Transformers.
+entre 1984 et 1993. Les Transformers ont commencé avec les lignes de jouets japonaises
+des années 1980, Micro Change et Diaclone. Elles présentaient des robots capables
+de se transformer en véhicules de tous les jours, en objets électroniques ou en armes.
+Hasbro a acheté les jouets Micro Change et Diaclone, et s'est associé à Takara.
+Marvel Comics est engagé par Hasbro pour créer l'histoire de fond ; le rédacteur en chef
+Jim Shooter a écrit une histoire générale et confie la tâche de créer les personnages au
+scénariste Dennis O'Neil. Mécontent du travail d'O'Neil (bien que ce dernier ait créé
+le nom "Optimus Prime"), Shooter choisit Bob Budiansky pour créer les personnages.
+
+Les mecha de Transformers ont été en grande partie conçus par Shōji Kawamori, le créateur
+de l'anime japonais Macross (qui a été adapté en Robotech en Amérique du Nord). Kawamori
+a eu l'idée de transformer des mechas transformables alors qu'il travaillait sur les
+franchises Diaclone et Macross au début des années 1980 (comme le VF-1 Valkyrie dans
+Macross et Robotech), et ses méchas Diaclone ont plus tard servi de base à Transformers.
Le concept principal de la Génération 1 est que l'héroïque Optimus Prime, le méchant
-le méchant Megatron, et leurs meilleurs soldats s'écrasent sur une Terre préhistorique
-dans l'Arche et le Némésis avant de se réveiller en 1985, Cybertron traversant à toute allure la zone neutre en raison de l'effet de la zone neutre.
-la Zone Neutre, conséquence de la guerre. La bande dessinée Marvel faisait à l'origine partie
-de l'univers principal de Marvel, avec des apparitions de Spider-Man et Nick Fury,
+Megatron, et leurs meilleurs soldats s'écrasent sur une Terre préhistorique dans l'Arche
+et le Némésis avant de se réveiller en 1985, Cybertron traversant à toute allure la zone
+neutre en raison de la guerre. La bande dessinée Marvel faisait à l'origine partie
+de l'univers principal de Marvel, avec des apparitions de Spider-Man et Nick Fury,
plus quelques caméos, ainsi qu'une visite à la Terre Sauvage.
-La série télévisée Transformers a commencé à peu près à la même époque. Produite par Sunbow
-Productions et Marvel Productions, puis Hasbro Productions, dès le début elle a
-contredit les histoires de Budiansky. La série TV montre les Autobots cherchant
-de nouvelles sources d'énergie et s'écrasent lors de l'attaque des Decepticons. Marvel
-a interprété les Autobots comme la destruction d'un astéroïde malveillant s'approchant de Cybertron.
-Shockwave est loyal envers Megatron dans la série TV, et maintient Cybertron dans une impasse en son absence.
-Cybertron dans une impasse pendant son absence, mais dans la BD, il tente de prendre le commandement
-des Decepticons. La série télévisée s'écarte aussi radicalement des origines que
-Budiansky avait créé pour les Dinobots, le Decepticon devenu Autobot Jetfire
-(connu sous le nom de Skyfire à la télévision), les Constructicons (qui s'associent pour former
-Devastator),[19][20] et Oméga Suprême. La bande dessinée Marvel établit très tôt
-que Prime manie la matrice de création, qui donne la vie aux machines. Dans la
-saison, l'épisode en deux parties The Key to Vector Sigma a introduit l'ancien ordinateur
-l'ancien ordinateur Vector Sigma, qui servait le même objectif original que la
-matrice de création (donner la vie aux Transformers), et son gardien Alpha Trion.
+La série télévisée Transformers a commencé à peu près à la même époque.
+Produite par Sunbow Productions et Marvel Productions, puis Hasbro Productions,
+dès le début elle a contredit les histoires de Budiansky. La série TV montre les Autobots
+cherchant de nouvelles sources d'énergie et s'écrasent lors de l'attaque des Decepticons.
+Marvel a interprété les Autobots comme la destruction d'un astéroïde malveillant
+s'approchant de Cybertron. Shockwave est loyal envers Megatron dans la série TV,
+et maintient Cybertron dans une impasse en son absence.
+Cybertron dans une impasse pendant son absence, mais dans la BD,
+il tente de prendre le commandement des Decepticons.
+La série télévisée s'écarte aussi radicalement des origines que Budiansky avait
+créé pour les Dinobots, le Decepticon devenu Autobot Jetfire
+(connu sous le nom de Skyfire à la télévision),
+les Constructicons (qui s'associent pour former Devastator) et Oméga Suprême.
+La bande dessinée Marvel établit très tôt que Prime manie la matrice de création,
+qui donne la vie aux machines. Dans la saison, l'épisode en deux parties
+The Key to Vector Sigma a introduit l'ancien ordinateur l'ancien ordinateur
+Vector Sigma, qui servait le même objectif original que la matrice de création
+(donner la vie aux Transformers), et son gardien Alpha Trion.
"""
inputs = tokenizer(text, return_tensors="pt")
@@ -130,7 +132,7 @@ logits = model(**inputs).logits
IndexError: index out of range in self
```
-Oh-oh, nous avons rencontré un problème. Le message d'erreur est bien plus énigmatique que ceux que nous avons vus dans la [section 2](/course/chapter8/fr/section2) ! Nous n'arrivons pas à comprendre l'historique complet, alors nous décidons de nous tourner vers le forum d'Hugging Face pour obtenir de l'aide. Comment pouvons-nous élaborer le sujet ?
+Oh nous avons rencontré un problème. Le message d'erreur est bien plus énigmatique que ceux que nous avons vus dans la [section 2](/course/chapter8/fr/section2) ! Nous n'arrivons pas à comprendre le *traceback* complet, alors nous décidons de nous tourner vers le forum d'Hugging Face pour obtenir de l'aide. Comment pouvons-nous élaborer le sujet ?
Pour commencer, nous devons cliquer sur le bouton *New Topic* dans le coin supérieur droit (notez que pour créer un sujet, nous devons être connectés) :
@@ -152,19 +154,19 @@ Puisque l'erreur semble concerner exclusivement 🤗 *Transformers*, nous allons
Bien que ce sujet contienne le message d'erreur pour lequel nous avons besoin d'aide, il y a quelques problèmes avec la façon dont il est écrit :
-1. le titre n'est pas très descriptif, de sorte que toute personne parcourant le forum ne sera pas en mesure de dire de quoi il s'agit sans lire également le corps du sujet,
-2. le corps du texte ne fournit pas suffisamment d'informations sur _l'origine de l'erreur et sur _la manière de la reproduire,
+1. le titre n'est pas très descriptif, ainsi toute personne parcourant le forum ne sera pas en mesure de dire de quoi il s'agit sans lire également le corps du sujet,
+2. le corps du texte ne fournit pas suffisamment d'informations sur *l'origine* de l'erreur et sur *la manière* de la reproduire,
3. le sujet s'adresse directement à quelques personnes sur un ton quelque peu exigeant.
-Les sujets comme celui-ci ne sont pas susceptibles d'obtenir une réponse rapide (si tant est qu'ils en obtiennent une), alors voyons comment nous pouvons l'améliorer. Commençons par la première question, celle du choix d'un bon titre.
+Les sujets comme celui-ci ne sont pas susceptibles d'obtenir une réponse rapide (si tant est qu'ils en obtiennent une) alors voyons comment nous pouvons l'améliorer. Commençons par la première question, celle du choix d'un bon titre.
### Choisir un titre descriptif
-Si vous essayez d'obtenir de l'aide pour résoudre un bogue dans votre code, une bonne règle de base consiste à inclure suffisamment d'informations dans le titre pour que les autres puissent rapidement déterminer s'ils pensent pouvoir répondre à votre question ou non. Dans notre exemple, nous connaissons le nom de l'exception qui est levée et nous savons qu'elle est déclenchée dans la passe avant du modèle, où nous appelons `model(**inputs)`. Pour communiquer cela, un titre possible pourrait être :
+Si vous essayez d'obtenir de l'aide pour résoudre un *bug* dans votre code, une bonne règle de base consiste à inclure suffisamment d'informations dans le titre pour que les autres puissent rapidement déterminer s'ils pensent pouvoir répondre à votre question ou non. Dans notre exemple, nous connaissons le nom de l'exception et savons qu'elle est déclenchée dans la passe avant du modèle, où nous appelons `model(**inputs)`. Pour communiquer cela, un titre possible pourrait être :
> Source de l'IndexError dans la passe avant d'AutoModel ?
-Ce titre indique au lecteur _où_ vous pensez que le bogue provient, et s'il a déjà rencontré un `IndexError`, il y a de fortes chances qu'il sache comment le déboguer. Bien sûr, le titre peut être ce que vous voulez, et d'autres variations comme :
+Ce titre indique au lecteur _où_ vous pensez que le *bug* provient, et s'il a déjà rencontré un `IndexError`, il y a de fortes chances qu'il sache comment le déboguer. Bien sûr, le titre peut être ce que vous voulez et d'autres variations comme :
> Pourquoi mon modèle produit-il un IndexError ?
@@ -172,34 +174,34 @@ pourrait également convenir. Maintenant que nous avons un titre descriptif, voy
### Formatage de vos extraits de code
-La lecture du code source est déjà difficile dans un IDE, mais c'est encore plus difficile lorsque le code est copié et collé en texte brut ! Heureusement, les forums Hugging Face supportent l'utilisation de Markdown, donc vous devriez toujours entourer vos blocs de code avec trois *backticks* (```) pour qu'ils soient plus facilement lisibles. Faisons cela pour embellir le message d'erreur et pendant que nous y sommes, rendons le corps un peu plus poli que notre version originale :
+La lecture du code source est déjà difficile dans un IDE, mais c'est encore plus difficile lorsque le code est copié et collé en texte brut ! Heureusement, le forum d'Hugging Face supporte l'utilisation de Markdown donc vous devriez toujours entourer vos blocs de code avec trois *backticks* (```) pour qu'ils soient plus facilement lisibles. Faisons cela pour embellir le message d'erreur et pendant que nous y sommes, rendons le corps un peu plus poli que notre version originale :
-Comme vous pouvez le voir dans la capture d'écran, le fait d'entourer les blocs de code de guillemets convertit le texte brut en code formaté, avec un style de couleur ! Notez également que des backticks simples peuvent être utilisés pour formater des variables en ligne, comme nous l'avons fait pour `distilbert-base-uncased`. Ce sujet a l'air bien meilleur, et avec un peu de chance, nous pourrions trouver quelqu'un dans la communauté qui pourrait deviner à quoi correspond l'erreur. Cependant, au lieu de compter sur la chance, rendons la vie plus facile en incluant la *traceback* dans ses moindres détails !
+Comme vous pouvez le voir dans la capture d'écran, le fait d'entourer les blocs de code de *backticks* convertit le texte brut en code formaté, avec un style de couleur ! Notez également que des *backticks* simples peuvent être utilisés pour formater des variables en ligne comme nous l'avons fait pour `distilbert-base-uncased`. Ce sujet a l'air bien meilleur, et avec un peu de chance, nous pourrions trouver quelqu'un dans la communauté qui pourrait deviner à quoi correspond l'erreur. Cependant, au lieu de compter sur la chance, rendons la vie plus facile en incluant le *traceback* dans ses moindres détails !
-### Inclure la *traceback* complète
+### Inclure le
traceback complet
-Puisque la dernière ligne de la *traceback* est souvent suffisante pour déboguer votre propre code, il peut être tentant de ne fournir que cela dans votre sujet pour "gagner de la place". Bien que bien intentionné, cela rend en fait le débogage du problème _plus difficile_ pour les autres, car les informations situées plus haut dans la *traceback* peuvent également être très utiles. Une bonne pratique consiste donc à copier et coller la *traceback* _entière_, en veillant à ce qu'elle soit bien formatée. Comme ces tracebacks peuvent être assez longs, certaines personnes préfèrent les montrer après avoir expliqué le code source. C'est ce que nous allons faire. Maintenant, notre sujet de forum ressemble à ce qui suit :
+Puisque la dernière ligne de le *traceback* est souvent suffisante pour déboguer votre propre code, il peut être tentant de ne fournir que cela dans votre sujet pour "gagner de la place". Bien que bien intentionné, cela rend en fait le débogage du problème _plus difficile_ pour les autres, car les informations situées plus haut dans le *traceback* peuvent également être très utiles. Une bonne pratique consiste donc à copier et coller le *traceback* _entier_, en veillant à ce qu'elle soit bien formatée. Comme ces tracebacks peuvent être assez longs, certaines personnes préfèrent les montrer après avoir expliqué le code source. C'est ce que nous allons faire. Maintenant, notre sujet de forum ressemble à ce qui suit :
-Ceci est beaucoup plus informatif, et un lecteur attentif pourrait être en mesure d'indiquer que le problème semble être dû à la transmission d'une longue entrée en raison de cette ligne dans la *traceback* :
+Ceci est beaucoup plus informatif et un lecteur attentif pourrait être en mesure d'indiquer que le problème semble être dû à la transmission d'une longue entrée en raison de cette ligne dans le *traceback* :
-> La longueur de la séquence des indices de *tokens* est supérieure à la longueur de séquence maximale spécifiée pour ce modèle (583 > 512).
+> Token indices sequence length is longer than the specified maximum sequence length for this model (583 > 512).
-Cependant, nous pouvons leur faciliter les choses en leur fournissant le code réel qui a déclenché l'erreur. C'est ce que nous allons faire maintenant.
+Cependant, nous pouvons leur faciliter les choses en leur fournissant le code qui a déclenché l'erreur. C'est ce que nous allons faire maintenant.
### Fournir un exemple reproductible
-Si vous avez déjà essayé de déboguer le code de quelqu'un d'autre, vous avez probablement d'abord essayé de recréer le problème qu'il a signalé afin de pouvoir commencer à travailler sur la *traceback* pour identifier l'erreur. Il en va de même lorsqu'il s'agit d'obtenir (ou de donner) de l'aide sur les forums. Il est donc très utile de pouvoir fournir un petit exemple qui reproduit l'erreur. La moitié du temps, le simple fait de faire cet exercice vous aidera à comprendre ce qui ne va pas. Dans tous les cas, la pièce manquante de notre exemple est de montrer les _entrées_ que nous avons fournies au modèle. En faisant cela, nous obtenons quelque chose comme l'exemple complet suivant :
+Si vous avez déjà essayé de déboguer le code de quelqu'un d'autre, vous avez probablement d'abord essayé de recréer le problème qu'il a signalé afin de pouvoir commencer à travailler sur le *traceback* pour identifier l'erreur. Il en va de même lorsqu'il s'agit d'obtenir (ou de donner) de l'aide sur les forums. Il est donc très utile de pouvoir fournir un petit exemple qui reproduit l'erreur. La moitié du temps, le simple fait de faire cet exercice vous aidera à comprendre ce qui ne va pas. Dans tous les cas, la pièce manquante de notre exemple est de montrer les _entrées_ que nous avons fournies au modèle. En faisant cela, nous obtenons quelque chose comme l'exemple complet suivant :
-Ce sujet contient maintenant un bon lot d'informations, et il est rédigé d'une manière qui a beaucoup plus de chances d'attirer l'attention de la communauté et d'obtenir une réponse utile. Avec ces directives de base, vous pouvez maintenant créer de superbes sujets pour trouver les réponses à vos questions sur 🤗 *Transformers* !
+Ce sujet contient maintenant un bon lot d'informations et il est rédigé d'une manière qui a beaucoup plus de chances d'attirer l'attention de la communauté et d'obtenir une réponse utile. Avec ces directives de base, vous pouvez maintenant créer de superbes sujets pour trouver les réponses à vos questions sur 🤗 *Transformers* !
diff --git a/chapters/fr/chapter8/4.mdx b/chapters/fr/chapter8/4.mdx
index 8d1c6ab77..2ea4a9ece 100644
--- a/chapters/fr/chapter8/4.mdx
+++ b/chapters/fr/chapter8/4.mdx
@@ -9,17 +9,17 @@
{label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/chapter8/section4_pt.ipynb"},
]} />
-Vous avez écrit un magnifique script pour entraîner ou *finetuner* un modèle sur une tâche donnée, en suivant consciencieusement les conseils du [Chapitre 7](/course/fr/chapter7). Mais lorsque vous lancez la commande `model.fit()`, quelque chose d'horrible se produit : vous obtenez une erreur 😱 ! Ou pire, tout semble aller bien et l'entraînement se déroule sans erreur, mais le modèle résultant est merdique. Dans cette section, nous allons vous montrer ce que vous pouvez faire pour déboguer ce genre de problèmes.
+Vous avez écrit un magnifique script pour entraîner ou *finetuner* un modèle sur une tâche donnée en suivant consciencieusement les conseils du [chapitre 7](/course/fr/chapter7). Mais lorsque vous lancez la commande `model.fit()`, quelque chose d'horrible se produit : vous obtenez une erreur 😱 ! Ou pire, tout semble aller bien et l'entraînement se déroule sans erreur mais le modèle résultant est mauvais. Dans cette section, nous allons vous montrer ce que vous pouvez faire pour déboguer ce genre de problèmes.
## Déboguer le pipeline d'entraînement
-Le problème lorsque vous rencontrez une erreur dans `trainer.train()` est qu'elle peut provenir de plusieurs sources, car le `Trainer` assemble généralement des batchs de choses. Il convertit les jeux de données en chargeurs de données, donc le problème pourrait être quelque chose d'erroné dans votre jeu de données, ou un problème en essayant de regrouper les éléments des jeux de données ensemble. Ensuite, il prend un batch de données et le transmet au modèle, le problème peut donc se situer dans le code du modèle. Après cela, il calcule les gradients et effectue l'étape d'optimisation, le problème peut donc également se situer dans votre optimiseur. Et même si tout se passe bien pendant l'entraînement, quelque chose peut encore mal tourner pendant l'évaluation si votre métrique pose problème.
+Le problème lorsque vous rencontrez une erreur dans `trainer.train()` est qu'elle peut provenir de plusieurs sources, car la fonction `Trainer` assemble généralement des batchs de choses. Elle convertit les jeux de données en chargeurs de données donc le problème pourrait être quelque chose d'erroné dans votre jeu de données, ou un problème en essayant de regrouper les éléments des jeux de données ensemble. Ensuite, elle prend un batch de données et le transmet au modèle, le problème peut donc se situer dans le code du modèle. Après cela, elle calcule les gradients et effectue l'étape d'optimisation, le problème peut donc également se situer dans votre optimiseur. Et même si tout se passe bien pendant l'entraînement, quelque chose peut encore mal tourner pendant l'évaluation si votre métrique pose problème.
La meilleure façon de déboguer une erreur qui survient dans `trainer.train()` est de passer manuellement en revue tout le pipeline pour voir où les choses se sont mal passées. L'erreur est alors souvent très facile à résoudre.
-Pour le démontrer, nous utiliserons le script suivant qui tente d'ajuster un modèle DistilBERT sur le [jeu de données MNLI](https://huggingface.co/datasets/glue) :
+Pour le démontrer, nous utiliserons le script suivant qui tente de *finetuner* un modèle DistilBERT sur le [jeu de données MNLI](https://huggingface.co/datasets/glue) :
```py
from datasets import load_dataset, load_metric
@@ -78,7 +78,7 @@ Si vous essayez de l'exécuter, vous serez confronté à une erreur plutôt cryp
### Vérifiez vos données
-Cela va sans dire, mais si vos données sont corrompues, le `Trainer` ne sera pas capable de former des batchs, et encore moins d'entraîner votre modèle. Donc, tout d'abord, vous devez jeter un coup d'oeil à ce qui se trouve dans votre ensemble d'entraînement.
+Cela va sans dire, mais si vos données sont corrompues, le `Trainer` ne sera pas capable de former des batchs et encore moins d'entraîner votre modèle. Donc, tout d'abord, vous devez jeter un coup d'oeil à ce qui se trouve dans votre jeu d'entraînement.
Pour éviter d'innombrables heures passées à essayer de corriger quelque chose qui n'est pas la source du bug, nous vous recommandons d'utiliser `trainer.train_dataset` pour vos vérifications et rien d'autre. Faisons donc cela ici :
@@ -93,9 +93,9 @@ trainer.train_dataset[0]
'premise': 'Conceptually cream skimming has two basic dimensions - product and geography.'}
```
-Vous remarquez quelque chose d'anormal ? Ceci, en conjonction avec le message d'erreur sur les `input_ids` manquants, devrait vous faire réaliser que ce sont des textes, et non des nombres que le modèle peut comprendre. Ici, l'erreur originale est très trompeuse parce que le `Trainer` enlève automatiquement les colonnes qui ne correspondent pas à la signature du modèle (c'est-à-dire, les arguments attendus par le modèle). Cela signifie qu'ici, tout, sauf les étiquettes, a été éliminé. Il n'y avait donc aucun problème à créer des batchs et à les envoyer ensuite au modèle, qui s'est plaint à son tour de ne pas avoir reçu les bons arguments.
+Vous remarquez quelque chose d'anormal ? Ceci, en conjonction avec le message d'erreur sur les `input_ids` manquants, devrait vous faire réaliser que ce sont des textes et non des nombres que le modèle peut comprendre. Ici, l'erreur originale est très trompeuse parce que le `Trainer` enlève automatiquement les colonnes qui ne correspondent pas à la signature du modèle (c'est-à-dire, les arguments attendus par le modèle). Cela signifie qu'ici, tout, sauf les étiquettes, a été éliminé. Il n'y avait donc aucun problème à créer des batchs et à les envoyer ensuite au modèle, qui s'est plaint à son tour de ne pas avoir reçu les bons arguments.
-Pourquoi les données n'ont-elles pas été traitées ? Nous avons utilisé la méthode `Dataset.map()` sur les ensembles de données pour appliquer le *tokenizer* sur chaque échantillon. Mais si vous regardez attentivement le code, vous verrez que nous avons fait une erreur en passant les ensembles d'entraînement et d'évaluation au `Trainer`. Au lieu d'utiliser `tokenized_datasets` ici, nous avons utilisé `raw_datasets` 🤦. Alors corrigeons ça !
+Pourquoi les données n'ont-elles pas été traitées ? Nous avons utilisé la méthode `Dataset.map()` sur les jeux de données pour appliquer le *tokenizer* sur chaque échantillon. Mais si vous regardez attentivement le code, vous verrez que nous avons fait une erreur en passant les ensembles d'entraînement et d'évaluation au `Trainer`. Au lieu d'utiliser `tokenized_datasets` ici, nous avons utilisé `raw_datasets` 🤦. Alors corrigeons ça !
```py
from datasets import load_dataset, load_metric
@@ -146,13 +146,13 @@ trainer = Trainer(
trainer.train()
```
-Ce nouveau code donnera maintenant une erreur différente (progrès !) :
+Ce nouveau code donnera maintenant une erreur différente (c'est un progrès !) :
```python out
'ValueError: expected sequence of length 43 at dim 1 (got 37)'
```
-En regardant la trace, nous pouvons voir que l'erreur se produit dans l'étape de collationnement des données :
+En regardant le *traceback*, nous pouvons voir que l'erreur se produit dans l'étape de collationnement des données :
```python out
~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features)
@@ -163,9 +163,9 @@ En regardant la trace, nous pouvons voir que l'erreur se produit dans l'étape d
109 return batch
```
-Donc, nous devrions passer à cela. Mais avant cela, finissons d'inspecter nos données, pour être sûrs à 100% qu'elles sont correctes.
+Donc, nous devrions passer à cela. Mais avant finissons d'inspecter nos données, pour être sûrs à 100% qu'elles sont correctes.
-Une chose que vous devriez toujours faire lorsque vous déboguez une session d'entraînement est de jeter un coup d'oeil aux entrées décodées de votre modèle. Nous ne pouvons pas donner un sens aux chiffres que nous lui fournissons directement, nous devons donc examiner ce que ces chiffres représentent. Dans le domaine de la vision par ordinateur, par exemple, cela signifie regarder les images décodées des pixels que vous passez, dans le domaine de la parole, cela signifie écouter les échantillons audio décodés, et pour notre exemple NLP, cela signifie utiliser notre *tokenizer* pour décoder les entrées :
+Une chose que vous devriez toujours faire lorsque vous déboguez une session d'entraînement est de jeter un coup d'oeil aux entrées décodées de votre modèle. Nous ne pouvons pas donner un sens aux chiffres que nous lui fournissons directement, nous devons donc examiner ce que ces chiffres représentent. Dans le domaine de la vision par ordinateur cela signifie regarder les images décodées des pixels que vous passez, dans le domaine de la parole cela signifie écouter les échantillons audio décodés, et pour notre exemple de NLP cela signifie utiliser notre *tokenizer* pour décoder les entrées :
```py
tokenizer.decode(trainer.train_dataset[0]["input_ids"])
@@ -175,7 +175,7 @@ tokenizer.decode(trainer.train_dataset[0]["input_ids"])
'[CLS] conceptually cream skimming has two basic dimensions - product and geography. [SEP] product and geography are what make cream skimming work. [SEP]'
```
-Cela semble donc correct. Vous devriez faire cela pour toutes les clés dans les entrées :
+Cela semble correct. Vous devriez faire cela pour toutes les clés dans les entrées :
```py
trainer.train_dataset[0].keys()
@@ -185,7 +185,7 @@ trainer.train_dataset[0].keys()
dict_keys(['attention_mask', 'hypothesis', 'idx', 'input_ids', 'label', 'premise'])
```
-Note that the keys that don't correspond to inputs accepted by the model will be automatically discarded, so here we will only keep `input_ids`, `attention_mask`, and `label` (which will be renamed `labels`). To double-check the model signature, you can print the class of your model, then go check its documentation:
+Notez que les clés qui ne correspondent pas à des entrées acceptées par le modèle seront automatiquement écartées, donc ici nous ne garderons que `input_ids`, `attention_mask`, et `label` (qui sera renommé `labels`). Pour revérifier la signature du modèle, vous pouvez imprimer la classe de votre modèle, puis aller consulter sa documentation :
```py
type(trainer.model)
@@ -197,7 +197,7 @@ transformers.models.distilbert.modeling_distilbert.DistilBertForSequenceClassifi
Donc dans notre cas, nous pouvons vérifier les paramètres acceptés sur [cette page](https://huggingface.co/transformers/model_doc/distilbert.html#distilbertforsequenceclassification). Le `Trainer` va également enregistrer les colonnes qu'il rejette.
-Nous avons vérifié que les IDs d'entrée sont corrects en les décodant. Ensuite, il y a le `attention_mask` :
+Nous avons vérifié que les identifiants d'entrée sont corrects en les décodant. Ensuite, il y a le `attention_mask` :
```py
tokenizer.decode(trainer.train_dataset[0]["attention_mask"])
@@ -229,7 +229,7 @@ trainer.train_dataset[0]["label"]
1
```
-Comme les ID d'entrée, c'est un nombre qui n'a pas vraiment de sens en soi. Comme nous l'avons vu précédemment, la correspondance entre les entiers et les noms d'étiquettes est stockée dans l'attribut `names` de la *caractéristique* correspondante de l'ensemble de données :
+Comme les identifiants d'entrée, c'est un nombre qui n'a pas vraiment de sens en soi. Comme nous l'avons vu précédemment, la correspondance entre les entiers et les noms d'étiquettes est stockée dans l'attribut `names` de la *caractéristique* correspondante du jeu de données :
```py
trainer.train_dataset.features["label"].names
@@ -239,30 +239,30 @@ trainer.train_dataset.features["label"].names
['entailment', 'neutral', 'contradiction']
```
-Donc `1` signifie `neutral`, ce qui signifie que les deux phrases que nous avons vues ci-dessus ne sont pas en contradiction, et que la première n'implique pas la seconde. Cela semble correct !
+Donc `1` signifie `neutral`, ce qui signifie que les deux phrases que nous avons vues ci-dessus ne sont pas en contradiction : la première n'implique pas la seconde. Cela semble correct !
-Nous n'avons pas d'ID de type de *token* ici, puisque DistilBERT ne les attend pas ; si vous en avez dans votre modèle, vous devriez également vous assurer qu'ils correspondent correctement à l'endroit où se trouvent la première et la deuxième phrase dans l'entrée.
+Nous n'avons pas de *token* de type identifiant ici puisque DistilBERT ne les attend pas. Si vous en avez dans votre modèle, vous devriez également vous assurer qu'ils correspondent correctement à l'endroit où se trouvent la première et la deuxième phrase dans l'entrée.
-✏️ *Votre tour !* Vérifiez que tout semble correct avec le deuxième élément du jeu de données d'entraînement.
+✏️ *A votre tour !* Vérifiez que tout semble correct avec le deuxième élément du jeu de données d'entraînement.
-Nous ne vérifions ici que l'ensemble d'entraînement, mais vous devez bien sûr vérifier de la même façon les ensembles de validation et de test.
+Ici nous ne vérifions que le jeu d'entraînement. Vous devez bien sûr vérifier de la même façon les jeux de validation et de test.
-Maintenant que nous savons que nos ensembles de données sont bons, il est temps de vérifier l'étape suivante du pipeline d'entraînement.
+Maintenant que nous savons que nos jeux de données sont bons, il est temps de vérifier l'étape suivante du pipeline d'entraînement.
### Des jeux de données aux chargeurs de données
-La prochaine chose qui peut mal tourner dans le pipeline d'entraînement est lorsque le `Trainer` essaie de former des batchs à partir de l'ensemble d'entraînement ou de validation. Une fois que vous êtes sûr que les jeux de données du `Trainer` sont corrects, vous pouvez essayer de former manuellement un batch en exécutant ce qui suit (remplacez `train` par `eval` pour le dataloader de validation) :
+La prochaine chose qui peut mal tourner dans le pipeline d'entraînement est lorsque le `Trainer` essaie de former des batchs à partir du jeu d'entraînement ou de validation. Une fois que vous êtes sûr que les jeux de données du `Trainer` sont corrects, vous pouvez essayer de former manuellement un batch en exécutant ce qui suit (remplacez `train` par `eval` pour le *dataloader* de validation) :
```py
for batch in trainer.get_train_dataloader():
break
```
-Ce code crée le dataloader d'entraînement, puis le parcourt en s'arrêtant à la première itération. Si le code s'exécute sans erreur, vous avez le premier batch d'entraînement que vous pouvez inspecter, et si le code se trompe, vous êtes sûr que le problème se situe dans le dataloader, comme c'est le cas ici :
+Ce code crée le *dataloader* d'entraînement puis le parcourt en s'arrêtant à la première itération. Si le code s'exécute sans erreur, vous avez le premier batch d'entraînement que vous pouvez inspecter, et si le code se trompe, vous êtes sûr que le problème se situe dans le *dataloader*, comme c'est le cas ici :
```python out
~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features)
@@ -275,7 +275,7 @@ Ce code crée le dataloader d'entraînement, puis le parcourt en s'arrêtant à
ValueError: expected sequence of length 45 at dim 1 (got 76)
```
-L'inspection de la dernière image du traceback devrait suffire à vous donner un indice, mais creusons un peu plus. La plupart des problèmes lors de la création d'un batch sont dus à l'assemblage des exemples en un seul batch, donc la première chose à vérifier en cas de doute est le `collate_fn` utilisé par votre `DataLoader` :
+L'inspection de la dernière image du *traceback* devrait suffire à vous donner un indice mais creusons un peu plus. La plupart des problèmes lors de la création d'un batch sont dus à l'assemblage des exemples en un seul batch. La première chose à vérifier en cas de doute est le `collate_fn` utilisé par votre `DataLoader` :
```py
data_collator = trainer.get_train_dataloader().collate_fn
@@ -286,9 +286,9 @@ data_collator
Dict[str, Any]>
```
-C'est donc le collateur `default_data_collator`, mais ce n'est pas ce que nous voulons dans ce cas. Nous voulons rembourrer nos exemples à la phrase la plus longue du batch, ce qui est fait par le collateur `DataCollatorWithPadding`. Et ce collateur de données est censé être utilisé par défaut par le `Trainer`, alors pourquoi n'est-il pas utilisé ici ?
+C'est donc `default_data_collator`, mais ce n'est pas ce que nous voulons dans ce cas. Nous voulons rembourrer nos exemples à la phrase la plus longue du batch, ce qui est fait par `DataCollatorWithPadding`. Et cette assembleur de données est censé être utilisé par défaut par le `Trainer`, alors pourquoi n'est-il pas utilisé ici ?
-La réponse est que nous n'avons pas passé le `tokenizer` au `Trainer`, donc il ne pouvait pas créer le `DataCollatorWithPadding` que nous voulons. En pratique, il ne faut jamais hésiter à transmettre explicitement le collateur de données que l'on veut utiliser, pour être sûr d'éviter ce genre d'erreurs. Adaptons notre code pour faire exactement cela :
+La réponse est que nous n'avons pas passé le `tokenizer` au `Trainer`, donc il ne pouvait pas créer le `DataCollatorWithPadding` que nous voulons. En pratique, il ne faut jamais hésiter à transmettre explicitement l'assembleur de données que l'on veut utiliser pour être sûr d'éviter ce genre d'erreurs. Adaptons notre code pour faire exactement cela :
```py
from datasets import load_dataset, load_metric
@@ -350,16 +350,16 @@ La bonne nouvelle ? Nous n'avons plus la même erreur qu'avant, ce qui est un pr
RuntimeError: CUDA error: CUBLAS_STATUS_ALLOC_FAILED when calling `cublasCreate(handle)`
```
-C'est une mauvaise chose car les erreurs CUDA sont extrêmement difficiles à déboguer en général. Nous verrons dans une minute comment résoudre ce problème, mais terminons d'abord notre analyse de la création de batchs.
+C'est une mauvaise chose car les erreurs CUDA sont extrêmement difficiles à déboguer en général. Nous verrons dans une minute comment résoudre ce problème mais terminons d'abord notre analyse de la création de batchs.
-Si vous êtes sûr que votre collecteur de données est le bon, vous devriez essayer de l'appliquer sur quelques échantillons de votre ensemble de données :
+Si vous êtes sûr que votre collecteur de données est le bon, vous devriez essayer de l'appliquer sur quelques échantillons de votre jeu de données :
```py
data_collator = trainer.get_train_dataloader().collate_fn
batch = data_collator([trainer.train_dataset[i] for i in range(4)])
```
-Ce code échouera parce que le `train_dataset` contient des colonnes de type string, que le `Trainer` supprime habituellement. Vous pouvez les supprimer manuellement, ou si vous voulez reproduire exactement ce que le `Trainer` fait en coulisse, vous pouvez appeler la méthode privée `Trainer._remove_unused_columns()` qui fait cela :
+Ce code échouera parce que le `train_dataset` contient des colonnes de type *string* que le `Trainer` supprime habituellement. Vous pouvez les supprimer manuellement ou si vous voulez reproduire exactement ce que le `Trainer` fait en coulisse, vous pouvez appeler la méthode `Trainer._remove_unused_columns()` qui fait cela :
```py
data_collator = trainer.get_train_dataloader().collate_fn
@@ -371,6 +371,7 @@ Vous devriez alors être en mesure de déboguer manuellement ce qui se passe dan
Maintenant que nous avons débogué le processus de création de batch, il est temps d'en passer un dans le modèle !
+
### Passage par le modèle
Vous devriez être en mesure d'obtenir un batch en exécutant la commande suivante :
@@ -380,11 +381,11 @@ for batch in trainer.get_train_dataloader():
break
```
-Si vous exécutez ce code dans un *notebook*, vous risquez d'obtenir une erreur CUDA similaire à celle que nous avons vue précédemment, auquel cas vous devrez redémarrer votre notebook et réexécuter le dernier extrait sans la ligne `trainer.train()`. C'est la deuxième chose la plus ennuyeuse à propos des erreurs CUDA : elles cassent irrémédiablement votre noyau. La chose la plus ennuyeuse à leur sujet est le fait qu'elles sont difficiles à déboguer.
+Si vous exécutez ce code dans un *notebook*, vous risquez d'obtenir une erreur CUDA similaire à celle que nous avons vue précédemment, auquel cas vous devrez redémarrer votre *notebook* et réexécuter le dernier extrait sans la ligne `trainer.train()`. C'est la deuxième chose la plus ennuyeuse à propos des erreurs CUDA : elles cassent irrémédiablement votre noyau. La première plus ennuyeuse est le fait qu'elles sont difficiles à déboguer.
-Comment cela se fait-il ? Cela tient à la façon dont les GPU fonctionnent. Ils sont extrêmement efficaces pour exécuter un batch d'opérations en parallèle, mais l'inconvénient est que lorsque l'une de ces instructions entraîne une erreur, vous ne le savez pas immédiatement. Ce n'est que lorsque le programme appelle une synchronisation des multiples processus sur le GPU qu'il réalise que quelque chose s'est mal passé, de sorte que l'erreur est en fait soulevée à un endroit qui n'a rien à voir avec ce qui l'a créée. Par exemple, si nous regardons notre traceback précédent, l'erreur a été soulevée pendant la passe arrière, mais nous verrons dans une minute qu'elle provient en fait de quelque chose dans la passe avant.
+Comment cela se fait-il ? Cela tient à la façon dont les GPUs fonctionnent. Ils sont extrêmement efficaces pour exécuter un batch d'opérations en parallèle, mais l'inconvénient est que lorsque l'une de ces instructions entraîne une erreur, vous ne le savez pas immédiatement. Ce n'est que lorsque le programme appelle une synchronisation des multiples processus sur le GPU qu'il réalise que quelque chose s'est mal passé, de sorte que l'erreur est en fait mentionnée à un endroit qui n'a rien à voir avec ce qui l'a créée. Par exemple, si nous regardons notre *traceback* précédent, l'erreur a été soulevée pendant la passe arrière, mais nous verrons dans une minute qu'elle provient en fait de quelque chose dans la passe avant.
-Alors comment déboguer ces erreurs ? La réponse est simple : nous ne le faisons pas. À moins que votre erreur CUDA ne soit une erreur out-of-memory (ce qui signifie qu'il n'y a pas assez de mémoire dans votre GPU), vous devez toujours revenir au CPU pour la déboguer.
+Alors comment déboguer ces erreurs ? La réponse est simple : nous ne le faisons pas. À moins que votre erreur CUDA ne soit une erreur *out-of-memory* (ce qui signifie qu'il n'y a pas assez de mémoire dans votre GPU), vous devez toujours revenir au CPU pour la déboguer.
Pour faire cela dans notre cas, nous devons juste remettre le modèle sur le CPU et l'appeler sur notre batch. Le batch retourné par le `DataLoader` n'a pas encore été déplacé sur le GPU :
@@ -403,7 +404,7 @@ outputs = trainer.model.cpu()(**batch)
IndexError: Target 2 is out of bounds.
```
-Donc, l'image devient plus claire. Au lieu d'avoir une erreur CUDA, nous avons maintenant une `IndexError` dans le calcul de la perte (donc rien à voir avec le backward pass, comme nous l'avons dit plus tôt). Plus précisément, nous pouvons voir que c'est la cible 2 qui crée l'erreur, donc c'est un très bon moment pour vérifier le nombre de labels de notre modèle :
+L'image devient plus claire. Au lieu d'avoir une erreur CUDA, nous avons maintenant une `IndexError` dans le calcul de la perte (donc rien à voir avec la passe arrière comme nous l'avons dit plus tôt). Plus précisément, nous pouvons voir que c'est la cible 2 qui crée l'erreur, donc c'est un bon moment pour vérifier le nombre de labels de notre modèle :
```python
trainer.model.config.num_labels
@@ -413,7 +414,7 @@ trainer.model.config.num_labels
2
```
-Avec deux étiquettes, seuls les 0 et les 1 sont autorisés comme cibles, mais d'après le message d'erreur, nous avons obtenu un 2. Obtenir un 2 est en fait normal : si nous nous souvenons des noms d'étiquettes que nous avons extraits plus tôt, il y en avait trois, donc nous avons des indices 0, 1 et 2 dans notre ensemble de données. Le problème est que nous n'avons pas indiqué cela à notre modèle, qui aurait dû être créé avec trois étiquettes. Alors, corrigeons cela !
+Avec deux étiquettes, seuls les 0 et les 1 sont autorisés comme cibles, mais d'après le message d'erreur, nous avons obtenu un 2. Obtenir un 2 est en fait normal : si nous nous souvenons des noms des étiquettes que nous avons extraits plus tôt, il y en avait trois, donc nous avons les indices 0, 1 et 2 dans notre jeu de données. Le problème est que nous n'avons pas indiqué cela à notre modèle, qui aurait dû être créé avec trois étiquettes. Alors, corrigeons cela !
```py
from datasets import load_dataset, load_metric
@@ -468,7 +469,7 @@ trainer = Trainer(
)
```
-Nous n'incluons pas encore la ligne `trainer.train()`, pour prendre le temps de vérifier que tout se passe bien. Si nous demandons un batch et le passons à notre modèle, il fonctionne maintenant sans erreur !
+Nous n'incluons pas encore la ligne `trainer.train()` pour prendre le temps de vérifier que tout se passe bien. Si nous passons un batch à notre modèle, il fonctionne maintenant sans erreur !
```py
for batch in trainer.get_train_dataloader():
@@ -512,15 +513,15 @@ trainer.optimizer.step()
Encore une fois, si vous utilisez l'optimiseur par défaut dans le `Trainer`, vous ne devriez pas avoir d'erreur à ce stade, mais si vous avez un optimiseur personnalisé, il pourrait y avoir quelques problèmes à déboguer ici. N'oubliez pas de revenir au CPU si vous obtenez une erreur CUDA bizarre à ce stade. En parlant d'erreurs CUDA, nous avons mentionné précédemment un cas particulier. Voyons cela maintenant.
-### Gérer les erreurs CUDA hors-mémoire
+### Gérer les erreurs CUDA out of memory
-Chaque fois que vous obtenez un message d'erreur qui commence par `RuntimeError : CUDA out of memory`, cela indique que vous êtes à court de mémoire GPU. Cela n'est pas directement lié à votre code, et cela peut arriver avec un script qui fonctionne parfaitement bien. Cette erreur signifie que vous avez essayé de mettre trop de choses dans la mémoire interne de votre GPU, et que cela a entraîné une erreur. Comme pour d'autres erreurs CUDA, vous devrez redémarrer votre noyau pour être en mesure d'exécuter à nouveau votre entraînement.
+Chaque fois que vous obtenez un message d'erreur qui commence par `RuntimeError : CUDA out of memory`, cela indique que vous êtes à court de mémoire GPU. Cela n'est pas directement lié à votre code et peut arriver avec un script qui fonctionne parfaitement bien. Cette erreur signifie que vous avez essayé de mettre trop de choses dans la mémoire interne de votre GPU et que cela a entraîné une erreur. Comme pour d'autres erreurs CUDA, vous devrez redémarrer votre noyau pour être en mesure d'exécuter à nouveau votre entraînement.
-Pour résoudre ce problème, il suffit d'utiliser moins d'espace GPU, ce qui est souvent plus facile à dire qu'à faire. Tout d'abord, assurez-vous que vous n'avez pas deux modèles sur le GPU en même temps (sauf si cela est nécessaire pour votre problème, bien sûr). Ensuite, vous devriez probablement réduire la taille de votre batch, car elle affecte directement les tailles de toutes les sorties intermédiaires du modèle et leurs gradients. Si le problème persiste, envisagez d'utiliser une version plus petite de votre modèle.
+Pour résoudre ce problème, il suffit d'utiliser moins d'espace GPU, ce qui est souvent plus facile à dire qu'à faire. Tout d'abord, assurez-vous que vous n'avez pas deux modèles sur le GPU en même temps (sauf si cela est nécessaire pour votre problème, bien sûr). Ensuite, vous devriez probablement réduire la taille de votre batch car elle affecte directement les tailles de toutes les sorties intermédiaires du modèle et leurs gradients. Si le problème persiste, envisagez d'utiliser une version plus petite de votre modèle.
-In the next part of the course, we'll look at more advanced techniques that can help you reduce your memory footprint and let you fine-tune the biggest models.
+Dans la prochaine partie du cours, nous examinerons des techniques plus avancées qui peuvent vous aider à réduire votre empreinte mémoire et vous permettre de finetuner les plus grands modèles.
@@ -529,7 +530,7 @@ In the next part of the course, we'll look at more advanced techniques that can
Maintenant que nous avons résolu tous les problèmes liés à notre code, tout est parfait et l'entraînement devrait se dérouler sans problème, n'est-ce pas ? Pas si vite ! Si vous exécutez la commande `trainer.train()`, tout aura l'air bien au début, mais après un moment vous obtiendrez ce qui suit :
```py
-# This will take a long time and error out, so you shouldn't run this cell
+# Cela prendra beaucoup de temps et se soldera par une erreur, vous ne devriez donc pas utiliser cette cellule.
trainer.train()
```
@@ -567,7 +568,7 @@ with torch.no_grad():
outputs = trainer.model(**batch)
```
-L'erreur survient plus tard, à la fin de la phase d'évaluation, et si nous regardons la *traceback*, nous voyons ceci :
+L'erreur survient plus tard, à la fin de la phase d'évaluation, et si nous regardons le *traceback*, nous voyons ceci :
```python trace
~/git/datasets/src/datasets/metric.py in add_batch(self, predictions, references)
@@ -601,7 +602,7 @@ predictions.shape, labels.shape
((8, 3), (8,))
```
-Nos prédictions sont toujours des logits, et non les prédictions réelles, c'est pourquoi la métrique retourne cette erreur (quelque peu obscure). La correction est assez simple, il suffit d'ajouter un argmax dans la fonction `compute_metrics()` :
+Nos prédictions sont toujours des logits et non les prédictions réelles, c'est pourquoi la métrique retourne cette erreur (quelque peu obscure). La correction est assez simple, il suffit d'ajouter un argmax dans la fonction `compute_metrics()` :
```py
import numpy as np
@@ -680,7 +681,7 @@ trainer = Trainer(
trainer.train()
```
-Dans ce cas, il n'y a plus de problème, et notre script va affiner un modèle qui devrait donner des résultats raisonnables. Mais que faire lorsque l'entraînement se déroule sans erreur, et que le modèle entraîné n'est pas du tout performant ? C'est la partie la plus difficile de l'apprentissage automatique, et nous allons vous montrer quelques techniques qui peuvent vous aider.
+Dans ce cas, il n'y a plus de problème, et notre script va *finetuner* un modèle qui devrait donner des résultats raisonnables. Mais que faire lorsque l'entraînement se déroule sans erreur et que le modèle entraîné n'est pas du tout performant ? C'est la partie la plus difficile de l'apprentissage automatique et nous allons vous montrer quelques techniques qui peuvent vous aider.
@@ -690,34 +691,34 @@ Dans ce cas, il n'y a plus de problème, et notre script va affiner un modèle q
## Déboguer les erreurs silencieuses pendant l'entraînement
-Que peut-on faire pour déboguer un entraînement qui se termine sans erreur mais qui ne donne pas de bons résultats ? Nous allons vous donner quelques pistes ici, mais sachez que ce type de débogage est la partie la plus difficile de l'apprentissage automatique, et qu'il n'y a pas de réponse magique.
+Que peut-on faire pour déboguer un entraînement qui se termine sans erreur mais qui ne donne pas de bons résultats ? Nous allons vous donner quelques pistes ici, mais sachez que ce type de débogage est la partie la plus difficile de l'apprentissage automatique et qu'il n'y a pas de réponse magique.
### Vérifiez vos données (encore !)
-Votre modèle n'apprendra quelque chose que s'il est réellement possible d'apprendre quelque chose de vos données. Si un bogue corrompt les données ou si les étiquettes sont attribuées de manière aléatoire, il est très probable que vous n'obtiendrez aucun entraînement de modèle sur votre ensemble de données. Commencez donc toujours par revérifier vos entrées et étiquettes décodées, et posez-vous les questions suivantes :
+Votre modèle n'apprendra quelque chose que s'il est réellement possible d'apprendre quelque chose de vos données. Si un *bug* corrompt les données ou si les étiquettes sont attribuées de manière aléatoire, il est très probable que vous n'obtiendrez aucun entraînement de modèle sur votre jeu de données. Commencez donc toujours par revérifier vos entrées et étiquettes décodées, et posez-vous les questions suivantes :
- les données décodées sont-elles compréhensibles ?
- êtes-vous d'accord avec les étiquettes ?
- y a-t-il une étiquette qui est plus courante que les autres ?
-- quelle devrait être la perte/métrie si le modèle prédisait une réponse aléatoire/toujours la même réponse ?
+- quelle devrait être la perte/métrique si le modèle prédisait une réponse aléatoire/toujours la même réponse ?
-⚠️ Si vous effectuez un entraînement distribué, imprimez des échantillons de votre ensemble de données dans chaque processus et vérifiez par trois fois que vous obtenez la même chose. Un bug courant consiste à avoir une source d'aléa dans la création des données qui fait que chaque processus a une version différente de l'ensemble de données.
+⚠️ Si vous effectuez un entraînement distribué, imprimez des échantillons de votre ensemble de données dans chaque processus et vérifiez par trois fois que vous obtenez la même chose. Un bug courant consiste à avoir une source d'aléa dans la création des données qui fait que chaque processus a une version différente du jeu de données.
-Après avoir examiné vos données, examinez quelques-unes des prédictions du modèle et décodez-les également. Si le modèle prédit toujours la même chose, c'est peut-être parce que votre ensemble de données est biaisé en faveur d'une catégorie (pour les problèmes de classification) ; des techniques comme le suréchantillonnage de classes rares peuvent aider.
+Après avoir examiné vos données, examinez quelques-unes des prédictions du modèle. Si votre modèle produit des *tokens*, essayez aussi de les décoder ! Si le modèle prédit toujours la même chose, cela peut être dû au fait que votre jeu de données est biaisé en faveur d'une catégorie (pour les problèmes de classification). Des techniques telles que le suréchantillonnage des classes rares peuvent aider. D'autre part, cela peut également être dû à des problèmes d'entraînement tels que de mauvais réglages des hyperparamètres.
-Si la perte/la métrique que vous obtenez sur votre modèle initial est très différente de la perte/la métrique à laquelle vous vous attendez pour des prédictions aléatoires, vérifiez à nouveau la façon dont votre perte ou votre métrique est calculée, car il y a probablement un bug à ce niveau. Si vous utilisez plusieurs pertes que vous ajoutez à la fin, assurez-vous qu'elles sont de la même échelle.
+Si la perte/la métrique que vous obtenez sur votre modèle initial avant entraînement est très différente de la perte/la métrique à laquelle vous vous attendez pour des prédictions aléatoires, vérifiez la façon dont votre perte ou votre métrique est calculée. Il y a probablement un bug. Si vous utilisez plusieurs pertes que vous ajoutez à la fin, assurez-vous qu'elles sont de la même échelle.
Lorsque vous êtes sûr que vos données sont parfaites, vous pouvez voir si le modèle est capable de s'entraîner sur elles grâce à un test simple.
### Surentraînement du modèle sur un seul batch
-Le surentraînement est généralement une chose que nous essayons d'éviter lors de l'entraînement, car cela signifie que le modèle n'apprend pas à reconnaître les caractéristiques générales que nous voulons qu'il reconnaisse, mais qu'il se contente de mémoriser les échantillons d'entraînement. Cependant, essayer d'entraîner votre modèle sur un batch encore et encore est un bon test pour vérifier si le problème tel que vous l'avez formulé peut être résolu par le modèle que vous essayez d'entraîner. Cela vous aidera également à voir si votre taux d'apprentissage initial est trop élevé.
+Le surentraînement est généralement une chose que nous essayons d'éviter lors de l'entraînement car cela signifie que le modèle n'apprend pas à reconnaître les caractéristiques générales que nous voulons qu'il reconnaisse et se contente de mémoriser les échantillons d'entraînement. Cependant, essayer d'entraîner votre modèle sur un batch encore et encore est un bon test pour vérifier si le problème tel que vous l'avez formulé peut être résolu par le modèle que vous essayez d'entraîner. Cela vous aidera également à voir si votre taux d'apprentissage initial est trop élevé.
-Une fois que vous avez défini votre `Trainer`, c'est très facile ; il suffit de prendre un batch de données d'entraînement, puis d'exécuter une petite boucle d'entraînement manuel en utilisant uniquement ce batch pour quelque chose comme 20 étapes :
+Une fois que vous avez défini votre `modèle`, c'est très facile. Il suffit de prendre un batch de données d'entraînement, puis de le traiter comme votre jeu de données entier que vous *finetunez* sur un grand nombre d'époques :
```py
for batch in trainer.get_train_dataloader():
@@ -757,7 +758,7 @@ compute_metrics((preds.cpu().numpy(), labels.cpu().numpy()))
100% de précision, voilà un bel exemple de surentraînement (ce qui signifie que si vous essayez votre modèle sur n'importe quelle autre phrase, il vous donnera très probablement une mauvaise réponse) !
-Si vous ne parvenez pas à ce que votre modèle obtienne des résultats parfaits comme celui-ci, cela signifie qu'il y a quelque chose qui ne va pas dans la façon dont vous avez formulé le problème ou dans vos données, et vous devez donc y remédier. Ce n'est que lorsque vous parviendrez à passer le test de surentraînement que vous pourrez être sûr que votre modèle peut réellement apprendre quelque chose.
+Si vous ne parvenez pas à ce que votre modèle obtienne des résultats parfaits comme celui-ci, cela signifie qu'il y a quelque chose qui ne va pas dans la façon dont vous avez formulé le problème ou dans vos données. Vous devez donc y remédier. Ce n'est que lorsque vous parviendrez à passer le test de surentraînement que vous pourrez être sûr que votre modèle peut réellement apprendre quelque chose.
@@ -765,23 +766,23 @@ Si vous ne parvenez pas à ce que votre modèle obtienne des résultats parfaits
-### Ne réglez rien tant que vous n'avez pas une première ligne de base.
+### Ne réglez rien tant que vous n'avez pas une première ligne de base
-Le réglage des hyperparamètres est toujours considéré comme la partie la plus difficile de l'apprentissage automatique, mais c'est juste la dernière étape pour vous aider à gagner un peu sur la métrique. La plupart du temps, les hyperparamètres par défaut du `Trainer` fonctionneront très bien pour vous donner de bons résultats, donc ne vous lancez pas dans une recherche d'hyperparamètres longue et coûteuse jusqu'à ce que vous ayez quelque chose qui batte la ligne de base que vous avez sur votre jeu de données.
+Le réglage des hyperparamètres est toujours considéré comme la partie la plus difficile de l'apprentissage automatique mais c'est juste la dernière étape pour vous aider à gagner un peu sur la métrique. La plupart du temps, les hyperparamètres par défaut du `Trainer` fonctionneront très bien pour vous donner de bons résultats. Donc ne vous lancez pas dans une recherche d'hyperparamètres longue et coûteuse jusqu'à ce que vous ayez quelque chose qui batte la ligne de base que vous avez sur votre jeu de données.
-Une fois que vous avez un modèle suffisamment bon, vous pouvez commencer à l'affiner un peu. N'essayez pas de lancer un millier d'exécutions avec différents hyperparamètres, mais comparez quelques exécutions avec différentes valeurs pour un hyperparamètre afin de vous faire une idée de celui qui a le plus d'impact.
+Une fois que vous avez un modèle suffisamment bon, vous pouvez commencer à le *finetuner* un peu. N'essayez pas de lancer un millier d'exécutions avec différents hyperparamètres mais comparez quelques exécutions avec différentes valeurs pour un hyperparamètre afin de vous faire une idée de celui qui a le plus d'impact.
Si vous modifiez le modèle lui-même, restez simple et n'essayez rien que vous ne puissiez raisonnablement justifier. Veillez toujours à revenir au test de surentraînement pour vérifier que votre modification n'a pas eu de conséquences inattendues.
### Demander de l'aide
-Nous espérons que vous avez trouvé dans cette section des conseils qui vous ont aidé à résoudre votre problème, mais si ce n'est pas le cas, n'oubliez pas que vous pouvez toujours demander de l'aide à la communauté sur le [forum](https://discuss.huggingface.co/).
+Nous espérons que vous avez trouvé dans cette section des conseils qui vous ont aidé à résoudre votre problème. Si ce n'est pas le cas, n'oubliez pas que vous pouvez toujours demander de l'aide à la communauté sur le [forum](https://discuss.huggingface.co/).
Voici quelques ressources (en anglais) supplémentaires qui peuvent s'avérer utiles :
-- ["La reproductibilité comme vecteur des meilleures pratiques d'ingénierie"](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) par Joel Grus
-- ["Liste de contrôle pour le débogage des réseaux neuronaux"](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) par Cecelia Shao
-- ["Comment tester unitairement le code d'apprentissage automatique"](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) par Chase Roberts
-- ["Une recette pour Entraîner les réseaux neuronaux"](http://karpathy.github.io/2019/04/25/recipe/) par Andrej Karpathy
+- [La reproductibilité comme vecteur des meilleures pratiques d'ingénierie](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) par Joel Grus
+- [Liste de contrôle pour le débogage des réseaux de neurones](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) par Cecelia Shao
+- [Comment tester unitairement le code d'apprentissage automatique](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) par Chase Roberts
+- [Une recette pour entraîner les réseaux de neurones](http://karpathy.github.io/2019/04/25/recipe/) par Andrej Karpathy
-Bien sûr, tous les problèmes rencontrés lors de l'Entraînement des réseaux neuronaux ne sont pas forcément de votre faute ! Si vous rencontrez quelque chose dans la bibliothèque 🤗 *Transformers* ou 🤗 *Datasets* qui ne semble pas correct, vous avez peut-être rencontré un bogue. Vous devez absolument nous en parler, et dans la section suivante, nous allons vous expliquer exactement comment faire.
+Bien sûr, tous les problèmes rencontrés lors de l'entraînement ne sont pas forcément de votre faute ! Si vous rencontrez quelque chose dans la bibliothèque 🤗 *Transformers* ou 🤗 *Datasets* qui ne semble pas correct, vous avez peut-être trouver un *bug*. Vous devez absolument nous en parler pour qu'on puisse le corriger. Dans la section suivante, nous allons vous expliquer exactement comment faire.
diff --git a/chapters/fr/chapter8/4_tf.mdx b/chapters/fr/chapter8/4_tf.mdx
index b1a01e75a..e178f6842 100644
--- a/chapters/fr/chapter8/4_tf.mdx
+++ b/chapters/fr/chapter8/4_tf.mdx
@@ -9,17 +9,17 @@
{label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/chapter8/section4_tf.ipynb"},
]} />
-Vous avez écrit un magnifique script pour entraîner ou *finetuner* un modèle sur une tâche donnée, en suivant consciencieusement les conseils du [Chapitre 7](/course/fr/chapter7). Mais lorsque vous lancez la commande `model.fit()`, quelque chose d'horrible se produit : vous obtenez une erreur 😱 ! Ou pire, tout semble aller bien et l'entraînement se déroule sans erreur, mais le modèle résultant est merdique. Dans cette section, nous allons vous montrer ce que vous pouvez faire pour déboguer ce genre de problèmes.
+Vous avez écrit un magnifique script pour entraîner ou *finetuner* un modèle sur une tâche donnée en suivant consciencieusement les conseils du [chapitre 7](/course/fr/chapter7). Mais lorsque vous lancez la commande `model.fit()`, quelque chose d'horrible se produit : vous obtenez une erreur 😱 ! Ou pire, tout semble aller bien et l'entraînement se déroule sans erreur mais le modèle résultant est mauvais. Dans cette section, nous allons vous montrer ce que vous pouvez faire pour déboguer ce genre de problèmes.
## Déboguer le pipeline d'entraînement
-Le problème lorsque vous rencontrez une erreur dans `trainer.train()` est qu'elle peut provenir de plusieurs sources, car le `Trainer` assemble généralement des batchs de choses. Il convertit les jeux de données en chargeurs de données, donc le problème pourrait être quelque chose d'erroné dans votre jeu de données, ou un problème en essayant de regrouper les éléments des jeux de données ensemble. Ensuite, il prend un batch de données et le transmet au modèle, le problème peut donc se situer dans le code du modèle. Après cela, il calcule les gradients et effectue l'étape d'optimisation, le problème peut donc également se situer dans votre optimiseur. Et même si tout se passe bien pendant l'entraînement, quelque chose peut encore mal tourner pendant l'évaluation si votre métrique pose problème.
+Le problème lorsque vous rencontrez une erreur dans `trainer.train()` est qu'elle peut provenir de plusieurs sources, car la fonction `Trainer` assemble généralement des batchs de choses. Elle convertit les jeux de données en chargeurs de données donc le problème pourrait être quelque chose d'erroné dans votre jeu de données, ou un problème en essayant de regrouper les éléments des jeux de données ensemble. Ensuite, elle prend un batch de données et le transmet au modèle, le problème peut donc se situer dans le code du modèle. Après cela, elle calcule les gradients et effectue l'étape d'optimisation, le problème peut donc également se situer dans votre optimiseur. Et même si tout se passe bien pendant l'entraînement, quelque chose peut encore mal tourner pendant l'évaluation si votre métrique pose problème.
La meilleure façon de déboguer une erreur qui survient dans `trainer.train()` est de passer manuellement en revue tout le pipeline pour voir où les choses se sont mal passées. L'erreur est alors souvent très facile à résoudre.
-Pour le démontrer, nous utiliserons le script suivant qui tente d'ajuster un modèle DistilBERT sur le [jeu de données MNLI](https://huggingface.co/datasets/glue) :
+Pour le démontrer, nous utiliserons le script suivant qui tente de *finetuner* un modèle DistilBERT sur le [jeu de données MNLI](https://huggingface.co/datasets/glue) :
```py
from datasets import load_dataset, load_metric
@@ -55,28 +55,28 @@ model.compile(loss="sparse_categorical_crossentropy", optimizer="adam")
model.fit(train_dataset)
```
-Si vous essayez de l'exécuter, il se peut que vous obteniez des `VisibleDeprecationWarning`s lors de la conversion du jeu de données. Il s'agit d'un problème UX connu que nous avons, donc veuillez l'ignorer. Si vous lisez le cours après, disons, novembre 2021 et que cela se produit encore, envoyez des tweets de rage à @carrigmat jusqu'à ce qu'il le corrige.
+Si vous essayez de l'exécuter, il se peut que vous obteniez des `VisibleDeprecationWarning`s lors de la conversion du jeu de données. Il s'agit d'un problème UX connu par l'équipe d'Hugging Face, donc veuillez l'ignorer. Si vous lisez le cours après novembre 2021 et que cela se produit encore, envoyez des tweets de rage à @carrigmat jusqu'à ce qu'il le corrige.
-Le problème le plus grave, cependant, c'est que nous avons une erreur flagrante. Et c'est vraiment, terriblement long :
+Le problème cependant est que nous avons une erreur flagrante. Et c'est vraiment, terriblement long :
```python out
ValueError: No gradients provided for any variable: ['tf_distil_bert_for_sequence_classification/distilbert/embeddings/word_embeddings/weight:0', '...']
```
-Qu'est-ce que cela signifie ? Nous avons essayé de nous entraîner sur nos données, mais nous n'avons pas obtenu de gradient ? C'est assez déconcertant ; comment commencer à déboguer quelque chose comme ça ? Lorsque l'erreur que vous obtenez ne suggère pas immédiatement l'origine du problème, la meilleure solution consiste souvent à procéder par étapes, en s'assurant à chaque fois que tout semble correct. Et bien sûr, il faut toujours commencer par...
+Qu'est-ce que cela signifie ? Nous avons essayé d'entraîner sur nos données mais nous n'avons pas obtenu de gradient. C'est assez déconcertant. Comment commencer à déboguer quelque chose comme ça ? Lorsque l'erreur que vous obtenez ne suggère pas immédiatement l'origine du problème, la meilleure solution consiste souvent à procéder par étapes, en s'assurant à chaque fois que tout semble correct. Et bien sûr, il faut toujours commencer par...
### Vérifier vos données
Cela va sans dire, mais si vos données sont corrompues, Keras ne sera pas en mesure de les réparer pour vous. Avant toute chose, vous devez donc jeter un coup d'œil à ce que contient votre ensemble d'entraînement.
-Bien qu'il soit tentant de regarder dans les `raw_datasets` et les `tokenized_datasets`, nous vous recommandons fortement d'aller voir les données au moment où elles vont entrer dans le modèle. Cela signifie lire une sortie du `tf.data.Dataset` que vous avez créé avec la fonction `to_tf_dataset()` ! Alors comment faire ? Les objets `tf.data.Dataset` nous donnent des batchs entiers à la fois et ne supportent pas l'indexation, donc nous ne pouvons pas simplement demander `train_dataset[0]`. Nous pouvons, cependant, lui demander poliment un batch :
+Bien qu'il soit tentant de regarder dans `raw_datasets` et `tokenized_datasets`, nous vous recommandons fortement d'aller voir les données au moment où elles vont entrer dans le modèle. Cela signifie lire une sortie du `tf.data.Dataset` que vous avez créé avec la fonction `to_tf_dataset()` ! Alors comment faire ? Les objets `tf.data.Dataset` nous donnent des batchs entiers à la fois et ne supportent pas l'indexation, donc nous ne pouvons pas simplement demander `train_dataset[0]`. Nous pouvons, cependant, lui demander poliment un batch :
```py
for batch in train_dataset:
break
```
-`break` ends the loop after one iteration, so this grabs the first batch that comes out of `train_dataset` and saves it as `batch`. Now, let's take a look at what's inside:
+`break` termine la boucle après une itération, donc cela prend le premier batch qui sort de `train_dataset` et l'enregistre comme `batch`. Maintenant, jetons un coup d'oeil à ce qu'il y a à l'intérieur :
```python out
{'attention_mask': }
```
-Cela semble correct, n'est-ce pas ? Nous passons les `labels`, `attention_mask`, et `input_ids` au modèle, ce qui devrait être tout ce dont il a besoin pour calculer les sorties et la perte. Alors pourquoi n'avons-nous pas de gradient ? Regardez de plus près : nous passons un seul dictionnaire en entrée, mais un batch d'entraînement est généralement un tenseur ou un dictionnaire d'entrée, plus un tenseur d'étiquettes. Nos étiquettes sont juste une clé dans notre dictionnaire d'entrée.
+Cela semble correct. Nous passons les `labels`, `attention_mask`, et `input_ids` au modèle, ce qui devrait être tout ce dont il a besoin pour calculer les sorties et la perte. Alors pourquoi n'avons-nous pas de gradient ? Regardez de plus près : nous passons un seul dictionnaire en entrée mais un batch d'entraînement est généralement un tenseur ou un dictionnaire d'entrée, plus un tenseur d'étiquettes. Nos étiquettes sont juste une clé dans notre dictionnaire d'entrée.
-Est-ce un problème ? Pas toujours, en fait ! Mais c'est l'un des problèmes les plus courants que vous rencontrerez lorsque vous entraînerez des modèles Transformer avec TensorFlow. Nos modèles peuvent tous calculer la perte en interne, mais pour ce faire, les étiquettes doivent être transmises dans le dictionnaire d'entrée. C'est la perte qui est utilisée lorsque nous ne spécifions pas de valeur de perte à `compile()`. Keras, d'autre part, s'attend généralement à ce que les étiquettes soient passées séparément du dictionnaire d'entrée, et les calculs de perte échoueront généralement si vous ne le faites pas.
+Est-ce un problème ? Pas toujours, en fait ! Mais c'est l'un des problèmes les plus courants que vous rencontrerez lorsque vous entraînerez des *transformers* avec TensorFlow. Nos modèles peuvent tous calculer la perte en interne, mais pour ce faire, les étiquettes doivent être transmises dans le dictionnaire d'entrée. C'est la perte qui est utilisée lorsque nous ne spécifions pas de valeur de perte à `compile()`. Keras, d'autre part, s'attend généralement à ce que les étiquettes soient passées séparément du dictionnaire d'entrée, et les calculs de perte échoueront généralement si vous ne le faites pas.
Le problème est maintenant devenu plus clair : nous avons passé un argument `loss`, ce qui signifie que nous demandons à Keras de calculer les pertes pour nous, mais nous avons passé nos étiquettes comme entrées au modèle, et non comme étiquettes à l'endroit où Keras les attend ! Nous devons choisir l'un ou l'autre : soit nous utilisons la perte interne du modèle et gardons les étiquettes où elles sont, soit nous continuons à utiliser les pertes de Keras, mais nous déplaçons les étiquettes à l'endroit où Keras les attend. Pour simplifier, prenons la première approche. Changez l'appel à `compile()` pour lire :
@@ -108,11 +108,11 @@ Le problème est maintenant devenu plus clair : nous avons passé un argument `l
model.compile(optimizer="adam")
```
-Maintenant, nous allons utiliser la perte interne du modèle, et ce problème devrait être résolu !
+Maintenant, nous allons utiliser la perte interne du modèle et ce problème devrait être résolu !
-✏️ *A votre tour !* Comme défi optionnel après avoir résolu les autres problèmes, vous pouvez essayer de revenir à cette étape et faire fonctionner le modèle avec la perte originale calculée par Keras au lieu de la perte interne. Vous devrez ajouter `"labels"` à l'argument `label_cols` de `to_tf_dataset()` pour vous assurer que les labels sont correctement sortis, ce qui vous donnera des gradients -- mais il y a un autre problème avec la perte que nous avons spécifiée. L'Entraînement fonctionnera toujours avec ce problème, mais l'apprentissage sera très lent et se stabilisera à une perte d'entraînement élevée. Pouvez-vous trouver ce que c'est ?
+✏️ *A votre tour !* Comme défi optionnel après avoir résolu les autres problèmes, vous pouvez essayer de revenir à cette étape et faire fonctionner le modèle avec la perte originale calculée par Keras au lieu de la perte interne. Vous devrez ajouter `"labels"` à l'argument `label_cols` de `to_tf_dataset()` pour vous assurer que les labels sont correctement sortis, ce qui vous donnera des gradients. Mais il y a un autre problème avec la perte que nous avons spécifiée. L'entraînement fonctionnera toujours avec ce problème mais l'apprentissage sera très lent et se stabilisera à une perte d'entraînement élevée. Pouvez-vous trouver ce que c'est ?
Un indice codé en ROT13, si vous êtes coincé : Vs lbh ybbx ng gur bhgchgf bs FrdhraprPynffvsvpngvba zbqryf va Genafsbezref, gurve svefg bhgchg vf `ybtvgf`. Jung ner ybtvgf ?
@@ -120,7 +120,7 @@ Et un deuxième indice : Jura lbh fcrpvsl bcgvzvmref, npgvingvbaf be ybffrf jvgu
-Maintenant, essayons de nous entraîner. Nous devrions obtenir des gradients maintenant, donc avec un peu de chance (la musique de mauvais augure joue ici) nous pouvons juste appeler `model.fit()` et tout fonctionnera bien !
+Maintenant, essayons d'entraîner. Nous devrions obtenir des gradients maintenant, donc avec un peu de chance nous pouvons juste appeler `model.fit()` et tout fonctionnera bien !
```python out
246/24543 [..............................] - ETA: 15:52 - loss: nan
@@ -128,11 +128,11 @@ Maintenant, essayons de nous entraîner. Nous devrions obtenir des gradients mai
Oh non.
-`nan` n'est pas une valeur de perte très encourageante. Pourtant, nous avons vérifié nos données, et elles semblent plutôt bonnes. Si ce n'est pas le problème, quelle est la prochaine étape ? La prochaine étape évidente est de...
+`nan` n'est pas une valeur de perte très encourageante. Pourtant, nous avons vérifié nos données et elles semblent plutôt bonnes. Si ce n'est pas le problème, quelle est la prochaine étape ? La prochaine étape évidente est de...
### Vérifier votre modèle
-`model.fit()` est une fonction très pratique dans Keras, mais elle fait beaucoup de choses pour vous, et cela peut rendre plus difficile de trouver exactement où un problème est survenu. Si vous déboguez votre modèle, une stratégie qui peut vraiment vous aider est de passer un seul batch au modèle et d'examiner les sorties de ce batch en détail. Une autre astuce vraiment utile si le modèle jette des erreurs est de `compiler()` le modèle avec `run_eagerly=True`. Cela le rendra beaucoup plus lent, mais les messages d'erreur seront beaucoup plus compréhensibles, car ils indiqueront exactement où le problème est survenu dans le code de votre modèle.
+`model.fit()` est une fonction très pratique dans Keras, mais elle fait beaucoup de choses pour vous. Cela peut rendre plus difficile de trouver exactement où un problème est survenu. Si vous déboguez votre modèle, une stratégie qui peut vraiment vous aider est de passer un seul batch au modèle et d'examiner les sorties de ce batch en détail. Une autre astuce vraiment utile est de `compiler()` le modèle avec `run_eagerly=True`. Cela le rendra beaucoup plus lent mais les messages d'erreur seront beaucoup plus compréhensibles car ils indiqueront exactement où le problème est survenu dans le code de votre modèle.
Pour l'instant, cependant, nous n'avons pas besoin de `run_eagerly`. Exécutons le `batch` que nous avons obtenu précédemment à travers le modèle et voyons à quoi ressemblent les résultats :
@@ -162,7 +162,8 @@ array([[nan, nan],
[nan, nan]], dtype=float32)>, hidden_states=None, attentions=None)
```
-Eh bien, c'est délicat. Tout est "nan" ! Mais c'est étrange, n'est-ce pas ? Comment tous nos logits pourraient-ils devenir `nan` ? "NAN" signifie "not a number". Les valeurs `nan` apparaissent souvent quand on effectue une opération interdite, comme la division par zéro. Mais une chose très importante à savoir sur `nan` en apprentissage automatique est que cette valeur a tendance à *se propager*. Si vous multipliez un nombre par `nan`, le résultat sera également `nan`. Et si vous obtenez une valeur `nan` n'importe où dans votre sortie, votre perte ou votre gradient, alors elle se propagera rapidement à travers tout votre modèle. Ceci parce que lorsque cette valeur `nan` est propagée à travers votre réseau, vous obtiendrez des gradients `nan`, et lorsque les mises à jour des poids sont calculées avec ces gradients, vous obtiendrez des poids `nan`, et ces poids calculeront encore plus de sorties `nan` ! Très vite, le réseau entier ne sera plus qu'un gros bloc de `nan`. Une fois que cela arrive, il est assez difficile de voir où le problème a commencé. Comment peut-on isoler l'endroit où les `nan` se sont introduits en premier ?
+Eh bien, c'est délicat. Tout est "nan" ! Mais c'est étrange, n'est-ce pas ? Comment tous nos logits pourraient-ils devenir `nan` ? "NAN" signifie "*not a number*". Les valeurs `nan` apparaissent souvent quand on effectue une opération interdite comme la division par zéro. Mais une chose très importante à savoir sur `nan` en apprentissage automatique est que cette valeur a tendance à *se propager*. Si vous multipliez un nombre par `nan`, le résultat sera également `nan`. Et si vous obtenez une valeur `nan` n'importe où dans votre sortie, votre perte ou votre gradient, alors elle se propagera rapidement à travers tout votre modèle.
+Ceci parce que lorsque cette valeur `nan` est propagée à travers votre réseau, vous obtiendrez des gradients `nan`, et lorsque les mises à jour des poids sont calculées avec ces gradients, vous obtiendrez des poids `nan`, et ces poids calculeront encore plus de sorties `nan` ! Très vite, le réseau entier ne sera plus qu'un gros bloc de `nan`. Une fois que cela arrive, il est assez difficile de voir où le problème a commencé. Comment peut-on isoler l'endroit où les `nan` se sont introduits en premier ?
La réponse est d'essayer de *reinitialiser* notre modèle. Une fois que nous avons commencé l'entraînement, nous avons eu un `nan` quelque part et il s'est rapidement propagé à travers tout le modèle. Donc, chargeons le modèle à partir d'un checkpoint et ne faisons aucune mise à jour de poids, et voyons où nous obtenons une valeur `nan` :
@@ -197,7 +198,7 @@ array([[-0.04761693, -0.06509043],
[-0.08141848, -0.07110836]], dtype=float32)>, hidden_states=None, attentions=None)
```
-*Maintenant* on arrive à quelque chose ! Il n'y a pas de valeurs `nan` dans nos logits, ce qui est rassurant. Mais nous voyons quelques valeurs `nan` dans notre perte ! Y a-t-il quelque chose dans ces échantillons en particulier qui cause ce problème ? Voyons de quels échantillons il s'agit (notez que si vous exécutez ce code vous-même, vous pouvez obtenir des indices différents parce que l'ensemble de données a été mélangé) :
+*Maintenant* on arrive à quelque chose ! Il n'y a pas de valeurs `nan` dans nos logits, ce qui est rassurant. Mais nous voyons quelques valeurs `nan` dans notre perte ! Y a-t-il quelque chose dans ces échantillons en particulier qui cause ce problème ? Voyons de quels échantillons il s'agit (notez que si vous exécutez ce code vous-même, vous pouvez obtenir des indices différents parce que le jeu de données a été mélangé) :
```python
import numpy as np
@@ -211,7 +212,7 @@ indices
array([ 1, 2, 5, 7, 9, 10, 11, 13, 14])
```
-Let's look at the samples these indices came from:
+Examinons les échantillons d'où proviennent ces indices :
```python
input_ids = batch["input_ids"].numpy()
@@ -311,7 +312,7 @@ array([[ 101, 2007, 2032, 2001, 1037, 16480, 3917, 2594, 4135,
0, 0, 0, 0]])
```
-Il y a beaucoup de batchs ici, mais rien d'inhabituel. Regardons les étiquettes :
+Il y a beaucoup de batchs ici mais rien d'inhabituel. Regardons les étiquettes :
```python out
labels = batch['labels'].numpy()
@@ -322,7 +323,7 @@ labels[indices]
array([2, 2, 2, 2, 2, 2, 2, 2, 2])
```
-Ah ! Les échantillons `nan` ont tous le même label, et c'est le label 2. C'est un indice très fort. Le fait que nous n'obtenions une perte de `nan` que lorsque notre étiquette est 2 suggère que c'est un très bon moment pour vérifier le nombre d'étiquettes dans notre modèle :
+Ah ! Les échantillons `nan` ont tous le même label. C'est un gros indice. Le fait que nous n'obtenions une perte de `nan` que lorsque notre étiquette vaut 2 suggère que c'est un très bon moment pour vérifier le nombre d'étiquettes dans notre modèle :
```python
model.config.num_labels
@@ -332,7 +333,7 @@ model.config.num_labels
2
```
-Nous voyons maintenant le problème : le modèle pense qu'il n'y a que deux classes, mais les étiquettes vont jusqu'à 2, ce qui signifie qu'il y a en fait trois classes (car 0 est aussi une classe). C'est ainsi que nous avons obtenu un `nan` - en essayant de calculer la perte pour une classe inexistante ! Essayons de changer cela et de réajuster le modèle :
+Nous voyons maintenant le problème : le modèle pense qu'il n'y a que deux classes, mais les étiquettes vont jusqu'à 2, ce qui signifie qu'il y a en fait trois classes (car 0 est aussi une classe). C'est ainsi que nous avons obtenu un `nan`. En essayant de calculer la perte pour une classe inexistante ! Essayons de changer cela et de réajuster le modèle :
```
model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)
@@ -344,15 +345,15 @@ model.fit(train_dataset)
869/24543 [>.............................] - ETA: 15:29 - loss: 1.1032
```
-On s'entraîne ! Plus de `nan`, et nos pertes diminuent... en quelque sorte. Si vous le regardez pendant un certain temps, vous pouvez commencer à vous impatienter, car la valeur des pertes reste obstinément élevée. Arrêtons l'entraînement ici et essayons de réfléchir à ce qui pourrait causer ce problème. À ce stade, nous sommes pratiquement sûrs que les données et le modèle sont corrects, mais notre modèle n'apprend pas bien. Que reste-t-il d'autre ? Il est temps de...
+On entraîne ! Plus de `nan` et nos pertes diminuent... en quelque sorte. Si vous regardez pendant un certain temps, vous pouvez commencer à vous impatienter car la valeur des pertes reste obstinément élevée. Arrêtons l'entraînement ici et essayons de réfléchir à ce qui pourrait causer ce problème. À ce stade, nous sommes pratiquement sûrs que les données et le modèle sont corrects, mais notre modèle n'apprend pas bien. Que reste-t-il d'autre ? Il est temps de...
-### Vérifier vos hyperparamètres
+### Vérifier les hyperparamètres
-Si vous regardez le code ci-dessus, vous ne verrez peut-être aucun hyperparamètre, sauf peut-être le `batch_size`, et cela ne semble pas être un coupable probable. Ne soyez pas dupe, cependant ; il y a toujours des hyperparamètres, et si vous ne pouvez pas les voir, cela signifie simplement que vous ne savez pas à quoi ils sont réglés. En particulier, souvenez-vous d'une chose essentielle à propos de Keras : si vous définissez une fonction de perte, d'optimisation ou d'activation avec une chaîne, _tous ses arguments seront définis sur leurs valeurs par défaut_. Cela signifie que, même si l'utilisation de chaînes de caractères est très pratique, vous devez être très prudent, car cela peut facilement vous cacher des éléments critiques. (Toute personne essayant le défi optionnel ci-dessus devrait prendre bonne note de ce fait).
+Si vous regardez le code ci-dessus, vous ne verrez peut-être aucun hyperparamètre, sauf peut-être le `batch_size` qui ne semble pas être un coupable probable. Cependant, ne soyez pas dupe, il y a toujours des hyperparamètres. Si vous ne pouvez pas les voir, cela signifie simplement que vous ne connaissez pas leur réglage. En particulier, souvenez-vous d'une chose essentielle à propos de Keras : si vous définissez une fonction de perte, d'optimisation ou d'activation avec une chaîne, _tous ses arguments seront définis sur leurs valeurs par défaut_. Cela signifie que, même si l'utilisation de chaînes de caractères est très pratique, vous devez être très prudent car cela peut facilement vous cacher des éléments critiques. (Toute personne essayant le défi optionnel ci-dessus devrait prendre bonne note de ce fait).
-Dans ce cas, où avons-nous défini un argument avec une chaîne ? Au départ, nous définissions la perte avec une chaîne, mais nous ne le faisons plus. Cependant, nous définissons l'optimiseur avec une chaîne de caractères. Cela pourrait-il nous cacher quelque chose ? Jetons un coup d'œil à [ses arguments](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam).
+Dans ce cas, où avons-nous défini un argument avec une chaîne de caractères ? Au départ, nous définissions la perte avec une chaîne de caractères, mais nous ne le faisons plus. Cependant, nous le faisons pour l'optimiseur. Cela pourrait-il nous cacher quelque chose ? Jetons un coup d'œil à [ses arguments](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam).
-Y a-t-il quelque chose qui ressort ? C'est exact : le taux d'apprentissage ! Lorsque nous utilisons simplement la chaîne `'adam'`, nous allons obtenir le taux d'apprentissage par défaut, qui est de 0.001, ou 1e-3. C'est beaucoup trop élevé pour un modèle Transformer ! En général, nous recommandons d'essayer des taux d'apprentissage entre 1e-5 et 1e-4 pour vos modèles ; c'est quelque part entre 10X et 100X plus petit que la valeur que nous utilisons ici. Cela semble être un problème majeur, alors essayons de le réduire. Pour ce faire, nous devons importer l'objet `optimizer`. Pendant que nous y sommes, réinitialisons le modèle à partir du point de contrôle, au cas où l'entraînement avec un taux d'apprentissage élevé aurait endommagé ses poids :
+Y a-t-il quelque chose qui ressort ? C'est exact : le taux d'apprentissage ! Lorsque nous indiquons simplement `'adam'` nous allons obtenir le taux d'apprentissage par défaut qui est de 0.001 (ou 1e-3). C'est beaucoup trop élevé pour un *transformer* ! En général, nous recommandons d'essayer des taux d'apprentissage entre 1e-5 et 1e-4 pour vos modèles soit entre 10X et 100X plus petit que la valeur que nous utilisons ici. Cela semble être un problème majeur, alors essayons de le réduire. Pour ce faire, nous devons importer l'objet `optimizer`. Pendant que nous y sommes, réinitialisons le modèle à partir du *checkpoint* au cas où l'entraînement avec un taux d'apprentissage élevé aurait endommagé ses poids :
```python
from tensorflow.keras.optimizers import Adam
@@ -363,11 +364,11 @@ model.compile(optimizer=Adam(5e-5))
-💡 Vous pouvez également importer la fonction `create_optimizer()` de 🤗 *Transformers*, qui vous donnera un optimiseur AdamW avec une décroissance de poids correcte ainsi qu'un réchauffement et une décroissance du taux d'apprentissage. Cet optimiseur produira souvent des résultats légèrement meilleurs que ceux que vous obtenez avec l'optimiseur Adam par défaut.
+💡 Vous pouvez également importer la fonction `create_optimizer()` de 🤗 Transformers qui vous donnera un optimiseur AdamW avec une décroissance du taux des poids correcte ainsi qu'un réchauffement et une décroissance du taux d'apprentissage. Cet optimiseur produira souvent des résultats légèrement meilleurs que ceux que vous obtenez avec l'optimiseur Adam par défaut.
-Maintenant, nous pouvons essayer d'ajuster le modèle avec le nouveau taux d'apprentissage amélioré :
+Maintenant, nous pouvons essayer de *finetuner* le modèle avec le nouveau taux d'apprentissage :
```python
model.fit(train_dataset)
@@ -377,7 +378,7 @@ model.fit(train_dataset)
319/24543 [..............................] - ETA: 16:07 - loss: 0.9718
```
-Maintenant notre perte va vraiment aller quelque part ! L'entraînement semble enfin fonctionner. Il y a une leçon à tirer ici : lorsque votre modèle fonctionne mais que la perte ne diminue pas, et que vous êtes sûr que vos données sont correctes, c'est une bonne idée de vérifier les hyperparamètres comme le taux d'apprentissage et la décroissance du poids. Un réglage trop élevé de l'un ou l'autre de ces paramètres risque fort de faire " caler " l'entraînement à une valeur de perte élevée.
+Maintenant notre perte va vraiment aller quelque part ! L'entraînement semble enfin fonctionner. Il y a une leçon à tirer ici : lorsque votre modèle fonctionne mais que la perte ne diminue pas, et que vous êtes sûr que vos données sont correctes, c'est une bonne idée de vérifier les hyperparamètres comme le taux d'apprentissage et le taux de décroissance des poids. Un réglage trop élevé de l'un ou l'autre de ces paramètres risque fort de faire « caler » l'entraînement à une valeur de perte élevée.
## Autres problèmes potentiels
@@ -385,26 +386,26 @@ Nous avons couvert les problèmes dans le script ci-dessus, mais il existe plusi
### Gérer les erreurs de manque de mémoire
-Le signe révélateur d'un manque de mémoire est une erreur du type "OOM when allocating tensor" -- OOM est l'abréviation de "out of memory". Il s'agit d'un risque très courant lorsque l'on traite de grands modèles de langage. Si vous rencontrez ce problème, une bonne stratégie consiste à diviser par deux la taille de votre batch et à réessayer. Gardez à l'esprit, cependant, que certains modèles sont *très* grands. Par exemple, le modèle GPT-2 complet possède 1,5 Go de paramètres, ce qui signifie que vous aurez besoin de 6 Go de mémoire rien que pour stocker le modèle, et 6 autres Go pour ses gradients ! Entraîner le modèle GPT-2 complet nécessite généralement plus de 20 Go de VRAM, quelle que soit la taille du batch utilisé, ce dont seuls quelques GPU sont dotés. Des modèles plus légers comme `distilbert-base-cased` sont beaucoup plus faciles à exécuter, et s'entraînent aussi beaucoup plus rapidement.
+Le signe révélateur d'un manque de mémoire est une erreur du type "OOM when allocating tensor" (OOM étant l'abréviation de *out of memory*). Il s'agit d'un risque très courant lorsque l'on utilise de grands modèles de langage. Si vous rencontrez ce problème, une bonne stratégie consiste à diviser par deux la taille de votre batch et à réessayer. Gardez à l'esprit, cependant, que certains modèles sont *très* grands. Par exemple, le modèle GPT-2 complet possède 1,5 Go de paramètres, ce qui signifie que vous aurez besoin de 6 Go de mémoire rien que pour stocker le modèle, et 6 autres Go pour ses gradients ! Entraîner le modèle GPT-2 complet nécessite généralement plus de 20 Go de VRAM, quelle que soit la taille du batch utilisé, ce dont seuls quelques GPUs sont dotés. Des modèles plus légers comme `distilbert-base-cased` sont beaucoup plus faciles à exécuter et s'entraînent aussi beaucoup plus rapidement.
-Dans la prochaine partie du cours, nous examinerons des techniques plus avancées qui peuvent vous aider à réduire votre empreinte mémoire et vous permettre d'affiner les plus grands modèles.
+Dans la prochaine partie du cours, nous examinerons des techniques plus avancées qui peuvent vous aider à réduire votre empreinte mémoire et vous permettre de finetuner les plus grands modèles.
-### Hungry Hungry TensorFlow 🦛
+### TensorFlow affamé 🦛
-Une bizarrerie particulière de TensorFlow dont vous devez être conscient est qu'il s'alloue *toute* la mémoire de votre GPU dès que vous chargez un modèle ou que vous effectuez un entraînement, puis il divise cette mémoire selon les besoins. Ce comportement est différent de celui d'autres frameworks, comme PyTorch, qui alloue la mémoire selon les besoins avec CUDA plutôt que de le faire en interne. L'un des avantages de l'approche de TensorFlow est qu'elle peut souvent donner des erreurs utiles lorsque vous manquez de mémoire, et qu'elle peut récupérer de cet état sans planter tout le noyau CUDA. Mais il y a aussi un inconvénient important : si vous exécutez deux processus TensorFlow en même temps, alors **vous allez passer un mauvais moment**.
+Une bizarrerie particulière de TensorFlow dont vous devez être conscient est qu'il s'alloue *toute* la mémoire de votre GPU dès que vous chargez un modèle ou que vous effectuez un entraînement. Puis il divise cette mémoire selon les besoins. Ce comportement est différent de celui d'autres *frameworks*, comme PyTorch, qui alloue la mémoire selon les besoins avec CUDA plutôt que de le faire en interne. L'un des avantages de l'approche de TensorFlow est qu'elle peut souvent donner des erreurs utiles lorsque vous manquez de mémoire et qu'elle peut récupérer de cet état sans planter tout le noyau CUDA. Mais il y a aussi un inconvénient important : si vous exécutez deux processus TensorFlow en même temps alors **vous allez passer un mauvais moment**.
-Si vous travaillez sur Colab, vous n'avez pas à vous soucier de cela, mais si vous travaillez localement, vous devez absolument faire attention. En particulier, sachez que la fermeture d'un onglet de notebook n'entraîne pas nécessairement la fermeture de ce *notebook* ! Vous devrez peut-être sélectionner les blocs-notes en cours d'exécution (ceux qui ont une icône verte) et les fermer manuellement dans la liste des répertoires. Tout *notebook* en cours d'exécution qui utilisait TensorFlow peut encore utiliser une grande partie de la mémoire de votre GPU, ce qui signifie que tout nouveau notebook que vous démarrez peut rencontrer des problèmes très étranges.
+Si vous travaillez sur Colab, vous n'avez pas à vous soucier de cela. Si vous travaillez localement, vous devez absolument faire attention. En particulier, sachez que la fermeture d'un onglet de *notebook* n'entraîne pas nécessairement la fermeture de ce *notebook* ! Vous devrez peut-être sélectionner les *notebooks* en cours d'exécution (ceux qui ont une icône verte) et les fermer manuellement dans la liste des répertoires. Tout *notebook* en cours d'exécution qui utilisait TensorFlow peut encore utiliser une grande partie de la mémoire de votre GPU, ce qui signifie que tout nouveau *notebook* que vous démarrez peut rencontrer des problèmes très étranges.
-Si vous commencez à obtenir des erreurs concernant CUDA, BLAS ou cuBLAS dans du code qui fonctionnait auparavant, c'est très souvent le coupable. Vous pouvez utiliser une commande comme `nvidia-smi` pour vérifier - quand vous éteignez ou redémarrez votre *notebook* actuel, est-ce que la plupart de votre mémoire est libre, ou est-elle toujours utilisée ? Si elle est toujours utilisée, c'est que quelque chose d'autre s'y accroche !
+Si vous commencez à obtenir des erreurs concernant CUDA, BLAS ou cuBLAS dans du code qui fonctionnait auparavant, c'est très souvent le coupable. Vous pouvez utiliser une commande comme `nvidia-smi` pour vérifier si la plupart de votre mémoire est libre ou toujours utilisée. Si elle est toujours utilisée, c'est que quelque chose d'autre s'y accroche !
### Vérifiez vos données (encore !)
-Votre modèle n'apprendra quelque chose que s'il est réellement possible d'apprendre quelque chose de vos données. S'il y a un bug qui corrompt les données ou si les étiquettes sont attribuées de manière aléatoire, il est très probable que vous n'obtiendrez aucun entraînement de modèle sur votre jeu de données. Un outil utile ici est `tokenizer.decode()`. Cela transformera les `input_ids` en chaînes de caractères, afin que vous puissiez visualiser les données et voir si vos données d'entraînement enseignent ce que vous voulez qu'elles enseignent. Par exemple, après avoir obtenu un `batch` de votre `tf.data.Dataset` comme nous l'avons fait ci-dessus, vous pouvez décoder le premier élément comme suit :
+Votre modèle n'apprendra quelque chose que s'il est réellement possible d'apprendre quelque chose de vos données. S'il y a un *bug* qui corrompt les données ou si les étiquettes sont attribuées de manière aléatoire, il est très probable que vous n'obtiendrez aucun entraînement de modèle sur votre jeu de données. Un outil utile ici est `tokenizer.decode()`. Il transformera les `input_ids` en chaînes de caractères, afin que vous puissiez visualiser les données et voir si vos données d'entraînement renseignent ce que vous voulez. Par exemple, après avoir obtenu un `batch` de votre `tf.data.Dataset` comme nous l'avons fait ci-dessus, vous pouvez décoder le premier élément comme suit :
```py
@@ -424,27 +425,27 @@ Une fois que vous pouvez visualiser vos données de cette manière, vous pouvez
- les données décodées sont-elles compréhensibles ?
- êtes-vous d'accord avec les étiquettes ?
- y a-t-il une étiquette qui est plus courante que les autres ?
-- quelle devrait être la perte/métrie si le modèle prédisait une réponse aléatoire/toujours la même réponse ?
+- quelle devrait être la perte/métrique si le modèle prédisait une réponse aléatoire/toujours la même réponse ?
-Après avoir examiné vos données, examinez quelques-unes des prédictions du modèle - si votre modèle produit des tokens, essayez aussi de les décoder ! Si le modèle prédit toujours la même chose, cela peut être dû au fait que votre ensemble de données est biaisé en faveur d'une catégorie (pour les problèmes de classification), des techniques telles que le suréchantillonnage des classes rares peuvent aider. Des techniques telles que le suréchantillonnage des classes rares peuvent donc être utiles. D'autre part, cela peut également être dû à des problèmes d'entraînement tels que de mauvais réglages des hyperparamètres.
+Après avoir examiné vos données, examinez quelques-unes des prédictions du modèle. Si votre modèle produit des *tokens*, essayez aussi de les décoder ! Si le modèle prédit toujours la même chose, cela peut être dû au fait que votre jeu de données est biaisé en faveur d'une catégorie (pour les problèmes de classification). Des techniques telles que le suréchantillonnage des classes rares peuvent aider. D'autre part, cela peut également être dû à des problèmes d'entraînement tels que de mauvais réglages des hyperparamètres.
-Si la perte/la métrique que vous obtenez sur votre modèle initial avant tout entraînement est très différente de la perte/la métrique à laquelle vous vous attendez pour des prédictions aléatoires, vérifiez la façon dont votre perte ou votre métrique est calculée, car il y a probablement un bug. Si vous utilisez plusieurs pertes que vous ajoutez à la fin, assurez-vous qu'elles sont de la même échelle.
+Si la perte/la métrique que vous obtenez sur votre modèle initial avant entraînement est très différente de la perte/la métrique à laquelle vous vous attendez pour des prédictions aléatoires, vérifiez la façon dont votre perte ou votre métrique est calculée. Il y a probablement un bug. Si vous utilisez plusieurs pertes que vous ajoutez à la fin, assurez-vous qu'elles sont de la même échelle.
Lorsque vous êtes sûr que vos données sont parfaites, vous pouvez voir si le modèle est capable de s'entraîner sur elles grâce à un test simple.
### Surentraînement du modèle sur un seul batch
-Le surentrâinement est généralement une chose que nous essayons d'éviter lors de l'entraînement, car cela signifie que le modèle n'apprend pas à reconnaître les caractéristiques générales que nous voulons qu'il reconnaisse, mais qu'il se contente de mémoriser les échantillons d'entraînement. Cependant, essayer d'entraîner votre modèle sur un batch encore et encore est un bon test pour vérifier si le problème tel que vous l'avez formulé peut être résolu par le modèle que vous essayez d'entraîner. Cela vous aidera également à voir si votre taux d'apprentissage initial est trop élevé.
+Le surentraînement est généralement une chose que nous essayons d'éviter lors de l'entraînement car cela signifie que le modèle n'apprend pas à reconnaître les caractéristiques générales que nous voulons qu'il reconnaisse et se contente de mémoriser les échantillons d'entraînement. Cependant, essayer d'entraîner votre modèle sur un batch encore et encore est un bon test pour vérifier si le problème tel que vous l'avez formulé peut être résolu par le modèle que vous essayez d'entraîner. Cela vous aidera également à voir si votre taux d'apprentissage initial est trop élevé.
-Une fois que vous avez défini votre `modèle`, c'est très facile ; il suffit de prendre un batch de données d'entraînement, puis de traiter ce `batch` comme votre ensemble de données entier, en l'ajustant sur un grand nombre d'époques :
+Une fois que vous avez défini votre `modèle`, c'est très facile. Il suffit de prendre un batch de données d'entraînement, puis de le traiter comme votre jeu de données entier que vous *finetunez* sur un grand nombre d'époques :
```py
for batch in train_dataset:
break
-# Make sure you have run model.compile() and set your optimizer,
-# and your loss/metrics if you're using them
+# Assurez-vous que vous avez exécuté model.compile() et défini votre optimiseur,
+# et vos pertes/métriques si vous les utilisez.
model.fit(batch, epochs=20)
```
@@ -457,7 +458,7 @@ model.fit(batch, epochs=20)
Le modèle résultant devrait avoir des résultats proches de la perfection sur le `batch`, avec une perte diminuant rapidement vers 0 (ou la valeur minimale pour la perte que vous utilisez).
-Si vous ne parvenez pas à ce que votre modèle obtienne des résultats parfaits comme celui-ci, cela signifie qu'il y a quelque chose qui ne va pas dans la façon dont vous avez formulé le problème ou dans vos données, et vous devez donc y remédier. Ce n'est que lorsque vous parviendrez à passer le test de surentraînement que vous pourrez être sûr que votre modèle peut réellement apprendre quelque chose.
+Si vous ne parvenez pas à ce que votre modèle obtienne des résultats parfaits comme celui-ci, cela signifie qu'il y a quelque chose qui ne va pas dans la façon dont vous avez formulé le problème ou dans vos données et vous devez donc y remédier. Ce n'est que lorsque vous parviendrez à passer le test de surentraînement que vous pourrez être sûr que votre modèle peut réellement apprendre quelque chose.
@@ -467,21 +468,21 @@ Si vous ne parvenez pas à ce que votre modèle obtienne des résultats parfaits
### Ne réglez rien tant que vous n'avez pas une première ligne de base
-Le réglage des hyperparamètres est toujours considéré comme la partie la plus difficile de l'apprentissage automatique, mais c'est juste la dernière étape pour vous aider à gagner un peu sur la métrique. La plupart du temps, les hyperparamètres par défaut du `Trainer` fonctionneront très bien pour vous donner de bons résultats, donc ne vous lancez pas dans une recherche d'hyperparamètres longue et coûteuse jusqu'à ce que vous ayez quelque chose qui batte la ligne de base que vous avez sur votre jeu de données.
+Le réglage des hyperparamètres est toujours considéré comme la partie la plus difficile de l'apprentissage automatique mais c'est juste la dernière étape pour vous aider à gagner un peu sur la métrique. La plupart du temps, les hyperparamètres par défaut du `Trainer` fonctionneront très bien pour vous donner de bons résultats. Donc ne vous lancez pas dans une recherche d'hyperparamètres longue et coûteuse jusqu'à ce que vous ayez quelque chose qui batte la ligne de base que vous avez sur votre jeu de données.
-Une fois que vous avez un modèle suffisamment bon, vous pouvez commencer à l'affiner un peu. N'essayez pas de lancer un millier d'exécutions avec différents hyperparamètres, mais comparez quelques exécutions avec différentes valeurs pour un hyperparamètre afin de vous faire une idée de celui qui a le plus d'impact.
+Une fois que vous avez un modèle suffisamment bon, vous pouvez commencer à le *finetuner* un peu. N'essayez pas de lancer un millier d'exécutions avec différents hyperparamètres mais comparez quelques exécutions avec différentes valeurs pour un hyperparamètre afin de vous faire une idée de celui qui a le plus d'impact.
Si vous modifiez le modèle lui-même, restez simple et n'essayez rien que vous ne puissiez raisonnablement justifier. Veillez toujours à revenir au test de surentraînement pour vérifier que votre modification n'a pas eu de conséquences inattendues.
### Demander de l'aide
-Nous espérons que vous avez trouvé dans cette section des conseils qui vous ont aidé à résoudre votre problème, mais si ce n'est pas le cas, n'oubliez pas que vous pouvez toujours demander de l'aide à la communauté sur le [forum](https://discuss.huggingface.co/).
+Nous espérons que vous avez trouvé dans cette section des conseils qui vous ont aidé à résoudre votre problème. Si ce n'est pas le cas, n'oubliez pas que vous pouvez toujours demander de l'aide à la communauté sur le [forum](https://discuss.huggingface.co/).
Voici quelques ressources (en anglais) supplémentaires qui peuvent s'avérer utiles :
-- ["La reproductibilité comme vecteur des meilleures pratiques d'ingénierie"](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) par Joel Grus
-- ["Liste de contrôle pour le débogage des réseaux neuronaux"](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) par Cecelia Shao
-- ["Comment tester unitairement le code d'apprentissage automatique"](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) par Chase Roberts
-- ["Une recette pour Entraîner les réseaux neuronaux"](http://karpathy.github.io/2019/04/25/recipe/) par Andrej Karpathy
+- [La reproductibilité comme vecteur des meilleures pratiques d'ingénierie](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) par Joel Grus
+- [Liste de contrôle pour le débogage des réseaux de neurones](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) par Cecelia Shao
+- [Comment tester unitairement le code d'apprentissage automatique](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) par Chase Roberts
+- [Une recette pour entraîner les réseaux de neurones](http://karpathy.github.io/2019/04/25/recipe/) par Andrej Karpathy
-Bien sûr, tous les problèmes rencontrés lors de l'Entraînement des réseaux neuronaux ne sont pas forcément de votre faute ! Si vous rencontrez quelque chose dans la bibliothèque 🤗 *Transformers* ou 🤗 *Datasets* qui ne semble pas correct, vous avez peut-être rencontré un bogue. Vous devez absolument nous en parler, et dans la section suivante, nous allons vous expliquer exactement comment faire.
+Bien sûr, tous les problèmes rencontrés lors de l'entraînement ne sont pas forcément de votre faute ! Si vous rencontrez quelque chose dans la bibliothèque 🤗 *Transformers* ou 🤗 *Datasets* qui ne semble pas correct, vous avez peut-être trouver un *bug*. Vous devez absolument nous en parler pour qu'on puisse le corriger. Dans la section suivante, nous allons vous expliquer exactement comment faire.
diff --git a/chapters/fr/chapter8/5.mdx b/chapters/fr/chapter8/5.mdx
index ee47ca77e..330305c27 100644
--- a/chapters/fr/chapter8/5.mdx
+++ b/chapters/fr/chapter8/5.mdx
@@ -1,4 +1,4 @@
-# Comment rédiger une bonne *issue*
+# Comment rédiger une bonne issue
-Lorsque vous rencontrez un problème avec l'une des bibliothèques Hugging Face, vous devez nous le faire savoir afin que nous puissions le corriger (il en va de même pour toute bibliothèque open source). Si vous n'êtes pas complètement certain que le bug se trouve dans votre propre code ou dans l'une de nos bibliothèques, le premier endroit à vérifier est le [forum](https://discuss.huggingface.co/). La communauté vous aidera à résoudre ce problème, et l'équipe Hugging Face suit également de près les discussions qui s'y déroulent.
+Lorsque vous rencontrez un problème avec l'une des bibliothèques d'Hugging Face, faites le nous savoir afin que nous puissions le corriger (il en va de même pour toute bibliothèque open source).
+Si vous n'êtes pas complètement certain que le *bug* se trouve dans votre propre code ou dans l'une de nos bibliothèques, le premier endroit à vérifier est le [forum](https://discuss.huggingface.co/). La communauté vous aidera à résoudre votre problème et l'équipe d'Hugging Face y suit de près les discussions qui s'y déroulent.
-Lorsque vous êtes sûr d'avoir un bogue en main, la première étape consiste à construire un exemple minimal reproductible.
+Lorsque vous êtes sûr d'avoir identifier un *bug*, la première étape consiste à construire un exemple minimal qui soit reproductible.
## Créer un exemple minimal reproductible
-Il est très important d'isoler le morceau de code qui produit le bug, car personne dans l'équipe Hugging Face n'est (encore) un magicien, et ils ne peuvent pas réparer ce qu'ils ne peuvent pas voir. Un exemple minimal reproductible doit, comme son nom l'indique, être reproductible. Cela signifie qu'il ne doit pas dépendre de fichiers ou de données externes que vous pourriez avoir. Essayez de remplacer les données que vous utilisez par des valeurs fictives qui ressemblent à vos valeurs réelles et qui produisent toujours la même erreur.
+Il est très important d'isoler le morceau de code qui produit le *bug* car personne dans l'équipe d'Hugging Face n'est (encore) un magicien et on ne peut pas réparer ce qu'on ne peut pas voir. Un exemple minimal reproductible doit, comme son nom l'indique, être reproductible. Cela signifie qu'il ne doit pas dépendre de fichiers ou de données externes que vous pourriez avoir. Essayez de remplacer les données que vous utilisez par des valeurs fictives qui ressemblent à vos valeurs réelles et qui produisent toujours la même erreur.
@@ -23,25 +24,25 @@ Il est très important d'isoler le morceau de code qui produit le bug, car perso
-Une fois que vous avez quelque chose d'autonome, vous pouvez essayer de le réduire à encore moins de lignes de code, en construisant ce que nous appelons un _exemple reproductible minimal_. Bien que cela nécessite un peu plus de travail de votre part, vous serez presque assuré d'obtenir de l'aide et une correction si vous fournissez un exemple reproductible court et agréable.
+Une fois que vous avez quelque chose d'autonome, essayez de le réduire au moins de lignes de code possible, en construisant ce que nous appelons un _exemple reproductible minimal_. Bien que cela nécessite un peu plus de travail de votre part, vous serez presque assuré d'obtenir de l'aide et une correction si vous fournissez un exemple reproductible court et agréable.
-Si vous vous sentez suffisamment à l'aise, allez inspecter le code source où se trouve votre bogue. Vous trouverez peut-être une solution à votre problème (dans ce cas, vous pouvez même suggérer une pull request pour le corriger), mais plus généralement, cela peut aider les mainteneurs à mieux comprendre le code source lorsqu'ils lisent votre rapport.
+Si vous vous sentez suffisamment à l'aise, allez inspecter le code source où se trouve votre *bug*. Vous trouverez peut-être une solution à votre problème (dans ce cas, vous pouvez même suggérer une *pull request* pour le corriger), mais plus généralement, cela peut aider les mainteneurs à mieux comprendre le code source lorsqu'ils lisent votre message.
-## Remplir le modèle de problème
+## Remplir le gabarit de problème
-Lorsque vous déposez votre problème, vous remarquerez qu'il y a un modèle à remplir. Nous suivrons ici celui pour [🤗 *Transformers* issues](https://github.com/huggingface/transformers/issues/new/choose), mais le même type d'information sera requis si vous rapportez un problème dans un autre dépôt. Ne laissez pas le modèle vide : prendre le temps de le remplir maximisera vos chances d'obtenir une réponse et de résoudre votre problème.
+Lorsque vous ouvrerez votre *issue* vous remarquerez qu'il y a un gabarit à remplir. Nous suivrons ici celui pour la bibliothèque [🤗 *Transformers*](https://github.com/huggingface/transformers/issues/new/choose) mais le même type d'information sera requis dans un autre dépôt. Ne laissez pas le gabarit vide : prendre le temps de le remplir maximisera vos chances d'obtenir une réponse et de résoudre votre problème.
-En général, lorsque vous signalez un problème, restez toujours courtois. Il s'agit d'un projet open source, vous utilisez donc un logiciel libre, et personne n'est obligé de vous aider. Vous pouvez inclure dans votre problème des critiques qui vous semblent justifiées, mais les mainteneurs pourraient très bien les prendre mal et ne pas être pressés de vous aider. Assurez-vous de lire le [code de conduite](https://github.com/huggingface/transformers/blob/master/CODE_OF_CONDUCT.md) du projet.
+En général, lorsque vous signalez un problème, restez toujours courtois. Il s'agit d'un projet open source, vous utilisez donc un logiciel libre, et personne n'est obligé de vous aider. Vous pouvez inclure dans votre *issue* des critiques qui vous semblent justifiées mais les mainteneurs pourraient très bien les prendre mal et ne pas être pressés de vous aider. Assurez-vous de lire le [code de conduite](https://github.com/huggingface/transformers/blob/master/CODE_OF_CONDUCT.md) du projet.
### Inclure les informations sur votre environnement
-🤗 *Transformers* fournit un utilitaire pour obtenir toutes les informations dont nous avons besoin sur votre environnement. Il suffit de taper ce qui suit dans votre terminal :
+🤗 *Transformers* fournit un utilitaire pour obtenir toutes les informations nécessaire concernant votre environnement. Il suffit de taper ce qui suit dans votre terminal :
```
transformers-cli env
```
-et vous devriez obtenir quelque chose comme ça :
+et vous devriez obtenir quelque chose comme :
```out
Copy-and-paste the text below in your GitHub issue and FILL OUT the two last points.
@@ -58,34 +59,34 @@ Copy-and-paste the text below in your GitHub issue and FILL OUT the two last poi
- Using distributed or parallel set-up in script?:
```
-Vous pouvez également ajouter un `!` au début de la commande `transformers-cli env` pour l'exécuter depuis une cellule du *notebook* puis copier et coller le résultat au début de votre problème.
+Vous pouvez également ajouter un `!` au début de la commande `transformers-cli env` pour l'exécuter depuis une cellule de *notebook* puis copier et coller le résultat au début de votre *issue*.
-### Marquer des personnes
+### Taguer des personnes
-Marquer des personnes en tapant un `@` suivi de leur identifiant GitHub leur enverra une notification afin qu'elles voient votre problème et puissent répondre plus rapidement. Utilisez cette fonction avec modération, car les personnes que vous marquez peuvent ne pas apprécier d'être notifiées si elles n'ont pas de lien direct avec le problème. Si vous avez regardé les fichiers sources liés à votre bogue, vous devriez étiqueter la dernière personne qui a fait des changements à la ligne que vous pensez être responsable de votre problème (vous pouvez trouver cette information en regardant ladite ligne sur GitHub, en la sélectionnant, puis en cliquant sur "View git blame").
+Taguer des personnes en tapant un `@` suivi de leur identifiant GitHub leur enverra une notification afin qu'elles voient votre problème et puissent répondre plus rapidement. Néanmoins utilisez cette fonction avec modération car les personnes que vous taguez peuvent ne pas apprécier d'être notifiées si elles n'ont pas de lien direct avec le problème. Si vous avez regardé les fichiers sources liés à votre *bug*, vous devriez taguer la dernière personne qui a fait des changements à la ligne que vous pensez être responsable de votre problème (vous pouvez trouver cette information en regardant ladite ligne sur GitHub, en la sélectionnant, puis en cliquant sur « *View git blame* »).
-Sinon, le modèle propose des suggestions de personnes à étiqueter. En général, ne marquez jamais plus de trois personnes !
+Sinon, le gabarit propose des suggestions de personnes à taguer. En général, ne marquez jamais plus de trois personnes !
### Inclure un exemple reproductible
-Si vous avez réussi à créer un exemple autonome qui produit le bogue, il est temps de l'inclure ! Tapez une ligne avec trois backticks suivis de `python`, comme ceci :
+Si vous avez réussi à créer un exemple autonome qui produit le *bug*, il est temps de l'inclure ! Tapez une ligne avec trois *backticks* suivis de `python`, comme ceci :
```
```python
```
-puis collez votre exemple minimal reproductible et tapez une nouvelle ligne avec trois backticks. Cela permettra de s'assurer que votre code est correctement formaté.
+puis collez votre exemple minimal reproductible et tapez une nouvelle ligne avec trois *backticks*. Cela permettra de s'assurer que votre code est correctement formaté.
-Si vous n'avez pas réussi à créer un exemple reproductible, expliquez en étapes claires comment vous êtes arrivé à votre problème. Si vous le pouvez, incluez un lien vers un *notebook* de Google Colab où vous avez trouvé l'erreur. Plus vous partagerez d'informations, plus les mainteneurs seront en mesure de vous répondre.
+Si vous n'avez pas réussi à créer un exemple reproductible, expliquez en des étapes claires comment vous êtes arrivé à votre problème. Si vous le pouvez, incluez un lien vers un *notebook* d'un Google Colab où vous avez trouvé l'erreur. Plus vous partagerez d'informations, plus les mainteneurs seront en mesure de vous répondre.
-Dans tous les cas, vous devez copier et coller l'intégralité du message d'erreur que vous obtenez. Si vous travaillez dans Colab, n'oubliez pas que certains cadres peuvent être automatiquement réduits dans la trace de la pile, et veillez donc à les développer avant de les copier. Comme pour l'exemple de code, placez le message d'erreur entre deux lignes avec trois points de suspension, afin qu'il soit correctement formaté.
+Dans tous les cas, vous devez copier et coller l'intégralité du message d'erreur que vous obtenez. Si vous travaillez dans Colab, n'oubliez pas que certaines cellules peuvent être automatiquement réduites dans la trace de la pile et veillez donc à les afficher avant de les copier. Comme pour l'exemple de code, placez le message d'erreur entre deux lignes avec trois *backticks* afin qu'il soit correctement formaté.
### Décrire le comportement attendu
-Expliquez en quelques lignes ce que vous vous attendiez à obtenir, afin que les mainteneurs comprennent bien le problème. Cette partie est généralement assez évidente, elle devrait donc tenir en une seule phrase, mais dans certains cas, vous pouvez avoir beaucoup à dire.
+Expliquez en quelques lignes ce que vous vous attendiez à obtenir afin que les mainteneurs comprennent bien le problème. Cette partie est généralement assez évidente, elle devrait donc tenir en une seule phrase mais dans certains cas vous pouvez avoir beaucoup à dire.
## Et ensuite ?
-Une fois que votre problème est classé, vérifiez rapidement que tout est en ordre. Vous pouvez modifier le problème si vous avez fait une erreur, ou même changer son titre si vous vous rendez compte que le problème est différent de ce que vous pensiez initialement.
+Une fois que votre problème est classé, vérifiez rapidement que tout est en ordre. Vous pouvez modifier le problème si vous avez fait une erreur ou même changer son titre si vous vous rendez compte que le problème est différent de ce que vous pensiez initialement.
Il est inutile d'envoyer des messages aux personnes concernées si vous n'obtenez pas de réponse. Si personne ne vous aide au bout de quelques jours, il est probable que personne n'a pu donner un sens à votre problème. N'hésitez pas à revenir à l'exemple reproductible. Pouvez-vous le rendre plus court et plus concis ? Si vous n'obtenez pas de réponse au bout d'une semaine, vous pouvez laisser un message demandant gentiment de l'aide, surtout si vous avez modifié votre question pour inclure plus d'informations sur le problème.
diff --git a/chapters/fr/chapter8/7.mdx b/chapters/fr/chapter8/7.mdx
index 217771167..6f93e4e7f 100644
--- a/chapters/fr/chapter8/7.mdx
+++ b/chapters/fr/chapter8/7.mdx
@@ -4,7 +4,7 @@
Testons ce que vous avez appris dans ce chapitre !
-### 1. Dans quel ordre devez-vous lire un *traceback* Python ?
+### 1. Dans quel ordre devez-vous lire un traceback Python ?
traceback de Python montrant l'exception en bas est qu'il est plus facile de déboguer lorsque vous travaillez dans le terminal et que c'est la dernière ligne que vous voyez.",
+ explain: "L'avantage d'un traceback de Python montrant l'exception en bas est qu'il est plus facile de déboguer lorsque vous travaillez dans le terminal et que c'est la dernière ligne que vous voyez.",
correct: true
}
]}
@@ -25,12 +25,12 @@ Testons ce que vous avez appris dans ce chapitre !
transformer à partir d'un article de recherche.",
+ text: "Une implémentation simmple d'un transformer à partir d'un article de recherche.",
explain: "Bien qu'il soit très éducatif d'implémenter vos propres modèles de transformers à partir de zéro, ce n'est pas ce dont nous parlons ici."
},
{
text: "Un bloc de code compact et autonome qui peut être exécuté sans aucune dépendance externe sur des fichiers ou des données privées.",
- explain: "Corrigez ! Des exemples minimaux reproductibles aident les mainteneurs de la bibliothèque à reproduire le problème que vous rencontrez, afin qu'ils puissent trouver des solutions plus rapidement.",
+ explain: "Des exemples minimaux reproductibles aident les mainteneurs de la bibliothèque à reproduire le problème que vous rencontrez, afin qu'ils puissent trouver des solutions plus rapidement.",
correct: true
},
{
@@ -72,12 +72,12 @@ Lequel des éléments suivants pourrait être un bon choix pour le titre d'un su
},
{
text: "Pourquoi je ne peux pas importer GPT3ForSequenceClassification
?",
- explain: "Bon choix ! Ce titre est concis et donne au lecteur un indice sur ce qui pourrait être erroné (par exemple, que GPT-3 n'est pas pris en charge dans 🤗 Transformers).",
+ explain: "Ce titre est concis et donne au lecteur un indice sur ce qui pourrait être erroné (par exemple, que GPT-3 n'est pas pris en charge dans 🤗 Transformers).",
correct: true
},
{
text: "Le GPT-3 est-il pris en charge dans 🤗 Transformers ?",
- explain: "Bien vu ! Utiliser des questions comme titres de sujets est un excellent moyen de communiquer le problème à la communauté..",
+ explain: "Utiliser des questions comme titres de sujets est un excellent moyen de communiquer le problème à la communauté.",
correct: true
}
]}
@@ -89,7 +89,7 @@ Lequel des éléments suivants pourrait être un bon choix pour le titre d'un su
choices={[
{
text: "L'étape d'optimisation où nous calculons les gradients et effectuons la rétropropagation.",
- explain: "Bien qu'il puisse y avoir des bogues dans votre optimiseur, cela se produit généralement à plusieurs étapes du pipeline d'entraînement, il y a donc d'autres choses à vérifier d'abord. Essayez à nouveau !"
+ explain: "Bien qu'il puisse y avoir des bugs dans votre optimiseur, cela se produit généralement à plusieurs étapes du pipeline d'entraînement, il y a donc d'autres choses à vérifier d'abord. Essayez à nouveau !"
},
{
text: "L'étape d'évaluation où nous calculons les métriques",
@@ -140,8 +140,8 @@ Lequel des éléments suivants pourrait être un bon choix pour le titre d'un su
bug.",
+ explain: "C'est la meilleure façon d'aider les mainteneurs à trouver votre bogue. Que devez-vous faire d'autre ?",
correct: true
},
{
@@ -166,7 +166,7 @@ Lequel des éléments suivants pourrait être un bon choix pour le titre d'un su
},
{
text: "Elle nous permet de vérifier que le modèle est capable de réduire la perte à zéro.",
- explain: "Correct ! Avec un petit batch d'à peine deux exemples, nous pouvons rapidement vérifier si le modèle est capable d'apprendre.",
+ explain: "Avec un petit batch d'à peine deux exemples, nous pouvons rapidement vérifier si le modèle est capable d'apprendre.",
correct: true
},
{
@@ -182,17 +182,18 @@ Lequel des éléments suivants pourrait être un bon choix pour le titre d'un su
choices={[
{
text: "Cela permet aux mainteneurs de comprendre quelle version de la bibliothèque vous utilisez.",
- explain: "Correct ! Comme chaque version majeure de la bibliothèque peut comporter des modifications de l'API, le fait de connaître la version spécifique que vous utilisez peut vous aider à circonscrire le problème. Quels sont les autres avantages ?",
+ explain: "Comme chaque version majeure de la bibliothèque peut comporter des modifications de l'API, le fait de connaître la version spécifique que vous utilisez peut vous aider à circonscrire le problème. Quels sont les autres avantages ?",
correct: true
},
{
text: "Il permet aux mainteneurs de savoir si vous exécutez le code sous Windows, macOS ou Linux.",
- explain: "Correct ! Les erreurs peuvent parfois être causées par le système d'exploitation spécifique que vous utilisez, et le fait de le savoir aide les mainteneurs à les reproduire localement. Mais ce n'est pas la seule raison.",
+ explain: "Les erreurs peuvent parfois être causées par le système d'exploitation spécifique que vous utilisez, et le fait de le savoir aide les mainteneurs à les reproduire localement. Mais ce n'est pas la seule raison.",
correct: true
},
{
text: "Il permet aux mainteneurs de savoir si le code est exécuté sur un GPU ou un CPU.",
- explain: "Correct ! Comme nous l'avons vu dans ce chapitre, les erreurs sur les GPU et les CPU peuvent avoir une saveur très différente, et savoir quel matériel vous utilisez peut aider à focaliser l'attention des mainteneurs. Mais ce n'est pas le seul avantage...",
+ explain: "Comme nous l'avons vu dans ce chapitre, les erreurs sur les GPU et les CPU peuvent avoir une saveur très différente, et savoir quel matériel vous utilisez peut aider à focaliser l'attention des mainteneurs. Mais ce n'est pas le seul avantage...",
+ correct: true
}
]}
/>
diff --git a/chapters/fr/chapter9/1.mdx b/chapters/fr/chapter9/1.mdx
new file mode 100644
index 000000000..d0be0c8be
--- /dev/null
+++ b/chapters/fr/chapter9/1.mdx
@@ -0,0 +1,32 @@
+# Introduction à Gradio
+
+Dans ce chapitre, nous allons apprendre à construire des **démos interactives** pour vos modèles d'apprentissage automatique.
+
+Pourquoi construire une démo ou une interface graphique pour votre modèle d'apprentissage automatique ? Les démos permettent :
+
+- aux **développeurs en apprentissage automatique** de présenter facilement leur travail à un large public, y compris des équipes non techniques ou des clients.
+- aux **chercheurs** de reproduire plus facilement les modèles d'apprentissage automatique et leur comportement.
+- aux **testeurs qualité** ou **utilisateurs finaux** d'identifier et de déboguer plus facilement les points de défaillance des modèles.
+- aux **utilisateurs divers** de découvrir les biais algorithmiques des modèles.
+
+Nous utiliserons la bibliothèque *Gradio* pour construire des démos pour nos modèles. *Gradio* vous permet de construire, de personnaliser et de partager des démos en ligne pour n'importe quel modèle d'apprentissage automatique. Et cela entièrement en Python.
+
+Voici quelques exemples de démos d'apprentissage automatique construites avec Gradio :
+
+* Un modèle de **reconnaissance de croquis** qui prend un croquis et produit des étiquettes de ce qu'il pense être dessiné :
+
+
+
+* Un modèle extractif de **réponse à une question** qui prend en entrée un paragraphe de contexte et une requête et produit une réponse et un score de probabilité (nous avons discuté de ce type de modèle [au chapitre 7](/course/fr/chapter7/7)) :
+
+
+
+* Un modèle de **suppression de l'arrière-plan** qui prend une image et la restitue avec l'arrière-plan supprimé :
+
+
+
+Ce chapitre est divisé en sections qui comprennent à la fois des _concepts_ et des _applications_. Après avoir appris le concept dans chaque section, vous l'appliquerez pour construire un type particulier de démo, allant de la classification d'images à la reconnaissance vocale. À la fin de ce chapitre, vous serez en mesure de créer ces démos (et bien d'autres !) en quelques lignes de code Python seulement.
+
+
+👀 Consultez Hugging Face Spaces pour voir de nombreux exemples récents de démos d'apprentissage automatique construites par la communauté !
+
diff --git a/chapters/fr/chapter9/2.mdx b/chapters/fr/chapter9/2.mdx
new file mode 100644
index 000000000..339680935
--- /dev/null
+++ b/chapters/fr/chapter9/2.mdx
@@ -0,0 +1,110 @@
+# Construire votre première démo
+
+Commençons par installer *Gradio* ! Comme il s'agit d'un *package* Python, il suffit de l'exécuter :
+
+`$ pip install gradio `
+
+Vous pouvez exécuter *Gradio* n'importe où, que ce soit dans votre IDE Python préféré, dans des *notebooks* ou même dans Google Colab 🤯 !
+Alors installez *Gradio* partout où vous exécutez Python !
+
+Commençons par un exemple simple de type « *Hello World* » pour nous familiariser avec la syntaxe de *Gradio* :
+
+```py
+import gradio as gr
+
+
+def greet(name):
+ return "Hello " + name
+
+
+demo = gr.Interface(fn=greet, inputs="text", outputs="text")
+
+demo.launch()
+```
+
+Parcourons le code ci-dessus :
+
+- D'abord, nous définissons une fonction appelée `greet()`. Dans ce cas, c'est une simple fonction qui ajoute « *Hello* » devant votre nom, mais cela peut être *n'importe quelle* fonction Python en général. Par exemple, dans les applications d'apprentissage automatique, cette fonction pourrait *appeler un modèle pour faire une prédiction* sur une entrée et retourner la sortie.
+- Ensuite, nous créons une `Interface` *Gradio* avec trois arguments, `fn`, `inputs`, et `outputs`. Ces arguments définissent la fonction de prédiction, ainsi que le _type_ de composants d'entrée et de sortie que nous souhaitons. Dans notre cas, les deux composants sont de simples boîtes de texte.
+- Nous appelons ensuite la méthode `launch()` sur l'`Interface` que nous avons créée.
+
+Si vous exécutez ce code, l'interface ci-dessous apparaîtra automatiquement dans un *notebook* Jupyter/Colab ou dans un navigateur sur **[http://localhost:7860](http://localhost:7860/)** si vous l'exécutez à partir d'un script.
+
+
+
+Essayez d'utiliser cette interface maintenant avec votre propre nom ou une autre entrée !
+
+Vous remarquerez que dedans, *Gradio* a automatiquement déduit le nom du paramètre d'entrée (`name`) et l'a appliqué comme étiquette au dessus de la zone de texte. Que faire si vous souhaitez changer cela ?
+Ou si vous souhaitez personnaliser la zone de texte d'une autre manière ? Dans ce cas, vous pouvez instancier un objet de classe représentant le composant de saisie.
+
+Jetez un coup d'œil à l'exemple ci-dessous :
+
+```py
+import gradio as gr
+
+
+def greet(name):
+ return "Hello " + name
+
+
+# Nous instancions la classe Textbox
+textbox = gr.Textbox(label="Type your name here:", placeholder="John Doe", lines=2)
+
+gr.Interface(fn=greet, inputs=textbox, outputs="text").launch()
+```
+
+
+
+Ici, nous avons créé une zone de texte d'entrée avec une étiquette, un espace réservé et un nombre de lignes défini.
+Vous pourriez faire la même chose pour la zone de texte de sortie, mais nous allons laisser cela pour le moment.
+
+Nous avons vu qu'avec seulement quelques lignes de code, *Gradio* vous permet de créer une interface simple autour de n'importe quelle fonction
+avec n'importe quel type d'entrées ou de sorties. Dans cette section, nous avons commencé par une simple boîte de texte mais dans les sections suivantes, nous couvrirons d'autres types d'entrées et de sorties. Voyons maintenant comment inclure un peu de NLP dans une application *Gradio*.
+
+
+## 🤖 Inclure les prédictions du modèle
+
+Construisons maintenant une interface simple qui permet de faire une démo d'un modèle de **génération de texte** comme le GPT-2.
+
+Nous allons charger notre modèle en utilisant la fonction `pipeline()` de 🤗 *Transformers*.
+Si vous avez besoin d'un rafraîchissement rapide, vous pouvez revenir à [cette section du chapitre 1](/course/fr/chapter1/3#text-generation).
+
+Tout d'abord, nous définissons une fonction de prédiction qui prend une invite de texte et renvoie la complétion du texte :
+
+```py
+from transformers import pipeline
+
+model = pipeline("text-generation")
+
+
+def predict(prompt):
+ completion = model(prompt)[0]["generated_text"]
+ return completion
+```
+
+Cette fonction complète le texte que vous fournissez, et vous pouvez l'exécuter avec les votres pour voir comment elle fonctionne. Voici un exemple (vous obtiendrez peut-être un résultat différent) :
+
+
+```
+predict("My favorite programming language is") # Mon langage de programmation préféré est
+```
+
+```
+>> My favorite programming language is Haskell. I really enjoyed the Haskell language, but it doesn't have all the features that can be applied to any other language. For example, all it does is compile to a byte array.
+# Mon langage de programmation préféré est Haskell. J'ai vraiment apprécié le langage Haskell, mais il n'a pas toutes les caractéristiques que l'on peut appliquer à n'importe quel autre langage. Par exemple, il ne fait que compiler un tableau d'octets.
+```
+
+Maintenant que nous avons une fonction pour générer des prédictions, nous pouvons créer et lancer une `Interface` de la même manière que nous l'avons fait précédemment :
+
+```py
+import gradio as gr
+
+gr.Interface(fn=predict, inputs="text", outputs="text").launch()
+```
+
+
+C'est fait ! Vous pouvez maintenant utiliser cette interface pour générer du texte en utilisant le modèle GPT-2 comme indiqué ci-dessous 🤯.
+
+
+
+Continuez votre lecture du cours pour voir comment construire d'autres types de démos avec *Gradio* !
\ No newline at end of file
diff --git a/chapters/fr/chapter9/3.mdx b/chapters/fr/chapter9/3.mdx
new file mode 100644
index 000000000..2117c1511
--- /dev/null
+++ b/chapters/fr/chapter9/3.mdx
@@ -0,0 +1,160 @@
+# Comprendre la classe Interface
+
+Dans cette section, nous allons examiner de plus près la classe `Interface`, et comprendre les principaux paramètres utilisés pour en créer une.
+
+## Comment créer une interface
+
+Vous remarquerez que la classe `Interface` a 3 paramètres obligatoires :
+
+`Interface(fn, inputs, outputs, ...)`
+
+Ces paramètres sont :
+
+ - `fn`: la fonction de prédiction qui est enveloppée par l'interface *Gradio*. Cette fonction peut prendre un ou plusieurs paramètres et retourner une ou plusieurs valeurs.
+ - `inputs`: le(s) type(s) de composant(s) d'entrée. *Gradio* fournit de nombreux composants préconstruits tels que`"image"` ou `"mic"`.
+ - `outputs`: le(s) type(s) de composant(s) de sortie. Encore une fois, *Gradio* fournit de nombreux composants pré-construits, par ex. `"image"` ou `"label"`.
+
+Pour une liste complète des composants, [jetez un coup d'œil à la documentation de *Gradio*](https://gradio.app/docs). Chaque composant préconstruit peut être personnalisé en instanciant la classe correspondant au composant.
+
+Par exemple, comme nous l'avons vu dans la [section précédente](/course/fr/chapter9/2), au lieu de passer le paramètre `inputs` par `"textbox"`, vous pouvez passer un composant `Textbox(lines=7, label="Prompt")` pour créer une zone de texte avec 7 lignes et un label.
+
+Voyons un autre exemple, cette fois avec un composant `Audio`.
+
+## Un exemple simple avec audio
+
+Comme mentionné précédemment,*Gradio* fournit de nombreuses entrées et sorties différentes.
+Construisons donc une `Interface` qui fonctionne avec l'audio.
+
+Dans cet exemple, nous allons construire une fonction audio-vers-audio qui prend un fichier audio et l'inverse simplement.
+
+Nous utiliserons comme entrée le composant `Audio`. Lorsque vous utilisez le composant `Audio`, vous pouvez spécifier si vous voulez que la `source` de l'audio soit un fichier que l'utilisateur télécharge ou un microphone avec lequel l'utilisateur enregistre sa voix. Dans ce cas, nous allons choisir un "microphone". Juste pour le plaisir, nous allons ajouter une étiquette à notre `Audio` qui dit « *Speak here...* » (Parler ici).
+
+De plus, nous aimerions recevoir l'audio sous la forme d'un tableau numpy afin de pouvoir facilement l'inverser. Nous allons donc définir le `"type"` comme étant `"numpy"`, ce qui permet de passer les données d'entrée comme un *tuple* de (`sample_rate`, `data`) dans notre fonction.
+
+Nous utiliserons également le composant de sortie `Audio` qui peut automatiquement rendre un *tuple* avec un taux d'échantillonnage et un tableau numpy de données comme un fichier audio lisible.
+Dans ce cas, nous n'avons pas besoin de faire de personnalisation, donc nous utiliserons le raccourci de la chaîne `"audio"`.
+
+
+```py
+import numpy as np
+import gradio as gr
+
+
+def reverse_audio(audio):
+ sr, data = audio
+ reversed_audio = (sr, np.flipud(data))
+ return reversed_audio
+
+
+mic = gr.Audio(source="microphone", type="numpy", label="Speak here...")
+gr.Interface(reverse_audio, mic, "audio").launch()
+```
+
+Le code ci-dessus produira une interface comme celle qui suit (si votre navigateur ne vous demande pas l'autorisation pour accéder au microphone, ouvrez la démo dans un onglet séparé).
+
+
+
+Vous devriez maintenant être capable d'enregistrer votre voix et de vous entendre parler à l'envers. Effrayant 👻 !
+
+## Gérer les entrées et sorties multiples
+
+Imaginons que nous ayons une fonction plus compliquée, avec plusieurs entrées et sorties.
+Dans l'exemple ci-dessous, nous avons une fonction qui prend un index de liste déroulante, une valeur de curseur et un nombre, et renvoie un échantillon audio d'une tonalité musicale.
+
+Regardez comment nous passons une liste de composants d'entrée et de sortie, et voyez si vous pouvez suivre ce qui se passe.
+
+La clé ici est que lorsque vous passez :
+* une liste de composants d'entrée, chaque composant correspond à un paramètre dans l'ordre.
+* une liste de composants de sortie, chaque composant correspond à une valeur retournée.
+
+L'extrait de code ci-dessous montre comment trois composants d'entrée correspondent aux trois arguments de la fonction `generate_tone()` :
+
+```py
+import numpy as np
+import gradio as gr
+
+notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
+
+
+def generate_tone(note, octave, duration):
+ sr = 48000
+ a4_freq, tones_from_a4 = 440, 12 * (octave - 4) + (note - 9)
+ frequency = a4_freq * 2 ** (tones_from_a4 / 12)
+ duration = int(duration)
+ audio = np.linspace(0, duration, duration * sr)
+ audio = (20000 * np.sin(audio * (2 * np.pi * frequency))).astype(np.int16)
+ return (sr, audio)
+
+
+gr.Interface(
+ generate_tone,
+ [
+ gr.Dropdown(notes, type="index"),
+ gr.Slider(minimum=4, maximum=6, step=1),
+ gr.Textbox(type="number", value=1, label="Duration in seconds"),
+ ],
+ "audio",
+).launch()
+```
+
+
+
+
+### La méthode `launch()`.
+
+Jusqu'à présent, nous avons utilisé la méthode `launch()` pour lancer l'interface, mais nous n'avons pas vraiment discuté de ce qu'elle fait.
+
+Par défaut, la méthode `launch()` lancera la démo dans un serveur web qui tourne localement. Si vous exécutez votre code dans un *notebook* Jupyter ou Colab, *Gradio* va intégrer l'interface graphique de la démo dans le *notebook* afin que vous puissiez l'utiliser facilement.
+
+Vous pouvez personnaliser le comportement de `launch()` à travers différents paramètres :
+
+ - `inline` : si vous voulez afficher l'interface en ligne sur les *notebooks* Python.
+ - `inbrowser` : pour lancer automatiquement l'interface dans un nouvel onglet du navigateur par défaut.
+ - `share` : si vous voulez créer un lien public partageable depuis votre ordinateur pour l'interface. Un peu comme un lien Google Drive !
+
+Nous couvrirons le paramètre `share` plus en détail dans la section suivante !
+
+## ✏️ Appliquons-le !
+
+Construisons une interface qui vous permette de faire la démonstration d'un modèle de **reconnaissance vocale**.
+Pour rendre la chose intéressante, nous accepterons *soit* une entrée micro, soit un fichier téléchargé.
+
+Comme d'habitude, nous allons charger notre modèle de reconnaissance vocale en utilisant la fonction `pipeline()` de 🤗 *Transformers*.
+Si vous avez besoin d'un rafraîchissement rapide, vous pouvez revenir à [cette section du chapitre 1](/course/fr/chapter1/3). Ensuite, nous allons implémenter une fonction `transcribe_audio()` qui traite l'audio et retourne la transcription (en anglais). Enfin, nous allons envelopper cette fonction dans une `Interface` avec les composants `Audio` pour les entrées et juste le texte pour la sortie. Au total, le code de cette application est le suivant :
+
+```py
+from transformers import pipeline
+import gradio as gr
+
+model = pipeline("automatic-speech-recognition")
+
+
+def transcribe_audio(mic=None, file=None):
+ if mic is not None:
+ audio = mic
+ elif file is not None:
+ audio = file
+ else:
+ return "You must either provide a mic recording or a file"
+ transcription = model(audio)["text"]
+ return transcription
+
+
+gr.Interface(
+ fn=transcribe_audio,
+ inputs=[
+ gr.Audio(source="microphone", type="filepath", optional=True),
+ gr.Audio(source="upload", type="filepath", optional=True),
+ ],
+ outputs="text",
+).launch()
+```
+
+Si votre navigateur ne vous demande pas l'autorisation pour accéder au microphone, ouvrez la démo dans un onglet séparé.
+
+
+
+
+Voilà, c'est fait ! Vous pouvez maintenant utiliser cette interface pour transcrire de l'audio. Remarquez ici qu'en passant le paramètre `optional` à `True`, nous permettons à l'utilisateur de soit fournir un microphone ou un fichier audio (ou aucun des deux, mais cela retournera un message d'erreur).
+
+Continuez pour voir comment partager votre interface avec d'autres !
\ No newline at end of file
diff --git a/chapters/fr/chapter9/4.mdx b/chapters/fr/chapter9/4.mdx
new file mode 100644
index 000000000..64ffcd196
--- /dev/null
+++ b/chapters/fr/chapter9/4.mdx
@@ -0,0 +1,140 @@
+# Partager ses démos avec les autres
+
+Maintenant que vous avez construit une démo, vous voudrez probablement la partager à d'autres personnes. Les démos *Gradio* peuvent être partagées de deux façons : en utilisant un lien de partage temporaire (***temporary share link***) ou un hébergement permanent (***permanent hosting on Spaces***).
+
+Nous aborderons ces deux approches sous peu. Mais avant de partager votre démo, vous voudrez peut-être la peaufiner 💅.
+
+### Polir votre démo Gradio
+
+
+
+
+
+
+Pour ajouter du contenu supplémentaire à votre démo, la classe `Interface` supporte quelques paramètres optionnels :
+ - `title` : vous pouvez donner un titre à votre démo, qui apparaît _au-dessus_ des composants d'entrée et de sortie.
+ - `description` : vous pouvez donner une description (en texte, Markdown, ou HTML) pour l'interface, qui apparaît au-dessus des composants d'entrée et de sortie et en dessous du titre.
+ - `article` : vous pouvez également écrire un article étendu (en texte, Markdown ou HTML) expliquant l'interface. S'il est fourni, il apparaît _sous_ les composants d'entrée et de sortie.
+ - `theme` : vous n'aimez pas les couleurs par défaut ? Définissez le thème pour utiliser une des couleurs suivantes : `default`, `huggingface`, `grass`, `peach`. Vous pouvez également ajouter le préfixe `dark-`, par exemple `dark-peach` pour un thème sombre (ou juste `dark` pour le thème sombre par défaut).
+ - `examples` : pour rendre votre démo *beaucoup plus facile à utiliser*, vous pouvez fournir quelques exemples d'entrées pour la fonction. Ceux-ci apparaissent sous les composants de l'interface utilisateur et peuvent être utilisés pour remplir l'interface. Ils doivent être fournis sous forme de liste imbriquée, dans laquelle la liste extérieure est constituée d'exemples et chaque liste intérieure est constituée d'une entrée correspondant à chaque composant d'entrée.
+ - `live` : si vous voulez que votre modèle soit relancé à chaque fois que l'entrée change, vous pouvez mettre `live=True`. Ceci est utile pour les modèles rapides (nous verrons un exemple à la fin de cette section).
+En utilisant les options ci-dessus, nous obtenons une interface plus complète. Exécutez le code ci-dessous pour pouvoir discuter avec Rick et Morty :
+
+```python out
+title = "Ask Rick a Question" # "Posez une question à Rick"
+description =
+"""
+The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything!
+# Le robot a été entraîné à répondre à des questions basées sur les dialogues de Rick et Morty.
+# Demandez à Rick ce que vous voulez !
+
+"""
+
+article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of."
+# Jetez un coup d'œil au [bot original Rick et Morty] (https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) sur lequel cette démo est basée.
+
+gr.Interface(
+ fn=predict,
+ inputs="textbox",
+ outputs="text",
+ title=title,
+ description=description,
+ article=article,
+ examples=[["What are you doing?"], ["Where should we time travel to?"]]
+ # ["Que faites-vous ?"], ["Où devrions-nous voyager dans le temps ?"]
+).launch()
+```
+
+En utilisant les options ci-dessus, nous obtenons une interface plus complète. Essayez l'interface ci-dessous :
+
+
+
+### Partager votre démo avec des liens temporaires
+Maintenant que nous avons une démo fonctionnelle de notre modèle d'apprentissage automatique, apprenons à partager facilement un lien vers notre interface.
+Les interfaces peuvent être facilement partagées publiquement en mettant `share=True` dans la méthode `launch()` :
+
+```python
+gr.Interface(classify_image, "image", "label").launch(share=True)
+```
+
+Cela génère un lien public et partageable que vous pouvez envoyer à n'importe qui ! Lorsque vous envoyez ce lien, l'utilisateur de l'autre côté peut essayer le modèle dans son navigateur pendant 72 heures au maximum. Le traitement s'effectuant sur votre appareil (tant qu'il reste allumé !), vous n'avez pas à vous soucier de la mise en place de dépendances. Si vous travaillez à partir d'un *notebook* Google Colab, un lien de partage est toujours créé automatiquement. Il ressemble généralement à quelque chose comme ceci : **XXXXX.gradio.app**. Bien que le lien soit servi par un lien *Gradio*, nous ne sommes qu'un proxy pour votre serveur local, et nous ne stockons pas les données envoyées par les interfaces.
+
+Gardez cependant à l'esprit que ces liens sont accessibles au public, ce qui signifie que n'importe qui peut utiliser votre modèle pour la prédiction ! Par conséquent, assurez-vous de ne pas exposer d'informations sensibles à travers les fonctions que vous écrivez, ou de permettre que des changements critiques se produisent sur votre appareil. Si vous définissez `share=False` (la valeur par défaut), seul un lien local est créé.
+
+### Hébergement de votre démo sur Hugging Face Spaces
+
+Un lien de partage que vous pouvez passer à vos collègues est cool, mais comment pouvez-vous héberger de façon permanente votre démo et la faire exister dans son propre « espace » sur internet ?
+
+*Hugging Face Spaces* fournit l'infrastructure pour héberger de façon permanente votre démo *Gradio* sur internet et **gratuitement** ! *Spaces* vous permet de créer et de pousser vers un dépôt (public ou privé) le code de votre interface *Gradio*. Il sera placé dans un fichier `app.py`. [Lisez ce tutoriel étape par étape](https://huggingface.co/blog/gradio-spaces) pour commencer ou regardez la vidéo ci-dessous.
+
+
+
+## ✏️ Let's apply it!
+
+En utilisant ce que nous avons appris dans les sections précédentes, créons la démo de reconnaissance de croquis que nous avons décrit dans la [section un de ce chapitre] (/course/fr/chapter9/1). Ajoutons quelques personnalisations à notre interface et définissons `share=True` pour créer un lien public que nous pouvons faire circuler.
+
+Nous pouvons charger les étiquettes depuis [class_names.txt](https://huggingface.co/spaces/dawood/Sketch-Recognition/blob/main/class_names.txt) et charger le modèle Pytorch pré-entraîné depuis [pytorch_model.bin](https://huggingface.co/spaces/dawood/Sketch-Recognition/blob/main/pytorch_model.bin). Téléchargez ces fichiers en suivant le lien et en cliquant sur « *download* » dans le coin supérieur gauche de l'aperçu du fichier. Regardons le code ci-dessous pour voir comment nous utilisons ces fichiers pour charger notre modèle et créer une fonction `predict()` :
+
+```py
+from pathlib import Path
+import torch
+import gradio as gr
+from torch import nn
+
+LABELS = Path("class_names.txt").read_text().splitlines()
+
+model = nn.Sequential(
+ nn.Conv2d(1, 32, 3, padding="same"),
+ nn.ReLU(),
+ nn.MaxPool2d(2),
+ nn.Conv2d(32, 64, 3, padding="same"),
+ nn.ReLU(),
+ nn.MaxPool2d(2),
+ nn.Conv2d(64, 128, 3, padding="same"),
+ nn.ReLU(),
+ nn.MaxPool2d(2),
+ nn.Flatten(),
+ nn.Linear(1152, 256),
+ nn.ReLU(),
+ nn.Linear(256, len(LABELS)),
+)
+state_dict = torch.load("pytorch_model.bin", map_location="cpu")
+model.load_state_dict(state_dict, strict=False)
+model.eval()
+
+
+def predict(im):
+ x = torch.tensor(im, dtype=torch.float32).unsqueeze(0).unsqueeze(0) / 255.0
+ with torch.no_grad():
+ out = model(x)
+ probabilities = torch.nn.functional.softmax(out[0], dim=0)
+ values, indices = torch.topk(probabilities, 5)
+ return {LABELS[i]: v.item() for i, v in zip(indices, values)}
+```
+
+Maintenant que nous avons une fonction `predict()`. La prochaine étape est de définir et de lancer notre interface *Gradio* :
+
+```py
+interface = gr.Interface(
+ predict,
+ inputs="sketchpad",
+ outputs="label",
+ theme="huggingface",
+ title="Sketch Recognition",
+ description="Who wants to play Pictionary? Draw a common object like a shovel or a laptop, and the algorithm will guess in real time!",
+ # Qui veut jouer au Pictionary ? Dessinez un objet courant comme une pelle ou un ordinateur portable, et l'algorithme le devinera en temps réel !
+ article="Sketch Recognition | Demo Model
",
+ live=True,
+)
+interface.launch(share=True)
+```
+
+
+
+
+Notice the `live=True` parameter in `Interface`, which means that the sketch demo makes a prediction every time someone draws on the sketchpad (no submit button!).
+
+De plus, nous avons également défini l'argument `share=True` dans la méthode `launch()`.
+Cela créera un lien public que vous pourrez envoyer à n'importe qui ! Lorsque vous envoyez ce lien, l'utilisateur de l'autre côté peut essayer le modèle de reconnaissance de croquis. Pour réitérer, vous pouvez également héberger le modèle sur *Hugging Face Spaces*, ce qui nous permet d'intégrer la démo ci-dessus.
+
+La prochaine fois, nous couvrirons d'autres façons dont *Gradio* peut être utilisé avec l'écosystème d'*Hugging Face* !
\ No newline at end of file
diff --git a/chapters/fr/chapter9/5.mdx b/chapters/fr/chapter9/5.mdx
new file mode 100644
index 000000000..18f1b9c67
--- /dev/null
+++ b/chapters/fr/chapter9/5.mdx
@@ -0,0 +1,66 @@
+# Intégrations avec le Hub d'Hugging Face
+
+Pour vous rendre la vie encore plus facile, *Gradio* s'intègre directement avec *Hub* et *Spaces*.
+Vous pouvez charger des démos depuis le *Hub* et les *Spaces* avec seulement *une ligne de code*.
+
+### Chargement de modèles depuis lle Hub d'Hugging Face
+Pour commencer, choisissez un des milliers de modèles qu'*Hugging Face* offre à travers le *Hub*, comme décrit dans le [chapitre 4](/course/fr/chapter4/2).
+
+En utilisant la méthode spéciale `Interface.load()`, vous passez `"model/"` (ou, de manière équivalente, `"huggingface/"`) suivi du nom du modèle.
+Par exemple, voici le code pour construire une démo pour le [GPT-J](https://huggingface.co/EleutherAI/gpt-j-6B), un grand modèle de langue, ajouter quelques exemples d'entrées :
+
+```py
+import gradio as gr
+
+title = "GPT-J-6B"
+description = "Gradio Demo for GPT-J 6B, a transformer model trained using Ben Wang's Mesh Transformer JAX. 'GPT-J' refers to the class of model, while '6B' represents the number of trainable parameters. To use it, simply add your text, or click one of the examples to load them. Read more at the links below."
+# Démo Gradio pour GPT-J 6B, un modèle de transformer entraîné avec le Mesh Transformer JAX de Ben Wang. GPT-J fait référence à la classe du modèle, tandis que '6B' représente le nombre de paramètres entraînables. Pour l'utiliser, il suffit d'ajouter votre texte, ou de cliquer sur l'un des exemples pour le charger. Pour en savoir plus, consultez les liens ci-dessous.
+article = "GPT-J-6B: A 6 Billion Parameter Autoregressive Language Model
"
+# GPT-J-6B : Un modèle linguistique autorégressif à 6 milliards de paramètres
+examples = [
+ ["The tower is 324 metres (1,063 ft) tall,"],
+ # La tour mesure 324 mètres (1 063 pieds) de haut,
+ ["The Moon's orbit around Earth has"],
+ # L'orbite de la Lune autour de la Terre a
+ ["The smooth Borealis basin in the Northern Hemisphere covers 40%"],
+ # Le bassin de Borealis dans l'hémisphère nord couvre 40 %.
+]
+gr.Interface.load(
+ "huggingface/EleutherAI/gpt-j-6B",
+ inputs=gr.Textbox(lines=5, label="Input Text"),
+ title=title,
+ description=description,
+ article=article,
+ examples=examples,
+ enable_queue=True,
+).launch()
+```
+
+Le code ci-dessus produira l'interface ci-dessous :
+
+
+
+Le chargement d'un modèle de cette manière utilise l'[API d'Inference] (https://huggingface.co/inference-api) de *Hugging Face* au lieu de charger le modèle en mémoire. C'est idéal pour les modèles énormes comme GPT-J ou T0pp qui nécessitent beaucoup de RAM.
+
+### Chargement depuis Hugging Face Spaces
+Pour charger n'importe quel *Space* depuis le *Hub* et le recréer localement, vous pouvez passer `spaces/` à l'`Interface`, suivi du nom du *Space*.
+
+Vous vous souvenez de la démo de la section 1 qui supprime le fond d'une image ? Chargeons-la à partir de *Hugging Face Spaces* :
+
+```py
+gr.Interface.load("spaces/abidlabs/remove-bg").launch()
+```
+
+
+
+L'un des avantages du chargement de démos à partir du *Hub* ou de *Spaces* est que vous pouvez les personnaliser en remplaçant n'importe lequel des paramètres. Ici, nous ajoutons un titre et faisons en sorte qu'elle fonctionne avec une webcam à la place :
+
+```py
+gr.Interface.load(
+ "spaces/abidlabs/remove-bg", inputs="webcam", title="Remove your webcam background!"
+).launch()
+```
+
+
+
+Maintenant que nous avons exploré quelques façons d'intégrer *Gradio* avec le *Hub*, jetons un coup d'oeil à certaines fonctionnalités avancées de la classe `Interface`. C'est le sujet de la prochaine section !
\ No newline at end of file
diff --git a/chapters/fr/chapter9/6.mdx b/chapters/fr/chapter9/6.mdx
new file mode 100644
index 000000000..6a3413fb9
--- /dev/null
+++ b/chapters/fr/chapter9/6.mdx
@@ -0,0 +1,132 @@
+# Fonctionnalités avancées de l'interface
+
+Maintenant que nous pouvons construire et partager une interface de base, explorons quelques fonctionnalités plus avancées comme l'état, l'interprétation et l'authentification.
+
+### Utilisation de l'état pour faire persister les données
+
+*Gradio* supporte *l'état de session* où les données persistent à travers plusieurs soumissions dans un chargement de page. L'état de session est utile pour construire des démos où vous souhaitez faire persister les données au fur et à mesure que l'utilisateur interagit avec le modèle (par exemple des chatbots). Notez que l'état de session ne partage pas les données entre les différents utilisateurs de votre modèle.
+
+Pour stocker des données dans un état de session, vous devez faire trois choses :
+
+- Passez un *paramètre supplémentaire* dans votre fonction, qui représente l'état de l'interface.
+- A la fin de la fonction, renvoyer la valeur mise à jour de l'état comme une *valeur de retour supplémentaire*.
+- Ajoutez les composants "state" input et "state" output lors de la création de votre `Interface`.
+
+Voir l'exemple de chatbot ci-dessous :
+
+```py
+import random
+
+import gradio as gr
+
+
+def chat(message, history):
+ history = history or []
+ if message.startswith("How many"):
+ response = random.randint(1, 10)
+ elif message.startswith("How"):
+ response = random.choice(["Great", "Good", "Okay", "Bad"])
+ elif message.startswith("Where"):
+ response = random.choice(["Here", "There", "Somewhere"])
+ else:
+ response = "I don't know"
+ history.append((message, response))
+ return history, history
+
+
+iface = gr.Interface(
+ chat,
+ ["text", "state"],
+ ["chatbot", "state"],
+ allow_screenshot=False,
+ allow_flagging="never",
+)
+iface.launch()
+```
+
+
+
+Remarquez comment l'état du composant de sortie persiste entre les soumissions.
+Remarque : vous pouvez transmettre une valeur par défaut au paramètre state, qui est utilisée comme valeur initiale de l'état.
+
+### Utilisation de l'interprétation pour comprendre les prédictions
+
+La plupart des modèles d'apprentissage automatique sont des boîtes noires et la logique interne de la fonction est cachée à l'utilisateur final. Pour encourager la transparence, nous avons fait en sorte qu'il soit très facile d'ajouter l'interprétation à votre modèle en définissant simplement le mot-clé interprétation dans la classe Interface par défaut. Cela permet à vos utilisateurs de comprendre quelles parties de l'entrée sont responsables de la sortie. Jetez un coup d'œil à l'interface simple ci-dessous qui montre un classificateur d'images incluant l'interprétation :
+
+```py
+import requests
+import tensorflow as tf
+
+import gradio as gr
+
+inception_net = tf.keras.applications.MobileNetV2() # charger le modèle
+
+# Télécharger des étiquettes lisibles par l'homme pour ImageNet
+response = requests.get("https://git.io/JJkYN")
+labels = response.text.split("\n")
+
+
+def classify_image(inp):
+ inp = inp.reshape((-1, 224, 224, 3))
+ inp = tf.keras.applications.mobilenet_v2.preprocess_input(inp)
+ prediction = inception_net.predict(inp).flatten()
+ return {labels[i]: float(prediction[i]) for i in range(1000)}
+
+
+image = gr.Image(shape=(224, 224))
+label = gr.Label(num_top_classes=3)
+
+title = "Gradio Image Classifiction + Interpretation Example"
+gr.Interface(
+ fn=classify_image, inputs=image, outputs=label, interpretation="default", title=title
+).launch()
+```
+
+Testez la fonction d'interprétation en soumettant une entrée puis en cliquant sur « Interpréter » sous le composant de sortie.
+
+
+
+En plus de la méthode d'interprétation par défaut fournie par *Gradio*, vous pouvez également spécifier `shap` pour le paramètre `interpretation` et définir le paramètre `num_shap`. Ceci utilise l'interprétation basée sur Shapley, dont vous pouvez lire plus sur [ici](https://christophm.github.io/interpretable-ml-book/shap.html).
+Enfin, vous pouvez aussi passer votre propre fonction d'interprétation dans le paramètre `interpretation`. Vous trouverez un exemple dans la page de démarrage de *Gradio* [ici](https://gradio.app/getting_started/).
+
+
+### Ajouter l'authentification
+
+Vous pouvez vouloir ajouter une authentification à votre interface *Gradio* afin de contrôler qui peut accéder et utiliser votre démo.
+
+L'authentification peut être ajoutée en fournissant une liste de tuples de nom d'utilisateur/mot de passe au paramètre `auth` de la méthode `launch()`. Pour une gestion plus complexe de l'authentification, vous pouvez passer une fonction qui prend un nom d'utilisateur et un mot de passe comme arguments, et retourne `True` pour permettre l'authentification, `False` sinon.
+
+Prenons la démo de classification d'images ci-dessus et ajoutons l'authentification :
+
+```py
+import requests
+import tensorflow as tf
+
+import gradio as gr
+
+inception_net = tf.keras.applications.MobileNetV2() # charger le modèle
+
+# Télécharger des étiquettes lisibles par l'homme pour ImageNet
+response = requests.get("https://git.io/JJkYN")
+labels = response.text.split("\n")
+
+
+def classify_image(inp):
+ inp = inp.reshape((-1, 224, 224, 3))
+ inp = tf.keras.applications.mobilenet_v2.preprocess_input(inp)
+ prediction = inception_net.predict(inp).flatten()
+ return {labels[i]: float(prediction[i]) for i in range(1000)}
+
+
+image = gr.Image(shape=(224, 224))
+label = gr.Label(num_top_classes=3)
+
+title = "Gradio Image Classifiction + Interpretation Example"
+gr.Interface(
+ fn=classify_image, inputs=image, outputs=label, interpretation="default", title=title
+).launch(auth=("admin", "pass1234"))
+```
+
+
+
+Ceci conclut notre plongée dans la classe `Interface` de *Gradio*. Comme nous l'avons vu, cette classe permet de créer facilement des démos d'apprentissage automatique en quelques lignes de code Python. Cependant, vous voudrez parfois personnaliser votre démo en changeant la mise en page ou en enchaînant plusieurs fonctions de prédiction. Ne serait-il pas agréable de pouvoir diviser l'interface en blocs personnalisables ? Heureusement, c'est possible ! C'est le sujet de la dernière section.
\ No newline at end of file
diff --git a/chapters/fr/chapter9/7.mdx b/chapters/fr/chapter9/7.mdx
new file mode 100644
index 000000000..edf7c91df
--- /dev/null
+++ b/chapters/fr/chapter9/7.mdx
@@ -0,0 +1,233 @@
+# Introduction à la classe Blocks
+
+Dans les sections précédentes, nous avons exploré et créé des démos en utilisant la classe `Interface`. Dans cette section, nous allons présenter une **nouvelle** API de bas niveau appelée `gradio.Blocks`.
+
+Quelle est la différence entre `Interface` et `Blocks` ?
+
+- ⚡ `Interface` : une API de haut niveau qui vous permet de créer une démo complète d'apprentissage automatique simplement en fournissant une liste d'entrées et de sorties.
+
+- 🧱 `Blocks` : une API de bas niveau qui vous permet d'avoir un contrôle total sur les flux de données et la disposition de votre application. Vous pouvez construire des applications très complexes, en plusieurs étapes, en utilisant `Blocks`.
+
+
+### Pourquoi Blocks 🧱 ?
+
+Comme nous l'avons vu dans les sections précédentes, la classe `Interface` vous permet de créer facilement des démos d'apprentissage automatique à part entière avec seulement quelques lignes de code. L'API `Interface` est extrêmement facile à utiliser, mais elle n'a pas la flexibilité qu'offre l'API `Blocks`. Par exemple, vous pourriez vouloir :
+
+- regrouper des démos connexes sous forme d'onglets multiples dans une application web,
+- modifier la mise en page de votre démo, par exemple pour spécifier l'emplacement des entrées et des sorties,
+- disposer d'interfaces multi-étapes dans lesquelles la sortie d'un modèle devient l'entrée du modèle suivant ou avoir des flux de données plus flexibles en général,
+- modifier les propriétés d'un composant (par exemple, les choix dans une liste déroulante) ou sa visibilité en fonction des entrées de l'utilisateur.
+
+Nous allons explorer tous ces concepts ci-dessous.
+
+### Création d'une démo simple en utilisant Blocks
+
+Après avoir installé *Gradio*, exécutez le code ci-dessous sous forme de script Python, de *notebook* Jupyter ou de *notebook* Colab.
+
+```py
+import gradio as gr
+
+
+def flip_text(x):
+ return x[::-1]
+
+
+demo = gr.Blocks()
+
+with demo:
+ gr.Markdown(
+ """
+ # Flip Text!
+ Start typing below to see the output.
+ """
+ )
+ input = gr.Textbox(placeholder="Flip this text")
+ output = gr.Textbox()
+
+ input.change(fn=flip_text, inputs=input, outputs=output)
+
+demo.launch()
+```
+
+
+
+Ce simple exemple ci-dessus introduit 4 concepts qui sous-tendent les *Blocks* :
+
+1. Les *Blocks* vous permettent de construire des applications web qui combinent Markdown, HTML, boutons et composants interactifs simplement en instanciant des objets en Python dans un contexte `with gradio.Blocks`.
+
+
+🙋Si vous n'êtes pas familier avec l'instruction `with` en Python, nous vous recommandons de consulter l'excellent [tutoriel](https://realpython.com/python-with-statement/) de Real Python. Revenez ici après l'avoir lu 🤗
+
+
+L'ordre dans lequel vous instanciez les composants est important car chaque élément est restitué dans l'application Web dans l'ordre où il a été créé. (Les mises en page plus complexes sont abordées ci-dessous)
+
+2. Vous pouvez définir des fonctions Python ordinaires n'importe où dans votre code et les exécuter avec des entrées utilisateur en utilisant les `Blocks`. Dans notre exemple, nous avons une fonction simple qui inverse le texte entré mais vous pouvez écrire n'importe quelle fonction Python, du simple calcul au traitement des prédictions d'un modèle d'apprentissage automatique.
+
+3. Vous pouvez assigner des événements à n'importe quel composant `Blocks`. Ainsi, votre fonction sera exécutée lorsque le composant sera cliqué, modifié, etc. Lorsque vous assignez un événement, vous passez trois paramètres :
+- `fn` : la fonction qui doit être appelée,
+- `inputs` : la (liste) des composants d'entrée
+- `outputs` : la (liste) des composants de sortie qui doivent être appelés.
+ Dans l'exemple ci-dessus, nous exécutons la fonction `flip_text()` lorsque la valeur de la `Textbox` nommée input `input` change. L'événement lit la valeur dans `input`, la passe comme paramètre de nom à `flip_text()`, qui renvoie alors une valeur qui est assignée à notre seconde `Textbox` nommée `output`.
+ Pour voir la liste des événements que chaque composant supporte, consultez la [documentation](https://www.gradio.app/docs/) de *Gradio*.
+
+4. *Blocks* détermine automatiquement si un composant doit être interactif (accepter les entrées de l'utilisateur) ou non, en fonction des déclencheurs d'événements que vous définissez. Dans notre exemple, la première zone de texte est interactive, puisque sa valeur est utilisée par la fonction `flip_text()`. La deuxième zone de texte n'est pas interactive, puisque sa valeur n'est jamais utilisée comme entrée. Dans certains cas, vous voudrez peut-être passer outre, ce que vous pouvez faire en passant un booléen au paramètre `interactive` du composant (par exemple, `gr.Textbox(placeholder="Flip this text", interactive=True)`).
+
+
+### Personnaliser la mise en page de votre démo
+
+Comment pouvons-nous utiliser `Blocks` pour personnaliser la mise en page de notre démo ? Par défaut, `Blocks` affiche verticalement dans une colonne les composants que vous créez. Vous pouvez changer cela en créant des colonnes supplémentaires `avec gradio.Column():` ou des lignes `avec gradio.Row():` et en créant des composants dans ces contextes.
+
+Voici ce que vous devez garder à l'esprit : tout composant créé sous une `Column` (c'est aussi le défaut) sera disposé verticalement. Tout composant créé sous une `Row` sera disposé horizontalement, comme le [modèle flexbox dans le développement web](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox).
+
+Enfin, vous pouvez également créer des onglets pour votre démo en utilisant le gestionnaire de contexte `with gradio.Tabs()`. Dans ce contexte, vous pouvez créer plusieurs onglets en spécifiant des enfants `with gradio.TabItem(name_of_tab):`. Tout composant créé dans un contexte `with gradio.TabItem(name_of_tab):` apparaît dans cet onglet.
+
+Maintenant, ajoutons une fonction `flip_image()` à notre démo et ajoutons un nouvel onglet qui retourne les images. Vous trouverez ci-dessous un exemple avec 2 onglets et utilisant également une `Row` :
+
+```py
+import numpy as np
+import gradio as gr
+
+demo = gr.Blocks()
+
+
+def flip_text(x):
+ return x[::-1]
+
+
+def flip_image(x):
+ return np.fliplr(x)
+
+
+with demo:
+ gr.Markdown("Flip text or image files using this demo.")
+ with gr.Tabs():
+ with gr.TabItem("Flip Text"):
+ with gr.Row():
+ text_input = gr.Textbox()
+ text_output = gr.Textbox()
+ text_button = gr.Button("Flip")
+ with gr.TabItem("Flip Image"):
+ with gr.Row():
+ image_input = gr.Image()
+ image_output = gr.Image()
+ image_button = gr.Button("Flip")
+
+ text_button.click(flip_text, inputs=text_input, outputs=text_output)
+ image_button.click(flip_image, inputs=image_input, outputs=image_output)
+
+demo.launch()
+```
+
+
+
+
+Vous remarquerez que dans cet exemple, nous avons également créé un composant `Button` dans chaque onglet et avons assigné un événement de clic à chaque bouton qui est l'élément qui exécute réellement la fonction.
+
+### Exploration des événements et de l'état
+
+De la même manière que vous pouvez contrôler la mise en page, `Blocks` vous donne un contrôle fin sur les événements qui déclenchent les appels de fonction. Chaque composant et de nombreux layouts ont des événements spécifiques qu'ils supportent.
+
+Par exemple, le composant `Textbox` a 2 événements : `change()` (lorsque la valeur contenue dans la zone de texte change), et `submit()` (lorsqu'un utilisateur appuie sur la touche Entrée alors qu'il est concentré sur la zone de texte). Les composants plus complexes peuvent avoir encore plus d'événements : par exemple, le composant `Audio` a aussi des événements séparés pour quand le fichier audio est joué, effacé, mis en pause, etc. Consultez la documentation pour connaître les événements pris en charge par chaque composant.
+
+Vous pouvez attacher un déclencheur d'événement à aucun, un ou plusieurs de ces événements. Vous créez un déclencheur d'événement en appelant le nom de l'événement sur l'instance du composant comme une fonction. Par exemple, `textbox.change(...)` ou `btn.click(...)`. La fonction prend trois paramètres, comme indiqué ci-dessus :
+
+- `fn` : la fonction à exécuter
+- `inputs` : une (liste de) composante(s) dont les valeurs doivent être fournies comme paramètres d'entrée à la fonction. La valeur de chaque composant est mise en correspondance avec le paramètre de fonction correspondant, dans l'ordre. Ce paramètre peut être `None` si la fonction ne prend aucun paramètre.
+- `outputs` : un (liste de) composant(s) dont les valeurs doivent être mises à jour en fonction des valeurs retournées par la fonction. Chaque valeur de retour met à jour la valeur du composant correspondant, dans l'ordre. Ce paramètre peut être None si la fonction ne retourne rien.
+
+Vous pouvez même faire en sorte que le composant d'entrée et de sortie soit le même composant, comme nous le faisons dans cet exemple qui utilise un modèle GPT pour compléter du texte :
+
+```py
+import gradio as gr
+
+api = gr.Interface.load("huggingface/EleutherAI/gpt-j-6B")
+
+
+def complete_with_gpt(text):
+ # Utilise les 50 derniers caractères du texte comme contexte.
+ return text[:-50] + api(text[-50:])
+
+
+with gr.Blocks() as demo:
+ textbox = gr.Textbox(placeholder="Type here and press enter...", lines=4)
+ btn = gr.Button("Generate")
+
+ btn.click(complete_with_gpt, textbox, textbox)
+
+demo.launch()
+```
+
+
+
+### Création de démos multi-étapes
+
+Dans certains cas, vous pouvez vouloir une _démo multi-étapes_, dans laquelle vous réutilisez la sortie d'une fonction comme entrée de la suivante. C'est très facile à faire avec les `Blocks`, car vous pouvez utiliser un composant pour l'entrée d'un déclencheur d'événement mais la sortie d'un autre. Regardez le composant texte dans l'exemple ci-dessous, sa valeur est le résultat d'un modèle de conversion de la parole en texte, mais il est également transmis à un modèle d'analyse des sentiments :
+
+```py
+from transformers import pipeline
+
+import gradio as gr
+
+asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h")
+classifier = pipeline("text-classification")
+
+
+def speech_to_text(speech):
+ text = asr(speech)["text"]
+ return text
+
+
+def text_to_sentiment(text):
+ return classifier(text)[0]["label"]
+
+
+demo = gr.Blocks()
+
+with demo:
+ audio_file = gr.Audio(type="filepath")
+ text = gr.Textbox()
+ label = gr.Label()
+
+ b1 = gr.Button("Recognize Speech")
+ b2 = gr.Button("Classify Sentiment")
+
+ b1.click(speech_to_text, inputs=audio_file, outputs=text)
+ b2.click(text_to_sentiment, inputs=text, outputs=label)
+
+demo.launch()
+```
+
+
+
+### Mise à jour des propriétés des composants
+
+Jusqu'à présent, nous avons vu comment créer des événements pour mettre à jour la valeur d'un autre composant. Mais que se passe-t-il si vous voulez modifier d'autres propriétés d'un composant, comme la visibilité d'une zone de texte ou les choix dans un groupe de boutons radio ? Vous pouvez le faire en renvoyant la méthode `update()` d'une classe de composant au lieu d'une valeur de retour normale de votre fonction.
+
+L'exemple le plus facile à illustrer est le suivant :
+
+```py
+import gradio as gr
+
+
+def change_textbox(choice):
+ if choice == "short":
+ return gr.Textbox.update(lines=2, visible=True)
+ elif choice == "long":
+ return gr.Textbox.update(lines=8, visible=True)
+ else:
+ return gr.Textbox.update(visible=False)
+
+
+with gr.Blocks() as block:
+ radio = gr.Radio(
+ ["short", "long", "none"], label="What kind of essay would you like to write?"
+ )
+ text = gr.Textbox(lines=2, interactive=True)
+
+ radio.change(fn=change_textbox, inputs=radio, outputs=text)
+ block.launch()
+```
+
+
+
+Nous venons d'explorer tous les concepts de base des `Blocks` ! Tout comme avec `Interface`, vous pouvez créer des démos sympas qui peuvent être partagées en utilisant `share=True` dans la méthode `launch()` ou déployées sur [*Spaces*](https://huggingface.co/spaces).
\ No newline at end of file
diff --git a/chapters/fr/chapter9/8.mdx b/chapters/fr/chapter9/8.mdx
new file mode 100644
index 000000000..1225f0eb3
--- /dev/null
+++ b/chapters/fr/chapter9/8.mdx
@@ -0,0 +1,234 @@
+
+
+# Quiz de fin de chapitre
+
+Testons ce que vous avez appris dans ce chapitre !
+
+### 1. Que pouvez-vous faire avec Gradio ?
+
+share=True dans la méthode de lancement, vous pouvez générer un lien de partage à envoyer à tout le monde.",
+ correct: true
+ },
+ {
+ text: "Déboguez votre modèle.",
+ explain: "L'un des avantages d'une démo Gradio est de pouvoir tester votre modèle avec des données réelles que vous pouvez modifier et observer les prédictions du modèle changer en temps réel, ce qui vous aide à déboguer votre modèle.",
+ correct: true
+ },
+ {
+ text: "Entraîner votre modèle.",
+ explain: "Gradio est conçu pour être utilisé pour l'inférence, APRÈS que votre modèle a été entraîné.",
+ }
+ ]}
+/>
+
+### 2. Gradio fonctionne UNIQUEMENT avec les modèles en PyTorch
+
+Gradio fonctionne avec les modèles Pytorch mais aussi pour tout type de modèle d'apprentissage automatique !"
+ },
+ {
+ text: "False",
+ explain: "Gradio est indifférent au modèle ce qui signifie que vous pouvez créer une démo pour tout type de modèle d'apprentissage automatique.",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. D'où pouvez-vous lancer une démo Gradio ?
+
+Gradio fonctionne parfaitement avec votre IDE préféré.",
+ correct: true
+ },
+ {
+ text: "De notebooks Google Colab",
+ explain: "Vous pouvez créer et lancer une démo dans votre notebook Google Colab.",
+ correct: true
+ },
+ {
+ text: "De notebooks Jupyter",
+ explain: "Vous pouvez créer et lancer une démo dans votre notebook Jupyter.",
+ correct: true
+ }
+ ]}
+/>
+
+### 4. Gradio est conçu principalement pour les modèles de NLP
+
+Gradio fonctionne avec pratiquement tous les types de données, pas seulement avec le NLP."
+ },
+ {
+ text: "False",
+ explain: "Gradio fournit aux développeurs une bibliothèque de composants préconstruits pour pratiquement tous les types de données.",
+ correct: true
+ }
+ ]}
+/>
+
+### 5. Parmi les fonctionnalités suivantes, lesquelles sont prises en charge par Gradio ?
+
+Gradio. Tout ce que vous devez faire est de passer une liste d'entrées et de sorties à leurs paramètres correspondants.",
+ correct: true
+ },
+ {
+ text: "État pour la persistance des données.",
+ explain: "Gradio est capable d'ajouter un état à votre interface.",
+ correct: true
+ },
+ {
+ text: "Authentification par nom d'utilisateur et mot de passe.",
+ explain: "Passez une liste de tuples de nom d'utilisateur/mot de passe à la méthode de lancement pour ajouter l'authentification.",
+ correct: true
+ },
+ {
+ text: "Analyse automatique de l'utilisation de votre démo Gradio.",
+ explain: "Gradio ne fournit pas aux développeurs des analyses sur les personnes qui utilisent leurs démos."
+ },
+ {
+ text: "Chargement d'un modèle à partir du Hub ou de Space.",
+ explain: "Charger n'importe quel modèle de Hugging Face en utilisant la méthode gr.Interface.load()
.",
+ correct: true
+ }
+ ]}
+/>
+
+### 6. Lesquelles des méthodes suivantes sont valides pour charger un modèle à partir du Hub ou de Space ?
+
+gr.Interface.load('huggingface/{user}/{model_name}')",
+ explain: "Il s'agit d'une méthode valide de chargement d'un modèle à partir du Hub.",
+ correct: true
+ },
+ {
+ text: "gr.Interface.load('model/{user}/{model_name}')
",
+ explain: "Il s'agit d'une méthode valide de chargement d'un modèle à partir du Hub.",
+ correct: true
+ },
+ {
+ text: "gr.Interface.load('demos/{user}/{model_name}')
",
+ explain: "Vous ne pouvez pas charger un modèle en utilisant le préfixe demos
."
+ },
+ {
+ text: "gr.Interface.load('spaces/{user}/{model_name}')
",
+ explain: "Il s'agit d'une méthode valide de chargement d'un modèle à partir de Space.",
+ correct: true
+ }
+ ]}
+/>
+
+### 7. Sélectionnez toutes les étapes nécessaires pour ajouter un état à votre interface Gradio
+
+Gradio fournit un composant d'entrée et de sortie d'état pour persister les données.",
+ correct: true
+ }
+ ]}
+/>
+
+### 8. Lesquels des éléments suivants sont des composants inclus dans la bibliothèque Gradio ?
+
+Textbox.",
+ explain: "Oui, vous pouvez créer des zones de texte avec le composant Textbox
.",
+ correct: true
+ },
+ {
+ text: "Graph
.",
+ explain: "Il n'y a actuellement aucun composant Graph
.",
+ },
+ {
+ text: "Image
.",
+ explain: "Oui, vous pouvez créer un widget de téléchargement d'images avec le composant Image
.",
+ correct: true
+ },
+ {
+ text: "Audio
.",
+ explain: "Oui, vous pouvez créer un widget de téléchargement audio avec le composant Audio
.",
+ correct: true
+ },
+ ]}
+/>
+
+### 9. Qu'est-ce que les `Blocks` vous permet de faire ?
+
+with gradio.Tabs(): pour ajouter des onglets pour plusieurs démos.",
+ correct: true
+ },
+ {
+ text: "Attribuer des déclencheurs d'événements tels que clicked/changed/etc aux composants Blocks
.",
+ explain: "Lorsque vous assignez un événement, vous passez trois paramètres : fn
qui est la fonction qui doit être appelée, inputs
qui est la (liste) des composants d'entrée, et outputs
qui est la (liste) des composants de sortie qui doivent être appelés.",
+ correct: true
+ },
+ {
+ text: "Déterminer automatiquement quel composant Blocks
doit être interactif ou statique.",
+ explain: "En fonction des déclencheurs d'événements que vous définissez, Blocks
détermine automatiquement si un composant doit accepter ou non les entrées de l'utilisateur..",
+ correct: true
+ },
+ {
+ text: "Créer des démos en plusieurs étapes, c'est-à-dire vous permettre de réutiliser la sortie d'un composant comme entrée pour le suivant.",
+ explain: "Vous pouvez utiliser un composant pour l'entrée d'un déclencheur d'événement mais la sortie d'un autre.",
+ correct: true
+ },
+ ]}
+/>
+
+### 10. Vous pouvez partager un lien public vers une démo Blocks
et accueillir une démo Blocks
sur Space
+
+Interface, toutes les capacités de partage et d'hébergement sont les mêmes pour les démos basées sur Blocks
!",
+ correct: true
+ },
+ {
+ text: "False",
+ explain: "Tout comme Interface
, toutes les capacités de partage et d'hébergement sont les mêmes pour les démos basées sur Blocks
!",
+ correct: false
+ }
+ ]}
+/>
\ No newline at end of file
diff --git a/chapters/fr/event/1.mdx b/chapters/fr/event/1.mdx
index ab52d0489..e6d766bd7 100644
--- a/chapters/fr/event/1.mdx
+++ b/chapters/fr/event/1.mdx
@@ -1,170 +1,170 @@
-# Événement pour le lancement de la partie 2
-
-Pour la sortie de la deuxième partie du cours, nous avons organisé un événement en direct consistant en deux jours de conférences suivies d’un *sprint* de *finetuning*. Si vous l'avez manqué, vous pouvez rattraper les présentations qui sont toutes listées ci-dessous !
-
-## Jour 1 : Une vue d'ensemble des *transformers* et comment les entraîner
-
-
-**Thomas Wolf :** *L'apprentissage par transfert et la naissance de la bibliothèque 🤗 Transformers*
-
-
-
-
-
-
-
-
-
-Thomas Wolf est cofondateur et directeur scientifique d’Hugging Face. Les outils créés par Thomas Wolf et l'équipe d’Hugging Face sont utilisés par plus de 5 000 organismes de recherche, dont Facebook Artificial Intelligence Research, Google Research, DeepMind, Amazon Research, Apple, l'Allen Institute for Artificial Intelligence ainsi que la plupart des départements universitaires. Thomas Wolf est l'initiateur et le président principal de la plus grande collaboration de recherche qui ait jamais existé dans le domaine de l'intelligence artificielle : [« BigScience »](https://bigscience.huggingface.co), ainsi que d'un ensemble de [bibliothèques et outils](https://github.com/huggingface/) largement utilisés. Thomas Wolf est également un éducateur prolifique, un *leader* d'opinion dans le domaine de l'intelligence artificielle et du traitement du langage naturel, et un orateur régulièrement invité à des conférences dans le monde entier [https://thomwolf.io](https://thomwolf.io).
-
-**Jay Alammar :** *Une introduction visuelle douce aux transformers*
-
-
-
-
-
-
-
-
-
-Grâce à son blog d’apprentissage automatique très populaire, Jay a aidé des millions de chercheurs et d'ingénieurs à comprendre visuellement les outils et les concepts de l'apprentissage automatique, des plus élémentaires (qui se retrouvent dans les docs NumPy et Pandas) aux plus pointus (Transformer, BERT, GPT-3).
-
-**Margaret Mitchell :** *Les valeurs dans le développement de l’apprentissage automatique*
-
-
-
-
-
-
-
-
-
-Margaret Mitchell est une chercheuse travaillant sur l'IA éthique. Elle se concentre actuellement sur les tenants et aboutissants du développement de l'IA éthique dans le domaine de la technologie. Elle a publié plus de cinquante articles sur la génération de langage naturel, les technologies d'assistance, la vision par ordinateur et l'IA éthique. Elle détient plusieurs brevets dans le domaine de la génération de conversations et celui de la classification des sentiments. Elle a précédemment travaillé chez Google AI en tant que chercheuse où elle a fondé et codirigé le groupe d'IA éthique de Google. Ce groupe est axé sur la recherche fondamentale en matière d'IA éthique et l'opérationnalisation de d'IA éthique en interne à Google. Avant de rejoindre Google, elle a été chercheuse chez Microsoft Research où elle s'est concentrée sur la génération de la vision par ordinateur vers le langage et a été post-doc à Johns Hopkins où elle s'est concentrée sur la modélisation bayésienne et l'extraction d'informations. Elle est titulaire d'un doctorat en informatique de l'université d'Aberdeen et d'une maîtrise en linguistique informatique de l'université de Washington. Tout en obtenant ses diplômes, elle a également travaillé de 2005 à 2012 sur l'apprentissage automatique, les troubles neurologiques et les technologies d'assistance à l'Oregon Health and Science University. Elle a dirigé un certain nombre d'ateliers et d'initiatives au croisement de la diversité, de l'inclusion, de l'informatique et de l'éthique. Ses travaux ont été récompensés par le secrétaire à la défense Ash Carter et la Fondation américaine pour les aveugles, et ont été implémenté par plusieurs entreprises technologiques.
-
-**Matthew Watson et Chen Qian :** *Les flux de travail en NLP avec Keras*
-
-
-
-
-
-
-
-
-
-Matthew Watson est ingénieur en apprentissage automatique au sein de l'équipe Keras et se concentre sur les API de modélisation de haut niveau. Il a étudié l'infographie pendant ses études et a obtenu un master à l'université de Stanford. Il s'est orienté vers l'informatique après avoir étudié l'anglais. Il est passionné par le travail interdisciplinaire et par la volonté de rendre le traitement automatique des langues accessible à un public plus large.
-
-Chen Qian est un ingénieur logiciel de l'équipe Keras spécialisé dans les API de modélisation de haut niveau. Chen est titulaire d'un master en génie électrique de l'université de Stanford et s'intéresse particulièrement à la simplification de l'implémentation du code des tâches d’apprentissage automatique et le passage à grande échelle de ces codes.
-
-
-**Mark Saroufim :** *Comment entraîner un modèle avec PyTorch*
-
-
-
-
-
-
-
-
-
-Mark Saroufim est ingénieur partenaire chez PyTorch et travaille sur les outils de production OSS, notamment TorchServe et PyTorch Enterprise. Dans ses vies antérieures, Mark a été un scientifique appliqué et un chef de produit chez Graphcore, [yuri.ai](http://yuri.ai/), Microsoft et au JPL de la NASA. Sa principale passion est de rendre la programmation plus amusante.
-
-**Jakob Uszkoreit :** *Ce n'est pas cassé alors ne réparez pas cassez tout*
-
-
-
-
-
-
-
-
-
-Jakob Uszkoreit est le cofondateur d'Inceptive. Inceptive conçoit des molécules d'ARN pour les vaccins et les thérapies en utilisant l'apprentissage profond à grande échelle. Le tout en boucle étroite avec des expériences à haut débit, dans le but de rendre les médicaments à base d'ARN plus accessibles, plus efficaces et plus largement applicables. Auparavant, Jakob a travaillé chez Google pendant plus de dix ans, dirigeant des équipes de recherche et de développement au sein de Google Brain, Research et Search, travaillant sur les fondamentaux de l'apprentissage profond, la vision par ordinateur, la compréhension du langage et la traduction automatique.
-
-## Jour 2 : Les outils à utiliser
-
-
-**Lewis Tunstall :** *Un entraînement simple avec la fonction *Trainer* de la bibliotèque 🤗 Transformers*
-
-
-
-
-
-Lewis est un ingénieur en apprentissage machine chez Hugging Face qui se concentre sur le développement d'outils open-source et les rend accessibles à la communauté. Il est également co-auteur d'un livre à paraître chez O'Reilly sur les *transformers*. Vous pouvez le suivre sur Twitter (@_lewtun) pour des conseils et astuces en traitement du langage naturel !
-
-**Matthew Carrigan :** *Nouvelles fonctionnalités en TensorFlow pour 🤗 Transformers et 🤗 Datasets*
-
-
-
-
-
-Matt est responsable de la maintenance des modèles en TensorFlow chez *Transformers*. Il finira par mener un coup d'État contre la faction PyTorch en place Celui sera probablement coordonné via son compte Twitter @carrigmat.
-
-**Lysandre Debut :** *Le Hub d’Hugging Face, un moyen de collaborer et de partager des projets d'apprentissage automatique*
-
-
-
-
-
-
-
-
-
-Lysandre est ingénieur en apprentissage machine chez Hugging Face où il participe à de nombreux projets open source. Son objectif est de rendre l'apprentissage automatique accessible à tous en développant des outils puissants avec une API très simple.
-
-**Lucile Saulnier :** *Avoir son propre tokenizer avec 🤗 Transformers & 🤗 Tokenizers*
-
-
-
-
-
-Lucile est ingénieure en apprentissage automatique chez Hugging Face où elle développe et soutient l'utilisation d'outils open source. Elle est également activement impliquée dans de nombreux projets de recherche dans le domaine du traitement du langage naturel tels que l’entraînement collaboratif et BigScience.
-
-**Sylvain Gugger :** *Optimisez votre boucle d'entraînement PyTorch avec
-🤗 Accelerate*
-
-
-
-
-
-Sylvain est ingénieur de recherche chez Hugging Face. Il est l'un des principaux mainteneurs de 🤗 Transformers et le développeur derrière 🤗 Accelerate. Il aime rendre l'apprentissage des modèles plus accessible.
-
-**Merve Noyan :** *Présentez vos démonstrations de modèles avec
-🤗 Spaces*
-
-
-
-
-
-Merve est *developer advocate* chez Hugging Face travaillant au développement d'outils et à la création de contenu autour d'eux afin de démocratiser l'apprentissage automatique pour tous.
-
-**Abubakar Abid :** *Créer rapidement des applications d'apprentissage automatique*
-
-
-
-
-
-
-
-
-
-Abubakar Abid est le PDG de [Gradio](www.gradio.app). Il a obtenu sa licence en génie électrique et en informatique au MIT en 2015, et son doctorat en apprentissage automatique appliqué à Stanford en 2021. En tant que PDG de Gradio, Abubakar s'efforce de faciliter la démonstration, le débogage et le déploiement des modèles d'apprentissage automatique.
-
-**Mathieu Desvé :** *AWS ML Vision : Rendre l'apprentissage automatique accessible à tous les clients*
-
-
-
-
-
-
-
-
-
-Passionné de technologie, il est un créateur pendant son temps libre. Il aime les défis et résoudre les problèmes des clients et des utilisateurs ainsi que travailler avec des personnes talentueuses pour apprendre chaque jour. Depuis 2004, il a occupé plusieurs postes, passant du frontend au backend, de l'infrastructure aux opérations et à la gestion. Il essaie de résoudre les problèmes techniques et de gestion courants de manière agile.
-
-**Philipp Schmid :** *Entraînement dirigé avec Amazon SageMaker et 🤗 Transformers*
-
-
-
-
-
-Philipp Schmid est ingénieur en apprentissage machine et *Tech Lead* chez Hugging Face où il dirige la collaboration avec l'équipe Amazon SageMaker. Il est passionné par la démocratisation et la mise en production de modèles de traitement du langage naturel de pointe et par l'amélioration de la facilité d'utilisation de l'apprentissage profond.
+# Événement pour le lancement de la partie 2
+
+Pour la sortie de la deuxième partie du cours, nous avons organisé un événement en direct consistant en deux jours de conférences suivies d’un *sprint* de *finetuning*. Si vous l'avez manqué, vous pouvez rattraper les présentations qui sont toutes listées ci-dessous !
+
+## Jour 1 : Une vue d'ensemble des transformers et comment les entraîner
+
+
+**Thomas Wolf :** *L'apprentissage par transfert et la naissance de la bibliothèque 🤗 Transformers*
+
+
+
+
+
+
+
+
+
+Thomas Wolf est cofondateur et directeur scientifique d’Hugging Face. Les outils créés par Thomas Wolf et l'équipe d’Hugging Face sont utilisés par plus de 5 000 organismes de recherche, dont Facebook Artificial Intelligence Research, Google Research, DeepMind, Amazon Research, Apple, l'Allen Institute for Artificial Intelligence ainsi que la plupart des départements universitaires. Thomas Wolf est l'initiateur et le président principal de la plus grande collaboration de recherche qui ait jamais existé dans le domaine de l'intelligence artificielle : [« BigScience »](https://bigscience.huggingface.co), ainsi que d'un ensemble de [bibliothèques et outils](https://github.com/huggingface/) largement utilisés. Thomas Wolf est également un éducateur prolifique, un *leader* d'opinion dans le domaine de l'intelligence artificielle et du traitement du langage naturel, et un orateur régulièrement invité à des conférences dans le monde entier [https://thomwolf.io](https://thomwolf.io).
+
+**Jay Alammar :** *Une introduction visuelle douce aux transformers*
+
+
+
+
+
+
+
+
+
+Grâce à son blog d’apprentissage automatique très populaire, Jay a aidé des millions de chercheurs et d'ingénieurs à comprendre visuellement les outils et les concepts de l'apprentissage automatique, des plus élémentaires (qui se retrouvent dans les docs NumPy et Pandas) aux plus pointus (Transformer, BERT, GPT-3).
+
+**Margaret Mitchell :** *Les valeurs dans le développement de l’apprentissage automatique*
+
+
+
+
+
+
+
+
+
+Margaret Mitchell est une chercheuse travaillant sur l'IA éthique. Elle se concentre actuellement sur les tenants et aboutissants du développement de l'IA éthique dans le domaine de la technologie. Elle a publié plus de cinquante articles sur la génération de langage naturel, les technologies d'assistance, la vision par ordinateur et l'IA éthique. Elle détient plusieurs brevets dans le domaine de la génération de conversations et celui de la classification des sentiments. Elle a précédemment travaillé chez Google AI en tant que chercheuse où elle a fondé et codirigé le groupe d'IA éthique de Google. Ce groupe est axé sur la recherche fondamentale en matière d'IA éthique et l'opérationnalisation de d'IA éthique en interne à Google. Avant de rejoindre Google, elle a été chercheuse chez Microsoft Research où elle s'est concentrée sur la génération de la vision par ordinateur vers le langage et a été post-doc à Johns Hopkins où elle s'est concentrée sur la modélisation bayésienne et l'extraction d'informations. Elle est titulaire d'un doctorat en informatique de l'université d'Aberdeen et d'une maîtrise en linguistique informatique de l'université de Washington. Tout en obtenant ses diplômes, elle a également travaillé de 2005 à 2012 sur l'apprentissage automatique, les troubles neurologiques et les technologies d'assistance à l'Oregon Health and Science University. Elle a dirigé un certain nombre d'ateliers et d'initiatives au croisement de la diversité, de l'inclusion, de l'informatique et de l'éthique. Ses travaux ont été récompensés par le secrétaire à la défense Ash Carter et la Fondation américaine pour les aveugles, et ont été implémenté par plusieurs entreprises technologiques.
+
+**Matthew Watson et Chen Qian :** *Les flux de travail en NLP avec Keras*
+
+
+
+
+
+
+
+
+
+Matthew Watson est ingénieur en apprentissage automatique au sein de l'équipe Keras et se concentre sur les API de modélisation de haut niveau. Il a étudié l'infographie pendant ses études et a obtenu un master à l'université de Stanford. Il s'est orienté vers l'informatique après avoir étudié l'anglais. Il est passionné par le travail interdisciplinaire et par la volonté de rendre le traitement automatique des langues accessible à un public plus large.
+
+Chen Qian est un ingénieur logiciel de l'équipe Keras spécialisé dans les API de modélisation de haut niveau. Chen est titulaire d'un master en génie électrique de l'université de Stanford et s'intéresse particulièrement à la simplification de l'implémentation du code des tâches d’apprentissage automatique et le passage à grande échelle de ces codes.
+
+
+**Mark Saroufim :** *Comment entraîner un modèle avec PyTorch*
+
+
+
+
+
+
+
+
+
+Mark Saroufim est ingénieur partenaire chez PyTorch et travaille sur les outils de production OSS, notamment TorchServe et PyTorch Enterprise. Dans ses vies antérieures, Mark a été un scientifique appliqué et un chef de produit chez Graphcore, [yuri.ai](http://yuri.ai/), Microsoft et au JPL de la NASA. Sa principale passion est de rendre la programmation plus amusante.
+
+**Jakob Uszkoreit :** *Ce n'est pas cassé alors ne réparez pas cassez tout*
+
+
+
+
+
+
+
+
+
+Jakob Uszkoreit est le cofondateur d'Inceptive. Inceptive conçoit des molécules d'ARN pour les vaccins et les thérapies en utilisant l'apprentissage profond à grande échelle. Le tout en boucle étroite avec des expériences à haut débit, dans le but de rendre les médicaments à base d'ARN plus accessibles, plus efficaces et plus largement applicables. Auparavant, Jakob a travaillé chez Google pendant plus de dix ans, dirigeant des équipes de recherche et de développement au sein de Google Brain, Research et Search, travaillant sur les fondamentaux de l'apprentissage profond, la vision par ordinateur, la compréhension du langage et la traduction automatique.
+
+## Jour 2 : Les outils à utiliser
+
+
+**Lewis Tunstall :** *Un entraînement simple avec la fonction *Trainer* de la bibliotèque 🤗 Transformers*
+
+
+
+
+
+Lewis est un ingénieur en apprentissage machine chez Hugging Face qui se concentre sur le développement d'outils open-source et les rend accessibles à la communauté. Il est également co-auteur du livre [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098103231/) paru chez O'Reilly. Vous pouvez le suivre sur Twitter (@_lewtun) pour des conseils et astuces en traitement du langage naturel !
+
+**Matthew Carrigan :** *Nouvelles fonctionnalités en TensorFlow pour 🤗 Transformers et 🤗 Datasets*
+
+
+
+
+
+Matt est responsable de la maintenance des modèles en TensorFlow chez *Transformers*. Il finira par mener un coup d'État contre la faction PyTorch en place Celui sera probablement coordonné via son compte Twitter @carrigmat.
+
+**Lysandre Debut :** *Le Hub d’Hugging Face, un moyen de collaborer et de partager des projets d'apprentissage automatique*
+
+
+
+
+
+
+
+
+
+Lysandre est ingénieur en apprentissage machine chez Hugging Face où il participe à de nombreux projets open source. Son objectif est de rendre l'apprentissage automatique accessible à tous en développant des outils puissants avec une API très simple.
+
+**Lucile Saulnier :** *Avoir son propre tokenizer avec 🤗 Transformers & 🤗 Tokenizers*
+
+
+
+
+
+Lucile est ingénieure en apprentissage automatique chez Hugging Face où elle développe et soutient l'utilisation d'outils open source. Elle est également activement impliquée dans de nombreux projets de recherche dans le domaine du traitement du langage naturel tels que l’entraînement collaboratif et BigScience.
+
+**Sylvain Gugger :** *Optimisez votre boucle d'entraînement PyTorch avec
+🤗 Accelerate*
+
+
+
+
+
+Sylvain est ingénieur de recherche chez Hugging Face. Il est l'un des principaux mainteneurs de 🤗 Transformers et le développeur derrière 🤗 Accelerate. Il aime rendre l'apprentissage des modèles plus accessible.
+
+**Merve Noyan :** *Présentez vos démonstrations de modèles avec
+🤗 Spaces*
+
+
+
+
+
+Merve est *developer advocate* chez Hugging Face travaillant au développement d'outils et à la création de contenu autour d'eux afin de démocratiser l'apprentissage automatique pour tous.
+
+**Abubakar Abid :** *Créer rapidement des applications d'apprentissage automatique*
+
+
+
+
+
+
+
+
+
+Abubakar Abid est le PDG de [Gradio](www.gradio.app). Il a obtenu sa licence en génie électrique et en informatique au MIT en 2015, et son doctorat en apprentissage automatique appliqué à Stanford en 2021. En tant que PDG de Gradio, Abubakar s'efforce de faciliter la démonstration, le débogage et le déploiement des modèles d'apprentissage automatique.
+
+**Mathieu Desvé :** *AWS ML Vision : Rendre l'apprentissage automatique accessible à tous les clients*
+
+
+
+
+
+
+
+
+
+Passionné de technologie, il est un créateur pendant son temps libre. Il aime les défis et résoudre les problèmes des clients et des utilisateurs ainsi que travailler avec des personnes talentueuses pour apprendre chaque jour. Depuis 2004, il a occupé plusieurs postes, passant du frontend au backend, de l'infrastructure aux opérations et à la gestion. Il essaie de résoudre les problèmes techniques et de gestion courants de manière agile.
+
+**Philipp Schmid :** *Entraînement dirigé avec Amazon SageMaker et 🤗 Transformers*
+
+
+
+
+
+Philipp Schmid est ingénieur en apprentissage machine et *Tech Lead* chez Hugging Face où il dirige la collaboration avec l'équipe Amazon SageMaker. Il est passionné par la démocratisation et la mise en production de modèles de traitement du langage naturel de pointe et par l'amélioration de la facilité d'utilisation de l'apprentissage profond.
From ab803f377c7cbd42c2d895448337bd76746f4336 Mon Sep 17 00:00:00 2001
From: Sebastian Sosa <37946988+CakeCrusher@users.noreply.github.com>
Date: Tue, 17 May 2022 00:16:31 -0400
Subject: [PATCH 161/789] Part 7: Training a causal... fixes (#179)
* typo & error mitigation
* consistency
* Trainer.predict() returns 3 fields
* ran make style
---
chapters/en/chapter7/6.mdx | 13 ++++++++++---
chapters/en/chapter7/7.mdx | 2 +-
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/chapters/en/chapter7/6.mdx b/chapters/en/chapter7/6.mdx
index 927c2be9f..27dc8cbbd 100644
--- a/chapters/en/chapter7/6.mdx
+++ b/chapters/en/chapter7/6.mdx
@@ -67,6 +67,11 @@ False True
We can use this to create a function that will stream the dataset and filter the elements we want:
```py
+from collections import defaultdict
+from tqdm import tqdm
+from datasets import Dataset
+
+
def filter_streaming_dataset(dataset, filters):
filtered_dict = defaultdict(list)
total = 0
@@ -105,7 +110,7 @@ Filtering the full dataset can take 2-3h depending on your machine and bandwidth
from datasets import load_dataset, DatasetDict
ds_train = load_dataset("huggingface-course/codeparrot-ds-train", split="train")
-ds_valid = load_dataset("huggingface-course/codeparrot-ds-valid", split="train")
+ds_valid = load_dataset("huggingface-course/codeparrot-ds-valid", split="validation")
raw_datasets = DatasetDict(
{
@@ -347,7 +352,7 @@ data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False, return_ten
Let's have a look at an example:
```py
-out = data_collator([tokenized_dataset["train"][i] for i in range(5)])
+out = data_collator([tokenized_datasets["train"][i] for i in range(5)])
for key in out:
print(f"{key} shape: {out[key].shape}")
```
@@ -799,6 +804,8 @@ model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
Now that we have sent our `train_dataloader` to `accelerator.prepare()`, we can use its length to compute the number of training steps. Remember that we should always do this after preparing the dataloader, as that method will change its length. We use a classic linear schedule from the learning rate to 0:
```py
+from transformers import get_scheduler
+
num_train_epochs = 1
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch
@@ -856,7 +863,7 @@ model.train()
completed_steps = 0
for epoch in range(num_train_epochs):
for step, batch in tqdm(
- enumerate(train_dataloader, start=1), total=len(train_dataloader)
+ enumerate(train_dataloader, start=1), total=num_training_steps
):
logits = model(batch["input_ids"]).logits
loss = keytoken_weighted_loss(batch["input_ids"], logits, keytoken_ids)
diff --git a/chapters/en/chapter7/7.mdx b/chapters/en/chapter7/7.mdx
index 756500fa7..d8e1942e4 100644
--- a/chapters/en/chapter7/7.mdx
+++ b/chapters/en/chapter7/7.mdx
@@ -955,7 +955,7 @@ Note that while the training happens, each time the model is saved (here, every
Once the training is complete, we can finally evaluate our model (and pray we didn't spend all that compute time on nothing). The `predict()` method of the `Trainer` will return a tuple where the first elements will be the predictions of the model (here a pair with the start and end logits). We send this to our `compute_metrics()` function:
```python
-predictions, _ = trainer.predict(validation_dataset)
+predictions, _, _ = trainer.predict(validation_dataset)
start_logits, end_logits = predictions
compute_metrics(start_logits, end_logits, validation_dataset, raw_datasets["validation"])
```
From 6c78c3e502bbd87638a1cd9f0d5418437dd96572 Mon Sep 17 00:00:00 2001
From: tanersekmen <56790802+tanersekmen@users.noreply.github.com>
Date: Tue, 17 May 2022 07:18:12 +0300
Subject: [PATCH 162/789] =?UTF-8?q?[TR]=20Translated=20Chapter=201.6=20?=
=?UTF-8?q?=F0=9F=A4=97=20(#185)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* added chapter 1/6 to _toctree.yml
* [TR] Translated Chapter 1.6 🤗
---
README.md | 1 +
chapters/tr/_toctree.yml | 3 +++
chapters/tr/chapter1/6.mdx | 16 ++++++++++++++++
3 files changed, 20 insertions(+)
create mode 100644 chapters/tr/chapter1/6.mdx
diff --git a/README.md b/README.md
index fe404de8e..d8eea1a28 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ This repo contains the content that's used to create the **[Hugging Face course]
| [Russian](https://huggingface.co/course/ru/chapter1/1) (WIP) | [`chapters/ru`](https://github.com/huggingface/course/tree/main/chapters/ru) | [@pdumin](https://github.com/pdumin), [@svv73](https://github.com/svv73) |
| [Spanish](https://huggingface.co/course/es/chapter1/1) (WIP) | [`chapters/es`](https://github.com/huggingface/course/tree/main/chapters/es) | [@camartinezbu](https://github.com/camartinezbu), [@munozariasjm](https://github.com/munozariasjm), [@fordaz](https://github.com/fordaz) |
| [Thai](https://huggingface.co/course/th/chapter1/1) (WIP) | [`chapters/th`](https://github.com/huggingface/course/tree/main/chapters/th) | [@peeraponw](https://github.com/peeraponw), [@a-krirk](https://github.com/a-krirk), [@jomariya23156](https://github.com/jomariya23156), [@ckingkan](https://github.com/ckingkan) |
+| [Turkish](https://huggingface.co/course/tr/chapter1/1) (WIP) | [`chapters/tr`](https://github.com/huggingface/course/tree/main/chapters/tr) | [@tanersekmen](https://github.com/tanersekmen), [@mertbozkir](https://github.com/mertbozkir), [@ftarlaci](https://github.com/ftarlaci), [@akkasayaz](https://github.com/akkasayaz) |
### Translating the course into your language
diff --git a/chapters/tr/_toctree.yml b/chapters/tr/_toctree.yml
index 5e9cc1073..4b48680b3 100644
--- a/chapters/tr/_toctree.yml
+++ b/chapters/tr/_toctree.yml
@@ -12,6 +12,9 @@
title: Doğal Dil İşleme
- local: chapter1/5
title: Encoder modelleri
+ - local: chapter1/6
+ title: Decoder modelleri
+
- title: 2. 🤗 Transformers Kullanımı
sections:
diff --git a/chapters/tr/chapter1/6.mdx b/chapters/tr/chapter1/6.mdx
new file mode 100644
index 000000000..4599582de
--- /dev/null
+++ b/chapters/tr/chapter1/6.mdx
@@ -0,0 +1,16 @@
+# Decoder modelleri
+
+
+
+Decoder modeller, yalnızca bir Transformer modelinin decoderini kullanır. Her aşamada, attention katmanları sadece cümlede kendisinden önce gelen kelimelere erişebilir. Bu modeller *auto-regressive models* olarak isimlendirilir.
+
+Decoder modellerin ön eğitimi genellikle cümledeki bir sonraki kelimeyi tahmin etme şeklinde görevlendirilir.
+
+Bu modeller, en çok metin oluşturmayı içeren görevler için uygundur.
+
+Bu model ailelerinin temsilcileri şunları kapsar:
+
+- [CTRL](https://huggingface.co/transformers/model_doc/ctrl.html)
+- [GPT](https://huggingface.co/transformers/model_doc/gpt.html)
+- [GPT-2](https://huggingface.co/transformers/model_doc/gpt2.html)
+- [Transformer XL](https://huggingface.co/transformers/model_doc/transformerxl.html)
From 1fe96c860802bd399740eb48cb1e5cfc5ab735b7 Mon Sep 17 00:00:00 2001
From: Victor Costa <54755870+victorescosta@users.noreply.github.com>
Date: Tue, 17 May 2022 01:20:21 -0300
Subject: [PATCH 163/789] [PT][Chapter 01 - 2.mdx] - issue #51 (#170)
---
chapters/pt/_toctree.yml | 2 ++
chapters/pt/chapter1/2.mdx | 21 +++++++++++++++++++++
2 files changed, 23 insertions(+)
create mode 100644 chapters/pt/chapter1/2.mdx
diff --git a/chapters/pt/_toctree.yml b/chapters/pt/_toctree.yml
index 36fcacd23..da5d377ae 100644
--- a/chapters/pt/_toctree.yml
+++ b/chapters/pt/_toctree.yml
@@ -7,6 +7,8 @@
sections:
- local: chapter1/1
title: Introdução
+ - local: chapter1/2
+ title: Processamento de Linguagem Natural
- title: 2. Usando 🤗 Transformers
sections:
diff --git a/chapters/pt/chapter1/2.mdx b/chapters/pt/chapter1/2.mdx
new file mode 100644
index 000000000..a3413a2a8
--- /dev/null
+++ b/chapters/pt/chapter1/2.mdx
@@ -0,0 +1,21 @@
+# Processamento de Linguagem Natural (NLP)
+
+Antes de irmos direto para os modelos Transformers, vamos fazer um rápido esboço sobre o que é processamento de linguagem natural e o porquê nós nos importamos com isso.
+
+## O que é NLP?
+
+NLP é um campo da linguística e da Aprendizagem de Máquina (ML) focada em entender tudo relacionado a linguagem humana. O objetivo das tarefas de NLP não é apenas entender palavras soltas individualmente, mas ser capaz de entender o contexto dessas palavras.
+
+A seguir uma lista de tarefas comuns de NLP, com alguns exemplos:
+
+- **Classificação de sentenças completas**: Capturar o sentimento de uma revisão, detectar se um email é spam, determinar se a sentença é gramaticalmente correta ou onde duas sentenças são logicamente relacionadas ou não
+- **Classificação de cada palavra em uma sentença**: Identificar os componentes gramaticais de uma sentença (substantivo, verbo, adjetivo), ou as entidades nomeadas (pessoa, local, organização)
+- **Geração de conteúdo textual**: Completar um trecho com autogeração textual, preenchendo as lacunas em um texto com palavras mascaradas
+- **Extrair uma resposta de um texto**: Dada uma pergunta e um contexto, extrair a resposta baseada na informação passada no contexto
+- **Gerar uma nova sentença a partir de uma entrada de texto**: Traduzir um texto para outro idioma, resumi-lo
+
+NLP não se limita ao texto escrito. Também engloba desafios complexos nos campos de reconhecimento de discurso e visão computacional, tal como a geração de transcrição de uma amostra de áudio ou a descrição de uma imagem.
+
+## Por que isso é desafiador?
+
+Os computadores não processam a informação da mesma forma que os seres humanos. Por exemplo, quando nós lemos a sentença "Estou com fome", nós podemos facilmente entender seu significado. Similarmente, dada duas sentenças como "Estou com fome" e "Estou triste", nós somos capazes de facilmente determinar quão similares elas são. Para modelos de Aprendizagem de Máquina (ML), tarefas como essas são mais difíceis. O texto precisa ser processado de um modo que possibilite o modelo aprender por ele. E porque a linguagem é complexa, nós precisamos pensar cuidadosamente sobre como esse processamento tem que ser feito. Tem se feito muita pesquisa sobre como representar um texto e nós iremos observar alguns desses métodos no próximo capítulo.
From a1e7c0804d78a176314004984eaf9108f3afcade Mon Sep 17 00:00:00 2001
From: gitkamyah
Date: Tue, 17 May 2022 08:58:49 +0200
Subject: [PATCH 164/789] review changes applied
---
chapters/fa/chapter3/2.mdx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/chapters/fa/chapter3/2.mdx b/chapters/fa/chapter3/2.mdx
index fd134ebe6..564fff30d 100644
--- a/chapters/fa/chapter3/2.mdx
+++ b/chapters/fa/chapter3/2.mdx
@@ -312,7 +312,7 @@ def tokenize_function(example):
این تابع دیکشنری (مثل اقلام داخل دیتاسِت) دریافت میکند و دیکشنری دیگری با کلیدهای `input_ids`، `attention_mask` و `token_type_ids` برمیگرداند. توجه داشته باشید از آنجایی که توکِنایزر روی لیستهایی از دو جملهها کار میکند، همانطور که قبلا مشاهده کردیم، این تابع نیز در صورتی که دیکشنری `example` شامل چندین نمونه (هر کلید به عنوان لیستی از جملهها) باشد کار میکند. این به ما این امکان را خواهد داد که از گزینه `batched=True` در فراخوانی تابع `map()` استفاده کنیم که توکٍنایزر را به میزان زیادی سریعتر خواهد کرد. این توکِنایزر با توکِنایزری در کتابخانه [Tokenizers](https://github.com/huggingface/tokenizers) از هاگینگفِیس که به زبان برنامهنویسی Rust نوشته شده پشتیبانی میشود. این توکٍنایزر میتواند بسیار سریع باشد، اما فقط به شرطی که ورودیهای زیادی را به صورت یکجا به آن بدهیم.
-توجه داشته باشید که ما آرگومان `padding` را در حال حاضر کنار گذاشتهایم. این به این خاطر است که انجام padding روی همه نمونهها برای بیشترین طول به صرفه نیست: بهتر است که padding را زمانی که در حال ساختن بَتچ هستیم روی نمونهها انجام دهیم، چرا که آنوقت فقط نیاز داریم که `padding` را روی همان بَتچ انجام دهیم، و نه بیشترین طول در سرتاسر دیتاسِت. این روش زمانی که ورودیها دارای طولهای بسیار متغیری هستند وقت و انرژی زیادی را صرفهجویی خواهد کرد.
+توجه داشته باشید که ما آرگومان همطولسازی را در حال حاضر کنار گذاشتهایم. این به این خاطر است که همطولسازی روی همه نمونهها برای بیشترین طول به صرفه نیست: بهتر است که آن را زمانی که در حال ساختن بَتچ هستیم روی نمونهها انجام دهیم، چرا که آنوقت فقط نیاز داریم همطولسازی را روی همان بَتچ انجام دهیم، و نه بیشترین طول در سرتاسر دیتاسِت. این روش زمانی که ورودیها دارای طولهای بسیار متغیری هستند وقت و انرژی زیادی را صرفهجویی خواهد کرد.
در اینجا نشان میدهیم چگونه تابع توکِنایزیشن را روی کل دیتاسِت به یکباره اعمال میکنیم. ما از `batched=True` در فراخوانی تابع `map` استفاده میکنیم بنابراین تابع روی چندین عنصر از دیتاسِت ما به یکباره عمل میکند نه روی هر عنصر به صورت جداگانه. این اجازه میدهد که پیشپردازش سریعتر انجام گیرد:
@@ -362,7 +362,7 @@ DatasetDict({
{#if fw === 'pt'}
-تابعی که مسئول کنارهم گذاشتن نمونهها در یک بَتچ میباشد *collate function* خوانده میشود. این تابع آرگومانی است که شما میتوانید هنگام ساختن یک `DataLoader` به داخل آن ارسال کنید، که در حالت پیش فرض تابعی است که نمونههای شما را به ماتریس پایتورچ تبدیل کرده و آنها را بهم الحاق میکند (اگر عناصر شما لیست، تاپل یا دیکشنری باشند این کار به صورت بازگشتی انجام میگیرد). از آنجایی که ورودیهای ما همه به یک اندازه نیستند این کار برای ما امکانپذیر نیست. ما پروسه همطولسازی را عمدا به تعویق انداختیم، تا فقط در زمان نیاز آنرا روی هر بَتچ اجرا کنیم و از داشتن ورودیهای بیش از اندازه طولانی با تعداد زیادی همطولسازی پیشگیری کنیم. این روش پروسه تعلیم را تا اندازهای سرعت میبخشد، اما توجه داشته باشید که اگر شما در حال تعلیم روی TPU هستید میتواند مشکل ساز باشد - TPUها اشکال معین را ترجیح میدهند، حتی اگر نیاز به همطولسازی اضافه داشته باشد.
+تابعی که مسئول کنار هم گذاشتن نمونهها در یک بَتچ میباشد *collate function* خوانده میشود. این تابع آرگومانی است که شما میتوانید هنگام ساختن یک `DataLoader` به داخل آن ارسال کنید، که در حالت پیش فرض تابعی است که نمونههای شما را به ماتریس پایتورچ تبدیل کرده و آنها را بهم الحاق میکند (اگر عناصر شما لیست، تاپل یا دیکشنری باشند این کار به صورت بازگشتی انجام میگیرد). از آنجایی که ورودیهای ما همه به یک اندازه نیستند این کار برای ما امکانپذیر نیست. ما پروسه همطولسازی را عمدا به تعویق انداختیم، تا فقط در زمان نیاز آنرا روی هر بَتچ اجرا کنیم و از داشتن ورودیهای بیش از اندازه طولانی با تعداد زیادی همطولسازی پیشگیری کنیم. این روش پروسه تعلیم را تا اندازهای سرعت میبخشد، اما توجه داشته باشید که اگر شما در حال تعلیم روی TPU هستید میتواند مشکل ساز باشد - TPUها اشکال معین را ترجیح میدهند، حتی اگر نیاز به همطولسازی اضافه داشته باشد.
{:else}
@@ -370,7 +370,7 @@ DatasetDict({
{/if}
-برای انجام این کار در عمل، ما باید یک تابع `collate` تعریف کنیم که میزان درستی از همطولسازی را به آیتمهای دیتاسِتهایی که ما میخواهیم باهم بَتچ کنیم اعمال کند. خوشبختانه، کتابخانه ترنسفورمرهای هاگینگفِیس چنین تابعی را تحت عنوان `DataCollatorWithPadding` در اختیار ما قرار میدهند. این تابع به محض اینکه آنرا تعریف کنیم (یعنی تعیین کنیم چه توکنی برای همطولسازی استفاده کند و اینکه مدل انتظار همطولسازی از سمت چپ ورودیها را داشته باشد یا از سمت راست آنها) یک توکنایزر را برداشته و هر کاری را که لازم دارید انجام میدهد:
+برای انجام این کار در عمل، ما باید یک تابع `collate` تعریف کنیم که میزان درستی از همطولسازی را به آیتمهای دیتاسِتهایی که ما میخواهیم باهم بَتچ کنیم اعمال کند. خوشبختانه، کتابخانه ترنسفورمرهای هاگینگفِیس چنین تابعی را تحت عنوان `DataCollatorWithPadding` در اختیار ما قرار میدهد. این تابع به محض اینکه آنرا تعریف کنیم (یعنی تعیین کنیم چه توکِنی برای همطولسازی استفاده کند و اینکه مدل انتظار همطولسازی از سمت چپ ورودیها را داشته باشد یا از سمت راست آنها) یک توکِنایزر را برداشته و هر کاری را که لازم دارید انجام میدهد:
{#if fw === 'pt'}
@@ -467,7 +467,7 @@ batch = data_collator(samples)
{#if fw === 'tf'}
-توجه داشته باشید که ما دیتاسِتمان و یک `collator` داده در اختیار داریم، حال نیاز داریم که آنها را کنار هم قرار دهیم. ما میتوانستیم بَتچها را دستی لود کرده و آنها را `collate` کنیم، اما این روش کار زیادی میبرد و احتمالا خیلی هم بهینه نخواهد بود. در عوض، یک روش ساده وجود دارد که راه حل بهینهای برای این مسئله ارائه میکند: `to_tf_dataset()`. این روش یک `tf.data.Dataset` را دور دیتاسِتتان میپیچد، با یک تابع `collation` دلخواه. `tf.data.Dataset` یک فرمت بومی تِنسورفلو است که کِراس میتواند برای `model.fit()` استفاده کند، در تنیجه همین یک تابع میتواند یک دیتاسِت هاگینگفِیس را به سرعت به فرمت آماده برای تعلیم تبدیل کند. اجازه دهید آنرا در عمل با دیتاسِتمان مشاهده کنیم!
+اکنون که دیتاسِتمان و یک `collator` داده در اختیار داریم، حال نیاز داریم که آنها را باهم بکار ببریم. ما میتوانستیم بَتچها را دستی لود کرده و آنها را `collate` کنیم، اما این روش کار زیادی میبرد و احتمالا خیلی هم بهینه نخواهد بود. در عوض، تابعی ساده وجود دارد که راه حل بهینهای برای این مسئله ارائه میکند: `to_tf_dataset()`. این تابع یک `tf.data.Dataset` شامل پارامتری اختیاری برای تابع `collation` را دور دیتاسِتتان میپیچد. `tf.data.Dataset` یک فرمت بومی تِنسورفلو است که کِراس میتواند برای `model.fit()` استفاده کند، در تنیجه همین یک تابع میتواند یک دیتاسِت هاگینگفِیس را به سرعت به فرمت آماده برای تعلیم تبدیل کند. اجازه دهید آنرا در عمل با دیتاسِتمان مشاهده کنیم!
From e9d680915fb35607f8e2fa951354badfcfe595cc Mon Sep 17 00:00:00 2001
From: gitkamyah
Date: Wed, 18 May 2022 08:21:35 +0200
Subject: [PATCH 165/789] review coments applied
---
chapters/fa/chapter3/2.mdx | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/chapters/fa/chapter3/2.mdx b/chapters/fa/chapter3/2.mdx
index 564fff30d..b0746be86 100644
--- a/chapters/fa/chapter3/2.mdx
+++ b/chapters/fa/chapter3/2.mdx
@@ -312,9 +312,9 @@ def tokenize_function(example):
این تابع دیکشنری (مثل اقلام داخل دیتاسِت) دریافت میکند و دیکشنری دیگری با کلیدهای `input_ids`، `attention_mask` و `token_type_ids` برمیگرداند. توجه داشته باشید از آنجایی که توکِنایزر روی لیستهایی از دو جملهها کار میکند، همانطور که قبلا مشاهده کردیم، این تابع نیز در صورتی که دیکشنری `example` شامل چندین نمونه (هر کلید به عنوان لیستی از جملهها) باشد کار میکند. این به ما این امکان را خواهد داد که از گزینه `batched=True` در فراخوانی تابع `map()` استفاده کنیم که توکٍنایزر را به میزان زیادی سریعتر خواهد کرد. این توکِنایزر با توکِنایزری در کتابخانه [Tokenizers](https://github.com/huggingface/tokenizers) از هاگینگفِیس که به زبان برنامهنویسی Rust نوشته شده پشتیبانی میشود. این توکٍنایزر میتواند بسیار سریع باشد، اما فقط به شرطی که ورودیهای زیادی را به صورت یکجا به آن بدهیم.
-توجه داشته باشید که ما آرگومان همطولسازی را در حال حاضر کنار گذاشتهایم. این به این خاطر است که همطولسازی روی همه نمونهها برای بیشترین طول به صرفه نیست: بهتر است که آن را زمانی که در حال ساختن بَتچ هستیم روی نمونهها انجام دهیم، چرا که آنوقت فقط نیاز داریم همطولسازی را روی همان بَتچ انجام دهیم، و نه بیشترین طول در سرتاسر دیتاسِت. این روش زمانی که ورودیها دارای طولهای بسیار متغیری هستند وقت و انرژی زیادی را صرفهجویی خواهد کرد.
+توجه داشته باشید که ما آرگومان همطولسازی را در تابع توکِن کنندهمان نادیده گرفتهایم. این به این خاطر است که همطولسازی روی همه نمونهها برای بیشترین طول به صرفه نیست: بهتر است که نمونهها را زمانی که در حال ساختن بَتچ هستیم همطول کنیم، در این صورت فقط نیاز داریم نمونهها را به اندازه بزرگترین طول همان بَتچ و نه بیشترین طول در سرتاسر دیتاسِت همطول کنیم. این روش زمانی که ورودیها دارای طولهای بسیار متغیری هستند وقت و انرژی زیادی را صرفهجویی خواهد کرد.
-در اینجا نشان میدهیم چگونه تابع توکِنایزیشن را روی کل دیتاسِت به یکباره اعمال میکنیم. ما از `batched=True` در فراخوانی تابع `map` استفاده میکنیم بنابراین تابع روی چندین عنصر از دیتاسِت ما به یکباره عمل میکند نه روی هر عنصر به صورت جداگانه. این اجازه میدهد که پیشپردازش سریعتر انجام گیرد:
+در اینجا نشان میدهیم چگونه تابع تولید توکِن را روی کل دیتاسِت به یکباره اعمال میکنیم. ما از `batched=True` در فراخوانی تابع `map` استفاده میکنیم بنابراین تابع روی چندین عنصر از دیتاسِت ما به یکباره عمل میکند نه روی هر عنصر به صورت جداگانه. این اجازه میدهد که پیشپردازش سریعتر انجام گیرد:
@@ -353,7 +353,7 @@ DatasetDict({
تابع `tokenize_function` ما یک دیکشنری شامل کلیدهای `input_ids`، `attention_mask` و `token_type_ids`، برمیگرداند به گونهای که این سه فیلد به همه بخشهای دیتاسِت افزوده گردند. توجه داشته باشید که اگر تابع پیشپردازش ما برای یک کلید موجود در دیتاسِت که تابع
`map()` به آن اعمال شده یک مقدار جدید بازمیگردانید همچنین میتوانستیم فیلدهای موجود را تغییر دهیم.
-آخرین چیزی که نیاز داریم انجام دهیم این است که هنگامی که عناصر را باهم بَتچ میکنیم، همه نمونهها را به اندازه طول بلندترین عنصر همطول کنیم - تکنیکی که ما به آن *همطولسازی پویا* میگوییم.
+آخرین چیزی که نیاز داریم انجام دهیم این است که هنگامی که عناصر را با هم در یک بَتچ قرار میدهیم، همه نمونهها را به اندازه طول بلندترین عنصر همطول کنیم - تکنیکی که ما به آن *همطولسازی پویا* میگوییم.
### همطولسازی پویا
@@ -366,11 +366,11 @@ DatasetDict({
{:else}
-تابعی که مسئول کنارهم گذاشتن نمونهها در یک بَتچ میباشد *collate function* خوانده میشود. `collator` پیشفرض تابعی است که فقط نمونههای شما را به `tf.Tensor` تبدیل کرده و آنها را بهم الحاق میکند (اگر عناصر شما لیست، تاپل یا دیکشنری باشند این کار به صورت بازگشتی انجام میگیرد). از آنجایی که ورودیها ما همه به یک اندازه نیستند این کار برای ما امکان پذیر نیست. ما پروسه همطولسازی را عمدا به تعویق انداختیم، تا فقط در زمان نیاز آنرا روی هر بَتچ اجرا کنیم و از داشتن ورودیهای بیش از اندازه طولانی با تعداد زیادی همطولسازی پیشگیری کنیم. این روش پروسه تعلیم را تا اندازهای سرعت میبخشد، اما توجه داشته باشید که اگر شما در حال تعلیم روی TPU هستید میتواند مشکل ساز باشد - TPUها اشکال معین را ترجیح میدهند، حتی اگر نیاز به همطولسازی اضافه داشته باشد.
+تابعی که مسئول کنار هم گذاشتن نمونهها در یک بَتچ میباشد *collate function* خوانده میشود. `collator` پیشفرض تابعی است که فقط نمونههای شما را به `tf.Tensor` تبدیل کرده و آنها را بهم الحاق میکند (اگر عناصر شما لیست، تاپل یا دیکشنری باشند این کار به صورت بازگشتی انجام میگیرد). از آنجایی که ورودیهای ما همه به یک اندازه نیستند این کار برای ما امکان پذیر نیست. ما فرایند همطولسازی را عمدا به تعویق انداختیم، تا فقط در زمان نیاز آن را روی هر بَتچ اجرا کنیم و از داشتن ورودیهای بیش از اندازه طولانی با تعداد زیادی همطولسازی پیشگیری کنیم. این روش، فرایند تعلیم را تا اندازهای سرعت میبخشد، اما توجه داشته باشید که اگر شما در حال تعلیم روی TPU هستید این کار میتواند مشکل ساز باشد چرا که TPU اشکال معین را ترجیح میدهد، حتی اگر نیاز به همطولسازی اضافه داشته باشد.
{/if}
-برای انجام این کار در عمل، ما باید یک تابع `collate` تعریف کنیم که میزان درستی از همطولسازی را به آیتمهای دیتاسِتهایی که ما میخواهیم باهم بَتچ کنیم اعمال کند. خوشبختانه، کتابخانه ترنسفورمرهای هاگینگفِیس چنین تابعی را تحت عنوان `DataCollatorWithPadding` در اختیار ما قرار میدهد. این تابع به محض اینکه آنرا تعریف کنیم (یعنی تعیین کنیم چه توکِنی برای همطولسازی استفاده کند و اینکه مدل انتظار همطولسازی از سمت چپ ورودیها را داشته باشد یا از سمت راست آنها) یک توکِنایزر را برداشته و هر کاری را که لازم دارید انجام میدهد:
+برای انجام این کار در عمل، ما باید یک تابع مرتب کننده تعریف کنیم که میزان درستی از همطولسازی را به آیتمهای دیتاسِتهایی که ما میخواهیم باهم در یک بَتچ قرار دهیم اعمال کند. خوشبختانه، کتابخانه ترنسفورمرهای هاگینگفِیس چنین تابعی را تحت عنوان `DataCollatorWithPadding` در اختیار ما قرار میدهد. این تابع به محض این که آن را تعریف کنیم (یعنی تعیین کنیم چه توکِنی برای همطولسازی استفاده کند و مدل انتظار همطولسازی از سمت چپ یا راست ورودیها را داشته باشد) یک توکِنایزر را برداشته و هر کاری را که لازم دارید انجام میدهد:
{#if fw === 'pt'}
@@ -398,7 +398,7 @@ data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf"
{/if}
-اجازه دهید چند نمونه از مجموعه `training`مان را که میخواهیم باهم بَتچ کنیم برداریم تا این ابزار جدید را امتحان کنیم. در اینجا ستونهای `idx`، `sentence1` و `sentence2` را حذف میکنیم چرا که احتیاج نخواهند شد و شامل رشتههای متنی میشوند (که ما نمیتوانیم تنسورهایی از رشتههای متنی ایجاد کنیم) و سپس نگاهی میاندازیم به طول هر ورودی در هر بَتچ:
+اجازه دهید چند نمونه از مجموعه `training` را که میخواهیم باهم در یک بَتچ قرار دهیم برداریم تا این ابزار جدید را امتحان کنیم. در اینجا ستونهای `idx`، `sentence1` و `sentence2` را حذف میکنیم چرا که احتیاج نخواهند شد و شامل رشتههای متنی میشوند (که ما نمیتوانیم تنسورهایی از رشتههای متنی ایجاد کنیم) و سپس نگاهی میاندازیم به طول هر ورودی در هر بَتچ:
From d19424aeb4f69c2acedf74f34bd8061185c7812a Mon Sep 17 00:00:00 2001
From: lewtun
Date: Wed, 18 May 2022 12:18:28 +0200
Subject: [PATCH 166/789] Fix Gradio ToC (#193)
---
chapters/en/_toctree.yml | 3 +--
chapters/fr/_toctree.yml | 15 +++++++--------
2 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/chapters/en/_toctree.yml b/chapters/en/_toctree.yml
index 18ec59d4d..f8c2fe4c4 100644
--- a/chapters/en/_toctree.yml
+++ b/chapters/en/_toctree.yml
@@ -167,8 +167,7 @@
title: End-of-chapter quiz
quiz: 8
-- local: chapter9
- title: 9. Building and sharing demos
+- title: 9. Building and sharing demos
new: true
subtitle: I trained a model, but how can I show it off?
sections:
diff --git a/chapters/fr/_toctree.yml b/chapters/fr/_toctree.yml
index 8f4b86150..c839d61bc 100644
--- a/chapters/fr/_toctree.yml
+++ b/chapters/fr/_toctree.yml
@@ -2,7 +2,7 @@
sections:
- local: chapter0/1
title: Introduction
-
+
- title: 1. Les transformers
sections:
- local: chapter1/1
@@ -26,7 +26,7 @@
- local: chapter1/10
title: Quiz de fin de chapitre
quiz: 1
-
+
- title: 2. Utilisation de 🤗 Transformers
sections:
- local: chapter2/1
@@ -46,11 +46,11 @@
- local: chapter2/8
title: Quiz de fin de chapitre
quiz: 2
-
+
- title: 3. Finetuner un modèle pré-entraîné
sections:
- local: chapter3/1
- title: Introduction
+ title: Introduction
- local: chapter3/2
title: Traîter les données
- local: chapter3/3
@@ -79,7 +79,7 @@
- local: chapter4/6
title: Quiz de fin de chapitre
quiz: 4
-
+
- title: 5. La bibliothèque 🤗 Datasets
sections:
- local: chapter5/1
@@ -167,8 +167,7 @@
title: Quiz de fin de chapitre
quiz: 8
-- local: chapter9
- title: 9. Construire et partager des démos
+- title: 9. Construire et partager des démos
new: true
subtitle: J'ai entraîné un modèle, mais comment puis-je le montrer ?
sections:
@@ -189,7 +188,7 @@
- local: chapter9/8
title: Quiz de fin de chapitre
quiz: 9
-
+
- title: Evènements liés au cours d'Hugging Face
sections:
- local: event/1
From b9548e97d45a3e84e4063c4db58840729b0b3c72 Mon Sep 17 00:00:00 2001
From: lewtun