diff --git a/README.md b/README.md
index 9f46e8eef..49654517c 100644
--- a/README.md
+++ b/README.md
@@ -4,20 +4,27 @@ This repo contains the content that's used to create the **[Hugging Face course]
## 🌎 Languages and translations
-| Language | Source | Authors |
-|:-------------------------------------------------------|:-----------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [English](https://huggingface.co/course/en/chapter1/1) | [`chapters/en`](https://github.com/huggingface/course/tree/main/chapters/en) | [@sgugger](https://github.com/sgugger), [@lewtun](https://github.com/lewtun), [@LysandreJik](https://github.com/LysandreJik), [@Rocketknight1](https://github.com/Rocketknight1), [@sashavor](https://github.com/sashavor), [@osanseviero](https://github.com/osanseviero), [@SaulLu](https://github.com/SaulLu), [@lvwerra](https://github.com/lvwerra) |
-| [Chinese (simplified)](https://huggingface.co/course/zh/chapter1/1) (WIP) | [`chapters/zh`](https://github.com/huggingface/course/tree/main/chapters/zh) | [@zhlhyx](https://github.com/zhlhyx), [petrichor1122](https://github.com/petrichor1122), [@1375626371](https://github.com/1375626371) |
-| [French](https://huggingface.co/course/fr/chapter1/1) (WIP) | [`chapters/fr`](https://github.com/huggingface/course/tree/main/chapters/fr) | [@lbourdois](https://github.com/lbourdois), [@ChainYo](https://github.com/ChainYo), [@melaniedrevet](https://github.com/melaniedrevet), [@abdouaziz](https://github.com/abdouaziz) |
-| [Gujarati](https://huggingface.co/course/gu/chapter1/1) (WIP) | [`chapters/gu`](https://github.com/huggingface/course/tree/main/chapters/gu) | [@pandyaved98](https://github.com/pandyaved98) |
-| [Hindi](https://huggingface.co/course/hi/chapter1/1) (WIP) | [`chapters/hi`](https://github.com/huggingface/course/tree/main/chapters/hi) | [@pandyaved98](https://github.com/pandyaved98) |
-| [Korean](https://huggingface.co/course/ko/chapter1/1) (WIP) | [`chapters/ko`](https://github.com/huggingface/course/tree/main/chapters/ko) | [@Doohae](https://github.com/Doohae) |
-| [Persian](https://huggingface.co/course/fa/chapter1/1) (WIP) | [`chapters/fa`](https://github.com/huggingface/course/tree/main/chapters/fa) | [@jowharshamshiri](https://github.com/jowharshamshiri), [@schoobani](https://github.com/schoobani) |
-| [Portuguese](https://huggingface.co/course/pt/chapter1/1) (WIP) | [`chapters/pt`](https://github.com/huggingface/course/tree/main/chapters/pt) | [@johnnv1](https://github.com/johnnv1), [@victorescosta](https://github.com/victorescosta), [@LincolnVS](https://github.com/LincolnVS) |
-| [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) |
+| Language | Source | Authors |
+|:------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [English](https://huggingface.co/course/en/chapter1/1) | [`chapters/en`](https://github.com/huggingface/course/tree/main/chapters/en) | [@sgugger](https://github.com/sgugger), [@lewtun](https://github.com/lewtun), [@LysandreJik](https://github.com/LysandreJik), [@Rocketknight1](https://github.com/Rocketknight1), [@sashavor](https://github.com/sashavor), [@osanseviero](https://github.com/osanseviero), [@SaulLu](https://github.com/SaulLu), [@lvwerra](https://github.com/lvwerra) |
+| [Bengali](https://huggingface.co/course/bn/chapter1/1) (WIP) | [`chapters/bn`](https://github.com/huggingface/course/tree/main/chapters/bn) | [@avishek-018](https://github.com/avishek-018), [@eNipu](https://github.com/eNipu) |
+| [German](https://huggingface.co/course/de/chapter1/1) (WIP) | [`chapters/de`](https://github.com/huggingface/course/tree/main/chapters/de) | [@JesperDramsch](https://github.com/JesperDramsch), [@MarcusFra](https://github.com/MarcusFra) |
+| [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) |
+| [Persian](https://huggingface.co/course/fa/chapter1/1) (WIP) | [`chapters/fa`](https://github.com/huggingface/course/tree/main/chapters/fa) | [@jowharshamshiri](https://github.com/jowharshamshiri), [@schoobani](https://github.com/schoobani) |
+| [French](https://huggingface.co/course/fr/chapter1/1) | [`chapters/fr`](https://github.com/huggingface/course/tree/main/chapters/fr) | [@lbourdois](https://github.com/lbourdois), [@ChainYo](https://github.com/ChainYo), [@melaniedrevet](https://github.com/melaniedrevet), [@abdouaziz](https://github.com/abdouaziz) |
+| [Gujarati](https://huggingface.co/course/gu/chapter1/1) (WIP) | [`chapters/gu`](https://github.com/huggingface/course/tree/main/chapters/gu) | [@pandyaved98](https://github.com/pandyaved98) |
+| [Hebrew](https://huggingface.co/course/he/chapter1/1) (WIP) | [`chapters/he`](https://github.com/huggingface/course/tree/main/chapters/he) | [@omer-dor](https://github.com/omer-dor) |
+| [Hindi](https://huggingface.co/course/hi/chapter1/1) (WIP) | [`chapters/hi`](https://github.com/huggingface/course/tree/main/chapters/hi) | [@pandyaved98](https://github.com/pandyaved98) |
+| [Italian](https://huggingface.co/course/it/chapter1/1) (WIP) | [`chapters/it`](https://github.com/huggingface/course/tree/main/chapters/it) | [@CaterinaBi](https://github.com/CaterinaBi), [@ClonedOne](https://github.com/ClonedOne), [@Nolanogenn](https://github.com/Nolanogenn), [@EdAbati](https://github.com/EdAbati), [@gdacciaro](https://github.com/gdacciaro) |
+| [Japanese](https://huggingface.co/course/ja/chapter1/1) (WIP) | [`chapters/ja`](https://github.com/huggingface/course/tree/main/chapters/ja) | [@hiromu166](https://github.com/@hiromu166), [@younesbelkada](https://github.com/@younesbelkada), [@HiromuHota](https://github.com/@HiromuHota) |
+| [Korean](https://huggingface.co/course/ko/chapter1/1) (WIP) | [`chapters/ko`](https://github.com/huggingface/course/tree/main/chapters/ko) | [@Doohae](https://github.com/Doohae) |
+| [Portuguese](https://huggingface.co/course/pt/chapter1/1) (WIP) | [`chapters/pt`](https://github.com/huggingface/course/tree/main/chapters/pt) | [@johnnv1](https://github.com/johnnv1), [@victorescosta](https://github.com/victorescosta), [@LincolnVS](https://github.com/LincolnVS) |
+| [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) |
+| [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) |
+| [Vietnamese](https://huggingface.co/course/vi/chapter1/1) (WIP) | [`chapters/vi`](https://github.com/huggingface/course/tree/main/chapters/vi) | [@honghanhh](https://github.com/honghanhh) |
+| [Chinese (simplified)](https://huggingface.co/course/zh-CN/chapter1/1) (WIP) | [`chapters/zh-CN`](https://github.com/huggingface/course/tree/main/chapters/zh-CN) | [@zhlhyx](https://github.com/zhlhyx), [petrichor1122](https://github.com/petrichor1122), [@1375626371](https://github.com/1375626371) |
+| [Chinese (traditional)](https://huggingface.co/course/zh-TW/chapter1/1) (WIP) | [`chapters/zh-TW`](https://github.com/huggingface/course/tree/main/chapters/zh-TW) | [@davidpeng86](https://github.com/davidpeng86) |
### Translating the course into your language
diff --git a/chapters/bn/chapter1/1.mdx b/chapters/bn/chapter1/1.mdx
index 339d066d5..c40753b53 100644
--- a/chapters/bn/chapter1/1.mdx
+++ b/chapters/bn/chapter1/1.mdx
@@ -1,5 +1,10 @@
# ভূমিকা
+
+
## 🤗 কোর্সে স্বাগতম!
diff --git a/chapters/bn/chapter2/1.mdx b/chapters/bn/chapter2/1.mdx
index 3fbb16aa2..358c1f474 100644
--- a/chapters/bn/chapter2/1.mdx
+++ b/chapters/bn/chapter2/1.mdx
@@ -1,5 +1,10 @@
# ভূমিকা
+
+
[অধ্যায় ১](/course/bn/chapter1) এ আমরা দেখে এসেছি যে Transformer মডেলগুলো সাধারণত অনেক বড় হয়। লাখ-লাখ কোটি-কোটি প্যারামিটার সম্বলিত এই মডেল গুলো কে ট্রেনিং এবং ডেপ্লয় করা বেশ জটিল ও কষ্টসাধ্য একটা কাজ। তাছাড়াও প্রায় প্রতিদিনই নতুন নতুন মডেল রিলিজ হচ্ছে এবং সবগুলোরই নিজস্ব বাস্তবায়ন রয়েছে। এই সবকিছু একসাথে এপ্লাই করা খুব সহজ একটা কাজ নয়।
এই 🤗 Transformers লাইব্রেরিটা বানানো হয়েছে এই সমস্যাগুলো সমাধান করার জন্য। এর আসল উদ্দেশ্য হলো এমন একটি API প্রদান করা যার মাধ্যমে যেকোনো Transformer মডেলকে লোড করা, ট্রেইন করা কিংবা সেভ করা যাবে। লাইব্রেরিটির আসল ফিচারগুলো হলঃ
diff --git a/chapters/de/chapter3/1.mdx b/chapters/de/chapter3/1.mdx
index 0f1c7041c..7d275b917 100644
--- a/chapters/de/chapter3/1.mdx
+++ b/chapters/de/chapter3/1.mdx
@@ -2,6 +2,11 @@
# Einführung
+
+
In [Kapitel 2](/course/chapter2) haben wir behandelt, wie man Tokenizer und vortrainierte Modelle verwendet, um Vorhersagen zu treffen. Was passiert aber, wenn wir ein vortrainiertes Modell für unseren eigenen Datensatz optimieren möchten? Das ist das Thema dieses Kapitels! Folgendes wirst du lernen:
{#if fw === 'pt'}
diff --git a/chapters/de/chapter3/2.mdx b/chapters/de/chapter3/2.mdx
index 21c6df41a..9248e9b0f 100644
--- a/chapters/de/chapter3/2.mdx
+++ b/chapters/de/chapter3/2.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/de/chapter3/3.mdx b/chapters/de/chapter3/3.mdx
index ee38b9c67..466c25d29 100644
--- a/chapters/de/chapter3/3.mdx
+++ b/chapters/de/chapter3/3.mdx
@@ -2,9 +2,9 @@
# Fine-tuning eine Modells mit der Trainer API
-
diff --git a/chapters/de/chapter3/3_tf.mdx b/chapters/de/chapter3/3_tf.mdx
index 6290506eb..686d8bb71 100644
--- a/chapters/de/chapter3/3_tf.mdx
+++ b/chapters/de/chapter3/3_tf.mdx
@@ -2,9 +2,9 @@
# Modell mit Keras fein-tunen
-
diff --git a/chapters/de/chapter3/4.mdx b/chapters/de/chapter3/4.mdx
index c940e4030..2eaca08e8 100644
--- a/chapters/de/chapter3/4.mdx
+++ b/chapters/de/chapter3/4.mdx
@@ -1,8 +1,8 @@
# Komplettes Training
-
diff --git a/chapters/de/chapter3/5.mdx b/chapters/de/chapter3/5.mdx
index 944442f6a..a017a1189 100644
--- a/chapters/de/chapter3/5.mdx
+++ b/chapters/de/chapter3/5.mdx
@@ -2,6 +2,11 @@
# Fein-tunen, Check!
+
+
Das hat Spaß gemacht! In den ersten beiden Kapiteln hast du etwas über Modelle und Tokenizer gelernt, und jetzt weißt du, wie du sie auf deine eigenen Daten fein-tunen kannst. Rekapitulieren wir, was du in diesem Kapitel gelernt hast:
{#if fw === 'pt'}
diff --git a/chapters/de/chapter3/6.mdx b/chapters/de/chapter3/6.mdx
index c031089f0..cd82492e7 100644
--- a/chapters/de/chapter3/6.mdx
+++ b/chapters/de/chapter3/6.mdx
@@ -4,6 +4,11 @@
# Quiz am Ende des Kapitels
+
+
Teste, was du in diesem Kapitel gelernt hast!
### 1. Der Datensatz `emotion` enthält Twitter-Nachrichten, die mit Emotionen gelabelt sind. Suche im [Hub](https://huggingface.co/datasets) nach dem Datensatz und lies die Datensatzkarte. Welche der folgenden Emotionen gehört nicht zu den grundlegenden Emotionen?
diff --git a/chapters/en/chapter1/1.mdx b/chapters/en/chapter1/1.mdx
index cd1851129..b5e8bd360 100644
--- a/chapters/en/chapter1/1.mdx
+++ b/chapters/en/chapter1/1.mdx
@@ -1,5 +1,10 @@
# Introduction
+
+
## Welcome to the 🤗 Course!
diff --git a/chapters/en/chapter1/10.mdx b/chapters/en/chapter1/10.mdx
index 355cade7f..7c1b22080 100644
--- a/chapters/en/chapter1/10.mdx
+++ b/chapters/en/chapter1/10.mdx
@@ -2,6 +2,11 @@
# End-of-chapter quiz
+
+
This chapter covered a lot of ground! Don't worry if you didn't grasp all the details; the next chapters will help you understand how things work under the hood.
First, though, let's test what you learned in this chapter!
diff --git a/chapters/en/chapter1/2.mdx b/chapters/en/chapter1/2.mdx
index 4e4aecc1a..548e65d63 100644
--- a/chapters/en/chapter1/2.mdx
+++ b/chapters/en/chapter1/2.mdx
@@ -1,5 +1,10 @@
# Natural Language Processing
+
+
Before jumping into Transformer models, let's do a quick overview of what natural language processing is and why we care about it.
## What is NLP?
diff --git a/chapters/en/chapter1/3.mdx b/chapters/en/chapter1/3.mdx
index ac22e7e8f..d25513cd4 100644
--- a/chapters/en/chapter1/3.mdx
+++ b/chapters/en/chapter1/3.mdx
@@ -1,8 +1,8 @@
# Transformers, what can they do?
-
diff --git a/chapters/en/chapter1/4.mdx b/chapters/en/chapter1/4.mdx
index 6d286a42e..6792a8a57 100644
--- a/chapters/en/chapter1/4.mdx
+++ b/chapters/en/chapter1/4.mdx
@@ -1,5 +1,10 @@
# How do Transformers work?
+
+
In this section, we will take a high-level look at the architecture of Transformer models.
## A bit of Transformer history
diff --git a/chapters/en/chapter1/5.mdx b/chapters/en/chapter1/5.mdx
index 1c707033b..59c9d3a5a 100644
--- a/chapters/en/chapter1/5.mdx
+++ b/chapters/en/chapter1/5.mdx
@@ -1,5 +1,10 @@
# Encoder models
+
+
Encoder models use only the encoder of a Transformer model. At each stage, the attention layers can access all the words in the initial sentence. These models are often characterized as having "bi-directional" attention, and are often called *auto-encoding models*.
diff --git a/chapters/en/chapter1/6.mdx b/chapters/en/chapter1/6.mdx
index d86cea9e5..2ec974012 100644
--- a/chapters/en/chapter1/6.mdx
+++ b/chapters/en/chapter1/6.mdx
@@ -1,5 +1,10 @@
# Decoder models
+
+
Decoder models use only the decoder of a Transformer model. At each stage, for a given word the attention layers can only access the words positioned before it in the sentence. These models are often called *auto-regressive models*.
diff --git a/chapters/en/chapter1/7.mdx b/chapters/en/chapter1/7.mdx
index 3639c2a81..0474bcf76 100644
--- a/chapters/en/chapter1/7.mdx
+++ b/chapters/en/chapter1/7.mdx
@@ -1,5 +1,10 @@
# Sequence-to-sequence models
+
+
Encoder-decoder models (also called *sequence-to-sequence models*) use both parts of the Transformer architecture. At each stage, the attention layers of the encoder can access all the words in the initial sentence, whereas the attention layers of the decoder can only access the words positioned before a given word in the input.
diff --git a/chapters/en/chapter1/8.mdx b/chapters/en/chapter1/8.mdx
index 90c80665d..08135867b 100644
--- a/chapters/en/chapter1/8.mdx
+++ b/chapters/en/chapter1/8.mdx
@@ -1,8 +1,8 @@
# Bias and limitations
-
diff --git a/chapters/en/chapter1/9.mdx b/chapters/en/chapter1/9.mdx
index 4cd91feac..b9f6b5d99 100644
--- a/chapters/en/chapter1/9.mdx
+++ b/chapters/en/chapter1/9.mdx
@@ -1,5 +1,10 @@
# Summary
+
+
In this chapter, you saw how to approach different NLP tasks using the high-level `pipeline()` function from 🤗 Transformers. You also saw how to search for and use models in the Hub, as well as how to use the Inference API to test the models directly in your browser.
We discussed how Transformer models work at a high level, and talked about the importance of transfer learning and fine-tuning. A key aspect is that you can use the full architecture or only the encoder or decoder, depending on what kind of task you aim to solve. The following table summarizes this:
diff --git a/chapters/en/chapter2/1.mdx b/chapters/en/chapter2/1.mdx
index 9ab184b82..ce9c18224 100644
--- a/chapters/en/chapter2/1.mdx
+++ b/chapters/en/chapter2/1.mdx
@@ -1,5 +1,10 @@
# Introduction
+
+
As you saw in [Chapter 1](/course/chapter1), Transformer models are usually very large. With millions to tens of *billions* of parameters, training and deploying these models is a complicated undertaking. Furthermore, with new models being released on a near-daily basis and each having its own implementation, trying them all out is no easy task.
The 🤗 Transformers library was created to solve this problem. Its goal is to provide a single API through which any Transformer model can be loaded, trained, and saved. The library's main features are:
diff --git a/chapters/en/chapter2/2.mdx b/chapters/en/chapter2/2.mdx
index d1304d737..d65e4983e 100644
--- a/chapters/en/chapter2/2.mdx
+++ b/chapters/en/chapter2/2.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter2/3.mdx b/chapters/en/chapter2/3.mdx
index c9100c42c..61e60b564 100644
--- a/chapters/en/chapter2/3.mdx
+++ b/chapters/en/chapter2/3.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter2/4.mdx b/chapters/en/chapter2/4.mdx
index ccebe04ec..9699ef2fc 100644
--- a/chapters/en/chapter2/4.mdx
+++ b/chapters/en/chapter2/4.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter2/5.mdx b/chapters/en/chapter2/5.mdx
index 5a692aa19..a268b4ce5 100644
--- a/chapters/en/chapter2/5.mdx
+++ b/chapters/en/chapter2/5.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter2/6.mdx b/chapters/en/chapter2/6.mdx
index 974123515..49e9e0bca 100644
--- a/chapters/en/chapter2/6.mdx
+++ b/chapters/en/chapter2/6.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter2/7.mdx b/chapters/en/chapter2/7.mdx
index 122728d08..d5306c56f 100644
--- a/chapters/en/chapter2/7.mdx
+++ b/chapters/en/chapter2/7.mdx
@@ -1,5 +1,10 @@
# Basic usage completed!
+
+
Great job following the course up to here! To recap, in this chapter you:
- Learned the basic building blocks of a Transformer model.
diff --git a/chapters/en/chapter2/8.mdx b/chapters/en/chapter2/8.mdx
index 861aacb39..96dfd107e 100644
--- a/chapters/en/chapter2/8.mdx
+++ b/chapters/en/chapter2/8.mdx
@@ -4,6 +4,11 @@
# End-of-chapter quiz
+
+
### 1. What is the order of the language modeling pipeline?
+
In [Chapter 2](/course/chapter2) we explored how to use tokenizers and pretrained models to make predictions. But what if you want to fine-tune a pretrained model for your own dataset? That's the topic of this chapter! You will learn:
{#if fw === 'pt'}
diff --git a/chapters/en/chapter3/2.mdx b/chapters/en/chapter3/2.mdx
index d6d4ceb49..dd340b738 100644
--- a/chapters/en/chapter3/2.mdx
+++ b/chapters/en/chapter3/2.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter3/3.mdx b/chapters/en/chapter3/3.mdx
index ebd301469..4420d705b 100644
--- a/chapters/en/chapter3/3.mdx
+++ b/chapters/en/chapter3/3.mdx
@@ -2,9 +2,9 @@
# Fine-tuning a model with the Trainer API
-
diff --git a/chapters/en/chapter3/3_tf.mdx b/chapters/en/chapter3/3_tf.mdx
index 6357be0b2..72f84ba20 100644
--- a/chapters/en/chapter3/3_tf.mdx
+++ b/chapters/en/chapter3/3_tf.mdx
@@ -2,9 +2,9 @@
# Fine-tuning a model with Keras
-
diff --git a/chapters/en/chapter3/4.mdx b/chapters/en/chapter3/4.mdx
index a515ce2af..53550b40c 100644
--- a/chapters/en/chapter3/4.mdx
+++ b/chapters/en/chapter3/4.mdx
@@ -1,8 +1,8 @@
# A full training
-
diff --git a/chapters/en/chapter3/5.mdx b/chapters/en/chapter3/5.mdx
index dda8cb7fe..bdbea1c52 100644
--- a/chapters/en/chapter3/5.mdx
+++ b/chapters/en/chapter3/5.mdx
@@ -2,6 +2,11 @@
# Fine-tuning, Check!
+
+
That was fun! In the first two chapters you learned about models and tokenizers, and now you know how to fine-tune them for your own data. To recap, in this chapter you:
{#if fw === 'pt'}
diff --git a/chapters/en/chapter3/6.mdx b/chapters/en/chapter3/6.mdx
index 63e6c7052..75a7cf9f5 100644
--- a/chapters/en/chapter3/6.mdx
+++ b/chapters/en/chapter3/6.mdx
@@ -4,6 +4,11 @@
# End-of-chapter quiz
+
+
Test what you learned in this chapter!
### 1. The `emotion` dataset contains Twitter messages labeled with emotions. Search for it in the [Hub](https://huggingface.co/datasets), and read the dataset card. Which of these is not one of its basic emotions?
diff --git a/chapters/en/chapter4/1.mdx b/chapters/en/chapter4/1.mdx
index 5b3a7f706..264326231 100644
--- a/chapters/en/chapter4/1.mdx
+++ b/chapters/en/chapter4/1.mdx
@@ -1,5 +1,10 @@
# The Hugging Face Hub
+
+
The [Hugging Face Hub](https://huggingface.co/) –- our main website –- is a central platform that enables anyone to discover, use, and contribute new state-of-the-art models and datasets. It hosts a wide variety of models, with more than 10,000 publicly available. We'll focus on the models in this chapter, and take a look at the datasets in Chapter 5.
The models in the Hub are not limited to 🤗 Transformers or even NLP. There are models from [Flair](https://github.com/flairNLP/flair) and [AllenNLP](https://github.com/allenai/allennlp) for NLP, [Asteroid](https://github.com/asteroid-team/asteroid) and [pyannote](https://github.com/pyannote/pyannote-audio) for speech, and [timm](https://github.com/rwightman/pytorch-image-models) for vision, to name a few.
diff --git a/chapters/en/chapter4/2.mdx b/chapters/en/chapter4/2.mdx
index 9519cddac..bca54f883 100644
--- a/chapters/en/chapter4/2.mdx
+++ b/chapters/en/chapter4/2.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter4/3.mdx b/chapters/en/chapter4/3.mdx
index a2dbc7ee8..3fb6e0a5c 100644
--- a/chapters/en/chapter4/3.mdx
+++ b/chapters/en/chapter4/3.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter4/4.mdx b/chapters/en/chapter4/4.mdx
index 8d9a75817..b609b479a 100644
--- a/chapters/en/chapter4/4.mdx
+++ b/chapters/en/chapter4/4.mdx
@@ -1,5 +1,10 @@
# Building a model card
+
+
The model card is a file which is arguably as important as the model and tokenizer files in a model repository. It is the central definition of the model, ensuring reusability by fellow community members and reproducibility of results, and providing a platform on which other members may build their artifacts.
Documenting the training and evaluation process helps others understand what to expect of a model — and providing sufficient information regarding the data that was used and the preprocessing and postprocessing that were done ensures that the limitations, biases, and contexts in which the model is and is not useful can be identified and understood.
diff --git a/chapters/en/chapter4/5.mdx b/chapters/en/chapter4/5.mdx
index 9edff8a97..341f9f348 100644
--- a/chapters/en/chapter4/5.mdx
+++ b/chapters/en/chapter4/5.mdx
@@ -1,5 +1,10 @@
# Part 1 completed!
+
+
This is the end of the first part of the course! Part 2 will be released on November 15th with a big community event, see more information [here](https://huggingface.co/blog/course-launch-event).
You should now be able to fine-tune a pretrained model on a text classification problem (single or pairs of sentences) and upload the result to the Model Hub. To make sure you mastered this first section, you should do exactly that on a problem that interests you (and not necessarily in English if you speak another language)! You can find help in the [Hugging Face forums](https://discuss.huggingface.co/) and share your project in [this topic](https://discuss.huggingface.co/t/share-your-projects/6803) once you're finished.
diff --git a/chapters/en/chapter4/6.mdx b/chapters/en/chapter4/6.mdx
index 6e968972f..4317fdb4f 100644
--- a/chapters/en/chapter4/6.mdx
+++ b/chapters/en/chapter4/6.mdx
@@ -4,6 +4,11 @@
# End-of-chapter quiz
+
+
Let's test what you learned in this chapter!
### 1. What are models on the Hub limited to?
diff --git a/chapters/en/chapter5/1.mdx b/chapters/en/chapter5/1.mdx
index f65c89609..04fbbed52 100644
--- a/chapters/en/chapter5/1.mdx
+++ b/chapters/en/chapter5/1.mdx
@@ -1,5 +1,10 @@
# Introduction
+
+
In [Chapter 3](/course/chapter3) you got your first taste of the 🤗 Datasets library and saw that there were three main steps when it came to fine-tuning a model:
1. Load a dataset from the Hugging Face Hub.
diff --git a/chapters/en/chapter5/2.mdx b/chapters/en/chapter5/2.mdx
index 853595311..9b9c84d05 100644
--- a/chapters/en/chapter5/2.mdx
+++ b/chapters/en/chapter5/2.mdx
@@ -1,8 +1,8 @@
# What if my dataset isn't on the Hub?
-
diff --git a/chapters/en/chapter5/3.mdx b/chapters/en/chapter5/3.mdx
index f47d5abbb..075a88ceb 100644
--- a/chapters/en/chapter5/3.mdx
+++ b/chapters/en/chapter5/3.mdx
@@ -1,8 +1,8 @@
# Time to slice and dice
-
diff --git a/chapters/en/chapter5/4.mdx b/chapters/en/chapter5/4.mdx
index cb90067f4..7de820546 100644
--- a/chapters/en/chapter5/4.mdx
+++ b/chapters/en/chapter5/4.mdx
@@ -1,8 +1,8 @@
# Big data? 🤗 Datasets to the rescue!
-
diff --git a/chapters/en/chapter5/5.mdx b/chapters/en/chapter5/5.mdx
index d5b0605ca..363afacbc 100644
--- a/chapters/en/chapter5/5.mdx
+++ b/chapters/en/chapter5/5.mdx
@@ -1,8 +1,8 @@
# Creating your own dataset
-
diff --git a/chapters/en/chapter5/6.mdx b/chapters/en/chapter5/6.mdx
index 1db80cc9e..b2aa21057 100644
--- a/chapters/en/chapter5/6.mdx
+++ b/chapters/en/chapter5/6.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter5/7.mdx b/chapters/en/chapter5/7.mdx
index 7b67dbe10..d4f42a6a7 100644
--- a/chapters/en/chapter5/7.mdx
+++ b/chapters/en/chapter5/7.mdx
@@ -1,5 +1,10 @@
# 🤗 Datasets, check!
+
+
Well, that was quite a tour through the 🤗 Datasets library -- congratulations on making it this far! With the knowledge that you've gained from this chapter, you should be able to:
- Load datasets from anywhere, be it the Hugging Face Hub, your laptop, or a remote server at your company.
diff --git a/chapters/en/chapter5/8.mdx b/chapters/en/chapter5/8.mdx
index 69888b348..ae049731e 100644
--- a/chapters/en/chapter5/8.mdx
+++ b/chapters/en/chapter5/8.mdx
@@ -2,6 +2,11 @@
# End-of-chapter quiz
+
+
This chapter covered a lot of ground! Don't worry if you didn't grasp all the details; the next chapters will help you understand how things work under the hood.
Before moving on, though, let's test what you learned in this chapter.
diff --git a/chapters/en/chapter6/1.mdx b/chapters/en/chapter6/1.mdx
index 5e5c63c9a..778883488 100644
--- a/chapters/en/chapter6/1.mdx
+++ b/chapters/en/chapter6/1.mdx
@@ -1,5 +1,10 @@
# Introduction
+
+
In [Chapter 3](/course/chapter3), we looked at how to fine-tune a model on a given task. When we do that, we use the same tokenizer that the model was pretrained with -- but what do we do when we want to train a model from scratch? In these cases, using a tokenizer that was pretrained on a corpus from another domain or language is typically suboptimal. For example, a tokenizer that's trained on an English corpus will perform poorly on a corpus of Japanese texts because the use of spaces and punctuation is very different in the two languages.
In this chapter, you will learn how to train a brand new tokenizer on a corpus of texts, so it can then be used to pretrain a language model. This will all be done with the help of the [🤗 Tokenizers](https://github.com/huggingface/tokenizers) library, which provides the "fast" tokenizers in the [🤗 Transformers](https://github.com/huggingface/transformers) library. We'll take a close look at the features that this library provides, and explore how the fast tokenizers differ from the "slow" versions.
diff --git a/chapters/en/chapter6/10.mdx b/chapters/en/chapter6/10.mdx
index 6d488332d..3a2fec5dd 100644
--- a/chapters/en/chapter6/10.mdx
+++ b/chapters/en/chapter6/10.mdx
@@ -2,6 +2,11 @@
# End-of-chapter quiz
+
+
Let's test what you learned in this chapter!
### 1. When should you train a new tokenizer?
diff --git a/chapters/en/chapter6/2.mdx b/chapters/en/chapter6/2.mdx
index 7eba91c92..4effa7320 100644
--- a/chapters/en/chapter6/2.mdx
+++ b/chapters/en/chapter6/2.mdx
@@ -1,8 +1,8 @@
# Training a new tokenizer from an old one
-
diff --git a/chapters/en/chapter6/3.mdx b/chapters/en/chapter6/3.mdx
index 85b24c195..4f9bd30db 100644
--- a/chapters/en/chapter6/3.mdx
+++ b/chapters/en/chapter6/3.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter6/3b.mdx b/chapters/en/chapter6/3b.mdx
index 61fad33d0..6b8ccd02f 100644
--- a/chapters/en/chapter6/3b.mdx
+++ b/chapters/en/chapter6/3b.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter6/4.mdx b/chapters/en/chapter6/4.mdx
index 741e57e8c..58a68f23b 100644
--- a/chapters/en/chapter6/4.mdx
+++ b/chapters/en/chapter6/4.mdx
@@ -1,8 +1,8 @@
# Normalization and pre-tokenization
-
diff --git a/chapters/en/chapter6/5.mdx b/chapters/en/chapter6/5.mdx
index a9f070b0e..18b189bc5 100644
--- a/chapters/en/chapter6/5.mdx
+++ b/chapters/en/chapter6/5.mdx
@@ -1,8 +1,8 @@
# Byte-Pair Encoding tokenization
-
diff --git a/chapters/en/chapter6/6.mdx b/chapters/en/chapter6/6.mdx
index d4152cd5e..7e60dabb0 100644
--- a/chapters/en/chapter6/6.mdx
+++ b/chapters/en/chapter6/6.mdx
@@ -1,8 +1,8 @@
# WordPiece tokenization
-
diff --git a/chapters/en/chapter6/7.mdx b/chapters/en/chapter6/7.mdx
index e23d6f473..2a4d6f2f3 100644
--- a/chapters/en/chapter6/7.mdx
+++ b/chapters/en/chapter6/7.mdx
@@ -1,8 +1,8 @@
# Unigram tokenization
-
diff --git a/chapters/en/chapter6/8.mdx b/chapters/en/chapter6/8.mdx
index 301648c7e..d831eb239 100644
--- a/chapters/en/chapter6/8.mdx
+++ b/chapters/en/chapter6/8.mdx
@@ -1,8 +1,8 @@
# Building a tokenizer, block by block
-
diff --git a/chapters/en/chapter6/9.mdx b/chapters/en/chapter6/9.mdx
index 6d2e0f36e..132206d25 100644
--- a/chapters/en/chapter6/9.mdx
+++ b/chapters/en/chapter6/9.mdx
@@ -1,5 +1,10 @@
# Tokenizers, check!
+
+
Great job finishing this chapter!
After this deep dive into tokenizers, you should:
diff --git a/chapters/en/chapter7/1.mdx b/chapters/en/chapter7/1.mdx
index 58215cb40..020d7ea5d 100644
--- a/chapters/en/chapter7/1.mdx
+++ b/chapters/en/chapter7/1.mdx
@@ -2,6 +2,11 @@
# Introduction
+
+
In [Chapter 3](/course/chapter3), you saw how to fine-tune a model for text classification. In this chapter, we will tackle the following common NLP tasks:
- Token classification
diff --git a/chapters/en/chapter7/2.mdx b/chapters/en/chapter7/2.mdx
index 3eaba62c8..b052dc207 100644
--- a/chapters/en/chapter7/2.mdx
+++ b/chapters/en/chapter7/2.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter7/3.mdx b/chapters/en/chapter7/3.mdx
index 1ea3d6ee5..63eeba0a9 100644
--- a/chapters/en/chapter7/3.mdx
+++ b/chapters/en/chapter7/3.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter7/4.mdx b/chapters/en/chapter7/4.mdx
index e68bb376b..cc0a41c49 100644
--- a/chapters/en/chapter7/4.mdx
+++ b/chapters/en/chapter7/4.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter7/5.mdx b/chapters/en/chapter7/5.mdx
index e6df6fc31..f66a4f429 100644
--- a/chapters/en/chapter7/5.mdx
+++ b/chapters/en/chapter7/5.mdx
@@ -1,1051 +1,1051 @@
-
-
-# Summarization
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-
-In this section we'll take a look at how Transformer models can be used to condense long documents into summaries, a task known as _text summarization_. This is one of the most challenging NLP tasks as it requires a range of abilities, such as understanding long passages and generating coherent text that captures the main topics in a document. However, when done well, text summarization is a powerful tool that can speed up various business processes by relieving the burden of domain experts to read long documents in detail.
-
-
-
-Although there already exist various fine-tuned models for summarization on the [Hugging Face Hub](https://huggingface.co/models?pipeline_tag=summarization&sort=downloads), almost all of these are only suitable for English documents. So, to add a twist in this section, we'll train a bilingual model for English and Spanish. By the end of this section, you'll have a [model](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es) that can summarize customer reviews like the one shown here:
-
-
-
-As we'll see, these summaries are concise because they're learned from the titles that customers provide in their product reviews. Let's start by putting together a suitable bilingual corpus for this task.
-
-## Preparing a multilingual corpus
-
-We'll use the [Multilingual Amazon Reviews Corpus](https://huggingface.co/datasets/amazon_reviews_multi) to create our bilingual summarizer. This corpus consists of Amazon product reviews in six languages and is typically used to benchmark multilingual classifiers. However, since each review is accompanied by a short title, we can use the titles as the target summaries for our model to learn from! To get started, let's download the English and Spanish subsets from the Hugging Face 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
- })
-})
-```
-
-As you can see, for each language there are 200,000 reviews for the `train` split, and 5,000 reviews for each of the `validation` and `test` splits. The review information we are interested in is contained in the `review_body` and `review_title` columns. Let's take a look at a few examples by creating a simple function that takes a random sample from the training set with the techniques we learned in [Chapter 5](/course/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'
-'>> 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.'
-
-'>> 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'
-
-'>> Title: Can\'t beat these for the money'
-'>> 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.'
-```
-
-
-
-✏️ **Try it out!** Change the random seed in the `Dataset.shuffle()` command to explore other reviews in the corpus. If you're a Spanish speaker, take a look at some of the reviews in `spanish_dataset` to see if the titles also seem like reasonable summaries.
-
-
-
-This sample shows the diversity of reviews one typically finds online, ranging from positive to negative (and everything in between!). Although the example with the "meh" title is not very informative, the other titles look like decent summaries of the reviews themselves. Training a summarization model on all 400,000 reviews would take far too long on a single GPU, so instead we'll focus on generating summaries for a single domain of products. To get a feel for what domains we can choose from, let's convert `english_dataset` to a `pandas.DataFrame` and compute the number of reviews per product category:
-
-```python
-english_dataset.set_format("pandas")
-english_df = english_dataset["train"][:]
-# Show counts for top 20 products
-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
-```
-
-The most popular products in the English dataset are about household items, clothing, and wireless electronics. To stick with the Amazon theme, though, let's focus on summarizing book reviews -- after all, this is what the company was founded on! We can see two product categories that fit the bill (`book` and `digital_ebook_purchase`), so let's filter the datasets in both languages for just these products. As we saw in [Chapter 5](/course/chapter5), the `Dataset.filter()` function allows us to slice a dataset very efficiently, so we can define a simple function to do this:
-
-```python
-def filter_books(example):
- return (
- example["product_category"] == "book"
- or example["product_category"] == "digital_ebook_purchase"
- )
-```
-
-Now when we apply this function to `english_dataset` and `spanish_dataset`, the result will contain just those rows involving the book categories. Before applying the filter, let's switch the format of `english_dataset` from `"pandas"` back to `"arrow"`:
-
-```python
-english_dataset.reset_format()
-```
-
-We can then apply the filter function, and as a sanity check let's inspect a sample of reviews to see if they are indeed about books:
-
-```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.'
-'>> 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.'
-
-'>> Title: Good art, good price, poor 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'
-
-'>> Title: Helpful'
-'>> Review: Nearly all the tips useful and. I consider myself an intermediate to advanced user of OneNote. I would highly recommend.'
-```
-
-Okay, we can see that the reviews are not strictly about books and might refer to things like calendars and electronic applications such as OneNote. Nevertheless, the domain seems about right to train a summarization model on. Before we look at various models that are suitable for this task, we have one last bit of data preparation to do: combining the English and Spanish reviews as a single `DatasetDict` object. 🤗 Datasets provides a handy `concatenate_datasets()` function that (as the name suggests) will stack two `Dataset` objects on top of each other. So, to create our bilingual dataset, we'll loop over each split, concatenate the datasets for that split, and shuffle the result to ensure our model doesn't overfit to a single language:
-
-```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)
-
-# Peek at a few examples
-show_samples(books_dataset)
-```
-
-```python out
-'>> Title: Easy to follow!!!!'
-'>> 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.'
-
-'>> Title: PARCIALMENTE DAÑADO'
-'>> 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).'
-
-'>> Title: no lo he podido descargar'
-'>> Review: igual que el anterior'
-```
-
-This certainly looks like a mix of English and Spanish reviews! Now that we have a training corpus, one final thing to check is the distribution of words in the reviews and their titles. This is especially important for summarization tasks, where short reference summaries in the data can bias the model to only output one or two words in the generated summaries. The plots below show the word distributions, and we can see that the titles are heavily skewed toward just 1-2 words:
-
-
-
-
-
-
-To deal with this, we'll filter out the examples with very short titles so that our model can produce more interesting summaries. Since we're dealing with English and Spanish texts, we can use a rough heuristic to split the titles on whitespace and then use our trusty `Dataset.filter()` method as follows:
-
-```python
-books_dataset = books_dataset.filter(lambda x: len(x["review_title"].split()) > 2)
-```
-
-Now that we've prepared our corpus, let's take a look at a few possible Transformer models that one might fine-tune on it!
-
-## Models for text summarization
-
-If you think about it, text summarization is a similar sort of task to machine translation: we have a body of text like a review that we'd like to "translate" into a shorter version that captures the salient features of the input. Accordingly, most Transformer models for summarization adopt the encoder-decoder architecture that we first encountered in [Chapter 1](/course/chapter1), although there are some exceptions like the GPT family of models which can also be used for summarization in few-shot settings. The following table lists some popular pretrained models that can be fine-tuned for summarization.
-
-| Transformer model | Description | Multilingual? |
-| :---------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------: |
-| [GPT-2](https://huggingface.co/gpt2-xl) | Although trained as an auto-regressive language model, you can make GPT-2 generate summaries by appending "TL;DR" at the end of the input text. | ❌ |
-| [PEGASUS](https://huggingface.co/google/pegasus-large) | Uses a pretraining objective to predict masked sentences in multi-sentence texts. This pretraining objective is closer to summarization than vanilla language modeling and scores highly on popular benchmarks. | ❌ |
-| [T5](https://huggingface.co/t5-base) | A universal Transformer architecture that formulates all tasks in a text-to-text framework; e.g., the input format for the model to summarize a document is `summarize: ARTICLE`. | ❌ |
-| [mT5](https://huggingface.co/google/mt5-base) | A multilingual version of T5, pretrained on the multilingual Common Crawl corpus (mC4), covering 101 languages. | ✅ |
-| [BART](https://huggingface.co/facebook/bart-base) | A novel Transformer architecture with both an encoder and a decoder stack trained to reconstruct corrupted input that combines the pretraining schemes of BERT and GPT-2. | ❌ |
-| [mBART-50](https://huggingface.co/facebook/mbart-large-50) | A multilingual version of BART, pretrained on 50 languages. | ✅ |
-
-As you can see from this table, the majority of Transformer models for summarization (and indeed most NLP tasks) are monolingual. This is great if your task is in a "high-resource" language like English or German, but less so for the thousands of other languages in use across the world. Fortunately, there is a class of multilingual Transformer models, like mT5 and mBART, that come to the rescue. These models are pretrained using language modeling, but with a twist: instead of training on a corpus of one language, they are trained jointly on texts in over 50 languages at once!
-
-We'll focus on mT5, an interesting architecture based on T5 that was pretrained in a text-to-text framework. In T5, every NLP task is formulated in terms of a prompt prefix like `summarize:` which conditions the model to adapt the generated text to the prompt. As shown in the figure below, this makes T5 extremely versatile, as you can solve many tasks with a single model!
-
-
-
-
-
-
-mT5 doesn't use prefixes, but shares much of the versatility of T5 and has the advantage of being multilingual. Now that we've picked a model, let's take a look at preparing our data for training.
-
-
-
-
-✏️ **Try it out!** Once you've worked through this section, see how well mT5 compares to mBART by fine-tuning the latter with the same techniques. For bonus points, you can also try fine-tuning T5 on just the English reviews. Since T5 has a special prefix prompt, you'll need to prepend `summarize:` to the input examples in the preprocessing steps below.
-
-
-
-## Preprocessing the data
-
-
-
-Our next task is to tokenize and encode our reviews and their titles. As usual, we begin by loading the tokenizer associated with the pretrained model checkpoint. We'll use `mt5-small` as our checkpoint so we can fine-tune the model in a reasonable amount of time:
-
-```python
-from transformers import AutoTokenizer
-
-model_checkpoint = "google/mt5-small"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-```
-
-
-
-💡 In the early stages of your NLP projects, a good practice is to train a class of "small" models on a small sample of data. This allows you to debug and iterate faster toward an end-to-end workflow. Once you are confident in the results, you can always scale up the model by simply changing the model checkpoint!
-
-
-
-Let's test out the mT5 tokenizer on a small example:
-
-```python
-inputs = tokenizer("I loved reading the 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]}
-```
-
-Here we can see the familiar `input_ids` and `attention_mask` that we encountered in our first fine-tuning experiments back in [Chapter 3](/course/chapter3). Let's decode these input IDs with the tokenizer's `convert_ids_to_tokens()` function to see what kind of tokenizer we're dealing with:
-
-```python
-tokenizer.convert_ids_to_tokens(inputs.input_ids)
-```
-
-```python out
-['▁I', '▁', 'loved', '▁reading', '▁the', '▁Hung', 'er', '▁Games', '']
-```
-
-The special Unicode character `▁` and end-of-sequence token `` indicate that we're dealing with the SentencePiece tokenizer, which is based on the Unigram segmentation algorithm discussed in [Chapter 6](/course/chapter6). Unigram is especially useful for multilingual corpora since it allows SentencePiece to be agnostic about accents, punctuation, and the fact that many languages, like Japanese, do not have whitespace characters.
-
-To tokenize our corpus, we have to deal with a subtlety associated with summarization: because our labels are also text, it is possible that they exceed the model's maximum context size. This means we need to apply truncation to both the reviews and their titles to ensure we don't pass excessively long inputs to our model. The tokenizers in 🤗 Transformers provide a nifty `as_target_tokenizer()` function that allows you to tokenize the labels in parallel to the inputs. This is typically done using a context manager inside a preprocessing function that first encodes the inputs, and then encodes the labels as a separate column. Here is an example of such a function for 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
- )
- # Set up the tokenizer for targets
- 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
-```
-
-Let's walk through this code to understand what's happening. The first thing we've done is define values for `max_input_length` and `max_target_length`, which set the upper limits for how long our reviews and titles can be. Since the review body is typically much larger than the title, we've scaled these values accordingly. Then, in the `preprocess_function()` itself we can see the reviews are first tokenized, followed by the titles with `as_target_tokenizer()`.
-
-With `preprocess_function()`, it is then a simple matter to tokenize the whole corpus using the handy `Dataset.map()` function we've used extensively throughout this course:
-
-```python
-tokenized_datasets = books_dataset.map(preprocess_function, batched=True)
-```
-
-Now that the corpus has been preprocessed, let's take a look at some metrics that are commonly used for summarization. As we'll see, there is no silver bullet when it comes to measuring the quality of machine-generated text.
-
-
-
-💡 You may have noticed that we used `batched=True` in our `Dataset.map()` function above. This encodes the examples in batches of 1,000 (the default) and allows you to make use of the multithreading capabilities of the fast tokenizers in 🤗 Transformers. Where possible, try using `batched=True` to get the most out of your preprocessing!
-
-
-
-
-## Metrics for text summarization
-
-
-
-In comparison to most of the other tasks we've covered in this course, measuring the performance of text generation tasks like summarization or translation is not as straightforward. For example, given a review like "I loved reading the Hunger Games", there are multiple valid summaries, like "I loved the Hunger Games" or "Hunger Games is a great read". Clearly, applying some sort of exact match between the generated summary and the label is not a good solution -- even humans would fare poorly under such a metric, because we all have our own writing style.
-
-For summarization, one of the most commonly used metrics is the [ROUGE score](https://en.wikipedia.org/wiki/ROUGE_(metric)) (short for Recall-Oriented Understudy for Gisting Evaluation). The basic idea behind this metric is to compare a generated summary against a set of reference summaries that are typically created by humans. To make this more precise, suppose we want to compare the following two summaries:
-
-```python
-generated_summary = "I absolutely loved reading the Hunger Games"
-reference_summary = "I loved reading the Hunger Games"
-```
-
-One way to compare them could be to count the number of overlapping words, which in this case would be 6. However, this is a bit crude, so instead ROUGE is based on computing the _precision_ and _recall_ scores for the overlap.
-
-
-
-🙋 Don't worry if this is the first time you've heard of precision and recall -- we'll go through some explicit examples together to make it all clear. These metrics are usually encountered in classification tasks, so if you want to understand how precision and recall are defined in that context, we recommend checking out the `scikit-learn` [guides](https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html).
-
-
-
-For ROUGE, recall measures how much of the reference summary is captured by the generated one. If we are just comparing words, recall can be calculated according to the following formula:
-
-$$ \mathrm{Recall} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, reference\, summary}} $$
-
-For our simple example above, this formula gives a perfect recall of 6/6 = 1; i.e., all the words in the reference summary have been produced by the model. This may sound great, but imagine if our generated summary had been "I really really loved reading the Hunger Games all night". This would also have perfect recall, but is arguably a worse summary since it is verbose. To deal with these scenarios we also compute the precision, which in the ROUGE context measures how much of the generated summary was relevant:
-
-$$ \mathrm{Precision} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, generated\, summary}} $$
-
-Applying this to our verbose summary gives a precision of 6/10 = 0.6, which is considerably worse than the precision of 6/7 = 0.86 obtained by our shorter one. In practice, both precision and recall are usually computed, and then the F1-score (the harmonic mean of precision and recall) is reported. We can do this easily in 🤗 Datasets by first installing the `rouge_score` package:
-
-```py
-!pip install rouge_score
-```
-
-and then loading the ROUGE metric as follows:
-
-```python
-import evaluate
-
-rouge_score = evaluate.load("rouge")
-```
-
-Then we can use the `rouge_score.compute()` function to calculate all the metrics at once:
-
-```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, there's a lot of information in that output -- what does it all mean? First, 🤗 Datasets actually computes confidence intervals for precision, recall, and F1-score; these are the `low`, `mid`, and `high` attributes you can see here. Moreover, 🤗 Datasets computes a variety of ROUGE scores which are based on different types of text granularity when comparing the generated and reference summaries. The `rouge1` variant is the overlap of unigrams -- this is just a fancy way of saying the overlap of words and is exactly the metric we've discussed above. To verify this, let's pull out the `mid` value of our scores:
-
-```python
-scores["rouge1"].mid
-```
-
-```python out
-Score(precision=0.86, recall=1.0, fmeasure=0.92)
-```
-
-Great, the precision and recall numbers match up! Now what about those other ROUGE scores? `rouge2` measures the overlap between bigrams (think the overlap of pairs of words), while `rougeL` and `rougeLsum` measure the longest matching sequences of words by looking for the longest common substrings in the generated and reference summaries. The "sum" in `rougeLsum` refers to the fact that this metric is computed over a whole summary, while `rougeL` is computed as the average over individual sentences.
-
-
-
-✏️ **Try it out!** Create your own example of a generated and reference summary and see if the resulting ROUGE scores agree with a manual calculation based on the formulas for precision and recall. For bonus points, split the text into bigrams and compare the precision and recall for the `rouge2` metric.
-
-
-
-We'll use these ROUGE scores to track the performance of our model, but before doing that let's do something every good NLP practitioner should do: create a strong, yet simple baseline!
-
-### Creating a strong baseline
-
-A common baseline for text summarization is to simply take the first three sentences of an article, often called the _lead-3_ baseline. We could use full stops to track the sentence boundaries, but this will fail on acronyms like "U.S." or "U.N." -- so instead we'll use the `nltk` library, which includes a better algorithm to handle these cases. You can install the package using `pip` as follows:
-
-```python
-!pip install nltk
-```
-
-and then download the punctuation rules:
-
-```python
-import nltk
-
-nltk.download("punkt")
-```
-
-Next, we import the sentence tokenizer from `nltk` and create a simple function to extract the first three sentences in a review. The convention in text summarization is to separate each summary with a newline, so let's also include this and test it on a training example:
-
-```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.'
-'Still,when a friend was looking for something suspenseful too read, I suggested Koontz.'
-'She found Strangers.'
-```
-
-This seems to work, so let's now implement a function that extracts these "summaries" from a dataset and computes the ROUGE scores for the baseline:
-
-```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"])
-```
-
-We can then use this function to compute the ROUGE scores over the validation set and prettify them a bit using 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}
-```
-
-We can see that the `rouge2` score is significantly lower than the rest; this likely reflects the fact that review titles are typically concise and so the lead-3 baseline is too verbose. Now that we have a good baseline to work from, let's turn our attention toward fine-tuning mT5!
-
-{#if fw === 'pt'}
-
-## Fine-tuning mT5 with the `Trainer` API
-
-Fine-tuning a model for summarization is very similar to the other tasks we've covered in this chapter. The first thing we need to do is load the pretrained model from the `mt5-small` checkpoint. Since summarization is a sequence-to-sequence task, we can load the model with the `AutoModelForSeq2SeqLM` class, which will automatically download and cache the weights:
-
-```python
-from transformers import AutoModelForSeq2SeqLM
-
-model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
-```
-
-{:else}
-
-## Fine-tuning mT5 with Keras
-
-Fine-tuning a model for summarization is very similar to the other tasks we've covered in this chapter. The first thing we need to do is load the pretrained model from the `mt5-small` checkpoint. Since summarization is a sequence-to-sequence task, we can load the model with the `TFAutoModelForSeq2SeqLM` class, which will automatically download and cache the weights:
-
-```python
-from transformers import TFAutoModelForSeq2SeqLM
-
-model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
-```
-
-{/if}
-
-
-
-💡 If you're wondering why you don't see any warnings about fine-tuning the model on a downstream task, that's because for sequence-to-sequence tasks we keep all the weights of the network. Compare this to our text classification model in [Chapter 3](/course/chapter3), where the head of the pretrained model was replaced with a randomly initialized network.
-
-
-
-The next thing we need to do is log in to the Hugging Face Hub. If you're running this code in a notebook, you can do so with the following utility function:
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-which will display a widget where you can enter your credentials. Alternatively, you can run this command in your terminal and log in there:
-
-```
-huggingface-cli login
-```
-
-{#if fw === 'pt'}
-
-We'll need to generate summaries in order to compute ROUGE scores during training. Fortunately, 🤗 Transformers provides dedicated `Seq2SeqTrainingArguments` and `Seq2SeqTrainer` classes that can do this for us automatically! To see how this works, let's first define the hyperparameters and other arguments for our experiments:
-
-```python
-from transformers import Seq2SeqTrainingArguments
-
-batch_size = 8
-num_train_epochs = 8
-# Show the training loss with every epoch
-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,
-)
-```
-
-Here, the `predict_with_generate` argument has been set to indicate that we should generate summaries during evaluation so that we can compute ROUGE scores for each epoch. As discussed in [Chapter 1](/course/chapter1), the decoder performs inference by predicting tokens one by one, and this is implemented by the model's `generate()` method. Setting `predict_with_generate=True` tells the `Seq2SeqTrainer` to use that method for evaluation. We've also adjusted some of the default hyperparameters, like the learning rate, number of epochs, and weight decay, and we've set the `save_total_limit` option to only save up to 3 checkpoints during training -- this is because even the "small" version of mT5 uses around a GB of hard drive space, and we can save a bit of room by limiting the number of copies we save.
-
-The `push_to_hub=True` argument will allow us to push the model to the Hub after training; you'll find the repository under your user profile in the location defined by `output_dir`. Note that you can specify the name of the repository you want to push to with the `hub_model_id` argument (in particular, you will have to use this argument to push to an organization). For instance, when we pushed the model to the [`huggingface-course` organization](https://huggingface.co/huggingface-course), we added `hub_model_id="huggingface-course/mt5-finetuned-amazon-en-es"` to `Seq2SeqTrainingArguments`.
-
-The next thing we need to do is provide the trainer with a `compute_metrics()` function so that we can evaluate our model during training. For summarization this is a bit more involved than simply calling `rouge_score.compute()` on the model's predictions, since we need to _decode_ the outputs and labels into text before we can compute the ROUGE scores. The following function does exactly that, and also makes use of the `sent_tokenize()` function from `nltk` to separate the summary sentences with newlines:
-
-```python
-import numpy as np
-
-
-def compute_metrics(eval_pred):
- predictions, labels = eval_pred
- # Decode generated summaries into text
- decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
- # Replace -100 in the labels as we can't decode them
- labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
- # Decode reference summaries into text
- decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
- # ROUGE expects a newline after each sentence
- 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]
- # Compute ROUGE scores
- 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}
-
-Next, we need to define a data collator for our sequence-to-sequence task. Since mT5 is an encoder-decoder Transformer model, one subtlety with preparing our batches is that during decoding we need to shift the labels to the right by one. This is required to ensure that the decoder only sees the previous ground truth labels and not the current or future ones, which would be easy for the model to memorize. This is similar to how masked self-attention is applied to the inputs in a task like [causal language modeling](/course/chapter7/6).
-
-Luckily, 🤗 Transformers provides a `DataCollatorForSeq2Seq` collator that will dynamically pad the inputs and the labels for us. To instantiate this collator, we simply need to provide the `tokenizer` and `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}
-
-Let's see what this collator produces when fed a small batch of examples. First, we need to remove the columns with strings because the collator won't know how to pad these elements:
-
-```python
-tokenized_datasets = tokenized_datasets.remove_columns(
- books_dataset["train"].column_names
-)
-```
-
-Since the collator expects a list of `dict`s, where each `dict` represents a single example in the dataset, we also need to wrangle the data into the expected format before passing it to the data collator:
-
-```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]])}
-```
-
-The main thing to notice here is that the first example is longer than the second one, so the `input_ids` and `attention_mask` of the second example have been padded on the right with a `[PAD]` token (whose ID is `0`). Similarly, we can see that the `labels` have been padded with `-100`s, to make sure the padding tokens are ignored by the loss function. And finally, we can see a new `decoder_input_ids` which has shifted the labels to the right by inserting a `[PAD]` token in the first entry.
-
-{#if fw === 'pt'}
-
-We finally have all the ingredients we need to train with! We now simply need to instantiate the trainer with the standard arguments:
-
-```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,
-)
-```
-
-and launch our training run:
-
-```python
-trainer.train()
-```
-
-During training, you should see the training loss decrease and the ROUGE scores increase with each epoch. Once the training is complete, you can see the final ROUGE scores by running `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}
-```
-
-From the scores we can see that our model has handily outperformed our lead-3 baseline -- nice! The final thing to do is push the model weights to the Hub, as follows:
-
-```
-trainer.push_to_hub(commit_message="Training complete", tags="summarization")
-```
-
-```python out
-'https://huggingface.co/huggingface-course/mt5-finetuned-amazon-en-es/commit/aa0536b829b28e73e1e4b94b8a5aacec420d40e0'
-```
-
-This will save the checkpoint and configuration files to `output_dir`, before uploading all the files to the Hub. By specifying the `tags` argument, we also ensure that the widget on the Hub will be one for a summarization pipeline instead of the default text generation one associated with the mT5 architecture (for more information about model tags, see the [🤗 Hub documentation](https://huggingface.co/docs/hub/main#how-is-a-models-type-of-inference-api-and-widget-determined)). The output from `trainer.push_to_hub()` is a URL to the Git commit hash, so you can easily see the changes that were made to the model repository!
-
-To wrap up this section, let's take a look at how we can also fine-tune mT5 using the low-level features provided by 🤗 Accelerate.
-
-{:else}
-
-We're almost ready to train! We just need to convert our datasets to `tf.data.Dataset`s using the data collator we defined above, and then `compile()` and `fit()` the model. First, the datasets:
-
-```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,
-)
-```
-
-Now, we define our training hyperparameters and compile:
-
-```python
-from transformers import create_optimizer
-import tensorflow as tf
-
-# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied
-# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset,
-# not the original Hugging Face Dataset, so its len() is already 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)
-
-# Train in mixed-precision float16
-tf.keras.mixed_precision.set_global_policy("mixed_float16")
-```
-
-And finally, we fit the model. We use a `PushToHubCallback` to save the model to the Hub after each epoch, which will allow us to use it for inference later:
-
-```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
-)
-```
-
-We got some loss values during training, but really we'd like to see the ROUGE metrics we computed earlier. To get those metrics, we'll need to generate outputs from the model and convert them to strings. Let's build some lists of labels and predictions for the ROUGE metric to compare (note that if you get import errors for this section, you may need to`!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)
-```
-
-Once we have our lists of label and prediction strings, computing the ROUGE score is easy:
-
-```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'}
-
-## Fine-tuning mT5 with 🤗 Accelerate
-
-Fine-tuning our model with 🤗 Accelerate is very similar to the text classification example we encountered in [Chapter 3](/course/chapter3). The main differences will be the need to explicitly generate our summaries during training and define how we compute the ROUGE scores (recall that the `Seq2SeqTrainer` took care of the generation for us). Let's take a look how we can implement these two requirements within 🤗 Accelerate!
-
-### Preparing everything for training
-
-The first thing we need to do is create a `DataLoader` for each of our splits. Since the PyTorch dataloaders expect batches of tensors, we need to set the format to `"torch"` in our datasets:
-
-```python
-tokenized_datasets.set_format("torch")
-```
-
-Now that we've got datasets consisting of just tensors, the next thing to do is instantiate the `DataCollatorForSeq2Seq` again. For this we need to provide a fresh version of the model, so let's load it again from our cache:
-
-```python
-model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
-```
-
-We can then instantiate the data collator and use this to define our dataloaders:
-
-```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
-)
-```
-
-The next thing to do is define the optimizer we want to use. As in our other examples, we'll use `AdamW`, which works well for most problems:
-
-```python
-from torch.optim import AdamW
-
-optimizer = AdamW(model.parameters(), lr=2e-5)
-```
-
-Finally, we feed our model, optimizer, and dataloaders to the `accelerator.prepare()` method:
-
-```python
-from accelerate import Accelerator
-
-accelerator = Accelerator()
-model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
- model, optimizer, train_dataloader, eval_dataloader
-)
-```
-
-
-
-🚨 If you're training on a TPU, you'll need to move all the code above into a dedicated training function. See [Chapter 3](/course/chapter3) for more details.
-
-
-
-Now that we've prepared our objects, there are three remaining things to do:
-
-* Define the learning rate schedule.
-* Implement a function to post-process the summaries for evaluation.
-* Create a repository on the Hub that we can push our model to.
-
-For the learning rate schedule, we'll use the standard linear one from previous sections:
-
-```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,
-)
-```
-
-For post-processing, we need a function that splits the generated summaries into sentences that are separated by newlines. This is the format the ROUGE metric expects, and we can achieve this with the following snippet of code:
-
-```python
-def postprocess_text(preds, labels):
- preds = [pred.strip() for pred in preds]
- labels = [label.strip() for label in labels]
-
- # ROUGE expects a newline after each sentence
- 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
-```
-
-This should look familiar to you if you recall how we defined the `compute_metrics()` function of the `Seq2SeqTrainer`.
-
-Finally, we need to create a model repository on the Hugging Face Hub. For this, we can use the appropriately titled 🤗 Hub library. We just need to define a name for our repository, and the library has a utility function to combine the repository ID with the user profile:
-
-```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'
-```
-
-Now we can use this repository name to clone a local version to our results directory that will store the training artifacts:
-
-```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.
-
-### Training loop
-
-The training loop for summarization is quite similar to the other 🤗 Accelerate examples that we've encountered and is roughly split into four main steps:
-
-1. Train the model by iterating over all the examples in `train_dataloader` for each epoch.
-2. Generate model summaries at the end of each epoch, by first generating the tokens and then decoding them (and the reference summaries) into text.
-3. Compute the ROUGE scores using the same techniques we saw earlier.
-4. Save the checkpoints and push everything to the Hub. Here we rely on the nifty `blocking=False` argument of the `Repository` object so that we can push the checkpoints per epoch _asynchronously_. This allows us to continue training without having to wait for the somewhat slow upload associated with a GB-sized model!
-
-These steps can be seen in the following block of code:
-
-```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):
- # Training
- 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"]
-
- # If we did not pad to max length, we need to pad the labels too
- 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()
-
- # Replace -100 in the labels as we can't decode them
- 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)
-
- # Compute metrics
- 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)
-
- # Save and upload
- 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}
-```
-
-And that's it! Once you run this, you'll have a model and results that are pretty similar to the ones we obtained with the `Trainer`.
-
-{/if}
-
-## Using your fine-tuned model
-
-Once you've pushed the model to the Hub, you can play with it either via the inference widget or with a `pipeline` object, as follows:
-
-```python
-from transformers import pipeline
-
-hub_model_id = "huggingface-course/mt5-small-finetuned-amazon-en-es"
-summarizer = pipeline("summarization", model=hub_model_id)
-```
-
-We can feed some examples from the test set (which the model has not seen) to our pipeline to get a feel for the quality of the summaries. First let's implement a simple function to show the review, title, and generated summary together:
-
-```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}'")
-```
-
-Let's take a look at one of the English examples we get:
-
-```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.'
-
-'>>> Title: Not impressed at all... buy something else'
-
-'>>> Summary: Nothing special at all about this product'
-```
-
-This is not too bad! We can see that our model has actually been able to perform _abstractive_ summarization by augmenting parts of the review with new words. And perhaps the coolest aspect of our model is that it is bilingual, so we can also generate summaries of Spanish reviews:
-
-```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'
-
-'>>> Title: Buena literatura para adolescentes'
-
-'>>> Summary: Muy facil de leer'
-```
-
-The summary translates into "Very easy to read" in English, which we can see in this case was extracted directly from the review. Nevertheless, this shows the versatility of the mT5 model and has given you a taste of what it's like to deal with a multilingual corpus!
-
-Next, we'll turn our attention to a slightly more complex task: training a language model from scratch.
+
+
+# Summarization
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+
+In this section we'll take a look at how Transformer models can be used to condense long documents into summaries, a task known as _text summarization_. This is one of the most challenging NLP tasks as it requires a range of abilities, such as understanding long passages and generating coherent text that captures the main topics in a document. However, when done well, text summarization is a powerful tool that can speed up various business processes by relieving the burden of domain experts to read long documents in detail.
+
+
+
+Although there already exist various fine-tuned models for summarization on the [Hugging Face Hub](https://huggingface.co/models?pipeline_tag=summarization&sort=downloads), almost all of these are only suitable for English documents. So, to add a twist in this section, we'll train a bilingual model for English and Spanish. By the end of this section, you'll have a [model](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es) that can summarize customer reviews like the one shown here:
+
+
+
+As we'll see, these summaries are concise because they're learned from the titles that customers provide in their product reviews. Let's start by putting together a suitable bilingual corpus for this task.
+
+## Preparing a multilingual corpus
+
+We'll use the [Multilingual Amazon Reviews Corpus](https://huggingface.co/datasets/amazon_reviews_multi) to create our bilingual summarizer. This corpus consists of Amazon product reviews in six languages and is typically used to benchmark multilingual classifiers. However, since each review is accompanied by a short title, we can use the titles as the target summaries for our model to learn from! To get started, let's download the English and Spanish subsets from the Hugging Face 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
+ })
+})
+```
+
+As you can see, for each language there are 200,000 reviews for the `train` split, and 5,000 reviews for each of the `validation` and `test` splits. The review information we are interested in is contained in the `review_body` and `review_title` columns. Let's take a look at a few examples by creating a simple function that takes a random sample from the training set with the techniques we learned in [Chapter 5](/course/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'
+'>> 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.'
+
+'>> 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'
+
+'>> Title: Can\'t beat these for the money'
+'>> 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.'
+```
+
+
+
+✏️ **Try it out!** Change the random seed in the `Dataset.shuffle()` command to explore other reviews in the corpus. If you're a Spanish speaker, take a look at some of the reviews in `spanish_dataset` to see if the titles also seem like reasonable summaries.
+
+
+
+This sample shows the diversity of reviews one typically finds online, ranging from positive to negative (and everything in between!). Although the example with the "meh" title is not very informative, the other titles look like decent summaries of the reviews themselves. Training a summarization model on all 400,000 reviews would take far too long on a single GPU, so instead we'll focus on generating summaries for a single domain of products. To get a feel for what domains we can choose from, let's convert `english_dataset` to a `pandas.DataFrame` and compute the number of reviews per product category:
+
+```python
+english_dataset.set_format("pandas")
+english_df = english_dataset["train"][:]
+# Show counts for top 20 products
+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
+```
+
+The most popular products in the English dataset are about household items, clothing, and wireless electronics. To stick with the Amazon theme, though, let's focus on summarizing book reviews -- after all, this is what the company was founded on! We can see two product categories that fit the bill (`book` and `digital_ebook_purchase`), so let's filter the datasets in both languages for just these products. As we saw in [Chapter 5](/course/chapter5), the `Dataset.filter()` function allows us to slice a dataset very efficiently, so we can define a simple function to do this:
+
+```python
+def filter_books(example):
+ return (
+ example["product_category"] == "book"
+ or example["product_category"] == "digital_ebook_purchase"
+ )
+```
+
+Now when we apply this function to `english_dataset` and `spanish_dataset`, the result will contain just those rows involving the book categories. Before applying the filter, let's switch the format of `english_dataset` from `"pandas"` back to `"arrow"`:
+
+```python
+english_dataset.reset_format()
+```
+
+We can then apply the filter function, and as a sanity check let's inspect a sample of reviews to see if they are indeed about books:
+
+```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.'
+'>> 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.'
+
+'>> Title: Good art, good price, poor 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'
+
+'>> Title: Helpful'
+'>> Review: Nearly all the tips useful and. I consider myself an intermediate to advanced user of OneNote. I would highly recommend.'
+```
+
+Okay, we can see that the reviews are not strictly about books and might refer to things like calendars and electronic applications such as OneNote. Nevertheless, the domain seems about right to train a summarization model on. Before we look at various models that are suitable for this task, we have one last bit of data preparation to do: combining the English and Spanish reviews as a single `DatasetDict` object. 🤗 Datasets provides a handy `concatenate_datasets()` function that (as the name suggests) will stack two `Dataset` objects on top of each other. So, to create our bilingual dataset, we'll loop over each split, concatenate the datasets for that split, and shuffle the result to ensure our model doesn't overfit to a single language:
+
+```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)
+
+# Peek at a few examples
+show_samples(books_dataset)
+```
+
+```python out
+'>> Title: Easy to follow!!!!'
+'>> 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.'
+
+'>> Title: PARCIALMENTE DAÑADO'
+'>> 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).'
+
+'>> Title: no lo he podido descargar'
+'>> Review: igual que el anterior'
+```
+
+This certainly looks like a mix of English and Spanish reviews! Now that we have a training corpus, one final thing to check is the distribution of words in the reviews and their titles. This is especially important for summarization tasks, where short reference summaries in the data can bias the model to only output one or two words in the generated summaries. The plots below show the word distributions, and we can see that the titles are heavily skewed toward just 1-2 words:
+
+
+
+
+
+
+To deal with this, we'll filter out the examples with very short titles so that our model can produce more interesting summaries. Since we're dealing with English and Spanish texts, we can use a rough heuristic to split the titles on whitespace and then use our trusty `Dataset.filter()` method as follows:
+
+```python
+books_dataset = books_dataset.filter(lambda x: len(x["review_title"].split()) > 2)
+```
+
+Now that we've prepared our corpus, let's take a look at a few possible Transformer models that one might fine-tune on it!
+
+## Models for text summarization
+
+If you think about it, text summarization is a similar sort of task to machine translation: we have a body of text like a review that we'd like to "translate" into a shorter version that captures the salient features of the input. Accordingly, most Transformer models for summarization adopt the encoder-decoder architecture that we first encountered in [Chapter 1](/course/chapter1), although there are some exceptions like the GPT family of models which can also be used for summarization in few-shot settings. The following table lists some popular pretrained models that can be fine-tuned for summarization.
+
+| Transformer model | Description | Multilingual? |
+| :---------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------: |
+| [GPT-2](https://huggingface.co/gpt2-xl) | Although trained as an auto-regressive language model, you can make GPT-2 generate summaries by appending "TL;DR" at the end of the input text. | ❌ |
+| [PEGASUS](https://huggingface.co/google/pegasus-large) | Uses a pretraining objective to predict masked sentences in multi-sentence texts. This pretraining objective is closer to summarization than vanilla language modeling and scores highly on popular benchmarks. | ❌ |
+| [T5](https://huggingface.co/t5-base) | A universal Transformer architecture that formulates all tasks in a text-to-text framework; e.g., the input format for the model to summarize a document is `summarize: ARTICLE`. | ❌ |
+| [mT5](https://huggingface.co/google/mt5-base) | A multilingual version of T5, pretrained on the multilingual Common Crawl corpus (mC4), covering 101 languages. | ✅ |
+| [BART](https://huggingface.co/facebook/bart-base) | A novel Transformer architecture with both an encoder and a decoder stack trained to reconstruct corrupted input that combines the pretraining schemes of BERT and GPT-2. | ❌ |
+| [mBART-50](https://huggingface.co/facebook/mbart-large-50) | A multilingual version of BART, pretrained on 50 languages. | ✅ |
+
+As you can see from this table, the majority of Transformer models for summarization (and indeed most NLP tasks) are monolingual. This is great if your task is in a "high-resource" language like English or German, but less so for the thousands of other languages in use across the world. Fortunately, there is a class of multilingual Transformer models, like mT5 and mBART, that come to the rescue. These models are pretrained using language modeling, but with a twist: instead of training on a corpus of one language, they are trained jointly on texts in over 50 languages at once!
+
+We'll focus on mT5, an interesting architecture based on T5 that was pretrained in a text-to-text framework. In T5, every NLP task is formulated in terms of a prompt prefix like `summarize:` which conditions the model to adapt the generated text to the prompt. As shown in the figure below, this makes T5 extremely versatile, as you can solve many tasks with a single model!
+
+
+
+
+
+
+mT5 doesn't use prefixes, but shares much of the versatility of T5 and has the advantage of being multilingual. Now that we've picked a model, let's take a look at preparing our data for training.
+
+
+
+
+✏️ **Try it out!** Once you've worked through this section, see how well mT5 compares to mBART by fine-tuning the latter with the same techniques. For bonus points, you can also try fine-tuning T5 on just the English reviews. Since T5 has a special prefix prompt, you'll need to prepend `summarize:` to the input examples in the preprocessing steps below.
+
+
+
+## Preprocessing the data
+
+
+
+Our next task is to tokenize and encode our reviews and their titles. As usual, we begin by loading the tokenizer associated with the pretrained model checkpoint. We'll use `mt5-small` as our checkpoint so we can fine-tune the model in a reasonable amount of time:
+
+```python
+from transformers import AutoTokenizer
+
+model_checkpoint = "google/mt5-small"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+```
+
+
+
+💡 In the early stages of your NLP projects, a good practice is to train a class of "small" models on a small sample of data. This allows you to debug and iterate faster toward an end-to-end workflow. Once you are confident in the results, you can always scale up the model by simply changing the model checkpoint!
+
+
+
+Let's test out the mT5 tokenizer on a small example:
+
+```python
+inputs = tokenizer("I loved reading the 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]}
+```
+
+Here we can see the familiar `input_ids` and `attention_mask` that we encountered in our first fine-tuning experiments back in [Chapter 3](/course/chapter3). Let's decode these input IDs with the tokenizer's `convert_ids_to_tokens()` function to see what kind of tokenizer we're dealing with:
+
+```python
+tokenizer.convert_ids_to_tokens(inputs.input_ids)
+```
+
+```python out
+['▁I', '▁', 'loved', '▁reading', '▁the', '▁Hung', 'er', '▁Games', '']
+```
+
+The special Unicode character `▁` and end-of-sequence token `` indicate that we're dealing with the SentencePiece tokenizer, which is based on the Unigram segmentation algorithm discussed in [Chapter 6](/course/chapter6). Unigram is especially useful for multilingual corpora since it allows SentencePiece to be agnostic about accents, punctuation, and the fact that many languages, like Japanese, do not have whitespace characters.
+
+To tokenize our corpus, we have to deal with a subtlety associated with summarization: because our labels are also text, it is possible that they exceed the model's maximum context size. This means we need to apply truncation to both the reviews and their titles to ensure we don't pass excessively long inputs to our model. The tokenizers in 🤗 Transformers provide a nifty `as_target_tokenizer()` function that allows you to tokenize the labels in parallel to the inputs. This is typically done using a context manager inside a preprocessing function that first encodes the inputs, and then encodes the labels as a separate column. Here is an example of such a function for 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
+ )
+ # Set up the tokenizer for targets
+ 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
+```
+
+Let's walk through this code to understand what's happening. The first thing we've done is define values for `max_input_length` and `max_target_length`, which set the upper limits for how long our reviews and titles can be. Since the review body is typically much larger than the title, we've scaled these values accordingly. Then, in the `preprocess_function()` itself we can see the reviews are first tokenized, followed by the titles with `as_target_tokenizer()`.
+
+With `preprocess_function()`, it is then a simple matter to tokenize the whole corpus using the handy `Dataset.map()` function we've used extensively throughout this course:
+
+```python
+tokenized_datasets = books_dataset.map(preprocess_function, batched=True)
+```
+
+Now that the corpus has been preprocessed, let's take a look at some metrics that are commonly used for summarization. As we'll see, there is no silver bullet when it comes to measuring the quality of machine-generated text.
+
+
+
+💡 You may have noticed that we used `batched=True` in our `Dataset.map()` function above. This encodes the examples in batches of 1,000 (the default) and allows you to make use of the multithreading capabilities of the fast tokenizers in 🤗 Transformers. Where possible, try using `batched=True` to get the most out of your preprocessing!
+
+
+
+
+## Metrics for text summarization
+
+
+
+In comparison to most of the other tasks we've covered in this course, measuring the performance of text generation tasks like summarization or translation is not as straightforward. For example, given a review like "I loved reading the Hunger Games", there are multiple valid summaries, like "I loved the Hunger Games" or "Hunger Games is a great read". Clearly, applying some sort of exact match between the generated summary and the label is not a good solution -- even humans would fare poorly under such a metric, because we all have our own writing style.
+
+For summarization, one of the most commonly used metrics is the [ROUGE score](https://en.wikipedia.org/wiki/ROUGE_(metric)) (short for Recall-Oriented Understudy for Gisting Evaluation). The basic idea behind this metric is to compare a generated summary against a set of reference summaries that are typically created by humans. To make this more precise, suppose we want to compare the following two summaries:
+
+```python
+generated_summary = "I absolutely loved reading the Hunger Games"
+reference_summary = "I loved reading the Hunger Games"
+```
+
+One way to compare them could be to count the number of overlapping words, which in this case would be 6. However, this is a bit crude, so instead ROUGE is based on computing the _precision_ and _recall_ scores for the overlap.
+
+
+
+🙋 Don't worry if this is the first time you've heard of precision and recall -- we'll go through some explicit examples together to make it all clear. These metrics are usually encountered in classification tasks, so if you want to understand how precision and recall are defined in that context, we recommend checking out the `scikit-learn` [guides](https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html).
+
+
+
+For ROUGE, recall measures how much of the reference summary is captured by the generated one. If we are just comparing words, recall can be calculated according to the following formula:
+
+$$ \mathrm{Recall} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, reference\, summary}} $$
+
+For our simple example above, this formula gives a perfect recall of 6/6 = 1; i.e., all the words in the reference summary have been produced by the model. This may sound great, but imagine if our generated summary had been "I really really loved reading the Hunger Games all night". This would also have perfect recall, but is arguably a worse summary since it is verbose. To deal with these scenarios we also compute the precision, which in the ROUGE context measures how much of the generated summary was relevant:
+
+$$ \mathrm{Precision} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, generated\, summary}} $$
+
+Applying this to our verbose summary gives a precision of 6/10 = 0.6, which is considerably worse than the precision of 6/7 = 0.86 obtained by our shorter one. In practice, both precision and recall are usually computed, and then the F1-score (the harmonic mean of precision and recall) is reported. We can do this easily in 🤗 Datasets by first installing the `rouge_score` package:
+
+```py
+!pip install rouge_score
+```
+
+and then loading the ROUGE metric as follows:
+
+```python
+import evaluate
+
+rouge_score = evaluate.load("rouge")
+```
+
+Then we can use the `rouge_score.compute()` function to calculate all the metrics at once:
+
+```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, there's a lot of information in that output -- what does it all mean? First, 🤗 Datasets actually computes confidence intervals for precision, recall, and F1-score; these are the `low`, `mid`, and `high` attributes you can see here. Moreover, 🤗 Datasets computes a variety of ROUGE scores which are based on different types of text granularity when comparing the generated and reference summaries. The `rouge1` variant is the overlap of unigrams -- this is just a fancy way of saying the overlap of words and is exactly the metric we've discussed above. To verify this, let's pull out the `mid` value of our scores:
+
+```python
+scores["rouge1"].mid
+```
+
+```python out
+Score(precision=0.86, recall=1.0, fmeasure=0.92)
+```
+
+Great, the precision and recall numbers match up! Now what about those other ROUGE scores? `rouge2` measures the overlap between bigrams (think the overlap of pairs of words), while `rougeL` and `rougeLsum` measure the longest matching sequences of words by looking for the longest common substrings in the generated and reference summaries. The "sum" in `rougeLsum` refers to the fact that this metric is computed over a whole summary, while `rougeL` is computed as the average over individual sentences.
+
+
+
+✏️ **Try it out!** Create your own example of a generated and reference summary and see if the resulting ROUGE scores agree with a manual calculation based on the formulas for precision and recall. For bonus points, split the text into bigrams and compare the precision and recall for the `rouge2` metric.
+
+
+
+We'll use these ROUGE scores to track the performance of our model, but before doing that let's do something every good NLP practitioner should do: create a strong, yet simple baseline!
+
+### Creating a strong baseline
+
+A common baseline for text summarization is to simply take the first three sentences of an article, often called the _lead-3_ baseline. We could use full stops to track the sentence boundaries, but this will fail on acronyms like "U.S." or "U.N." -- so instead we'll use the `nltk` library, which includes a better algorithm to handle these cases. You can install the package using `pip` as follows:
+
+```python
+!pip install nltk
+```
+
+and then download the punctuation rules:
+
+```python
+import nltk
+
+nltk.download("punkt")
+```
+
+Next, we import the sentence tokenizer from `nltk` and create a simple function to extract the first three sentences in a review. The convention in text summarization is to separate each summary with a newline, so let's also include this and test it on a training example:
+
+```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.'
+'Still,when a friend was looking for something suspenseful too read, I suggested Koontz.'
+'She found Strangers.'
+```
+
+This seems to work, so let's now implement a function that extracts these "summaries" from a dataset and computes the ROUGE scores for the baseline:
+
+```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"])
+```
+
+We can then use this function to compute the ROUGE scores over the validation set and prettify them a bit using 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}
+```
+
+We can see that the `rouge2` score is significantly lower than the rest; this likely reflects the fact that review titles are typically concise and so the lead-3 baseline is too verbose. Now that we have a good baseline to work from, let's turn our attention toward fine-tuning mT5!
+
+{#if fw === 'pt'}
+
+## Fine-tuning mT5 with the `Trainer` API
+
+Fine-tuning a model for summarization is very similar to the other tasks we've covered in this chapter. The first thing we need to do is load the pretrained model from the `mt5-small` checkpoint. Since summarization is a sequence-to-sequence task, we can load the model with the `AutoModelForSeq2SeqLM` class, which will automatically download and cache the weights:
+
+```python
+from transformers import AutoModelForSeq2SeqLM
+
+model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
+```
+
+{:else}
+
+## Fine-tuning mT5 with Keras
+
+Fine-tuning a model for summarization is very similar to the other tasks we've covered in this chapter. The first thing we need to do is load the pretrained model from the `mt5-small` checkpoint. Since summarization is a sequence-to-sequence task, we can load the model with the `TFAutoModelForSeq2SeqLM` class, which will automatically download and cache the weights:
+
+```python
+from transformers import TFAutoModelForSeq2SeqLM
+
+model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
+```
+
+{/if}
+
+
+
+💡 If you're wondering why you don't see any warnings about fine-tuning the model on a downstream task, that's because for sequence-to-sequence tasks we keep all the weights of the network. Compare this to our text classification model in [Chapter 3](/course/chapter3), where the head of the pretrained model was replaced with a randomly initialized network.
+
+
+
+The next thing we need to do is log in to the Hugging Face Hub. If you're running this code in a notebook, you can do so with the following utility function:
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+which will display a widget where you can enter your credentials. Alternatively, you can run this command in your terminal and log in there:
+
+```
+huggingface-cli login
+```
+
+{#if fw === 'pt'}
+
+We'll need to generate summaries in order to compute ROUGE scores during training. Fortunately, 🤗 Transformers provides dedicated `Seq2SeqTrainingArguments` and `Seq2SeqTrainer` classes that can do this for us automatically! To see how this works, let's first define the hyperparameters and other arguments for our experiments:
+
+```python
+from transformers import Seq2SeqTrainingArguments
+
+batch_size = 8
+num_train_epochs = 8
+# Show the training loss with every epoch
+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,
+)
+```
+
+Here, the `predict_with_generate` argument has been set to indicate that we should generate summaries during evaluation so that we can compute ROUGE scores for each epoch. As discussed in [Chapter 1](/course/chapter1), the decoder performs inference by predicting tokens one by one, and this is implemented by the model's `generate()` method. Setting `predict_with_generate=True` tells the `Seq2SeqTrainer` to use that method for evaluation. We've also adjusted some of the default hyperparameters, like the learning rate, number of epochs, and weight decay, and we've set the `save_total_limit` option to only save up to 3 checkpoints during training -- this is because even the "small" version of mT5 uses around a GB of hard drive space, and we can save a bit of room by limiting the number of copies we save.
+
+The `push_to_hub=True` argument will allow us to push the model to the Hub after training; you'll find the repository under your user profile in the location defined by `output_dir`. Note that you can specify the name of the repository you want to push to with the `hub_model_id` argument (in particular, you will have to use this argument to push to an organization). For instance, when we pushed the model to the [`huggingface-course` organization](https://huggingface.co/huggingface-course), we added `hub_model_id="huggingface-course/mt5-finetuned-amazon-en-es"` to `Seq2SeqTrainingArguments`.
+
+The next thing we need to do is provide the trainer with a `compute_metrics()` function so that we can evaluate our model during training. For summarization this is a bit more involved than simply calling `rouge_score.compute()` on the model's predictions, since we need to _decode_ the outputs and labels into text before we can compute the ROUGE scores. The following function does exactly that, and also makes use of the `sent_tokenize()` function from `nltk` to separate the summary sentences with newlines:
+
+```python
+import numpy as np
+
+
+def compute_metrics(eval_pred):
+ predictions, labels = eval_pred
+ # Decode generated summaries into text
+ decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
+ # Replace -100 in the labels as we can't decode them
+ labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
+ # Decode reference summaries into text
+ decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
+ # ROUGE expects a newline after each sentence
+ 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]
+ # Compute ROUGE scores
+ 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}
+
+Next, we need to define a data collator for our sequence-to-sequence task. Since mT5 is an encoder-decoder Transformer model, one subtlety with preparing our batches is that during decoding we need to shift the labels to the right by one. This is required to ensure that the decoder only sees the previous ground truth labels and not the current or future ones, which would be easy for the model to memorize. This is similar to how masked self-attention is applied to the inputs in a task like [causal language modeling](/course/chapter7/6).
+
+Luckily, 🤗 Transformers provides a `DataCollatorForSeq2Seq` collator that will dynamically pad the inputs and the labels for us. To instantiate this collator, we simply need to provide the `tokenizer` and `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}
+
+Let's see what this collator produces when fed a small batch of examples. First, we need to remove the columns with strings because the collator won't know how to pad these elements:
+
+```python
+tokenized_datasets = tokenized_datasets.remove_columns(
+ books_dataset["train"].column_names
+)
+```
+
+Since the collator expects a list of `dict`s, where each `dict` represents a single example in the dataset, we also need to wrangle the data into the expected format before passing it to the data collator:
+
+```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]])}
+```
+
+The main thing to notice here is that the first example is longer than the second one, so the `input_ids` and `attention_mask` of the second example have been padded on the right with a `[PAD]` token (whose ID is `0`). Similarly, we can see that the `labels` have been padded with `-100`s, to make sure the padding tokens are ignored by the loss function. And finally, we can see a new `decoder_input_ids` which has shifted the labels to the right by inserting a `[PAD]` token in the first entry.
+
+{#if fw === 'pt'}
+
+We finally have all the ingredients we need to train with! We now simply need to instantiate the trainer with the standard arguments:
+
+```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,
+)
+```
+
+and launch our training run:
+
+```python
+trainer.train()
+```
+
+During training, you should see the training loss decrease and the ROUGE scores increase with each epoch. Once the training is complete, you can see the final ROUGE scores by running `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}
+```
+
+From the scores we can see that our model has handily outperformed our lead-3 baseline -- nice! The final thing to do is push the model weights to the Hub, as follows:
+
+```
+trainer.push_to_hub(commit_message="Training complete", tags="summarization")
+```
+
+```python out
+'https://huggingface.co/huggingface-course/mt5-finetuned-amazon-en-es/commit/aa0536b829b28e73e1e4b94b8a5aacec420d40e0'
+```
+
+This will save the checkpoint and configuration files to `output_dir`, before uploading all the files to the Hub. By specifying the `tags` argument, we also ensure that the widget on the Hub will be one for a summarization pipeline instead of the default text generation one associated with the mT5 architecture (for more information about model tags, see the [🤗 Hub documentation](https://huggingface.co/docs/hub/main#how-is-a-models-type-of-inference-api-and-widget-determined)). The output from `trainer.push_to_hub()` is a URL to the Git commit hash, so you can easily see the changes that were made to the model repository!
+
+To wrap up this section, let's take a look at how we can also fine-tune mT5 using the low-level features provided by 🤗 Accelerate.
+
+{:else}
+
+We're almost ready to train! We just need to convert our datasets to `tf.data.Dataset`s using the data collator we defined above, and then `compile()` and `fit()` the model. First, the datasets:
+
+```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,
+)
+```
+
+Now, we define our training hyperparameters and compile:
+
+```python
+from transformers import create_optimizer
+import tensorflow as tf
+
+# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied
+# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset,
+# not the original Hugging Face Dataset, so its len() is already 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)
+
+# Train in mixed-precision float16
+tf.keras.mixed_precision.set_global_policy("mixed_float16")
+```
+
+And finally, we fit the model. We use a `PushToHubCallback` to save the model to the Hub after each epoch, which will allow us to use it for inference later:
+
+```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
+)
+```
+
+We got some loss values during training, but really we'd like to see the ROUGE metrics we computed earlier. To get those metrics, we'll need to generate outputs from the model and convert them to strings. Let's build some lists of labels and predictions for the ROUGE metric to compare (note that if you get import errors for this section, you may need to`!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)
+```
+
+Once we have our lists of label and prediction strings, computing the ROUGE score is easy:
+
+```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'}
+
+## Fine-tuning mT5 with 🤗 Accelerate
+
+Fine-tuning our model with 🤗 Accelerate is very similar to the text classification example we encountered in [Chapter 3](/course/chapter3). The main differences will be the need to explicitly generate our summaries during training and define how we compute the ROUGE scores (recall that the `Seq2SeqTrainer` took care of the generation for us). Let's take a look how we can implement these two requirements within 🤗 Accelerate!
+
+### Preparing everything for training
+
+The first thing we need to do is create a `DataLoader` for each of our splits. Since the PyTorch dataloaders expect batches of tensors, we need to set the format to `"torch"` in our datasets:
+
+```python
+tokenized_datasets.set_format("torch")
+```
+
+Now that we've got datasets consisting of just tensors, the next thing to do is instantiate the `DataCollatorForSeq2Seq` again. For this we need to provide a fresh version of the model, so let's load it again from our cache:
+
+```python
+model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
+```
+
+We can then instantiate the data collator and use this to define our dataloaders:
+
+```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
+)
+```
+
+The next thing to do is define the optimizer we want to use. As in our other examples, we'll use `AdamW`, which works well for most problems:
+
+```python
+from torch.optim import AdamW
+
+optimizer = AdamW(model.parameters(), lr=2e-5)
+```
+
+Finally, we feed our model, optimizer, and dataloaders to the `accelerator.prepare()` method:
+
+```python
+from accelerate import Accelerator
+
+accelerator = Accelerator()
+model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
+ model, optimizer, train_dataloader, eval_dataloader
+)
+```
+
+
+
+🚨 If you're training on a TPU, you'll need to move all the code above into a dedicated training function. See [Chapter 3](/course/chapter3) for more details.
+
+
+
+Now that we've prepared our objects, there are three remaining things to do:
+
+* Define the learning rate schedule.
+* Implement a function to post-process the summaries for evaluation.
+* Create a repository on the Hub that we can push our model to.
+
+For the learning rate schedule, we'll use the standard linear one from previous sections:
+
+```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,
+)
+```
+
+For post-processing, we need a function that splits the generated summaries into sentences that are separated by newlines. This is the format the ROUGE metric expects, and we can achieve this with the following snippet of code:
+
+```python
+def postprocess_text(preds, labels):
+ preds = [pred.strip() for pred in preds]
+ labels = [label.strip() for label in labels]
+
+ # ROUGE expects a newline after each sentence
+ 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
+```
+
+This should look familiar to you if you recall how we defined the `compute_metrics()` function of the `Seq2SeqTrainer`.
+
+Finally, we need to create a model repository on the Hugging Face Hub. For this, we can use the appropriately titled 🤗 Hub library. We just need to define a name for our repository, and the library has a utility function to combine the repository ID with the user profile:
+
+```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'
+```
+
+Now we can use this repository name to clone a local version to our results directory that will store the training artifacts:
+
+```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.
+
+### Training loop
+
+The training loop for summarization is quite similar to the other 🤗 Accelerate examples that we've encountered and is roughly split into four main steps:
+
+1. Train the model by iterating over all the examples in `train_dataloader` for each epoch.
+2. Generate model summaries at the end of each epoch, by first generating the tokens and then decoding them (and the reference summaries) into text.
+3. Compute the ROUGE scores using the same techniques we saw earlier.
+4. Save the checkpoints and push everything to the Hub. Here we rely on the nifty `blocking=False` argument of the `Repository` object so that we can push the checkpoints per epoch _asynchronously_. This allows us to continue training without having to wait for the somewhat slow upload associated with a GB-sized model!
+
+These steps can be seen in the following block of code:
+
+```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):
+ # Training
+ 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"]
+
+ # If we did not pad to max length, we need to pad the labels too
+ 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()
+
+ # Replace -100 in the labels as we can't decode them
+ 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)
+
+ # Compute metrics
+ 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)
+
+ # Save and upload
+ 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}
+```
+
+And that's it! Once you run this, you'll have a model and results that are pretty similar to the ones we obtained with the `Trainer`.
+
+{/if}
+
+## Using your fine-tuned model
+
+Once you've pushed the model to the Hub, you can play with it either via the inference widget or with a `pipeline` object, as follows:
+
+```python
+from transformers import pipeline
+
+hub_model_id = "huggingface-course/mt5-small-finetuned-amazon-en-es"
+summarizer = pipeline("summarization", model=hub_model_id)
+```
+
+We can feed some examples from the test set (which the model has not seen) to our pipeline to get a feel for the quality of the summaries. First let's implement a simple function to show the review, title, and generated summary together:
+
+```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}'")
+```
+
+Let's take a look at one of the English examples we get:
+
+```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.'
+
+'>>> Title: Not impressed at all... buy something else'
+
+'>>> Summary: Nothing special at all about this product'
+```
+
+This is not too bad! We can see that our model has actually been able to perform _abstractive_ summarization by augmenting parts of the review with new words. And perhaps the coolest aspect of our model is that it is bilingual, so we can also generate summaries of Spanish reviews:
+
+```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'
+
+'>>> Title: Buena literatura para adolescentes'
+
+'>>> Summary: Muy facil de leer'
+```
+
+The summary translates into "Very easy to read" in English, which we can see in this case was extracted directly from the review. Nevertheless, this shows the versatility of the mT5 model and has given you a taste of what it's like to deal with a multilingual corpus!
+
+Next, we'll turn our attention to a slightly more complex task: training a language model from scratch.
diff --git a/chapters/en/chapter7/6.mdx b/chapters/en/chapter7/6.mdx
index 27dc8cbbd..e113b9423 100644
--- a/chapters/en/chapter7/6.mdx
+++ b/chapters/en/chapter7/6.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter7/7.mdx b/chapters/en/chapter7/7.mdx
index d32fc7d8d..2dd81123a 100644
--- a/chapters/en/chapter7/7.mdx
+++ b/chapters/en/chapter7/7.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/en/chapter7/8.mdx b/chapters/en/chapter7/8.mdx
index 0ac5ccc02..dcda455a5 100644
--- a/chapters/en/chapter7/8.mdx
+++ b/chapters/en/chapter7/8.mdx
@@ -1,5 +1,10 @@
# Mastering NLP
+
+
If you've made it this far in the course, congratulations -- you now have all the knowledge and tools you need to tackle (almost) any NLP task with 🤗 Transformers and the Hugging Face ecosystem!
We have seen a lot of different data collators, so we made this little video to help you find which one to use for each task:
diff --git a/chapters/en/chapter7/9.mdx b/chapters/en/chapter7/9.mdx
index 1763e56ed..e6da1f801 100644
--- a/chapters/en/chapter7/9.mdx
+++ b/chapters/en/chapter7/9.mdx
@@ -4,6 +4,11 @@
# End-of-chapter quiz
+
+
Let's test what you learned in this chapter!
### 1. Which of the following tasks can be framed as a token classification problem?
diff --git a/chapters/en/chapter8/1.mdx b/chapters/en/chapter8/1.mdx
index 5a14a567f..644e438a1 100644
--- a/chapters/en/chapter8/1.mdx
+++ b/chapters/en/chapter8/1.mdx
@@ -1,5 +1,10 @@
# Introduction
+
+
Now that you know how to tackle the most common NLP tasks with 🤗 Transformers, you should be able to get started on your own projects! In this chapter we will explore what to do when you hit a problem. You'll learn how to successfully debug your code or your training, and how to ask the community for help if you don't manage to solve the problem by yourself. And if you think you've found a bug in one of the Hugging Face libraries, we'll show you the best way to report it so that the issue is resolved as quickly as possible.
More precisely, in this chapter you will learn:
diff --git a/chapters/en/chapter8/2.mdx b/chapters/en/chapter8/2.mdx
index b366666fc..20dac3a77 100644
--- a/chapters/en/chapter8/2.mdx
+++ b/chapters/en/chapter8/2.mdx
@@ -1,8 +1,8 @@
# What to do when you get an error
-
diff --git a/chapters/en/chapter8/3.mdx b/chapters/en/chapter8/3.mdx
index 46a1564d0..0dd7c2ec3 100644
--- a/chapters/en/chapter8/3.mdx
+++ b/chapters/en/chapter8/3.mdx
@@ -1,8 +1,8 @@
# Asking for help on the forums
-
diff --git a/chapters/en/chapter8/4.mdx b/chapters/en/chapter8/4.mdx
index 54232cdc9..8d2306321 100644
--- a/chapters/en/chapter8/4.mdx
+++ b/chapters/en/chapter8/4.mdx
@@ -2,9 +2,9 @@
# Debugging the training pipeline
-
diff --git a/chapters/en/chapter8/4_tf.mdx b/chapters/en/chapter8/4_tf.mdx
index 6a241216d..a9f595b48 100644
--- a/chapters/en/chapter8/4_tf.mdx
+++ b/chapters/en/chapter8/4_tf.mdx
@@ -2,9 +2,9 @@
# Debugging the training pipeline
-
diff --git a/chapters/en/chapter8/5.mdx b/chapters/en/chapter8/5.mdx
index f58cc94b9..51f71c15e 100644
--- a/chapters/en/chapter8/5.mdx
+++ b/chapters/en/chapter8/5.mdx
@@ -1,8 +1,8 @@
# How to write a good issue
-
diff --git a/chapters/en/chapter8/6.mdx b/chapters/en/chapter8/6.mdx
index 3db86f7ae..2c8b97712 100644
--- a/chapters/en/chapter8/6.mdx
+++ b/chapters/en/chapter8/6.mdx
@@ -1,5 +1,10 @@
# Part 2 completed!
+
+
Congratulations, you've made it through the second part of the course! We're actively working on the third one, so subscribe to our [newsletter](https://huggingface.curated.co/) to make sure you don't miss its release.
You should now be able to tackle a range of NLP tasks, and fine-tune or pretrain a model on them. Don't forget to share your results with the community on the [Model Hub](https://huggingface.co/models).
diff --git a/chapters/en/chapter8/7.mdx b/chapters/en/chapter8/7.mdx
index 9d29e8fcb..219328edd 100644
--- a/chapters/en/chapter8/7.mdx
+++ b/chapters/en/chapter8/7.mdx
@@ -2,6 +2,11 @@
# End-of-chapter quiz
+
+
Let's test what you learned in this chapter!
### 1. In which order should you read a Python traceback?
diff --git a/chapters/en/chapter9/1.mdx b/chapters/en/chapter9/1.mdx
index edfcb13f9..74ea8a0a3 100644
--- a/chapters/en/chapter9/1.mdx
+++ b/chapters/en/chapter9/1.mdx
@@ -1,5 +1,10 @@
# Introduction to Gradio
+
+
In this chapter we will be learning about how to build **interactive demos** for your machine learning models.
Why build a demo or a GUI for your machine learning model in the first place? Demos allow:
diff --git a/chapters/en/chapter9/2.mdx b/chapters/en/chapter9/2.mdx
index d6445763a..3cf23d896 100644
--- a/chapters/en/chapter9/2.mdx
+++ b/chapters/en/chapter9/2.mdx
@@ -1,8 +1,8 @@
# Building your first demo
-
diff --git a/chapters/en/chapter9/3.mdx b/chapters/en/chapter9/3.mdx
index be34af06d..bd829ec95 100644
--- a/chapters/en/chapter9/3.mdx
+++ b/chapters/en/chapter9/3.mdx
@@ -1,8 +1,8 @@
# Understanding the Interface class
-
diff --git a/chapters/en/chapter9/4.mdx b/chapters/en/chapter9/4.mdx
index ad8b4714a..fc31404d0 100644
--- a/chapters/en/chapter9/4.mdx
+++ b/chapters/en/chapter9/4.mdx
@@ -1,8 +1,8 @@
# Sharing demos with others
-
diff --git a/chapters/en/chapter9/5.mdx b/chapters/en/chapter9/5.mdx
index 31c796ce6..b7c727a7f 100644
--- a/chapters/en/chapter9/5.mdx
+++ b/chapters/en/chapter9/5.mdx
@@ -1,8 +1,8 @@
# Integrations with the Hugging Face Hub
-
diff --git a/chapters/en/chapter9/6.mdx b/chapters/en/chapter9/6.mdx
index 28a8c17f4..ce4ee3ecc 100644
--- a/chapters/en/chapter9/6.mdx
+++ b/chapters/en/chapter9/6.mdx
@@ -1,8 +1,8 @@
# Advanced Interface features
-
diff --git a/chapters/en/chapter9/7.mdx b/chapters/en/chapter9/7.mdx
index 7d6de8c1b..eb0fd3f61 100644
--- a/chapters/en/chapter9/7.mdx
+++ b/chapters/en/chapter9/7.mdx
@@ -1,8 +1,8 @@
# Introduction to Gradio Blocks
-
diff --git a/chapters/en/chapter9/8.mdx b/chapters/en/chapter9/8.mdx
index 9380d2e50..1c6dd04c2 100644
--- a/chapters/en/chapter9/8.mdx
+++ b/chapters/en/chapter9/8.mdx
@@ -1,5 +1,10 @@
# Gradio, check!
+
+
This wraps up the chapter on building cool ML demos with Gradio - we hope you enjoyed it! To recap, in this chapter we learned:
- How to create Gradio demos with the high-level `Interface` API, and how to configure different input and output modalities.
diff --git a/chapters/en/chapter9/9.mdx b/chapters/en/chapter9/9.mdx
index 7d2dfb8db..0bb98fca8 100644
--- a/chapters/en/chapter9/9.mdx
+++ b/chapters/en/chapter9/9.mdx
@@ -2,6 +2,11 @@
# End-of-chapter quiz
+
+
Let's test what you learned in this chapter!
### 1. What can you use Gradio to do?
diff --git a/chapters/es/chapter1/1.mdx b/chapters/es/chapter1/1.mdx
index fc53d0700..5c82e6bba 100644
--- a/chapters/es/chapter1/1.mdx
+++ b/chapters/es/chapter1/1.mdx
@@ -1,5 +1,10 @@
# Introducción
+
+
## ¡Te damos la bienvenida al curso de 🤗!
diff --git a/chapters/es/chapter1/10.mdx b/chapters/es/chapter1/10.mdx
index 18e0262ae..6749eeee5 100644
--- a/chapters/es/chapter1/10.mdx
+++ b/chapters/es/chapter1/10.mdx
@@ -2,6 +2,11 @@
# Quiz de final de capítulo
+
+
¡Este capítulo cubrió una gran variedad de temas! No te preocupes si no entendiste todos los detalles; los siguientes capítulos te ayudarán a entender cómo funcionan las cosas detrás de cámaras.
Por ahora, ¡revisemos lo que aprendiste en este capítulo!
diff --git a/chapters/es/chapter1/2.mdx b/chapters/es/chapter1/2.mdx
index 662379538..e799de435 100644
--- a/chapters/es/chapter1/2.mdx
+++ b/chapters/es/chapter1/2.mdx
@@ -1,5 +1,10 @@
# Procesamiento de Lenguaje Natural
+
+
Antes de ver los Transformadores, hagamos una revisión rápida de qué es el procesamiento de lenguaje natural y por qué nos interesa.
## ¿Qué es PLN?
diff --git a/chapters/es/chapter1/3.mdx b/chapters/es/chapter1/3.mdx
index c725bb68d..539f62dca 100644
--- a/chapters/es/chapter1/3.mdx
+++ b/chapters/es/chapter1/3.mdx
@@ -1,8 +1,8 @@
# Transformadores, ¿qué pueden hacer?
-
diff --git a/chapters/es/chapter1/4.mdx b/chapters/es/chapter1/4.mdx
index 3f28b352d..7d6b958b0 100644
--- a/chapters/es/chapter1/4.mdx
+++ b/chapters/es/chapter1/4.mdx
@@ -1,5 +1,10 @@
# ¿Cómo funcionan los Transformadores?
+
+
En esta sección, daremos una mirada de alto nivel a la arquitectura de los Transformadores.
## Un poco de historia sobre los Transformadores
diff --git a/chapters/es/chapter1/5.mdx b/chapters/es/chapter1/5.mdx
index 0d587219f..b06cb7b75 100644
--- a/chapters/es/chapter1/5.mdx
+++ b/chapters/es/chapter1/5.mdx
@@ -1,5 +1,10 @@
# Modelos de codificadores
+
+
Los modelos de codificadores usan únicamente el codificador del Transformador. En cada etapa, las capas de atención pueden acceder a todas las palabras de la oración inicial. Estos modelos se caracterizan generalmente por tener atención "bidireccional" y se suelen llamar modelos *auto-encoding*.
diff --git a/chapters/es/chapter1/6.mdx b/chapters/es/chapter1/6.mdx
index 803ba35bb..b4462cd8e 100644
--- a/chapters/es/chapter1/6.mdx
+++ b/chapters/es/chapter1/6.mdx
@@ -1,5 +1,10 @@
# Modelos de decodificadores
+
+
Los modelos de decodificadores usan únicamente el decodificador del Transformador. En cada etapa, para una palabra dada las capas de atención pueden acceder sólamente a las palabras que se ubican antes en la oración. Estos modelos se suelen llamar modelos *auto-regressive*.
diff --git a/chapters/es/chapter1/8.mdx b/chapters/es/chapter1/8.mdx
index de17dfed0..aae4180a9 100644
--- a/chapters/es/chapter1/8.mdx
+++ b/chapters/es/chapter1/8.mdx
@@ -1,8 +1,8 @@
# Sesgos y limitaciones
-
diff --git a/chapters/es/chapter1/9.mdx b/chapters/es/chapter1/9.mdx
index 71eb8b4f6..49228f34f 100644
--- a/chapters/es/chapter1/9.mdx
+++ b/chapters/es/chapter1/9.mdx
@@ -1,5 +1,10 @@
# Resumen
+
+
En este capítulo viste cómo abordar diferentes tareas de PLN usando la función de alto nivel `pipeline()` de 🤗 Transformers. También viste como buscar modelos en el Hub, así como usar la API de Inferencia para probar los modelos directamente en tu navegador.
Discutimos brevemente el funcionamiento de los Transformadores y hablamos sobre la importancia de la transferencia de aprendizaje y el ajuste. Un aspecto clave es que puedes usar la arquitectura completa o sólo el codificador o decodificador, dependiendo de qué tipo de tarea quieres resolver. La siguiente tabla resume lo anterior:
diff --git a/chapters/es/chapter2/4.mdx b/chapters/es/chapter2/4.mdx
index 7a3b40160..b53535f03 100644
--- a/chapters/es/chapter2/4.mdx
+++ b/chapters/es/chapter2/4.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/es/chapter2/5.mdx b/chapters/es/chapter2/5.mdx
index 606f9bc89..366b53839 100644
--- a/chapters/es/chapter2/5.mdx
+++ b/chapters/es/chapter2/5.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/es/chapter3/1.mdx b/chapters/es/chapter3/1.mdx
index 16ae3c5d2..2f0398087 100644
--- a/chapters/es/chapter3/1.mdx
+++ b/chapters/es/chapter3/1.mdx
@@ -2,6 +2,11 @@
# Introducción
+
+
En el [Capítulo 2](/course/chapter2) exploramos como usar los tokenizadores y modelos preentrenados para realizar predicciones. Pero qué tal si deseas ajustar un modelo preentrenado con tu propio conjunto de datos ?
{#if fw === 'pt'}
diff --git a/chapters/es/chapter3/2.mdx b/chapters/es/chapter3/2.mdx
index 3e7e3d91a..df59b8a2c 100644
--- a/chapters/es/chapter3/2.mdx
+++ b/chapters/es/chapter3/2.mdx
@@ -4,18 +4,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/es/chapter3/4.mdx b/chapters/es/chapter3/4.mdx
index b5bd3f7cf..96e07b050 100644
--- a/chapters/es/chapter3/4.mdx
+++ b/chapters/es/chapter3/4.mdx
@@ -1,8 +1,8 @@
# Un entrenamiento completo
-
diff --git a/chapters/es/chapter8/1.mdx b/chapters/es/chapter8/1.mdx
index 4ebde8120..262a46d4f 100644
--- a/chapters/es/chapter8/1.mdx
+++ b/chapters/es/chapter8/1.mdx
@@ -1,5 +1,10 @@
# Introducción
+
+
Ahora sabes cómo abordar las tareas de PLN más comunes con la librería 🤗 Transformers, ¡deberías ser capaz de iniciar tus propios proyectos! En este capítulo exploraremos qué debes hacer cuando te encuentras con un problema. Aprenderás a cómo depurar (debug) exitosamente tu código o tu entrenamiento, y cómo solicitar ayuda si no consigues resolver el problema por ti mismo. Además, si crees que has encontrado un error (bug) en una de las librerías de Hugging Face, te indicaremos la mejor manera de reportarlo para que se resuelva tan pronto como sea posible.
Más precisamente, en este capítulo aprenderás:
diff --git a/chapters/es/chapter8/2.mdx b/chapters/es/chapter8/2.mdx
index 34a4e9392..0e7553eae 100644
--- a/chapters/es/chapter8/2.mdx
+++ b/chapters/es/chapter8/2.mdx
@@ -1,8 +1,8 @@
# ¿Qué hacer cuando se produce un error?
-
diff --git a/chapters/fa/chapter1/1.mdx b/chapters/fa/chapter1/1.mdx
index 5b826a280..88d60c612 100644
--- a/chapters/fa/chapter1/1.mdx
+++ b/chapters/fa/chapter1/1.mdx
@@ -1,6 +1,11 @@
# مقدمه
+
+
## به دوره آموزشی هاگینگفِیس خوش آمدید
diff --git a/chapters/fa/chapter1/2.mdx b/chapters/fa/chapter1/2.mdx
index 3342d6e47..9b3962c1c 100644
--- a/chapters/fa/chapter1/2.mdx
+++ b/chapters/fa/chapter1/2.mdx
@@ -1,6 +1,11 @@
# پردازش زبان طبیعی
+
+
قبل از اینکه به سراغ مدلهای ترنسفومر برویم، بیایید نگاهی سریع بیاندازیم به اینکه پردازش زبان طبیعی[^1] چیست و چرا برای ما حائز اهمیت است.
## NLP چیست؟
diff --git a/chapters/fa/chapter2/1.mdx b/chapters/fa/chapter2/1.mdx
index 5d61827cf..ad6bdd22e 100644
--- a/chapters/fa/chapter2/1.mdx
+++ b/chapters/fa/chapter2/1.mdx
@@ -2,6 +2,11 @@
# مقدمه
+
+
همان طور که در [فصل اول](/course/chapter1) دیدید، مدلهای ترنسفورمر معمولا بسیار بزرگ هستند. با داشتن میلیونها یا حتی دهها میلیارد پارامتر، تعلیم و بکارگیری این مدلها کار بسیار پیچیدهای است. علاوه بر این، تقریبا هر روز مدلهای جدیدی عرضه میشوند که هرکدام شیوه پیادهسازی خود را دارند و امتحان کردن تمام آنها کار آسانی نیست.
کتابخانه ترنسفومرهای هاگینگفِیس برای حل این مشکل تولید شده است. هدف آن، ارائه یک API واحد برای بارگذاری، تعلیم و ذخیرهسازی مدلهای ترنسفورمر است. ویژگی های اصلی این کتابخانه از این قرار است:
diff --git a/chapters/fa/chapter2/2.mdx b/chapters/fa/chapter2/2.mdx
index 71abc5e16..9ae1d64e2 100644
--- a/chapters/fa/chapter2/2.mdx
+++ b/chapters/fa/chapter2/2.mdx
@@ -5,18 +5,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/fa/chapter2/3.mdx b/chapters/fa/chapter2/3.mdx
index 0155c5634..eace2dc12 100644
--- a/chapters/fa/chapter2/3.mdx
+++ b/chapters/fa/chapter2/3.mdx
@@ -5,18 +5,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/fa/chapter3/1.mdx b/chapters/fa/chapter3/1.mdx
index b3f520423..cf96cdbf8 100644
--- a/chapters/fa/chapter3/1.mdx
+++ b/chapters/fa/chapter3/1.mdx
@@ -4,6 +4,11 @@
# مقدمه
+
+
در [فصل ۲](/course/chapter2) نحوه استفاده از توکِنایزرها و مدلهای از پیش تعلیم دیده را جهت انجام پیشبینیهای جدید بررسی کردیم. اما چگونه میتوانید یک مدل از پیش تعلیم دیده را خودتان کوک کنید؟
{#if fw === 'pt'}
diff --git a/chapters/fa/chapter3/2.mdx b/chapters/fa/chapter3/2.mdx
index 092a92b30..deabbc5f0 100644
--- a/chapters/fa/chapter3/2.mdx
+++ b/chapters/fa/chapter3/2.mdx
@@ -6,18 +6,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/fa/chapter4/1.mdx b/chapters/fa/chapter4/1.mdx
index b071b63df..7f6995ac0 100644
--- a/chapters/fa/chapter4/1.mdx
+++ b/chapters/fa/chapter4/1.mdx
@@ -1,6 +1,11 @@
# هاب هاگینگفِیس
+
+
[هاب هاگینگفِیس](https://huggingface.co/) –- وبسایت اصلی ما –- پلتفرمی مرکزی است که به همه امکان مشارکت، کشف و استفاده از جدیدترین و بهترین مدلها و دیتاسِتها را میدهد. این هاب طیف گستردهای از مدلها را در خود جای داده، که بالغ بر ۱۰ هزار مورد از آنها در دسترس عموم قرار دارد. در این فصل بر روی مدلها متمرکز شده و در فصل ۵ نیز نگاهی به دیتاسِتها خواهیم داشت.
مدلهای موجود در هاب، محدود به ترنسفورمرهای هاگینگفِیس یا حتی NLP نیستند. از جمله آنها میتوان مدلهایی از قبیل [Flair](https://github.com/flairNLP/flair) و [AllenNLP](https://github.com/allenai/allennlp) برای پردازش زبان طبیعی، [Asteroid](https://github.com/asteroid-team/asteroid) و [pyannote](https://github.com/pyannote/pyannote-audio) برای پردازش گفتار، و [timm](https://github.com/rwightman/pytorch-image-models) را برای پردازش تصویر برشمرد.
diff --git a/chapters/fa/chapter4/2.mdx b/chapters/fa/chapter4/2.mdx
index 01960357d..2514c1849 100644
--- a/chapters/fa/chapter4/2.mdx
+++ b/chapters/fa/chapter4/2.mdx
@@ -5,18 +5,18 @@
{#if fw === 'pt'}
-
{:else}
-
diff --git a/chapters/fr/chapter1/1.mdx b/chapters/fr/chapter1/1.mdx
index a0ff68898..6dd1423c5 100644
--- a/chapters/fr/chapter1/1.mdx
+++ b/chapters/fr/chapter1/1.mdx
@@ -1,55 +1,60 @@
-# Introduction
-
-## Bienvenue au cours 🤗 !
-
-
-
-Ce cours vous apprendra à utiliser les bibliothèques de NLP de l'écosystème [Hugging Face](https://huggingface.co/) : [🤗 *Transformers*](https://github.com/huggingface/transformers), [🤗 *Datasets*](https://github.com/huggingface/datasets), [🤗 *Tokenizers*](https://github.com/huggingface/tokenizers) et [🤗 *Accelerate*](https://github.com/huggingface/accelerate), ainsi que le [*Hub*](https://huggingface.co/models). C'est totalement gratuit et sans publicité.
-
-## À quoi s'attendre ?
-
-Voici un bref aperçu du cours :
-
-
-
-
-
-
-- Les chapitres 1 à 4 présentent les principaux concepts de la bibliothèque 🤗 *Transformers*. À la fin de ce chapitre, vous serez familier avec le fonctionnement des *transformers* et vous saurez comment utiliser un modèle présent sur le [*Hub*](https://huggingface.co/models), le *finetuner* sur un jeu de données, et partager vos résultats sur le *Hub* !
-- Les chapitres 5 à 8 présentent les bases des librairies 🤗 *Datasets* et 🤗 *Tokenizers* ainsi qu'une découverte des problèmes classiques de NLP. À la fin de ce chapitre, vous serez capable de résoudre les problèmes de NLP les plus communs par vous-même.
-- Les chapitres 9 à 12 proposent d'aller plus loin et d'explorer comment les *transformers* peuvent être utilisés pour résoudre des problèmes de traitement de la parole et de vision par ordinateur. En suivant ces chapitres, vous apprendrez à construire et à partager vos modèles via des démonstrateurs, et vous serez capable d'optimiser ces modèles pour des environnements de production. Enfin, vous serez prêt à appliquer 🤗 *Transformers* à (presque) n'importe quel problème d'apprentissage automatique !
-
-Ce cours :
-
-* requiert un bon niveau en Python,
-* se comprend mieux si vous avez déjà suivi un cours d'introduction à l'apprentissage profond comme [fast.ai's](https://www.fast.ai/), [*Practical Deep Learning for Coders*](https://course.fast.ai/) ou un des cours développés par [*DeepLearning.AI*](https://www.deeplearning.ai/),
-* n'attend pas une connaissance appronfondie de [PyTorch](https://pytorch.org/) ou de [TensorFlow](https://www.tensorflow.org/), bien qu'être familiarisé avec l'un d'entre eux peut aider.
-
-Après avoir terminé ce cours, nous vous recommandons de suivre la [Spécialisation en NLP](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh) dispensée par DeepLearning.AI, qui couvre une grande partie des modèles traditionnels de NLP comme le Bayésien naïf et les LSTMs qui sont importants à connaître!
-
-## Qui sommes-nous ?
-
-À propos des auteurs de ce cours :
-
-*Abubakar Abid** a obtenu son doctorat à Stanford en apprentissage automatique appliqué. Pendant son doctorat, il a fondé [Gradio](https://github.com/gradio-app/gradio), une bibliothèque Python open-source qui a été utilisée pour construire plus de 600 000 démos d'apprentissage automatique. Gradio a été rachetée par Hugging Face, où Abubakar occupe désormais le poste de responsable de l'équipe d'apprentissage automatique.
-
-**Matthew Carrigan** est ingénieur en apprentissage machine chez Hugging Face. Il vit à Dublin en Irlande. Il a travaillé auparavant comme ingénieur en apprentissage machine chez Parse.ly et avant cela comme chercheur postdoctoral au Trinity College Dublin. Il ne croit pas que nous arrivions à l'*AGI* en mettant à l'échelle les architectures existantes mais a tout de même beaucoup d'espoir dans l'immortalité des robots.
-
-**Lysandre Debut** est ingénieur en apprentissage machine chez Hugging Face et a travaillé sur la bibliothèque 🤗 *Transformers* depuis les premières phases de développement. Son but est de rendre le NLP accessible à tous en développant des outils disposant d'une API très simple.
-
-**Sylvain Gugger** est ingénieur recherche chez Hugging Face et un des principaux responsables de la bibliothèque 🤗 *Transformers*. Avant cela, il était chercheur en en apprentissage machine chez fast.ai et a écrit le livre [*Deep Learning for Coders with fastai and PyTorch*](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/) avec Jeremy Howard. Son but est de rendre l'apprentissage profond plus accessible, en développant et en améliorant des techniques permettant aux modèles d'apprendre rapidement sur des ressources limitées.
-
-**Dawood Khan** est un ingénieur en apprentissage automatique chez Hugging Face. Il vient de New York et est diplômé de l'Université de New York en informatique. Après avoir travaillé comme ingénieur iOS pendant quelques années, Dawood a quitté son poste pour créer Gradio avec ses cofondateurs. Gradio a finalement été acquis par Hugging Face.
-
-**Merve Noyan** est développeuse *advocate* chez Hugging Face et travaille à la création d'outils et de contenus visant à démocratiser l'apprentissage machine pour tous.
-
-**Lucile Saulnier** est ingénieure en apprentissage machine chez Hugging Face et travaille au développement et à l'implémentation de nombreux outils *open source*. Elle est également activement impliquée dans de nombreux projets de recherche dans le domaine du NLP comme l'entraînement collaboratif de modèles et le projet BigScience.
-
-**Lewis Tunstall** est ingénieur en apprentissage machine chez Hugging Face et dévoué au développement d'outils open source avec la volonté de les rendre accessibles à une communauté plus large. Il est également co-auteur du livre [*Natural Language Processing with Transformers*](https://www.oreilly.com/library/view/natural-language-processing/9781098103231/).
-
-**Leandro von Werra** est ingénieur en apprentissage machine dans l'équipe *open source* d'Hugging Face et également co-auteur du livre [*Natural Language Processing with Transformers*](https://www.oreilly.com/library/view/natural-language-processing/9781098103231/). Il a plusieurs années d'expérience dans l'industrie où il a pu déployer des projets de NLP en production et travailler sur toutes les étapes clefs du déploiement.
-
-Êtes-vous prêt à commencer ? Dans ce chapitre, vous apprendrez :
-* à utiliser la fonction `pipeline()` pour résoudre des problèmes de NLP comme la génération de texte et la classification,
-* l'architecture d'un *transformer*,
-* comment faire la distinction entre les différentes architectures d'encodeur, de décodeur et d'encodeur-décodeur ainsi que leurs différents cas d'usage.
+# Introduction
+
+
+
+## Bienvenue au cours 🤗 !
+
+
+
+Ce cours vous apprendra à utiliser les bibliothèques de NLP de l'écosystème [Hugging Face](https://huggingface.co/) : [🤗 *Transformers*](https://github.com/huggingface/transformers), [🤗 *Datasets*](https://github.com/huggingface/datasets), [🤗 *Tokenizers*](https://github.com/huggingface/tokenizers) et [🤗 *Accelerate*](https://github.com/huggingface/accelerate), ainsi que le [*Hub*](https://huggingface.co/models). C'est totalement gratuit et sans publicité.
+
+## À quoi s'attendre ?
+
+Voici un bref aperçu du cours :
+
+
+
+
+
+
+- Les chapitres 1 à 4 présentent les principaux concepts de la bibliothèque 🤗 *Transformers*. À la fin de ce chapitre, vous serez familier avec le fonctionnement des *transformers* et vous saurez comment utiliser un modèle présent sur le [*Hub*](https://huggingface.co/models), le *finetuner* sur un jeu de données, et partager vos résultats sur le *Hub* !
+- Les chapitres 5 à 8 présentent les bases des librairies 🤗 *Datasets* et 🤗 *Tokenizers* ainsi qu'une découverte des problèmes classiques de NLP. À la fin de ce chapitre, vous serez capable de résoudre les problèmes de NLP les plus communs par vous-même.
+- Les chapitres 9 à 12 proposent d'aller plus loin et d'explorer comment les *transformers* peuvent être utilisés pour résoudre des problèmes de traitement de la parole et de vision par ordinateur. En suivant ces chapitres, vous apprendrez à construire et à partager vos modèles via des démonstrateurs, et vous serez capable d'optimiser ces modèles pour des environnements de production. Enfin, vous serez prêt à appliquer 🤗 *Transformers* à (presque) n'importe quel problème d'apprentissage automatique !
+
+Ce cours :
+
+* requiert un bon niveau en Python,
+* se comprend mieux si vous avez déjà suivi un cours d'introduction à l'apprentissage profond comme [fast.ai's](https://www.fast.ai/), [*Practical Deep Learning for Coders*](https://course.fast.ai/) ou un des cours développés par [*DeepLearning.AI*](https://www.deeplearning.ai/),
+* n'attend pas une connaissance appronfondie de [PyTorch](https://pytorch.org/) ou de [TensorFlow](https://www.tensorflow.org/), bien qu'être familiarisé avec l'un d'entre eux peut aider.
+
+Après avoir terminé ce cours, nous vous recommandons de suivre la [Spécialisation en NLP](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh) dispensée par DeepLearning.AI, qui couvre une grande partie des modèles traditionnels de NLP comme le Bayésien naïf et les LSTMs qui sont importants à connaître!
+
+## Qui sommes-nous ?
+
+À propos des auteurs de ce cours :
+
+*Abubakar Abid** a obtenu son doctorat à Stanford en apprentissage automatique appliqué. Pendant son doctorat, il a fondé [Gradio](https://github.com/gradio-app/gradio), une bibliothèque Python open-source qui a été utilisée pour construire plus de 600 000 démos d'apprentissage automatique. Gradio a été rachetée par Hugging Face, où Abubakar occupe désormais le poste de responsable de l'équipe d'apprentissage automatique.
+
+**Matthew Carrigan** est ingénieur en apprentissage machine chez Hugging Face. Il vit à Dublin en Irlande. Il a travaillé auparavant comme ingénieur en apprentissage machine chez Parse.ly et avant cela comme chercheur postdoctoral au Trinity College Dublin. Il ne croit pas que nous arrivions à l'*AGI* en mettant à l'échelle les architectures existantes mais a tout de même beaucoup d'espoir dans l'immortalité des robots.
+
+**Lysandre Debut** est ingénieur en apprentissage machine chez Hugging Face et a travaillé sur la bibliothèque 🤗 *Transformers* depuis les premières phases de développement. Son but est de rendre le NLP accessible à tous en développant des outils disposant d'une API très simple.
+
+**Sylvain Gugger** est ingénieur recherche chez Hugging Face et un des principaux responsables de la bibliothèque 🤗 *Transformers*. Avant cela, il était chercheur en en apprentissage machine chez fast.ai et a écrit le livre [*Deep Learning for Coders with fastai and PyTorch*](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/) avec Jeremy Howard. Son but est de rendre l'apprentissage profond plus accessible, en développant et en améliorant des techniques permettant aux modèles d'apprendre rapidement sur des ressources limitées.
+
+**Dawood Khan** est un ingénieur en apprentissage automatique chez Hugging Face. Il vient de New York et est diplômé de l'Université de New York en informatique. Après avoir travaillé comme ingénieur iOS pendant quelques années, Dawood a quitté son poste pour créer Gradio avec ses cofondateurs. Gradio a finalement été acquis par Hugging Face.
+
+**Merve Noyan** est développeuse *advocate* chez Hugging Face et travaille à la création d'outils et de contenus visant à démocratiser l'apprentissage machine pour tous.
+
+**Lucile Saulnier** est ingénieure en apprentissage machine chez Hugging Face et travaille au développement et à l'implémentation de nombreux outils *open source*. Elle est également activement impliquée dans de nombreux projets de recherche dans le domaine du NLP comme l'entraînement collaboratif de modèles et le projet BigScience.
+
+**Lewis Tunstall** est ingénieur en apprentissage machine chez Hugging Face et dévoué au développement d'outils open source avec la volonté de les rendre accessibles à une communauté plus large. Il est également co-auteur du livre [*Natural Language Processing with Transformers*](https://www.oreilly.com/library/view/natural-language-processing/9781098103231/).
+
+**Leandro von Werra** est ingénieur en apprentissage machine dans l'équipe *open source* d'Hugging Face et également co-auteur du livre [*Natural Language Processing with Transformers*](https://www.oreilly.com/library/view/natural-language-processing/9781098103231/). Il a plusieurs années d'expérience dans l'industrie où il a pu déployer des projets de NLP en production et travailler sur toutes les étapes clefs du déploiement.
+
+Êtes-vous prêt à commencer ? Dans ce chapitre, vous apprendrez :
+* à utiliser la fonction `pipeline()` pour résoudre des problèmes de NLP comme la génération de texte et la classification,
+* l'architecture d'un *transformer*,
+* comment faire la distinction entre les différentes architectures d'encodeur, de décodeur et d'encodeur-décodeur ainsi que leurs différents cas d'usage.
diff --git a/chapters/fr/chapter1/10.mdx b/chapters/fr/chapter1/10.mdx
index d48d06003..889612715 100644
--- a/chapters/fr/chapter1/10.mdx
+++ b/chapters/fr/chapter1/10.mdx
@@ -1,258 +1,263 @@
-
-
-# Quiz de fin de chapitre
-
-Ce chapitre a couvert un grand nombre de notions ! Ne vous inquiétez pas si vous n'avez pas compris tous les détails, les chapitres suivants vous aideront à comprendre comment les choses fonctionnent concrètement.
-
-Mais avant d'aller plus loin, prenons un instant pour voir ce que vous avez appris dans ce chapitre !
-
-
-### 1. Explorez le *Hub* et cherchez le modèle `roberta-large-mnli`. Quelle tâche accomplit-il ?
-
-
-
page roberta-large-mnli."
- },
- {
- text: "Classification de texte",
- explain: "Pour être plus précis, il classifie si deux phrases sont logiquement liées entre elles parmis trois possibilités (contradiction, neutre, lien). Il s'agit d'une tâche aussi appelée inference de langage naturel.",
- correct: true
- },
- {
- text: "Génération de texte",
- explain: "Regardez à nouveau sur la page roberta-large-mnli."
- }
- ]}
-/>
-
-### 2. Que renvoie le code suivant ?
-
-```py
-from transformers import pipeline
-
-ner = pipeline("ner", grouped_entities=True)
-ner(
- "My name is Sylvain and I work at Hugging Face in Brooklyn."
-) # Je m'appelle Sylvain et je travaille à Hugging Face à Brooklyn.
-```
-
-d'analyse de sentiment (sentiment-analysis dans la documentation d'Hugging-Face)."
- },
- {
- text: "Il renvoie un texte généré qui complète cette phrase.",
- explain: "Cela correspondrait au pipeline de génération de texte
(text-generation dans la documentation d'Hugging-Face)."
- },
- {
- text: "Il renvoie les entités nommées dans cette phrase, telles que les personnes, les organisations ou lieux.",
- explain: "De plus, avec grouped_entities=True
, cela regroupe les mots appartenant à la même entité, comme par exemple \"Hugging Face\".",
- correct: true
- }
- ]}
-/>
-
-### 3. Que remplace « ... » dans ce code ?
-
-```py
-from transformers import pipeline
-
-filler = pipeline("fill-mask", model="bert-base-cased")
-result = filler("...")
-```
-
- has been waiting for you. # Ce <mask> vous attend.",
- explain: "Regardez la description du modèle bert-base-cased
et essayez de trouver votre erreur."
- },
- {
- text: "This [MASK] has been waiting for you. # Ce [MASK] vous attend.",
- explain: "Le modèle utilise [MASK] comme mot-masque.",
- correct: true
- },
- {
- text: "This man has been waiting for you. # Cet homme vous attend.",
- explain: "Ce pipeline permet de remplacer les mot manquants donc il a besoin d'un mot-masque."
- }
- ]}
-/>
-
-### 4. Pourquoi ce code ne fonctionne-t-il pas ?
-
-```py
-from transformers import pipeline
-
-classifier = pipeline("zero-shot-classification")
-result = classifier(
- "This is a course about the Transformers library"
-) # C'est un cours sur la bibliothèque Transformers
-```
-
-candidate_labels=[...].",
- correct: true
- },
- {
- text: "Ce pipeline nécessite que des phrases soient données, pas juste une phrase.",
- explain: "Bien que ce pipeline puisse prendre une liste de phrases à traiter (comme tous les autres pipelines)."
- },
- {
- text: "La bibliothèque 🤗 Transformers est cassée, comme d'habitude.",
- explain: "Nous n'avons aucun commentaire pour cette réponse !",
- },
- {
- text: "Ce pipeline nécessite des phrases plus longues, celle-ci est trop courte.",
- explain: "Notez que si un texte est très long, il est tronqué par le pipeline."
- }
- ]}
-/>
-
-### 5. Que signifie « apprentissage par transfert » ?
-
-
-
-### 6. Vrai ou faux ? Un modèle de langage n'a généralement pas besoin d'étiquettes pour son pré-entraînement.
-
-
-autosupervisé, ce qui signifie que les étiquettes sont créées automatiquement à partir des données d'entrée (comme prédire le mot suivant ou remplacer des mots masqués).",
- correct: true
- },
- {
- text: "Faux",
- explain: "Ce n'est pas la bonne réponse."
- }
- ]}
-/>
-
-### 7. Sélectionnez la phrase qui décrit le mieux les termes « modèle », « architecture » et « poids ».
-
-
-
-
-### 8. Parmi ces types de modèles, quel est le plus approprié pour générer du texte à partir d'une instruction (*prompt*) ?
-
-
-
-### 9. Parmi ces types de modèles, quel est le plus approprié pour le résumé de texte ?
-
-
-
-### 10. Quel type de modèle utiliseriez-vous pour classifier des entrées de texte en fonction de certains labels ?
-
-
-
-### 11. De quelle source possible peut être le biais observé dans un modèle ?
-
-finetunée d'un modèle pré-entraîné et il a conservé ses biais.",
- explain: "Avec l'apprentissage par transfert, les biais du modèle pré-entraîné perdurent dans le modèle finetuné.",
- correct: true
- },
- {
- text: "Le modèle a été entraîné sur des données qui sont biaisées.",
- explain: "Ceci représente la source de biais la plus évidente mais n'est pas la seule possible.",
- correct: true
- },
- {
- text: "La métrique optimisée lors de l'entraînement du modèle est biaisée.",
- explain: "Une source moins évidente est la façon dont le modèle est entraîné. Votre modèle va de façon aveugle optimiser la métrique que vous avez sélectionnée, sans prendre aucun recul.",
- correct: true
- }
- ]}
-/>
+
+
+# Quiz de fin de chapitre
+
+
+
+Ce chapitre a couvert un grand nombre de notions ! Ne vous inquiétez pas si vous n'avez pas compris tous les détails, les chapitres suivants vous aideront à comprendre comment les choses fonctionnent concrètement.
+
+Mais avant d'aller plus loin, prenons un instant pour voir ce que vous avez appris dans ce chapitre !
+
+
+### 1. Explorez le *Hub* et cherchez le modèle `roberta-large-mnli`. Quelle tâche accomplit-il ?
+
+
+page roberta-large-mnli."
+ },
+ {
+ text: "Classification de texte",
+ explain: "Pour être plus précis, il classifie si deux phrases sont logiquement liées entre elles parmis trois possibilités (contradiction, neutre, lien). Il s'agit d'une tâche aussi appelée inference de langage naturel.",
+ correct: true
+ },
+ {
+ text: "Génération de texte",
+ explain: "Regardez à nouveau sur la page roberta-large-mnli."
+ }
+ ]}
+/>
+
+### 2. Que renvoie le code suivant ?
+
+```py
+from transformers import pipeline
+
+ner = pipeline("ner", grouped_entities=True)
+ner(
+ "My name is Sylvain and I work at Hugging Face in Brooklyn."
+) # Je m'appelle Sylvain et je travaille à Hugging Face à Brooklyn.
+```
+
+d'analyse de sentiment (sentiment-analysis dans la documentation d'Hugging-Face)."
+ },
+ {
+ text: "Il renvoie un texte généré qui complète cette phrase.",
+ explain: "Cela correspondrait au pipeline de génération de texte
(text-generation dans la documentation d'Hugging-Face)."
+ },
+ {
+ text: "Il renvoie les entités nommées dans cette phrase, telles que les personnes, les organisations ou lieux.",
+ explain: "De plus, avec grouped_entities=True
, cela regroupe les mots appartenant à la même entité, comme par exemple \"Hugging Face\".",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. Que remplace « ... » dans ce code ?
+
+```py
+from transformers import pipeline
+
+filler = pipeline("fill-mask", model="bert-base-cased")
+result = filler("...")
+```
+
+ has been waiting for you. # Ce <mask> vous attend.",
+ explain: "Regardez la description du modèle bert-base-cased
et essayez de trouver votre erreur."
+ },
+ {
+ text: "This [MASK] has been waiting for you. # Ce [MASK] vous attend.",
+ explain: "Le modèle utilise [MASK] comme mot-masque.",
+ correct: true
+ },
+ {
+ text: "This man has been waiting for you. # Cet homme vous attend.",
+ explain: "Ce pipeline permet de remplacer les mot manquants donc il a besoin d'un mot-masque."
+ }
+ ]}
+/>
+
+### 4. Pourquoi ce code ne fonctionne-t-il pas ?
+
+```py
+from transformers import pipeline
+
+classifier = pipeline("zero-shot-classification")
+result = classifier(
+ "This is a course about the Transformers library"
+) # C'est un cours sur la bibliothèque Transformers
+```
+
+candidate_labels=[...].",
+ correct: true
+ },
+ {
+ text: "Ce pipeline nécessite que des phrases soient données, pas juste une phrase.",
+ explain: "Bien que ce pipeline puisse prendre une liste de phrases à traiter (comme tous les autres pipelines)."
+ },
+ {
+ text: "La bibliothèque 🤗 Transformers est cassée, comme d'habitude.",
+ explain: "Nous n'avons aucun commentaire pour cette réponse !",
+ },
+ {
+ text: "Ce pipeline nécessite des phrases plus longues, celle-ci est trop courte.",
+ explain: "Notez que si un texte est très long, il est tronqué par le pipeline."
+ }
+ ]}
+/>
+
+### 5. Que signifie « apprentissage par transfert » ?
+
+
+
+### 6. Vrai ou faux ? Un modèle de langage n'a généralement pas besoin d'étiquettes pour son pré-entraînement.
+
+
+autosupervisé, ce qui signifie que les étiquettes sont créées automatiquement à partir des données d'entrée (comme prédire le mot suivant ou remplacer des mots masqués).",
+ correct: true
+ },
+ {
+ text: "Faux",
+ explain: "Ce n'est pas la bonne réponse."
+ }
+ ]}
+/>
+
+### 7. Sélectionnez la phrase qui décrit le mieux les termes « modèle », « architecture » et « poids ».
+
+
+
+
+### 8. Parmi ces types de modèles, quel est le plus approprié pour générer du texte à partir d'une instruction (*prompt*) ?
+
+
+
+### 9. Parmi ces types de modèles, quel est le plus approprié pour le résumé de texte ?
+
+
+
+### 10. Quel type de modèle utiliseriez-vous pour classifier des entrées de texte en fonction de certains labels ?
+
+
+
+### 11. De quelle source possible peut être le biais observé dans un modèle ?
+
+finetunée d'un modèle pré-entraîné et il a conservé ses biais.",
+ explain: "Avec l'apprentissage par transfert, les biais du modèle pré-entraîné perdurent dans le modèle finetuné.",
+ correct: true
+ },
+ {
+ text: "Le modèle a été entraîné sur des données qui sont biaisées.",
+ explain: "Ceci représente la source de biais la plus évidente mais n'est pas la seule possible.",
+ correct: true
+ },
+ {
+ text: "La métrique optimisée lors de l'entraînement du modèle est biaisée.",
+ explain: "Une source moins évidente est la façon dont le modèle est entraîné. Votre modèle va de façon aveugle optimiser la métrique que vous avez sélectionnée, sans prendre aucun recul.",
+ correct: true
+ }
+ ]}
+/>
diff --git a/chapters/fr/chapter1/2.mdx b/chapters/fr/chapter1/2.mdx
index ef5803d79..c3b8db6ac 100644
--- a/chapters/fr/chapter1/2.mdx
+++ b/chapters/fr/chapter1/2.mdx
@@ -1,21 +1,26 @@
-# Traitement du langage naturel (NLP pour Natural Language Processing)
-
-Avant de commencer avec les *transformers*, voyons succinctement ce qu'est le traitement du langage naturel et pourquoi il est important.
-
-## Le NLP, qu'est-ce que c'est ?
-
-Le traitement du langage naturel est un domaine de linguistique et d'apprentissage automatique se concentrant sur la compréhension de tout ce qui est lié à la langue humaine. L'objectif des tâches de NLP est non seulement de comprendre individuellement chaque mot, mais aussi de comprendre le contexte associé à l'utilisation de ces mots.
-
-La liste suivante regroupe les tâches de NLP les plus courantes, avec pour chacune quelques exemples :
-
-- **Classification de phrases entières** : analyser le sentiment d'un avis, détecter si un email est un spam, déterminer si une phrase est grammaticalement correcte, déterminer si deux phrases sont logiquement reliées ou non, etc.
-- **Classification de chaque mot d'une phrase** : identifier les composants grammaticaux d'une phrase (nom, verbe, adjectif), identifier les entités nommées (personne, lieu, organisation), etc.
-- **Génération de texte** : compléter le début d'un texte avec un texte généré automatiquement, remplacer les mots manquants ou masqués dans un texte, etc.
-- **Extraction d'une réponse à partir d'un texte** : étant donné une question et un contexte extraire la réponse à la question en fonction des informations fournies par le contexte, etc.
-- **Génération de nouvelles phrases à partir d'un texte** : traduire un texte dans une autre langue, faire le résumé d'un texte, etc.
-
-Le traitement du langage naturel ne se limite pas qu'à la compréhension du texte. Il s'intéresse aussi aux problèmes complexes de reconnaissance de la parole et de vision par ordinateur tels que la génération d'une transcription à partir d'un échantillon audio ou la description d'une image.
-
-## Pourquoi est-ce difficile ?
-
-Les ordinateurs ne traitent pas les informations de la même manière que les humains. Par exemple, lorsque nous lisons la phrase « j'ai faim », nous comprenons très facilement son sens. De même, lorsque nous lisons deux phrases telles que « j'ai faim » et « je suis triste », nous pouvons facilement déterminer s'il existe des similitudes entre elles. Pour les modèles d'apprentissage automatique, ces tâches sont plus difficiles. Le texte doit être traité de manière à permettre au modèle d'apprendre. Et parce que le langage est complexe, nous devons prendre soin de réfléchir à la meilleure façon de faire ce traitement. Il y a eu beaucoup de recherches sur la façon de représenter le texte et nous allons voir quelques-unes de ces méthodes dans le chapitre suivant.
+# Traitement du langage naturel (NLP pour Natural Language Processing)
+
+
+
+Avant de commencer avec les *transformers*, voyons succinctement ce qu'est le traitement du langage naturel et pourquoi il est important.
+
+## Le NLP, qu'est-ce que c'est ?
+
+Le traitement du langage naturel est un domaine de linguistique et d'apprentissage automatique se concentrant sur la compréhension de tout ce qui est lié à la langue humaine. L'objectif des tâches de NLP est non seulement de comprendre individuellement chaque mot, mais aussi de comprendre le contexte associé à l'utilisation de ces mots.
+
+La liste suivante regroupe les tâches de NLP les plus courantes, avec pour chacune quelques exemples :
+
+- **Classification de phrases entières** : analyser le sentiment d'un avis, détecter si un email est un spam, déterminer si une phrase est grammaticalement correcte, déterminer si deux phrases sont logiquement reliées ou non, etc.
+- **Classification de chaque mot d'une phrase** : identifier les composants grammaticaux d'une phrase (nom, verbe, adjectif), identifier les entités nommées (personne, lieu, organisation), etc.
+- **Génération de texte** : compléter le début d'un texte avec un texte généré automatiquement, remplacer les mots manquants ou masqués dans un texte, etc.
+- **Extraction d'une réponse à partir d'un texte** : étant donné une question et un contexte extraire la réponse à la question en fonction des informations fournies par le contexte, etc.
+- **Génération de nouvelles phrases à partir d'un texte** : traduire un texte dans une autre langue, faire le résumé d'un texte, etc.
+
+Le traitement du langage naturel ne se limite pas qu'à la compréhension du texte. Il s'intéresse aussi aux problèmes complexes de reconnaissance de la parole et de vision par ordinateur tels que la génération d'une transcription à partir d'un échantillon audio ou la description d'une image.
+
+## Pourquoi est-ce difficile ?
+
+Les ordinateurs ne traitent pas les informations de la même manière que les humains. Par exemple, lorsque nous lisons la phrase « j'ai faim », nous comprenons très facilement son sens. De même, lorsque nous lisons deux phrases telles que « j'ai faim » et « je suis triste », nous pouvons facilement déterminer s'il existe des similitudes entre elles. Pour les modèles d'apprentissage automatique, ces tâches sont plus difficiles. Le texte doit être traité de manière à permettre au modèle d'apprendre. Et parce que le langage est complexe, nous devons prendre soin de réfléchir à la meilleure façon de faire ce traitement. Il y a eu beaucoup de recherches sur la façon de représenter le texte et nous allons voir quelques-unes de ces méthodes dans le chapitre suivant.
diff --git a/chapters/fr/chapter1/3.mdx b/chapters/fr/chapter1/3.mdx
index 5428aff2b..d10b714e2 100644
--- a/chapters/fr/chapter1/3.mdx
+++ b/chapters/fr/chapter1/3.mdx
@@ -1,381 +1,381 @@
-# Que peuvent faire les transformers ?
-
-
-
-Dans cette section, nous allons voir ce que peuvent faire les *transformers* et utiliser notre premier outil de la bibliothèque 🤗 *Transformers* : la fonction `pipeline()`.
-
-
-👀 Vous voyez ce bouton Open in Colab en haut à droite ? Cliquez dessus pour ouvrir un notebook Colab avec tous les exemples de code de cette section. Ce bouton sera présent dans n'importe quelle section contenant des exemples de code.
-
-Si vous souhaitez exécuter les codes en local, nous vous recommandons de jeter un œil au chapitre configuration.
-
-
-## Les transformers sont partout !
-
-Les *transformers* sont utilisés pour résoudre toute sorte de tâches de NLP comme celles mentionnées dans la section précédente. Voici quelques-unes des entreprises et organisations qui utilisent Hugging Face, les *transformers* et qui contribuent aussi à la communauté en partageant leurs modèles :
-
-
-
-La bibliothèque [🤗 *Transformers*](https://github.com/huggingface/transformers) fournit toutes les fonctionnalités nécessaires pour créer et utiliser les modèles partagés. Le [*Hub*](https://huggingface.co/models) contient des milliers de modèles pré-entraînés que n'importe qui peut télécharger et utiliser. Vous pouvez également transférer vos propres modèles vers le Hub !
-
-
- ⚠️ Le Hub n'est pas limité aux transformers. Tout le monde peut partager n'importe quel modèle ou jeu de données s'il le souhaite ! Créez un compte sur huggingface.co pour bénéficier de toutes les fonctionnalités disponibles !
-
-
-Avant de découvrir en détail comment les *transformers* fonctionnent, nous allons voir quelques exemples de comment ils peuvent être utilisés pour résoudre des problèmes intéressants de NLP.
-
-## Travailler avec les pipelines
-
-
-
-L'outil le plus basique de la bibliothèque 🤗 *Transformers* est la fonction `pipeline()`. Elle relie un modèle avec ses étapes de pré-traitement et de post-traitement, permettant d'entrer n'importe quel texte et d'obtenir une réponse compréhensible :
-
-```python
-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 d'HuggingFace toute ma vie.
-```
-
-```python out
-[{'label': 'POSITIVE', 'score': 0.9598047137260437}]
-```
-
-On peut même passer plusieurs phrases !
-
-```python
-classifier(
- [
- "I've been waiting for a HuggingFace course my whole life.",
- "I hate this so much!",
- ] # « J'ai attendu un cours d'HuggingFace toute ma vie. », « Je déteste tellement ça ! »
-)
-```
-
-```python out
-[{'label': 'POSITIVE', 'score': 0.9598047137260437},
- {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
-```
-
-Par défaut, ce pipeline sélectionne un modèle pré-entraîné qui a été spécifiquement entraîné pour l'analyse de sentiment en anglais. Le modèle est téléchargé et mis en cache lorsque vous créez l'objet `classifier`. Si vous réexécutez la commande, c'est le modèle mis en cache qui sera utilisé et il n'y a pas besoin de télécharger le modèle à nouveau.
-
-Il y a trois étapes principales lorsque vous passez du texte à un pipeline :
-
-1. le texte est prétraité pour qu'il ait un format compréhensible par le modèle,
-2. les données prétraitées sont passées au modèle,
-3. les prédictions du modèle sont post-traitées de sorte que vous puissiez les comprendre.
-
-
-Voici une liste non-exhaustive des [pipelines disponibles](https://huggingface.co/transformers/main_classes/pipelines.html) :
-
-- `feature-extraction` (pour obtenir la représentation vectorielle d'un texte)
-- `fill-mask`
-- `ner` (*named entity recognition* ou reconnaissance d'entités nommées en français)
-- `question-answering`
-- `sentiment-analysis`
-- `summarization`
-- `text-generation`
-- `translation`
-- `zero-shot-classification`
-
-Regardons de plus près certains d'entre eux !
-
-## Zero-shot classification
-
-Nous allons commencer par nous attaquer à une tâche plus difficile où nous devons classer des textes qui n'ont pas été annotés. C'est un scénario très répandu dans les projets réels car l'annotation de textes est généralement longue et nécessite parfois une expertise dans un domaine. Pour ce cas d'usage, le pipeline `zero-shot-classification` est très puissant : il vous permet de spécifier les labels à utiliser pour la classification, de sorte que vous n'ayez pas à vous soucier des labels du modèle pré-entraîné. Nous avons déjà vu comment le modèle peut classer un texte comme positif ou négatif en utilisant ces deux labels mais il peut également classer le texte en utilisant n'importe quel autre ensemble de labels que vous souhaitez.
-
-```python
-from transformers import pipeline
-
-classifier = pipeline("zero-shot-classification")
-classifier(
- "This is a course about the Transformers library",
- # C'est un cours sur la bibliothèque Transformers
- candidate_labels=["education", "politics", "business"],
-)
-```
-
-```python out
-{'sequence': 'This is a course about the Transformers library',
-# C'est un cours sur la bibliothèque Transformers
- 'labels': ['education', 'business', 'politics'],
- 'scores': [0.8445963859558105, 0.111976258456707, 0.043427448719739914]}
-```
-
-Ce pipeline est appelé _zero-shot_ car vous n'avez pas besoin d'entraîner spécifiquement le modèle sur vos données pour l'utiliser. Il peut directement retourner des scores de probabilité pour n'importe quel ensemble de labels que vous choisissez !
-
-
-
-✏️ **Essayez !** Jouez avec vos propres séquences et labels et voyez comment le modèle fonctionne.
-
-
-
-
-## Génération de texte
-
-Maintenant, nous allons voir comment utiliser un pipeline pour générer du texte. L'idée principale ici est que vous fournissez seulement un extrait de texte qui va être complété par du texte généré automatiquement par le modèle. Cette fonction est similaire à la fonction de texte prédictif que l'on trouve sur de nombreux téléphones portables. La génération de texte implique de l'aléatoire, donc il est normal que vous n'obteniez pas les mêmes résultats que ceux présentés ci-dessous.
-
-```python
-from transformers import pipeline
-
-generator = pipeline("text-generation")
-generator(
- "In this course, we will teach you how to"
-) # Dans ce cours, nous vous enseignerons comment
-```
-
-```python out
-[{'generated_text': 'In this course, we will teach you how to understand and use '
- # Dans ce cours, nous vous enseignerons comment comprendre et utiliser
- 'data flow and data interchange when handling user data. We '
- # flux de données et l'échange de données lors de la manipulation des données utilisateur. Nous
- 'will be working with one or more of the most commonly used '
- # travailleront avec un ou plusieurs des plus couramment utilisés
- 'data flows — data flows of various types, as seen by the '
- # flux de données - flux de données de différents types, tels qu'ils sont vus par
- 'HTTP'}] # HTTP
-```
-
-Il est possible de contrôler le nombre de séquences générées avec l'argument `num_return_sequences` et la longueur totale du texte généré avec l'argument `max_length`.
-
-
-
-✏️ **Essayez !** Utilisez les arguments `num_return_sequences` et `max_length` pour générer deux phrases de 15 mots chacune.
-
-
-
-
-## Utiliser n'importe quel modèle du Hub dans un pipeline
-
-Les exemples précédents utilisaient le modèle par défaut pour la tâche en question mais vous pouvez aussi choisir un modèle particulier du *Hub* et l'utiliser dans un pipeline pour une tâche spécifique comme par exemple la génération de texte. Rendez-vous sur le [*Hub*](https://huggingface.co/models) et cliquez sur le *filtre* correspondant sur la gauche pour afficher seulement les modèles supportés pour cette tâche. Vous devriez arriver sur une page comme [celle-ci](https://huggingface.co/models?pipeline_tag=text-generation).
-
-Essayons le modèle [`distilgpt2`](https://huggingface.co/distilgpt2) ! Voici comment charger le modèle dans le même pipeline que précédemment :
-
-```python
-from transformers import pipeline
-
-generator = pipeline("text-generation", model="distilgpt2")
-generator(
- "In this course, we will teach you how to",
- # Dans ce cours, nous vous enseignerons comment
- max_length=30,
- num_return_sequences=2,
-)
-```
-
-```python out
-[{'generated_text': 'In this course, we will teach you how to manipulate the world and '
- # Dans ce cours, nous vous enseignerons comment manipuler le monde et
- 'move your mental and physical capabilities to your advantage.'},
- # utiliser vos capacités mentales et physiques à votre avantage.
- {'generated_text': 'In this course, we will teach you how to become an expert and '
- # Dans ce cours, nous vous apprendrons comment devenir un expert et
- 'practice realtime, and with a hands on experience on both real '
- # pratique en temps réel, et avec une expérience pratique à la fois sur de vrais
- 'time and real'}]
- # temps et réel
-```
-
-Vous pouvez améliorer votre recherche de modèle en cliquant sur les *filtres* de langue et choisir un modèle qui génère du texte dans une autre langue. Le *Hub* contient également des *checkpoints* pour des modèles multilingues qui supportent plusieurs langues.
-
-Une fois que vous avez choisi un modèle, vous verrez que vous pouvez tester son fonctionnement en ligne directement. Cela vous permet de tester rapidement les capacités du modèle avant de le télécharger.
-
-
-
-✏️ **Essayez !** Utilisez les filtres pour trouver un modèle de génération de texte pour une autre langue. N'hésitez pas à jouer avec le *widget* et l'utiliser dans un pipeline !
-
-
-
-### L'API d'inférence
-
-Tous les modèles peuvent être testé directement depuis votre navigateur en utilisant l'API d'inférence qui est disponible sur le site [Hugging Face](https://huggingface.co/). Vous pouvez jouer avec le modèle directement sur sa page en entrant du texte personnalisé et en regardant le modèle traiter les données d'entrée.
-
-L'API d'inférence qui est utilisée par le *widget* est également disponible en tant que produit payant si vous avez besoin de l'API pour votre travail. Consultez la [page des prix](https://huggingface.co/pricing) pour plus de détails.
-
-## Remplacement des mots manquants
-
-Le prochain pipeline que vous allez essayer est celui de `fill-mask`. L'idée de cette tâche est de remplir les mots manquants d'un texte donné :
-
-```python
-from transformers import pipeline
-
-unmasker = pipeline("fill-mask")
-unmasker("This course will teach you all about models.", top_k=2)
-```
-
-```python out
-[{'sequence': 'This course will teach you all about mathematical models.',
-# Ce cours vous apprendra tout sur les modèles mathématiques.
- 'score': 0.19619831442832947,
- 'token': 30412,
- 'token_str': ' mathematical'},
- {'sequence': 'This course will teach you all about computational models.',
- # Ce cours vous apprendra tout sur les modèles mathématiques.
- 'score': 0.04052725434303284,
- 'token': 38163,
- 'token_str': ' computational'}]
-```
-
-L'argument `top_k` permet de contrôler le nombre de possibilités que vous souhaitez afficher. Notez que dans ce cas, le modèle remplace le mot spécial ``, qui est souvent appelé un *mot masqué*. D'autres modèles permettant de remplacer les mots manquants peuvent avoir des mots masqués différents, donc il est toujours bon de vérifier le mot masqué approprié lorsque vous comparez d'autres modèles. Une façon de le vérifier est de regarder le mot masqué utilisé dans l'outil de test de la page du modèle.
-
-
-
-✏️ **Essayez !** Recherchez le modèle `bert-base-cased` sur le *Hub* et identifiez le mot masqué dans l'outil d'inférence. Que prédit le modèle pour la phrase dans notre exemple de pipeline au-dessus ?
-
-
-
-## Reconnaissance d'entités nommées
-
-La reconnaissance d'entités nommées ou NER (pour *Named Entity Recognition*) est une tâche où le modèle doit trouver les parties du texte d'entrée qui correspondent à des entités telles que des personnes, des lieux ou des organisations. Voyons un exemple :
-
-```python
-from transformers import pipeline
-
-ner = pipeline("ner", grouped_entities=True)
-ner(
- "My name is Sylvain and I work at Hugging Face in Brooklyn."
-) # Je m'appelle Sylvain et je travaille à Hugging Face à Brooklyn.
-```
-
-```python out
-[{'entity_group': 'PER', 'score': 0.99816, 'word': 'Sylvain', 'start': 11, 'end': 18},
- {'entity_group': 'ORG', 'score': 0.97960, 'word': 'Hugging Face', 'start': 33, 'end': 45},
- {'entity_group': 'LOC', 'score': 0.99321, 'word': 'Brooklyn', 'start': 49, 'end': 57}
-]
-```
-
-Nous pouvons voir que le modèle a correctement identifié Sylvain comme une personne (PER), Hugging Face comme une organisation (ORG) et Brooklyn comme un lieu (LOC).
-
-Il est possible d'utiliser l'option `grouped_entities=True` lors de la création du pipeline pour regrouper les parties du texte qui correspondent à la même entité : ici le modèle à correctement regroupé `Hugging` et `Face` comme une seule organisation, même si le nom comporte plusieurs mots. En effet, comme nous allons voir dans le prochain chapitre, la prétraitement du texte sépare parfois certains mots en plus petites parties. Par exemple, `Sylvain` est séparé en quatre morceaux : `S`, `##yl`, `##va`, et `##in`. Dans l'étape de post-traitement, le pipeline a réussi à regrouper ces morceaux.
-
-
-
-✏️ **Essayez !** Recherchez sur le *Hub* un modèle capable de reconnaître les différentes parties du langage (généralement abrégé en POS pour *Part-of-speech*) en anglais. Que prédit le modèle pour la phrase dans notre exemple du pipeline au-dessus ?
-
-
-
-## Réponse à des questions
-
-Le pipeline `question-answering` répond à des questions en utilisant des informations données en contexte :
-
-```python
-from transformers import pipeline
-
-question_answerer = pipeline("question-answering")
-question_answerer(
- question="Where do I work?", # Où est-ce que je travaille ?
- context="My name is Sylvain and I work at Hugging Face in Brooklyn",
- # Je m'appelle Sylvain et je travaille à Hugging Face à Brooklyn.
-)
-```
-
-```python out
-{'score': 0.6385916471481323, 'start': 33, 'end': 45, 'answer': 'Hugging Face'}
-```
-
-Notez que ce pipeline fonctionne par extraction d'information depuis le contexte fourni, il ne génère pas la réponse.
-
-## Résumé
-
-Le résumé est une tâche de réduction d'un texte en un texte plus court, tout en gardant tous (ou presque tous) les aspects importants référencés dans le texte. Voici un exemple :
-
-```python
-from transformers import pipeline
-
-summarizer = pipeline("summarization")
-summarizer(
- """
- America has changed dramatically during recent years. Not only has the number of
- graduates in traditional engineering disciplines such as mechanical, civil,
- electrical, chemical, and aeronautical engineering declined, but in most of
- the premier American universities engineering curricula now concentrate on
- and encourage largely the study of engineering science. As a result, there
- are declining offerings in engineering subjects dealing with infrastructure,
- the environment, and related issues, and greater concentration on high
- technology subjects, largely supporting increasingly complex scientific
- developments. While the latter is important, it should not be at the expense
- of more traditional engineering.
-
- Rapidly developing economies such as China and India, as well as other
- industrial countries in Europe and Asia, continue to encourage and advance
- the teaching of engineering. Both China and India, respectively, graduate
- six and eight times as many traditional engineers as does the United States.
- Other industrial countries at minimum maintain their output, while America
- suffers an increasingly serious decline in the number of engineering graduates
- and a lack of well-educated engineers.
-"""
-)
-
-"""
- L'Amérique a changé de façon spectaculaire au cours des dernières années. Non seulement le nombre de
- diplômés dans les disciplines traditionnelles de l'ingénierie telles que le génie mécanique, civil,
- l'électricité, la chimie et l'aéronautique a diminué, mais dans la plupart
- des grandes universités américaines, les programmes d'études d'ingénierie se concentrent désormais sur
- et encouragent largement l'étude des sciences de l'ingénieur. Par conséquent, il y a
- de moins en moins d'offres dans les sujets d'ingénierie traitant de l'infrastructure,
- l'environnement et les questions connexes, et une plus grande concentration sur les sujets de haute
- technologie, qui soutiennent en grande partie des développements scientifiques de plus en plus
- complexes. Si cette dernière est importante, elle ne doit pas se faire au détriment
- de l'ingénierie plus traditionnelle.
-
- Les économies en développement rapide telles que la Chine et l'Inde, ainsi que d'autres
- pays industrialisés d'Europe et d'Asie, continuent d'encourager et de promouvoir
- l'enseignement de l'ingénierie. La Chine et l'Inde, respectivement, diplôment
- six et huit fois plus d'ingénieurs traditionnels que les États-Unis.
- Les autres pays industriels maintiennent au minimum leur production, tandis que l'Amérique
- souffre d'une baisse de plus en plus importante du nombre de diplômés en ingénierie
- et un manque d'ingénieurs bien formés.
-"""
-```
-
-```python out
-[{'summary_text': ' America has changed dramatically during recent years . The '
- # L'Amérique a changé de façon spectaculaire au cours des dernières années. Le
- 'number of engineering graduates in the U.S. has declined in '
- # nombre de diplômés en ingénierie aux États-Unis a diminué dans
- 'traditional engineering disciplines such as mechanical, civil '
- # dans les disciplines traditionnelles de l'ingénierie, telles que le génie mécanique, civil
- ', electrical, chemical, and aeronautical engineering . Rapidly '
- # l'électricité, la chimie et l'aéronautique. Les économies
- 'developing economies such as China and India, as well as other '
- # en développement rapide comme la Chine et l'Inde, ainsi que d'autres
- 'industrial countries in Europe and Asia, continue to encourage '
- # pays industriels d'Europe et d'Asie, continuent d'encourager
- 'and advance engineering.'}]
- # et à faire progresser l'ingénierie.
-```
-
-Comme pour la génération de texte, vous pouvez spécifier une `max_length` (longueur maximale) ou une `min_length` (longueur minimale) pour le résultat.
-
-
-## Traduction
-
-Pour la traduction, vous pouvez utiliser un modèle par défaut si vous fournissez un couple de langues dans le nom de la tâche (comme `"translation_en_to_fr"`), mais le plus simple reste d'utiliser un modèle adéquat disponible sur le [*Hub*](https://huggingface.co/models). Ici, nous allons essayer de traduire du français en anglais :
-
-```python
-from transformers import pipeline
-
-translator = pipeline("translation", model="Helsinki-NLP/opus-mt-fr-en")
-translator("Ce cours est produit par Hugging Face.")
-```
-
-```python out
-[{'translation_text': 'This course is produced by Hugging Face.'}]
-```
-
-Comme pour la génération de texte et le résumé de texte, il est possible de spécifier une `max_length` (longueur maximale) ou une `min_length` (longueur minimale) pour le résultat.
-
-
-
-✏️ **Essayez !** Recherchez d'autres modèles de traduction sur le Hub et essayez de traduire la phrase précédente en plusieurs langues différentes.
-
-
-
-Les pipelines présentés jusqu'ici sont principalement destinés à des fins de démonstration. Ils ont été programmés pour des tâches spécifiques et ne peuvent pas effectuer de variations de celles-ci. Dans le chapitre suivant, vous apprendrez ce qu'il y a dans un `pipeline()` et comment modifier son comportement.
+# Que peuvent faire les transformers ?
+
+
+
+Dans cette section, nous allons voir ce que peuvent faire les *transformers* et utiliser notre premier outil de la bibliothèque 🤗 *Transformers* : la fonction `pipeline()`.
+
+
+👀 Vous voyez ce bouton Open in Colab en haut à droite ? Cliquez dessus pour ouvrir un notebook Colab avec tous les exemples de code de cette section. Ce bouton sera présent dans n'importe quelle section contenant des exemples de code.
+
+Si vous souhaitez exécuter les codes en local, nous vous recommandons de jeter un œil au chapitre configuration.
+
+
+## Les transformers sont partout !
+
+Les *transformers* sont utilisés pour résoudre toute sorte de tâches de NLP comme celles mentionnées dans la section précédente. Voici quelques-unes des entreprises et organisations qui utilisent Hugging Face, les *transformers* et qui contribuent aussi à la communauté en partageant leurs modèles :
+
+
+
+La bibliothèque [🤗 *Transformers*](https://github.com/huggingface/transformers) fournit toutes les fonctionnalités nécessaires pour créer et utiliser les modèles partagés. Le [*Hub*](https://huggingface.co/models) contient des milliers de modèles pré-entraînés que n'importe qui peut télécharger et utiliser. Vous pouvez également transférer vos propres modèles vers le Hub !
+
+
+ ⚠️ Le Hub n'est pas limité aux transformers. Tout le monde peut partager n'importe quel modèle ou jeu de données s'il le souhaite ! Créez un compte sur huggingface.co pour bénéficier de toutes les fonctionnalités disponibles !
+
+
+Avant de découvrir en détail comment les *transformers* fonctionnent, nous allons voir quelques exemples de comment ils peuvent être utilisés pour résoudre des problèmes intéressants de NLP.
+
+## Travailler avec les pipelines
+
+
+
+L'outil le plus basique de la bibliothèque 🤗 *Transformers* est la fonction `pipeline()`. Elle relie un modèle avec ses étapes de pré-traitement et de post-traitement, permettant d'entrer n'importe quel texte et d'obtenir une réponse compréhensible :
+
+```python
+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 d'HuggingFace toute ma vie.
+```
+
+```python out
+[{'label': 'POSITIVE', 'score': 0.9598047137260437}]
+```
+
+On peut même passer plusieurs phrases !
+
+```python
+classifier(
+ [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "I hate this so much!",
+ ] # « J'ai attendu un cours d'HuggingFace toute ma vie. », « Je déteste tellement ça ! »
+)
+```
+
+```python out
+[{'label': 'POSITIVE', 'score': 0.9598047137260437},
+ {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
+```
+
+Par défaut, ce pipeline sélectionne un modèle pré-entraîné qui a été spécifiquement entraîné pour l'analyse de sentiment en anglais. Le modèle est téléchargé et mis en cache lorsque vous créez l'objet `classifier`. Si vous réexécutez la commande, c'est le modèle mis en cache qui sera utilisé et il n'y a pas besoin de télécharger le modèle à nouveau.
+
+Il y a trois étapes principales lorsque vous passez du texte à un pipeline :
+
+1. le texte est prétraité pour qu'il ait un format compréhensible par le modèle,
+2. les données prétraitées sont passées au modèle,
+3. les prédictions du modèle sont post-traitées de sorte que vous puissiez les comprendre.
+
+
+Voici une liste non-exhaustive des [pipelines disponibles](https://huggingface.co/transformers/main_classes/pipelines.html) :
+
+- `feature-extraction` (pour obtenir la représentation vectorielle d'un texte)
+- `fill-mask`
+- `ner` (*named entity recognition* ou reconnaissance d'entités nommées en français)
+- `question-answering`
+- `sentiment-analysis`
+- `summarization`
+- `text-generation`
+- `translation`
+- `zero-shot-classification`
+
+Regardons de plus près certains d'entre eux !
+
+## Zero-shot classification
+
+Nous allons commencer par nous attaquer à une tâche plus difficile où nous devons classer des textes qui n'ont pas été annotés. C'est un scénario très répandu dans les projets réels car l'annotation de textes est généralement longue et nécessite parfois une expertise dans un domaine. Pour ce cas d'usage, le pipeline `zero-shot-classification` est très puissant : il vous permet de spécifier les labels à utiliser pour la classification, de sorte que vous n'ayez pas à vous soucier des labels du modèle pré-entraîné. Nous avons déjà vu comment le modèle peut classer un texte comme positif ou négatif en utilisant ces deux labels mais il peut également classer le texte en utilisant n'importe quel autre ensemble de labels que vous souhaitez.
+
+```python
+from transformers import pipeline
+
+classifier = pipeline("zero-shot-classification")
+classifier(
+ "This is a course about the Transformers library",
+ # C'est un cours sur la bibliothèque Transformers
+ candidate_labels=["education", "politics", "business"],
+)
+```
+
+```python out
+{'sequence': 'This is a course about the Transformers library',
+# C'est un cours sur la bibliothèque Transformers
+ 'labels': ['education', 'business', 'politics'],
+ 'scores': [0.8445963859558105, 0.111976258456707, 0.043427448719739914]}
+```
+
+Ce pipeline est appelé _zero-shot_ car vous n'avez pas besoin d'entraîner spécifiquement le modèle sur vos données pour l'utiliser. Il peut directement retourner des scores de probabilité pour n'importe quel ensemble de labels que vous choisissez !
+
+
+
+✏️ **Essayez !** Jouez avec vos propres séquences et labels et voyez comment le modèle fonctionne.
+
+
+
+
+## Génération de texte
+
+Maintenant, nous allons voir comment utiliser un pipeline pour générer du texte. L'idée principale ici est que vous fournissez seulement un extrait de texte qui va être complété par du texte généré automatiquement par le modèle. Cette fonction est similaire à la fonction de texte prédictif que l'on trouve sur de nombreux téléphones portables. La génération de texte implique de l'aléatoire, donc il est normal que vous n'obteniez pas les mêmes résultats que ceux présentés ci-dessous.
+
+```python
+from transformers import pipeline
+
+generator = pipeline("text-generation")
+generator(
+ "In this course, we will teach you how to"
+) # Dans ce cours, nous vous enseignerons comment
+```
+
+```python out
+[{'generated_text': 'In this course, we will teach you how to understand and use '
+ # Dans ce cours, nous vous enseignerons comment comprendre et utiliser
+ 'data flow and data interchange when handling user data. We '
+ # flux de données et l'échange de données lors de la manipulation des données utilisateur. Nous
+ 'will be working with one or more of the most commonly used '
+ # travailleront avec un ou plusieurs des plus couramment utilisés
+ 'data flows — data flows of various types, as seen by the '
+ # flux de données - flux de données de différents types, tels qu'ils sont vus par
+ 'HTTP'}] # HTTP
+```
+
+Il est possible de contrôler le nombre de séquences générées avec l'argument `num_return_sequences` et la longueur totale du texte généré avec l'argument `max_length`.
+
+
+
+✏️ **Essayez !** Utilisez les arguments `num_return_sequences` et `max_length` pour générer deux phrases de 15 mots chacune.
+
+
+
+
+## Utiliser n'importe quel modèle du Hub dans un pipeline
+
+Les exemples précédents utilisaient le modèle par défaut pour la tâche en question mais vous pouvez aussi choisir un modèle particulier du *Hub* et l'utiliser dans un pipeline pour une tâche spécifique comme par exemple la génération de texte. Rendez-vous sur le [*Hub*](https://huggingface.co/models) et cliquez sur le *filtre* correspondant sur la gauche pour afficher seulement les modèles supportés pour cette tâche. Vous devriez arriver sur une page comme [celle-ci](https://huggingface.co/models?pipeline_tag=text-generation).
+
+Essayons le modèle [`distilgpt2`](https://huggingface.co/distilgpt2) ! Voici comment charger le modèle dans le même pipeline que précédemment :
+
+```python
+from transformers import pipeline
+
+generator = pipeline("text-generation", model="distilgpt2")
+generator(
+ "In this course, we will teach you how to",
+ # Dans ce cours, nous vous enseignerons comment
+ max_length=30,
+ num_return_sequences=2,
+)
+```
+
+```python out
+[{'generated_text': 'In this course, we will teach you how to manipulate the world and '
+ # Dans ce cours, nous vous enseignerons comment manipuler le monde et
+ 'move your mental and physical capabilities to your advantage.'},
+ # utiliser vos capacités mentales et physiques à votre avantage.
+ {'generated_text': 'In this course, we will teach you how to become an expert and '
+ # Dans ce cours, nous vous apprendrons comment devenir un expert et
+ 'practice realtime, and with a hands on experience on both real '
+ # pratique en temps réel, et avec une expérience pratique à la fois sur de vrais
+ 'time and real'}]
+ # temps et réel
+```
+
+Vous pouvez améliorer votre recherche de modèle en cliquant sur les *filtres* de langue et choisir un modèle qui génère du texte dans une autre langue. Le *Hub* contient également des *checkpoints* pour des modèles multilingues qui supportent plusieurs langues.
+
+Une fois que vous avez choisi un modèle, vous verrez que vous pouvez tester son fonctionnement en ligne directement. Cela vous permet de tester rapidement les capacités du modèle avant de le télécharger.
+
+
+
+✏️ **Essayez !** Utilisez les filtres pour trouver un modèle de génération de texte pour une autre langue. N'hésitez pas à jouer avec le *widget* et l'utiliser dans un pipeline !
+
+
+
+### L'API d'inférence
+
+Tous les modèles peuvent être testé directement depuis votre navigateur en utilisant l'API d'inférence qui est disponible sur le site [Hugging Face](https://huggingface.co/). Vous pouvez jouer avec le modèle directement sur sa page en entrant du texte personnalisé et en regardant le modèle traiter les données d'entrée.
+
+L'API d'inférence qui est utilisée par le *widget* est également disponible en tant que produit payant si vous avez besoin de l'API pour votre travail. Consultez la [page des prix](https://huggingface.co/pricing) pour plus de détails.
+
+## Remplacement des mots manquants
+
+Le prochain pipeline que vous allez essayer est celui de `fill-mask`. L'idée de cette tâche est de remplir les mots manquants d'un texte donné :
+
+```python
+from transformers import pipeline
+
+unmasker = pipeline("fill-mask")
+unmasker("This course will teach you all about models.", top_k=2)
+```
+
+```python out
+[{'sequence': 'This course will teach you all about mathematical models.',
+# Ce cours vous apprendra tout sur les modèles mathématiques.
+ 'score': 0.19619831442832947,
+ 'token': 30412,
+ 'token_str': ' mathematical'},
+ {'sequence': 'This course will teach you all about computational models.',
+ # Ce cours vous apprendra tout sur les modèles mathématiques.
+ 'score': 0.04052725434303284,
+ 'token': 38163,
+ 'token_str': ' computational'}]
+```
+
+L'argument `top_k` permet de contrôler le nombre de possibilités que vous souhaitez afficher. Notez que dans ce cas, le modèle remplace le mot spécial ``, qui est souvent appelé un *mot masqué*. D'autres modèles permettant de remplacer les mots manquants peuvent avoir des mots masqués différents, donc il est toujours bon de vérifier le mot masqué approprié lorsque vous comparez d'autres modèles. Une façon de le vérifier est de regarder le mot masqué utilisé dans l'outil de test de la page du modèle.
+
+
+
+✏️ **Essayez !** Recherchez le modèle `bert-base-cased` sur le *Hub* et identifiez le mot masqué dans l'outil d'inférence. Que prédit le modèle pour la phrase dans notre exemple de pipeline au-dessus ?
+
+
+
+## Reconnaissance d'entités nommées
+
+La reconnaissance d'entités nommées ou NER (pour *Named Entity Recognition*) est une tâche où le modèle doit trouver les parties du texte d'entrée qui correspondent à des entités telles que des personnes, des lieux ou des organisations. Voyons un exemple :
+
+```python
+from transformers import pipeline
+
+ner = pipeline("ner", grouped_entities=True)
+ner(
+ "My name is Sylvain and I work at Hugging Face in Brooklyn."
+) # Je m'appelle Sylvain et je travaille à Hugging Face à Brooklyn.
+```
+
+```python out
+[{'entity_group': 'PER', 'score': 0.99816, 'word': 'Sylvain', 'start': 11, 'end': 18},
+ {'entity_group': 'ORG', 'score': 0.97960, 'word': 'Hugging Face', 'start': 33, 'end': 45},
+ {'entity_group': 'LOC', 'score': 0.99321, 'word': 'Brooklyn', 'start': 49, 'end': 57}
+]
+```
+
+Nous pouvons voir que le modèle a correctement identifié Sylvain comme une personne (PER), Hugging Face comme une organisation (ORG) et Brooklyn comme un lieu (LOC).
+
+Il est possible d'utiliser l'option `grouped_entities=True` lors de la création du pipeline pour regrouper les parties du texte qui correspondent à la même entité : ici le modèle à correctement regroupé `Hugging` et `Face` comme une seule organisation, même si le nom comporte plusieurs mots. En effet, comme nous allons voir dans le prochain chapitre, la prétraitement du texte sépare parfois certains mots en plus petites parties. Par exemple, `Sylvain` est séparé en quatre morceaux : `S`, `##yl`, `##va`, et `##in`. Dans l'étape de post-traitement, le pipeline a réussi à regrouper ces morceaux.
+
+
+
+✏️ **Essayez !** Recherchez sur le *Hub* un modèle capable de reconnaître les différentes parties du langage (généralement abrégé en POS pour *Part-of-speech*) en anglais. Que prédit le modèle pour la phrase dans notre exemple du pipeline au-dessus ?
+
+
+
+## Réponse à des questions
+
+Le pipeline `question-answering` répond à des questions en utilisant des informations données en contexte :
+
+```python
+from transformers import pipeline
+
+question_answerer = pipeline("question-answering")
+question_answerer(
+ question="Where do I work?", # Où est-ce que je travaille ?
+ context="My name is Sylvain and I work at Hugging Face in Brooklyn",
+ # Je m'appelle Sylvain et je travaille à Hugging Face à Brooklyn.
+)
+```
+
+```python out
+{'score': 0.6385916471481323, 'start': 33, 'end': 45, 'answer': 'Hugging Face'}
+```
+
+Notez que ce pipeline fonctionne par extraction d'information depuis le contexte fourni, il ne génère pas la réponse.
+
+## Résumé
+
+Le résumé est une tâche de réduction d'un texte en un texte plus court, tout en gardant tous (ou presque tous) les aspects importants référencés dans le texte. Voici un exemple :
+
+```python
+from transformers import pipeline
+
+summarizer = pipeline("summarization")
+summarizer(
+ """
+ America has changed dramatically during recent years. Not only has the number of
+ graduates in traditional engineering disciplines such as mechanical, civil,
+ electrical, chemical, and aeronautical engineering declined, but in most of
+ the premier American universities engineering curricula now concentrate on
+ and encourage largely the study of engineering science. As a result, there
+ are declining offerings in engineering subjects dealing with infrastructure,
+ the environment, and related issues, and greater concentration on high
+ technology subjects, largely supporting increasingly complex scientific
+ developments. While the latter is important, it should not be at the expense
+ of more traditional engineering.
+
+ Rapidly developing economies such as China and India, as well as other
+ industrial countries in Europe and Asia, continue to encourage and advance
+ the teaching of engineering. Both China and India, respectively, graduate
+ six and eight times as many traditional engineers as does the United States.
+ Other industrial countries at minimum maintain their output, while America
+ suffers an increasingly serious decline in the number of engineering graduates
+ and a lack of well-educated engineers.
+"""
+)
+
+"""
+ L'Amérique a changé de façon spectaculaire au cours des dernières années. Non seulement le nombre de
+ diplômés dans les disciplines traditionnelles de l'ingénierie telles que le génie mécanique, civil,
+ l'électricité, la chimie et l'aéronautique a diminué, mais dans la plupart
+ des grandes universités américaines, les programmes d'études d'ingénierie se concentrent désormais sur
+ et encouragent largement l'étude des sciences de l'ingénieur. Par conséquent, il y a
+ de moins en moins d'offres dans les sujets d'ingénierie traitant de l'infrastructure,
+ l'environnement et les questions connexes, et une plus grande concentration sur les sujets de haute
+ technologie, qui soutiennent en grande partie des développements scientifiques de plus en plus
+ complexes. Si cette dernière est importante, elle ne doit pas se faire au détriment
+ de l'ingénierie plus traditionnelle.
+
+ Les économies en développement rapide telles que la Chine et l'Inde, ainsi que d'autres
+ pays industrialisés d'Europe et d'Asie, continuent d'encourager et de promouvoir
+ l'enseignement de l'ingénierie. La Chine et l'Inde, respectivement, diplôment
+ six et huit fois plus d'ingénieurs traditionnels que les États-Unis.
+ Les autres pays industriels maintiennent au minimum leur production, tandis que l'Amérique
+ souffre d'une baisse de plus en plus importante du nombre de diplômés en ingénierie
+ et un manque d'ingénieurs bien formés.
+"""
+```
+
+```python out
+[{'summary_text': ' America has changed dramatically during recent years . The '
+ # L'Amérique a changé de façon spectaculaire au cours des dernières années. Le
+ 'number of engineering graduates in the U.S. has declined in '
+ # nombre de diplômés en ingénierie aux États-Unis a diminué dans
+ 'traditional engineering disciplines such as mechanical, civil '
+ # dans les disciplines traditionnelles de l'ingénierie, telles que le génie mécanique, civil
+ ', electrical, chemical, and aeronautical engineering . Rapidly '
+ # l'électricité, la chimie et l'aéronautique. Les économies
+ 'developing economies such as China and India, as well as other '
+ # en développement rapide comme la Chine et l'Inde, ainsi que d'autres
+ 'industrial countries in Europe and Asia, continue to encourage '
+ # pays industriels d'Europe et d'Asie, continuent d'encourager
+ 'and advance engineering.'}]
+ # et à faire progresser l'ingénierie.
+```
+
+Comme pour la génération de texte, vous pouvez spécifier une `max_length` (longueur maximale) ou une `min_length` (longueur minimale) pour le résultat.
+
+
+## Traduction
+
+Pour la traduction, vous pouvez utiliser un modèle par défaut si vous fournissez un couple de langues dans le nom de la tâche (comme `"translation_en_to_fr"`), mais le plus simple reste d'utiliser un modèle adéquat disponible sur le [*Hub*](https://huggingface.co/models). Ici, nous allons essayer de traduire du français en anglais :
+
+```python
+from transformers import pipeline
+
+translator = pipeline("translation", model="Helsinki-NLP/opus-mt-fr-en")
+translator("Ce cours est produit par Hugging Face.")
+```
+
+```python out
+[{'translation_text': 'This course is produced by Hugging Face.'}]
+```
+
+Comme pour la génération de texte et le résumé de texte, il est possible de spécifier une `max_length` (longueur maximale) ou une `min_length` (longueur minimale) pour le résultat.
+
+
+
+✏️ **Essayez !** Recherchez d'autres modèles de traduction sur le Hub et essayez de traduire la phrase précédente en plusieurs langues différentes.
+
+
+
+Les pipelines présentés jusqu'ici sont principalement destinés à des fins de démonstration. Ils ont été programmés pour des tâches spécifiques et ne peuvent pas effectuer de variations de celles-ci. Dans le chapitre suivant, vous apprendrez ce qu'il y a dans un `pipeline()` et comment modifier son comportement.
diff --git a/chapters/fr/chapter1/4.mdx b/chapters/fr/chapter1/4.mdx
index dc996234e..28de8aa80 100644
--- a/chapters/fr/chapter1/4.mdx
+++ b/chapters/fr/chapter1/4.mdx
@@ -1,169 +1,174 @@
-# Comment fonctionnent les transformers ?
-
-Dans cette partie, nous allons jeter un coup d'œil à l'architecture des *transformers*.
-
-## Court historique des transformers
-
-Voici quelques dates clefs dans la courte histoire des *transformers* :
-
-
-
-
-
-
-[L'architecture *Transformer*](https://arxiv.org/abs/1706.03762) a été présentée en juin 2017. Initialement, la recherche portait sur la tâche de traduction. Elle a été suivie par l'introduction de plusieurs modèles influents, notamment :
-
-- **Juin 2018** : [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), le premier *transformer* pré-entraîné et *finetuné* sur différentes tâches de NLP et ayant obtenu des résultats à l'état de l'art,
-
-- **Octobre 2018** : [BERT](https://arxiv.org/abs/1810.04805), autre grand modèle pré-entraîné ayant été construit pour produire de meilleurs résumés de texte (plus de détails dans le chapitre suivant !),
-
-- **Février 2019** : [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), une version améliorée (et plus grande) de GPT qui n'a pas été directement rendu publique pour cause de raisons éthiques,
-
-- **Octobre 2019** : [DistilBERT](https://arxiv.org/abs/1910.01108), une version distillée de BERT étant 60% plus rapide, 40% plus légère en mémoire et conservant tout de même 97% des performances initiales de BERT,
-
-- **Octobre 2019** : [BART](https://arxiv.org/abs/1910.13461) et [T5](https://arxiv.org/abs/1910.10683), deux modèles pré-entraînés utilisant la même architecture que le *transformer* original (les premiers à faire cela),
-
-- **Mai 2020** : [GPT-3](https://arxiv.org/abs/2005.14165), une version encore plus grande que GPT-2 ayant des performances très bonnes sur une variété de tâches ne nécessitant pas de *finetuning* (appelé _zero-shot learning_).
-
-Cette liste est loin d'être exhaustive et met en lumière certains *transformers*. Plus largement, ces modèles peuvent être regroupés en trois catégories :
-
-- ceux de type GPT (aussi appelés *transformers* _autorégressifs_)
-- ceux de type BERT (aussi appelés *transformers* _auto-encodeurs_)
-- ceux de type BART/T5 (aussi appelés *transformers* _séquence-à-séquence_)
-
-Nous verrons plus en profondeur ces familles de modèles plus tard.
-
-## Les transformers sont des modèles de langage
-
-Tous les *transformers* mentionnés ci-dessus (GPT, BERT, BART, T5, etc.) ont été entraînés comme des *modèles de langage*. Cela signifie qu'ils ont été entraînés sur une large quantité de textes bruts de manière autosupervisée. L'apprentissage autosupervisé est un type d'entraînement dans lequel l'objectif est automatiquement calculé à partir des entrées du modèle. Cela signifie que les humains ne sont pas nécessaires pour étiqueter les données !
-
-Ce type de modèle développe une compréhension statistique de la langue sur laquelle il a été entraîné, mais il n'est pas très utile pour des tâches pratiques spécifiques. Pour cette raison, le modèle pré-entraîné passe ensuite par un processus appelé apprentissage par transfert. Au cours de ce processus, le modèle est *finetuné* de manière supervisée (c'est-à-dire en utilisant des étiquettes annotées par des humains) pour une tâche donnée.
-
-Un exemple de tâche consiste à prédire le mot suivant dans une phrase après avoir lu les *n* mots précédents. Cette tâche est appelée *modélisation causale du langage* car la sortie dépend des entrées passées et présentes, mais pas des entrées futures.
-
-
-
-
-
-Un autre exemple est la *modélisation du langage masqué*, dans laquelle le modèle prédit un mot masqué dans la phrase.
-
-
-
-
-
-
-## Les transformers 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.
-
-
-
-
-
-Malheureusement, entraîner un modèle et particulièrement un très grand modèle, nécessite une importante quantité de données. Cela devient très coûteux en termes de temps et de ressources de calcul. Cela se traduit même par un impact environnemental comme le montre le graphique suivant.
-
-
-
-
-
-
-
-
-L'image montre l'empreinte carbone pour un projet d'entraînement d'un (très grand) modèle mené par une équipe qui pourtant essaie consciemment de réduire l'impact environnemental du pré-entraînement. L'empreinte de l'exécution de nombreux essais pour obtenir les meilleurs hyperparamètres serait encore plus élevée.
-
-Imaginez qu'à chaque fois qu'une équipe de recherche, une association d'étudiants ou une entreprise souhaite entraîner un modèle, elle le fasse en partant de zéro. Cela entraînerait des coûts globaux énormes et inutiles !
-
-C'est pourquoi le partage des modèles du langage est primordial : partager les poids d'entraînement et construire à partir de ces poids permet de réduire les coûts de calcul globaux ainsi que l'empreinte carbone de toute la communauté.
-
-## L'apprentissage par transfert
-
-
-
-Le pré-entraînement consiste à entraîner un modèle à partir de zéro : les poids sont initialisés de manière aléatoire et l'entraînement commence sans aucune connaissance préalable.
-
-
-
-
-
-
-Ce pré-entraînement est généralement effectué sur de très grandes quantités de données. Il nécessite donc un très grand corpus de données et l'entraînement peut prendre jusqu'à plusieurs semaines.
-
-Le *finetuning*, quant à lui, est l'entrainement effectué après qu'un modèle ait été pré-entraîné. Pour effectuer un *finetuning*, vous devez d'abord acquérir un modèle de langue pré-entraîné, puis effectuer un entraînement supplémentaire avec un jeu de données spécifiques. Mais pourquoi ne pas entraîner directement pour la tâche finale ? Il y a plusieurs raisons à cela :
-
-* Le modèle pré-entraîné a déjà été entraîné sur un jeu de données qui présente certaines similitudes avec le jeu de données de *finetuning*. Le processus de *finetuning* est donc en mesure de tirer parti des connaissances acquises par le modèle initial lors du pré-entraînement (par exemple, pour les problèmes de langage naturel, le modèle pré-entraîné aura une certaine compréhension statistique de la langue que vous utilisez pour votre tâche)
-* Comme le modèle pré-entraîné a déjà été entraîné sur de nombreuses données, le *finetuning* nécessite beaucoup moins de données pour obtenir des résultats décents.
-* Pour la même raison, le temps et les ressources nécessaires pour obtenir de bons résultats sont beaucoup moins importants.
-
-Par exemple, il est possible d'exploiter un modèle pré-entraîné entraîné sur la langue anglaise, puis de le *finetuner* sur un corpus arXiv, pour obtenir un modèle basé sur la science et la recherche. Le *finetuning* ne nécessitera qu'une quantité limitée de données : les connaissances acquises par le modèle pré-entraîné sont « transférées », d'où le terme d'apprentissage par transfert.
-
-
-
-
-
-
-Le *finetuning* d'un modèle a donc un coût moindre en termes de temps, de données, de finances et d'environnement. Il est aussi plus rapide et plus facile d'itérer sur différents schémas de *finetuning* car l'entraînement est moins contraignant qu'un pré-entraînement complet.
-
-Ce processus permet également d'obtenir de meilleurs résultats que l'entraînement à partir de zéro (à moins que vous ne disposiez d'un grand nombre de données). C'est pourquoi vous devez toujours essayer de tirer parti d'un modèle pré-entraîné, c'est-à-dire un modèle aussi proche que possible de la tâche que vous avez à accomplir, et de le *finetuner*.
-
-## Architecture générale
-
-Dans cette section, nous allons voir l'architecture générale des *transformers*. Pas d'inquiétudes si vous ne comprenez pas tous les concepts, des sections détaillées qui couvrent chaque composant seront abordées plus tard.
-
-
-
-## Introduction
-
-Le modèle est principalement composé de deux blocs :
-
-* **Encodeur (à gauche)** : l'encodeur reçoit une entrée et construit une représentation de celle-ci (ses caractéristiques). Cela signifie que le modèle est optimisé pour acquérir une compréhension venant de ces entrées.
-* **Décodeur (à droite)** : le décodeur utilise la représentation de l'encodeur (les caractéristiques) en plus des autres entrées pour générer une séquence cible. Cela signifie que le modèle est optimisé pour générer des sorties.
-
-
-
-
-
-
-Chacun de ces blocs peuvent être utilisés indépendamment en fonction de la tâche que l'on souhaite traiter :
-
-* **Modèles uniquement encodeurs** : adaptés pour des tâches qui nécessitent une compréhension de l'entrée, comme la classification de phrases et la reconnaissance d'entités nommées.
-* **Modèles uniquement décodeurs** : adaptés pour les tâches génératives telles que la génération de texte.
-* **Modèles encodeurs-décodeurs** (ou **modèles de séquence-à-séquence**) : adaptés aux tâches génératives qui nécessitent une entrée, telles que la traduction ou le résumé de texte.
-
-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* 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.
-
-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é.
-
-Maintenant que vous avez une idée plus précise des couches d'attentions, nous allons regarder de plus près l'architecture des *transformers*.
-
-## L'architecture originale
-
-L'architecture du *transformer* a initialement été construite pour la tâche de traduction. Pendant l'entraînement, l'encodeur reçoit des entrées (des phrases) dans une certaine langue, tandis que le décodeur reçoit la même phrase traduite dans la langue cible. Pour l'encodeur, les couches d'attention peuvent utiliser tous les mots d'une phrase (puisque comme nous venons de le voir, la traduction d'un mot donné peut dépendre de ce qui le suit ou le précède dans la phrase). Le décodeur, quant à lui, fonctionne de façon séquentielle et ne peut porter son attention qu'aux mots déjà traduits dans la phrase (donc uniquement les mots générés avant le mot en cours). Par exemple, lorsqu'on a prédit les trois premiers mots de la phrase cible, on les donne au décodeur qui utilise alors toutes les entrées de l'encodeur pour essayer de prédire le quatrième mot.
-
-Pour accélérer les choses pendant l'apprentissage (lorsque le modèle a accès aux phrases cibles), le décodeur est alimenté avec la cible entière, mais il n'est pas autorisé à utiliser les mots futurs (s'il avait accès au mot en position 2 lorsqu'il essayait de prédire le mot en position 2, le problème ne serait pas très difficile !). Par exemple, en essayant de prédire le quatrième mot, la couche d'attention n'aura accès qu'aux mots des positions 1 à 3.
-
-L'architecture originale du *transformer* ressemble à ceci, avec l'encodeur à gauche et le décodeur à droite :
-
-
-
-
-
-
-Notez que la première couche d'attention dans un bloc décodeur prête attention à toutes les entrées (passées) du décodeur, mais que la deuxième couche d'attention utilise la sortie de l'encodeur. Elle peut donc accéder à l'ensemble de la phrase d'entrée pour prédire au mieux le mot actuel. C'est très utile, car différentes langues peuvent avoir des règles grammaticales qui placent les mots dans un ordre différent, ou un contexte fourni plus tard dans la phrase peut être utile pour déterminer la meilleure traduction d'un mot donné.
-
-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
-
-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 :
-
-* **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.
-* **Modèle** : c'est un mot valise n'étant pas aussi précis que les mots « architecture » ou « *checkpoint* ». Il peut désigner l'un comme l'autre. Dans ce cours, il sera spécifié *architecture* ou *checkpoint* lorsqu'il sera essentiel de réduire toute ambiguïté.
-
-Par exemple, BERT est une architecture alors que `bert-base-cased` (un ensemble de poids entraîné par l'équipe de Google lors de la première sortie de BERT) est un *checkpoint*. Cependant, il est possible de dire « le modèle BERT » et « le modèle `bert-base-cased` ».
+# Comment fonctionnent les transformers ?
+
+
+
+Dans cette partie, nous allons jeter un coup d'œil à l'architecture des *transformers*.
+
+## Court historique des transformers
+
+Voici quelques dates clefs dans la courte histoire des *transformers* :
+
+
+
+
+
+
+[L'architecture *Transformer*](https://arxiv.org/abs/1706.03762) a été présentée en juin 2017. Initialement, la recherche portait sur la tâche de traduction. Elle a été suivie par l'introduction de plusieurs modèles influents, notamment :
+
+- **Juin 2018** : [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), le premier *transformer* pré-entraîné et *finetuné* sur différentes tâches de NLP et ayant obtenu des résultats à l'état de l'art,
+
+- **Octobre 2018** : [BERT](https://arxiv.org/abs/1810.04805), autre grand modèle pré-entraîné ayant été construit pour produire de meilleurs résumés de texte (plus de détails dans le chapitre suivant !),
+
+- **Février 2019** : [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), une version améliorée (et plus grande) de GPT qui n'a pas été directement rendu publique pour cause de raisons éthiques,
+
+- **Octobre 2019** : [DistilBERT](https://arxiv.org/abs/1910.01108), une version distillée de BERT étant 60% plus rapide, 40% plus légère en mémoire et conservant tout de même 97% des performances initiales de BERT,
+
+- **Octobre 2019** : [BART](https://arxiv.org/abs/1910.13461) et [T5](https://arxiv.org/abs/1910.10683), deux modèles pré-entraînés utilisant la même architecture que le *transformer* original (les premiers à faire cela),
+
+- **Mai 2020** : [GPT-3](https://arxiv.org/abs/2005.14165), une version encore plus grande que GPT-2 ayant des performances très bonnes sur une variété de tâches ne nécessitant pas de *finetuning* (appelé _zero-shot learning_).
+
+Cette liste est loin d'être exhaustive et met en lumière certains *transformers*. Plus largement, ces modèles peuvent être regroupés en trois catégories :
+
+- ceux de type GPT (aussi appelés *transformers* _autorégressifs_)
+- ceux de type BERT (aussi appelés *transformers* _auto-encodeurs_)
+- ceux de type BART/T5 (aussi appelés *transformers* _séquence-à-séquence_)
+
+Nous verrons plus en profondeur ces familles de modèles plus tard.
+
+## Les transformers sont des modèles de langage
+
+Tous les *transformers* mentionnés ci-dessus (GPT, BERT, BART, T5, etc.) ont été entraînés comme des *modèles de langage*. Cela signifie qu'ils ont été entraînés sur une large quantité de textes bruts de manière autosupervisée. L'apprentissage autosupervisé est un type d'entraînement dans lequel l'objectif est automatiquement calculé à partir des entrées du modèle. Cela signifie que les humains ne sont pas nécessaires pour étiqueter les données !
+
+Ce type de modèle développe une compréhension statistique de la langue sur laquelle il a été entraîné, mais il n'est pas très utile pour des tâches pratiques spécifiques. Pour cette raison, le modèle pré-entraîné passe ensuite par un processus appelé apprentissage par transfert. Au cours de ce processus, le modèle est *finetuné* de manière supervisée (c'est-à-dire en utilisant des étiquettes annotées par des humains) pour une tâche donnée.
+
+Un exemple de tâche consiste à prédire le mot suivant dans une phrase après avoir lu les *n* mots précédents. Cette tâche est appelée *modélisation causale du langage* car la sortie dépend des entrées passées et présentes, mais pas des entrées futures.
+
+
+
+
+
+Un autre exemple est la *modélisation du langage masqué*, dans laquelle le modèle prédit un mot masqué dans la phrase.
+
+
+
+
+
+
+## Les transformers 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.
+
+
+
+
+
+Malheureusement, entraîner un modèle et particulièrement un très grand modèle, nécessite une importante quantité de données. Cela devient très coûteux en termes de temps et de ressources de calcul. Cela se traduit même par un impact environnemental comme le montre le graphique suivant.
+
+
+
+
+
+
+
+
+L'image montre l'empreinte carbone pour un projet d'entraînement d'un (très grand) modèle mené par une équipe qui pourtant essaie consciemment de réduire l'impact environnemental du pré-entraînement. L'empreinte de l'exécution de nombreux essais pour obtenir les meilleurs hyperparamètres serait encore plus élevée.
+
+Imaginez qu'à chaque fois qu'une équipe de recherche, une association d'étudiants ou une entreprise souhaite entraîner un modèle, elle le fasse en partant de zéro. Cela entraînerait des coûts globaux énormes et inutiles !
+
+C'est pourquoi le partage des modèles du langage est primordial : partager les poids d'entraînement et construire à partir de ces poids permet de réduire les coûts de calcul globaux ainsi que l'empreinte carbone de toute la communauté.
+
+## L'apprentissage par transfert
+
+
+
+Le pré-entraînement consiste à entraîner un modèle à partir de zéro : les poids sont initialisés de manière aléatoire et l'entraînement commence sans aucune connaissance préalable.
+
+
+
+
+
+
+Ce pré-entraînement est généralement effectué sur de très grandes quantités de données. Il nécessite donc un très grand corpus de données et l'entraînement peut prendre jusqu'à plusieurs semaines.
+
+Le *finetuning*, quant à lui, est l'entrainement effectué après qu'un modèle ait été pré-entraîné. Pour effectuer un *finetuning*, vous devez d'abord acquérir un modèle de langue pré-entraîné, puis effectuer un entraînement supplémentaire avec un jeu de données spécifiques. Mais pourquoi ne pas entraîner directement pour la tâche finale ? Il y a plusieurs raisons à cela :
+
+* Le modèle pré-entraîné a déjà été entraîné sur un jeu de données qui présente certaines similitudes avec le jeu de données de *finetuning*. Le processus de *finetuning* est donc en mesure de tirer parti des connaissances acquises par le modèle initial lors du pré-entraînement (par exemple, pour les problèmes de langage naturel, le modèle pré-entraîné aura une certaine compréhension statistique de la langue que vous utilisez pour votre tâche)
+* Comme le modèle pré-entraîné a déjà été entraîné sur de nombreuses données, le *finetuning* nécessite beaucoup moins de données pour obtenir des résultats décents.
+* Pour la même raison, le temps et les ressources nécessaires pour obtenir de bons résultats sont beaucoup moins importants.
+
+Par exemple, il est possible d'exploiter un modèle pré-entraîné entraîné sur la langue anglaise, puis de le *finetuner* sur un corpus arXiv, pour obtenir un modèle basé sur la science et la recherche. Le *finetuning* ne nécessitera qu'une quantité limitée de données : les connaissances acquises par le modèle pré-entraîné sont « transférées », d'où le terme d'apprentissage par transfert.
+
+
+
+
+
+
+Le *finetuning* d'un modèle a donc un coût moindre en termes de temps, de données, de finances et d'environnement. Il est aussi plus rapide et plus facile d'itérer sur différents schémas de *finetuning* car l'entraînement est moins contraignant qu'un pré-entraînement complet.
+
+Ce processus permet également d'obtenir de meilleurs résultats que l'entraînement à partir de zéro (à moins que vous ne disposiez d'un grand nombre de données). C'est pourquoi vous devez toujours essayer de tirer parti d'un modèle pré-entraîné, c'est-à-dire un modèle aussi proche que possible de la tâche que vous avez à accomplir, et de le *finetuner*.
+
+## Architecture générale
+
+Dans cette section, nous allons voir l'architecture générale des *transformers*. Pas d'inquiétudes si vous ne comprenez pas tous les concepts, des sections détaillées qui couvrent chaque composant seront abordées plus tard.
+
+
+
+## Introduction
+
+Le modèle est principalement composé de deux blocs :
+
+* **Encodeur (à gauche)** : l'encodeur reçoit une entrée et construit une représentation de celle-ci (ses caractéristiques). Cela signifie que le modèle est optimisé pour acquérir une compréhension venant de ces entrées.
+* **Décodeur (à droite)** : le décodeur utilise la représentation de l'encodeur (les caractéristiques) en plus des autres entrées pour générer une séquence cible. Cela signifie que le modèle est optimisé pour générer des sorties.
+
+
+
+
+
+
+Chacun de ces blocs peuvent être utilisés indépendamment en fonction de la tâche que l'on souhaite traiter :
+
+* **Modèles uniquement encodeurs** : adaptés pour des tâches qui nécessitent une compréhension de l'entrée, comme la classification de phrases et la reconnaissance d'entités nommées.
+* **Modèles uniquement décodeurs** : adaptés pour les tâches génératives telles que la génération de texte.
+* **Modèles encodeurs-décodeurs** (ou **modèles de séquence-à-séquence**) : adaptés aux tâches génératives qui nécessitent une entrée, telles que la traduction ou le résumé de texte.
+
+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* 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.
+
+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é.
+
+Maintenant que vous avez une idée plus précise des couches d'attentions, nous allons regarder de plus près l'architecture des *transformers*.
+
+## L'architecture originale
+
+L'architecture du *transformer* a initialement été construite pour la tâche de traduction. Pendant l'entraînement, l'encodeur reçoit des entrées (des phrases) dans une certaine langue, tandis que le décodeur reçoit la même phrase traduite dans la langue cible. Pour l'encodeur, les couches d'attention peuvent utiliser tous les mots d'une phrase (puisque comme nous venons de le voir, la traduction d'un mot donné peut dépendre de ce qui le suit ou le précède dans la phrase). Le décodeur, quant à lui, fonctionne de façon séquentielle et ne peut porter son attention qu'aux mots déjà traduits dans la phrase (donc uniquement les mots générés avant le mot en cours). Par exemple, lorsqu'on a prédit les trois premiers mots de la phrase cible, on les donne au décodeur qui utilise alors toutes les entrées de l'encodeur pour essayer de prédire le quatrième mot.
+
+Pour accélérer les choses pendant l'apprentissage (lorsque le modèle a accès aux phrases cibles), le décodeur est alimenté avec la cible entière, mais il n'est pas autorisé à utiliser les mots futurs (s'il avait accès au mot en position 2 lorsqu'il essayait de prédire le mot en position 2, le problème ne serait pas très difficile !). Par exemple, en essayant de prédire le quatrième mot, la couche d'attention n'aura accès qu'aux mots des positions 1 à 3.
+
+L'architecture originale du *transformer* ressemble à ceci, avec l'encodeur à gauche et le décodeur à droite :
+
+
+
+
+
+
+Notez que la première couche d'attention dans un bloc décodeur prête attention à toutes les entrées (passées) du décodeur, mais que la deuxième couche d'attention utilise la sortie de l'encodeur. Elle peut donc accéder à l'ensemble de la phrase d'entrée pour prédire au mieux le mot actuel. C'est très utile, car différentes langues peuvent avoir des règles grammaticales qui placent les mots dans un ordre différent, ou un contexte fourni plus tard dans la phrase peut être utile pour déterminer la meilleure traduction d'un mot donné.
+
+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
+
+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 :
+
+* **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.
+* **Modèle** : c'est un mot valise n'étant pas aussi précis que les mots « architecture » ou « *checkpoint* ». Il peut désigner l'un comme l'autre. Dans ce cours, il sera spécifié *architecture* ou *checkpoint* lorsqu'il sera essentiel de réduire toute ambiguïté.
+
+Par exemple, BERT est une architecture alors que `bert-base-cased` (un ensemble de poids entraîné par l'équipe de Google lors de la première sortie de BERT) est un *checkpoint*. Cependant, il est possible de dire « le modèle BERT » et « le modèle `bert-base-cased` ».
diff --git a/chapters/fr/chapter1/5.mdx b/chapters/fr/chapter1/5.mdx
index 0f1fe9d3a..92d30c2a9 100644
--- a/chapters/fr/chapter1/5.mdx
+++ b/chapters/fr/chapter1/5.mdx
@@ -1,17 +1,22 @@
-# Les modèles basés sur l'encodeur
-
-
-
-Les modèles basés sur l'encodeur utilisent uniquement l'encodeur d'un *transformer*. À chaque étape, les couches d'attention peuvent accéder à tous les mots de la phrase initiale. Ces modèles sont souvent caractérisés comme ayant une attention bidirectionnelle et sont souvent appelés *modèles d'auto-encodage*.
-
-Le pré-entraînement de ces modèles se concentre généralement sur la modification d'une phrase donnée (par exemple, en masquant des mots aléatoires dans celle-ci) et en demandant au modèle de trouver ou de reconstruire la phrase initiale.
-
-Ces modèles sont les plus adaptés pour des tâches qui requièrent une compréhension complète de la phrase, telles que la classification de phrases, la reconnaissance d'entités nommées (et plus généralement la classification de mots) et les questions-réponses extractives.
-
-Les modèles les plus représentatifs de cette famille sont :
-
-- [ALBERT](https://huggingface.co/transformers/model_doc/albert.html)
-- [BERT](https://huggingface.co/transformers/model_doc/bert.html)
-- [DistilBERT](https://huggingface.co/transformers/model_doc/distilbert.html)
-- [ELECTRA](https://huggingface.co/transformers/model_doc/electra.html)
-- [RoBERTa](https://huggingface.co/transformers/model_doc/roberta.html)
+# Les modèles basés sur l'encodeur
+
+
+
+
+
+Les modèles basés sur l'encodeur utilisent uniquement l'encodeur d'un *transformer*. À chaque étape, les couches d'attention peuvent accéder à tous les mots de la phrase initiale. Ces modèles sont souvent caractérisés comme ayant une attention bidirectionnelle et sont souvent appelés *modèles d'auto-encodage*.
+
+Le pré-entraînement de ces modèles se concentre généralement sur la modification d'une phrase donnée (par exemple, en masquant des mots aléatoires dans celle-ci) et en demandant au modèle de trouver ou de reconstruire la phrase initiale.
+
+Ces modèles sont les plus adaptés pour des tâches qui requièrent une compréhension complète de la phrase, telles que la classification de phrases, la reconnaissance d'entités nommées (et plus généralement la classification de mots) et les questions-réponses extractives.
+
+Les modèles les plus représentatifs de cette famille sont :
+
+- [ALBERT](https://huggingface.co/transformers/model_doc/albert.html)
+- [BERT](https://huggingface.co/transformers/model_doc/bert.html)
+- [DistilBERT](https://huggingface.co/transformers/model_doc/distilbert.html)
+- [ELECTRA](https://huggingface.co/transformers/model_doc/electra.html)
+- [RoBERTa](https://huggingface.co/transformers/model_doc/roberta.html)
diff --git a/chapters/fr/chapter1/6.mdx b/chapters/fr/chapter1/6.mdx
index 338d6d25c..b2fbf236c 100644
--- a/chapters/fr/chapter1/6.mdx
+++ b/chapters/fr/chapter1/6.mdx
@@ -1,16 +1,21 @@
-# Les modèles basés sur le décodeur
-
-
-
-Les modèles basés sur le décodeur utilisent seulement le décodeur d'un *transformer*. À chaque étape, pour un mot donné, les couches d'attention ne peuvent strictement accéder qu'aux mots situés avant dans la phrase. Ces modèles sont souvent appelés *modèles autorégressifs*.
-
-Le pré-entraînement des modèles basés sur le décodeur se concentre généralement sur la prédiction du prochain mot dans la phrase.
-
-Ces modèles sont vraiment adaptés aux tâches qui impliquent la génération de texte.
-
-Les modèles qui représentent le mieux la famille des modèles décodeurs sont :
-
-- [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)
+# Les modèles basés sur le décodeur
+
+
+
+
+
+Les modèles basés sur le décodeur utilisent seulement le décodeur d'un *transformer*. À chaque étape, pour un mot donné, les couches d'attention ne peuvent strictement accéder qu'aux mots situés avant dans la phrase. Ces modèles sont souvent appelés *modèles autorégressifs*.
+
+Le pré-entraînement des modèles basés sur le décodeur se concentre généralement sur la prédiction du prochain mot dans la phrase.
+
+Ces modèles sont vraiment adaptés aux tâches qui impliquent la génération de texte.
+
+Les modèles qui représentent le mieux la famille des modèles décodeurs sont :
+
+- [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)
diff --git a/chapters/fr/chapter1/7.mdx b/chapters/fr/chapter1/7.mdx
index be4c527dc..7a94a5be3 100644
--- a/chapters/fr/chapter1/7.mdx
+++ b/chapters/fr/chapter1/7.mdx
@@ -1,16 +1,21 @@
-# Les modèles de séquence-à-séquence
-
-
-
-Les modèles encodeur-décodeur (également appelés modèles de séquence-à-séquence) utilisent les deux parties du *transformer*. À chaque étape, les couches d'attention de l'encodeur peuvent accéder à tous les mots de la phrase initiale, tandis que les couches d'attention du décodeur n'ont accès qu'aux mots positionnés avant un mot donné en entrée de ces couches d'attention.
-
-Le pré-entraînement de ces modèles peut être réalisé en utilisant les objectifs des modèles basés sur l'encodeur ou des modèles basés sur le décodeur. En général cela implique quelque chose de plus complexe. Par exemple, le modèle [T5](https://huggingface.co/t5-base) est pré-entraîné en remplaçant des zones aléatoires de texte (qui peuvent contenir plusieurs mots) par un masque spécial et l'objectif est alors de prédire le texte que ce masque cache.
-
-Les modèles de séquence-à-séquence sont les plus adaptés pour les tâches liées à la génération de nouvelles phrases en fonction d'une entrée donnée, comme le résumé de texte, la traduction ou la génération de questions-réponses.
-
-Les modèles qui représentent le mieux cette famille sont :
-
-- [BART](https://huggingface.co/transformers/model_doc/bart.html)
-- [mBART](https://huggingface.co/transformers/model_doc/mbart.html)
-- [Marian](https://huggingface.co/transformers/model_doc/marian.html)
-- [T5](https://huggingface.co/transformers/model_doc/t5.html)
+# Les modèles de séquence-à-séquence
+
+
+
+
+
+Les modèles encodeur-décodeur (également appelés modèles de séquence-à-séquence) utilisent les deux parties du *transformer*. À chaque étape, les couches d'attention de l'encodeur peuvent accéder à tous les mots de la phrase initiale, tandis que les couches d'attention du décodeur n'ont accès qu'aux mots positionnés avant un mot donné en entrée de ces couches d'attention.
+
+Le pré-entraînement de ces modèles peut être réalisé en utilisant les objectifs des modèles basés sur l'encodeur ou des modèles basés sur le décodeur. En général cela implique quelque chose de plus complexe. Par exemple, le modèle [T5](https://huggingface.co/t5-base) est pré-entraîné en remplaçant des zones aléatoires de texte (qui peuvent contenir plusieurs mots) par un masque spécial et l'objectif est alors de prédire le texte que ce masque cache.
+
+Les modèles de séquence-à-séquence sont les plus adaptés pour les tâches liées à la génération de nouvelles phrases en fonction d'une entrée donnée, comme le résumé de texte, la traduction ou la génération de questions-réponses.
+
+Les modèles qui représentent le mieux cette famille sont :
+
+- [BART](https://huggingface.co/transformers/model_doc/bart.html)
+- [mBART](https://huggingface.co/transformers/model_doc/mbart.html)
+- [Marian](https://huggingface.co/transformers/model_doc/marian.html)
+- [T5](https://huggingface.co/transformers/model_doc/t5.html)
diff --git a/chapters/fr/chapter1/8.mdx b/chapters/fr/chapter1/8.mdx
index e3e3c2310..9ab2ccbb3 100644
--- a/chapters/fr/chapter1/8.mdx
+++ b/chapters/fr/chapter1/8.mdx
@@ -1,34 +1,34 @@
-# Biais et limitations
-
-
-
-Si vous souhaitez utiliser un modèle pré-entraîné ou une version *finetunée* de celui-ci en production, il est important d'avoir conscience que, bien que ces modèles soient puissants, ils ont des limites. La plus importante de ces limitations est que, pour permettre le pré-entraînement des modèles sur de grandes quantités de données, les chercheurs récupèrent souvent tout le contenu qu'ils peuvent trouver et donc en prenant le meilleur et le pire de ce qui est disponible sur internet.
-
-Pour illustrer cela rapidement, revenons au pipeline *fill-mask* avec le modèle BERT :
-
-```python
-from transformers import pipeline
-
-unmasker = pipeline("fill-mask", model="bert-base-uncased")
-result = unmasker("This man works as a [MASK].") # Cet homme travaille comme [MASQUE]
-print([r["token_str"] for r in result])
-
-result = unmasker("This woman works as a [MASK].") # Cette femme travaille comme [MASQUE]
-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"]
-```
-
-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)).
-
-Donc lorsque vous utilisez ce genre d'outils, il est important de garder en tête que le modèle que vous utilisez peut rapidement générer du contenu sexiste, raciste ou homophobe. Le *finetuning* du modèle sur vos données ne fera en aucun cas disparaître ce biais intrinsèque.
+# Biais et limitations
+
+
+
+Si vous souhaitez utiliser un modèle pré-entraîné ou une version *finetunée* de celui-ci en production, il est important d'avoir conscience que, bien que ces modèles soient puissants, ils ont des limites. La plus importante de ces limitations est que, pour permettre le pré-entraînement des modèles sur de grandes quantités de données, les chercheurs récupèrent souvent tout le contenu qu'ils peuvent trouver et donc en prenant le meilleur et le pire de ce qui est disponible sur internet.
+
+Pour illustrer cela rapidement, revenons au pipeline *fill-mask* avec le modèle BERT :
+
+```python
+from transformers import pipeline
+
+unmasker = pipeline("fill-mask", model="bert-base-uncased")
+result = unmasker("This man works as a [MASK].") # Cet homme travaille comme [MASQUE]
+print([r["token_str"] for r in result])
+
+result = unmasker("This woman works as a [MASK].") # Cette femme travaille comme [MASQUE]
+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"]
+```
+
+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)).
+
+Donc lorsque vous utilisez ce genre d'outils, il est important de garder en tête que le modèle que vous utilisez peut rapidement générer du contenu sexiste, raciste ou homophobe. Le *finetuning* du modèle sur vos données ne fera en aucun cas disparaître ce biais intrinsèque.
diff --git a/chapters/fr/chapter1/9.mdx b/chapters/fr/chapter1/9.mdx
index 502bf459d..efe33bfc8 100644
--- a/chapters/fr/chapter1/9.mdx
+++ b/chapters/fr/chapter1/9.mdx
@@ -1,11 +1,16 @@
-# Résumé du chapitre
-
-Au cours de ce chapitre, vous avez vu comment approcher différents problèmes de NLP en utilisant la fonction `pipeline()` de la bibliothèque 🤗 *Transformers*. Vous avez également vu comment rechercher et utiliser des modèles dans le *Hub* ainsi que comment utiliser l'API d'inférence pour tester les modèles directement dans votre navigateur.
-
-Nous avons pu aborder le fonctionnement des *transformers* de façon générale et parler de l'importance de l'apprentissage par transfert et du *finetuning*. Un point important est que vous pouvez utiliser l'architecture complète ou seulement l'encodeur ou le décodeur, selon le type de tâche que vous souhaitez résoudre. Le tableau suivant résume ceci :
-
-| Modèle | Exemples | Tâches |
-|-------------------|--------------------------------------------|----------------------------------------------------------------------------------------------|
-| Encodeur | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa | Classification de phrase, reconnaissance d'entités nommées, extraction de question-réponse |
-| Décodeur | CTRL, GPT, GPT-2, Transformer XL | Génération de texte |
+# Résumé du chapitre
+
+
+
+Au cours de ce chapitre, vous avez vu comment approcher différents problèmes de NLP en utilisant la fonction `pipeline()` de la bibliothèque 🤗 *Transformers*. Vous avez également vu comment rechercher et utiliser des modèles dans le *Hub* ainsi que comment utiliser l'API d'inférence pour tester les modèles directement dans votre navigateur.
+
+Nous avons pu aborder le fonctionnement des *transformers* de façon générale et parler de l'importance de l'apprentissage par transfert et du *finetuning*. Un point important est que vous pouvez utiliser l'architecture complète ou seulement l'encodeur ou le décodeur, selon le type de tâche que vous souhaitez résoudre. Le tableau suivant résume ceci :
+
+| Modèle | Exemples | Tâches |
+|-------------------|--------------------------------------------|----------------------------------------------------------------------------------------------|
+| Encodeur | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa | Classification de phrase, reconnaissance d'entités nommées, extraction de question-réponse |
+| Décodeur | CTRL, GPT, GPT-2, Transformer XL | Génération de texte |
| Encodeur-décodeur | BART, T5, Marian, mBART | Résumé, traduction, génération de question-réponse |
\ No newline at end of file
diff --git a/chapters/fr/chapter2/1.mdx b/chapters/fr/chapter2/1.mdx
index 77a53ca12..aa72cc82f 100644
--- a/chapters/fr/chapter2/1.mdx
+++ b/chapters/fr/chapter2/1.mdx
@@ -1,24 +1,29 @@
-# 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()`.
-
-
- ⚠️ Afin de bénéficier de toutes les fonctionnalités disponibles avec le Hub et la bibliothèque 🤗 Transformers, nous vous recommandons de créer un compte.
-
+# 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()`.
+
+
+ ⚠️ Afin de bénéficier de toutes les fonctionnalités disponibles avec le Hub et la bibliothèque 🤗 Transformers, nous vous recommandons de créer un compte.
+
diff --git a/chapters/fr/chapter2/2.mdx b/chapters/fr/chapter2/2.mdx
index baaf88030..6aceac55a 100644
--- a/chapters/fr/chapter2/2.mdx
+++ b/chapters/fr/chapter2/2.mdx
@@ -1,349 +1,349 @@
-
-
-# Derrière le pipeline
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-
-Il s'agit de la première section dont le contenu est légèrement différent selon que vous utilisez PyTorch ou TensorFlow. Cliquez sur le bouton situé au-dessus du titre pour sélectionner la plateforme que vous préférez !
-
-
-{#if fw === 'pt'}
-
-{:else}
-
-{/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) :
-
-```python
-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 hate this so much!", # Je déteste tellement ça !
- ]
-)
-```
-
-la sortie :
-
-```python out
-[{'label': 'POSITIVE', 'score': 0.9598047137260437},
- {'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.
-
-
-
-
-
-
-Passons rapidement en revue chacun de ces éléments.
-
-## 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 [*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 :
-
-```python
-from transformers import AutoTokenizer
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-```
-
-Une fois que nous avons le *tokenizer* nous pouvons lui passer directement nos phrases et obtenir un dictionnaire prêt à être donné à notre modèle ! La seule chose qui reste à faire est de convertir en tenseurs la liste des identifiants d'entrée.
-
-Vous pouvez utiliser 🤗 *Transformers* sans avoir à vous soucier du *framework* utilisé comme *backend*. Il peut s'agir de PyTorch, de TensorFlow ou de Flax pour certains modèles. Cependant, les *transformers* n'acceptent que les *tenseurs* en entrée. Si c'est la première fois que vous entendez parler de tenseurs, vous pouvez les considérer comme des tableaux NumPy. Un tableau NumPy peut être un scalaire (0D), un vecteur (1D), une matrice (2D), ou avoir davantage de dimensions. Les tenseurs des autres *frameworks* d'apprentissage machine se comportent de manière similaire et sont généralement aussi simples à instancier que les tableaux NumPy.
-
-Pour spécifier le type de tenseurs que nous voulons récupérer (PyTorch, TensorFlow, ou simplement NumPy), nous utilisons l'argument `return_tensors` :
-
-{#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 hate this so much!", # Je déteste tellement ça !
-]
-inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
-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 hate this so much!", # Je déteste tellement ça !
-]
-inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="tf")
-print(inputs)
-```
-{/if}
-
-Ne vous préoccupez pas encore du remplissage (*padding*) et de la troncature, nous les expliquerons plus tard. Les principales choses à retenir ici sont que vous pouvez passer une phrase ou une liste de phrases, ainsi que spécifier le type de tenseurs que vous voulez récupérer (si aucun type n'est passé, par défaut vous obtiendrez une liste de listes comme résultat).
-
-{#if fw === 'pt'}
-
-Voici à quoi ressemblent les résultats sous forme de tenseurs PyTorch :
-
-```python out
-{
- 'input_ids': tensor([
- [ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102],
- [ 101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0]
- ]),
- '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, 0, 0, 0, 0, 0, 0, 0, 0, 0]
- ])
-}
-```
-{:else}
-
-Voici à quoi ressemblent les résultats sous forme de tenseurs TensorFlow :
-
-```python out
-{
- 'input_ids': ,
- 'attention_mask':
-}
-```
-{/if}
-
-La sortie elle-même est un dictionnaire contenant deux clés : `input_ids` et `attention_mask`. `input_ids` contient deux lignes d'entiers (une pour chaque phrase) qui sont les identifiants uniques des *tokens* dans chaque phrase. Nous expliquerons ce qu'est l'`attention_mask` plus tard dans ce chapitre.
-
-## Passage au modèle
-
-{#if fw === 'pt'}
-Nous pouvons télécharger notre modèle pré-entraîné de la même manière que nous l'avons fait avec notre *tokenizer*. 🤗 *Transformers* fournit une classe `AutoModel` qui possède également une méthode `from_pretrained()` :
-
-```python
-from transformers import AutoModel
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-model = AutoModel.from_pretrained(checkpoint)
-```
-{:else}
-Nous pouvons télécharger notre modèle prétraîné de la même manière que nous l'avons fait avec notre *tokenizer*. 🤗 *Transformers* fournit une classe `TFAutoModel` qui possède également une méthode `from_pretrained()` :
-
-```python
-from transformers import TFAutoModel
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-model = TFAutoModel.from_pretrained(checkpoint)
-```
-{/if}
-
-Dans cet extrait de code, nous avons téléchargé le même *checkpoint* que nous avons utilisé dans notre pipeline auparavant (il devrait en fait avoir déjà été mis en cache) et instancié un modèle avec lui.
-
-Cette architecture ne contient que le module de *transformer* de base : étant donné certaines entrées, il produit ce que nous appellerons des *états cachés*, également connus sous le nom de *caractéristiques*.
-Pour chaque entrée du modèle, nous récupérons un vecteur en grande dimension représentant la **compréhension contextuelle de cette entrée par le *transformer***.
-
-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.
-
-### Un vecteur de grande dimension ?
-
-Le vecteur produit en sortie par le *transformer* est généralement de grande dimension. Il a généralement trois dimensions :
-
-- **la taille du lot** : le nombre de séquences traitées à la fois (2 dans notre exemple),
-- **la longueur de la séquence** : la longueur de la représentation numérique de la séquence (16 dans notre exemple),
-- **la taille cachée** : la dimension du vecteur de chaque entrée du modèle.
-
-On dit qu'il est de « grande dimension » en raison de la dernière valeur. La taille cachée peut être très grande (généralement 768 pour les petits modèles et pour les grands modèles cela peut atteindre 3072 voire plus).
-
-Nous pouvons le constater si nous alimentons notre modèle avec les entrées que nous avons prétraitées :
-
-
-{#if fw === 'pt'}
-```python
-outputs = model(**inputs)
-print(outputs.last_hidden_state.shape)
-```
-
-```python out
-torch.Size([2, 16, 768])
-```
-{:else}
-```py
-outputs = model(inputs)
-print(outputs.last_hidden_state.shape)
-```
-
-```python out
-(2, 16, 768)
-```
-{/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]`).
-
-### 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 :
-
-
-
-
-
-La sortie du *transformer* est envoyée directement à la tête du modèle pour être traitée.
-Dans ce diagramme, le modèle est représenté par sa couche d’enchâssement et les couches suivantes. La couche d’enchâssement convertit chaque identifiant d'entrée dans l'entrée tokenisée en un vecteur qui représente le *token* associé. Les couches suivantes manipulent ces vecteurs en utilisant le mécanisme d'attention pour produire la représentation finale des phrases.
-Il existe de nombreuses architectures différentes disponibles dans la bibliothèque 🤗 *Transformers*, chacune étant conçue autour de la prise en charge d'une tâche spécifique. En voici une liste non exhaustive :
-- `*Model` (récupérer les états cachés)
-- `*ForCausalLM`
-- `*ForMaskedLM`
-- `*ForMultipleChoice`
-- `*ForQuestionAnswering`
-- `*ForSequenceClassification`
-- `*ForTokenClassification`
-- et autres 🤗
-
-{#if fw === 'pt'}
-Pour notre exemple, nous avons besoin d'un modèle avec une tête de classification de séquence (pour pouvoir classer les phrases comme positives ou négatives). Donc, nous n'utilisons pas réellement la classe `AutoModel` mais plutôt `AutoModelForSequenceClassification` :
-```python
-from transformers import AutoModelForSequenceClassification
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-outputs = model(**inputs)
-```
-{:else}
-Pour notre exemple, nous avons besoin d'un modèle avec une tête de classification de séquence (pour pouvoir classer les phrases comme positives ou négatives). Donc, nous n'utilisons pas réellement la classe ` TFAutoModel` mais plutôt ` TFAutoModelForSequenceClassification` :
-```python
-from transformers import TFAutoModelForSequenceClassification
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
-outputs = model(inputs)
-```
-{/if}
-
-Maintenant, si nous examinons la forme de nos entrées, la dimensionnalité est beaucoup plus faible. La tête du modèle prend en entrée les vecteurs de grande dimension que nous avons vus précédemment et elle produit des vecteurs contenant deux valeurs (une par étiquette) :
-```python
-print(outputs.logits.shape)
-```
-
-{#if fw === 'pt'}
-```python out
-torch.Size([2, 2])
-```
-{:else}
-```python out
-(2, 2)
-```
-{/if}
-
-Comme nous n'avons que deux phrases et deux étiquettes, le résultat que nous obtenons est de forme 2 x 2
-
-## Post-traitement de la sortie
-
-Les valeurs que nous obtenons en sortie de notre modèle n'ont pas nécessairement de sens en elles-mêmes. Jetons-y un coup d’œil :
-
-```python
-print(outputs.logits)
-```
-
-{#if fw === 'pt'}
-```python out
-tensor([[-1.5607, 1.6123],
- [ 4.1692, -3.3464]], grad_fn=)
-```
-{:else}
-```python out
-
-```
-{/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) :
-
-{#if fw === 'pt'}
-```py
-import torch
-
-predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
-print(predictions)
-```
-{:else}
-```py
-import tensorflow as tf
-
-predictions = tf.math.softmax(outputs.logits, axis=-1)
-print(predictions)
-```
-{/if}
-
-{#if fw === 'pt'}
-```python out
-tensor([[4.0195e-02, 9.5980e-01],
- [9.9946e-01, 5.4418e-04]], grad_fn=)
-```
-{:else}
-```python out
-tf.Tensor(
-[[4.01951671e-02 9.59804833e-01]
- [9.9945587e-01 5.4418424e-04]], shape=(2, 2), dtype=float32)
-```
-{/if}
-
-Maintenant nous pouvons voir que le modèle a prédit `[0.0402, 0.9598]` pour la première phrase et `[0.9995, 0.0005]` pour la seconde. Ce sont des scores de probabilité reconnaissables.
-
-Pour obtenir les étiquettes correspondant à chaque position, nous pouvons inspecter l'attribut `id2label` de la configuration du modèle (plus de détails dans la section suivante) :
-
-```python
-model.config.id2label
-```
-
-```python out
-{0: 'NEGATIVE', 1: 'POSITIVE'}
-```
-
-Nous pouvons maintenant conclure que le modèle a prédit ce qui suit :
-
-- première phrase : NEGATIVE: 0.0402, POSITIVE: 0.9598
-- deuxième phrase : NEGATIVE: 0.9995, POSITIVE: 0.0005
-
-Nous avons reproduit avec succès les trois étapes du pipeline : prétraitement avec les *tokenizers*, passage des entrées dans le modèle et post-traitement ! Prenons maintenant le temps de nous plonger plus profondément dans chacune de ces étapes.
-
-
-
-✏️ **Essayez !** Choisissez deux (ou plus) textes de votre choix (en anglais) et faites-les passer par le pipeline `sentiment-analysis`. Reproduisez ensuite vous-même les étapes vues ici et vérifiez que vous obtenez les mêmes résultats !
-
-
+
+
+# Derrière le pipeline
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+
+Il s'agit de la première section dont le contenu est légèrement différent selon que vous utilisez PyTorch ou TensorFlow. Cliquez sur le bouton situé au-dessus du titre pour sélectionner la plateforme que vous préférez !
+
+
+{#if fw === 'pt'}
+
+{:else}
+
+{/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) :
+
+```python
+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 hate this so much!", # Je déteste tellement ça !
+ ]
+)
+```
+
+la sortie :
+
+```python out
+[{'label': 'POSITIVE', 'score': 0.9598047137260437},
+ {'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.
+
+
+
+
+
+
+Passons rapidement en revue chacun de ces éléments.
+
+## 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 [*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 :
+
+```python
+from transformers import AutoTokenizer
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+
+Une fois que nous avons le *tokenizer* nous pouvons lui passer directement nos phrases et obtenir un dictionnaire prêt à être donné à notre modèle ! La seule chose qui reste à faire est de convertir en tenseurs la liste des identifiants d'entrée.
+
+Vous pouvez utiliser 🤗 *Transformers* sans avoir à vous soucier du *framework* utilisé comme *backend*. Il peut s'agir de PyTorch, de TensorFlow ou de Flax pour certains modèles. Cependant, les *transformers* n'acceptent que les *tenseurs* en entrée. Si c'est la première fois que vous entendez parler de tenseurs, vous pouvez les considérer comme des tableaux NumPy. Un tableau NumPy peut être un scalaire (0D), un vecteur (1D), une matrice (2D), ou avoir davantage de dimensions. Les tenseurs des autres *frameworks* d'apprentissage machine se comportent de manière similaire et sont généralement aussi simples à instancier que les tableaux NumPy.
+
+Pour spécifier le type de tenseurs que nous voulons récupérer (PyTorch, TensorFlow, ou simplement NumPy), nous utilisons l'argument `return_tensors` :
+
+{#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 hate this so much!", # Je déteste tellement ça !
+]
+inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
+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 hate this so much!", # Je déteste tellement ça !
+]
+inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="tf")
+print(inputs)
+```
+{/if}
+
+Ne vous préoccupez pas encore du remplissage (*padding*) et de la troncature, nous les expliquerons plus tard. Les principales choses à retenir ici sont que vous pouvez passer une phrase ou une liste de phrases, ainsi que spécifier le type de tenseurs que vous voulez récupérer (si aucun type n'est passé, par défaut vous obtiendrez une liste de listes comme résultat).
+
+{#if fw === 'pt'}
+
+Voici à quoi ressemblent les résultats sous forme de tenseurs PyTorch :
+
+```python out
+{
+ 'input_ids': tensor([
+ [ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102],
+ [ 101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0]
+ ]),
+ '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, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+ ])
+}
+```
+{:else}
+
+Voici à quoi ressemblent les résultats sous forme de tenseurs TensorFlow :
+
+```python out
+{
+ 'input_ids': ,
+ 'attention_mask':
+}
+```
+{/if}
+
+La sortie elle-même est un dictionnaire contenant deux clés : `input_ids` et `attention_mask`. `input_ids` contient deux lignes d'entiers (une pour chaque phrase) qui sont les identifiants uniques des *tokens* dans chaque phrase. Nous expliquerons ce qu'est l'`attention_mask` plus tard dans ce chapitre.
+
+## Passage au modèle
+
+{#if fw === 'pt'}
+Nous pouvons télécharger notre modèle pré-entraîné de la même manière que nous l'avons fait avec notre *tokenizer*. 🤗 *Transformers* fournit une classe `AutoModel` qui possède également une méthode `from_pretrained()` :
+
+```python
+from transformers import AutoModel
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+model = AutoModel.from_pretrained(checkpoint)
+```
+{:else}
+Nous pouvons télécharger notre modèle prétraîné de la même manière que nous l'avons fait avec notre *tokenizer*. 🤗 *Transformers* fournit une classe `TFAutoModel` qui possède également une méthode `from_pretrained()` :
+
+```python
+from transformers import TFAutoModel
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+model = TFAutoModel.from_pretrained(checkpoint)
+```
+{/if}
+
+Dans cet extrait de code, nous avons téléchargé le même *checkpoint* que nous avons utilisé dans notre pipeline auparavant (il devrait en fait avoir déjà été mis en cache) et instancié un modèle avec lui.
+
+Cette architecture ne contient que le module de *transformer* de base : étant donné certaines entrées, il produit ce que nous appellerons des *états cachés*, également connus sous le nom de *caractéristiques*.
+Pour chaque entrée du modèle, nous récupérons un vecteur en grande dimension représentant la **compréhension contextuelle de cette entrée par le *transformer***.
+
+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.
+
+### Un vecteur de grande dimension ?
+
+Le vecteur produit en sortie par le *transformer* est généralement de grande dimension. Il a généralement trois dimensions :
+
+- **la taille du lot** : le nombre de séquences traitées à la fois (2 dans notre exemple),
+- **la longueur de la séquence** : la longueur de la représentation numérique de la séquence (16 dans notre exemple),
+- **la taille cachée** : la dimension du vecteur de chaque entrée du modèle.
+
+On dit qu'il est de « grande dimension » en raison de la dernière valeur. La taille cachée peut être très grande (généralement 768 pour les petits modèles et pour les grands modèles cela peut atteindre 3072 voire plus).
+
+Nous pouvons le constater si nous alimentons notre modèle avec les entrées que nous avons prétraitées :
+
+
+{#if fw === 'pt'}
+```python
+outputs = model(**inputs)
+print(outputs.last_hidden_state.shape)
+```
+
+```python out
+torch.Size([2, 16, 768])
+```
+{:else}
+```py
+outputs = model(inputs)
+print(outputs.last_hidden_state.shape)
+```
+
+```python out
+(2, 16, 768)
+```
+{/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]`).
+
+### 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 :
+
+
+
+
+
+La sortie du *transformer* est envoyée directement à la tête du modèle pour être traitée.
+Dans ce diagramme, le modèle est représenté par sa couche d’enchâssement et les couches suivantes. La couche d’enchâssement convertit chaque identifiant d'entrée dans l'entrée tokenisée en un vecteur qui représente le *token* associé. Les couches suivantes manipulent ces vecteurs en utilisant le mécanisme d'attention pour produire la représentation finale des phrases.
+Il existe de nombreuses architectures différentes disponibles dans la bibliothèque 🤗 *Transformers*, chacune étant conçue autour de la prise en charge d'une tâche spécifique. En voici une liste non exhaustive :
+- `*Model` (récupérer les états cachés)
+- `*ForCausalLM`
+- `*ForMaskedLM`
+- `*ForMultipleChoice`
+- `*ForQuestionAnswering`
+- `*ForSequenceClassification`
+- `*ForTokenClassification`
+- et autres 🤗
+
+{#if fw === 'pt'}
+Pour notre exemple, nous avons besoin d'un modèle avec une tête de classification de séquence (pour pouvoir classer les phrases comme positives ou négatives). Donc, nous n'utilisons pas réellement la classe `AutoModel` mais plutôt `AutoModelForSequenceClassification` :
+```python
+from transformers import AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+outputs = model(**inputs)
+```
+{:else}
+Pour notre exemple, nous avons besoin d'un modèle avec une tête de classification de séquence (pour pouvoir classer les phrases comme positives ou négatives). Donc, nous n'utilisons pas réellement la classe ` TFAutoModel` mais plutôt ` TFAutoModelForSequenceClassification` :
+```python
+from transformers import TFAutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
+outputs = model(inputs)
+```
+{/if}
+
+Maintenant, si nous examinons la forme de nos entrées, la dimensionnalité est beaucoup plus faible. La tête du modèle prend en entrée les vecteurs de grande dimension que nous avons vus précédemment et elle produit des vecteurs contenant deux valeurs (une par étiquette) :
+```python
+print(outputs.logits.shape)
+```
+
+{#if fw === 'pt'}
+```python out
+torch.Size([2, 2])
+```
+{:else}
+```python out
+(2, 2)
+```
+{/if}
+
+Comme nous n'avons que deux phrases et deux étiquettes, le résultat que nous obtenons est de forme 2 x 2
+
+## Post-traitement de la sortie
+
+Les valeurs que nous obtenons en sortie de notre modèle n'ont pas nécessairement de sens en elles-mêmes. Jetons-y un coup d’œil :
+
+```python
+print(outputs.logits)
+```
+
+{#if fw === 'pt'}
+```python out
+tensor([[-1.5607, 1.6123],
+ [ 4.1692, -3.3464]], grad_fn=)
+```
+{:else}
+```python out
+
+```
+{/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) :
+
+{#if fw === 'pt'}
+```py
+import torch
+
+predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
+print(predictions)
+```
+{:else}
+```py
+import tensorflow as tf
+
+predictions = tf.math.softmax(outputs.logits, axis=-1)
+print(predictions)
+```
+{/if}
+
+{#if fw === 'pt'}
+```python out
+tensor([[4.0195e-02, 9.5980e-01],
+ [9.9946e-01, 5.4418e-04]], grad_fn=)
+```
+{:else}
+```python out
+tf.Tensor(
+[[4.01951671e-02 9.59804833e-01]
+ [9.9945587e-01 5.4418424e-04]], shape=(2, 2), dtype=float32)
+```
+{/if}
+
+Maintenant nous pouvons voir que le modèle a prédit `[0.0402, 0.9598]` pour la première phrase et `[0.9995, 0.0005]` pour la seconde. Ce sont des scores de probabilité reconnaissables.
+
+Pour obtenir les étiquettes correspondant à chaque position, nous pouvons inspecter l'attribut `id2label` de la configuration du modèle (plus de détails dans la section suivante) :
+
+```python
+model.config.id2label
+```
+
+```python out
+{0: 'NEGATIVE', 1: 'POSITIVE'}
+```
+
+Nous pouvons maintenant conclure que le modèle a prédit ce qui suit :
+
+- première phrase : NEGATIVE: 0.0402, POSITIVE: 0.9598
+- deuxième phrase : NEGATIVE: 0.9995, POSITIVE: 0.0005
+
+Nous avons reproduit avec succès les trois étapes du pipeline : prétraitement avec les *tokenizers*, passage des entrées dans le modèle et post-traitement ! Prenons maintenant le temps de nous plonger plus profondément dans chacune de ces étapes.
+
+
+
+✏️ **Essayez !** Choisissez deux (ou plus) textes de votre choix (en anglais) et faites-les passer par le pipeline `sentiment-analysis`. Reproduisez ensuite vous-même les étapes vues ici et vérifiez que vous obtenez les mêmes résultats !
+
+
diff --git a/chapters/fr/chapter2/3.mdx b/chapters/fr/chapter2/3.mdx
index 9fce3d02f..a16dc192d 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 2015763fb..6445f167e 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 fb85ff966..3e9bc09e4 100644
--- a/chapters/fr/chapter2/5.mdx
+++ b/chapters/fr/chapter2/5.mdx
@@ -1,354 +1,354 @@
-
-
-# Manipulation de plusieurs séquences
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-{#if fw === 'pt'}
-
-{:else}
-
-{/if}
-
-Dans la section précédente, nous avons exploré le cas d'utilisation le plus simple : faire une inférence sur une seule séquence de petite longueur. Cependant, certaines questions émergent déjà :
-
-- comment gérer de plusieurs séquences ?
-- comment gérer de plusieurs séquences *de longueurs différentes* ?
-- les indices du vocabulaire sont-ils les seules entrées qui permettent à un modèle de bien fonctionner ?
-- existe-t-il une séquence trop longue ?
-
-Voyons quels types de problèmes ces questions posent et comment nous pouvons les résoudre en utilisant l'API 🤗 *Transformers*.
-
-## Les modèles attendent un batch d'entrées
-
-Dans l'exercice précédent, vous avez vu comment les séquences sont traduites en listes de nombres.
-Convertissons cette liste de nombres en un tenseur et envoyons-le au modèle :
-
-{#if fw === 'pt'}
-```py
-import torch
-from transformers import AutoTokenizer, AutoModelForSequenceClassification
-
-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.
-
-tokens = tokenizer.tokenize(sequence)
-ids = tokenizer.convert_tokens_to_ids(tokens)
-input_ids = torch.tensor(ids)
-# Cette ligne va échouer.
-model(input_ids)
-```
-
-```python out
-IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)
-```
-{:else}
-```py
-import tensorflow as tf
-from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
-
-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.
-
-tokens = tokenizer.tokenize(sequence)
-ids = tokenizer.convert_tokens_to_ids(tokens)
-input_ids = tf.constant(ids)
-# This line will fail.
-model(input_ids)
-```
-
-```py out
-InvalidArgumentError: Input to reshape is a tensor with 14 values, but the requested shape has 196 [Op:Reshape]
-```
-{/if}
-
-Pourquoi cela a échoué ? Nous avons suivi les étapes du pipeline de la section 2.
-
-Le problème est que nous avons envoyé une seule séquence au modèle, alors que les modèles de l’API 🤗 *Transformers* attendent plusieurs phrases par défaut. Ici, nous avons essayé de faire ce que le *tokenizer* fait en coulisses lorsque nous l'avons appliqué à une `séquence`. Cependant si vous regardez de près, vous verrez qu'il n'a pas seulement converti la liste des identifiants d'entrée en un tenseur mais aussi ajouté une dimension par-dessus :
-
-
-{#if fw === 'pt'}
-```py
-tokenized_inputs = tokenizer(sequence, return_tensors="pt")
-print(tokenized_inputs["input_ids"])
-```
-
-```python out
-tensor([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172,
- 2607, 2026, 2878, 2166, 1012, 102]])
-```
-{:else}
-```py
-tokenized_inputs = tokenizer(sequence, return_tensors="tf")
-print(tokenized_inputs["input_ids"])
-```
-
-```py out
-
-```
-{/if}
-
-Essayons à nouveau en ajoutant une nouvelle dimension :
-
-{#if fw === 'pt'}
-```py
-import torch
-from transformers import AutoTokenizer, AutoModelForSequenceClassification
-
-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.
-
-
-tokens = tokenizer.tokenize(sequence)
-ids = tokenizer.convert_tokens_to_ids(tokens)
-
-input_ids = torch.tensor([ids])
-print("Input IDs:", input_ids)
-
-output = model(input_ids)
-print("Logits:", output.logits)
-```
-{:else}
-```py
-import tensorflow as tf
-from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
-
-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.
-
-
-tokens = tokenizer.tokenize(sequence)
-ids = tokenizer.convert_tokens_to_ids(tokens)
-
-input_ids = tf.constant([ids])
-print("Input IDs:", input_ids)
-
-output = model(input_ids)
-print("Logits:", output.logits)
-```
-{/if}
-
-Nous affichons les identifiants d'entrée ainsi que les logits résultants. Voici la sortie :
-
-{#if fw === 'pt'}
-```python out
-Input IDs: [[ 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]]
-Logits: [[-2.7276, 2.8789]]
-```
-{:else}
-```py out
-Input IDs: tf.Tensor(
-[[ 1045 1005 2310 2042 3403 2005 1037 17662 12172 2607 2026 2878
- 2166 1012]], shape=(1, 14), dtype=int32)
-Logits: tf.Tensor([[-2.7276208 2.8789377]], shape=(1, 2), dtype=float32)
-```
-{/if}
-
-Le « *batching* » est l'acte d'envoyer plusieurs phrases à travers le modèle, toutes en même temps. Si vous n'avez qu'une seule phrase, vous pouvez simplement construire un batch avec une seule séquence :
-
-```
-batched_ids = [ids, ids]
-```
-
-Il s'agit d'un batch de deux séquences identiques !
-
-
-
-✏️ **Essayez !** Convertissez cette liste `batched_ids` en un tenseur et passez-la dans votre modèle. Vérifiez que vous obtenez les mêmes logits que précédemment (mais deux fois) !
-
-
-
-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
-
-La liste de listes suivante ne peut pas être convertie en un tenseur :
-
-
-```py no-format
-batched_ids = [
- [200, 200, 200],
- [200, 200]
-]
-```
-
-Afin de contourner ce problème, nous utilisons le *padding* pour que nos tenseurs aient une forme rectangulaire. Le *padding* permet de s'assurer que toutes nos phrases ont la même longueur en ajoutant un mot spécial appelé *padding token* aux phrases ayant moins de valeurs. Par exemple, si vous avez 10 phrases de 10 mots et 1 phrase de 20 mots, le *padding* fait en sorte que toutes les phrases aient 20 mots. Dans notre exemple, le tenseur résultant ressemble à ceci :
-
-```py no-format
-padding_id = 100
-
-batched_ids = [
- [200, 200, 200],
- [200, 200, padding_id],
-]
-```
-
-L'identifiant du jeton de *padding* peut être trouvé dans `tokenizer.pad_token_id`. Utilisons-le et envoyons nos deux phrases à travers le modèle premièrement individuellement puis en étant mises dans un même batch :
-
-{#if fw === 'pt'}
-```py no-format
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-
-sequence1_ids = [[200, 200, 200]]
-sequence2_ids = [[200, 200]]
-batched_ids = [
- [200, 200, 200],
- [200, 200, tokenizer.pad_token_id],
-]
-
-print(model(torch.tensor(sequence1_ids)).logits)
-print(model(torch.tensor(sequence2_ids)).logits)
-print(model(torch.tensor(batched_ids)).logits)
-```
-
-```python out
-tensor([[ 1.5694, -1.3895]], grad_fn=)
-tensor([[ 0.5803, -0.4125]], grad_fn=)
-tensor([[ 1.5694, -1.3895],
- [ 1.3373, -1.2163]], grad_fn=)
-```
-{:else}
-```py no-format
-model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
-
-sequence1_ids = [[200, 200, 200]]
-sequence2_ids = [[200, 200]]
-batched_ids = [
- [200, 200, 200],
- [200, 200, tokenizer.pad_token_id],
-]
-
-print(model(tf.constant(sequence1_ids)).logits)
-print(model(tf.constant(sequence2_ids)).logits)
-print(model(tf.constant(batched_ids)).logits)
-```
-
-```py out
-tf.Tensor([[ 1.5693678 -1.3894581]], shape=(1, 2), dtype=float32)
-tf.Tensor([[ 0.5803005 -0.41252428]], shape=(1, 2), dtype=float32)
-tf.Tensor(
-[[ 1.5693681 -1.3894582]
- [ 1.3373486 -1.2163193]], shape=(2, 2), dtype=float32)
-```
-{/if}
-
-Il y a quelque chose qui ne va pas avec les logits de notre prédiction avec les séquences mises dans un même batch. La deuxième ligne devrait être la même que les logits pour la deuxième phrase, mais nous avons des valeurs complètement différentes !
-
-C'est parce que dans un *transformer* les couches d’attention *contextualisent* chaque *token*. Celles-ci prennent en compte les *tokens* de *padding* puisqu'elles analysent tous les *tokens* d'une séquence. Pour obtenir le même résultat lorsque l'on passe dans notre modèle des phrases individuelles de différentes longueurs ou un batch composé de mêmes phrases avec *padding*, nous devons dire à ces couches d'attention d'ignorer les jetons de *padding*. Ceci est fait en utilisant un masque d'attention.
-
-
-## Masques d'attention
-
-Les masques d'attention sont des tenseurs ayant exactement la même forme que le tenseur d'identifiants d'entrée, remplis de 0 et de 1 :
-- 1 indique que les *tokens* correspondants doivent être analysés
-- 0 indique que les *tokens* correspondants ne doivent pas être analysés (c'est-à-dire qu'ils doivent être ignorés par les couches d'attention du modèle).
-
-Complétons l'exemple précédent avec un masque d'attention :
-
-
-{#if fw === 'pt'}
-```py no-format
-batched_ids = [
- [200, 200, 200],
- [200, 200, tokenizer.pad_token_id],
-]
-
-attention_mask = [
- [1, 1, 1],
- [1, 1, 0],
-]
-
-outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask))
-print(outputs.logits)
-```
-
-```python out
-tensor([[ 1.5694, -1.3895],
- [ 0.5803, -0.4125]], grad_fn=)
-```
-{:else}
-```py no-format
-batched_ids = [
- [200, 200, 200],
- [200, 200, tokenizer.pad_token_id],
-]
-
-attention_mask = [
- [1, 1, 1],
- [1, 1, 0],
-]
-
-outputs = model(tf.constant(batched_ids), attention_mask=tf.constant(attention_mask))
-print(outputs.logits)
-```
-
-```py out
-tf.Tensor(
-[[ 1.5693681 -1.3894582 ]
- [ 0.5803021 -0.41252586]], shape=(2, 2), dtype=float32)
-```
-{/if}
-
-Nous obtenons maintenant les mêmes logits pour la deuxième phrase du batch.
-
-Remarquez comment la dernière valeur de la deuxième séquence est un identifiant de *padding* valant 0 dans le masque d'attention.
-
-
-
-
-✏️ **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 !
-
-
-
-
-## Séquences plus longues
-
-Les *transformers* acceptent en entrée que des séquences d’une longueur limitée. La plupart des modèles traitent des séquences allant jusqu'à 512 ou 1024 *tokens* et plantent lorsqu'on leur demande de traiter des séquences plus longues. Il existe deux solutions à ce problème :
-
-- utiliser un modèle avec une longueur de séquence supportée plus longue,
-- tronquer les séquences.
-
-Certains modèles sont spécialisés dans le traitement de très longues séquences comme par exemple le [Longformer](https://huggingface.co/transformers/model_doc/longformer.html) ou le [LED](https://huggingface.co/transformers/model_doc/led.html). Si vous travaillez sur une tâche qui nécessite de très longues séquences, nous vous recommandons de jeter un coup d'œil à ces modèles.
-
-Sinon, nous vous recommandons de tronquer vos séquences en spécifiant le paramètre `max_sequence_length` :
-
-
-```py
-sequence = sequence[:max_sequence_length]
-```
+
+
+# Manipulation de plusieurs séquences
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+{#if fw === 'pt'}
+
+{:else}
+
+{/if}
+
+Dans la section précédente, nous avons exploré le cas d'utilisation le plus simple : faire une inférence sur une seule séquence de petite longueur. Cependant, certaines questions émergent déjà :
+
+- comment gérer de plusieurs séquences ?
+- comment gérer de plusieurs séquences *de longueurs différentes* ?
+- les indices du vocabulaire sont-ils les seules entrées qui permettent à un modèle de bien fonctionner ?
+- existe-t-il une séquence trop longue ?
+
+Voyons quels types de problèmes ces questions posent et comment nous pouvons les résoudre en utilisant l'API 🤗 *Transformers*.
+
+## Les modèles attendent un batch d'entrées
+
+Dans l'exercice précédent, vous avez vu comment les séquences sont traduites en listes de nombres.
+Convertissons cette liste de nombres en un tenseur et envoyons-le au modèle :
+
+{#if fw === 'pt'}
+```py
+import torch
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+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.
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+input_ids = torch.tensor(ids)
+# Cette ligne va échouer.
+model(input_ids)
+```
+
+```python out
+IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)
+```
+{:else}
+```py
+import tensorflow as tf
+from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
+
+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.
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+input_ids = tf.constant(ids)
+# This line will fail.
+model(input_ids)
+```
+
+```py out
+InvalidArgumentError: Input to reshape is a tensor with 14 values, but the requested shape has 196 [Op:Reshape]
+```
+{/if}
+
+Pourquoi cela a échoué ? Nous avons suivi les étapes du pipeline de la section 2.
+
+Le problème est que nous avons envoyé une seule séquence au modèle, alors que les modèles de l’API 🤗 *Transformers* attendent plusieurs phrases par défaut. Ici, nous avons essayé de faire ce que le *tokenizer* fait en coulisses lorsque nous l'avons appliqué à une `séquence`. Cependant si vous regardez de près, vous verrez qu'il n'a pas seulement converti la liste des identifiants d'entrée en un tenseur mais aussi ajouté une dimension par-dessus :
+
+
+{#if fw === 'pt'}
+```py
+tokenized_inputs = tokenizer(sequence, return_tensors="pt")
+print(tokenized_inputs["input_ids"])
+```
+
+```python out
+tensor([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172,
+ 2607, 2026, 2878, 2166, 1012, 102]])
+```
+{:else}
+```py
+tokenized_inputs = tokenizer(sequence, return_tensors="tf")
+print(tokenized_inputs["input_ids"])
+```
+
+```py out
+
+```
+{/if}
+
+Essayons à nouveau en ajoutant une nouvelle dimension :
+
+{#if fw === 'pt'}
+```py
+import torch
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+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.
+
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+
+input_ids = torch.tensor([ids])
+print("Input IDs:", input_ids)
+
+output = model(input_ids)
+print("Logits:", output.logits)
+```
+{:else}
+```py
+import tensorflow as tf
+from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
+
+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.
+
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+
+input_ids = tf.constant([ids])
+print("Input IDs:", input_ids)
+
+output = model(input_ids)
+print("Logits:", output.logits)
+```
+{/if}
+
+Nous affichons les identifiants d'entrée ainsi que les logits résultants. Voici la sortie :
+
+{#if fw === 'pt'}
+```python out
+Input IDs: [[ 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]]
+Logits: [[-2.7276, 2.8789]]
+```
+{:else}
+```py out
+Input IDs: tf.Tensor(
+[[ 1045 1005 2310 2042 3403 2005 1037 17662 12172 2607 2026 2878
+ 2166 1012]], shape=(1, 14), dtype=int32)
+Logits: tf.Tensor([[-2.7276208 2.8789377]], shape=(1, 2), dtype=float32)
+```
+{/if}
+
+Le « *batching* » est l'acte d'envoyer plusieurs phrases à travers le modèle, toutes en même temps. Si vous n'avez qu'une seule phrase, vous pouvez simplement construire un batch avec une seule séquence :
+
+```
+batched_ids = [ids, ids]
+```
+
+Il s'agit d'un batch de deux séquences identiques !
+
+
+
+✏️ **Essayez !** Convertissez cette liste `batched_ids` en un tenseur et passez-la dans votre modèle. Vérifiez que vous obtenez les mêmes logits que précédemment (mais deux fois) !
+
+
+
+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
+
+La liste de listes suivante ne peut pas être convertie en un tenseur :
+
+
+```py no-format
+batched_ids = [
+ [200, 200, 200],
+ [200, 200]
+]
+```
+
+Afin de contourner ce problème, nous utilisons le *padding* pour que nos tenseurs aient une forme rectangulaire. Le *padding* permet de s'assurer que toutes nos phrases ont la même longueur en ajoutant un mot spécial appelé *padding token* aux phrases ayant moins de valeurs. Par exemple, si vous avez 10 phrases de 10 mots et 1 phrase de 20 mots, le *padding* fait en sorte que toutes les phrases aient 20 mots. Dans notre exemple, le tenseur résultant ressemble à ceci :
+
+```py no-format
+padding_id = 100
+
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, padding_id],
+]
+```
+
+L'identifiant du jeton de *padding* peut être trouvé dans `tokenizer.pad_token_id`. Utilisons-le et envoyons nos deux phrases à travers le modèle premièrement individuellement puis en étant mises dans un même batch :
+
+{#if fw === 'pt'}
+```py no-format
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+
+sequence1_ids = [[200, 200, 200]]
+sequence2_ids = [[200, 200]]
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, tokenizer.pad_token_id],
+]
+
+print(model(torch.tensor(sequence1_ids)).logits)
+print(model(torch.tensor(sequence2_ids)).logits)
+print(model(torch.tensor(batched_ids)).logits)
+```
+
+```python out
+tensor([[ 1.5694, -1.3895]], grad_fn=)
+tensor([[ 0.5803, -0.4125]], grad_fn=)
+tensor([[ 1.5694, -1.3895],
+ [ 1.3373, -1.2163]], grad_fn=)
+```
+{:else}
+```py no-format
+model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
+
+sequence1_ids = [[200, 200, 200]]
+sequence2_ids = [[200, 200]]
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, tokenizer.pad_token_id],
+]
+
+print(model(tf.constant(sequence1_ids)).logits)
+print(model(tf.constant(sequence2_ids)).logits)
+print(model(tf.constant(batched_ids)).logits)
+```
+
+```py out
+tf.Tensor([[ 1.5693678 -1.3894581]], shape=(1, 2), dtype=float32)
+tf.Tensor([[ 0.5803005 -0.41252428]], shape=(1, 2), dtype=float32)
+tf.Tensor(
+[[ 1.5693681 -1.3894582]
+ [ 1.3373486 -1.2163193]], shape=(2, 2), dtype=float32)
+```
+{/if}
+
+Il y a quelque chose qui ne va pas avec les logits de notre prédiction avec les séquences mises dans un même batch. La deuxième ligne devrait être la même que les logits pour la deuxième phrase, mais nous avons des valeurs complètement différentes !
+
+C'est parce que dans un *transformer* les couches d’attention *contextualisent* chaque *token*. Celles-ci prennent en compte les *tokens* de *padding* puisqu'elles analysent tous les *tokens* d'une séquence. Pour obtenir le même résultat lorsque l'on passe dans notre modèle des phrases individuelles de différentes longueurs ou un batch composé de mêmes phrases avec *padding*, nous devons dire à ces couches d'attention d'ignorer les jetons de *padding*. Ceci est fait en utilisant un masque d'attention.
+
+
+## Masques d'attention
+
+Les masques d'attention sont des tenseurs ayant exactement la même forme que le tenseur d'identifiants d'entrée, remplis de 0 et de 1 :
+- 1 indique que les *tokens* correspondants doivent être analysés
+- 0 indique que les *tokens* correspondants ne doivent pas être analysés (c'est-à-dire qu'ils doivent être ignorés par les couches d'attention du modèle).
+
+Complétons l'exemple précédent avec un masque d'attention :
+
+
+{#if fw === 'pt'}
+```py no-format
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, tokenizer.pad_token_id],
+]
+
+attention_mask = [
+ [1, 1, 1],
+ [1, 1, 0],
+]
+
+outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask))
+print(outputs.logits)
+```
+
+```python out
+tensor([[ 1.5694, -1.3895],
+ [ 0.5803, -0.4125]], grad_fn=)
+```
+{:else}
+```py no-format
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, tokenizer.pad_token_id],
+]
+
+attention_mask = [
+ [1, 1, 1],
+ [1, 1, 0],
+]
+
+outputs = model(tf.constant(batched_ids), attention_mask=tf.constant(attention_mask))
+print(outputs.logits)
+```
+
+```py out
+tf.Tensor(
+[[ 1.5693681 -1.3894582 ]
+ [ 0.5803021 -0.41252586]], shape=(2, 2), dtype=float32)
+```
+{/if}
+
+Nous obtenons maintenant les mêmes logits pour la deuxième phrase du batch.
+
+Remarquez comment la dernière valeur de la deuxième séquence est un identifiant de *padding* valant 0 dans le masque d'attention.
+
+
+
+
+✏️ **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 !
+
+
+
+
+## Séquences plus longues
+
+Les *transformers* acceptent en entrée que des séquences d’une longueur limitée. La plupart des modèles traitent des séquences allant jusqu'à 512 ou 1024 *tokens* et plantent lorsqu'on leur demande de traiter des séquences plus longues. Il existe deux solutions à ce problème :
+
+- utiliser un modèle avec une longueur de séquence supportée plus longue,
+- tronquer les séquences.
+
+Certains modèles sont spécialisés dans le traitement de très longues séquences comme par exemple le [Longformer](https://huggingface.co/transformers/model_doc/longformer.html) ou le [LED](https://huggingface.co/transformers/model_doc/led.html). Si vous travaillez sur une tâche qui nécessite de très longues séquences, nous vous recommandons de jeter un coup d'œil à ces modèles.
+
+Sinon, nous vous recommandons de tronquer vos séquences en spécifiant le paramètre `max_sequence_length` :
+
+
+```py
+sequence = sequence[:max_sequence_length]
+```
diff --git a/chapters/fr/chapter2/6.mdx b/chapters/fr/chapter2/6.mdx
index 9602b2a96..4752f7bf0 100644
--- a/chapters/fr/chapter2/6.mdx
+++ b/chapters/fr/chapter2/6.mdx
@@ -1,188 +1,188 @@
-
-
-# Tout assembler
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Dans les dernières sections, nous avons fait de notre mieux pour effectuer la plupart du travail manuellement. Nous avons exploré le fonctionnement des *tokenizers* et examiné la tokenisation, la conversion en identifiants d'entrée, le *padding*, la troncature et les masques d'attention.
-
-Cependant, comme nous l'avons vu dans la section 2, l'API 🤗 *Transformers* peut gérer tout cela pour nous via une fonction dans laquelle nous allons nous plonger ici. Lorsque vous appelez votre `tokenizer` directement sur la phrase, vous récupérez des entrées qui sont prêtes à être passées dans votre modèle :
-
-
-```py
-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.
-
-model_inputs = tokenizer(sequence)
-```
-
-Ici, la variable `model_inputs` contient tout ce qui est nécessaire au bon fonctionnement d'un modèle. Pour DistilBERT, cela inclut les identifiants d'entrée ainsi que le masque d'attention. D'autres modèles qui acceptent des entrées supplémentaires sont également fournis par l'objet `tokenizer`.
-
-Comme nous allons le voir dans les quelques exemples ci-dessous, cette méthode est très puissante. Premièrement, elle peut tokeniser une seule séquence :
-
-
-```py
-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)
-```
-
-Elle gère également plusieurs séquences à la fois, sans modification de l'API :
-
-
-```py
-sequences = [
- "I've been waiting for a HuggingFace course my whole life.",
- "So have I!",
-] # « J'ai attendu un cours de HuggingFace toute ma vie. », « Moi aussi ! »
-
-model_inputs = tokenizer(sequences)
-```
-
-Il est possible de faire du *padding* selon plusieurs objectifs :
-
-```py
-# 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 pour BERT ou DistilBERT)
-model_inputs = tokenizer(sequences, padding="max_length")
-
-# Remplit les séquences jusqu'à la longueur maximale spécifiée
-model_inputs = tokenizer(sequences, padding="max_length", max_length=8)
-```
-
-La fonction peut également tronquer les séquences :
-
-```py
-sequences = [
- "I've been waiting for a HuggingFace course my whole life.",
- "So have I!",
-] # « 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 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
-model_inputs = tokenizer(sequences, max_length=8, truncation=True)
-```
-
-L'objet `tokenizer` peut gérer la conversion en des tenseurs de *frameworks* spécifiques. Ils peuvent ensuite être directement envoyés au modèle. Par exemple, dans le code suivant, nous demandons au *tokenizer* de retourner des tenseurs PyTorch lorsque l’on spécifie `"pt"`, de retourner des tenseurs TensorFlow lorsque l’on spécifie `"tf"` et des tableaux NumPy lorsque l’on indique `"np"` :
-
-```py
-sequences = [
- "I've been waiting for a HuggingFace course my whole life.",
- "So have I!",
-] # « J'ai attendu un cours de HuggingFace toute ma vie. », « Moi aussi ! »
-
-# Retourne des tenseurs PyTorch
-model_inputs = tokenizer(sequences, padding=True, return_tensors="pt")
-
-# Retourne des tenseurs TensorFlow
-model_inputs = tokenizer(sequences, padding=True, return_tensors="tf")
-
-# Retourne des tableaux NumPy
-model_inputs = tokenizer(sequences, padding=True, return_tensors="np")
-```
-
-## Jetons spéciaux
-
-Si nous jetons un coup d'œil aux identifiants d'entrée renvoyés par le *tokenizer*, nous verrons qu'ils sont un peu différents de ceux que nous avions précédemment :
-
-
-```py
-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"])
-
-tokens = tokenizer.tokenize(sequence)
-ids = tokenizer.convert_tokens_to_ids(tokens)
-print(ids)
-```
-
-```python out
-[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102]
-[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]
-```
-
-Un identifiant symbolique a été ajouté au début ainsi qu’un autre à la fin. Décodons les deux séquences d'identifiants ci-dessus pour voir de quoi il s'agit :
-
-```py
-print(tokenizer.decode(model_inputs["input_ids"]))
-print(tokenizer.decode(ids))
-```
-
-```python out
-"[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
-"i've been waiting for a huggingface course my whole life."
-```
-
-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
-
-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 :
-
-
-{#if fw === 'pt'}
-```py
-import torch
-from transformers import AutoTokenizer, AutoModelForSequenceClassification
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-sequences = [
- "I've been waiting for a HuggingFace course my whole life.",
- "So have I!",
-] # « J'ai attendu un cours de HuggingFace toute ma vie. », « Moi aussi ! »
-
-
-tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
-output = model(**tokens)
-```
-{:else}
-```py
-import tensorflow as tf
-from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
-sequences = [
- "I've been waiting for a HuggingFace course my whole life.",
- "So have I!",
-] # « J'ai attendu un cours de HuggingFace toute ma vie. », « Moi aussi ! »
-
-tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="tf")
-output = model(**tokens)
-```
-{/if}
+
+
+# Tout assembler
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Dans les dernières sections, nous avons fait de notre mieux pour effectuer la plupart du travail manuellement. Nous avons exploré le fonctionnement des *tokenizers* et examiné la tokenisation, la conversion en identifiants d'entrée, le *padding*, la troncature et les masques d'attention.
+
+Cependant, comme nous l'avons vu dans la section 2, l'API 🤗 *Transformers* peut gérer tout cela pour nous via une fonction dans laquelle nous allons nous plonger ici. Lorsque vous appelez votre `tokenizer` directement sur la phrase, vous récupérez des entrées qui sont prêtes à être passées dans votre modèle :
+
+
+```py
+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.
+
+model_inputs = tokenizer(sequence)
+```
+
+Ici, la variable `model_inputs` contient tout ce qui est nécessaire au bon fonctionnement d'un modèle. Pour DistilBERT, cela inclut les identifiants d'entrée ainsi que le masque d'attention. D'autres modèles qui acceptent des entrées supplémentaires sont également fournis par l'objet `tokenizer`.
+
+Comme nous allons le voir dans les quelques exemples ci-dessous, cette méthode est très puissante. Premièrement, elle peut tokeniser une seule séquence :
+
+
+```py
+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)
+```
+
+Elle gère également plusieurs séquences à la fois, sans modification de l'API :
+
+
+```py
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "So have I!",
+] # « J'ai attendu un cours de HuggingFace toute ma vie. », « Moi aussi ! »
+
+model_inputs = tokenizer(sequences)
+```
+
+Il est possible de faire du *padding* selon plusieurs objectifs :
+
+```py
+# 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 pour BERT ou DistilBERT)
+model_inputs = tokenizer(sequences, padding="max_length")
+
+# Remplit les séquences jusqu'à la longueur maximale spécifiée
+model_inputs = tokenizer(sequences, padding="max_length", max_length=8)
+```
+
+La fonction peut également tronquer les séquences :
+
+```py
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "So have I!",
+] # « 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 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
+model_inputs = tokenizer(sequences, max_length=8, truncation=True)
+```
+
+L'objet `tokenizer` peut gérer la conversion en des tenseurs de *frameworks* spécifiques. Ils peuvent ensuite être directement envoyés au modèle. Par exemple, dans le code suivant, nous demandons au *tokenizer* de retourner des tenseurs PyTorch lorsque l’on spécifie `"pt"`, de retourner des tenseurs TensorFlow lorsque l’on spécifie `"tf"` et des tableaux NumPy lorsque l’on indique `"np"` :
+
+```py
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "So have I!",
+] # « J'ai attendu un cours de HuggingFace toute ma vie. », « Moi aussi ! »
+
+# Retourne des tenseurs PyTorch
+model_inputs = tokenizer(sequences, padding=True, return_tensors="pt")
+
+# Retourne des tenseurs TensorFlow
+model_inputs = tokenizer(sequences, padding=True, return_tensors="tf")
+
+# Retourne des tableaux NumPy
+model_inputs = tokenizer(sequences, padding=True, return_tensors="np")
+```
+
+## Jetons spéciaux
+
+Si nous jetons un coup d'œil aux identifiants d'entrée renvoyés par le *tokenizer*, nous verrons qu'ils sont un peu différents de ceux que nous avions précédemment :
+
+
+```py
+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"])
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+print(ids)
+```
+
+```python out
+[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102]
+[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]
+```
+
+Un identifiant symbolique a été ajouté au début ainsi qu’un autre à la fin. Décodons les deux séquences d'identifiants ci-dessus pour voir de quoi il s'agit :
+
+```py
+print(tokenizer.decode(model_inputs["input_ids"]))
+print(tokenizer.decode(ids))
+```
+
+```python out
+"[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
+"i've been waiting for a huggingface course my whole life."
+```
+
+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
+
+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 :
+
+
+{#if fw === 'pt'}
+```py
+import torch
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "So have I!",
+] # « J'ai attendu un cours de HuggingFace toute ma vie. », « Moi aussi ! »
+
+
+tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
+output = model(**tokens)
+```
+{:else}
+```py
+import tensorflow as tf
+from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "So have I!",
+] # « J'ai attendu un cours de HuggingFace toute ma vie. », « Moi aussi ! »
+
+tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="tf")
+output = model(**tokens)
+```
+{/if}
diff --git a/chapters/fr/chapter2/7.mdx b/chapters/fr/chapter2/7.mdx
index 2b6c11798..31ea012a5 100644
--- a/chapters/fr/chapter2/7.mdx
+++ b/chapters/fr/chapter2/7.mdx
@@ -1,12 +1,17 @@
-# 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 a3fa30228..9b55bd866 100644
--- a/chapters/fr/chapter2/8.mdx
+++ b/chapters/fr/chapter2/8.mdx
@@ -1,307 +1,312 @@
-
-
-
-
-# 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}
+
+
+
+
+# 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/1.mdx b/chapters/fr/chapter3/1.mdx
index 3ce9c58f7..3b9d16505 100644
--- a/chapters/fr/chapter3/1.mdx
+++ b/chapters/fr/chapter3/1.mdx
@@ -1,23 +1,28 @@
-
-
-# Introduction
-
-Dans le [chapitre 2](/course/fr/chapter2) nous avons étudié comment utiliser les *tokenizers* et les modèles pré-entraînés pour faire des prédictions.
-Mais que faire si vous souhaitez *finetuner* un modèle pré-entraîné pour votre propre jeu de données ? C'est le sujet de ce chapitre ! Vous allez apprendre à :
-
-{#if fw === 'pt'}
-* savoir comment préparer un très grand jeu de données à partir du *Hub*,
-* savoir comment utiliser l'API de haut niveau `Trainer` pour *finetuner* un modèle,
-* savoir comment utiliser une boucle d'entraînement personnalisée,
-* savoir comment tirer parti de la bibliothèque 🤗 *Accelerate* pour exécuter facilement cette boucle d'entraînement personnalisée sur n'importe quelle configuration distribuée.
-configuration distribuée
-
-{:else}
-* savoir comment préparer un très grand jeu de données à partir du *Hub*,
-* savoir comment utiliser Keras pour *finetuner* un modèle,
-* savoir comment utiliser Keras pour obtenir des prédictions,
-* savoir comment utiliser des métriques personnalisées.
-
-{/if}
-
+
+
+# Introduction
+
+
+
+Dans le [chapitre 2](/course/fr/chapter2) nous avons étudié comment utiliser les *tokenizers* et les modèles pré-entraînés pour faire des prédictions.
+Mais que faire si vous souhaitez *finetuner* un modèle pré-entraîné pour votre propre jeu de données ? C'est le sujet de ce chapitre ! Vous allez apprendre à :
+
+{#if fw === 'pt'}
+* savoir comment préparer un très grand jeu de données à partir du *Hub*,
+* savoir comment utiliser l'API de haut niveau `Trainer` pour *finetuner* un modèle,
+* savoir comment utiliser une boucle d'entraînement personnalisée,
+* savoir comment tirer parti de la bibliothèque 🤗 *Accelerate* pour exécuter facilement cette boucle d'entraînement personnalisée sur n'importe quelle configuration distribuée.
+configuration distribuée
+
+{:else}
+* savoir comment préparer un très grand jeu de données à partir du *Hub*,
+* savoir comment utiliser Keras pour *finetuner* un modèle,
+* savoir comment utiliser Keras pour obtenir des prédictions,
+* savoir comment utiliser des métriques personnalisées.
+
+{/if}
+
Afin de télécharger vos *checkpoints* entraînés sur le *Hub* Hugging Face, vous aurez besoin d'un compte huggingface.co : [créer un compte](https://huggingface.co/join)
\ No newline at end of file
diff --git a/chapters/fr/chapter3/2.mdx b/chapters/fr/chapter3/2.mdx
index 297c260f5..5a3186a74 100644
--- a/chapters/fr/chapter3/2.mdx
+++ b/chapters/fr/chapter3/2.mdx
@@ -1,385 +1,385 @@
-
-
-# Préparer les données
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-{#if fw === 'pt'}
-
-En continuant avec l'exemple du [chapitre précédent](/course/fr/chapter2), voici comment entraîner un classifieur de séquences sur un batch avec PyTorch :
-
-```python
-import torch
-from transformers import AdamW, AutoTokenizer, AutoModelForSequenceClassification
-
-# Même chose que précédemment
-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.
- "This course is amazing!", # Ce cours est incroyable !
-]
-batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
-
-# Ceci est nouveau
-batch["labels"] = torch.tensor([1, 1])
-
-optimizer = AdamW(model.parameters())
-loss = model(**batch).loss
-loss.backward()
-optimizer.step()
-```
-{:else}
-
-En continuant avec l'exemple du [chapitre précédent](/course/fr/chapter2), voici comment entraîner un classifieur de séquences sur un batch avec TensorFlow :
-
-```python
-import tensorflow as tf
-import numpy as np
-from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
-
-# Même chose que précédemment
-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.
- "This course is amazing!", # Ce cours est incroyable !
-]
-batch = dict(tokenizer(sequences, padding=True, truncation=True, return_tensors="tf"))
-
-# Ceci est nouveau
-model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
-labels = tf.convert_to_tensor([1, 1])
-model.train_on_batch(batch, labels)
-```
-{/if}
-Evidemment, entraîner un modèle avec seulement deux phrases ne va pas donner de bons résultats. Pour obtenir de meilleurs résultats, vous allez avoir à préparer un plus grand jeu de données.
-
-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
-
-{#if fw === 'pt'}
-
-{:else}
-
-{/if}
-
-Le *Hub* ne contient pas seulement des modèles mais aussi plusieurs jeux de données dans un tas de langues différentes. Vous pouvez explorer les jeux de données [ici](https://huggingface.co/datasets) et nous vous conseillons d'essayer de charger un nouveau jeu de données une fois que vous avez étudié cette section (voir la documentation générale [ici](https://huggingface.co/docs/datasets/loading_datasets.html#from-the-huggingface-hub)). Mais pour l'instant, concentrons-nous sur le jeu de données MRPC ! Il s'agit de l'un des 10 jeux de données qui constituent le [*benchmark* GLUE](https://gluebenchmark.com/) qui est un *benchmark* académique utilisé pour mesurer les performances des modèles d'apprentissage automatique sur 10 différentes tâches de classification de textes.
-
-La bibliothèque 🤗 *Datasets* propose une commande très simple pour télécharger et mettre en cache un jeu de données à partir du *Hub*. On peut télécharger le jeu de données MRPC comme ceci :
-
-```py
-from datasets import load_dataset
-
-raw_datasets = load_dataset("glue", "mrpc")
-raw_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['sentence1', 'sentence2', 'label', 'idx'],
- num_rows: 3668
- })
- validation: Dataset({
- features: ['sentence1', 'sentence2', 'label', 'idx'],
- num_rows: 408
- })
- test: Dataset({
- features: ['sentence1', 'sentence2', 'label', 'idx'],
- num_rows: 1725
- })
-})
-```
-
-Comme vous le voyez, on obtient un objet de type `DatasetDict` qui contient le jeu de données d'entraînement, celui de validation et celui de test. Chacun d'eux contient plusieurs colonnes (`sentence1`, `sentence2`, `label` et `idx`) et une variable nombre de lignes qui contient le nombre d'éléments dans chaque jeu de données (il y a donc 3.668 paires de phrases dans le jeu d'entraînement, 408 dans celui de validation et 1.725 dans celui de test).
-
-Cette commande télécharge et met en cache le jeu de données dans *~/.cache/huggingface/dataset*. Rappelez-vous que comme vu au chapitre 2, vous pouvez personnaliser votre dossier cache en modifiant la variable d'environnement `HF_HOME`.
-
-Nous pouvons accéder à chaque paire de phrase de notre objet `raw_datasets` par les indices, comme avec un dictionnaire :
-
-```py
-raw_train_dataset = raw_datasets["train"]
-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.
-```
-
-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 :
-
-```py
-raw_train_dataset.features
-```
-
-```python out
-{'sentence1': Value(dtype='string', id=None),
- 'sentence2': Value(dtype='string', id=None),
- 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
- 'idx': Value(dtype='int32', id=None)}
-```
-
-En réalité, `label` est de type `ClassLabel` et la correspondance des entiers aux noms des labels est enregistrée le dossier *names*. `0` correspond à `not_equivalent` et `1` correspond à `equivalent`.
-
-
-
-✏️ **Essayez !** Regardez l'élément 15 de l'ensemble d'entraînement et l'élément 87 de l'ensemble de validation. Quelles sont leurs étiquettes ?
-
-
-### Prétraitement d'un jeu de données
-
-{#if fw === 'pt'}
-
-{:else}
-
-{/if}
-
-Pour prétraiter le jeu de données, nous devons convertir le texte en chiffres compréhensibles par le modèle. Comme vous l'avez vu dans le [chapitre précédent](/course/fr/chapter2), cette conversion est effectuée par un *tokenizer*. Nous pouvons fournir au *tokenizer* une phrase ou une liste de phrases, de sorte que nous pouvons directement tokeniser toutes les premières phrases et toutes les secondes phrases de chaque paire comme ceci :
-
-```py
-from transformers import AutoTokenizer
-
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
-tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
-```
-
-Cependant, nous ne pouvons pas simplement passer deux séquences au modèle et obtenir une prédiction pour savoir si les deux phrases sont des paraphrases ou non. Nous devons traiter les deux séquences comme une paire, et appliquer le prétraitement approprié. Heureusement, le *tokenizer* peut également prendre une paire de séquences et la préparer de la manière attendue par notre modèle BERT :
-
-```py
-inputs = tokenizer(
- "This is the first sentence.", "This is the second one."
-) # "C'est la première phrase.", "C'est la deuxième."
-inputs
-```
-
-```python out
-{
- 'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
- 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
- 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
-}
-```
-
-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.
-
-
-
-✏️ **Essayez !** Prenez l'élément 15 de l'ensemble d'entraînement et tokenisez les deux phrases séparément et par paire. Quelle est la différence entre les deux résultats ?
-
-
-
-Si on décode les IDs dans `input_ids` en mots :
-
-```py
-tokenizer.convert_ids_to_tokens(inputs["input_ids"])
-```
-
-nous aurons :
-
-```python out
-['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
-```
-
-Nous voyons donc que le modèle s'attend à ce que les entrées soient de la forme `[CLS] phrase1 [SEP] phrase2 [SEP]` lorsqu'il y a deux phrases. En alignant cela avec les `token_type_ids`, on obtient :
-
-```python out
-['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
-[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
-```
-
-Comme vous pouvez le voir, les parties de l'entrée correspondant à `[CLS] sentence1 [SEP]` ont toutes un *token* de type ID de `0`, tandis que les autres parties, correspondant à `sentence2 [SEP]`, ont toutes un *token* de type ID de `1`.
-
-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.
-
-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 :
-
-```py
-tokenized_dataset = tokenizer(
- raw_datasets["train"]["sentence1"],
- raw_datasets["train"]["sentence2"],
- padding=True,
- truncation=True,
-)
-```
-
-Cela fonctionne bien, mais a l'inconvénient de retourner un dictionnaire (avec nos clés, `input_ids`, `attention_mask`, et `token_type_ids`, et des valeurs qui sont des listes de listes). Cela ne fonctionnera également que si vous avez assez de RAM pour stocker l'ensemble de votre jeu de données pendant la tokenisation (alors que les jeux de données de la bibliothèque 🤗 *Datasets* sont des fichiers [Apache Arrow](https://arrow.apache.org/) stockés sur le disque, vous ne gardez donc en mémoire que les échantillons que vous demandez).
-
-Pour conserver les données sous forme de jeu de données, nous utiliserons la méthode [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasets.Dataset.map). Cela nous permet également une certaine flexibilité, si nous avons besoin d'un prétraitement plus poussé que la simple tokenisation. La méthode `map()` fonctionne en appliquant une fonction sur chaque élément de l'ensemble de données, donc définissons une fonction qui tokenise nos entrées :
-
-```py
-def tokenize_function(example):
- return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
-```
-
-Cette fonction prend un dictionnaire (comme les éléments de notre jeu de données) et retourne un nouveau dictionnaire avec les clés `input_ids`, `attention_mask`, et `token_type_ids`. Notez que cela fonctionne également si le dictionnaire `example` contient plusieurs échantillons (chaque clé étant une liste de phrases) puisque le `tokenizer` travaille sur des listes de paires de phrases, comme vu précédemment. Cela nous permettra d'utiliser l'option `batched=True` dans notre appel à `map()`, ce qui accélérera grandement la tokénisation. Le `tokenizer` est soutenu par un *tokenizer* écrit en Rust à partir de la bibliothèque [🤗 *Tokenizers*](https://github.com/huggingface/tokenizers). Ce *tokenizer* peut être très rapide, mais seulement si on lui donne beaucoup d'entrées en même temps.
-
-Notez que nous avons laissé l'argument `padding` hors de notre fonction de *tokenizer* pour le moment. C'est parce que le *padding* de tous les échantillons à la longueur maximale n'est pas efficace : il est préférable de remplir les échantillons lorsque nous construisons un batch, car alors nous avons seulement besoin de remplir à la longueur maximale dans ce batch, et non la longueur maximale dans l'ensemble des données. Cela peut permettre de gagner beaucoup de temps et de puissance de traitement lorsque les entrées ont des longueurs très variables !
-
-Voici comment nous appliquons la fonction de tokenization sur tous nos jeux de données en même temps. Nous utilisons `batched=True` dans notre appel à `map` pour que la fonction soit appliquée à plusieurs éléments de notre jeu de données en une fois, et non à chaque élément séparément. Cela permet un prétraitement plus rapide.
-
-```py
-tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
-tokenized_datasets
-```
-
-La façon dont la bibliothèque 🤗 *Datasets* applique ce traitement consiste à ajouter de nouveaux champs aux jeux de données, un pour chaque clé du dictionnaire renvoyé par la fonction de prétraitement :
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
- num_rows: 3668
- })
- validation: Dataset({
- features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
- num_rows: 408
- })
- test: Dataset({
- features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
- num_rows: 1725
- })
-})
-```
-
-Vous pouvez même utiliser le multitraitement lorsque vous appliquez votre fonction de prétraitement avec `map()` en passant un argument `num_proc`. Nous ne l'avons pas fait ici parce que la bibliothèque 🤗 *Tokenizers* utilise déjà plusieurs *threads* pour tokeniser nos échantillons plus rapidement, mais si vous n'utilisez pas un *tokenizer* rapide soutenu par cette bibliothèque, cela pourrait accélérer votre prétraitement.
-
-Notre `tokenize_function` retourne un dictionnaire avec les clés `input_ids`, `attention_mask`, et `token_type_ids`, donc ces trois champs sont ajoutés à toutes les divisions de notre jeu de données. Notez que nous aurions également pu modifier des champs existants si notre fonction de prétraitement avait retourné une nouvelle valeur pour une clé existante dans l'ensemble de données auquel nous avons appliqué `map()`.
-
-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
-
-
-
-{#if fw === 'pt'}
-La fonction qui est responsable de l'assemblage des échantillons dans un batch est appelée *fonction de rassemblement*. C'est un argument que vous pouvez passer quand vous construisez un `DataLoader`, la valeur par défaut étant une fonction qui va juste convertir vos échantillons en tenseurs PyTorch et les concaténer (récursivement si vos éléments sont des listes, des *tuples* ou des dictionnaires). Cela ne sera pas possible dans notre cas puisque les entrées que nous avons ne seront pas toutes de la même taille. Nous avons délibérément reporté le *padding*, pour ne l'appliquer que si nécessaire sur chaque batch et éviter d'avoir des entrées trop longues avec beaucoup de remplissage. Cela accélère considérablement l'entraînement, mais notez que si vous vous entraînez sur un TPU, cela peut poser des problèmes. En effet, les TPU préfèrent les formes fixes, même si cela nécessite un *padding* supplémentaire.
-
-{:else}
-La fonction qui est responsable de l'assemblage des échantillons dans un batch est appelée *fonction de rassemblement*. C'est un argument que vous pouvez passer quand vous construisez un `DataLoader`, la valeur par défaut étant une fonction qui va juste convertir vos échantillons en type tf.Tensor et les concaténer (récursivement si les éléments sont des listes, des *tuples* ou des dictionnaires). Cela ne sera pas possible dans notre cas puisque les entrées que nous avons ne seront pas toutes de la même taille. Nous avons délibérément reporté le *padding*, pour ne l'appliquer que si nécessaire sur chaque batch et éviter d'avoir des entrées trop longues avec beaucoup de remplissage. Cela accélère considérablement l'entraînement, mais notez que si vous vous entraînez sur un TPU, cela peut poser des problèmes. En effet, les TPU préfèrent les formes fixes, même si cela nécessite un *padding* supplémentaire.
-
-{/if}
-Pour faire cela en pratique, nous devons définir une fonction de rassemblement qui appliquera la bonne quantité de *padding* aux éléments du jeu de données que nous voulons regrouper. Heureusement, la bibliothèque 🤗 *Transformers* nous fournit une telle fonction via `DataCollatorWithPadding`. Elle prend un *tokenizer* lorsque vous l'instanciez (pour savoir quel *token* de *padding* utiliser et si le modèle s'attend à ce que le *padding* soit à gauche ou à droite des entrées) et fera tout ce dont vous avez besoin :
-
-{#if fw === 'pt'}
-```py
-from transformers import DataCollatorWithPadding
-
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
-```
-{:else}
-```py
-from transformers import DataCollatorWithPadding
-
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
-```
-{/if}
-
-Pour tester notre nouveau jouet, prenons quelques éléments de notre jeu d'entraînement avec lesquels nous allons former un batch. Ici, on supprime les colonnes `idx`, `sentence1` et `sentence2` puisque nous n'en aurons pas besoin et qu'elles contiennent des *strings* (et nous ne pouvons pas créer des tenseurs avec des *strings*) et on regarde la longueur de chaque entrée du batch :
-
-```py
-samples = tokenized_datasets["train"][:8]
-samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
-[len(x) for x in samples["input_ids"]]
-```
-
-```python out
-[50, 59, 47, 67, 59, 50, 62, 32]
-```
-
-Sans surprise, nous obtenons des échantillons de longueur variable, de 32 à 67. Le *padding* dynamique signifie que les échantillons de ce batch doivent tous être rembourrés à une longueur de 67, la longueur maximale dans le batch. Sans le *padding* dynamique, tous les échantillons devraient être rembourrés à la longueur maximale du jeu de données entier, ou à la longueur maximale que le modèle peut accepter. Vérifions à nouveau que notre `data_collator` rembourre dynamiquement le batch correctement :
-
-```py
-batch = data_collator(samples)
-{k: v.shape for k, v in batch.items()}
-```
-
-{#if fw === 'tf'}
-
-```python out
-{'attention_mask': TensorShape([8, 67]),
- 'input_ids': TensorShape([8, 67]),
- 'token_type_ids': TensorShape([8, 67]),
- 'labels': TensorShape([8])}
-```
-
-{:else}
-
-```python out
-{'attention_mask': torch.Size([8, 67]),
- 'input_ids': torch.Size([8, 67]),
- 'token_type_ids': torch.Size([8, 67]),
- 'labels': torch.Size([8])}
-```
-
-C'est beau ! Maintenant que nous sommes passés du texte brut à des batchs que notre modèle peut traiter, nous sommes prêts à le *finetuner* !
-
-{/if}
-
-
-
-✏️ **Essayez !** Reproduisez le prétraitement sur le jeu de données GLUE SST-2. C'est un peu différent puisqu'il est composé de phrases simples au lieu de paires, mais le reste de ce que nous avons fait devrait être identique. Pour un défi plus difficile, essayez d'écrire une fonction de prétraitement qui fonctionne sur toutes les tâches GLUE.
-
-
-
-{#if fw === 'tf'}
-
-Maintenant que nous disposons de notre jeu de données et d'un collecteur de données, nous devons les assembler. Nous pourrions charger manuellement des batchs et les assembler mais c'est beaucoup de travail et probablement pas très performant non plus. A la place, il existe une méthode simple qui offre une solution performante à ce problème : `to_tf_dataset()`. Cela va envelopper un `tf.data.Dataset` autour de votre jeu de données, avec une fonction de collation optionnelle. `tf.data.Dataset` est un format natif de TensorFlow que Keras peut utiliser pour `model.fit()`, donc cette seule méthode convertit immédiatement un *dataset* en un format prêt pour l'entraînement. Voyons cela en action avec notre jeu de données !
-
-```py
-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,
-)
-```
-
-Et c'est tout ! Nous pouvons utiliser ces jeux de données dans le prochain cours, où l'entraînement sera agréablement simple après tout le dur travail de prétraitement des données.
-
-{/if}
+
+
+# Préparer les données
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+{#if fw === 'pt'}
+
+En continuant avec l'exemple du [chapitre précédent](/course/fr/chapter2), voici comment entraîner un classifieur de séquences sur un batch avec PyTorch :
+
+```python
+import torch
+from transformers import AdamW, AutoTokenizer, AutoModelForSequenceClassification
+
+# Même chose que précédemment
+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.
+ "This course is amazing!", # Ce cours est incroyable !
+]
+batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
+
+# Ceci est nouveau
+batch["labels"] = torch.tensor([1, 1])
+
+optimizer = AdamW(model.parameters())
+loss = model(**batch).loss
+loss.backward()
+optimizer.step()
+```
+{:else}
+
+En continuant avec l'exemple du [chapitre précédent](/course/fr/chapter2), voici comment entraîner un classifieur de séquences sur un batch avec TensorFlow :
+
+```python
+import tensorflow as tf
+import numpy as np
+from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
+
+# Même chose que précédemment
+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.
+ "This course is amazing!", # Ce cours est incroyable !
+]
+batch = dict(tokenizer(sequences, padding=True, truncation=True, return_tensors="tf"))
+
+# Ceci est nouveau
+model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
+labels = tf.convert_to_tensor([1, 1])
+model.train_on_batch(batch, labels)
+```
+{/if}
+Evidemment, entraîner un modèle avec seulement deux phrases ne va pas donner de bons résultats. Pour obtenir de meilleurs résultats, vous allez avoir à préparer un plus grand jeu de données.
+
+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
+
+{#if fw === 'pt'}
+
+{:else}
+
+{/if}
+
+Le *Hub* ne contient pas seulement des modèles mais aussi plusieurs jeux de données dans un tas de langues différentes. Vous pouvez explorer les jeux de données [ici](https://huggingface.co/datasets) et nous vous conseillons d'essayer de charger un nouveau jeu de données une fois que vous avez étudié cette section (voir la documentation générale [ici](https://huggingface.co/docs/datasets/loading_datasets.html#from-the-huggingface-hub)). Mais pour l'instant, concentrons-nous sur le jeu de données MRPC ! Il s'agit de l'un des 10 jeux de données qui constituent le [*benchmark* GLUE](https://gluebenchmark.com/) qui est un *benchmark* académique utilisé pour mesurer les performances des modèles d'apprentissage automatique sur 10 différentes tâches de classification de textes.
+
+La bibliothèque 🤗 *Datasets* propose une commande très simple pour télécharger et mettre en cache un jeu de données à partir du *Hub*. On peut télécharger le jeu de données MRPC comme ceci :
+
+```py
+from datasets import load_dataset
+
+raw_datasets = load_dataset("glue", "mrpc")
+raw_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 3668
+ })
+ validation: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 408
+ })
+ test: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 1725
+ })
+})
+```
+
+Comme vous le voyez, on obtient un objet de type `DatasetDict` qui contient le jeu de données d'entraînement, celui de validation et celui de test. Chacun d'eux contient plusieurs colonnes (`sentence1`, `sentence2`, `label` et `idx`) et une variable nombre de lignes qui contient le nombre d'éléments dans chaque jeu de données (il y a donc 3.668 paires de phrases dans le jeu d'entraînement, 408 dans celui de validation et 1.725 dans celui de test).
+
+Cette commande télécharge et met en cache le jeu de données dans *~/.cache/huggingface/dataset*. Rappelez-vous que comme vu au chapitre 2, vous pouvez personnaliser votre dossier cache en modifiant la variable d'environnement `HF_HOME`.
+
+Nous pouvons accéder à chaque paire de phrase de notre objet `raw_datasets` par les indices, comme avec un dictionnaire :
+
+```py
+raw_train_dataset = raw_datasets["train"]
+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.
+```
+
+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 :
+
+```py
+raw_train_dataset.features
+```
+
+```python out
+{'sentence1': Value(dtype='string', id=None),
+ 'sentence2': Value(dtype='string', id=None),
+ 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
+ 'idx': Value(dtype='int32', id=None)}
+```
+
+En réalité, `label` est de type `ClassLabel` et la correspondance des entiers aux noms des labels est enregistrée le dossier *names*. `0` correspond à `not_equivalent` et `1` correspond à `equivalent`.
+
+
+
+✏️ **Essayez !** Regardez l'élément 15 de l'ensemble d'entraînement et l'élément 87 de l'ensemble de validation. Quelles sont leurs étiquettes ?
+
+
+### Prétraitement d'un jeu de données
+
+{#if fw === 'pt'}
+
+{:else}
+
+{/if}
+
+Pour prétraiter le jeu de données, nous devons convertir le texte en chiffres compréhensibles par le modèle. Comme vous l'avez vu dans le [chapitre précédent](/course/fr/chapter2), cette conversion est effectuée par un *tokenizer*. Nous pouvons fournir au *tokenizer* une phrase ou une liste de phrases, de sorte que nous pouvons directement tokeniser toutes les premières phrases et toutes les secondes phrases de chaque paire comme ceci :
+
+```py
+from transformers import AutoTokenizer
+
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
+tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
+```
+
+Cependant, nous ne pouvons pas simplement passer deux séquences au modèle et obtenir une prédiction pour savoir si les deux phrases sont des paraphrases ou non. Nous devons traiter les deux séquences comme une paire, et appliquer le prétraitement approprié. Heureusement, le *tokenizer* peut également prendre une paire de séquences et la préparer de la manière attendue par notre modèle BERT :
+
+```py
+inputs = tokenizer(
+ "This is the first sentence.", "This is the second one."
+) # "C'est la première phrase.", "C'est la deuxième."
+inputs
+```
+
+```python out
+{
+ 'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
+ 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
+ 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+}
+```
+
+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.
+
+
+
+✏️ **Essayez !** Prenez l'élément 15 de l'ensemble d'entraînement et tokenisez les deux phrases séparément et par paire. Quelle est la différence entre les deux résultats ?
+
+
+
+Si on décode les IDs dans `input_ids` en mots :
+
+```py
+tokenizer.convert_ids_to_tokens(inputs["input_ids"])
+```
+
+nous aurons :
+
+```python out
+['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
+```
+
+Nous voyons donc que le modèle s'attend à ce que les entrées soient de la forme `[CLS] phrase1 [SEP] phrase2 [SEP]` lorsqu'il y a deux phrases. En alignant cela avec les `token_type_ids`, on obtient :
+
+```python out
+['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
+[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
+```
+
+Comme vous pouvez le voir, les parties de l'entrée correspondant à `[CLS] sentence1 [SEP]` ont toutes un *token* de type ID de `0`, tandis que les autres parties, correspondant à `sentence2 [SEP]`, ont toutes un *token* de type ID de `1`.
+
+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.
+
+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 :
+
+```py
+tokenized_dataset = tokenizer(
+ raw_datasets["train"]["sentence1"],
+ raw_datasets["train"]["sentence2"],
+ padding=True,
+ truncation=True,
+)
+```
+
+Cela fonctionne bien, mais a l'inconvénient de retourner un dictionnaire (avec nos clés, `input_ids`, `attention_mask`, et `token_type_ids`, et des valeurs qui sont des listes de listes). Cela ne fonctionnera également que si vous avez assez de RAM pour stocker l'ensemble de votre jeu de données pendant la tokenisation (alors que les jeux de données de la bibliothèque 🤗 *Datasets* sont des fichiers [Apache Arrow](https://arrow.apache.org/) stockés sur le disque, vous ne gardez donc en mémoire que les échantillons que vous demandez).
+
+Pour conserver les données sous forme de jeu de données, nous utiliserons la méthode [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasets.Dataset.map). Cela nous permet également une certaine flexibilité, si nous avons besoin d'un prétraitement plus poussé que la simple tokenisation. La méthode `map()` fonctionne en appliquant une fonction sur chaque élément de l'ensemble de données, donc définissons une fonction qui tokenise nos entrées :
+
+```py
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+```
+
+Cette fonction prend un dictionnaire (comme les éléments de notre jeu de données) et retourne un nouveau dictionnaire avec les clés `input_ids`, `attention_mask`, et `token_type_ids`. Notez que cela fonctionne également si le dictionnaire `example` contient plusieurs échantillons (chaque clé étant une liste de phrases) puisque le `tokenizer` travaille sur des listes de paires de phrases, comme vu précédemment. Cela nous permettra d'utiliser l'option `batched=True` dans notre appel à `map()`, ce qui accélérera grandement la tokénisation. Le `tokenizer` est soutenu par un *tokenizer* écrit en Rust à partir de la bibliothèque [🤗 *Tokenizers*](https://github.com/huggingface/tokenizers). Ce *tokenizer* peut être très rapide, mais seulement si on lui donne beaucoup d'entrées en même temps.
+
+Notez que nous avons laissé l'argument `padding` hors de notre fonction de *tokenizer* pour le moment. C'est parce que le *padding* de tous les échantillons à la longueur maximale n'est pas efficace : il est préférable de remplir les échantillons lorsque nous construisons un batch, car alors nous avons seulement besoin de remplir à la longueur maximale dans ce batch, et non la longueur maximale dans l'ensemble des données. Cela peut permettre de gagner beaucoup de temps et de puissance de traitement lorsque les entrées ont des longueurs très variables !
+
+Voici comment nous appliquons la fonction de tokenization sur tous nos jeux de données en même temps. Nous utilisons `batched=True` dans notre appel à `map` pour que la fonction soit appliquée à plusieurs éléments de notre jeu de données en une fois, et non à chaque élément séparément. Cela permet un prétraitement plus rapide.
+
+```py
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+tokenized_datasets
+```
+
+La façon dont la bibliothèque 🤗 *Datasets* applique ce traitement consiste à ajouter de nouveaux champs aux jeux de données, un pour chaque clé du dictionnaire renvoyé par la fonction de prétraitement :
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 3668
+ })
+ validation: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 408
+ })
+ test: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 1725
+ })
+})
+```
+
+Vous pouvez même utiliser le multitraitement lorsque vous appliquez votre fonction de prétraitement avec `map()` en passant un argument `num_proc`. Nous ne l'avons pas fait ici parce que la bibliothèque 🤗 *Tokenizers* utilise déjà plusieurs *threads* pour tokeniser nos échantillons plus rapidement, mais si vous n'utilisez pas un *tokenizer* rapide soutenu par cette bibliothèque, cela pourrait accélérer votre prétraitement.
+
+Notre `tokenize_function` retourne un dictionnaire avec les clés `input_ids`, `attention_mask`, et `token_type_ids`, donc ces trois champs sont ajoutés à toutes les divisions de notre jeu de données. Notez que nous aurions également pu modifier des champs existants si notre fonction de prétraitement avait retourné une nouvelle valeur pour une clé existante dans l'ensemble de données auquel nous avons appliqué `map()`.
+
+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
+
+
+
+{#if fw === 'pt'}
+La fonction qui est responsable de l'assemblage des échantillons dans un batch est appelée *fonction de rassemblement*. C'est un argument que vous pouvez passer quand vous construisez un `DataLoader`, la valeur par défaut étant une fonction qui va juste convertir vos échantillons en tenseurs PyTorch et les concaténer (récursivement si vos éléments sont des listes, des *tuples* ou des dictionnaires). Cela ne sera pas possible dans notre cas puisque les entrées que nous avons ne seront pas toutes de la même taille. Nous avons délibérément reporté le *padding*, pour ne l'appliquer que si nécessaire sur chaque batch et éviter d'avoir des entrées trop longues avec beaucoup de remplissage. Cela accélère considérablement l'entraînement, mais notez que si vous vous entraînez sur un TPU, cela peut poser des problèmes. En effet, les TPU préfèrent les formes fixes, même si cela nécessite un *padding* supplémentaire.
+
+{:else}
+La fonction qui est responsable de l'assemblage des échantillons dans un batch est appelée *fonction de rassemblement*. C'est un argument que vous pouvez passer quand vous construisez un `DataLoader`, la valeur par défaut étant une fonction qui va juste convertir vos échantillons en type tf.Tensor et les concaténer (récursivement si les éléments sont des listes, des *tuples* ou des dictionnaires). Cela ne sera pas possible dans notre cas puisque les entrées que nous avons ne seront pas toutes de la même taille. Nous avons délibérément reporté le *padding*, pour ne l'appliquer que si nécessaire sur chaque batch et éviter d'avoir des entrées trop longues avec beaucoup de remplissage. Cela accélère considérablement l'entraînement, mais notez que si vous vous entraînez sur un TPU, cela peut poser des problèmes. En effet, les TPU préfèrent les formes fixes, même si cela nécessite un *padding* supplémentaire.
+
+{/if}
+Pour faire cela en pratique, nous devons définir une fonction de rassemblement qui appliquera la bonne quantité de *padding* aux éléments du jeu de données que nous voulons regrouper. Heureusement, la bibliothèque 🤗 *Transformers* nous fournit une telle fonction via `DataCollatorWithPadding`. Elle prend un *tokenizer* lorsque vous l'instanciez (pour savoir quel *token* de *padding* utiliser et si le modèle s'attend à ce que le *padding* soit à gauche ou à droite des entrées) et fera tout ce dont vous avez besoin :
+
+{#if fw === 'pt'}
+```py
+from transformers import DataCollatorWithPadding
+
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+{:else}
+```py
+from transformers import DataCollatorWithPadding
+
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
+```
+{/if}
+
+Pour tester notre nouveau jouet, prenons quelques éléments de notre jeu d'entraînement avec lesquels nous allons former un batch. Ici, on supprime les colonnes `idx`, `sentence1` et `sentence2` puisque nous n'en aurons pas besoin et qu'elles contiennent des *strings* (et nous ne pouvons pas créer des tenseurs avec des *strings*) et on regarde la longueur de chaque entrée du batch :
+
+```py
+samples = tokenized_datasets["train"][:8]
+samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
+[len(x) for x in samples["input_ids"]]
+```
+
+```python out
+[50, 59, 47, 67, 59, 50, 62, 32]
+```
+
+Sans surprise, nous obtenons des échantillons de longueur variable, de 32 à 67. Le *padding* dynamique signifie que les échantillons de ce batch doivent tous être rembourrés à une longueur de 67, la longueur maximale dans le batch. Sans le *padding* dynamique, tous les échantillons devraient être rembourrés à la longueur maximale du jeu de données entier, ou à la longueur maximale que le modèle peut accepter. Vérifions à nouveau que notre `data_collator` rembourre dynamiquement le batch correctement :
+
+```py
+batch = data_collator(samples)
+{k: v.shape for k, v in batch.items()}
+```
+
+{#if fw === 'tf'}
+
+```python out
+{'attention_mask': TensorShape([8, 67]),
+ 'input_ids': TensorShape([8, 67]),
+ 'token_type_ids': TensorShape([8, 67]),
+ 'labels': TensorShape([8])}
+```
+
+{:else}
+
+```python out
+{'attention_mask': torch.Size([8, 67]),
+ 'input_ids': torch.Size([8, 67]),
+ 'token_type_ids': torch.Size([8, 67]),
+ 'labels': torch.Size([8])}
+```
+
+C'est beau ! Maintenant que nous sommes passés du texte brut à des batchs que notre modèle peut traiter, nous sommes prêts à le *finetuner* !
+
+{/if}
+
+
+
+✏️ **Essayez !** Reproduisez le prétraitement sur le jeu de données GLUE SST-2. C'est un peu différent puisqu'il est composé de phrases simples au lieu de paires, mais le reste de ce que nous avons fait devrait être identique. Pour un défi plus difficile, essayez d'écrire une fonction de prétraitement qui fonctionne sur toutes les tâches GLUE.
+
+
+
+{#if fw === 'tf'}
+
+Maintenant que nous disposons de notre jeu de données et d'un collecteur de données, nous devons les assembler. Nous pourrions charger manuellement des batchs et les assembler mais c'est beaucoup de travail et probablement pas très performant non plus. A la place, il existe une méthode simple qui offre une solution performante à ce problème : `to_tf_dataset()`. Cela va envelopper un `tf.data.Dataset` autour de votre jeu de données, avec une fonction de collation optionnelle. `tf.data.Dataset` est un format natif de TensorFlow que Keras peut utiliser pour `model.fit()`, donc cette seule méthode convertit immédiatement un *dataset* en un format prêt pour l'entraînement. Voyons cela en action avec notre jeu de données !
+
+```py
+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,
+)
+```
+
+Et c'est tout ! Nous pouvons utiliser ces jeux de données dans le prochain cours, où l'entraînement sera agréablement simple après tout le dur travail de prétraitement des données.
+
+{/if}
diff --git a/chapters/fr/chapter3/3.mdx b/chapters/fr/chapter3/3.mdx
index 2624cdd5a..eba84e1b1 100644
--- a/chapters/fr/chapter3/3.mdx
+++ b/chapters/fr/chapter3/3.mdx
@@ -1,171 +1,171 @@
-
-
-# Finetuner un modèle avec l'API Trainer
-
-
-
-
-
-La bibliothèque 🤗 *Transformers* fournit une classe `Trainer` pour vous aider à *finetuner* n'importe lequel des modèles pré-entraînés qu'elle met à disposition sur votre jeu de données. 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 définir le `Trainer`. La partie la plus difficile sera probablement de préparer l'environnement pour exécuter `Trainer.train()`, car elle fonctionnera 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
-
-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)
-```
-
-### Entraînement
-
-La première étape avant de pouvoir définir notre `Trainer` est de définir une classe `TrainingArguments` qui contiendra tous les hyperparamètres que le `Trainer` utilisera pour l'entraînement et l'évaluation. Le seul argument que vous devez fournir est un répertoire où le modèle entraîné sera sauvegardé, ainsi que les *checkpoints*. Pour tout le reste, vous pouvez laisser les valeurs par défaut, qui devraient fonctionner assez bien pour un *finetuning* de base.
-
-```py
-from transformers import TrainingArguments
-
-training_args = TrainingArguments("test-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).
-
-
-
-La deuxième étape consiste à définir notre modèle. Comme dans le [chapitre précédent](/course/fr/chapter2), nous utiliserons la classe `AutoModelForSequenceClassification`, avec deux labels :
-
-```py
-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.
-
-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` :
-
-```py
-from transformers import Trainer
-
-trainer = Trainer(
- model,
- training_args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- tokenizer=tokenizer,
-)
-```
-
-Notez que lorsque vous passez le `tokenizer` comme nous l'avons fait ici, le `data_collator` par défaut utilisé par le `Trainer` sera un `DataCollatorWithPadding` comme défini précédemment. Ainsi, vous pouvez sauter la ligne `data_collator=data_collator` dans cet appel. Il était quand même important de vous montrer cette partie du traitement dans la section 2 !
-
-Pour *finetuner* le modèle sur notre jeu de données, il suffit d'appeler la méthode `train()` de notre `Trainer` :
-
-```py
-trainer.train()
-```
-
-Cela lancera le *finetuning* (qui devrait prendre quelques minutes sur un GPU) et indiquera la perte d'entraînement tous les 500 pas. Cependant, elle ne vous dira pas si votre modèle fonctionne bien (ou mal). Ceci est dû au fait que :
-
-1. nous n'avons pas dit au `Trainer` d'évaluer pendant l'entraînement en réglant `evaluation_strategy` à soit `"steps"` (évaluer chaque `eval_steps`) ou `"epoch"` (évaluer à la fin de chaque *epoch*).
-2. nous n'avons pas fourni au `Trainer` une fonction `compute_metrics()` pour calculer une métrique pendant ladite évaluation (sinon l'évaluation aurait juste affiché la perte, qui n'est pas un nombre très intuitif).
-
-
-### Evaluation
-
-Voyons comment nous pouvons construire une fonction `compute_metrics()` utile et l'utiliser la prochaine fois que nous entraînons. La fonction doit prendre un objet `EvalPrediction` (qui est un *tuple* nommé avec un champ `predictions` et un champ `label_ids`) et retournera un dictionnaire de chaînes de caractères vers des flottants (les chaînes de caractères étant les noms des métriques retournées, et les flottants leurs valeurs). Pour obtenir des prédictions de notre modèle, nous pouvons utiliser la commande `Trainer.predict()` :
-
-```py
-predictions = trainer.predict(tokenized_datasets["validation"])
-print(predictions.predictions.shape, predictions.label_ids.shape)
-```
-
-```python out
-(408, 2) (408,)
-```
-
-La sortie de la méthode `predict()` est un autre *tuple* nommé avec trois champs : `predictions`, `label_ids`, et `metrics`. Le champ `metrics` contiendra juste la perte sur le jeu de données passé, ainsi que quelques mesures de temps (combien de temps il a fallu pour prédire, au total et en moyenne). Une fois que nous aurons complété notre fonction `compute_metrics()` et que nous l'aurons passé au `Trainer`, ce champ contiendra également les métriques retournées par `compute_metrics()`.
-
-Comme vous pouvez le voir, `predictions` est un tableau bidimensionnel de forme 408 x 2 (408 étant le nombre d'éléments dans le jeu de données que nous avons utilisé). Ce sont les logits pour chaque élément du jeu de données que nous avons passé à `predict()` (comme vous l'avez vu dans le [chapitre précédent](/course/fr/chapter2), tous les *transformers* retournent des logits). Pour les transformer en prédictions que nous pouvons comparer à nos étiquettes, nous devons prendre l'indice avec la valeur maximale sur le second axe :
-
-```py
-import numpy as np
-
-preds = np.argmax(predictions.predictions, axis=-1)
-```
-
-Nous pouvons maintenant comparer ces `preds` aux étiquettes. Pour construire notre fonction `compute_metric()`, nous allons nous appuyer sur les métriques de la bibliothèque 🤗 [*Evaluate*](https://github.com/huggingface/evaluate/). 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 `evaluate.load()`. L'objet retourné possède une méthode `compute()` que nous pouvons utiliser pour effectuer le calcul de la métrique :
-
-```py
-import evaluate
-
-metric = evaluate.load("glue", "mrpc")
-metric.compute(predictions=preds, references=predictions.label_ids)
-```
-
-```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.
-
-En regroupant le tout, nous obtenons notre fonction `compute_metrics()` :
-
-```py
-def compute_metrics(eval_preds):
- metric = evaluate.load("glue", "mrpc")
- logits, labels = eval_preds
- predictions = np.argmax(logits, axis=-1)
- return metric.compute(predictions=predictions, references=labels)
-```
-
-Et pour le voir utilisé en action pour rapporter les métriques à la fin de chaque époque, voici comment nous définissons un nouveau `Trainer` avec cette fonction `compute_metrics()` :
-
-```py
-training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-
-trainer = Trainer(
- model,
- training_args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- tokenizer=tokenizer,
- compute_metrics=compute_metrics,
-)
-```
-
-Notez que nous créons un nouveau `TrainingArguments` avec sa `evaluation_strategy` définie sur `"epoch"` et un nouveau modèle. Sinon, nous ne ferions que continuer l'entraînement du modèle que nous avons déjà entraîné. Pour lancer un nouveau cycle d'entraînement, nous exécutons :
-
-```
-trainer.train()
-```
-
-Cette fois, il indiquera la perte et les mesures de validation à la fin de chaque époque, en plus de la perte d'entraînement. Encore une fois, le score exact de précision/F1 que vous atteignez peut être un peu différent de ce que nous avons trouvé, en raison de l'initialisation aléatoire de la tête du modèle, mais il devrait être dans la même fourchette.
-
-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.
-
-
-
-✏️ **Essayez !** *Finetunez* un modèle sur le jeu de données GLUE SST-2, en utilisant le traitement des données que vous avez fait dans la section 2.
-
-
+
+
+# Finetuner un modèle avec l'API Trainer
+
+
+
+
+
+La bibliothèque 🤗 *Transformers* fournit une classe `Trainer` pour vous aider à *finetuner* n'importe lequel des modèles pré-entraînés qu'elle met à disposition sur votre jeu de données. 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 définir le `Trainer`. La partie la plus difficile sera probablement de préparer l'environnement pour exécuter `Trainer.train()`, car elle fonctionnera 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
+
+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)
+```
+
+### Entraînement
+
+La première étape avant de pouvoir définir notre `Trainer` est de définir une classe `TrainingArguments` qui contiendra tous les hyperparamètres que le `Trainer` utilisera pour l'entraînement et l'évaluation. Le seul argument que vous devez fournir est un répertoire où le modèle entraîné sera sauvegardé, ainsi que les *checkpoints*. Pour tout le reste, vous pouvez laisser les valeurs par défaut, qui devraient fonctionner assez bien pour un *finetuning* de base.
+
+```py
+from transformers import TrainingArguments
+
+training_args = TrainingArguments("test-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).
+
+
+
+La deuxième étape consiste à définir notre modèle. Comme dans le [chapitre précédent](/course/fr/chapter2), nous utiliserons la classe `AutoModelForSequenceClassification`, avec deux labels :
+
+```py
+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.
+
+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` :
+
+```py
+from transformers import Trainer
+
+trainer = Trainer(
+ model,
+ training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ tokenizer=tokenizer,
+)
+```
+
+Notez que lorsque vous passez le `tokenizer` comme nous l'avons fait ici, le `data_collator` par défaut utilisé par le `Trainer` sera un `DataCollatorWithPadding` comme défini précédemment. Ainsi, vous pouvez sauter la ligne `data_collator=data_collator` dans cet appel. Il était quand même important de vous montrer cette partie du traitement dans la section 2 !
+
+Pour *finetuner* le modèle sur notre jeu de données, il suffit d'appeler la méthode `train()` de notre `Trainer` :
+
+```py
+trainer.train()
+```
+
+Cela lancera le *finetuning* (qui devrait prendre quelques minutes sur un GPU) et indiquera la perte d'entraînement tous les 500 pas. Cependant, elle ne vous dira pas si votre modèle fonctionne bien (ou mal). Ceci est dû au fait que :
+
+1. nous n'avons pas dit au `Trainer` d'évaluer pendant l'entraînement en réglant `evaluation_strategy` à soit `"steps"` (évaluer chaque `eval_steps`) ou `"epoch"` (évaluer à la fin de chaque *epoch*).
+2. nous n'avons pas fourni au `Trainer` une fonction `compute_metrics()` pour calculer une métrique pendant ladite évaluation (sinon l'évaluation aurait juste affiché la perte, qui n'est pas un nombre très intuitif).
+
+
+### Evaluation
+
+Voyons comment nous pouvons construire une fonction `compute_metrics()` utile et l'utiliser la prochaine fois que nous entraînons. La fonction doit prendre un objet `EvalPrediction` (qui est un *tuple* nommé avec un champ `predictions` et un champ `label_ids`) et retournera un dictionnaire de chaînes de caractères vers des flottants (les chaînes de caractères étant les noms des métriques retournées, et les flottants leurs valeurs). Pour obtenir des prédictions de notre modèle, nous pouvons utiliser la commande `Trainer.predict()` :
+
+```py
+predictions = trainer.predict(tokenized_datasets["validation"])
+print(predictions.predictions.shape, predictions.label_ids.shape)
+```
+
+```python out
+(408, 2) (408,)
+```
+
+La sortie de la méthode `predict()` est un autre *tuple* nommé avec trois champs : `predictions`, `label_ids`, et `metrics`. Le champ `metrics` contiendra juste la perte sur le jeu de données passé, ainsi que quelques mesures de temps (combien de temps il a fallu pour prédire, au total et en moyenne). Une fois que nous aurons complété notre fonction `compute_metrics()` et que nous l'aurons passé au `Trainer`, ce champ contiendra également les métriques retournées par `compute_metrics()`.
+
+Comme vous pouvez le voir, `predictions` est un tableau bidimensionnel de forme 408 x 2 (408 étant le nombre d'éléments dans le jeu de données que nous avons utilisé). Ce sont les logits pour chaque élément du jeu de données que nous avons passé à `predict()` (comme vous l'avez vu dans le [chapitre précédent](/course/fr/chapter2), tous les *transformers* retournent des logits). Pour les transformer en prédictions que nous pouvons comparer à nos étiquettes, nous devons prendre l'indice avec la valeur maximale sur le second axe :
+
+```py
+import numpy as np
+
+preds = np.argmax(predictions.predictions, axis=-1)
+```
+
+Nous pouvons maintenant comparer ces `preds` aux étiquettes. Pour construire notre fonction `compute_metric()`, nous allons nous appuyer sur les métriques de la bibliothèque 🤗 [*Evaluate*](https://github.com/huggingface/evaluate/). 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 `evaluate.load()`. L'objet retourné possède une méthode `compute()` que nous pouvons utiliser pour effectuer le calcul de la métrique :
+
+```py
+import evaluate
+
+metric = evaluate.load("glue", "mrpc")
+metric.compute(predictions=preds, references=predictions.label_ids)
+```
+
+```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.
+
+En regroupant le tout, nous obtenons notre fonction `compute_metrics()` :
+
+```py
+def compute_metrics(eval_preds):
+ metric = evaluate.load("glue", "mrpc")
+ logits, labels = eval_preds
+ predictions = np.argmax(logits, axis=-1)
+ return metric.compute(predictions=predictions, references=labels)
+```
+
+Et pour le voir utilisé en action pour rapporter les métriques à la fin de chaque époque, voici comment nous définissons un nouveau `Trainer` avec cette fonction `compute_metrics()` :
+
+```py
+training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+
+trainer = Trainer(
+ model,
+ training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ tokenizer=tokenizer,
+ compute_metrics=compute_metrics,
+)
+```
+
+Notez que nous créons un nouveau `TrainingArguments` avec sa `evaluation_strategy` définie sur `"epoch"` et un nouveau modèle. Sinon, nous ne ferions que continuer l'entraînement du modèle que nous avons déjà entraîné. Pour lancer un nouveau cycle d'entraînement, nous exécutons :
+
+```
+trainer.train()
+```
+
+Cette fois, il indiquera la perte et les mesures de validation à la fin de chaque époque, en plus de la perte d'entraînement. Encore une fois, le score exact de précision/F1 que vous atteignez peut être un peu différent de ce que nous avons trouvé, en raison de l'initialisation aléatoire de la tête du modèle, mais il devrait être dans la même fourchette.
+
+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.
+
+
+
+✏️ **Essayez !** *Finetunez* un modèle sur le jeu de données GLUE SST-2, en utilisant le traitement des données que vous avez fait dans la section 2.
+
+
diff --git a/chapters/fr/chapter3/3_tf.mdx b/chapters/fr/chapter3/3_tf.mdx
index 9a84d533d..bace781f0 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 `evaluate.load()`. L'objet retourné a une méthode `compute()` que nous pouvons utiliser pour faire le calcul de la métrique :
-
-```py
-import evaluate
-
-metric = evaluate.load("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 `evaluate.load()`. L'objet retourné a une méthode `compute()` que nous pouvons utiliser pour faire le calcul de la métrique :
+
+```py
+import evaluate
+
+metric = evaluate.load("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 e66caa6db..b04812639 100644
--- a/chapters/fr/chapter3/4.mdx
+++ b/chapters/fr/chapter3/4.mdx
@@ -1,359 +1,359 @@
-# Un entraînement complet
-
-
-
-
-
-Maintenant nous allons voir comment obtenir les mêmes résultats que dans la dernière section sans utiliser la classe `Trainer`. Encore une fois, nous supposons que vous avez fait le traitement des données dans la section 2. Voici un court résumé couvrant tout ce dont vous aurez besoin :
-
-```py
-from datasets import load_dataset
-from transformers import AutoTokenizer, DataCollatorWithPadding
-
-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)
-```
-
-### Préparer l'entraînement
-
-Avant d'écrire réellement notre boucle d'entraînement, nous devons définir quelques objets. Les premiers sont les *dataloaders* que nous utiliserons pour itérer sur les batchs. Mais avant de pouvoir définir ces chargeurs de données, nous devons appliquer un peu de post-traitement à nos `tokenized_datasets`, pour prendre soin de certaines choses que le `Trainer` fait pour nous automatiquement. Spécifiquement, nous devons :
-
-- supprimer les colonnes correspondant aux valeurs que le modèle n'attend pas (comme les colonnes `sentence1` et `sentence2`),
-- renommer la colonne `label` en `labels` (parce que le modèle s'attend à ce que l'argument soit nommé `labels`),
-- définir le format des jeux de données pour qu'ils retournent des tenseurs PyTorch au lieu de listes.
-
-Notre `tokenized_datasets` a une méthode pour chacune de ces étapes :
-
-```py
-tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
-tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
-tokenized_datasets.set_format("torch")
-tokenized_datasets["train"].column_names
-```
-
-Nous pouvons alors vérifier que le résultat ne comporte que des colonnes que notre modèle acceptera :
-
-```python
-["attention_mask", "input_ids", "labels", "token_type_ids"]
-```
-
-Maintenant que cela est fait, nous pouvons facilement définir nos *dataloaders* :
-
-```py
-from torch.utils.data import DataLoader
-
-train_dataloader = DataLoader(
- tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
-)
-eval_dataloader = DataLoader(
- tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
-)
-```
-
-Pour vérifier rapidement qu'il n'y a pas d'erreur dans le traitement des données, nous pouvons inspecter un batch comme celui-ci :
-
-```py
-for batch in train_dataloader:
- break
-{k: v.shape for k, v in batch.items()}
-```
-
-```python out
-{'attention_mask': torch.Size([8, 65]),
- 'input_ids': torch.Size([8, 65]),
- 'labels': torch.Size([8]),
- 'token_type_ids': torch.Size([8, 65])}
-```
-
-Notez que les formes réelles seront probablement légèrement différentes pour vous puisque nous avons défini `shuffle=True` pour le chargeur de données d'entraînement et que nous *paddons* à la longueur maximale dans le batch.
-
-Maintenant que nous en avons terminé avec le prétraitement des données (un objectif satisfaisant mais difficile à atteindre pour tout praticien d'apprentissage automatique), passons au modèle. Nous l'instancions exactement comme nous l'avons fait dans la section précédente :
-
-```py
-from transformers import AutoModelForSequenceClassification
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-```
-
-Pour s'assurer que tout se passera bien pendant l'entraînement, nous transmettons notre batch à ce modèle :
-
-```py
-outputs = model(**batch)
-print(outputs.loss, outputs.logits.shape)
-```
-
-```python out
-tensor(0.5441, grad_fn=) torch.Size([8, 2])
-```
-
-Tous les modèles 🤗 *Transformers* renvoient la perte lorsque les `labels` sont fournis. Nous obtenons également les logits (deux pour chaque entrée de notre batch, donc un tenseur de taille 8 x 2).
-
-Nous sommes presque prêts à écrire notre boucle d'entraînement ! Il nous manque juste deux choses : un optimiseur et un planificateur de taux d'apprentissage. Puisque nous essayons de reproduire à la main ce que fait la fonction `Trainer`, utilisons les mêmes paramètres par défaut. L'optimiseur utilisé par `Trainer` est `AdamW`, qui est le même qu'Adam, mais avec une torsion pour la régularisation par décroissance de poids (voir [*Decoupled Weight Decay Regularization*](https://arxiv.org/abs/1711.05101) par Ilya Loshchilov et Frank Hutter) :
-
-```py
-from transformers import AdamW
-
-optimizer = AdamW(model.parameters(), lr=5e-5)
-```
-
-Enfin, le planificateur du taux d'apprentissage utilisé par défaut est juste une décroissance linéaire de la valeur maximale (5e-5) à 0. Pour le définir correctement, nous devons connaître le nombre d'étapes d'entraînement que nous prendrons, qui est le nombre d'époques que nous voulons exécuter multiplié par le nombre de batch d'entraînement (qui est la longueur de notre *dataloader* d'entraînement). Le `Trainer` utilise trois époques par défaut, nous allons donc suivre ça :
-
-```py
-from transformers import get_scheduler
-
-num_epochs = 3
-num_training_steps = num_epochs * len(train_dataloader)
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-print(num_training_steps)
-```
-
-```python out
-1377
-```
-
-### La boucle d'entraînement
-
-Une dernière chose : nous voulons utiliser le GPU si nous en avons un (sur un CPU, l'entraînement peut prendre plusieurs heures au lieu de quelques minutes). Pour ce faire, nous définissons un `device` sur lequel nous allons placer notre modèle et nos batchs :
-
-```py
-import torch
-
-device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-model.to(device)
-device
-```
-
-```python out
-device(type='cuda')
-```
-
-Nous sommes maintenant prêts à entraîner ! Pour avoir une idée du moment où l'entraînement sera terminé, nous ajoutons une barre de progression sur le nombre d'étapes d'entraînement, en utilisant la bibliothèque `tqdm` :
-
-```py
-from tqdm.auto import tqdm
-
-progress_bar = tqdm(range(num_training_steps))
-
-model.train()
-for epoch in range(num_epochs):
- for batch in train_dataloader:
- batch = {k: v.to(device) for k, v in batch.items()}
- outputs = model(**batch)
- loss = outputs.loss
- loss.backward()
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-Vous pouvez voir que le cœur de la boucle d'entraînement ressemble beaucoup à celui de l'introduction. Nous n'avons pas demandé de rapport, donc cette boucle d'entraînement ne nous dira rien sur les résultats du modèle. Pour cela, nous devons ajouter une boucle d'évaluation.
-
-
-### La boucle d'évaluation
-
-Comme nous l'avons fait précédemment, nous allons utiliser une métrique fournie par la bibliothèque 🤗 *Evaluate*. Nous avons déjà vu la méthode `metric.compute()`, mais les métriques peuvent en fait accumuler des batchs pour nous au fur et à mesure que nous parcourons la boucle de prédiction avec la méthode `add_batch()`. Une fois que nous avons accumulé tous les batchs, nous pouvons obtenir le résultat final avec `metric.compute()`. Voici comment implémenter tout cela dans une boucle d'évaluation :
-
-```py
-import evaluate
-
-metric = evaluate.load("glue", "mrpc")
-model.eval()
-for batch in eval_dataloader:
- batch = {k: v.to(device) for k, v in batch.items()}
- with torch.no_grad():
- outputs = model(**batch)
-
- logits = outputs.logits
- predictions = torch.argmax(logits, dim=-1)
- metric.add_batch(predictions=predictions, references=batch["labels"])
-
-metric.compute()
-```
-
-```python out
-{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
-```
-
-Une fois encore, vos résultats seront légèrement différents en raison du caractère aléatoire de l'initialisation de la tête du modèle et du mélange des données, mais ils devraient se situer dans la même fourchette.
-
-
-
-✏️ **Essayez** Modifiez la boucle d'entraînement précédente pour *finetuner* votre modèle sur le jeu de données SST-2.
-
-
-
-### Optimisez votre boucle d'entraînement avec 🤗 Accelerate
-
-
-
-La boucle d'entraînement que nous avons définie précédemment fonctionne bien sur un seul CPU ou GPU. Mais en utilisant la bibliothèque [🤗 *Accelerate*](https://github.com/huggingface/accelerate), il suffit de quelques ajustements pour permettre un entraînement distribué sur plusieurs GPUs ou TPUs. En partant de la création des *dataloaders* d'entraînement et de validation, voici à quoi ressemble notre boucle d'entraînement manuel :
-
-```py
-from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-optimizer = AdamW(model.parameters(), lr=3e-5)
-
-device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-model.to(device)
-
-num_epochs = 3
-num_training_steps = num_epochs * len(train_dataloader)
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-
-progress_bar = tqdm(range(num_training_steps))
-
-model.train()
-for epoch in range(num_epochs):
- for batch in train_dataloader:
- batch = {k: v.to(device) for k, v in batch.items()}
- outputs = model(**batch)
- loss = outputs.loss
- loss.backward()
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-Et voici les changements :
-
-```diff
-+ from accelerate import Accelerator
- from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
-
-+ accelerator = Accelerator()
-
- model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
- optimizer = AdamW(model.parameters(), lr=3e-5)
-
-- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-- model.to(device)
-
-+ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare(
-+ train_dataloader, eval_dataloader, model, optimizer
-+ )
-
- num_epochs = 3
- num_training_steps = num_epochs * len(train_dataloader)
- lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps
- )
-
- progress_bar = tqdm(range(num_training_steps))
-
- model.train()
- for epoch in range(num_epochs):
- for batch in train_dataloader:
-- batch = {k: v.to(device) for k, v in batch.items()}
- outputs = model(**batch)
- loss = outputs.loss
-- loss.backward()
-+ accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-La première ligne à ajouter est la ligne d'importation. La deuxième ligne instancie un objet `Accelerator` qui va regarder l'environnement et initialiser la bonne configuration distribuée. 🤗 *Accelerate* gère le placement des périphériques pour vous, donc vous pouvez enlever les lignes qui placent le modèle sur le périphérique (ou, si vous préférez, les changer pour utiliser `accelerator.device` au lieu de `device`).
-
-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.
-
-
-Si vous souhaitez faire un copier-coller pour jouer, voici à quoi ressemble la boucle d'entraînement complète avec 🤗 Accelerate :
-
-```py
-from accelerate import Accelerator
-from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
-
-accelerator = Accelerator()
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-optimizer = AdamW(model.parameters(), lr=3e-5)
-
-train_dl, eval_dl, model, optimizer = accelerator.prepare(
- train_dataloader, eval_dataloader, model, optimizer
-)
-
-num_epochs = 3
-num_training_steps = num_epochs * len(train_dl)
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-
-progress_bar = tqdm(range(num_training_steps))
-
-model.train()
-for epoch in range(num_epochs):
- for batch in train_dl:
- outputs = model(**batch)
- loss = outputs.loss
- accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-En plaçant ceci dans un script `train.py`, cela sera exécutable sur n'importe quel type d'installation distribuée. Pour l'essayer dans votre installation distribuée, exécutez la commande :
-
-```bash
-accelerate config
-```
-
-qui vous demandera de répondre à quelques questions et enregistrera vos réponses dans un fichier de configuration utilisé par cette commande :
-
-```
-accelerate launch train.py
-```
-
-qui lancera l'entraînement distribué.
-
-Si vous voulez essayer ceci dans un *notebook* (par exemple, pour le tester avec des TPUs sur Colab), collez simplement le code dans une `training_function()` et lancez une dernière cellule avec :
-
-```python
-from accelerate import notebook_launcher
-
-notebook_launcher(training_function)
-```
-
-Vous trouverez d'autres exemples dans le dépôt d'[🤗 *Accelerate*](https://github.com/huggingface/accelerate/tree/main/examples).
+# Un entraînement complet
+
+
+
+
+
+Maintenant nous allons voir comment obtenir les mêmes résultats que dans la dernière section sans utiliser la classe `Trainer`. Encore une fois, nous supposons que vous avez fait le traitement des données dans la section 2. Voici un court résumé couvrant tout ce dont vous aurez besoin :
+
+```py
+from datasets import load_dataset
+from transformers import AutoTokenizer, DataCollatorWithPadding
+
+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)
+```
+
+### Préparer l'entraînement
+
+Avant d'écrire réellement notre boucle d'entraînement, nous devons définir quelques objets. Les premiers sont les *dataloaders* que nous utiliserons pour itérer sur les batchs. Mais avant de pouvoir définir ces chargeurs de données, nous devons appliquer un peu de post-traitement à nos `tokenized_datasets`, pour prendre soin de certaines choses que le `Trainer` fait pour nous automatiquement. Spécifiquement, nous devons :
+
+- supprimer les colonnes correspondant aux valeurs que le modèle n'attend pas (comme les colonnes `sentence1` et `sentence2`),
+- renommer la colonne `label` en `labels` (parce que le modèle s'attend à ce que l'argument soit nommé `labels`),
+- définir le format des jeux de données pour qu'ils retournent des tenseurs PyTorch au lieu de listes.
+
+Notre `tokenized_datasets` a une méthode pour chacune de ces étapes :
+
+```py
+tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
+tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
+tokenized_datasets.set_format("torch")
+tokenized_datasets["train"].column_names
+```
+
+Nous pouvons alors vérifier que le résultat ne comporte que des colonnes que notre modèle acceptera :
+
+```python
+["attention_mask", "input_ids", "labels", "token_type_ids"]
+```
+
+Maintenant que cela est fait, nous pouvons facilement définir nos *dataloaders* :
+
+```py
+from torch.utils.data import DataLoader
+
+train_dataloader = DataLoader(
+ tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
+)
+eval_dataloader = DataLoader(
+ tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
+)
+```
+
+Pour vérifier rapidement qu'il n'y a pas d'erreur dans le traitement des données, nous pouvons inspecter un batch comme celui-ci :
+
+```py
+for batch in train_dataloader:
+ break
+{k: v.shape for k, v in batch.items()}
+```
+
+```python out
+{'attention_mask': torch.Size([8, 65]),
+ 'input_ids': torch.Size([8, 65]),
+ 'labels': torch.Size([8]),
+ 'token_type_ids': torch.Size([8, 65])}
+```
+
+Notez que les formes réelles seront probablement légèrement différentes pour vous puisque nous avons défini `shuffle=True` pour le chargeur de données d'entraînement et que nous *paddons* à la longueur maximale dans le batch.
+
+Maintenant que nous en avons terminé avec le prétraitement des données (un objectif satisfaisant mais difficile à atteindre pour tout praticien d'apprentissage automatique), passons au modèle. Nous l'instancions exactement comme nous l'avons fait dans la section précédente :
+
+```py
+from transformers import AutoModelForSequenceClassification
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+```
+
+Pour s'assurer que tout se passera bien pendant l'entraînement, nous transmettons notre batch à ce modèle :
+
+```py
+outputs = model(**batch)
+print(outputs.loss, outputs.logits.shape)
+```
+
+```python out
+tensor(0.5441, grad_fn=) torch.Size([8, 2])
+```
+
+Tous les modèles 🤗 *Transformers* renvoient la perte lorsque les `labels` sont fournis. Nous obtenons également les logits (deux pour chaque entrée de notre batch, donc un tenseur de taille 8 x 2).
+
+Nous sommes presque prêts à écrire notre boucle d'entraînement ! Il nous manque juste deux choses : un optimiseur et un planificateur de taux d'apprentissage. Puisque nous essayons de reproduire à la main ce que fait la fonction `Trainer`, utilisons les mêmes paramètres par défaut. L'optimiseur utilisé par `Trainer` est `AdamW`, qui est le même qu'Adam, mais avec une torsion pour la régularisation par décroissance de poids (voir [*Decoupled Weight Decay Regularization*](https://arxiv.org/abs/1711.05101) par Ilya Loshchilov et Frank Hutter) :
+
+```py
+from transformers import AdamW
+
+optimizer = AdamW(model.parameters(), lr=5e-5)
+```
+
+Enfin, le planificateur du taux d'apprentissage utilisé par défaut est juste une décroissance linéaire de la valeur maximale (5e-5) à 0. Pour le définir correctement, nous devons connaître le nombre d'étapes d'entraînement que nous prendrons, qui est le nombre d'époques que nous voulons exécuter multiplié par le nombre de batch d'entraînement (qui est la longueur de notre *dataloader* d'entraînement). Le `Trainer` utilise trois époques par défaut, nous allons donc suivre ça :
+
+```py
+from transformers import get_scheduler
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dataloader)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+print(num_training_steps)
+```
+
+```python out
+1377
+```
+
+### La boucle d'entraînement
+
+Une dernière chose : nous voulons utiliser le GPU si nous en avons un (sur un CPU, l'entraînement peut prendre plusieurs heures au lieu de quelques minutes). Pour ce faire, nous définissons un `device` sur lequel nous allons placer notre modèle et nos batchs :
+
+```py
+import torch
+
+device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+model.to(device)
+device
+```
+
+```python out
+device(type='cuda')
+```
+
+Nous sommes maintenant prêts à entraîner ! Pour avoir une idée du moment où l'entraînement sera terminé, nous ajoutons une barre de progression sur le nombre d'étapes d'entraînement, en utilisant la bibliothèque `tqdm` :
+
+```py
+from tqdm.auto import tqdm
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ outputs = model(**batch)
+ loss = outputs.loss
+ loss.backward()
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+Vous pouvez voir que le cœur de la boucle d'entraînement ressemble beaucoup à celui de l'introduction. Nous n'avons pas demandé de rapport, donc cette boucle d'entraînement ne nous dira rien sur les résultats du modèle. Pour cela, nous devons ajouter une boucle d'évaluation.
+
+
+### La boucle d'évaluation
+
+Comme nous l'avons fait précédemment, nous allons utiliser une métrique fournie par la bibliothèque 🤗 *Evaluate*. Nous avons déjà vu la méthode `metric.compute()`, mais les métriques peuvent en fait accumuler des batchs pour nous au fur et à mesure que nous parcourons la boucle de prédiction avec la méthode `add_batch()`. Une fois que nous avons accumulé tous les batchs, nous pouvons obtenir le résultat final avec `metric.compute()`. Voici comment implémenter tout cela dans une boucle d'évaluation :
+
+```py
+import evaluate
+
+metric = evaluate.load("glue", "mrpc")
+model.eval()
+for batch in eval_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ with torch.no_grad():
+ outputs = model(**batch)
+
+ logits = outputs.logits
+ predictions = torch.argmax(logits, dim=-1)
+ metric.add_batch(predictions=predictions, references=batch["labels"])
+
+metric.compute()
+```
+
+```python out
+{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
+```
+
+Une fois encore, vos résultats seront légèrement différents en raison du caractère aléatoire de l'initialisation de la tête du modèle et du mélange des données, mais ils devraient se situer dans la même fourchette.
+
+
+
+✏️ **Essayez** Modifiez la boucle d'entraînement précédente pour *finetuner* votre modèle sur le jeu de données SST-2.
+
+
+
+### Optimisez votre boucle d'entraînement avec 🤗 Accelerate
+
+
+
+La boucle d'entraînement que nous avons définie précédemment fonctionne bien sur un seul CPU ou GPU. Mais en utilisant la bibliothèque [🤗 *Accelerate*](https://github.com/huggingface/accelerate), il suffit de quelques ajustements pour permettre un entraînement distribué sur plusieurs GPUs ou TPUs. En partant de la création des *dataloaders* d'entraînement et de validation, voici à quoi ressemble notre boucle d'entraînement manuel :
+
+```py
+from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+optimizer = AdamW(model.parameters(), lr=3e-5)
+
+device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+model.to(device)
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dataloader)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ outputs = model(**batch)
+ loss = outputs.loss
+ loss.backward()
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+Et voici les changements :
+
+```diff
++ from accelerate import Accelerator
+ from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
+
++ accelerator = Accelerator()
+
+ model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+ optimizer = AdamW(model.parameters(), lr=3e-5)
+
+- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+- model.to(device)
+
++ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare(
++ train_dataloader, eval_dataloader, model, optimizer
++ )
+
+ num_epochs = 3
+ num_training_steps = num_epochs * len(train_dataloader)
+ lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps
+ )
+
+ progress_bar = tqdm(range(num_training_steps))
+
+ model.train()
+ for epoch in range(num_epochs):
+ for batch in train_dataloader:
+- batch = {k: v.to(device) for k, v in batch.items()}
+ outputs = model(**batch)
+ loss = outputs.loss
+- loss.backward()
++ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+La première ligne à ajouter est la ligne d'importation. La deuxième ligne instancie un objet `Accelerator` qui va regarder l'environnement et initialiser la bonne configuration distribuée. 🤗 *Accelerate* gère le placement des périphériques pour vous, donc vous pouvez enlever les lignes qui placent le modèle sur le périphérique (ou, si vous préférez, les changer pour utiliser `accelerator.device` au lieu de `device`).
+
+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.
+
+
+Si vous souhaitez faire un copier-coller pour jouer, voici à quoi ressemble la boucle d'entraînement complète avec 🤗 Accelerate :
+
+```py
+from accelerate import Accelerator
+from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
+
+accelerator = Accelerator()
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+optimizer = AdamW(model.parameters(), lr=3e-5)
+
+train_dl, eval_dl, model, optimizer = accelerator.prepare(
+ train_dataloader, eval_dataloader, model, optimizer
+)
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dl)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dl:
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+En plaçant ceci dans un script `train.py`, cela sera exécutable sur n'importe quel type d'installation distribuée. Pour l'essayer dans votre installation distribuée, exécutez la commande :
+
+```bash
+accelerate config
+```
+
+qui vous demandera de répondre à quelques questions et enregistrera vos réponses dans un fichier de configuration utilisé par cette commande :
+
+```
+accelerate launch train.py
+```
+
+qui lancera l'entraînement distribué.
+
+Si vous voulez essayer ceci dans un *notebook* (par exemple, pour le tester avec des TPUs sur Colab), collez simplement le code dans une `training_function()` et lancez une dernière cellule avec :
+
+```python
+from accelerate import notebook_launcher
+
+notebook_launcher(training_function)
+```
+
+Vous trouverez d'autres exemples dans le dépôt d'[🤗 *Accelerate*](https://github.com/huggingface/accelerate/tree/main/examples).
diff --git a/chapters/fr/chapter3/5.mdx b/chapters/fr/chapter3/5.mdx
index db244e545..782e3f5d8 100644
--- a/chapters/fr/chapter3/5.mdx
+++ b/chapters/fr/chapter3/5.mdx
@@ -1,20 +1,25 @@
-
-
-# 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 :
-
-{#if fw === 'pt'}
-* avez appris à connaître les jeux de données dans le [*Hub*](https://huggingface.co/datasets),
-* avez appris à charger et à prétraiter des jeux de données, notamment en utilisant le remplissage dynamique et les assembleurs,
-* avez implémenté votre propre *finetuning* et évaluation d'un modèle,
-* avez implémenté une boucle d'entraînement de niveau inférieur,
-* avez utilisé 🤗 *Accelerate* pour adapter facilement votre boucle d'entraînement afin qu'elle fonctionne pour plusieurs GPUs ou TPUs.
-
-{:else}
-* avez appris à connaître les jeux de données dans le [*Hub*](https://huggingface.co/datasets),
-* avez appris comment charger et prétraiter les jeux de données,
-* avez appris comment *finetuner* et évaluer un modèle avec Keras,
-* avez implémenté une métrique personnalisée.
-
+
+
+# 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 :
+
+{#if fw === 'pt'}
+* avez appris à connaître les jeux de données dans le [*Hub*](https://huggingface.co/datasets),
+* avez appris à charger et à prétraiter des jeux de données, notamment en utilisant le remplissage dynamique et les assembleurs,
+* avez implémenté votre propre *finetuning* et évaluation d'un modèle,
+* avez implémenté une boucle d'entraînement de niveau inférieur,
+* avez utilisé 🤗 *Accelerate* pour adapter facilement votre boucle d'entraînement afin qu'elle fonctionne pour plusieurs GPUs ou TPUs.
+
+{:else}
+* avez appris à connaître les jeux de données dans le [*Hub*](https://huggingface.co/datasets),
+* avez appris comment charger et prétraiter les jeux de données,
+* avez appris comment *finetuner* et évaluer un modèle avec Keras,
+* avez implémenté une métrique personnalisée.
+
{/if}
\ No newline at end of file
diff --git a/chapters/fr/chapter3/6.mdx b/chapters/fr/chapter3/6.mdx
index 44c0fdf44..5379a1fbe 100644
--- a/chapters/fr/chapter3/6.mdx
+++ b/chapters/fr/chapter3/6.mdx
@@ -1,296 +1,301 @@
-
-
-
-
-# Quiz de fin de chapitre
-
-Testez ce que vous avez appris dans ce chapitre !
-
-### 1. Le jeu de données `emotion` contient des messages Twitter étiquetés avec des émotions. Cherchez-le dans le [*Hub*](https://huggingface.co/datasets) et lisez la carte du jeu de données. Laquelle de ces émotions n'est pas une de ses émotions de base ?
-
-
-
-### 2. Cherchez le jeu de données `ar_sarcasme` dans le [*Hub*](https://huggingface.co/datasets). Quelle tâche prend-il en charge ?
-
-tags.",
- correct: true
- },
- {
- text: "Traduction automatique",
- explain: "Ce n'est pas ça. Jetez un autre coup d'œil à la carte du jeu de données !"
- },
- {
- text: "Reconnaissance des entités nommées",
- explain: "Ce n'est pas ça. Jetez un autre coup d'œil à la carte du jeu de données !"
- },
- {
- text: "Réponse aux questions",
- explain: "Hélas, cette question n'a pas reçu de réponse correcte. Essayez à nouveau !"
- }
- ]}
-/>
-
-### 3. Comment le modèle BERT attend-il qu'une paire de phrases soit traitée ?
-
-[SEP] est nécessaire pour séparer les deux phrases, mais ce n'est pas tout !"
- },
- {
- text: "[CLS] Tokens_de_la_phrase_1 Tokens_de_la_phrase_2",
- explain: "Un jeton spécial [CLS]
est requis au début, mais ce n'est pas la seule chose !"
- },
- {
- text: "[CLS] Tokens_de_la_phrase_1 [SEP] Tokens_de_la_phrase_2 [SEP]",
- explain: "C'est exact !",
- correct: true
- },
- {
- text: "[CLS] Tokens_de_la_phrase_1 [SEP] Tokens_de_la_phrase_2",
- explain: "Un jeton spécial [CLS]
est nécessaire au début, ainsi qu'un jeton spécial [SEP]
pour séparer les deux phrases, mais ce n'est pas tout !"
- }
- ]}
-/>
-
-{#if fw === 'pt'}
-### 4. Quels sont les avantages de la méthode `Dataset.map()` ?
-
-
-
-### 5. Que signifie le remplissage (*padding*) dynamique ?
-
-tokens que la précédente dans le jeu de données.",
- 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."
- },
- ]}
-/>
-
-### 6. Quel est le but d'une fonction de rassemblement ?
-
-DataCollatorWithPadding."
- },
- {
- text: "Elle rassemble tous les échantillons dans un batch.",
- 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
- },
- {
- text: "Elle pré-traite tout le jeu de données.",
- explain: "Ce serait une fonction de prétraitement, pas une fonction de rassemblement."
- },
- {
- text: "Elle tronque les séquences dans le jeu de données.",
- explain: "Une fonction de rassemblement est impliquée dans le traitement des batchs individuels, et non de tout le jeu de données. Si vous êtes intéressé par la troncature, vous pouvez utiliser la fonction truncate
en argument du tokenizer
."
- }
- ]}
-/>
-
-### 7. Que se passe-t-il lorsque vous instanciez une des classes `AutoModelForXxx` avec un modèle de langage pré-entraîné (tel que `bert-base-uncased`) qui correspond à une tâche différente de celle pour laquelle il a été entraîné ?
-
-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
- },
- {
- text: "La tête du modèle pré-entraîné est supprimée.",
- explain: "Quelque chose d'autre doit se produire. Essayez encore !"
- },
- {
- text: "Rien, puisque le modèle peut encore être finetuné pour les différentes tâches.",
- explain: "La tête du modèle pré-entraîné n'a pas été entraînée à résoudre cette tâche, nous devons donc la supprimer !"
- }
- ]}
-/>
-
-### 8. Quel est le but de `TrainingArguments` ?
-
-Trainer.",
- explain: "",
- correct: true
- },
- {
- text: "Préciser la taille du modèle.",
- explain: "La taille du modèle est définie par la configuration du modèle, et non par la classe TrainingArguments
."
- },
- {
- text: "Juste contenir les hyperparamètres utilisés pour l'évaluation.",
- explain: "Dans l'exemple, nous avons spécifié où le modèle et ses checkpoints seront sauvegardés. Essayez à nouveau !"
- },
- {
- text: "Contenir seulement les hyperparamètres utilisés pour l'entraînement.",
- explain: "Dans l'exemple, nous avons utilisé une evaluation_strategy
également, ce qui a un impact sur l'évaluation. Essayez à nouveau !"
- }
- ]}
-/>
-
-### 9. Pourquoi devriez-vous utiliser la librairie 🤗 *Accelerate* ?
-
-Accelerate ne fournit aucun modèles."
- },
- {
- text: "Elle fournit une API de haut niveau qui évite d'avoir à mettre en place sa propre boucle d'entraînement.",
- explain: "C'est ce que nous avons fait avec le Trainer
mais pas avec la librairie 🤗 Accelerate. Essayez à nouveau !"
- },
- {
- text: "Elle permet à nos boucles d'entraînement de fonctionner avec des stratégies distribuées.",
- explain: "Avec 🤗 Accelerate, vos boucles d'entraînement fonctionneront pour plusieurs GPUs et TPUs.",
- correct: true
- },
- {
- text: "Elle offre davantage de fonctions d'optimisation.",
- explain: "Non, la librairie 🤗 Accelerate ne fournit pas de fonctions d'optimisation."
- }
- ]}
-/>
-
-{:else}
-### 4. Que se passe-t-il lorsque vous instanciez une des classes `TFAutoModelForXxx` avec un modèle de langage pré-entraîné (tel que `bert-base-uncased`) qui correspond à une tâche différente de celle pour laquelle il a été entraîné ?
-
-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
- },
- {
- text: "La tête du modèle pré-entraîné est supprimée.",
- explain: "Quelque chose d'autre doit se produire. Essayez encore !"
- },
- {
- text: "Rien, puisque le modèle peut encore être finetuné pour les différentes tâches.",
- explain: "La tête du modèle pré-entraîné n'a pas été entraînée à résoudre cette tâche, nous devons donc la supprimer !"
- }
- ]}
-/>
-
-### 5. Les modèles TensorFlow de `transformers` sont déjà des modèles Keras. Quel avantage cela offre-t-il ?
-
-TPUStrategy, y compris l'initialisation du modèle."
- },
- {
- text: "Vous pouvez tirer parti des méthodes existantes telles que compile()
, fit()
et predict()
.",
- 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: "Mais nous cherchons quelque chose d'autre :)",
- correct: true
- },
- {
- text: "Vous pouvez facilement calculer les métriques liées au jeu de données.",
- explain: "Keras nous aide à entraîner et à évaluer le modèle, et non à calculer les paramètres liés aux jeux de données."
- }
- ]}
-/>
-
-### 6. Comment pouvez-vous définir votre propre métrique personnalisée ?
-
-tf.keras.metrics.Metric.",
- explain: "Excellent !",
- correct: true
- },
- {
- text: "Utilisation de l'API fonctionnelle de Keras.",
- explain: "Essayez à nouveau !"
- },
- {
- text: "En utilisant un callable avec la signature metric_fn(y_true, y_pred)
.",
- explain: " ",
- correct: true
- },
- {
- text: "En le googlant.",
- explain: "Ce n'est pas la réponse que nous cherchons, mais cela devrait vous aider à la trouver.",
- correct: true
- }
- ]}
-/>
-
-{/if}
+
+
+
+
+# Quiz de fin de chapitre
+
+
+
+Testez ce que vous avez appris dans ce chapitre !
+
+### 1. Le jeu de données `emotion` contient des messages Twitter étiquetés avec des émotions. Cherchez-le dans le [*Hub*](https://huggingface.co/datasets) et lisez la carte du jeu de données. Laquelle de ces émotions n'est pas une de ses émotions de base ?
+
+
+
+### 2. Cherchez le jeu de données `ar_sarcasme` dans le [*Hub*](https://huggingface.co/datasets). Quelle tâche prend-il en charge ?
+
+tags.",
+ correct: true
+ },
+ {
+ text: "Traduction automatique",
+ explain: "Ce n'est pas ça. Jetez un autre coup d'œil à la carte du jeu de données !"
+ },
+ {
+ text: "Reconnaissance des entités nommées",
+ explain: "Ce n'est pas ça. Jetez un autre coup d'œil à la carte du jeu de données !"
+ },
+ {
+ text: "Réponse aux questions",
+ explain: "Hélas, cette question n'a pas reçu de réponse correcte. Essayez à nouveau !"
+ }
+ ]}
+/>
+
+### 3. Comment le modèle BERT attend-il qu'une paire de phrases soit traitée ?
+
+[SEP] est nécessaire pour séparer les deux phrases, mais ce n'est pas tout !"
+ },
+ {
+ text: "[CLS] Tokens_de_la_phrase_1 Tokens_de_la_phrase_2",
+ explain: "Un jeton spécial [CLS]
est requis au début, mais ce n'est pas la seule chose !"
+ },
+ {
+ text: "[CLS] Tokens_de_la_phrase_1 [SEP] Tokens_de_la_phrase_2 [SEP]",
+ explain: "C'est exact !",
+ correct: true
+ },
+ {
+ text: "[CLS] Tokens_de_la_phrase_1 [SEP] Tokens_de_la_phrase_2",
+ explain: "Un jeton spécial [CLS]
est nécessaire au début, ainsi qu'un jeton spécial [SEP]
pour séparer les deux phrases, mais ce n'est pas tout !"
+ }
+ ]}
+/>
+
+{#if fw === 'pt'}
+### 4. Quels sont les avantages de la méthode `Dataset.map()` ?
+
+
+
+### 5. Que signifie le remplissage (*padding*) dynamique ?
+
+tokens que la précédente dans le jeu de données.",
+ 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."
+ },
+ ]}
+/>
+
+### 6. Quel est le but d'une fonction de rassemblement ?
+
+DataCollatorWithPadding."
+ },
+ {
+ text: "Elle rassemble tous les échantillons dans un batch.",
+ 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
+ },
+ {
+ text: "Elle pré-traite tout le jeu de données.",
+ explain: "Ce serait une fonction de prétraitement, pas une fonction de rassemblement."
+ },
+ {
+ text: "Elle tronque les séquences dans le jeu de données.",
+ explain: "Une fonction de rassemblement est impliquée dans le traitement des batchs individuels, et non de tout le jeu de données. Si vous êtes intéressé par la troncature, vous pouvez utiliser la fonction truncate
en argument du tokenizer
."
+ }
+ ]}
+/>
+
+### 7. Que se passe-t-il lorsque vous instanciez une des classes `AutoModelForXxx` avec un modèle de langage pré-entraîné (tel que `bert-base-uncased`) qui correspond à une tâche différente de celle pour laquelle il a été entraîné ?
+
+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
+ },
+ {
+ text: "La tête du modèle pré-entraîné est supprimée.",
+ explain: "Quelque chose d'autre doit se produire. Essayez encore !"
+ },
+ {
+ text: "Rien, puisque le modèle peut encore être finetuné pour les différentes tâches.",
+ explain: "La tête du modèle pré-entraîné n'a pas été entraînée à résoudre cette tâche, nous devons donc la supprimer !"
+ }
+ ]}
+/>
+
+### 8. Quel est le but de `TrainingArguments` ?
+
+Trainer.",
+ explain: "",
+ correct: true
+ },
+ {
+ text: "Préciser la taille du modèle.",
+ explain: "La taille du modèle est définie par la configuration du modèle, et non par la classe TrainingArguments
."
+ },
+ {
+ text: "Juste contenir les hyperparamètres utilisés pour l'évaluation.",
+ explain: "Dans l'exemple, nous avons spécifié où le modèle et ses checkpoints seront sauvegardés. Essayez à nouveau !"
+ },
+ {
+ text: "Contenir seulement les hyperparamètres utilisés pour l'entraînement.",
+ explain: "Dans l'exemple, nous avons utilisé une evaluation_strategy
également, ce qui a un impact sur l'évaluation. Essayez à nouveau !"
+ }
+ ]}
+/>
+
+### 9. Pourquoi devriez-vous utiliser la librairie 🤗 *Accelerate* ?
+
+Accelerate ne fournit aucun modèles."
+ },
+ {
+ text: "Elle fournit une API de haut niveau qui évite d'avoir à mettre en place sa propre boucle d'entraînement.",
+ explain: "C'est ce que nous avons fait avec le Trainer
mais pas avec la librairie 🤗 Accelerate. Essayez à nouveau !"
+ },
+ {
+ text: "Elle permet à nos boucles d'entraînement de fonctionner avec des stratégies distribuées.",
+ explain: "Avec 🤗 Accelerate, vos boucles d'entraînement fonctionneront pour plusieurs GPUs et TPUs.",
+ correct: true
+ },
+ {
+ text: "Elle offre davantage de fonctions d'optimisation.",
+ explain: "Non, la librairie 🤗 Accelerate ne fournit pas de fonctions d'optimisation."
+ }
+ ]}
+/>
+
+{:else}
+### 4. Que se passe-t-il lorsque vous instanciez une des classes `TFAutoModelForXxx` avec un modèle de langage pré-entraîné (tel que `bert-base-uncased`) qui correspond à une tâche différente de celle pour laquelle il a été entraîné ?
+
+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
+ },
+ {
+ text: "La tête du modèle pré-entraîné est supprimée.",
+ explain: "Quelque chose d'autre doit se produire. Essayez encore !"
+ },
+ {
+ text: "Rien, puisque le modèle peut encore être finetuné pour les différentes tâches.",
+ explain: "La tête du modèle pré-entraîné n'a pas été entraînée à résoudre cette tâche, nous devons donc la supprimer !"
+ }
+ ]}
+/>
+
+### 5. Les modèles TensorFlow de `transformers` sont déjà des modèles Keras. Quel avantage cela offre-t-il ?
+
+TPUStrategy, y compris l'initialisation du modèle."
+ },
+ {
+ text: "Vous pouvez tirer parti des méthodes existantes telles que compile()
, fit()
et predict()
.",
+ 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: "Mais nous cherchons quelque chose d'autre :)",
+ correct: true
+ },
+ {
+ text: "Vous pouvez facilement calculer les métriques liées au jeu de données.",
+ explain: "Keras nous aide à entraîner et à évaluer le modèle, et non à calculer les paramètres liés aux jeux de données."
+ }
+ ]}
+/>
+
+### 6. Comment pouvez-vous définir votre propre métrique personnalisée ?
+
+tf.keras.metrics.Metric.",
+ explain: "Excellent !",
+ correct: true
+ },
+ {
+ text: "Utilisation de l'API fonctionnelle de Keras.",
+ explain: "Essayez à nouveau !"
+ },
+ {
+ text: "En utilisant un callable avec la signature metric_fn(y_true, y_pred)
.",
+ explain: " ",
+ correct: true
+ },
+ {
+ text: "En le googlant.",
+ explain: "Ce n'est pas la réponse que nous cherchons, mais cela devrait vous aider à la trouver.",
+ correct: true
+ }
+ ]}
+/>
+
+{/if}
diff --git a/chapters/fr/chapter4/1.mdx b/chapters/fr/chapter4/1.mdx
index 7a0420461..b27ba639e 100644
--- a/chapters/fr/chapter4/1.mdx
+++ b/chapters/fr/chapter4/1.mdx
@@ -1,17 +1,22 @@
-# 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* :
-
-
-
+# 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 86579685f..6f86e8f4f 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 fabdd4a36..b9db79e6e 100644
--- a/chapters/fr/chapter4/3.mdx
+++ b/chapters/fr/chapter4/3.mdx
@@ -1,638 +1,638 @@
-
-
-# Partage de modèles pré-entraînés
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Dans les étapes ci-dessous, nous allons examiner les moyens les plus simples de partager des modèles pré-entraînés sur le 🤗 *Hub*. Il existe des outils et des services disponibles qui permettent de simplifier le partage et la mise à jour des modèles directement sur le *Hub*, que nous allons explorer ci-dessous.
-
-
-
-Nous encourageons tous les utilisateurs qui entraînent des modèles à contribuer en les partageant avec la communauté. Le partage des modèles, même s'ils ont été entraînés sur des jeux de données très spécifiques, aidera les autres, en leur faisant gagner du temps, des ressources de calcul et en leur donnant accès à des artefacts entraînés utiles. À votre tour, vous pourrez bénéficier du travail effectué par les autres !
-
-Il y a trois façons de créer de nouveaux dépôts de modèles :
-
-- en utilisant l'API `push_to_hub`,
-- en utilisant la bibliothèque Python `huggingface_hub`,
-- en utilisant l'interface web.
-
-Une fois que vous avez créé un dépôt, vous pouvez y charger des fichiers via git et git-lfs. Nous allons vous guider dans la création de dépôts de modèles et le téléchargement de fichiers dans les sections suivantes.
-
-
-## Utilisation de l'API `push_to_hub`
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-La façon la plus simple de télécharger des fichiers vers le *Hub* est d'utiliser l'API `push_to_hub`.
-
-Avant d'aller plus loin, vous devrez générer un jeton d'authentification afin que l'API `huggingface_hub` sache qui vous êtes et à quels espaces de noms vous avez accès en écriture. Assurez-vous que vous êtes dans un environnement où vous avez installé `transformers` (voir la [Configuration](/course/fr/chapter0)). Si vous êtes dans un *notebook*, vous pouvez utiliser la fonction suivante pour vous connecter :
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-Dans un terminal, vous pouvez exécuter :
-
-```bash
-huggingface-cli login
-```
-
-Dans les deux cas, vous serez invité à saisir votre nom d'utilisateur et votre mot de passe, qui sont les mêmes que ceux que vous utilisez pour vous connecter au *Hub*. Si vous n'avez pas encore de profil pour le Hub, vous devez en créer un [ici](https://huggingface.co/join).
-
-Super ! Votre jeton d'authentification est maintenant stocké dans votre dossier de cache. Créons quelques dépôts !
-
-{#if fw === 'pt'}
-
-Si vous avez joué avec l'API `Trainer` pour entraîner un modèle, le moyen le plus simple de le télécharger sur le *Hub* est de définir `push_to_hub=True` lorsque vous définissez vos `TrainingArguments` :
-
-```py
-from transformers import TrainingArguments
-
-training_args = TrainingArguments(
- "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True
-)
-```
-
-Lorsque vous appelez `trainer.train()`, le `Trainer` téléchargera alors votre modèle vers le *Hub* à chaque fois qu'il sera sauvegardé (ici à chaque époque) dans un dépôt dans votre espace personnel. Ce dépôt sera nommé comme le répertoire de sortie que vous avez choisi (ici `bert-finetuned-mrpc`) mais vous pouvez choisir un nom différent avec `hub_model_id = "a_different_name"`.
-
-Pour télécharger votre modèle vers une organisation dont vous êtes membre, passez-le simplement avec `hub_model_id = "my_organization/my_repo_name"`.
-
-Une fois que votre entraînement est terminé, vous devriez faire un dernier `trainer.push_to_hub()` pour télécharger la dernière version de votre modèle. Cela générera également une carte pour le modèle avec toutes les métadonnées pertinentes, rapportant les hyperparamètres utilisés et les résultats d'évaluation ! Voici un exemple du contenu que vous pourriez trouver dans une telle carte de modèle :
-
-
-
-
-{:else}
-
-Si vous utilisez Keras pour entraîner votre modèle, le moyen le plus simple de le télécharger sur le *Hub* est de passer un `PushToHubCallback` lorsque vous appelez `model.fit()` :
-
-```py
-from transformers import PushToHubCallback
-
-callback = PushToHubCallback(
- "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer
-)
-```
-
-Ensuite, vous devez ajouter `callbacks=[callback]` dans votre appel à `model.fit()`. Le *callback* téléchargera alors votre modèle vers le *Hub* à chaque fois qu'il sera sauvegardé (ici à chaque époque) dans un dépôt dans votre espace de noms. Ce dépôt sera nommé comme le répertoire de sortie que vous avez choisi (ici `bert-finetuned-mrpc`) mais vous pouvez choisir un nom différent avec `hub_model_id = "a_different_name"`.
-
-Pour télécharger votre modèle dans une organisation dont vous êtes membre, passez-le simplement avec `hub_model_id = "my_organization/my_repo_name"`.
-
-{/if}
-
-A un niveau inférieur, l'accès au *Hub* peut être fait directement sur les modèles, les *tokenizers* et les objets de configuration via leur méthode `push_to_hub()`. Cette méthode s'occupe à la fois de la création du dépôt et de l'envoi les fichiers du modèle et du *tokenizer* directement dans le dépôt. Aucune manipulation manuelle n'est nécessaire, contrairement à l'API que nous verrons plus loin.
-
-Pour avoir une idée de son fonctionnement, commençons par initialiser un modèle et un *tokenizer* :
-
-{#if fw === 'pt'}
-```py
-from transformers import AutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = AutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-```
-{:else}
-```py
-from transformers import TFAutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-```
-{/if}
-
-Vous êtes libre de faire ce que vous voulez avec ces objets : ajouter des *tokens* au *tokenizer*, entraîner le modèle, le *finetuner*. Une fois que vous êtes satisfait du modèle, des poids et du *tokenizer* obtenus, vous pouvez utiliser la méthode `push_to_hub()` directement disponible sur l'objet `model` :
-
-```py
-model.push_to_hub("dummy-model")
-```
-
-Cela va créer le nouveau dépôt `dummy-model` dans votre profil et le remplir avec les fichiers du modèle.
-Faites la même chose avec le *tokenizer*, de sorte que tous les fichiers sont maintenant disponibles dans ce dépôt :
-
-```py
-tokenizer.push_to_hub("dummy-model")
-```
-
-Si vous appartenez à une organisation, il suffit de spécifier l'argument `organization` pour télécharger dans l'espace de cette organisation :
-
-```py
-tokenizer.push_to_hub("dummy-model", organization="huggingface")
-```
-
-Si vous souhaitez utiliser un jeton Hugging Face spécifique, vous pouvez également le spécifier à la méthode `push_to_hub()` :
-
-```py
-tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="")
-```
-
-Maintenant, dirigez-vous sur *Hub* pour trouver votre modèle nouvellement téléchargé : *https://huggingface.co/user-or-organization/dummy-model*.
-
-Cliquez sur l'onglet « Fichiers et versions » et vous devriez voir les fichiers visibles dans la capture d'écran suivante :
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-
-
-✏️ **Essayez** Prenez le modèle et le *tokenizer* associés au *checkpoint* `bert-base-cased` et téléchargez-les vers un dépôt dans votre espace en utilisant la méthode `push_to_hub()`. Vérifiez que le dépôt apparaît correctement sur votre page avant de le supprimer.
-
-
-
-Comme vous l'avez vu, la méthode `push_to_hub()` accepte plusieurs arguments, ce qui permet de télécharger vers un dépôt ou un espace d'organisation spécifique, ou d'utiliser un jeton d'API différent. Nous vous recommandons de jeter un coup d'œil à la spécification de la méthode disponible directement dans la documentation de [🤗 *Transformers*](https://huggingface.co/transformers/model_sharing.html) pour avoir une idée de ce qui est possible.
-
-La méthode `push_to_hub()` est soutenue par le *package* Python [`huggingface_hub`](https://github.com/huggingface/huggingface_hub), qui offre une API directe au *Hub*. C'est intégré à 🤗 *Transformers* et à plusieurs autres bibliothèques d'apprentissage automatique, comme [`allenlp`](https://github.com/allenai/allennlp). Bien que nous nous concentrions sur l'intégration via 🤗 *Transformers* dans ce chapitre, son intégration dans votre propre code ou bibliothèque est simple.
-
-Passez à la dernière section pour voir comment télécharger des fichiers dans votre dépôt nouvellement créé !
-
-## Utilisation de la bibliothèque Python `huggingface_hub`
-
-La bibliothèque Python `huggingface_hub` est un *package* qui offre un ensemble d'outils pour les hubs des modèles et des jeux de données. Elle fournit des méthodes et des classes simples pour des tâches courantes telles qu'obtenir et gérer des informations à propos des dépôts sur le *Hub*. Elle fournit des APIs simples qui fonctionnent au-dessus de git pour gérer le contenu de ces dépôts et pour intégrer le *Hub* dans vos projets et bibliothèques.
-
-De la même manière que pour l'utilisation de l'API `push_to_hub`, vous devrez avoir votre jeton d'API enregistré dans votre cache. Pour ce faire, vous devrez utiliser la commande `login` de la CLI, comme mentionné dans la section précédente (encore une fois, assurez-vous de faire précéder ces commandes du caractère `!` si vous les exécutez dans Google Colab) :
-
-```bash
-huggingface-cli login
-```
-
-Le *package* `huggingface_hub` offre plusieurs méthodes et classes qui sont utiles pour notre objectif. Tout d'abord, il y a quelques méthodes pour gérer la création, la suppression des dépôts, et autres :
-
-```python no-format
-from huggingface_hub import (
- # Gestion des utilisateurs
- login,
- logout,
- whoami,
-
- # Création et gestion du dépôt
- create_repo,
- delete_repo,
- update_repo_visibility,
-
- # Et quelques méthodes pour récupérer/changer des informations sur le contenu
- list_models,
- list_datasets,
- list_metrics,
- list_repo_files,
- upload_file,
- delete_file,
-)
-```
-
-
-De plus, elle offre la très puissante classe `Repository` pour gérer un dépôt local. Nous allons explorer ces méthodes et cette classe dans les prochaines sections pour comprendre comment les exploiter.
-
-La méthode `create_repo` peut être utilisée pour créer un nouveau dépôt sur le *Hub* :
-
-```py
-from huggingface_hub import create_repo
-
-create_repo("dummy-model")
-```
-
-Ceci créera le dépôt `dummy-model` dans votre espace. Si vous le souhaitez, vous pouvez spécifier à quelle organisation le dépôt doit appartenir en utilisant l'argument `organization` :
-
-```py
-from huggingface_hub import create_repo
-
-create_repo("dummy-model", organization="huggingface")
-```
-
-Cela créera le dépôt `dummy-model` dans l'espace de nom `huggingface`, en supposant que vous appartenez à cette organisation.
-D'autres arguments qui peuvent être utiles sont :
-
-- `private`, afin de spécifier si le dépôt doit être visible des autres ou non,
-- `token`, si vous voulez remplacer le jeton stocké dans votre cache par un jeton donné,
-- `repo_type`, si vous souhaitez créer un `dataset` ou un `space` au lieu d'un modèle. Les valeurs acceptées sont `"dataset"` et `"space"`.
-
-Une fois que le dépôt est créé, nous devons y ajouter des fichiers ! Passez à la section suivante pour voir les trois façons dont cela peut être géré.
-
-
-## Utilisation de l'interface web
-
-L'interface web offre des outils pour gérer les dépôts directement dans le *Hub*. En utilisant l'interface, vous pouvez facilement créer des dépôts, ajouter des fichiers (même de grande taille !), explorer des modèles, visualiser les différences, et bien plus encore.
-
-Pour créer un nouveau dépôt, visitez [huggingface.co/new](https://huggingface.co/new) :
-
-
-
-
-
-Tout d'abord, indiquez le propriétaire du dépôt : il peut s'agir de vous ou de l'une des organisations auxquelles vous êtes affilié. Si vous choisissez une organisation, le modèle sera présenté sur la page de l'organisation et chaque membre de l'organisation aura la possibilité de contribuer au dépôt.
-
-Ensuite, saisissez le nom de votre modèle. Ce sera également le nom du dépôt. Enfin, vous pouvez préciser si vous souhaitez que votre modèle soit public ou privé. Les modèles privés sont cachés de la vue du public.
-
-Après avoir créé votre dépôt de modèles, vous devriez voir une page comme celle-ci :
-
-
-
-
-
-C'est là que votre modèle sera hébergé. Pour commencer à le remplir, vous pouvez ajouter un fichier README directement depuis l'interface web.
-
-
-
-
-
-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 « *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.
-
-
-
-
-
-Nous allons maintenant voir comment ajouter de nouveaux fichiers.
-
-## Téléchargement des fichiers du modèle
-
-Le système de gestion des fichiers sur le *Hub* est basé sur git pour les fichiers ordinaires et git-lfs (qui signifie [Git Large File Storage](https://git-lfs.github.com/)) pour les fichiers plus importants.
-
-Dans la section suivante, nous passons en revue trois façons différentes de télécharger des fichiers sur le *Hub* : par `huggingface_hub` et par des commandes git.
-
-### L'approche `upload_file'
-
-L'utilisation de `upload_file` ne nécessite pas que git et git-lfs soient installés sur votre système. Il pousse les fichiers directement vers le 🤗 *Hub* en utilisant des requêtes HTTP POST. Une limitation de cette approche est qu'elle ne gère pas les fichiers dont la taille est supérieure à 5 Go.
-Si vos fichiers ont une taille supérieure à 5 Go, veuillez suivre les deux autres méthodes détaillées ci-dessous.
-
-L'API peut être utilisée comme suit :
-
-```py
-from huggingface_hub import upload_file
-
-upload_file(
- "/config.json",
- path_in_repo="config.json",
- repo_id="/dummy-model",
-)
-```
-
-Ceci téléchargera le fichier `config.json` disponible à `` à la racine du dépôt en tant que `config.json`, vers le dépôt `dummy-model`.
-D'autres arguments qui peuvent être utiles sont :
-
-- `token`, si vous souhaitez remplacer le jeton stocké dans votre cache par un jeton donné,
-- `repo_type`, si vous souhaitez télécharger vers un `dataset` ou un `space` au lieu d'un modèle. Les valeurs acceptées sont `"dataset"` et `"space"`.
-
-
-### La classe `Repository`
-
-La classe `Repository` gère un dépôt local d'une manière similaire à git. Elle abstrait la plupart des problèmes que l'on peut rencontrer avec git pour fournir toutes les fonctionnalités dont nous avons besoin.
-
-L'utilisation de cette classe nécessite l'installation de git et de git-lfs, donc assurez-vous que git-lfs est installé (voir [ici](https://git-lfs.github.com/) pour les instructions d'installation) et configuré avant de commencer.
-
-Afin de commencer à jouer avec le dépôt que nous venons de créer, nous pouvons commencer par l'initialiser dans un dossier local en clonant le dépôt distant :
-
-```py
-from huggingface_hub import Repository
-
-repo = Repository("", clone_from="/dummy-model")
-```
-
-Cela a créé le dossier `` dans notre répertoire de travail. Ce dossier ne contient que le fichier `.gitattributes` car c'est le seul fichier créé lors de l'instanciation du dépôt par `create_repo`.
-
-A partir de maintenant, nous pouvons utiliser plusieurs des méthodes traditionnelles de git :
-
-```py
-repo.git_pull()
-repo.git_add()
-repo.git_commit()
-repo.git_push()
-repo.git_tag()
-```
-
-Et d'autres encore ! Nous vous recommandons de jeter un coup d’œil à la documentation de `Repository` disponible [ici](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management) pour une vue d'ensemble de toutes les méthodes disponibles.
-
-Actuellement, nous avons un modèle et un *tokenizer* que nous voulons pousser vers le *Hub*. Nous avons réussi à cloner le dépôt, nous pouvons donc enregistrer les fichiers dans ce dépôt.
-
-Nous nous assurons d'abord que notre clone local est à jour en récupérant les dernières modifications :
-
-```py
-repo.git_pull()
-```
-
-Une fois que c'est fait, nous sauvegardons les fichiers du modèle et du *tokenizer* :
-
-```py
-model.save_pretrained("")
-tokenizer.save_pretrained("")
-```
-
-Le `` contient maintenant tous les fichiers du modèle et du *tokenizer*. Nous suivons le flux de travail git habituel en ajoutant des fichiers à la zone de transit, en les validant et en les poussant vers le *Hub* :
-
-```py
-repo.git_add()
-repo.git_commit("Add model and tokenizer files")
-repo.git_push()
-```
-
-Félicitations ! Vous venez de pousser vos premiers fichiers sur le *Hub*.
-
-### L'approche basée sur git
-
-Il s'agit de l'approche la plus basique pour télécharger des fichiers : nous le ferons directement avec git et git-lfs. La plupart des difficultés sont abstraites par les approches précédentes, mais il y a quelques réserves avec la méthode suivante, nous allons donc suivre un cas d'utilisation plus complexe.
-
-L'utilisation de cette classe nécessite l'installation de git et de git-lfs, donc assurez-vous d'avoir [git-lfs](https://git-lfs.github.com/) installé et configuré avant de commencer.
-
-Commencez par initialiser git-lfs :
-
-```bash
-git lfs install
-```
-
-```bash
-Updated git hooks.
-Git LFS initialized.
-```
-
-Une fois que c'est fait, la première étape consiste à cloner votre dépôt de modèles :
-
-```bash
-git clone https://huggingface.co//
-```
-
-Mon nom d'utilisateur est `lysandre` et j'ai utilisé le nom de modèle `dummy`, donc pour moi la commande ressemble à ce qui suit :
-
-```
-git clone https://huggingface.co/lysandre/dummy
-```
-
-J'ai maintenant un dossier nommé *dummy* dans mon répertoire de travail. Je peux `cd` dans ce dossier et jeter un coup d'oeil à son contenu :
-
-```bash
-cd dummy && ls
-```
-
-```bash
-README.md
-```
-
-Si vous venez de créer votre dépôt en utilisant la méthode `create_repo` du *Hub*, ce dossier devrait seulement contenir un fichier caché `.gitattributes`. Si vous avez suivi les instructions de la section précédente pour créer un dépôt en utilisant l'interface web, le dossier devrait contenir un seul fichier *README.md* à côté du fichier caché `.gitattributes`, comme indiqué ici.
-
-L'ajout d'un fichier de taille normale, comme un fichier de configuration, un fichier de vocabulaire, ou tout autre fichier de moins de quelques mégaoctets, est fait exactement comme on le ferait dans n'importe quel système basé sur git. Cependant, les fichiers plus volumineux doivent être enregistrés via git-lfs afin de les pousser vers *huggingface.co*.
-
-Revenons un peu à Python pour générer un modèle et un *tokenizer* que nous souhaitons « commiter » dans notre dépôt fictif :
-
-{#if fw === 'pt'}
-```py
-from transformers import AutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = AutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-# Faites ce que vous voulez avec le modèle, entraînez-le, finetunez-le...
-
-model.save_pretrained("")
-tokenizer.save_pretrained("")
-```
-{:else}
-```py
-from transformers import TFAutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-# Faites ce que vous voulez avec le modèle, entraînez-le, finetunez-le...
-
-model.save_pretrained("")
-tokenizer.save_pretrained("")
-```
-{/if}
-
-Maintenant que nous avons sauvegardé quelques artefacts de modèle et de *tokenizer*, regardons à nouveau le dossier *dummy* :
-
-```bash
-ls
-```
-
-{#if fw === 'pt'}
-```bash
-config.json pytorch_model.bin README.md sentencepiece.bpe.model special_tokens_map.json tokenizer_config.json tokenizer.json
-```
-
-Si vous regardez la taille des fichiers (par exemple, avec `ls -lh`), vous devriez voir que le fichier d'état du modèle (*pytorch_model.bin*) est la seule exception, avec plus de 400 Mo.
-
-{:else}
-```bash
-config.json README.md sentencepiece.bpe.model special_tokens_map.json tf_model.h5 tokenizer_config.json tokenizer.json
-```
-
-Si vous regardez la taille des fichiers (par exemple, avec `ls -lh`), vous devriez voir que le fichier dict de l'état du modèle (*t5_model.h5*) est la seule aberration, avec plus de 400 Mo.
-
-{/if}
-
-
-✏️ Lors de la création du dépôt à partir de l'interface web, le fichier .gitattributes est automatiquement configuré pour considérer les fichiers avec certaines extensions, comme .bin et .h5, comme des fichiers volumineux, et git-lfs les suivra sans aucune configuration nécessaire de votre part.
-
-
-Nous pouvons maintenant aller de l'avant et procéder comme nous le ferions habituellement avec des dépôts Git traditionnels. Nous pouvons ajouter tous les fichiers à l'environnement Git en utilisant la commande `git add` :
-
-```bash
-git add .
-```
-
-Nous pouvons alors jeter un coup d'œil aux fichiers :
-
-```bash
-git status
-```
-
-{#if fw === 'pt'}
-```bash
-On branch main
-Your branch is up to date with 'origin/main'.
-
-Changes to be committed:
- (use "git restore --staged ..." to unstage)
- modified: .gitattributes
- new file: config.json
- new file: pytorch_model.bin
- new file: sentencepiece.bpe.model
- new file: special_tokens_map.json
- new file: tokenizer.json
- new file: tokenizer_config.json
-```
-{:else}
-```bash
-On branch main
-Your branch is up to date with 'origin/main'.
-
-Changes to be committed:
- (use "git restore --staged ..." to unstage)
- modified: .gitattributes
- new file: config.json
- new file: sentencepiece.bpe.model
- new file: special_tokens_map.json
- new file: tf_model.h5
- new file: tokenizer.json
- new file: tokenizer_config.json
-```
-{/if}
-
-De même, nous pouvons nous assurer que git-lfs suit les bons fichiers en utilisant sa commande `status` :
-
-```bash
-git lfs status
-```
-
-{#if fw === 'pt'}
-```bash
-On branch main
-Objects to be pushed to origin/main:
-
-
-Objects to be committed:
-
- config.json (Git: bc20ff2)
- pytorch_model.bin (LFS: 35686c2)
- sentencepiece.bpe.model (LFS: 988bc5a)
- special_tokens_map.json (Git: cb23931)
- tokenizer.json (Git: 851ff3e)
- tokenizer_config.json (Git: f0f7783)
-
-Objects not staged for commit:
-
-
-```
-
-Nous pouvons voir que tous les fichiers ont `Git` comme gestionnaire, sauf *pytorch_model.bin* et *sentencepiece.bpe.model*, qui ont `LFS`. Super !
-
-{:else}
-```bash
-On branch main
-Objects to be pushed to origin/main:
-
-
-Objects to be committed:
-
- config.json (Git: bc20ff2)
- sentencepiece.bpe.model (LFS: 988bc5a)
- special_tokens_map.json (Git: cb23931)
- tf_model.h5 (LFS: 86fce29)
- tokenizer.json (Git: 851ff3e)
- tokenizer_config.json (Git: f0f7783)
-
-Objects not staged for commit:
-
-
-```
-
-Nous pouvons voir que tous les fichiers ont `Git` comme gestionnaire, sauf *t5_model.h5* qui a `LFS`. Super !
-
-{/if}
-
-Passons aux étapes finales, *committing* et *pushing* vers le dépôt distant *huggingface.co* :
-
-```bash
-git commit -m "First model version"
-```
-
-{#if fw === 'pt'}
-```bash
-[main b08aab1] First model version
- 7 files changed, 29027 insertions(+)
- 6 files changed, 36 insertions(+)
- create mode 100644 config.json
- create mode 100644 pytorch_model.bin
- create mode 100644 sentencepiece.bpe.model
- create mode 100644 special_tokens_map.json
- create mode 100644 tokenizer.json
- create mode 100644 tokenizer_config.json
-```
-{:else}
-```bash
-[main b08aab1] First model version
- 6 files changed, 36 insertions(+)
- create mode 100644 config.json
- create mode 100644 sentencepiece.bpe.model
- create mode 100644 special_tokens_map.json
- create mode 100644 tf_model.h5
- create mode 100644 tokenizer.json
- create mode 100644 tokenizer_config.json
-```
-{/if}
-
-Le chargement peut prendre un peu de temps, en fonction de la vitesse de votre connexion Internet et de la taille de vos fichiers :
-
-```bash
-git push
-```
-
-```bash
-Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done.
-Enumerating objects: 11, done.
-Counting objects: 100% (11/11), done.
-Delta compression using up to 12 threads
-Compressing objects: 100% (9/9), done.
-Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done.
-Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
-To https://huggingface.co/lysandre/dummy
- 891b41d..b08aab1 main -> main
-```
-
-{#if fw === 'pt'}
-Si nous jetons un coup d'œil au dépôt du modèle, lorsque cette opération est terminée, nous pouvons voir tous les fichiers récemment ajoutés :
-
-
-
-
-
-L'interface utilisateur vous permet d'explorer les fichiers du modèle et les *commits* et de voir la différence introduite par chaque *commit* :
-
-
-
-
-{:else}
-Si nous jetons un coup d'œil au dépôt du modèle, lorsque cette opération est terminée, nous pouvons voir tous les fichiers récemment ajoutés :
-
-
-
-
-
-L'interface utilisateur vous permet d'explorer les fichiers du modèle et les *commits* et de voir la différence introduite par chaque *commit* :
-
-
-
-
-{/if}
+
+
+# Partage de modèles pré-entraînés
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Dans les étapes ci-dessous, nous allons examiner les moyens les plus simples de partager des modèles pré-entraînés sur le 🤗 *Hub*. Il existe des outils et des services disponibles qui permettent de simplifier le partage et la mise à jour des modèles directement sur le *Hub*, que nous allons explorer ci-dessous.
+
+
+
+Nous encourageons tous les utilisateurs qui entraînent des modèles à contribuer en les partageant avec la communauté. Le partage des modèles, même s'ils ont été entraînés sur des jeux de données très spécifiques, aidera les autres, en leur faisant gagner du temps, des ressources de calcul et en leur donnant accès à des artefacts entraînés utiles. À votre tour, vous pourrez bénéficier du travail effectué par les autres !
+
+Il y a trois façons de créer de nouveaux dépôts de modèles :
+
+- en utilisant l'API `push_to_hub`,
+- en utilisant la bibliothèque Python `huggingface_hub`,
+- en utilisant l'interface web.
+
+Une fois que vous avez créé un dépôt, vous pouvez y charger des fichiers via git et git-lfs. Nous allons vous guider dans la création de dépôts de modèles et le téléchargement de fichiers dans les sections suivantes.
+
+
+## Utilisation de l'API `push_to_hub`
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+La façon la plus simple de télécharger des fichiers vers le *Hub* est d'utiliser l'API `push_to_hub`.
+
+Avant d'aller plus loin, vous devrez générer un jeton d'authentification afin que l'API `huggingface_hub` sache qui vous êtes et à quels espaces de noms vous avez accès en écriture. Assurez-vous que vous êtes dans un environnement où vous avez installé `transformers` (voir la [Configuration](/course/fr/chapter0)). Si vous êtes dans un *notebook*, vous pouvez utiliser la fonction suivante pour vous connecter :
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Dans un terminal, vous pouvez exécuter :
+
+```bash
+huggingface-cli login
+```
+
+Dans les deux cas, vous serez invité à saisir votre nom d'utilisateur et votre mot de passe, qui sont les mêmes que ceux que vous utilisez pour vous connecter au *Hub*. Si vous n'avez pas encore de profil pour le Hub, vous devez en créer un [ici](https://huggingface.co/join).
+
+Super ! Votre jeton d'authentification est maintenant stocké dans votre dossier de cache. Créons quelques dépôts !
+
+{#if fw === 'pt'}
+
+Si vous avez joué avec l'API `Trainer` pour entraîner un modèle, le moyen le plus simple de le télécharger sur le *Hub* est de définir `push_to_hub=True` lorsque vous définissez vos `TrainingArguments` :
+
+```py
+from transformers import TrainingArguments
+
+training_args = TrainingArguments(
+ "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True
+)
+```
+
+Lorsque vous appelez `trainer.train()`, le `Trainer` téléchargera alors votre modèle vers le *Hub* à chaque fois qu'il sera sauvegardé (ici à chaque époque) dans un dépôt dans votre espace personnel. Ce dépôt sera nommé comme le répertoire de sortie que vous avez choisi (ici `bert-finetuned-mrpc`) mais vous pouvez choisir un nom différent avec `hub_model_id = "a_different_name"`.
+
+Pour télécharger votre modèle vers une organisation dont vous êtes membre, passez-le simplement avec `hub_model_id = "my_organization/my_repo_name"`.
+
+Une fois que votre entraînement est terminé, vous devriez faire un dernier `trainer.push_to_hub()` pour télécharger la dernière version de votre modèle. Cela générera également une carte pour le modèle avec toutes les métadonnées pertinentes, rapportant les hyperparamètres utilisés et les résultats d'évaluation ! Voici un exemple du contenu que vous pourriez trouver dans une telle carte de modèle :
+
+
+
+
+{:else}
+
+Si vous utilisez Keras pour entraîner votre modèle, le moyen le plus simple de le télécharger sur le *Hub* est de passer un `PushToHubCallback` lorsque vous appelez `model.fit()` :
+
+```py
+from transformers import PushToHubCallback
+
+callback = PushToHubCallback(
+ "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer
+)
+```
+
+Ensuite, vous devez ajouter `callbacks=[callback]` dans votre appel à `model.fit()`. Le *callback* téléchargera alors votre modèle vers le *Hub* à chaque fois qu'il sera sauvegardé (ici à chaque époque) dans un dépôt dans votre espace de noms. Ce dépôt sera nommé comme le répertoire de sortie que vous avez choisi (ici `bert-finetuned-mrpc`) mais vous pouvez choisir un nom différent avec `hub_model_id = "a_different_name"`.
+
+Pour télécharger votre modèle dans une organisation dont vous êtes membre, passez-le simplement avec `hub_model_id = "my_organization/my_repo_name"`.
+
+{/if}
+
+A un niveau inférieur, l'accès au *Hub* peut être fait directement sur les modèles, les *tokenizers* et les objets de configuration via leur méthode `push_to_hub()`. Cette méthode s'occupe à la fois de la création du dépôt et de l'envoi les fichiers du modèle et du *tokenizer* directement dans le dépôt. Aucune manipulation manuelle n'est nécessaire, contrairement à l'API que nous verrons plus loin.
+
+Pour avoir une idée de son fonctionnement, commençons par initialiser un modèle et un *tokenizer* :
+
+{#if fw === 'pt'}
+```py
+from transformers import AutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = AutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+{:else}
+```py
+from transformers import TFAutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+{/if}
+
+Vous êtes libre de faire ce que vous voulez avec ces objets : ajouter des *tokens* au *tokenizer*, entraîner le modèle, le *finetuner*. Une fois que vous êtes satisfait du modèle, des poids et du *tokenizer* obtenus, vous pouvez utiliser la méthode `push_to_hub()` directement disponible sur l'objet `model` :
+
+```py
+model.push_to_hub("dummy-model")
+```
+
+Cela va créer le nouveau dépôt `dummy-model` dans votre profil et le remplir avec les fichiers du modèle.
+Faites la même chose avec le *tokenizer*, de sorte que tous les fichiers sont maintenant disponibles dans ce dépôt :
+
+```py
+tokenizer.push_to_hub("dummy-model")
+```
+
+Si vous appartenez à une organisation, il suffit de spécifier l'argument `organization` pour télécharger dans l'espace de cette organisation :
+
+```py
+tokenizer.push_to_hub("dummy-model", organization="huggingface")
+```
+
+Si vous souhaitez utiliser un jeton Hugging Face spécifique, vous pouvez également le spécifier à la méthode `push_to_hub()` :
+
+```py
+tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="")
+```
+
+Maintenant, dirigez-vous sur *Hub* pour trouver votre modèle nouvellement téléchargé : *https://huggingface.co/user-or-organization/dummy-model*.
+
+Cliquez sur l'onglet « Fichiers et versions » et vous devriez voir les fichiers visibles dans la capture d'écran suivante :
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+
+
+✏️ **Essayez** Prenez le modèle et le *tokenizer* associés au *checkpoint* `bert-base-cased` et téléchargez-les vers un dépôt dans votre espace en utilisant la méthode `push_to_hub()`. Vérifiez que le dépôt apparaît correctement sur votre page avant de le supprimer.
+
+
+
+Comme vous l'avez vu, la méthode `push_to_hub()` accepte plusieurs arguments, ce qui permet de télécharger vers un dépôt ou un espace d'organisation spécifique, ou d'utiliser un jeton d'API différent. Nous vous recommandons de jeter un coup d'œil à la spécification de la méthode disponible directement dans la documentation de [🤗 *Transformers*](https://huggingface.co/transformers/model_sharing.html) pour avoir une idée de ce qui est possible.
+
+La méthode `push_to_hub()` est soutenue par le *package* Python [`huggingface_hub`](https://github.com/huggingface/huggingface_hub), qui offre une API directe au *Hub*. C'est intégré à 🤗 *Transformers* et à plusieurs autres bibliothèques d'apprentissage automatique, comme [`allenlp`](https://github.com/allenai/allennlp). Bien que nous nous concentrions sur l'intégration via 🤗 *Transformers* dans ce chapitre, son intégration dans votre propre code ou bibliothèque est simple.
+
+Passez à la dernière section pour voir comment télécharger des fichiers dans votre dépôt nouvellement créé !
+
+## Utilisation de la bibliothèque Python `huggingface_hub`
+
+La bibliothèque Python `huggingface_hub` est un *package* qui offre un ensemble d'outils pour les hubs des modèles et des jeux de données. Elle fournit des méthodes et des classes simples pour des tâches courantes telles qu'obtenir et gérer des informations à propos des dépôts sur le *Hub*. Elle fournit des APIs simples qui fonctionnent au-dessus de git pour gérer le contenu de ces dépôts et pour intégrer le *Hub* dans vos projets et bibliothèques.
+
+De la même manière que pour l'utilisation de l'API `push_to_hub`, vous devrez avoir votre jeton d'API enregistré dans votre cache. Pour ce faire, vous devrez utiliser la commande `login` de la CLI, comme mentionné dans la section précédente (encore une fois, assurez-vous de faire précéder ces commandes du caractère `!` si vous les exécutez dans Google Colab) :
+
+```bash
+huggingface-cli login
+```
+
+Le *package* `huggingface_hub` offre plusieurs méthodes et classes qui sont utiles pour notre objectif. Tout d'abord, il y a quelques méthodes pour gérer la création, la suppression des dépôts, et autres :
+
+```python no-format
+from huggingface_hub import (
+ # Gestion des utilisateurs
+ login,
+ logout,
+ whoami,
+
+ # Création et gestion du dépôt
+ create_repo,
+ delete_repo,
+ update_repo_visibility,
+
+ # Et quelques méthodes pour récupérer/changer des informations sur le contenu
+ list_models,
+ list_datasets,
+ list_metrics,
+ list_repo_files,
+ upload_file,
+ delete_file,
+)
+```
+
+
+De plus, elle offre la très puissante classe `Repository` pour gérer un dépôt local. Nous allons explorer ces méthodes et cette classe dans les prochaines sections pour comprendre comment les exploiter.
+
+La méthode `create_repo` peut être utilisée pour créer un nouveau dépôt sur le *Hub* :
+
+```py
+from huggingface_hub import create_repo
+
+create_repo("dummy-model")
+```
+
+Ceci créera le dépôt `dummy-model` dans votre espace. Si vous le souhaitez, vous pouvez spécifier à quelle organisation le dépôt doit appartenir en utilisant l'argument `organization` :
+
+```py
+from huggingface_hub import create_repo
+
+create_repo("dummy-model", organization="huggingface")
+```
+
+Cela créera le dépôt `dummy-model` dans l'espace de nom `huggingface`, en supposant que vous appartenez à cette organisation.
+D'autres arguments qui peuvent être utiles sont :
+
+- `private`, afin de spécifier si le dépôt doit être visible des autres ou non,
+- `token`, si vous voulez remplacer le jeton stocké dans votre cache par un jeton donné,
+- `repo_type`, si vous souhaitez créer un `dataset` ou un `space` au lieu d'un modèle. Les valeurs acceptées sont `"dataset"` et `"space"`.
+
+Une fois que le dépôt est créé, nous devons y ajouter des fichiers ! Passez à la section suivante pour voir les trois façons dont cela peut être géré.
+
+
+## Utilisation de l'interface web
+
+L'interface web offre des outils pour gérer les dépôts directement dans le *Hub*. En utilisant l'interface, vous pouvez facilement créer des dépôts, ajouter des fichiers (même de grande taille !), explorer des modèles, visualiser les différences, et bien plus encore.
+
+Pour créer un nouveau dépôt, visitez [huggingface.co/new](https://huggingface.co/new) :
+
+
+
+
+
+Tout d'abord, indiquez le propriétaire du dépôt : il peut s'agir de vous ou de l'une des organisations auxquelles vous êtes affilié. Si vous choisissez une organisation, le modèle sera présenté sur la page de l'organisation et chaque membre de l'organisation aura la possibilité de contribuer au dépôt.
+
+Ensuite, saisissez le nom de votre modèle. Ce sera également le nom du dépôt. Enfin, vous pouvez préciser si vous souhaitez que votre modèle soit public ou privé. Les modèles privés sont cachés de la vue du public.
+
+Après avoir créé votre dépôt de modèles, vous devriez voir une page comme celle-ci :
+
+
+
+
+
+C'est là que votre modèle sera hébergé. Pour commencer à le remplir, vous pouvez ajouter un fichier README directement depuis l'interface web.
+
+
+
+
+
+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 « *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.
+
+
+
+
+
+Nous allons maintenant voir comment ajouter de nouveaux fichiers.
+
+## Téléchargement des fichiers du modèle
+
+Le système de gestion des fichiers sur le *Hub* est basé sur git pour les fichiers ordinaires et git-lfs (qui signifie [Git Large File Storage](https://git-lfs.github.com/)) pour les fichiers plus importants.
+
+Dans la section suivante, nous passons en revue trois façons différentes de télécharger des fichiers sur le *Hub* : par `huggingface_hub` et par des commandes git.
+
+### L'approche `upload_file'
+
+L'utilisation de `upload_file` ne nécessite pas que git et git-lfs soient installés sur votre système. Il pousse les fichiers directement vers le 🤗 *Hub* en utilisant des requêtes HTTP POST. Une limitation de cette approche est qu'elle ne gère pas les fichiers dont la taille est supérieure à 5 Go.
+Si vos fichiers ont une taille supérieure à 5 Go, veuillez suivre les deux autres méthodes détaillées ci-dessous.
+
+L'API peut être utilisée comme suit :
+
+```py
+from huggingface_hub import upload_file
+
+upload_file(
+ "/config.json",
+ path_in_repo="config.json",
+ repo_id="/dummy-model",
+)
+```
+
+Ceci téléchargera le fichier `config.json` disponible à `` à la racine du dépôt en tant que `config.json`, vers le dépôt `dummy-model`.
+D'autres arguments qui peuvent être utiles sont :
+
+- `token`, si vous souhaitez remplacer le jeton stocké dans votre cache par un jeton donné,
+- `repo_type`, si vous souhaitez télécharger vers un `dataset` ou un `space` au lieu d'un modèle. Les valeurs acceptées sont `"dataset"` et `"space"`.
+
+
+### La classe `Repository`
+
+La classe `Repository` gère un dépôt local d'une manière similaire à git. Elle abstrait la plupart des problèmes que l'on peut rencontrer avec git pour fournir toutes les fonctionnalités dont nous avons besoin.
+
+L'utilisation de cette classe nécessite l'installation de git et de git-lfs, donc assurez-vous que git-lfs est installé (voir [ici](https://git-lfs.github.com/) pour les instructions d'installation) et configuré avant de commencer.
+
+Afin de commencer à jouer avec le dépôt que nous venons de créer, nous pouvons commencer par l'initialiser dans un dossier local en clonant le dépôt distant :
+
+```py
+from huggingface_hub import Repository
+
+repo = Repository("", clone_from="/dummy-model")
+```
+
+Cela a créé le dossier `` dans notre répertoire de travail. Ce dossier ne contient que le fichier `.gitattributes` car c'est le seul fichier créé lors de l'instanciation du dépôt par `create_repo`.
+
+A partir de maintenant, nous pouvons utiliser plusieurs des méthodes traditionnelles de git :
+
+```py
+repo.git_pull()
+repo.git_add()
+repo.git_commit()
+repo.git_push()
+repo.git_tag()
+```
+
+Et d'autres encore ! Nous vous recommandons de jeter un coup d’œil à la documentation de `Repository` disponible [ici](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management) pour une vue d'ensemble de toutes les méthodes disponibles.
+
+Actuellement, nous avons un modèle et un *tokenizer* que nous voulons pousser vers le *Hub*. Nous avons réussi à cloner le dépôt, nous pouvons donc enregistrer les fichiers dans ce dépôt.
+
+Nous nous assurons d'abord que notre clone local est à jour en récupérant les dernières modifications :
+
+```py
+repo.git_pull()
+```
+
+Une fois que c'est fait, nous sauvegardons les fichiers du modèle et du *tokenizer* :
+
+```py
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+
+Le `` contient maintenant tous les fichiers du modèle et du *tokenizer*. Nous suivons le flux de travail git habituel en ajoutant des fichiers à la zone de transit, en les validant et en les poussant vers le *Hub* :
+
+```py
+repo.git_add()
+repo.git_commit("Add model and tokenizer files")
+repo.git_push()
+```
+
+Félicitations ! Vous venez de pousser vos premiers fichiers sur le *Hub*.
+
+### L'approche basée sur git
+
+Il s'agit de l'approche la plus basique pour télécharger des fichiers : nous le ferons directement avec git et git-lfs. La plupart des difficultés sont abstraites par les approches précédentes, mais il y a quelques réserves avec la méthode suivante, nous allons donc suivre un cas d'utilisation plus complexe.
+
+L'utilisation de cette classe nécessite l'installation de git et de git-lfs, donc assurez-vous d'avoir [git-lfs](https://git-lfs.github.com/) installé et configuré avant de commencer.
+
+Commencez par initialiser git-lfs :
+
+```bash
+git lfs install
+```
+
+```bash
+Updated git hooks.
+Git LFS initialized.
+```
+
+Une fois que c'est fait, la première étape consiste à cloner votre dépôt de modèles :
+
+```bash
+git clone https://huggingface.co//
+```
+
+Mon nom d'utilisateur est `lysandre` et j'ai utilisé le nom de modèle `dummy`, donc pour moi la commande ressemble à ce qui suit :
+
+```
+git clone https://huggingface.co/lysandre/dummy
+```
+
+J'ai maintenant un dossier nommé *dummy* dans mon répertoire de travail. Je peux `cd` dans ce dossier et jeter un coup d'oeil à son contenu :
+
+```bash
+cd dummy && ls
+```
+
+```bash
+README.md
+```
+
+Si vous venez de créer votre dépôt en utilisant la méthode `create_repo` du *Hub*, ce dossier devrait seulement contenir un fichier caché `.gitattributes`. Si vous avez suivi les instructions de la section précédente pour créer un dépôt en utilisant l'interface web, le dossier devrait contenir un seul fichier *README.md* à côté du fichier caché `.gitattributes`, comme indiqué ici.
+
+L'ajout d'un fichier de taille normale, comme un fichier de configuration, un fichier de vocabulaire, ou tout autre fichier de moins de quelques mégaoctets, est fait exactement comme on le ferait dans n'importe quel système basé sur git. Cependant, les fichiers plus volumineux doivent être enregistrés via git-lfs afin de les pousser vers *huggingface.co*.
+
+Revenons un peu à Python pour générer un modèle et un *tokenizer* que nous souhaitons « commiter » dans notre dépôt fictif :
+
+{#if fw === 'pt'}
+```py
+from transformers import AutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = AutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+# Faites ce que vous voulez avec le modèle, entraînez-le, finetunez-le...
+
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+{:else}
+```py
+from transformers import TFAutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+# Faites ce que vous voulez avec le modèle, entraînez-le, finetunez-le...
+
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+{/if}
+
+Maintenant que nous avons sauvegardé quelques artefacts de modèle et de *tokenizer*, regardons à nouveau le dossier *dummy* :
+
+```bash
+ls
+```
+
+{#if fw === 'pt'}
+```bash
+config.json pytorch_model.bin README.md sentencepiece.bpe.model special_tokens_map.json tokenizer_config.json tokenizer.json
+```
+
+Si vous regardez la taille des fichiers (par exemple, avec `ls -lh`), vous devriez voir que le fichier d'état du modèle (*pytorch_model.bin*) est la seule exception, avec plus de 400 Mo.
+
+{:else}
+```bash
+config.json README.md sentencepiece.bpe.model special_tokens_map.json tf_model.h5 tokenizer_config.json tokenizer.json
+```
+
+Si vous regardez la taille des fichiers (par exemple, avec `ls -lh`), vous devriez voir que le fichier dict de l'état du modèle (*t5_model.h5*) est la seule aberration, avec plus de 400 Mo.
+
+{/if}
+
+
+✏️ Lors de la création du dépôt à partir de l'interface web, le fichier .gitattributes est automatiquement configuré pour considérer les fichiers avec certaines extensions, comme .bin et .h5, comme des fichiers volumineux, et git-lfs les suivra sans aucune configuration nécessaire de votre part.
+
+
+Nous pouvons maintenant aller de l'avant et procéder comme nous le ferions habituellement avec des dépôts Git traditionnels. Nous pouvons ajouter tous les fichiers à l'environnement Git en utilisant la commande `git add` :
+
+```bash
+git add .
+```
+
+Nous pouvons alors jeter un coup d'œil aux fichiers :
+
+```bash
+git status
+```
+
+{#if fw === 'pt'}
+```bash
+On branch main
+Your branch is up to date with 'origin/main'.
+
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: .gitattributes
+ new file: config.json
+ new file: pytorch_model.bin
+ new file: sentencepiece.bpe.model
+ new file: special_tokens_map.json
+ new file: tokenizer.json
+ new file: tokenizer_config.json
+```
+{:else}
+```bash
+On branch main
+Your branch is up to date with 'origin/main'.
+
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: .gitattributes
+ new file: config.json
+ new file: sentencepiece.bpe.model
+ new file: special_tokens_map.json
+ new file: tf_model.h5
+ new file: tokenizer.json
+ new file: tokenizer_config.json
+```
+{/if}
+
+De même, nous pouvons nous assurer que git-lfs suit les bons fichiers en utilisant sa commande `status` :
+
+```bash
+git lfs status
+```
+
+{#if fw === 'pt'}
+```bash
+On branch main
+Objects to be pushed to origin/main:
+
+
+Objects to be committed:
+
+ config.json (Git: bc20ff2)
+ pytorch_model.bin (LFS: 35686c2)
+ sentencepiece.bpe.model (LFS: 988bc5a)
+ special_tokens_map.json (Git: cb23931)
+ tokenizer.json (Git: 851ff3e)
+ tokenizer_config.json (Git: f0f7783)
+
+Objects not staged for commit:
+
+
+```
+
+Nous pouvons voir que tous les fichiers ont `Git` comme gestionnaire, sauf *pytorch_model.bin* et *sentencepiece.bpe.model*, qui ont `LFS`. Super !
+
+{:else}
+```bash
+On branch main
+Objects to be pushed to origin/main:
+
+
+Objects to be committed:
+
+ config.json (Git: bc20ff2)
+ sentencepiece.bpe.model (LFS: 988bc5a)
+ special_tokens_map.json (Git: cb23931)
+ tf_model.h5 (LFS: 86fce29)
+ tokenizer.json (Git: 851ff3e)
+ tokenizer_config.json (Git: f0f7783)
+
+Objects not staged for commit:
+
+
+```
+
+Nous pouvons voir que tous les fichiers ont `Git` comme gestionnaire, sauf *t5_model.h5* qui a `LFS`. Super !
+
+{/if}
+
+Passons aux étapes finales, *committing* et *pushing* vers le dépôt distant *huggingface.co* :
+
+```bash
+git commit -m "First model version"
+```
+
+{#if fw === 'pt'}
+```bash
+[main b08aab1] First model version
+ 7 files changed, 29027 insertions(+)
+ 6 files changed, 36 insertions(+)
+ create mode 100644 config.json
+ create mode 100644 pytorch_model.bin
+ create mode 100644 sentencepiece.bpe.model
+ create mode 100644 special_tokens_map.json
+ create mode 100644 tokenizer.json
+ create mode 100644 tokenizer_config.json
+```
+{:else}
+```bash
+[main b08aab1] First model version
+ 6 files changed, 36 insertions(+)
+ create mode 100644 config.json
+ create mode 100644 sentencepiece.bpe.model
+ create mode 100644 special_tokens_map.json
+ create mode 100644 tf_model.h5
+ create mode 100644 tokenizer.json
+ create mode 100644 tokenizer_config.json
+```
+{/if}
+
+Le chargement peut prendre un peu de temps, en fonction de la vitesse de votre connexion Internet et de la taille de vos fichiers :
+
+```bash
+git push
+```
+
+```bash
+Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done.
+Enumerating objects: 11, done.
+Counting objects: 100% (11/11), done.
+Delta compression using up to 12 threads
+Compressing objects: 100% (9/9), done.
+Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done.
+Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
+To https://huggingface.co/lysandre/dummy
+ 891b41d..b08aab1 main -> main
+```
+
+{#if fw === 'pt'}
+Si nous jetons un coup d'œil au dépôt du modèle, lorsque cette opération est terminée, nous pouvons voir tous les fichiers récemment ajoutés :
+
+
+
+
+
+L'interface utilisateur vous permet d'explorer les fichiers du modèle et les *commits* et de voir la différence introduite par chaque *commit* :
+
+
+
+
+{:else}
+Si nous jetons un coup d'œil au dépôt du modèle, lorsque cette opération est terminée, nous pouvons voir tous les fichiers récemment ajoutés :
+
+
+
+
+
+L'interface utilisateur vous permet d'explorer les fichiers du modèle et les *commits* et de voir la différence introduite par chaque *commit* :
+
+
+
+
+{/if}
diff --git a/chapters/fr/chapter4/4.mdx b/chapters/fr/chapter4/4.mdx
index 4f2086aea..3bd79c348 100644
--- a/chapters/fr/chapter4/4.mdx
+++ b/chapters/fr/chapter4/4.mdx
@@ -1,84 +1,89 @@
-# Construire une carte de modèle
-
-La carte de modèle est un fichier qui est sans doute aussi important que les fichiers du modèle et du *tokenizer* dans un dépôt de modèles. Il s'agit de la définition centrale du modèle, qui garantit la réutilisation par les autres membres de la communauté, la reproductibilité des résultats, et une plateforme sur laquelle les autres membres peuvent construire leurs artefacts.
-
-Documenter le processus d'entraînement et d'évaluation aide les autres à comprendre ce qu'ils peuvent attendre d'un modèle. Fournir suffisamment d'informations concernant les données utilisées, les prétraitements et post-traitements effectués permet d'identifier et de comprendre les limites, les biais et les contextes dans lesquels le modèle est ou n'est pas utile.
-
-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é.
-
-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 :
-
-- description du modèle
-- utilisations et limites prévues
-- comment utiliser le modèle
-- limites et biais
-- données d'entraînement
-- procédure d'entraînement
-- résultats de l'évaluation
-
-Voyons ce que chacune de ces sections doit contenir.
-
-
-### Description du modèle
-
-La description du modèle fournit des détails de base sur le modèle. Cela inclut l'architecture, la version, s'il a été présenté dans un article, si une implémentation originale est disponible, l'auteur et des informations générales sur le modèle. Tout droit d'auteur doit être attribué ici. Des informations générales sur les procédures d'entraînement, les paramètres et les avertissements importants peuvent également être mentionnés dans cette section.
-
-### Utilisations et limitations prévues
-
-Vous décrivez ici les cas d'utilisation auxquels le modèle est destiné, y compris les langues, les domaines et les champs où il peut être appliqué. Cette section de la fiche de modèle peut également documenter les domaines qui sont connus pour être hors de portée du modèle, ou dans lesquels il est susceptible de fonctionner de manière sous-optimale.
-
-### Comment utiliser
-
-Cette section doit inclure des exemples d'utilisation du modèle. Cela peut montrer l'utilisation de la fonction `pipeline()`, l'utilisation des classes du modèle et du *tokenizer*, et tout autre code que vous pensez être utile.
-
-### Données d'entraînement
-
-Cette partie doit indiquer sur quel(s) jeu(x) de données le modèle a été entraîné. Une brève description du ou des jeux de données est également la bienvenue.
-
-### Procédure d'entraînement
-
-Dans cette section, vous devez décrire tous les aspects pertinents de l'entraînement qui sont utiles du point de vue de la reproductibilité. Cela inclut tout prétraitement et post-traitement effectué sur les données, ainsi que des détails tels que le nombre d'époques pour lesquelles le modèle a été entraîné, la taille du batch, le taux d'apprentissage, etc.
-
-### Variable et métriques
-
-Décrivez ici les métriques que vous utilisez pour l'évaluation et les différents facteurs que vous mesurez. En mentionnant la ou les métriques utilisées, sur quel jeu de données et quelle division du jeu de données, il est plus facile de comparer les performances de votre modèle à celles d'autres modèles. Les sections précédentes, telles que les utilisateurs prévus et les cas d'utilisation, doivent être prises en compte.
-
-### Résultats de l'évaluation
-
-Enfin, fournissez une indication de la performance du modèle sur l'ensemble de données d'évaluation. Si le modèle utilise un seuil de décision, indiquez le seuil de décision utilisé dans l'évaluation ou fournissez des détails sur l'évaluation à différents seuils pour les utilisations prévues.
-
-
-## Exemple
-
-Voici quelques exemples de cartes de modèles bien conçues :
-
-- [`bert-base-case`](https://huggingface.co/bert-base-cased)
-- [`gpt2`](https://huggingface.co/gpt2)
-- [`distilbert`](https://huggingface.co/distilbert-base-uncased)
-
-D'autres exemples provenant de différentes organisations et entreprises sont disponibles [ici](https://github.com/huggingface/model_card/blob/master/examples.md).
-
-## Note
-
-Les fiches de modèle ne sont pas une exigence lors de la publication de modèles, et vous n'avez pas besoin d'inclure toutes les sections décrites ci-dessus lorsque vous en faites une. Cependant, une documentation explicite du modèle ne peut qu'être bénéfique aux futurs utilisateurs. Nous vous recommandons donc de remplir autant de sections que possible, au mieux de vos connaissances et de vos capacités.
-
-## Métadonnées de la carte de modèle
-
-Si vous avez exploré un peu le *Hub*, vous devriez avoir vu que certains modèles appartiennent à certaines catégories : vous pouvez les filtrer par tâches, langues, bibliothèques, et plus encore. Les catégories auxquelles appartient un modèle sont identifiées en fonction des métadonnées que vous ajoutez dans l'en-tête de la fiche du modèle.
-
-Par exemple, si vous regardez la fiche de modèle de [`camembert-base`](https://huggingface.co/camembert-base/blob/main/README.md), vous devriez voir les lignes suivantes dans l'en-tête de la fiche de modèle :
-
-```
----
-language: fr
-license: mit
-datasets:
-- oscar
----
-```
-
-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.
+# Construire une carte de modèle
+
+
+
+La carte de modèle est un fichier qui est sans doute aussi important que les fichiers du modèle et du *tokenizer* dans un dépôt de modèles. Il s'agit de la définition centrale du modèle, qui garantit la réutilisation par les autres membres de la communauté, la reproductibilité des résultats, et une plateforme sur laquelle les autres membres peuvent construire leurs artefacts.
+
+Documenter le processus d'entraînement et d'évaluation aide les autres à comprendre ce qu'ils peuvent attendre d'un modèle. Fournir suffisamment d'informations concernant les données utilisées, les prétraitements et post-traitements effectués permet d'identifier et de comprendre les limites, les biais et les contextes dans lesquels le modèle est ou n'est pas utile.
+
+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é.
+
+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 :
+
+- description du modèle
+- utilisations et limites prévues
+- comment utiliser le modèle
+- limites et biais
+- données d'entraînement
+- procédure d'entraînement
+- résultats de l'évaluation
+
+Voyons ce que chacune de ces sections doit contenir.
+
+
+### Description du modèle
+
+La description du modèle fournit des détails de base sur le modèle. Cela inclut l'architecture, la version, s'il a été présenté dans un article, si une implémentation originale est disponible, l'auteur et des informations générales sur le modèle. Tout droit d'auteur doit être attribué ici. Des informations générales sur les procédures d'entraînement, les paramètres et les avertissements importants peuvent également être mentionnés dans cette section.
+
+### Utilisations et limitations prévues
+
+Vous décrivez ici les cas d'utilisation auxquels le modèle est destiné, y compris les langues, les domaines et les champs où il peut être appliqué. Cette section de la fiche de modèle peut également documenter les domaines qui sont connus pour être hors de portée du modèle, ou dans lesquels il est susceptible de fonctionner de manière sous-optimale.
+
+### Comment utiliser
+
+Cette section doit inclure des exemples d'utilisation du modèle. Cela peut montrer l'utilisation de la fonction `pipeline()`, l'utilisation des classes du modèle et du *tokenizer*, et tout autre code que vous pensez être utile.
+
+### Données d'entraînement
+
+Cette partie doit indiquer sur quel(s) jeu(x) de données le modèle a été entraîné. Une brève description du ou des jeux de données est également la bienvenue.
+
+### Procédure d'entraînement
+
+Dans cette section, vous devez décrire tous les aspects pertinents de l'entraînement qui sont utiles du point de vue de la reproductibilité. Cela inclut tout prétraitement et post-traitement effectué sur les données, ainsi que des détails tels que le nombre d'époques pour lesquelles le modèle a été entraîné, la taille du batch, le taux d'apprentissage, etc.
+
+### Variable et métriques
+
+Décrivez ici les métriques que vous utilisez pour l'évaluation et les différents facteurs que vous mesurez. En mentionnant la ou les métriques utilisées, sur quel jeu de données et quelle division du jeu de données, il est plus facile de comparer les performances de votre modèle à celles d'autres modèles. Les sections précédentes, telles que les utilisateurs prévus et les cas d'utilisation, doivent être prises en compte.
+
+### Résultats de l'évaluation
+
+Enfin, fournissez une indication de la performance du modèle sur l'ensemble de données d'évaluation. Si le modèle utilise un seuil de décision, indiquez le seuil de décision utilisé dans l'évaluation ou fournissez des détails sur l'évaluation à différents seuils pour les utilisations prévues.
+
+
+## Exemple
+
+Voici quelques exemples de cartes de modèles bien conçues :
+
+- [`bert-base-case`](https://huggingface.co/bert-base-cased)
+- [`gpt2`](https://huggingface.co/gpt2)
+- [`distilbert`](https://huggingface.co/distilbert-base-uncased)
+
+D'autres exemples provenant de différentes organisations et entreprises sont disponibles [ici](https://github.com/huggingface/model_card/blob/master/examples.md).
+
+## Note
+
+Les fiches de modèle ne sont pas une exigence lors de la publication de modèles, et vous n'avez pas besoin d'inclure toutes les sections décrites ci-dessus lorsque vous en faites une. Cependant, une documentation explicite du modèle ne peut qu'être bénéfique aux futurs utilisateurs. Nous vous recommandons donc de remplir autant de sections que possible, au mieux de vos connaissances et de vos capacités.
+
+## Métadonnées de la carte de modèle
+
+Si vous avez exploré un peu le *Hub*, vous devriez avoir vu que certains modèles appartiennent à certaines catégories : vous pouvez les filtrer par tâches, langues, bibliothèques, et plus encore. Les catégories auxquelles appartient un modèle sont identifiées en fonction des métadonnées que vous ajoutez dans l'en-tête de la fiche du modèle.
+
+Par exemple, si vous regardez la fiche de modèle de [`camembert-base`](https://huggingface.co/camembert-base/blob/main/README.md), vous devriez voir les lignes suivantes dans l'en-tête de la fiche de modèle :
+
+```
+---
+language: fr
+license: mit
+datasets:
+- oscar
+---
+```
+
+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.
diff --git a/chapters/fr/chapter4/5.mdx b/chapters/fr/chapter4/5.mdx
index 4365a6733..cf0b3b764 100644
--- a/chapters/fr/chapter4/5.mdx
+++ b/chapters/fr/chapter4/5.mdx
@@ -1,7 +1,12 @@
-# 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é.
-
+# 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 a180d67fa..d0220522e 100644
--- a/chapters/fr/chapter4/6.mdx
+++ b/chapters/fr/chapter4/6.mdx
@@ -1,223 +1,228 @@
-
-
-
-
-# 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."
- }
- ]}
+
+
+
+
+# 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 2817734c9..3b1d9718f 100644
--- a/chapters/fr/chapter5/1.mdx
+++ b/chapters/fr/chapter5/1.mdx
@@ -1,17 +1,22 @@
-# 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* ?
-
+# 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 f05424005..ee20d7800 100644
--- a/chapters/fr/chapter5/2.mdx
+++ b/chapters/fr/chapter5/2.mdx
@@ -1,167 +1,167 @@
-# 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).
-
-
+# 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 1962c4dad..443681291 100644
--- a/chapters/fr/chapter5/3.mdx
+++ b/chapters/fr/chapter5/3.mdx
@@ -1,752 +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 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 !
+# 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 dc286c718..0b369438c 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 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 !
+# 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 4781cd83c..3b1f96a41 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 : « 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`.
-
-
+# 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 dcb42deb5..69762e98a 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 *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.
-
-
+
+
+# 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 55083fffa..e205ff607 100644
--- a/chapters/fr/chapter5/7.mdx
+++ b/chapters/fr/chapter5/7.mdx
@@ -1,10 +1,15 @@
-# 🤗 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 !
+# 🤗 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 54f4e770f..6abf66b0a 100644
--- a/chapters/fr/chapter5/8.mdx
+++ b/chapters/fr/chapter5/8.mdx
@@ -1,226 +1,231 @@
-
-
-# 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
- },
- ]}
-/>
+
+
+# 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 6869cc815..730e89a3f 100644
--- a/chapters/fr/chapter6/1.mdx
+++ b/chapters/fr/chapter6/1.mdx
@@ -1,13 +1,18 @@
-# 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 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 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.
-
+# 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 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 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 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 062b51f16..4846966e4 100644
--- a/chapters/fr/chapter6/10.mdx
+++ b/chapters/fr/chapter6/10.mdx
@@ -1,278 +1,283 @@
-
-
-# Quiz de fin de chapitre
-
-Testons ce que vous avez appris dans ce chapitre !
-
-### 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 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: "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é.",
- 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 par rapport à une liste de listes lors de l'utilisation de train_new_from_iterator()
?
-
-train_new_from_iterator() accepte.",
- explain: "Une liste de listes de textes est un type particulier de générateur de listes de textes, la méthode l'acceptera donc aussi. Essayez à nouveau !"
- },
- {
- text: "Vous éviterez de charger l'ensemble des données en mémoire en une seule fois.",
- 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: "Il utilisera le multiprocesseur dans tous les cas."
- },
- {
- text: "Le tokenizer que vous entraînez générera de meilleurs textes.",
- explain: "Le tokenizer ne génère pas de texte. Vous le confondez avec un modèle de langage ?"
- }
- ]}
-/>
-
-### 3. Quels sont les avantages d'utiliser un tokenizer « rapide » ?
-
-tokenizer lent lorsque vous faites des batchs d'entrées.",
- 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
- },
- {
- text: "Les tokenizers rapides sont toujours plus rapides que leurs homologues lents.",
- 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 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 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 ?
-
-token porte l'étiquette de l'entité, le mot entier est considéré comme étiqueté avec cette entité.",
- explain: "C'est une stratégie pour gérer les entités. Quelles autres réponses s'appliquent ici ?",
- correct: true
- },
- {
- text: "Lorsqu'un token a l'étiquette d'une entité donnée, tout autre token suivant ayant la même étiquette est considéré comme faisant partie de la même entité, à moins qu'il ne soit étiqueté comme le début d'une nouvelle entité.",
- explain: "C'est la façon la plus courante de regrouper des entités, mais ce n'est pas la seule bonne réponse.",
- correct: true
- }
- ]}
-/>
-
-### 5. Comment le pipeline `question-answering` gère-t-il les contextes longs ?
-
-
-
-### 6. Qu'est-ce que la normalisation ?
-
-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 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.",
- explain: "Cette étape est simplement appelée post-traitement."
- },
- {
- 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 ?
-
-tokenizer, pour diviser l'entrée en mots.",
- explain: "C'est la bonne réponse !",
- correct: true
- },
- {
- text: "Il s'agit de l'étape précédant l'application du modèle 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.
-
-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.",
- explain: "C'est exact !",
- correct: true
- },
- {
- 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: "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: " ",
- 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: "C'est la façon de faire d'un autre algorithme de tokenization."
- },
- ]}
-/>
-
-### 9. Sélectionnez les phrases qui s'appliquent au tokenizer WordPiece.
-
-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: "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: " ",
- correct: true
- },
- {
- text: "WordPiece tokenise les mots en sous-mots en trouvant la segmentation en tokens la plus probable, selon le modèle.",
- 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: "C'est ainsi que WordPiece procède pour l'encodage.",
- correct: true
- },
- ]}
-/>
-
-### 10. Sélectionnez les phrases qui s'appliquent au tokenizer Unigram.
-
-tokens.",
- explain: " ",
- correct: true
- },
- {
- text: "Unigram adapte son vocabulaire en minimisant une perte calculée sur l'ensemble du corpus.",
- explain: " ",
- correct: true
- },
- {
- text: "Unigram adapte son vocabulaire en conservant les sous-mots les plus fréquents.",
- explain: " "
- },
- {
- text: "Unigram segmente les mots en sous-mots en trouvant la segmentation la plus probable en tokens, selon le modèle.",
- 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: "C'est la façon de faire d'un autre algorithme de tokenization."
- },
- ]}
-/>
+
+
+# Quiz de fin de chapitre
+
+
+
+Testons ce que vous avez appris dans ce chapitre !
+
+### 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 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: "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é.",
+ 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 par rapport à une liste de listes lors de l'utilisation de train_new_from_iterator()
?
+
+train_new_from_iterator() accepte.",
+ explain: "Une liste de listes de textes est un type particulier de générateur de listes de textes, la méthode l'acceptera donc aussi. Essayez à nouveau !"
+ },
+ {
+ text: "Vous éviterez de charger l'ensemble des données en mémoire en une seule fois.",
+ 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: "Il utilisera le multiprocesseur dans tous les cas."
+ },
+ {
+ text: "Le tokenizer que vous entraînez générera de meilleurs textes.",
+ explain: "Le tokenizer ne génère pas de texte. Vous le confondez avec un modèle de langage ?"
+ }
+ ]}
+/>
+
+### 3. Quels sont les avantages d'utiliser un tokenizer « rapide » ?
+
+tokenizer lent lorsque vous faites des batchs d'entrées.",
+ 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
+ },
+ {
+ text: "Les tokenizers rapides sont toujours plus rapides que leurs homologues lents.",
+ 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 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 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 ?
+
+token porte l'étiquette de l'entité, le mot entier est considéré comme étiqueté avec cette entité.",
+ explain: "C'est une stratégie pour gérer les entités. Quelles autres réponses s'appliquent ici ?",
+ correct: true
+ },
+ {
+ text: "Lorsqu'un token a l'étiquette d'une entité donnée, tout autre token suivant ayant la même étiquette est considéré comme faisant partie de la même entité, à moins qu'il ne soit étiqueté comme le début d'une nouvelle entité.",
+ explain: "C'est la façon la plus courante de regrouper des entités, mais ce n'est pas la seule bonne réponse.",
+ correct: true
+ }
+ ]}
+/>
+
+### 5. Comment le pipeline `question-answering` gère-t-il les contextes longs ?
+
+
+
+### 6. Qu'est-ce que la normalisation ?
+
+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 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.",
+ explain: "Cette étape est simplement appelée post-traitement."
+ },
+ {
+ 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 ?
+
+tokenizer, pour diviser l'entrée en mots.",
+ explain: "C'est la bonne réponse !",
+ correct: true
+ },
+ {
+ text: "Il s'agit de l'étape précédant l'application du modèle 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.
+
+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.",
+ explain: "C'est exact !",
+ correct: true
+ },
+ {
+ 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: "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: " ",
+ 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: "C'est la façon de faire d'un autre algorithme de tokenization."
+ },
+ ]}
+/>
+
+### 9. Sélectionnez les phrases qui s'appliquent au tokenizer WordPiece.
+
+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: "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: " ",
+ correct: true
+ },
+ {
+ text: "WordPiece tokenise les mots en sous-mots en trouvant la segmentation en tokens la plus probable, selon le modèle.",
+ 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: "C'est ainsi que WordPiece procède pour l'encodage.",
+ correct: true
+ },
+ ]}
+/>
+
+### 10. Sélectionnez les phrases qui s'appliquent au tokenizer Unigram.
+
+tokens.",
+ explain: " ",
+ correct: true
+ },
+ {
+ text: "Unigram adapte son vocabulaire en minimisant une perte calculée sur l'ensemble du corpus.",
+ explain: " ",
+ correct: true
+ },
+ {
+ text: "Unigram adapte son vocabulaire en conservant les sous-mots les plus fréquents.",
+ explain: " "
+ },
+ {
+ text: "Unigram segmente les mots en sous-mots en trouvant la segmentation la plus probable en tokens, selon le modèle.",
+ 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: "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 9a29792dd..8eba2f385 100644
--- a/chapters/fr/chapter6/2.mdx
+++ b/chapters/fr/chapter6/2.mdx
@@ -1,265 +1,265 @@
-# 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 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 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 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
-
-# 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 au jeu d'entraînement pour voir quelles sont les colonnes auxquelles nous avons accès :
-
-```py
-raw_datasets["train"]
-```
-
-```python out
-Dataset({
- features: ['repository_name', 'func_path_in_repository', 'func_name', 'whole_func_string', 'language',
- 'func_code_string', 'func_code_tokens', 'func_documentation_string', 'func_documentation_tokens', 'split_name',
- 'func_code_url'
- ],
- num_rows: 412178
-})
-```
-
-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 nous affiche ce qui suit :
-
-```out
-def handle_simple_responses(
- self, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK):
- """Accepts normal responses from the device.
-
- Args:
- timeout_ms: Timeout in milliseconds to wait for each response.
- info_cb: Optional callback for text sent from the bootloader.
-
- Returns:
- OKAY packet's message.
- """
- return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)
-```
-
-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 :
-
-
-```py
-# Ne décommentez pas la ligne suivante à moins que votre jeu de données soit petit !
-# 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, 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 = (
- raw_datasets["train"][i : i + 1000]["whole_func_string"]
- for i in range(0, len(raw_datasets["train"]), 1000)
-)
-```
-
-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 :
-
-
-```py
-gen = (i for i in range(10))
-print(list(gen))
-print(list(gen))
-```
-
-on les reçoit une fois et ensuite une liste vide :
-
-```python out
-[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-[]
-```
-
-C'est pourquoi nous définissons une fonction qui renvoie un générateur à la place :
-
-```py
-def get_training_corpus():
- return (
- raw_datasets["train"][i : i + 1000]["whole_func_string"]
- for i in range(0, len(raw_datasets["train"]), 1000)
- )
-
-
-training_corpus = get_training_corpus()
-```
-
-Vous pouvez également définir votre générateur à l'intérieur d'une boucle `for` en utilisant l'instruction `yield` :
-
-```py
-def get_training_corpus():
- dataset = raw_datasets["train"]
- for start_idx in range(0, len(dataset), 1000):
- samples = dataset[start_idx : start_idx + 1000]
- yield samples["whole_func_string"]
-```
-
-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 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
-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 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 :
-
-
-```py
-example = '''def add_numbers(a, b):
- """Add the two numbers `a` and `b`."""
- return a + b'''
-
-tokens = old_tokenizer.tokenize(example)
-tokens
-```
-
-```python out
-['def', 'Ġadd', '_', 'n', 'umbers', '(', 'a', ',', 'Ġb', '):', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo',
- 'Ġ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 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()` :
-
-
-```py
-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. 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 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.
-
-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)
-tokens
-```
-
-```python out
-['def', 'Ġadd', '_', 'numbers', '(', 'a', ',', 'Ġb', '):', 'ĊĠĠĠ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo', 'Ġnumbers', 'Ġ`',
- 'a', '`', 'Ġand', 'Ġ`', 'b', '`."""', 'ĊĠĠĠ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
-```
-
-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))
-print(len(old_tokenizer.tokenize(example)))
-```
-
-```python out
-27
-36
-```
-
-Prenons un autre exemple :
-
-```python
-example = """class LinearLayer():
- def __init__(self, input_size, output_size):
- self.weight = torch.randn(input_size, output_size)
- self.bias = torch.zeros(output_size)
-
- def __call__(self, x):
- return x @ self.weights + self.bias
- """
-tokenizer.tokenize(example)
-```
-
-```python out
-['class', 'ĠLinear', 'Layer', '():', 'ĊĠĠĠ', 'Ġdef', 'Ġ__', 'init', '__(', 'self', ',', 'Ġinput', '_', 'size', ',',
- 'Ġoutput', '_', 'size', '):', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'weight', 'Ġ=', 'Ġtorch', '.', 'randn', '(', 'input', '_',
- 'size', ',', 'Ġoutput', '_', 'size', ')', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'bias', 'Ġ=', 'Ġtorch', '.', 'zeros', '(',
- 'output', '_', 'size', ')', 'ĊĊĠĠĠ', 'Ġdef', 'Ġ__', 'call', '__(', 'self', ',', 'Ġx', '):', 'ĊĠĠĠĠĠĠĠ',
- 'Ġ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*. 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
-
-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()` :
-
-
-```py
-tokenizer.save_pretrained("code-search-net-tokenizer")
-```
-
-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
-
-notebook_login()
-```
-
-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 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 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 réel pour utiliser votre propre tokenizer
-tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")
-```
-
+# 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 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 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 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
+
+# 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 au jeu d'entraînement pour voir quelles sont les colonnes auxquelles nous avons accès :
+
+```py
+raw_datasets["train"]
+```
+
+```python out
+Dataset({
+ features: ['repository_name', 'func_path_in_repository', 'func_name', 'whole_func_string', 'language',
+ 'func_code_string', 'func_code_tokens', 'func_documentation_string', 'func_documentation_tokens', 'split_name',
+ 'func_code_url'
+ ],
+ num_rows: 412178
+})
+```
+
+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 nous affiche ce qui suit :
+
+```out
+def handle_simple_responses(
+ self, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK):
+ """Accepts normal responses from the device.
+
+ Args:
+ timeout_ms: Timeout in milliseconds to wait for each response.
+ info_cb: Optional callback for text sent from the bootloader.
+
+ Returns:
+ OKAY packet's message.
+ """
+ return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)
+```
+
+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 :
+
+
+```py
+# Ne décommentez pas la ligne suivante à moins que votre jeu de données soit petit !
+# 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, 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 = (
+ raw_datasets["train"][i : i + 1000]["whole_func_string"]
+ for i in range(0, len(raw_datasets["train"]), 1000)
+)
+```
+
+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 :
+
+
+```py
+gen = (i for i in range(10))
+print(list(gen))
+print(list(gen))
+```
+
+on les reçoit une fois et ensuite une liste vide :
+
+```python out
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+[]
+```
+
+C'est pourquoi nous définissons une fonction qui renvoie un générateur à la place :
+
+```py
+def get_training_corpus():
+ return (
+ raw_datasets["train"][i : i + 1000]["whole_func_string"]
+ for i in range(0, len(raw_datasets["train"]), 1000)
+ )
+
+
+training_corpus = get_training_corpus()
+```
+
+Vous pouvez également définir votre générateur à l'intérieur d'une boucle `for` en utilisant l'instruction `yield` :
+
+```py
+def get_training_corpus():
+ dataset = raw_datasets["train"]
+ for start_idx in range(0, len(dataset), 1000):
+ samples = dataset[start_idx : start_idx + 1000]
+ yield samples["whole_func_string"]
+```
+
+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 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
+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 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 :
+
+
+```py
+example = '''def add_numbers(a, b):
+ """Add the two numbers `a` and `b`."""
+ return a + b'''
+
+tokens = old_tokenizer.tokenize(example)
+tokens
+```
+
+```python out
+['def', 'Ġadd', '_', 'n', 'umbers', '(', 'a', ',', 'Ġb', '):', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo',
+ 'Ġ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 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()` :
+
+
+```py
+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. 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 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.
+
+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)
+tokens
+```
+
+```python out
+['def', 'Ġadd', '_', 'numbers', '(', 'a', ',', 'Ġb', '):', 'ĊĠĠĠ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo', 'Ġnumbers', 'Ġ`',
+ 'a', '`', 'Ġand', 'Ġ`', 'b', '`."""', 'ĊĠĠĠ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
+```
+
+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))
+print(len(old_tokenizer.tokenize(example)))
+```
+
+```python out
+27
+36
+```
+
+Prenons un autre exemple :
+
+```python
+example = """class LinearLayer():
+ def __init__(self, input_size, output_size):
+ self.weight = torch.randn(input_size, output_size)
+ self.bias = torch.zeros(output_size)
+
+ def __call__(self, x):
+ return x @ self.weights + self.bias
+ """
+tokenizer.tokenize(example)
+```
+
+```python out
+['class', 'ĠLinear', 'Layer', '():', 'ĊĠĠĠ', 'Ġdef', 'Ġ__', 'init', '__(', 'self', ',', 'Ġinput', '_', 'size', ',',
+ 'Ġoutput', '_', 'size', '):', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'weight', 'Ġ=', 'Ġtorch', '.', 'randn', '(', 'input', '_',
+ 'size', ',', 'Ġoutput', '_', 'size', ')', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'bias', 'Ġ=', 'Ġtorch', '.', 'zeros', '(',
+ 'output', '_', 'size', ')', 'ĊĊĠĠĠ', 'Ġdef', 'Ġ__', 'call', '__(', 'self', ',', 'Ġx', '):', 'ĊĠĠĠĠĠĠĠ',
+ 'Ġ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*. 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
+
+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()` :
+
+
+```py
+tokenizer.save_pretrained("code-search-net-tokenizer")
+```
+
+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
+
+notebook_login()
+```
+
+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 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 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 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 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 c37fc06ff..41d437a8d 100644
--- a/chapters/fr/chapter6/3.mdx
+++ b/chapters/fr/chapter6/3.mdx
@@ -1,477 +1,477 @@
-
-
-# Pouvoirs spéciaux des tokenizers rapides
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/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 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 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
-:--------------:|:--------------:|:-------------:
-`batched=True` | 10.8s | 4min41s
-`batched=False` | 59.2s | 5min3s
-
-
-
-⚠️ 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.
-
-
-
-## L'objet BatchEncoding
-
-
-
-La sortie d'un *tokenizer* n'est pas un simple dictionnaire Python. Ce que nous obtenons est en fait un objet spécial `BatchEncoding`. C'est une sous-classe d'un dictionnaire (c'est pourquoi nous avons pu indexer ce résultat sans problème auparavant), mais avec des méthodes supplémentaires qui sont principalement utilisées par les *tokenizers* rapides.
-
-En plus de leurs capacités de parallélisation, la fonctionnalité clé des *tokenizers* rapides est qu'ils gardent toujours la trace de l'étendue originale des textes d'où proviennent les *tokens* finaux, une fonctionnalité que nous appelons *mapping offset*. Cela permet de débloquer des fonctionnalités telles que le mappage de chaque mot aux *tokens* qu'il a générés ou le mappage de chaque caractère du texte original au *token* qu'il contient, et vice versa.
-
-Prenons un exemple :
-
-```py
-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.
-encoding = tokenizer(example)
-print(type(encoding))
-```
-
-Comme mentionné précédemment, nous obtenons un objet `BatchEncoding` dans la sortie du *tokenizer* :
-
-```python out
-
-```
-
-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
-```
-
-```python out
-True
-```
-
-soit vérifier le même attribut mais avec notre `encoding` :
-
-```python
-encoding.is_fast
-```
-
-```python out
-True
-```
-
-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()
-```
-
-```python out
-['[CLS]', 'My', 'name', 'is', 'S', '##yl', '##va', '##in', 'and', 'I', 'work', 'at', 'Hu', '##gging', 'Face', 'in',
- 'Brooklyn', '.', '[SEP]']
-```
-
-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()
-```
-
-```python out
-[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` 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'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 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).
-
-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)
-example[start:end]
-```
-
-```python out
-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 d'*offsets*. Pour illustrer leur utilisation, nous allons maintenant vous montrer comment reproduire manuellement les résultats du pipeline `token-classification`.
-
-
-
-✏️ **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 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'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-### 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 une NER sur les phrases :
-
-```py
-from transformers import pipeline
-
-token_classifier = pipeline("token-classification")
-token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
-```
-
-```python out
-[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
- {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
- {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
- {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
- {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
- {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
- {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
- {'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é :
-
-```py
-from transformers import pipeline
-
-token_classifier = pipeline("token-classification", aggregation_strategy="simple")
-token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
-```
-
-```python out
-[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
- {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
- {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
-```
-
-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).
-- `"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()` !
-
-### Des entrées aux prédictions
-
-{#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/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
-
-model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-model = AutoModelForTokenClassification.from_pretrained(model_checkpoint)
-
-example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
-inputs = tokenizer(example, return_tensors="pt")
-outputs = model(**inputs)
-```
-
-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)
-print(outputs.logits.shape)
-```
-
-```python out
-torch.Size([1, 19])
-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/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
-
-model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-model = TFAutoModelForTokenClassification.from_pretrained(model_checkpoint)
-
-example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
-inputs = tokenizer(example, return_tensors="tf")
-outputs = model(**inputs)
-```
-
-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)
-print(outputs.logits.shape)
-```
-
-```python out
-(1, 19)
-(1, 19, 9)
-```
-
-{/if}
-
-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'}
-
-```py
-import torch
-
-probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)[0].tolist()
-predictions = outputs.logits.argmax(dim=-1)[0].tolist()
-print(predictions)
-```
-
-{:else}
-
-```py
-import tensorflow as tf
-
-probabilities = tf.math.softmax(outputs.logits, axis=-1)[0]
-probabilities = probabilities.numpy().tolist()
-predictions = tf.math.argmax(outputs.logits, axis=-1)[0]
-predictions = predictions.numpy().tolist()
-print(predictions)
-```
-
-{/if}
-
-```python out
-[0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0]
-```
-
-L'attribut `model.config.id2label` contient la correspondance entre les index et les étiquettes que nous pouvons utiliser pour donner un sens aux prédictions :
-
-```py
-model.config.id2label
-```
-
-```python out
-{0: 'O',
- 1: 'B-MISC',
- 2: 'I-MISC',
- 3: 'B-PER',
- 4: 'I-PER',
- 5: 'B-ORG',
- 6: 'I-ORG',
- 7: 'B-LOC',
- 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* (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é 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`.
-
-
-
-
-
-
-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 = []
-tokens = inputs.tokens()
-
-for idx, pred in enumerate(predictions):
- label = model.config.id2label[pred]
- if label != "O":
- results.append(
- {"entity": label, "score": probabilities[idx][pred], "word": tokens[idx]}
- )
-
-print(results)
-```
-
-```python out
-[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S'},
- {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl'},
- {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va'},
- {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in'},
- {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu'},
- {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging'},
- {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face'},
- {'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 *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)
-inputs_with_offsets["offset_mapping"]
-```
-
-```python out
-[(0, 0), (0, 2), (3, 7), (8, 10), (11, 12), (12, 14), (14, 16), (16, 18), (19, 22), (23, 24), (25, 29), (30, 32),
- (33, 35), (35, 40), (41, 45), (46, 48), (49, 57), (57, 58), (0, 0)]
-```
-
-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
-example[12:14]
-```
-
-nous obtenons le bon espace de texte sans le `##` :
-
-```python out
-yl
-```
-
-En utilisant cela, nous pouvons maintenant compléter les résultats précédents :
-
-```py
-results = []
-inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
-tokens = inputs_with_offsets.tokens()
-offsets = inputs_with_offsets["offset_mapping"]
-
-for idx, pred in enumerate(predictions):
- label = model.config.id2label[pred]
- if label != "O":
- start, end = offsets[idx]
- results.append(
- {
- "entity": label,
- "score": probabilities[idx][pred],
- "word": tokens[idx],
- "start": start,
- "end": end,
- }
- )
-
-print(results)
-```
-
-```python out
-[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
- {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
- {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
- {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
- {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
- {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
- {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
- {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
-```
-
-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).
-
-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]
-```
-
-```python out
-Hugging Face
-```
-
-Pour écrire le code qui post-traite les prédictions tout en regroupant les entités, nous regrouperons les entités qui sont consécutives et étiquetées avec `I-XXX`, à l'exception de la première, qui peut être étiquetée comme `B-XXX` ou `I-XXX` (ainsi, nous arrêtons de regrouper une entité lorsque nous obtenons un `O`, un nouveau type d'entité, ou un `B-XXX` qui nous indique qu'une entité du même type commence) :
-
-```py
-import numpy as np
-
-results = []
-inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
-tokens = inputs_with_offsets.tokens()
-offsets = inputs_with_offsets["offset_mapping"]
-
-idx = 0
-while idx < len(predictions):
- pred = predictions[idx]
- label = model.config.id2label[pred]
- if label != "O":
- # Enlever le B- ou le I-
- label = label[2:]
- start, _ = offsets[idx]
-
- # Récupérer tous les tokens étiquetés avec I-label
- all_scores = []
- while (
- idx < len(predictions)
- and model.config.id2label[predictions[idx]] == f"I-{label}"
- ):
- all_scores.append(probabilities[idx][pred])
- _, end = offsets[idx]
- idx += 1
-
- # 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(
- {
- "entity_group": label,
- "score": score,
- "word": word,
- "start": start,
- "end": end,
- }
- )
- idx += 1
-
-print(results)
-```
-
-Et nous obtenons les mêmes résultats qu'avec notre deuxième pipeline !
-
-```python out
-[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
- {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
- {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
-```
-
-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.
+
+
+# Pouvoirs spéciaux des tokenizers rapides
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/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 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 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
+:--------------:|:--------------:|:-------------:
+`batched=True` | 10.8s | 4min41s
+`batched=False` | 59.2s | 5min3s
+
+
+
+⚠️ 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.
+
+
+
+## L'objet BatchEncoding
+
+
+
+La sortie d'un *tokenizer* n'est pas un simple dictionnaire Python. Ce que nous obtenons est en fait un objet spécial `BatchEncoding`. C'est une sous-classe d'un dictionnaire (c'est pourquoi nous avons pu indexer ce résultat sans problème auparavant), mais avec des méthodes supplémentaires qui sont principalement utilisées par les *tokenizers* rapides.
+
+En plus de leurs capacités de parallélisation, la fonctionnalité clé des *tokenizers* rapides est qu'ils gardent toujours la trace de l'étendue originale des textes d'où proviennent les *tokens* finaux, une fonctionnalité que nous appelons *mapping offset*. Cela permet de débloquer des fonctionnalités telles que le mappage de chaque mot aux *tokens* qu'il a générés ou le mappage de chaque caractère du texte original au *token* qu'il contient, et vice versa.
+
+Prenons un exemple :
+
+```py
+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.
+encoding = tokenizer(example)
+print(type(encoding))
+```
+
+Comme mentionné précédemment, nous obtenons un objet `BatchEncoding` dans la sortie du *tokenizer* :
+
+```python out
+
+```
+
+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
+```
+
+```python out
+True
+```
+
+soit vérifier le même attribut mais avec notre `encoding` :
+
+```python
+encoding.is_fast
+```
+
+```python out
+True
+```
+
+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()
+```
+
+```python out
+['[CLS]', 'My', 'name', 'is', 'S', '##yl', '##va', '##in', 'and', 'I', 'work', 'at', 'Hu', '##gging', 'Face', 'in',
+ 'Brooklyn', '.', '[SEP]']
+```
+
+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()
+```
+
+```python out
+[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` 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'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 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).
+
+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)
+example[start:end]
+```
+
+```python out
+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 d'*offsets*. Pour illustrer leur utilisation, nous allons maintenant vous montrer comment reproduire manuellement les résultats du pipeline `token-classification`.
+
+
+
+✏️ **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 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'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+### 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 une NER sur les phrases :
+
+```py
+from transformers import pipeline
+
+token_classifier = pipeline("token-classification")
+token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+```python out
+[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
+ {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
+ {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
+ {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
+ {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
+ {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
+ {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
+ {'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é :
+
+```py
+from transformers import pipeline
+
+token_classifier = pipeline("token-classification", aggregation_strategy="simple")
+token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+```python out
+[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
+ {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
+ {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+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).
+- `"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()` !
+
+### Des entrées aux prédictions
+
+{#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/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
+
+model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = AutoModelForTokenClassification.from_pretrained(model_checkpoint)
+
+example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
+inputs = tokenizer(example, return_tensors="pt")
+outputs = model(**inputs)
+```
+
+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)
+print(outputs.logits.shape)
+```
+
+```python out
+torch.Size([1, 19])
+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/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
+
+model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = TFAutoModelForTokenClassification.from_pretrained(model_checkpoint)
+
+example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
+inputs = tokenizer(example, return_tensors="tf")
+outputs = model(**inputs)
+```
+
+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)
+print(outputs.logits.shape)
+```
+
+```python out
+(1, 19)
+(1, 19, 9)
+```
+
+{/if}
+
+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'}
+
+```py
+import torch
+
+probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)[0].tolist()
+predictions = outputs.logits.argmax(dim=-1)[0].tolist()
+print(predictions)
+```
+
+{:else}
+
+```py
+import tensorflow as tf
+
+probabilities = tf.math.softmax(outputs.logits, axis=-1)[0]
+probabilities = probabilities.numpy().tolist()
+predictions = tf.math.argmax(outputs.logits, axis=-1)[0]
+predictions = predictions.numpy().tolist()
+print(predictions)
+```
+
+{/if}
+
+```python out
+[0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0]
+```
+
+L'attribut `model.config.id2label` contient la correspondance entre les index et les étiquettes que nous pouvons utiliser pour donner un sens aux prédictions :
+
+```py
+model.config.id2label
+```
+
+```python out
+{0: 'O',
+ 1: 'B-MISC',
+ 2: 'I-MISC',
+ 3: 'B-PER',
+ 4: 'I-PER',
+ 5: 'B-ORG',
+ 6: 'I-ORG',
+ 7: 'B-LOC',
+ 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* (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é 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`.
+
+
+
+
+
+
+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 = []
+tokens = inputs.tokens()
+
+for idx, pred in enumerate(predictions):
+ label = model.config.id2label[pred]
+ if label != "O":
+ results.append(
+ {"entity": label, "score": probabilities[idx][pred], "word": tokens[idx]}
+ )
+
+print(results)
+```
+
+```python out
+[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S'},
+ {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl'},
+ {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va'},
+ {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in'},
+ {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu'},
+ {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging'},
+ {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face'},
+ {'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 *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)
+inputs_with_offsets["offset_mapping"]
+```
+
+```python out
+[(0, 0), (0, 2), (3, 7), (8, 10), (11, 12), (12, 14), (14, 16), (16, 18), (19, 22), (23, 24), (25, 29), (30, 32),
+ (33, 35), (35, 40), (41, 45), (46, 48), (49, 57), (57, 58), (0, 0)]
+```
+
+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
+example[12:14]
+```
+
+nous obtenons le bon espace de texte sans le `##` :
+
+```python out
+yl
+```
+
+En utilisant cela, nous pouvons maintenant compléter les résultats précédents :
+
+```py
+results = []
+inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
+tokens = inputs_with_offsets.tokens()
+offsets = inputs_with_offsets["offset_mapping"]
+
+for idx, pred in enumerate(predictions):
+ label = model.config.id2label[pred]
+ if label != "O":
+ start, end = offsets[idx]
+ results.append(
+ {
+ "entity": label,
+ "score": probabilities[idx][pred],
+ "word": tokens[idx],
+ "start": start,
+ "end": end,
+ }
+ )
+
+print(results)
+```
+
+```python out
+[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
+ {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
+ {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
+ {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
+ {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
+ {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
+ {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
+ {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+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).
+
+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]
+```
+
+```python out
+Hugging Face
+```
+
+Pour écrire le code qui post-traite les prédictions tout en regroupant les entités, nous regrouperons les entités qui sont consécutives et étiquetées avec `I-XXX`, à l'exception de la première, qui peut être étiquetée comme `B-XXX` ou `I-XXX` (ainsi, nous arrêtons de regrouper une entité lorsque nous obtenons un `O`, un nouveau type d'entité, ou un `B-XXX` qui nous indique qu'une entité du même type commence) :
+
+```py
+import numpy as np
+
+results = []
+inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
+tokens = inputs_with_offsets.tokens()
+offsets = inputs_with_offsets["offset_mapping"]
+
+idx = 0
+while idx < len(predictions):
+ pred = predictions[idx]
+ label = model.config.id2label[pred]
+ if label != "O":
+ # Enlever le B- ou le I-
+ label = label[2:]
+ start, _ = offsets[idx]
+
+ # Récupérer tous les tokens étiquetés avec I-label
+ all_scores = []
+ while (
+ idx < len(predictions)
+ and model.config.id2label[predictions[idx]] == f"I-{label}"
+ ):
+ all_scores.append(probabilities[idx][pred])
+ _, end = offsets[idx]
+ idx += 1
+
+ # 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(
+ {
+ "entity_group": label,
+ "score": score,
+ "word": word,
+ "start": start,
+ "end": end,
+ }
+ )
+ idx += 1
+
+print(results)
+```
+
+Et nous obtenons les mêmes résultats qu'avec notre deuxième pipeline !
+
+```python out
+[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
+ {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
+ {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+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 3e177a9fb..5002f9b4e 100644
--- a/chapters/fr/chapter6/3b.mdx
+++ b/chapters/fr/chapter6/3b.mdx
@@ -1,720 +1,720 @@
-
-
-# Tokenizer rapide dans le pipeline de QA
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-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'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-## 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 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 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)
-```
-
-```python out
-{'score': 0.97773,
- 'start': 78,
- 'end': 105,
- 'answer': 'Jax, PyTorch and TensorFlow'}
-```
-
-Contrairement aux autres pipelines, qui ne peuvent pas tronquer et diviser les textes dont la longueur est supérieure à la longueur maximale acceptée par le modèle (et qui peuvent donc manquer des informations à la fin d'un document), ce pipeline peut traiter des contextes très longs et retournera la réponse à la question même si elle se trouve à la fin :
-
-```py
-long_context = """
-🤗 Transformers: State of the Art NLP
-
-🤗 Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
-question answering, summarization, translation, text generation and more in over 100 languages.
-Its aim is to make cutting-edge NLP easier to use for everyone.
-
-🤗 Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
-then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
-can be modified to enable quick research experiments.
-
-Why should I use transformers?
-
-1. Easy-to-use state-of-the-art models:
- - High performance on NLU and NLG tasks.
- - Low barrier to entry for educators and practitioners.
- - Few user-facing abstractions with just three classes to learn.
- - A unified API for using all our pretrained models.
- - Lower compute costs, smaller carbon footprint:
-
-2. Researchers can share trained models instead of always retraining.
- - Practitioners can reduce compute time and production costs.
- - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.
-
-3. Choose the right framework for every part of a model's lifetime:
- - Train state-of-the-art models in 3 lines of code.
- - Move a single model between TF2.0/PyTorch frameworks at will.
- - Seamlessly pick the right framework for training, evaluation and production.
-
-4. Easily customize a model or an example to your needs:
- - We provide examples for each architecture to reproduce the results published by its original authors.
- - Model internals are exposed as consistently as possible.
- - Model files can be used independently of the library for quick experiments.
-
-🤗 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.
-"""
-
-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.
-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.
-puis de les partager avec la communauté sur notre hub de modèles. En même temps, chaque module python définissant une architecture est entièrement autonome et peut être modifié pour permettre des expériences de recherche rapides.
-peut être modifié pour permettre des expériences de recherche rapides.
-
-Pourquoi devrais-je utiliser des transformateurs ?
-
-1. Des modèles de pointe faciles à utiliser :
- - Haute performance sur les tâches NLU et NLG.
- - Faible barrière à l'entrée pour les éducateurs et les praticiens.
- - Peu d'abstractions pour l'utilisateur avec seulement trois classes à apprendre.
- - Une API unifiée pour utiliser tous nos modèles pré-entraînés.
- - Des coûts de calcul plus faibles, une empreinte carbone réduite :
-
-2. Les chercheurs peuvent partager les modèles formés au lieu de toujours les reformer.
- - Les praticiens peuvent réduire le temps de calcul et les coûts de production.
- - Des dizaines d'architectures avec plus de 10 000 modèles pré-formés, certains dans plus de 100 langues.
-
-3. Choisissez le cadre approprié pour chaque étape de la vie d'un modèle :
- - Entraînez des modèles de pointe en 3 lignes de code.
- - Déplacez un seul modèle entre les frameworks TF2.0/PyTorch à volonté.
- - Choisissez de manière transparente le bon framework pour l'entraînement, l'évaluation et la production.
-
-4. Adaptez facilement un modèle ou un exemple à vos besoins :
- - Nous fournissons des exemples pour chaque architecture afin de reproduire les résultats publiés par ses auteurs originaux.
- - Les éléments internes des modèles sont exposés de manière aussi cohérente que possible.
- - Les fichiers de modèles peuvent être utilisés indépendamment de la bibliothèque pour des expériences rapides.
-
-🤗 Transformers s'appuie sur les trois bibliothèques d'apprentissage profond les plus populaires (Jax, PyTorch et TensorFlow) avec une intégration parfaite
-entre elles. Il est simple d'entraîner vos modèles avec l'une avant de les charger pour l'inférence avec l'autre.
-"""
-question_answerer(question=question, context=long_context)
-```
-
-```python out
-{'score': 0.97149,
- 'start': 1892,
- 'end': 1919,
- 'answer': 'Jax, PyTorch and TensorFlow'}
-```
-
-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 *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'}
-
-```py
-from transformers import AutoTokenizer, AutoModelForQuestionAnswering
-
-model_checkpoint = "distilbert-base-cased-distilled-squad"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
-
-inputs = tokenizer(question, context, return_tensors="pt")
-outputs = model(**inputs)
-```
-
-{:else}
-
-```py
-from transformers import AutoTokenizer, TFAutoModelForQuestionAnswering
-
-model_checkpoint = "distilbert-base-cased-distilled-squad"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
-
-inputs = tokenizer(question, context, return_tensors="tf")
-outputs = model(**inputs)
-```
-
-{/if}
-
-Notez que nous tokenizons la question et le contexte comme une paire, la question en premier.
-
-
-
-
-
-
-Les modèles de réponse aux questions fonctionnent un peu différemment des modèles que nous avons vus jusqu'à présent. En utilisant l'image ci-dessus comme exemple, le modèle a été entraîné à prédire l'index du *token* de début de la réponse (ici 21) et l'index du *token* où la réponse se termine (ici 24). C'est pourquoi ces modèles ne retournent pas un tenseur de logits mais deux : un pour les logits correspondant au *token* de début de la réponse, et un pour les logits correspondant au *token* de fin de la réponse. Puisque dans ce cas nous n'avons qu'une seule entrée contenant 66 *tokens*, nous obtenons :
-
-```py
-start_logits = outputs.start_logits
-end_logits = outputs.end_logits
-print(start_logits.shape, end_logits.shape)
-```
-
-{#if fw === 'pt'}
-
-```python out
-torch.Size([1, 66]) torch.Size([1, 66])
-```
-
-{:else}
-
-```python out
-(1, 66) (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.
-
-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` :
-
-{#if fw === 'pt'}
-
-```py
-import torch
-
-sequence_ids = inputs.sequence_ids()
-# Masque tout, sauf les tokens du contexte
-mask = [i != 1 for i in sequence_ids]
-# Démasquer le token [CLS]
-mask[0] = False
-mask = torch.tensor(mask)[None]
-
-start_logits[mask] = -10000
-end_logits[mask] = -10000
-```
-
-{:else}
-
-```py
-import tensorflow as tf
-
-sequence_ids = inputs.sequence_ids()
-# Masque tout, sauf les tokens du contexte
-mask = [i != 1 for i in sequence_ids]
-# Démasquer le token [CLS]
-mask[0] = False
-mask = tf.constant(mask)[None]
-
-start_logits = tf.where(mask, -10000, start_logits)
-end_logits = tf.where(mask, -10000, end_logits)
-```
-
-{/if}
-
-Maintenant que nous avons correctement masqué les logits correspondant aux positions que nous ne voulons pas prédire, nous pouvons appliquer la softmax :
-
-{#if fw === 'pt'}
-
-```py
-start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)[0]
-end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)[0]
-```
-
-{:else}
-
-```py
-start_probabilities = tf.math.softmax(start_logits, axis=-1)[0].numpy()
-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é.
-
-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 :
-
-$$\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]$$
-
-Ainsi, pour calculer tous les scores, il suffit de calculer tous les produits \\(\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]\\) où `start_index <= end_index`.
-
-Calculons d'abord tous les produits possibles :
-
-```py
-scores = start_probabilities[:, None] * end_probabilities[None, :]
-```
-
-{#if fw === 'pt'}
-
-Ensuite, nous masquerons les valeurs où `start_index > end_index` en les mettant à `0` (les autres probabilités sont toutes des nombres positifs). La fonction `torch.triu()` renvoie la partie triangulaire supérieure du tenseur 2D passé en argument, elle fera donc ce masquage pour nous :
-
-```py
-scores = torch.triu(scores)
-```
-
-{:else}
-
-Ensuite, nous masquerons les valeurs où `start_index > end_index` en les mettant à `0` (les autres probabilités sont toutes des nombres positifs). La fonction `np.triu()` renvoie la partie triangulaire supérieure du tenseur 2D passé en argument, elle fera donc ce masquage pour nous :
-
-```py
-scores = np.triu(scores)
-```
-
-{/if}
-
-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()
-start_index = max_index // scores.shape[1]
-end_index = max_index % scores.shape[1]
-print(scores[start_index, end_index])
-```
-
-Nous n'avons pas encore tout à fait terminé, mais au moins nous avons déjà le score correct pour la réponse (vous pouvez le vérifier en le comparant au premier résultat de la section précédente) :
-
-```python out
-0.97773
-```
-
-
-
-✏️ **Essayez !** Calculez les indices de début et de fin pour les cinq réponses les plus probables.
-
-
-
-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)
-offsets = inputs_with_offsets["offset_mapping"]
-
-start_char, _ = offsets[start_index]
-_, end_char = offsets[end_index]
-answer = context[start_char:end_char]
-```
-
-Il ne nous reste plus qu'à tout formater pour obtenir notre résultat :
-
-```py
-result = {
- "answer": answer,
- "start": start_char,
- "end": end_char,
- "score": scores[start_index, end_index],
-}
-print(result)
-```
-
-```python out
-{'answer': 'Jax, PyTorch and TensorFlow',
- 'start': 78,
- 'end': 105,
- 'score': 0.97773}
-```
-
-Super ! C'est la même chose que dans notre premier exemple !
-
-
-
-✏️ **Essayez !** Utilisez les meilleurs scores que vous avez calculés précédemment pour afficher les cinq réponses les plus probables. Pour vérifier vos résultats, retournez au premier pipeline et passez dans `top_k=5` lorsque vous l'appelez.
-
-
-
-## Gestion des contextes longs
-
-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)
-print(len(inputs["input_ids"]))
-```
-
-```python out
-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 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")
-print(tokenizer.decode(inputs["input_ids"]))
-```
-
-```python out
-"""
-[CLS] Which deep learning libraries back [UNK] Transformers? [SEP] [UNK] Transformers : State of the Art NLP
-
-[UNK] Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
-question answering, summarization, translation, text generation and more in over 100 languages.
-Its aim is to make cutting-edge NLP easier to use for everyone.
-
-[UNK] Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
-then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
-can be modified to enable quick research experiments.
-
-Why should I use transformers?
-
-1. Easy-to-use state-of-the-art models:
- - High performance on NLU and NLG tasks.
- - Low barrier to entry for educators and practitioners.
- - Few user-facing abstractions with just three classes to learn.
- - A unified API for using all our pretrained models.
- - Lower compute costs, smaller carbon footprint:
-
-2. Researchers can share trained models instead of always retraining.
- - Practitioners can reduce compute time and production costs.
- - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.
-
-3. Choose the right framework for every part of a model's lifetime:
- - Train state-of-the-art models in 3 lines of code.
- - Move a single model between TF2.0/PyTorch frameworks at will.
- - Seamlessly pick the right framework for training, evaluation and production.
-
-4. Easily customize a model or an example to your needs:
- - We provide examples for each architecture to reproduce the results published by its original authors.
- - Model internal [SEP]
-"""
-
-"""
-[CLS] Quelles sont les bibliothèques d'apprentissage profond qui soutiennent [UNK] Transformers ? [SEP] [UNK] Transformers : l'état de l'art du NLP
-
-[UNK] 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é, la traduction, la génération de textes, etc,
-la réponse à des questions, le résumé, 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 tous.
-
-Transformers [UNK] 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.
-puis de les partager avec la communauté sur notre hub de modèles. En même temps, chaque module python définissant une architecture est entièrement autonome et peut être modifié pour permettre des expériences de recherche rapides.
-peut être modifié pour permettre des expériences de recherche rapides.
-
-Pourquoi devrais-je utiliser des transformateurs ?
-
-1. Des modèles de pointe faciles à utiliser :
- - Haute performance sur les tâches NLU et NLG.
- - Faible barrière à l'entrée pour les éducateurs et les praticiens.
- - Peu d'abstractions pour l'utilisateur avec seulement trois classes à apprendre.
- - Une API unifiée pour utiliser tous nos modèles pré-entraînés.
- - Des coûts de calcul plus faibles, une empreinte carbone réduite :
-
-2. Les chercheurs peuvent partager les modèles formés au lieu de toujours les reformer.
- - Les praticiens peuvent réduire le temps de calcul et les coûts de production.
- - Des dizaines d'architectures avec plus de 10 000 modèles pré-formés, certains dans plus de 100 langues.
-
-3. Choisissez le cadre approprié pour chaque étape de la vie d'un modèle :
- - Entraînez des modèles de pointe en 3 lignes de code.
- - Déplacez un seul modèle entre les frameworks TF2.0/PyTorch à volonté.
- - Choisissez de manière transparente le bon framework pour l'entraînement, l'évaluation et la production.
-
-4. Adaptez facilement un modèle ou un exemple à vos besoins :
- - Nous fournissons des exemples pour chaque architecture afin de reproduire les résultats publiés par ses auteurs originaux.
- - Modèle interne [SEP]
-"""
-```
-
-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."
-inputs = tokenizer(
- sentence, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
-)
-
-for ids in inputs["input_ids"]:
- print(tokenizer.decode(ids))
-```
-
-```python out
-'[CLS] This sentence is not [SEP]'
-'[CLS] is not too long [SEP]'
-'[CLS] too long but we [SEP]'
-'[CLS] but we are going [SEP]'
-'[CLS] are going to split [SEP]'
-'[CLS] to split it anyway [SEP]'
-'[CLS] it anyway. [SEP]'
-```
-
-Comme on peut le voir, la phrase a été découpée en morceaux de telle sorte que chaque entrée dans `inputs["input_ids"]` a au maximum 6 *tokens* (il faudrait ajouter du *padding* pour que la dernière entrée ait la même taille que les autres) et il y a un chevauchement de 2 *tokens* entre chacune des entrées.
-
-Regardons de plus près le résultat de la tokénisation :
-
-```py
-print(inputs.keys())
-```
-
-```python out
-dict_keys(['input_ids', 'attention_mask', 'overflow_to_sample_mapping'])
-```
-
-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"])
-```
-
-```python out
-[0, 0, 0, 0, 0, 0, 0]
-```
-
-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.
-]
-inputs = tokenizer(
- sentences, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
-)
-
-print(inputs["overflow_to_sample_mapping"])
-```
-
-nous donne :
-
-```python out
-[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.
-
-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(
- question,
- long_context,
- stride=128,
- max_length=384,
- padding="longest",
- truncation="only_second",
- return_overflowing_tokens=True,
- return_offsets_mapping=True,
-)
-```
-
-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'}
-
-```py
-_ = inputs.pop("overflow_to_sample_mapping")
-offsets = inputs.pop("offset_mapping")
-
-inputs = inputs.convert_to_tensors("pt")
-print(inputs["input_ids"].shape)
-```
-
-```python out
-torch.Size([2, 384])
-```
-
-{:else}
-
-```py
-_ = inputs.pop("overflow_to_sample_mapping")
-offsets = inputs.pop("offset_mapping")
-
-inputs = inputs.convert_to_tensors("tf")
-print(inputs["input_ids"].shape)
-```
-
-```python out
-(2, 384)
-```
-
-{/if}
-
-Notre contexte long a été divisé en deux, ce qui signifie qu'après avoir traversé notre modèle, nous aurons deux ensembles de logits de début et de fin :
-
-```py
-outputs = model(**inputs)
-
-start_logits = outputs.start_logits
-end_logits = outputs.end_logits
-print(start_logits.shape, end_logits.shape)
-```
-
-{#if fw === 'pt'}
-
-```python out
-torch.Size([2, 384]) torch.Size([2, 384])
-```
-
-{:else}
-
-```python out
-(2, 384) (2, 384)
-```
-
-{/if}
-
-Comme précédemment, nous masquons d'abord les *tokens* qui ne font pas partie du contexte avant de prendre le softmax. Nous masquons également tous les *tokens* de *padding* (tels que signalés par le masque d'attention) :
-
-{#if fw === 'pt'}
-
-```py
-sequence_ids = inputs.sequence_ids()
-# Masque tout, sauf les tokens du contexte
-mask = [i != 1 for i in sequence_ids]
-# Démasquer le jeton [CLS]
-mask[0] = False
-# Masquer tous les tokens [PAD]
-mask = torch.logical_or(torch.tensor(mask)[None], (inputs["attention_mask"] == 0))
-
-start_logits[mask] = -10000
-end_logits[mask] = -10000
-```
-
-{:else}
-
-```py
-sequence_ids = inputs.sequence_ids()
-# Masque tout, sauf les tokens du contexte
-mask = [i != 1 for i in sequence_ids]
-# Démasquer le jeton [CLS]
-mask[0] = False
-# Masquer tous les tokens [PAD]
-mask = tf.math.logical_or(tf.constant(mask)[None], inputs["attention_mask"] == 0)
-
-start_logits = tf.where(mask, -10000, start_logits)
-end_logits = tf.where(mask, -10000, end_logits)
-```
-
-{/if}
-
-Ensuite, nous pouvons utiliser la fonction softmax pour convertir nos logits en probabilités :
-
-{#if fw === 'pt'}
-
-```py
-start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)
-end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)
-```
-
-{:else}
-
-```py
-start_probabilities = tf.math.softmax(start_logits, axis=-1).numpy()
-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 morceaux. Nous attribuons un score à tous les espaces de réponse possibles puis nous prenons l'espace ayant le meilleur score :
-
-{#if fw === 'pt'}
-
-```py
-candidates = []
-for start_probs, end_probs in zip(start_probabilities, end_probabilities):
- scores = start_probs[:, None] * end_probs[None, :]
- idx = torch.triu(scores).argmax().item()
-
- start_idx = idx // scores.shape[0]
- end_idx = idx % scores.shape[0]
- score = scores[start_idx, end_idx].item()
- candidates.append((start_idx, end_idx, score))
-
-print(candidates)
-```
-
-{:else}
-
-```py
-candidates = []
-for start_probs, end_probs in zip(start_probabilities, end_probabilities):
- scores = start_probs[:, None] * end_probs[None, :]
- idx = np.triu(scores).argmax().item()
-
- start_idx = idx // scores.shape[0]
- end_idx = idx % scores.shape[0]
- score = scores[start_idx, end_idx].item()
- candidates.append((start_idx, end_idx, score))
-
-print(candidates)
-```
-
-{/if}
-
-```python out
-[(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 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).
-
-
-
-✏️ **Essayez !** Adaptez le code ci-dessus pour renvoyer les scores et les étendues des cinq réponses les plus probables (au total, pas par morceau).
-
-
-
-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):
- start_token, end_token, score = candidate
- start_char, _ = offset[start_token]
- _, end_char = offset[end_token]
- answer = long_context[start_char:end_char]
- result = {"answer": answer, "start": start_char, "end": end_char, "score": score}
- print(result)
-```
-
-```python out
-{'answer': '\n🤗 Transformers: State of the Art NLP', 'start': 0, 'end': 37, 'score': 0.33867}
-{'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 !
-
-
-
-✏️ **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.
-
-
-
+
+
+# Tokenizer rapide dans le pipeline de QA
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+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'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+## 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 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 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)
+```
+
+```python out
+{'score': 0.97773,
+ 'start': 78,
+ 'end': 105,
+ 'answer': 'Jax, PyTorch and TensorFlow'}
+```
+
+Contrairement aux autres pipelines, qui ne peuvent pas tronquer et diviser les textes dont la longueur est supérieure à la longueur maximale acceptée par le modèle (et qui peuvent donc manquer des informations à la fin d'un document), ce pipeline peut traiter des contextes très longs et retournera la réponse à la question même si elle se trouve à la fin :
+
+```py
+long_context = """
+🤗 Transformers: State of the Art NLP
+
+🤗 Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
+question answering, summarization, translation, text generation and more in over 100 languages.
+Its aim is to make cutting-edge NLP easier to use for everyone.
+
+🤗 Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
+then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
+can be modified to enable quick research experiments.
+
+Why should I use transformers?
+
+1. Easy-to-use state-of-the-art models:
+ - High performance on NLU and NLG tasks.
+ - Low barrier to entry for educators and practitioners.
+ - Few user-facing abstractions with just three classes to learn.
+ - A unified API for using all our pretrained models.
+ - Lower compute costs, smaller carbon footprint:
+
+2. Researchers can share trained models instead of always retraining.
+ - Practitioners can reduce compute time and production costs.
+ - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.
+
+3. Choose the right framework for every part of a model's lifetime:
+ - Train state-of-the-art models in 3 lines of code.
+ - Move a single model between TF2.0/PyTorch frameworks at will.
+ - Seamlessly pick the right framework for training, evaluation and production.
+
+4. Easily customize a model or an example to your needs:
+ - We provide examples for each architecture to reproduce the results published by its original authors.
+ - Model internals are exposed as consistently as possible.
+ - Model files can be used independently of the library for quick experiments.
+
+🤗 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.
+"""
+
+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.
+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.
+puis de les partager avec la communauté sur notre hub de modèles. En même temps, chaque module python définissant une architecture est entièrement autonome et peut être modifié pour permettre des expériences de recherche rapides.
+peut être modifié pour permettre des expériences de recherche rapides.
+
+Pourquoi devrais-je utiliser des transformateurs ?
+
+1. Des modèles de pointe faciles à utiliser :
+ - Haute performance sur les tâches NLU et NLG.
+ - Faible barrière à l'entrée pour les éducateurs et les praticiens.
+ - Peu d'abstractions pour l'utilisateur avec seulement trois classes à apprendre.
+ - Une API unifiée pour utiliser tous nos modèles pré-entraînés.
+ - Des coûts de calcul plus faibles, une empreinte carbone réduite :
+
+2. Les chercheurs peuvent partager les modèles formés au lieu de toujours les reformer.
+ - Les praticiens peuvent réduire le temps de calcul et les coûts de production.
+ - Des dizaines d'architectures avec plus de 10 000 modèles pré-formés, certains dans plus de 100 langues.
+
+3. Choisissez le cadre approprié pour chaque étape de la vie d'un modèle :
+ - Entraînez des modèles de pointe en 3 lignes de code.
+ - Déplacez un seul modèle entre les frameworks TF2.0/PyTorch à volonté.
+ - Choisissez de manière transparente le bon framework pour l'entraînement, l'évaluation et la production.
+
+4. Adaptez facilement un modèle ou un exemple à vos besoins :
+ - Nous fournissons des exemples pour chaque architecture afin de reproduire les résultats publiés par ses auteurs originaux.
+ - Les éléments internes des modèles sont exposés de manière aussi cohérente que possible.
+ - Les fichiers de modèles peuvent être utilisés indépendamment de la bibliothèque pour des expériences rapides.
+
+🤗 Transformers s'appuie sur les trois bibliothèques d'apprentissage profond les plus populaires (Jax, PyTorch et TensorFlow) avec une intégration parfaite
+entre elles. Il est simple d'entraîner vos modèles avec l'une avant de les charger pour l'inférence avec l'autre.
+"""
+question_answerer(question=question, context=long_context)
+```
+
+```python out
+{'score': 0.97149,
+ 'start': 1892,
+ 'end': 1919,
+ 'answer': 'Jax, PyTorch and TensorFlow'}
+```
+
+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 *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'}
+
+```py
+from transformers import AutoTokenizer, AutoModelForQuestionAnswering
+
+model_checkpoint = "distilbert-base-cased-distilled-squad"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
+
+inputs = tokenizer(question, context, return_tensors="pt")
+outputs = model(**inputs)
+```
+
+{:else}
+
+```py
+from transformers import AutoTokenizer, TFAutoModelForQuestionAnswering
+
+model_checkpoint = "distilbert-base-cased-distilled-squad"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
+
+inputs = tokenizer(question, context, return_tensors="tf")
+outputs = model(**inputs)
+```
+
+{/if}
+
+Notez que nous tokenizons la question et le contexte comme une paire, la question en premier.
+
+
+
+
+
+
+Les modèles de réponse aux questions fonctionnent un peu différemment des modèles que nous avons vus jusqu'à présent. En utilisant l'image ci-dessus comme exemple, le modèle a été entraîné à prédire l'index du *token* de début de la réponse (ici 21) et l'index du *token* où la réponse se termine (ici 24). C'est pourquoi ces modèles ne retournent pas un tenseur de logits mais deux : un pour les logits correspondant au *token* de début de la réponse, et un pour les logits correspondant au *token* de fin de la réponse. Puisque dans ce cas nous n'avons qu'une seule entrée contenant 66 *tokens*, nous obtenons :
+
+```py
+start_logits = outputs.start_logits
+end_logits = outputs.end_logits
+print(start_logits.shape, end_logits.shape)
+```
+
+{#if fw === 'pt'}
+
+```python out
+torch.Size([1, 66]) torch.Size([1, 66])
+```
+
+{:else}
+
+```python out
+(1, 66) (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.
+
+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` :
+
+{#if fw === 'pt'}
+
+```py
+import torch
+
+sequence_ids = inputs.sequence_ids()
+# Masque tout, sauf les tokens du contexte
+mask = [i != 1 for i in sequence_ids]
+# Démasquer le token [CLS]
+mask[0] = False
+mask = torch.tensor(mask)[None]
+
+start_logits[mask] = -10000
+end_logits[mask] = -10000
+```
+
+{:else}
+
+```py
+import tensorflow as tf
+
+sequence_ids = inputs.sequence_ids()
+# Masque tout, sauf les tokens du contexte
+mask = [i != 1 for i in sequence_ids]
+# Démasquer le token [CLS]
+mask[0] = False
+mask = tf.constant(mask)[None]
+
+start_logits = tf.where(mask, -10000, start_logits)
+end_logits = tf.where(mask, -10000, end_logits)
+```
+
+{/if}
+
+Maintenant que nous avons correctement masqué les logits correspondant aux positions que nous ne voulons pas prédire, nous pouvons appliquer la softmax :
+
+{#if fw === 'pt'}
+
+```py
+start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)[0]
+end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)[0]
+```
+
+{:else}
+
+```py
+start_probabilities = tf.math.softmax(start_logits, axis=-1)[0].numpy()
+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é.
+
+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 :
+
+$$\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]$$
+
+Ainsi, pour calculer tous les scores, il suffit de calculer tous les produits \\(\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]\\) où `start_index <= end_index`.
+
+Calculons d'abord tous les produits possibles :
+
+```py
+scores = start_probabilities[:, None] * end_probabilities[None, :]
+```
+
+{#if fw === 'pt'}
+
+Ensuite, nous masquerons les valeurs où `start_index > end_index` en les mettant à `0` (les autres probabilités sont toutes des nombres positifs). La fonction `torch.triu()` renvoie la partie triangulaire supérieure du tenseur 2D passé en argument, elle fera donc ce masquage pour nous :
+
+```py
+scores = torch.triu(scores)
+```
+
+{:else}
+
+Ensuite, nous masquerons les valeurs où `start_index > end_index` en les mettant à `0` (les autres probabilités sont toutes des nombres positifs). La fonction `np.triu()` renvoie la partie triangulaire supérieure du tenseur 2D passé en argument, elle fera donc ce masquage pour nous :
+
+```py
+scores = np.triu(scores)
+```
+
+{/if}
+
+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()
+start_index = max_index // scores.shape[1]
+end_index = max_index % scores.shape[1]
+print(scores[start_index, end_index])
+```
+
+Nous n'avons pas encore tout à fait terminé, mais au moins nous avons déjà le score correct pour la réponse (vous pouvez le vérifier en le comparant au premier résultat de la section précédente) :
+
+```python out
+0.97773
+```
+
+
+
+✏️ **Essayez !** Calculez les indices de début et de fin pour les cinq réponses les plus probables.
+
+
+
+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)
+offsets = inputs_with_offsets["offset_mapping"]
+
+start_char, _ = offsets[start_index]
+_, end_char = offsets[end_index]
+answer = context[start_char:end_char]
+```
+
+Il ne nous reste plus qu'à tout formater pour obtenir notre résultat :
+
+```py
+result = {
+ "answer": answer,
+ "start": start_char,
+ "end": end_char,
+ "score": scores[start_index, end_index],
+}
+print(result)
+```
+
+```python out
+{'answer': 'Jax, PyTorch and TensorFlow',
+ 'start': 78,
+ 'end': 105,
+ 'score': 0.97773}
+```
+
+Super ! C'est la même chose que dans notre premier exemple !
+
+
+
+✏️ **Essayez !** Utilisez les meilleurs scores que vous avez calculés précédemment pour afficher les cinq réponses les plus probables. Pour vérifier vos résultats, retournez au premier pipeline et passez dans `top_k=5` lorsque vous l'appelez.
+
+
+
+## Gestion des contextes longs
+
+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)
+print(len(inputs["input_ids"]))
+```
+
+```python out
+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 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")
+print(tokenizer.decode(inputs["input_ids"]))
+```
+
+```python out
+"""
+[CLS] Which deep learning libraries back [UNK] Transformers? [SEP] [UNK] Transformers : State of the Art NLP
+
+[UNK] Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
+question answering, summarization, translation, text generation and more in over 100 languages.
+Its aim is to make cutting-edge NLP easier to use for everyone.
+
+[UNK] Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
+then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
+can be modified to enable quick research experiments.
+
+Why should I use transformers?
+
+1. Easy-to-use state-of-the-art models:
+ - High performance on NLU and NLG tasks.
+ - Low barrier to entry for educators and practitioners.
+ - Few user-facing abstractions with just three classes to learn.
+ - A unified API for using all our pretrained models.
+ - Lower compute costs, smaller carbon footprint:
+
+2. Researchers can share trained models instead of always retraining.
+ - Practitioners can reduce compute time and production costs.
+ - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.
+
+3. Choose the right framework for every part of a model's lifetime:
+ - Train state-of-the-art models in 3 lines of code.
+ - Move a single model between TF2.0/PyTorch frameworks at will.
+ - Seamlessly pick the right framework for training, evaluation and production.
+
+4. Easily customize a model or an example to your needs:
+ - We provide examples for each architecture to reproduce the results published by its original authors.
+ - Model internal [SEP]
+"""
+
+"""
+[CLS] Quelles sont les bibliothèques d'apprentissage profond qui soutiennent [UNK] Transformers ? [SEP] [UNK] Transformers : l'état de l'art du NLP
+
+[UNK] 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é, la traduction, la génération de textes, etc,
+la réponse à des questions, le résumé, 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 tous.
+
+Transformers [UNK] 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.
+puis de les partager avec la communauté sur notre hub de modèles. En même temps, chaque module python définissant une architecture est entièrement autonome et peut être modifié pour permettre des expériences de recherche rapides.
+peut être modifié pour permettre des expériences de recherche rapides.
+
+Pourquoi devrais-je utiliser des transformateurs ?
+
+1. Des modèles de pointe faciles à utiliser :
+ - Haute performance sur les tâches NLU et NLG.
+ - Faible barrière à l'entrée pour les éducateurs et les praticiens.
+ - Peu d'abstractions pour l'utilisateur avec seulement trois classes à apprendre.
+ - Une API unifiée pour utiliser tous nos modèles pré-entraînés.
+ - Des coûts de calcul plus faibles, une empreinte carbone réduite :
+
+2. Les chercheurs peuvent partager les modèles formés au lieu de toujours les reformer.
+ - Les praticiens peuvent réduire le temps de calcul et les coûts de production.
+ - Des dizaines d'architectures avec plus de 10 000 modèles pré-formés, certains dans plus de 100 langues.
+
+3. Choisissez le cadre approprié pour chaque étape de la vie d'un modèle :
+ - Entraînez des modèles de pointe en 3 lignes de code.
+ - Déplacez un seul modèle entre les frameworks TF2.0/PyTorch à volonté.
+ - Choisissez de manière transparente le bon framework pour l'entraînement, l'évaluation et la production.
+
+4. Adaptez facilement un modèle ou un exemple à vos besoins :
+ - Nous fournissons des exemples pour chaque architecture afin de reproduire les résultats publiés par ses auteurs originaux.
+ - Modèle interne [SEP]
+"""
+```
+
+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."
+inputs = tokenizer(
+ sentence, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
+)
+
+for ids in inputs["input_ids"]:
+ print(tokenizer.decode(ids))
+```
+
+```python out
+'[CLS] This sentence is not [SEP]'
+'[CLS] is not too long [SEP]'
+'[CLS] too long but we [SEP]'
+'[CLS] but we are going [SEP]'
+'[CLS] are going to split [SEP]'
+'[CLS] to split it anyway [SEP]'
+'[CLS] it anyway. [SEP]'
+```
+
+Comme on peut le voir, la phrase a été découpée en morceaux de telle sorte que chaque entrée dans `inputs["input_ids"]` a au maximum 6 *tokens* (il faudrait ajouter du *padding* pour que la dernière entrée ait la même taille que les autres) et il y a un chevauchement de 2 *tokens* entre chacune des entrées.
+
+Regardons de plus près le résultat de la tokénisation :
+
+```py
+print(inputs.keys())
+```
+
+```python out
+dict_keys(['input_ids', 'attention_mask', 'overflow_to_sample_mapping'])
+```
+
+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"])
+```
+
+```python out
+[0, 0, 0, 0, 0, 0, 0]
+```
+
+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.
+]
+inputs = tokenizer(
+ sentences, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
+)
+
+print(inputs["overflow_to_sample_mapping"])
+```
+
+nous donne :
+
+```python out
+[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.
+
+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(
+ question,
+ long_context,
+ stride=128,
+ max_length=384,
+ padding="longest",
+ truncation="only_second",
+ return_overflowing_tokens=True,
+ return_offsets_mapping=True,
+)
+```
+
+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'}
+
+```py
+_ = inputs.pop("overflow_to_sample_mapping")
+offsets = inputs.pop("offset_mapping")
+
+inputs = inputs.convert_to_tensors("pt")
+print(inputs["input_ids"].shape)
+```
+
+```python out
+torch.Size([2, 384])
+```
+
+{:else}
+
+```py
+_ = inputs.pop("overflow_to_sample_mapping")
+offsets = inputs.pop("offset_mapping")
+
+inputs = inputs.convert_to_tensors("tf")
+print(inputs["input_ids"].shape)
+```
+
+```python out
+(2, 384)
+```
+
+{/if}
+
+Notre contexte long a été divisé en deux, ce qui signifie qu'après avoir traversé notre modèle, nous aurons deux ensembles de logits de début et de fin :
+
+```py
+outputs = model(**inputs)
+
+start_logits = outputs.start_logits
+end_logits = outputs.end_logits
+print(start_logits.shape, end_logits.shape)
+```
+
+{#if fw === 'pt'}
+
+```python out
+torch.Size([2, 384]) torch.Size([2, 384])
+```
+
+{:else}
+
+```python out
+(2, 384) (2, 384)
+```
+
+{/if}
+
+Comme précédemment, nous masquons d'abord les *tokens* qui ne font pas partie du contexte avant de prendre le softmax. Nous masquons également tous les *tokens* de *padding* (tels que signalés par le masque d'attention) :
+
+{#if fw === 'pt'}
+
+```py
+sequence_ids = inputs.sequence_ids()
+# Masque tout, sauf les tokens du contexte
+mask = [i != 1 for i in sequence_ids]
+# Démasquer le jeton [CLS]
+mask[0] = False
+# Masquer tous les tokens [PAD]
+mask = torch.logical_or(torch.tensor(mask)[None], (inputs["attention_mask"] == 0))
+
+start_logits[mask] = -10000
+end_logits[mask] = -10000
+```
+
+{:else}
+
+```py
+sequence_ids = inputs.sequence_ids()
+# Masque tout, sauf les tokens du contexte
+mask = [i != 1 for i in sequence_ids]
+# Démasquer le jeton [CLS]
+mask[0] = False
+# Masquer tous les tokens [PAD]
+mask = tf.math.logical_or(tf.constant(mask)[None], inputs["attention_mask"] == 0)
+
+start_logits = tf.where(mask, -10000, start_logits)
+end_logits = tf.where(mask, -10000, end_logits)
+```
+
+{/if}
+
+Ensuite, nous pouvons utiliser la fonction softmax pour convertir nos logits en probabilités :
+
+{#if fw === 'pt'}
+
+```py
+start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)
+end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)
+```
+
+{:else}
+
+```py
+start_probabilities = tf.math.softmax(start_logits, axis=-1).numpy()
+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 morceaux. Nous attribuons un score à tous les espaces de réponse possibles puis nous prenons l'espace ayant le meilleur score :
+
+{#if fw === 'pt'}
+
+```py
+candidates = []
+for start_probs, end_probs in zip(start_probabilities, end_probabilities):
+ scores = start_probs[:, None] * end_probs[None, :]
+ idx = torch.triu(scores).argmax().item()
+
+ start_idx = idx // scores.shape[0]
+ end_idx = idx % scores.shape[0]
+ score = scores[start_idx, end_idx].item()
+ candidates.append((start_idx, end_idx, score))
+
+print(candidates)
+```
+
+{:else}
+
+```py
+candidates = []
+for start_probs, end_probs in zip(start_probabilities, end_probabilities):
+ scores = start_probs[:, None] * end_probs[None, :]
+ idx = np.triu(scores).argmax().item()
+
+ start_idx = idx // scores.shape[0]
+ end_idx = idx % scores.shape[0]
+ score = scores[start_idx, end_idx].item()
+ candidates.append((start_idx, end_idx, score))
+
+print(candidates)
+```
+
+{/if}
+
+```python out
+[(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 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).
+
+
+
+✏️ **Essayez !** Adaptez le code ci-dessus pour renvoyer les scores et les étendues des cinq réponses les plus probables (au total, pas par morceau).
+
+
+
+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):
+ start_token, end_token, score = candidate
+ start_char, _ = offset[start_token]
+ _, end_char = offset[end_token]
+ answer = long_context[start_char:end_char]
+ result = {"answer": answer, "start": start_char, "end": end_char, "score": score}
+ print(result)
+```
+
+```python out
+{'answer': '\n🤗 Transformers: State of the Art NLP', 'start': 0, 'end': 37, 'score': 0.33867}
+{'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 !
+
+
+
+✏️ **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.
+
+
+
Ceci conclut notre plongée en profondeur dans les capacités du *tokenizer*. Nous mettrons à nouveau tout cela en pratique dans le prochain chapitre, lorsque nous vous montrerons comment *finetuner* un modèle sur une série de tâches NLP courantes.
\ No newline at end of file
diff --git a/chapters/fr/chapter6/4.mdx b/chapters/fr/chapter6/4.mdx
index 0d8ddd4ba..1438e7b6f 100644
--- a/chapters/fr/chapter6/4.mdx
+++ b/chapters/fr/chapter6/4.mdx
@@ -1,123 +1,123 @@
-# Normalisation et prétokenization
-
-
-
-Avant de nous plonger plus profondément dans les trois algorithmes de tokénisation en sous-mots les plus courants utilisés avec les *transformers* (*Byte-Pair Encoding* (BPE), *WordPiece* et *Unigram*), nous allons d'abord examiner le prétraitement que chaque *tokenizer* applique au texte. Voici un aperçu de haut niveau des étapes du pipeline de tokenisation :
-
-
-
-
-
-
-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 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* :
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
-print(type(tokenizer.backend_tokenizer))
-```
-
-```python out
-
-```
-
-L'attribut `normalizer` de l'objet `tokenizer` possède une méthode `normalize_str()` que nous pouvons utiliser pour voir comment la normalisation est effectuée :
-
-```py
-print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
-```
-
-```python out
-'hello how are u?'
-```
-
-Dans cet exemple, puisque nous avons choisi le *checkpoint* `bert-base-uncased`, la normalisation a mis le texte en minuscule et supprimé les accents.
-
-
-
-✏️ **Essayez !** Chargez un *tokenizer* depuis le *checkpoint* `bert-base-cased` et passez-lui le même exemple. Quelles sont les principales différences que vous pouvez voir entre les versions casée et non casée du *tokenizer* ?
-
-
-
-## 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.
-
-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?")
-```
-
-```python out
-[('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 *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 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?")
-```
-
-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)),
- ('?', (19, 20))]
-```
-
-Notez également que, contrairement au *tokenizer* de BERT, ce *tokenizer* n'ignore pas les doubles espaces.
-
-Pour un dernier exemple, regardons le *tokenizer* du 5, qui est basé sur l'algorithme SentencePiece :
-
-```py
-tokenizer = AutoTokenizer.from_pretrained("t5-small")
-tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
-```
-
-```python out
-[('▁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* 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.
-
-## SentencePiece
-
-[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 *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.
-
-## Vue d'ensemble des algorithmes
-
-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
-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 le BPE !
+# Normalisation et prétokenization
+
+
+
+Avant de nous plonger plus profondément dans les trois algorithmes de tokénisation en sous-mots les plus courants utilisés avec les *transformers* (*Byte-Pair Encoding* (BPE), *WordPiece* et *Unigram*), nous allons d'abord examiner le prétraitement que chaque *tokenizer* applique au texte. Voici un aperçu de haut niveau des étapes du pipeline de tokenisation :
+
+
+
+
+
+
+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 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* :
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
+print(type(tokenizer.backend_tokenizer))
+```
+
+```python out
+
+```
+
+L'attribut `normalizer` de l'objet `tokenizer` possède une méthode `normalize_str()` que nous pouvons utiliser pour voir comment la normalisation est effectuée :
+
+```py
+print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
+```
+
+```python out
+'hello how are u?'
+```
+
+Dans cet exemple, puisque nous avons choisi le *checkpoint* `bert-base-uncased`, la normalisation a mis le texte en minuscule et supprimé les accents.
+
+
+
+✏️ **Essayez !** Chargez un *tokenizer* depuis le *checkpoint* `bert-base-cased` et passez-lui le même exemple. Quelles sont les principales différences que vous pouvez voir entre les versions casée et non casée du *tokenizer* ?
+
+
+
+## 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.
+
+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?")
+```
+
+```python out
+[('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 *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 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?")
+```
+
+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)),
+ ('?', (19, 20))]
+```
+
+Notez également que, contrairement au *tokenizer* de BERT, ce *tokenizer* n'ignore pas les doubles espaces.
+
+Pour un dernier exemple, regardons le *tokenizer* du 5, qui est basé sur l'algorithme SentencePiece :
+
+```py
+tokenizer = AutoTokenizer.from_pretrained("t5-small")
+tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
+```
+
+```python out
+[('▁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* 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.
+
+## SentencePiece
+
+[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 *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.
+
+## Vue d'ensemble des algorithmes
+
+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
+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 le BPE !
diff --git a/chapters/fr/chapter6/5.mdx b/chapters/fr/chapter6/5.mdx
index 410d75af8..ee2ad8a52 100644
--- a/chapters/fr/chapter6/5.mdx
+++ b/chapters/fr/chapter6/5.mdx
@@ -1,364 +1,364 @@
-# Tokénisation Byte-Pair Encoding
-
-
-
-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.
-
-
-
-
-
-💡 Cette section couvre le BPE 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 tokenisation.
-
-
-
-## Algorithme d'entraînement
-
-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"
-```
-
-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 *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 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. 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 :
-
-```
-("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* :
-
-```
-("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 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"` 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. 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"`. 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"]
-Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)
-```
-
-Et nous continuons ainsi jusqu'à ce que nous atteignions la taille de vocabulaire souhaitée.
-
-
-
-✏️ **A votre tour !** A votre avis, quelle sera la prochaine règle de fusion ?
-
-
-
-## Algorithme de tokenisation
-
-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
-3. Découpage des mots en caractères individuels
-4. Application des règles de fusion apprises dans l'ordre sur ces divisions.
-
-Prenons l'exemple que nous avons utilisé pendant l'entraînement, avec les trois règles de fusion apprises :
-
-```
-("u", "g") -> "ug"
-("u", "n") -> "un"
-("h", "ug") -> "hug"
-```
-
-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 » (détacher en français) sera tokenized ?
-
-
-
-## 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.
-
-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.",
- # 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 celui du GPT-2), nous utiliserons le *tokenizer* `gpt2` pour la prétokénisation :
-
-```python
-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 :
-
-```python
-from collections import defaultdict
-
-word_freqs = defaultdict(int)
-
-for text in corpus:
- words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
- new_words = [word for word, offset in words_with_offsets]
- for word in new_words:
- word_freqs[word] += 1
-
-print(word_freqs)
-```
-
-```python out
-defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1,
- 'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1,
- 'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1,
- 'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1})
-```
-
-L'étape suivante consiste à calculer le vocabulaire de base, formé par tous les caractères utilisés dans le corpus :
-
-```python
-alphabet = []
-
-for word in word_freqs.keys():
- for letter in word:
- if letter not in alphabet:
- alphabet.append(letter)
-alphabet.sort()
-
-print(alphabet)
-```
-
-```python out
-[ ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's',
- '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 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 :
-
-```python
-splits = {word: [c for c in word] for word in word_freqs.keys()}
-```
-
-Maintenant que nous sommes prêts pour l'entraînement, écrivons une fonction qui calcule la fréquence de chaque paire. Nous devrons l'utiliser à chaque étape de l'entraînement :
-
-```python
-def compute_pair_freqs(splits):
- pair_freqs = defaultdict(int)
- for word, freq in word_freqs.items():
- split = splits[word]
- if len(split) == 1:
- continue
- for i in range(len(split) - 1):
- pair = (split[i], split[i + 1])
- pair_freqs[pair] += freq
- return pair_freqs
-```
-
-Jetons un coup d'œil à une partie de ce dictionnaire après les premières divisions :
-
-```python
-pair_freqs = compute_pair_freqs(splits)
-
-for i, key in enumerate(pair_freqs.keys()):
- print(f"{key}: {pair_freqs[key]}")
- if i >= 5:
- break
-```
-
-```python out
-('T', 'h'): 3
-('h', 'i'): 3
-('i', 's'): 5
-('Ġ', 'i'): 2
-('Ġ', 't'): 7
-('t', 'h'): 3
-```
-
-Maintenant, trouver la paire la plus fréquente ne demande qu'une rapide boucle :
-
-```python
-best_pair = ""
-max_freq = None
-
-for pair, freq in pair_freqs.items():
- if max_freq is None or max_freq < freq:
- best_pair = pair
- max_freq = freq
-
-print(best_pair, max_freq)
-```
-
-```python out
-('Ġ', 't') 7
-```
-
-Donc la première fusion à apprendre est `('Ġ', 't') -> 'Ġt'`, et on ajoute `'Ġt'` au vocabulaire :
-
-```python
-merges = {("Ġ", "t"): "Ġt"}
-vocab.append("Ġt")
-```
-
-Pour continuer, nous devons appliquer cette fusion dans notre dictionnaire `splits`. Écrivons une autre fonction pour cela :
-
-```python
-def merge_pair(a, b, splits):
- for word in word_freqs:
- split = splits[word]
- if len(split) == 1:
- continue
-
- i = 0
- while i < len(split) - 1:
- if split[i] == a and split[i + 1] == b:
- split = split[:i] + [a + b] + split[i + 2 :]
- else:
- i += 1
- splits[word] = split
- return splits
-```
-
-Et nous pouvons regarder le résultat de la première fusion :
-
-```py
-splits = merge_pair("Ġ", "t", splits)
-print(splits["Ġtrained"])
-```
-
-```python out
-['Ġt', 'r', 'a', 'i', 'n', 'e', 'd']
-```
-
-Maintenant, nous avons tout ce dont nous avons besoin pour boucler jusqu'à ce que nous ayons appris toutes les fusions que nous voulons. Visons une taille de vocabulaire de 50 :
-
-```python
-vocab_size = 50
-
-while len(vocab) < vocab_size:
- pair_freqs = compute_pair_freqs(splits)
- best_pair = ""
- max_freq = None
- for pair, freq in pair_freqs.items():
- if max_freq is None or max_freq < freq:
- best_pair = pair
- max_freq = freq
- splits = merge_pair(*best_pair, splits)
- merges[best_pair] = best_pair[0] + best_pair[1]
- 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) :
-
-```py
-print(merges)
-```
-
-```python out
-{('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en',
- ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok',
- ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe',
- ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'}
-```
-
-Et le vocabulaire est composé du *token* spécial, de l'alphabet initial, et de tous les résultats des fusions :
-
-```py
-print(vocab)
-```
-
-```python out
-['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o',
- 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se',
- 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni']
-```
-
-
-
-💡 Utiliser `train_new_from_iterator()` sur le même corpus ne donnera pas exactement le même vocabulaire. C'est parce que lorsqu'il y a un choix de la paire la plus fréquente, nous avons sélectionné la première rencontrée, alors que la bibliothèque 🤗 *Tokenizers* sélectionne la première en fonction de ses identifiants internes.
-
-
-
-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):
- pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
- pre_tokenized_text = [word for word, offset in pre_tokenize_result]
- splits = [[l for l in word] for word in pre_tokenized_text]
- for pair, merge in merges.items():
- for idx, split in enumerate(splits):
- i = 0
- while i < len(split) - 1:
- if split[i] == pair[0] and split[i + 1] == pair[1]:
- split = split[:i] + [merge] + split[i + 2 :]
- else:
- i += 1
- splits[idx] = split
-
- return sum(splits, [])
-```
-
-Nous pouvons essayer cela sur n'importe quel texte composé de caractères de l'alphabet :
-
-```py
-tokenize("This is not a token.")
-```
-
-```python out
-['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġ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 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é.
-
-
-
+# Tokénisation Byte-Pair Encoding
+
+
+
+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.
+
+
+
+
+
+💡 Cette section couvre le BPE 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 tokenisation.
+
+
+
+## Algorithme d'entraînement
+
+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"
+```
+
+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 *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 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. 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 :
+
+```
+("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* :
+
+```
+("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 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"` 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. 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"`. 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"]
+Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)
+```
+
+Et nous continuons ainsi jusqu'à ce que nous atteignions la taille de vocabulaire souhaitée.
+
+
+
+✏️ **A votre tour !** A votre avis, quelle sera la prochaine règle de fusion ?
+
+
+
+## Algorithme de tokenisation
+
+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
+3. Découpage des mots en caractères individuels
+4. Application des règles de fusion apprises dans l'ordre sur ces divisions.
+
+Prenons l'exemple que nous avons utilisé pendant l'entraînement, avec les trois règles de fusion apprises :
+
+```
+("u", "g") -> "ug"
+("u", "n") -> "un"
+("h", "ug") -> "hug"
+```
+
+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 » (détacher en français) sera tokenized ?
+
+
+
+## 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.
+
+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.",
+ # 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 celui du GPT-2), nous utiliserons le *tokenizer* `gpt2` pour la prétokénisation :
+
+```python
+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 :
+
+```python
+from collections import defaultdict
+
+word_freqs = defaultdict(int)
+
+for text in corpus:
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ new_words = [word for word, offset in words_with_offsets]
+ for word in new_words:
+ word_freqs[word] += 1
+
+print(word_freqs)
+```
+
+```python out
+defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1,
+ 'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1,
+ 'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1,
+ 'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1})
+```
+
+L'étape suivante consiste à calculer le vocabulaire de base, formé par tous les caractères utilisés dans le corpus :
+
+```python
+alphabet = []
+
+for word in word_freqs.keys():
+ for letter in word:
+ if letter not in alphabet:
+ alphabet.append(letter)
+alphabet.sort()
+
+print(alphabet)
+```
+
+```python out
+[ ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's',
+ '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 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 :
+
+```python
+splits = {word: [c for c in word] for word in word_freqs.keys()}
+```
+
+Maintenant que nous sommes prêts pour l'entraînement, écrivons une fonction qui calcule la fréquence de chaque paire. Nous devrons l'utiliser à chaque étape de l'entraînement :
+
+```python
+def compute_pair_freqs(splits):
+ pair_freqs = defaultdict(int)
+ for word, freq in word_freqs.items():
+ split = splits[word]
+ if len(split) == 1:
+ continue
+ for i in range(len(split) - 1):
+ pair = (split[i], split[i + 1])
+ pair_freqs[pair] += freq
+ return pair_freqs
+```
+
+Jetons un coup d'œil à une partie de ce dictionnaire après les premières divisions :
+
+```python
+pair_freqs = compute_pair_freqs(splits)
+
+for i, key in enumerate(pair_freqs.keys()):
+ print(f"{key}: {pair_freqs[key]}")
+ if i >= 5:
+ break
+```
+
+```python out
+('T', 'h'): 3
+('h', 'i'): 3
+('i', 's'): 5
+('Ġ', 'i'): 2
+('Ġ', 't'): 7
+('t', 'h'): 3
+```
+
+Maintenant, trouver la paire la plus fréquente ne demande qu'une rapide boucle :
+
+```python
+best_pair = ""
+max_freq = None
+
+for pair, freq in pair_freqs.items():
+ if max_freq is None or max_freq < freq:
+ best_pair = pair
+ max_freq = freq
+
+print(best_pair, max_freq)
+```
+
+```python out
+('Ġ', 't') 7
+```
+
+Donc la première fusion à apprendre est `('Ġ', 't') -> 'Ġt'`, et on ajoute `'Ġt'` au vocabulaire :
+
+```python
+merges = {("Ġ", "t"): "Ġt"}
+vocab.append("Ġt")
+```
+
+Pour continuer, nous devons appliquer cette fusion dans notre dictionnaire `splits`. Écrivons une autre fonction pour cela :
+
+```python
+def merge_pair(a, b, splits):
+ for word in word_freqs:
+ split = splits[word]
+ if len(split) == 1:
+ continue
+
+ i = 0
+ while i < len(split) - 1:
+ if split[i] == a and split[i + 1] == b:
+ split = split[:i] + [a + b] + split[i + 2 :]
+ else:
+ i += 1
+ splits[word] = split
+ return splits
+```
+
+Et nous pouvons regarder le résultat de la première fusion :
+
+```py
+splits = merge_pair("Ġ", "t", splits)
+print(splits["Ġtrained"])
+```
+
+```python out
+['Ġt', 'r', 'a', 'i', 'n', 'e', 'd']
+```
+
+Maintenant, nous avons tout ce dont nous avons besoin pour boucler jusqu'à ce que nous ayons appris toutes les fusions que nous voulons. Visons une taille de vocabulaire de 50 :
+
+```python
+vocab_size = 50
+
+while len(vocab) < vocab_size:
+ pair_freqs = compute_pair_freqs(splits)
+ best_pair = ""
+ max_freq = None
+ for pair, freq in pair_freqs.items():
+ if max_freq is None or max_freq < freq:
+ best_pair = pair
+ max_freq = freq
+ splits = merge_pair(*best_pair, splits)
+ merges[best_pair] = best_pair[0] + best_pair[1]
+ 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) :
+
+```py
+print(merges)
+```
+
+```python out
+{('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en',
+ ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok',
+ ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe',
+ ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'}
+```
+
+Et le vocabulaire est composé du *token* spécial, de l'alphabet initial, et de tous les résultats des fusions :
+
+```py
+print(vocab)
+```
+
+```python out
+['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se',
+ 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni']
+```
+
+
+
+💡 Utiliser `train_new_from_iterator()` sur le même corpus ne donnera pas exactement le même vocabulaire. C'est parce que lorsqu'il y a un choix de la paire la plus fréquente, nous avons sélectionné la première rencontrée, alors que la bibliothèque 🤗 *Tokenizers* sélectionne la première en fonction de ses identifiants internes.
+
+
+
+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):
+ pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ pre_tokenized_text = [word for word, offset in pre_tokenize_result]
+ splits = [[l for l in word] for word in pre_tokenized_text]
+ for pair, merge in merges.items():
+ for idx, split in enumerate(splits):
+ i = 0
+ while i < len(split) - 1:
+ if split[i] == pair[0] and split[i + 1] == pair[1]:
+ split = split[:i] + [merge] + split[i + 2 :]
+ else:
+ i += 1
+ splits[idx] = split
+
+ return sum(splits, [])
+```
+
+Nous pouvons essayer cela sur n'importe quel texte composé de caractères de l'alphabet :
+
+```py
+tokenize("This is not a token.")
+```
+
+```python out
+['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġ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 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 ! 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 e854edde1..44206d994 100644
--- a/chapters/fr/chapter6/6.mdx
+++ b/chapters/fr/chapter6/6.mdx
@@ -1,378 +1,378 @@
-# 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 *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.
-
-
-
-## Algorithme d'entraînement
-
-
-
-⚠️ 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, `"word"` est divisé comme ceci :
-
-```
-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 :
-
-$$\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 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 :
-
-```
-("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
-```
-
-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)
-```
-
-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 :
-
-```
-Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]
-Corpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)
-```
-
-À ce stade, `" ##u "` est dans toutes les paires possibles, donc elles finissent toutes par avoir le même score. Disons que dans ce cas, la première paire est fusionnée, donc `("h", "##u") -> "hu"`. Cela nous amène à :
-
-```
-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). Ainsi la première paire avec le plus grand score est fusionnée :
-
-```
-Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]
-Corpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
-```
-
-et nous continuons ainsi jusqu'à ce que nous atteignions la taille de vocabulaire souhaitée.
-
-
-
-✏️ **A votre tour !** Quelle sera la prochaine règle de fusion ?
-
-
-
-## Algorithme de tokenisation
-
-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"`.
-
-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.
-
-
-
-✏️ **A votre tour !** Comment le mot `"pugs"` sera-t-il tokenisé ?
-
-
-
-## 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.
-
-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.
-]
-```
-
-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
-
-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 :
-
-```python
-from collections import defaultdict
-
-word_freqs = defaultdict(int)
-for text in corpus:
- words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
- new_words = [word for word, offset in words_with_offsets]
- for word in new_words:
- word_freqs[word] += 1
-
-word_freqs
-```
-
-```python out
-defaultdict(
- int, {'This': 3, 'is': 2, 'the': 1, 'Hugging': 1, 'Face': 1, 'Course': 1, '.': 4, 'chapter': 1, 'about': 1,
- 'tokenization': 1, 'section': 1, 'shows': 1, 'several': 1, 'tokenizer': 1, 'algorithms': 1, 'Hopefully': 1,
- ',': 1, 'you': 1, 'will': 1, 'be': 1, 'able': 1, 'to': 1, 'understand': 1, 'how': 1, 'they': 1, 'are': 1,
- 'trained': 1, 'and': 1, 'generate': 1, 'tokens': 1})
-```
-
-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 = []
-for word in word_freqs.keys():
- if word[0] not in alphabet:
- alphabet.append(word[0])
- for letter in word[1:]:
- if f"##{letter}" not in alphabet:
- alphabet.append(f"##{letter}")
-
-alphabet.sort()
-alphabet
-
-print(alphabet)
-```
-
-```python out
-['##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', '##l', '##m', '##n', '##o', '##p', '##r', '##s',
- '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u',
- '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]"]` :
-
-```python
-vocab = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + alphabet.copy()
-```
-
-Ensuite, nous devons diviser chaque mot, avec toutes les lettres qui ne sont pas les premières préfixées par `##` :
-
-```python
-splits = {
- word: [c if i == 0 else f"##{c}" for i, c in enumerate(word)]
- for word in word_freqs.keys()
-}
-```
-
-Maintenant que nous sommes prêts pour l'entraînement, écrivons une fonction qui calcule le score de chaque paire. Nous devrons l'utiliser à chaque étape de l'entraînement :
-
-```python
-def compute_pair_scores(splits):
- letter_freqs = defaultdict(int)
- pair_freqs = defaultdict(int)
- for word, freq in word_freqs.items():
- split = splits[word]
- if len(split) == 1:
- letter_freqs[split[0]] += freq
- continue
- for i in range(len(split) - 1):
- pair = (split[i], split[i + 1])
- letter_freqs[split[i]] += freq
- pair_freqs[pair] += freq
- letter_freqs[split[-1]] += freq
-
- scores = {
- pair: freq / (letter_freqs[pair[0]] * letter_freqs[pair[1]])
- for pair, freq in pair_freqs.items()
- }
- return scores
-```
-
-Jetons un coup d'œil à une partie de ce dictionnaire après les premières divisions :
-
-```python
-pair_scores = compute_pair_scores(splits)
-for i, key in enumerate(pair_scores.keys()):
- print(f"{key}: {pair_scores[key]}")
- if i >= 5:
- break
-```
-
-```python out
-('T', '##h'): 0.125
-('##h', '##i'): 0.03409090909090909
-('##i', '##s'): 0.02727272727272727
-('i', '##s'): 0.1
-('t', '##h'): 0.03571428571428571
-('##h', '##e'): 0.011904761904761904
-```
-
-Maintenant, trouver la paire avec le meilleur score ne prend qu'une rapide boucle :
-
-```python
-best_pair = ""
-max_score = None
-for pair, score in pair_scores.items():
- if max_score is None or max_score < score:
- best_pair = pair
- max_score = score
-
-print(best_pair, max_score)
-```
-
-```python out
-('a', '##b') 0.2
-```
-
-Ainsi, la première fusion à apprendre est `('a', '##b') -> 'ab'` et nous ajoutons `'ab'` au vocabulaire :
-
-```python
-vocab.append("ab")
-```
-
-Pour continuer, nous devons appliquer cette fusion dans notre dictionnaire `splits`. Écrivons une autre fonction pour cela :
-
-```python
-def merge_pair(a, b, splits):
- for word in word_freqs:
- split = splits[word]
- if len(split) == 1:
- continue
- i = 0
- while i < len(split) - 1:
- if split[i] == a and split[i + 1] == b:
- merge = a + b[2:] if b.startswith("##") else a + b
- split = split[:i] + [merge] + split[i + 2 :]
- else:
- i += 1
- splits[word] = split
- return splits
-```
-
-Et nous pouvons regarder le résultat de la première fusion :
-
-```py
-splits = merge_pair("a", "##b", splits)
-splits["about"]
-```
-
-```python out
-['ab', '##o', '##u', '##t']
-```
-
-Nous avons maintenant tout ce dont nous avons besoin pour boucler jusqu'à ce que nous ayons appris toutes les fusions que nous voulons. Visons une taille de vocabulaire de 70 :
-
-```python
-vocab_size = 70
-while len(vocab) < vocab_size:
- scores = compute_pair_scores(splits)
- best_pair, max_score = "", None
- for pair, score in scores.items():
- if max_score is None or max_score < score:
- best_pair = pair
- max_score = score
- splits = merge_pair(*best_pair, splits)
- new_token = (
- best_pair[0] + best_pair[1][2:]
- if best_pair[1].startswith("##")
- else best_pair[0] + best_pair[1]
- )
- vocab.append(new_token)
-```
-
-Nous pouvons ensuite examiner le vocabulaire généré :
-
-```py
-print(vocab)
-```
-
-```python out
-['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', '##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k',
- '##l', '##m', '##n', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H',
- 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', '##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully',
- 'Th', 'ch', '##hm', 'cha', 'chap', 'chapt', '##thm', 'Hu', 'Hug', 'Hugg', 'sh', 'th', 'is', '##thms', '##za', '##zat',
- '##ut']
-```
-
-Comme nous pouvons le voir, comparé à BPE, ce *tokenizer* apprend les parties de mots comme des *tokens* un peu plus rapidement.
-
-
-
-💡 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 :
-
-```python
-def encode_word(word):
- tokens = []
- while len(word) > 0:
- i = len(word)
- while i > 0 and word[:i] not in vocab:
- i -= 1
- if i == 0:
- return ["[UNK]"]
- tokens.append(word[:i])
- word = word[i:]
- if len(word) > 0:
- word = f"##{word}"
- return tokens
-```
-
-Testons-le sur un mot qui fait partie du vocabulaire, et un autre qui n'en fait pas partie :
-
-```python
-print(encode_word("Hugging"))
-print(encode_word("HOgging"))
-```
-
-```python out
-['Hugg', '##i', '##n', '##g']
-['[UNK]']
-```
-
-Maintenant, écrivons une fonction qui permet de tokeniser un texte :
-
-```python
-def tokenize(text):
- pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
- pre_tokenized_text = [word for word, offset in pre_tokenize_result]
- encoded_words = [encode_word(word) for word in pre_tokenized_text]
- return sum(encoded_words, [])
-```
-
-On peut l'essayer sur n'importe quel texte :
-
-```python
-tokenize("This is the Hugging Face Course!") # C'est le cours d'Hugging Face
-```
-
-```python out
-['Th', '##i', '##s', 'is', 'th', '##e', 'Hugg', '##i', '##n', '##g', 'Fac', '##e', 'c', '##o', '##u', '##r', '##s',
- '##e', '[UNK]']
-```
-
-C'est tout pour l'algorithme *WordPiece* ! Maintenant, jetons un coup d'oeil à *Unigram*.
+# 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 *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.
+
+
+
+## Algorithme d'entraînement
+
+
+
+⚠️ 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, `"word"` est divisé comme ceci :
+
+```
+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 :
+
+$$\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 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 :
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+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)
+```
+
+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 :
+
+```
+Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]
+Corpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)
+```
+
+À ce stade, `" ##u "` est dans toutes les paires possibles, donc elles finissent toutes par avoir le même score. Disons que dans ce cas, la première paire est fusionnée, donc `("h", "##u") -> "hu"`. Cela nous amène à :
+
+```
+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). Ainsi la première paire avec le plus grand score est fusionnée :
+
+```
+Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]
+Corpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
+```
+
+et nous continuons ainsi jusqu'à ce que nous atteignions la taille de vocabulaire souhaitée.
+
+
+
+✏️ **A votre tour !** Quelle sera la prochaine règle de fusion ?
+
+
+
+## Algorithme de tokenisation
+
+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"`.
+
+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.
+
+
+
+✏️ **A votre tour !** Comment le mot `"pugs"` sera-t-il tokenisé ?
+
+
+
+## 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.
+
+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.
+]
+```
+
+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
+
+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 :
+
+```python
+from collections import defaultdict
+
+word_freqs = defaultdict(int)
+for text in corpus:
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ new_words = [word for word, offset in words_with_offsets]
+ for word in new_words:
+ word_freqs[word] += 1
+
+word_freqs
+```
+
+```python out
+defaultdict(
+ int, {'This': 3, 'is': 2, 'the': 1, 'Hugging': 1, 'Face': 1, 'Course': 1, '.': 4, 'chapter': 1, 'about': 1,
+ 'tokenization': 1, 'section': 1, 'shows': 1, 'several': 1, 'tokenizer': 1, 'algorithms': 1, 'Hopefully': 1,
+ ',': 1, 'you': 1, 'will': 1, 'be': 1, 'able': 1, 'to': 1, 'understand': 1, 'how': 1, 'they': 1, 'are': 1,
+ 'trained': 1, 'and': 1, 'generate': 1, 'tokens': 1})
+```
+
+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 = []
+for word in word_freqs.keys():
+ if word[0] not in alphabet:
+ alphabet.append(word[0])
+ for letter in word[1:]:
+ if f"##{letter}" not in alphabet:
+ alphabet.append(f"##{letter}")
+
+alphabet.sort()
+alphabet
+
+print(alphabet)
+```
+
+```python out
+['##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', '##l', '##m', '##n', '##o', '##p', '##r', '##s',
+ '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u',
+ '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]"]` :
+
+```python
+vocab = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + alphabet.copy()
+```
+
+Ensuite, nous devons diviser chaque mot, avec toutes les lettres qui ne sont pas les premières préfixées par `##` :
+
+```python
+splits = {
+ word: [c if i == 0 else f"##{c}" for i, c in enumerate(word)]
+ for word in word_freqs.keys()
+}
+```
+
+Maintenant que nous sommes prêts pour l'entraînement, écrivons une fonction qui calcule le score de chaque paire. Nous devrons l'utiliser à chaque étape de l'entraînement :
+
+```python
+def compute_pair_scores(splits):
+ letter_freqs = defaultdict(int)
+ pair_freqs = defaultdict(int)
+ for word, freq in word_freqs.items():
+ split = splits[word]
+ if len(split) == 1:
+ letter_freqs[split[0]] += freq
+ continue
+ for i in range(len(split) - 1):
+ pair = (split[i], split[i + 1])
+ letter_freqs[split[i]] += freq
+ pair_freqs[pair] += freq
+ letter_freqs[split[-1]] += freq
+
+ scores = {
+ pair: freq / (letter_freqs[pair[0]] * letter_freqs[pair[1]])
+ for pair, freq in pair_freqs.items()
+ }
+ return scores
+```
+
+Jetons un coup d'œil à une partie de ce dictionnaire après les premières divisions :
+
+```python
+pair_scores = compute_pair_scores(splits)
+for i, key in enumerate(pair_scores.keys()):
+ print(f"{key}: {pair_scores[key]}")
+ if i >= 5:
+ break
+```
+
+```python out
+('T', '##h'): 0.125
+('##h', '##i'): 0.03409090909090909
+('##i', '##s'): 0.02727272727272727
+('i', '##s'): 0.1
+('t', '##h'): 0.03571428571428571
+('##h', '##e'): 0.011904761904761904
+```
+
+Maintenant, trouver la paire avec le meilleur score ne prend qu'une rapide boucle :
+
+```python
+best_pair = ""
+max_score = None
+for pair, score in pair_scores.items():
+ if max_score is None or max_score < score:
+ best_pair = pair
+ max_score = score
+
+print(best_pair, max_score)
+```
+
+```python out
+('a', '##b') 0.2
+```
+
+Ainsi, la première fusion à apprendre est `('a', '##b') -> 'ab'` et nous ajoutons `'ab'` au vocabulaire :
+
+```python
+vocab.append("ab")
+```
+
+Pour continuer, nous devons appliquer cette fusion dans notre dictionnaire `splits`. Écrivons une autre fonction pour cela :
+
+```python
+def merge_pair(a, b, splits):
+ for word in word_freqs:
+ split = splits[word]
+ if len(split) == 1:
+ continue
+ i = 0
+ while i < len(split) - 1:
+ if split[i] == a and split[i + 1] == b:
+ merge = a + b[2:] if b.startswith("##") else a + b
+ split = split[:i] + [merge] + split[i + 2 :]
+ else:
+ i += 1
+ splits[word] = split
+ return splits
+```
+
+Et nous pouvons regarder le résultat de la première fusion :
+
+```py
+splits = merge_pair("a", "##b", splits)
+splits["about"]
+```
+
+```python out
+['ab', '##o', '##u', '##t']
+```
+
+Nous avons maintenant tout ce dont nous avons besoin pour boucler jusqu'à ce que nous ayons appris toutes les fusions que nous voulons. Visons une taille de vocabulaire de 70 :
+
+```python
+vocab_size = 70
+while len(vocab) < vocab_size:
+ scores = compute_pair_scores(splits)
+ best_pair, max_score = "", None
+ for pair, score in scores.items():
+ if max_score is None or max_score < score:
+ best_pair = pair
+ max_score = score
+ splits = merge_pair(*best_pair, splits)
+ new_token = (
+ best_pair[0] + best_pair[1][2:]
+ if best_pair[1].startswith("##")
+ else best_pair[0] + best_pair[1]
+ )
+ vocab.append(new_token)
+```
+
+Nous pouvons ensuite examiner le vocabulaire généré :
+
+```py
+print(vocab)
+```
+
+```python out
+['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', '##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k',
+ '##l', '##m', '##n', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H',
+ 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', '##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully',
+ 'Th', 'ch', '##hm', 'cha', 'chap', 'chapt', '##thm', 'Hu', 'Hug', 'Hugg', 'sh', 'th', 'is', '##thms', '##za', '##zat',
+ '##ut']
+```
+
+Comme nous pouvons le voir, comparé à BPE, ce *tokenizer* apprend les parties de mots comme des *tokens* un peu plus rapidement.
+
+
+
+💡 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 :
+
+```python
+def encode_word(word):
+ tokens = []
+ while len(word) > 0:
+ i = len(word)
+ while i > 0 and word[:i] not in vocab:
+ i -= 1
+ if i == 0:
+ return ["[UNK]"]
+ tokens.append(word[:i])
+ word = word[i:]
+ if len(word) > 0:
+ word = f"##{word}"
+ return tokens
+```
+
+Testons-le sur un mot qui fait partie du vocabulaire, et un autre qui n'en fait pas partie :
+
+```python
+print(encode_word("Hugging"))
+print(encode_word("HOgging"))
+```
+
+```python out
+['Hugg', '##i', '##n', '##g']
+['[UNK]']
+```
+
+Maintenant, écrivons une fonction qui permet de tokeniser un texte :
+
+```python
+def tokenize(text):
+ pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ pre_tokenized_text = [word for word, offset in pre_tokenize_result]
+ encoded_words = [encode_word(word) for word in pre_tokenized_text]
+ return sum(encoded_words, [])
+```
+
+On peut l'essayer sur n'importe quel texte :
+
+```python
+tokenize("This is the Hugging Face Course!") # C'est le cours d'Hugging Face
+```
+
+```python out
+['Th', '##i', '##s', 'is', 'th', '##e', 'Hugg', '##i', '##n', '##g', 'Fac', '##e', 'c', '##o', '##u', '##r', '##s',
+ '##e', '[UNK]']
+```
+
+C'est tout pour l'algorithme *WordPiece* ! Maintenant, jetons un coup d'oeil à *Unigram*.
diff --git a/chapters/fr/chapter6/7.mdx b/chapters/fr/chapter6/7.mdx
index bf5c970dc..3d262bbd1 100644
--- a/chapters/fr/chapter6/7.mdx
+++ b/chapters/fr/chapter6/7.mdx
@@ -1,385 +1,385 @@
-# Tokenisation Unigram
-
-
-
-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.
-
-
-
-
-
-💡 Cette section couvre *Unigram* 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.
-
-
-
-## Algorithme d'entraînement
-
-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.
-
-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é.
-
-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) # "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 :
-
-```
-["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
-```
-
-## Algorithme de tokenisation
-
-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.
-
-Voici les fréquences de tous les sous-mots possibles dans le vocabulaire :
-
-```
-("h", 15) ("u", 36) ("g", 20) ("hu", 15) ("ug", 20) ("p", 17) ("pu", 17) ("n", 16)
-("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.
-
-
-
-✏️ **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.
-
-
-
-Maintenant, pour tokeniser un mot donné, nous examinons toutes les segmentations possibles en *tokens* et calculons la probabilité de chacune d'entre elles selon le modèle *Unigram*. Puisque tous les *tokens* sont considérés comme indépendants, cette probabilité est juste le produit de la probabilité de chaque *token*. Par exemple, la tokenisation `["p", "u", "g"]` de `"pug"` a la probabilité :
-
-$$P([``p", ``u", ``g"]) = P(``p") \times P(``u") \times P(``g") = \frac{5}{210} \times \frac{36}{210} \times \frac{20}{210} = 0.000389$$
-
-Comparativement, la tokenization `["pu", "g"]` a la probabilité :
-
-$$P([``pu", ``g"]) = P(``pu") \times P(``g") = \frac{5}{210} \times \frac{20}{210} = 0.0022676$$
-
-donc celle-là est beaucoup plus probable. En général, les tokénisations comportant le moins de *tokens* possible auront la probabilité la plus élevée (en raison de la division par 210 répétée pour chaque *token*), ce qui correspond à ce que nous voulons intuitivement : diviser un mot en un nombre de *tokens* le plus faible possible.
-
-La tokenisation d'un mot avec le modèle *Unigram* est donc la tokenisation avec la plus haute probabilité. Dans l'exemple de `"pug"`, voici les probabilités que nous obtiendrions pour chaque segmentation possible :
-
-```
-["p", "u", "g"] : 0.000389
-["p", "ug"] : 0.0022676
-["pu", "g"] : 0.0022676
-```
-
-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-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 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 :
-
-```
-Character 0 (u): "u" (score 0.171429)
-Character 1 (n): "un" (score 0.076191)
-Character 2 (h): "un" "h" (score 0.005442)
-Character 3 (u): "un" "hu" (score 0.005442)
-Character 4 (g): "un" "hug" (score 0.005442)
-```
-
-Ainsi, `"unhug"` serait tokenisé comme `["un", "hug"]`.
-
-
-
-✏️ **A votre tour !** Déterminer la tokenization du mot `"huggun"` et son score.
-
-
-
-## Retour à l'entraînement
-
-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 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 :
-
-```
-("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
-```
-
-La tokenisation de chaque mot avec leurs scores respectifs est :
-
-```
-"hug": ["hug"] (score 0.071428)
-"pug": ["pu", "g"] (score 0.007710)
-"pun": ["pu", "n"] (score 0.006168)
-"bun": ["bu", "n"] (score 0.001451)
-"hugs": ["hug", "s"] (score 0.001701)
-```
-
-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. 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 :
-
-```
-"hug": ["hu", "g"] (score 0.006802)
-"hugs": ["hu", "gs"] (score 0.001701)
-```
-
-Ces changements entraîneront une augmentation de la perte de :
-
-```
-- 10 * (-log(0.071428)) + 10 * (-log(0.006802)) = 23.5
-```
-
-Par conséquent, le token `"pu"` sera probablement retiré du vocabulaire, mais pas `"hug"`.
-
-## Implémentation d'Unigram
-
-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.",
- # 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*.
-]
-```
-
-Cette fois, nous allons utiliser `xlnet-base-cased` comme modèle :
-
-```python
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("xlnet-base-cased")
-```
-
-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
-
-word_freqs = defaultdict(int)
-for text in corpus:
- words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
- new_words = [word for word, offset in words_with_offsets]
- for word in new_words:
- word_freqs[word] += 1
-
-word_freqs
-```
-
-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)
-subwords_freqs = defaultdict(int)
-for word, freq in word_freqs.items():
- for i in range(len(word)):
- char_freqs[word[i]] += freq
- # 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
-
-# Trier les sous-mots par fréquence
-sorted_subwords = sorted(subwords_freqs.items(), key=lambda x: x[1], reverse=True)
-sorted_subwords[:10]
-```
-
-```python out
-[('▁t', 7), ('is', 5), ('er', 5), ('▁a', 5), ('▁to', 4), ('to', 4), ('en', 4), ('▁T', 3), ('▁Th', 3), ('▁Thi', 3)]
-```
-
-Nous regroupons les caractères avec les meilleurs sous-mots pour arriver à un vocabulaire initial de taille 300 :
-
-```python
-token_freqs = list(char_freqs.items()) + sorted_subwords[: 300 - len(char_freqs)]
-token_freqs = {token: freq for token, freq in token_freqs}
-```
-
-
-
-💡 *SentencePiece* utilise un algorithme plus efficace appelé *Enhanced Suffix Array* (ESA) pour créer le vocabulaire initial.
-
-
-
-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
-
-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.
-
-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 :
-
-```python
-def encode_word(word, model):
- best_segmentations = [{"start": 0, "score": 1}] + [
- {"start": None, "score": None} for _ in range(len(word))
- ]
- for start_idx in range(len(word)):
- # 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
- # 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
- ):
- best_segmentations[end_idx] = {"start": start_idx, "score": score}
-
- segmentation = best_segmentations[-1]
- if segmentation["score"] is None:
- # Nous n'avons pas trouvé de tokenization du mot -> inconnu ()
- return [""], None
-
- score = segmentation["score"]
- start = segmentation["start"]
- end = len(word)
- tokens = []
- while start != 0:
- tokens.insert(0, word[start:end])
- next_start = best_segmentations[start]["start"]
- end = start
- start = next_start
- tokens.insert(0, word[start:end])
- return tokens, score
-```
-
-Nous pouvons déjà essayer notre modèle initial sur quelques mots :
-
-```python
-print(encode_word("Hopefully", model))
-print(encode_word("This", model))
-```
-
-```python out
-(['H', 'o', 'p', 'e', 'f', 'u', 'll', 'y'], 41.5157494601402)
-(['This'], 6.288267030694535)
-```
-
-Il est maintenant facile de calculer la perte du modèle sur le corpus !
-
-```python
-def compute_loss(model):
- loss = 0
- for word, freq in word_freqs.items():
- _, word_loss = encode_word(word, model)
- loss += freq * word_loss
- return loss
-```
-
-Nous pouvons vérifier que cela fonctionne sur le modèle que nous avons :
-
-```python
-compute_loss(model)
-```
-
-```python out
-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* :
-
-```python
-import copy
-
-
-def compute_scores(model):
- scores = {}
- model_loss = compute_loss(model)
- for token, score in model.items():
- # Nous gardons toujours les tokens de longueur 1.
- if len(token) == 1:
- continue
- model_without_token = copy.deepcopy(model)
- _ = model_without_token.pop(token)
- scores[token] = compute_loss(model_without_token) - model_loss
- return scores
-```
-
-Nous pouvons l'essayer sur un *token* donné :
-
-```python
-scores = compute_scores(model)
-print(scores["ll"])
-print(scores["his"])
-```
-
-Puisque `"ll"` est utilisé dans la tokenisation de `"Hopefully"`, et que le supprimer nous fera probablement utiliser le token `"l"` deux fois à la place, nous nous attendons à ce qu'il ait une perte positive. `"his"` n'est utilisé qu'à l'intérieur du mot `"This"`, qui est tokenisé comme lui-même, donc nous nous attendons à ce qu'il ait une perte nulle. Voici les résultats :
-
-```python out
-6.376412403623874
-0.0
-```
-
-
-
-💡 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.
-
-
-
-Une fois tout cela en place, la dernière chose à faire est d'ajouter les *tokens* spéciaux utilisés par le modèle au vocabulaire, puis de boucler jusqu'à ce que nous ayons élagué suffisamment de *tokens* du vocabulaire pour atteindre la taille souhaitée :
-
-```python
-percent_to_remove = 0.1
-while len(model) > 100:
- scores = compute_scores(model)
- sorted_scores = sorted(scores.items(), key=lambda x: x[1])
- # 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])
-
- total_sum = sum([freq for token, freq in token_freqs.items()])
- 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()` :
-
-```python
-def tokenize(text, model):
- words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
- pre_tokenized_text = [word for word, offset in words_with_offsets]
- encoded_words = [encode_word(word, model)[0] for word in pre_tokenized_text]
- return sum(encoded_words, [])
-
-
-tokenize("This is the Hugging Face course.", model)
-```
-
-```python out
-['▁This', '▁is', '▁the', '▁Hugging', '▁Face', '▁', 'c', 'ou', 'r', 's', 'e', '.']
-```
-
-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*.
+# Tokenisation Unigram
+
+
+
+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.
+
+
+
+
+
+💡 Cette section couvre *Unigram* 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.
+
+
+
+## Algorithme d'entraînement
+
+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.
+
+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é.
+
+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) # "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 :
+
+```
+["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
+```
+
+## Algorithme de tokenisation
+
+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.
+
+Voici les fréquences de tous les sous-mots possibles dans le vocabulaire :
+
+```
+("h", 15) ("u", 36) ("g", 20) ("hu", 15) ("ug", 20) ("p", 17) ("pu", 17) ("n", 16)
+("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.
+
+
+
+✏️ **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.
+
+
+
+Maintenant, pour tokeniser un mot donné, nous examinons toutes les segmentations possibles en *tokens* et calculons la probabilité de chacune d'entre elles selon le modèle *Unigram*. Puisque tous les *tokens* sont considérés comme indépendants, cette probabilité est juste le produit de la probabilité de chaque *token*. Par exemple, la tokenisation `["p", "u", "g"]` de `"pug"` a la probabilité :
+
+$$P([``p", ``u", ``g"]) = P(``p") \times P(``u") \times P(``g") = \frac{5}{210} \times \frac{36}{210} \times \frac{20}{210} = 0.000389$$
+
+Comparativement, la tokenization `["pu", "g"]` a la probabilité :
+
+$$P([``pu", ``g"]) = P(``pu") \times P(``g") = \frac{5}{210} \times \frac{20}{210} = 0.0022676$$
+
+donc celle-là est beaucoup plus probable. En général, les tokénisations comportant le moins de *tokens* possible auront la probabilité la plus élevée (en raison de la division par 210 répétée pour chaque *token*), ce qui correspond à ce que nous voulons intuitivement : diviser un mot en un nombre de *tokens* le plus faible possible.
+
+La tokenisation d'un mot avec le modèle *Unigram* est donc la tokenisation avec la plus haute probabilité. Dans l'exemple de `"pug"`, voici les probabilités que nous obtiendrions pour chaque segmentation possible :
+
+```
+["p", "u", "g"] : 0.000389
+["p", "ug"] : 0.0022676
+["pu", "g"] : 0.0022676
+```
+
+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-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 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 :
+
+```
+Character 0 (u): "u" (score 0.171429)
+Character 1 (n): "un" (score 0.076191)
+Character 2 (h): "un" "h" (score 0.005442)
+Character 3 (u): "un" "hu" (score 0.005442)
+Character 4 (g): "un" "hug" (score 0.005442)
+```
+
+Ainsi, `"unhug"` serait tokenisé comme `["un", "hug"]`.
+
+
+
+✏️ **A votre tour !** Déterminer la tokenization du mot `"huggun"` et son score.
+
+
+
+## Retour à l'entraînement
+
+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 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 :
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+La tokenisation de chaque mot avec leurs scores respectifs est :
+
+```
+"hug": ["hug"] (score 0.071428)
+"pug": ["pu", "g"] (score 0.007710)
+"pun": ["pu", "n"] (score 0.006168)
+"bun": ["bu", "n"] (score 0.001451)
+"hugs": ["hug", "s"] (score 0.001701)
+```
+
+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. 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 :
+
+```
+"hug": ["hu", "g"] (score 0.006802)
+"hugs": ["hu", "gs"] (score 0.001701)
+```
+
+Ces changements entraîneront une augmentation de la perte de :
+
+```
+- 10 * (-log(0.071428)) + 10 * (-log(0.006802)) = 23.5
+```
+
+Par conséquent, le token `"pu"` sera probablement retiré du vocabulaire, mais pas `"hug"`.
+
+## Implémentation d'Unigram
+
+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.",
+ # 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*.
+]
+```
+
+Cette fois, nous allons utiliser `xlnet-base-cased` comme modèle :
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("xlnet-base-cased")
+```
+
+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
+
+word_freqs = defaultdict(int)
+for text in corpus:
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ new_words = [word for word, offset in words_with_offsets]
+ for word in new_words:
+ word_freqs[word] += 1
+
+word_freqs
+```
+
+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)
+subwords_freqs = defaultdict(int)
+for word, freq in word_freqs.items():
+ for i in range(len(word)):
+ char_freqs[word[i]] += freq
+ # 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
+
+# Trier les sous-mots par fréquence
+sorted_subwords = sorted(subwords_freqs.items(), key=lambda x: x[1], reverse=True)
+sorted_subwords[:10]
+```
+
+```python out
+[('▁t', 7), ('is', 5), ('er', 5), ('▁a', 5), ('▁to', 4), ('to', 4), ('en', 4), ('▁T', 3), ('▁Th', 3), ('▁Thi', 3)]
+```
+
+Nous regroupons les caractères avec les meilleurs sous-mots pour arriver à un vocabulaire initial de taille 300 :
+
+```python
+token_freqs = list(char_freqs.items()) + sorted_subwords[: 300 - len(char_freqs)]
+token_freqs = {token: freq for token, freq in token_freqs}
+```
+
+
+
+💡 *SentencePiece* utilise un algorithme plus efficace appelé *Enhanced Suffix Array* (ESA) pour créer le vocabulaire initial.
+
+
+
+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
+
+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.
+
+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 :
+
+```python
+def encode_word(word, model):
+ best_segmentations = [{"start": 0, "score": 1}] + [
+ {"start": None, "score": None} for _ in range(len(word))
+ ]
+ for start_idx in range(len(word)):
+ # 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
+ # 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
+ ):
+ best_segmentations[end_idx] = {"start": start_idx, "score": score}
+
+ segmentation = best_segmentations[-1]
+ if segmentation["score"] is None:
+ # Nous n'avons pas trouvé de tokenization du mot -> inconnu ()
+ return [""], None
+
+ score = segmentation["score"]
+ start = segmentation["start"]
+ end = len(word)
+ tokens = []
+ while start != 0:
+ tokens.insert(0, word[start:end])
+ next_start = best_segmentations[start]["start"]
+ end = start
+ start = next_start
+ tokens.insert(0, word[start:end])
+ return tokens, score
+```
+
+Nous pouvons déjà essayer notre modèle initial sur quelques mots :
+
+```python
+print(encode_word("Hopefully", model))
+print(encode_word("This", model))
+```
+
+```python out
+(['H', 'o', 'p', 'e', 'f', 'u', 'll', 'y'], 41.5157494601402)
+(['This'], 6.288267030694535)
+```
+
+Il est maintenant facile de calculer la perte du modèle sur le corpus !
+
+```python
+def compute_loss(model):
+ loss = 0
+ for word, freq in word_freqs.items():
+ _, word_loss = encode_word(word, model)
+ loss += freq * word_loss
+ return loss
+```
+
+Nous pouvons vérifier que cela fonctionne sur le modèle que nous avons :
+
+```python
+compute_loss(model)
+```
+
+```python out
+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* :
+
+```python
+import copy
+
+
+def compute_scores(model):
+ scores = {}
+ model_loss = compute_loss(model)
+ for token, score in model.items():
+ # Nous gardons toujours les tokens de longueur 1.
+ if len(token) == 1:
+ continue
+ model_without_token = copy.deepcopy(model)
+ _ = model_without_token.pop(token)
+ scores[token] = compute_loss(model_without_token) - model_loss
+ return scores
+```
+
+Nous pouvons l'essayer sur un *token* donné :
+
+```python
+scores = compute_scores(model)
+print(scores["ll"])
+print(scores["his"])
+```
+
+Puisque `"ll"` est utilisé dans la tokenisation de `"Hopefully"`, et que le supprimer nous fera probablement utiliser le token `"l"` deux fois à la place, nous nous attendons à ce qu'il ait une perte positive. `"his"` n'est utilisé qu'à l'intérieur du mot `"This"`, qui est tokenisé comme lui-même, donc nous nous attendons à ce qu'il ait une perte nulle. Voici les résultats :
+
+```python out
+6.376412403623874
+0.0
+```
+
+
+
+💡 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.
+
+
+
+Une fois tout cela en place, la dernière chose à faire est d'ajouter les *tokens* spéciaux utilisés par le modèle au vocabulaire, puis de boucler jusqu'à ce que nous ayons élagué suffisamment de *tokens* du vocabulaire pour atteindre la taille souhaitée :
+
+```python
+percent_to_remove = 0.1
+while len(model) > 100:
+ scores = compute_scores(model)
+ sorted_scores = sorted(scores.items(), key=lambda x: x[1])
+ # 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])
+
+ total_sum = sum([freq for token, freq in token_freqs.items()])
+ 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()` :
+
+```python
+def tokenize(text, model):
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ pre_tokenized_text = [word for word, offset in words_with_offsets]
+ encoded_words = [encode_word(word, model)[0] for word in pre_tokenized_text]
+ return sum(encoded_words, [])
+
+
+tokenize("This is the Hugging Face course.", model)
+```
+
+```python out
+['▁This', '▁is', '▁the', '▁Hugging', '▁Face', '▁', 'c', 'ou', 'r', 's', 'e', '.']
+```
+
+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 46440deb7..79c0a58a8 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. 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*.
+# 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 11e4beeab..2d586dc7b 100644
--- a/chapters/fr/chapter6/9.mdx
+++ b/chapters/fr/chapter6/9.mdx
@@ -1,11 +1,16 @@
-# 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 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*.
+# 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 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/1.mdx b/chapters/fr/chapter7/1.mdx
index 49c7a7a84..603659553 100644
--- a/chapters/fr/chapter7/1.mdx
+++ b/chapters/fr/chapter7/1.mdx
@@ -1,33 +1,38 @@
-
-
-# Introduction
-
-Dans le [chapitre 3](/course/fr/chapter3), vous avez vu comment *finetuner* un modèle de classification de texte. Dans ce chapitre, nous nous attaquons aux tâches de NLP courantes suivantes :
-
-- la classification de *tokens*,
-- la modélisation du langage masqué (comme BERT),
-- les résumés,
-- la traduction,
-- le pré-entraînement à la modélisation causale du langage (comme GPT-2),
-- la réponse aux questions.
-
-{#if fw === 'pt'}
-
-Pour ce faire, vous devrez tirer parti de tout ce que vous avez appris sur l'API `Trainer`, sur la bibliothèque 🤗 *Accelerate* au [chapitre 3](/course/fr/chapitre3), sur la bibliothèque 🤗 *Datasets* au [chapitre 5](/course/fr/chapiter5) et sur la bibliothèque 🤗 *Tokenizers* au [chapitre 6](/course/fr/chapiter6). Nous téléchargerons également nos résultats sur le *Hub*, comme nous l'avons fait dans le [chapitre 4](/course/fr/chapiter4), donc c'est vraiment le chapitre où tout est réuni !
-
-Chaque section peut être lue indépendamment et vous montrera comment entraîner un modèle avec l'API `Trainer` ou avec 🤗 *Accelerate* et votre propre boucle d'entraînement. N'hésitez pas à sauter l'une ou l'autre partie et à vous concentrer sur celle qui vous intéresse le plus. L'API `Trainer` est idéale pour *finetuner* ou entraîner votre modèle sans vous soucier de ce qui se passe en coulisses, tandis que la boucle d'entraînement avec `Accelerate` vous permettra de personnaliser plus facilement toutes les parties que vous souhaitez.
-
-{:else}
-
-Pour ce faire, vous devrez tirer parti de tout ce que vous avez appris sur l'entraînement des modèles avec l'API Keras dans le [chapitre 3](/course/fr/chapiter3), sur la bibliothèque 🤗 *Accelerate* au [chapitre 3](/course/fr/chapitre3), sur la bibliothèque 🤗 *Datasets* au [chapitre 5](/course/fr/chapiter5) et sur la bibliothèque 🤗 *Tokenizers* au [chapitre 6](/course/fr/chapiter6). Nous téléchargerons également nos résultats sur le *Hub*, comme nous l'avons fait dans le [chapitre 4](/course/fr/chapiter4), donc c'est vraiment le chapitre où tout est réuni !
-
-Chaque section peut être lue indépendamment.
-
-{/if}
-
-
-
-
-Si vous lisez les sections dans l'ordre, vous remarquerez qu'elles ont beaucoup de code et de prose en commun. La répétition est intentionnelle, afin de vous permettre de vous plonger (ou de revenir plus tard) dans une tâche qui vous intéresse et de trouver un exemple fonctionnel complet.
-
-
+
+
+# Introduction
+
+
+
+Dans le [chapitre 3](/course/fr/chapter3), vous avez vu comment *finetuner* un modèle de classification de texte. Dans ce chapitre, nous nous attaquons aux tâches de NLP courantes suivantes :
+
+- la classification de *tokens*,
+- la modélisation du langage masqué (comme BERT),
+- les résumés,
+- la traduction,
+- le pré-entraînement à la modélisation causale du langage (comme GPT-2),
+- la réponse aux questions.
+
+{#if fw === 'pt'}
+
+Pour ce faire, vous devrez tirer parti de tout ce que vous avez appris sur l'API `Trainer`, sur la bibliothèque 🤗 *Accelerate* au [chapitre 3](/course/fr/chapitre3), sur la bibliothèque 🤗 *Datasets* au [chapitre 5](/course/fr/chapiter5) et sur la bibliothèque 🤗 *Tokenizers* au [chapitre 6](/course/fr/chapiter6). Nous téléchargerons également nos résultats sur le *Hub*, comme nous l'avons fait dans le [chapitre 4](/course/fr/chapiter4), donc c'est vraiment le chapitre où tout est réuni !
+
+Chaque section peut être lue indépendamment et vous montrera comment entraîner un modèle avec l'API `Trainer` ou avec 🤗 *Accelerate* et votre propre boucle d'entraînement. N'hésitez pas à sauter l'une ou l'autre partie et à vous concentrer sur celle qui vous intéresse le plus. L'API `Trainer` est idéale pour *finetuner* ou entraîner votre modèle sans vous soucier de ce qui se passe en coulisses, tandis que la boucle d'entraînement avec `Accelerate` vous permettra de personnaliser plus facilement toutes les parties que vous souhaitez.
+
+{:else}
+
+Pour ce faire, vous devrez tirer parti de tout ce que vous avez appris sur l'entraînement des modèles avec l'API Keras dans le [chapitre 3](/course/fr/chapiter3), sur la bibliothèque 🤗 *Accelerate* au [chapitre 3](/course/fr/chapitre3), sur la bibliothèque 🤗 *Datasets* au [chapitre 5](/course/fr/chapiter5) et sur la bibliothèque 🤗 *Tokenizers* au [chapitre 6](/course/fr/chapiter6). Nous téléchargerons également nos résultats sur le *Hub*, comme nous l'avons fait dans le [chapitre 4](/course/fr/chapiter4), donc c'est vraiment le chapitre où tout est réuni !
+
+Chaque section peut être lue indépendamment.
+
+{/if}
+
+
+
+
+Si vous lisez les sections dans l'ordre, vous remarquerez qu'elles ont beaucoup de code et de prose en commun. La répétition est intentionnelle, afin de vous permettre de vous plonger (ou de revenir plus tard) dans une tâche qui vous intéresse et de trouver un exemple fonctionnel complet.
+
+
diff --git a/chapters/fr/chapter7/2.mdx b/chapters/fr/chapter7/2.mdx
index 921f9629f..8a4cdc83a 100644
--- a/chapters/fr/chapter7/2.mdx
+++ b/chapters/fr/chapter7/2.mdx
@@ -1,982 +1,982 @@
-
-
-# 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* d'une phrase, tels que :
-
-- la **reconnaissance d'entités nommées (NER de l'anglais *Named Entity Recognition*)**, c'est-à-dire trouver les entités (telles que des personnes, des lieux ou des organisations) dans une phrase. Ce tâche peut être formulée comme l'attribution d'une étiquette à chaque *token* faisant parti d'une entité en ayant une classe spécifique par entité, et une classe pour les *tokens* ne faisant pas parti d'entité.
-- le ***part-of-speech tagging* (POS)**, c'est-à-dire marquer chaque mot dans une phrase comme correspondant à une partie particulière (comme un nom, un verbe, un adjectif, etc.).
-- le ***chunking***, c'est-à-dire 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 *finetuner* un modèle (BERT) sur la tâche de NER. Il sera alors capable de calculer des prédictions comme celle-ci :
-
-
-
-
-
-
-
-
-
-Vous pouvez trouver, télécharger et vérifier les précisions de ce modèle sur le [*Hub*](https://huggingface.co/huggingface-course/bert-finetuned-ner?text=My+nom+est+Sylvain+et+je+travaille+à+Hugging+Face+in+Brooklyn) les prédictions du modèle que nous allons entraîner.
-
-## 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 dans ce jeu de données 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 entrées textuelles 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 en 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 reconnaître 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 disponibles pour l'entraînement mais 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`. 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 au [chapitre 6](/course/fr/chapter6/3) lorsque nous nous sommes intéressés au pipeline `token-classification` mais nosu pouvons tout de même faire un rapide rappel :
-
-- `O` signifie que le mot ne correspond à aucune entité.
-- `B-PER`/`I-PER` signifie que le mot correspond au début/est à l'intérieur d'une entité *personne*.
-- `B-ORG`/`I-ORG` signifie que le mot correspond au début/est à l'intérieur d'une entité *organisation*.
-- `B-LOC`/`I-LOC` signifie que le mot correspond au début/est à l'intérieur d'une entité *location*.
-- `B-MISC`/`I-MISC` signifie que le mot correspond au début/est à 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 le quatrième élément du jeu d'entraînement :
-
-```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 « European Union » et « Werner Zwingmann », se voient attribuer une étiquette `B-` pour le premier mot et une étiquette `I-` pour le second.
-
-
-
-✏️ *A 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 au [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 BERT pré-entraîné, 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 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 é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 (l'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:
- # Début d'un nouveau mot !
- current_word = word_id
- label = -100 if word_id is None else labels[word_id]
- new_labels.append(label)
- elif word_id is None:
- # Token spécial
- new_labels.append(-100)
- else:
- # Même mot que le token précédent
- label = labels[word_id]
- # Si l'étiquette est B-XXX, nous la changeons en 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é `-100` pour les deux *tokens* spéciaux du début et de fin, et un nouveau `0` pour notre mot qui a été divisé en deux *tokens*.
-
-
-
-✏️ *A votre tour !* Certains chercheurs préfèrent n'attribuer qu'une seule étiquette 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 identifiants d'entrée en suivant cette règle.
-
-
-Pour prétraiter notre jeu 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. Nous allons donc é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 identifiants 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 rembourré nos entrées. Nous le ferons plus tard lors de la création des batchs 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 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 utilisant `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 ainsi que la fonction de calcul de la métrique.
-
-{:else}
-
-## Finetuning du modèle avec Keras
-
-Le code 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 ainsi que la fonction de calcul de la métrique.
-
-{/if}
-
-
-### Collation des données
-
-Nous ne pouvons pas simplement utiliser un `DataCollatorWithPadding` comme dans le [chapitre 3](/course/fr/chapter3) car cela ne fait que rembourrer les entrées (identifiants d'entrée, masque d'attention et *token* de type identifiants). Ici, nos étiquettes doivent être rembourréés 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 des `-100`.
-
-{: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 d'étiquettes 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'identifiant à l'étiquette 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 d'étiquettes, vous obtiendrez plus tard une erreur obscure lors de l'appel de `model.fit()`. Cela peut être ennuyeux à déboguer 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 ! 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 le taux de décroissance des poids et le taux de décroissance de l'apprentissage, les deux améliorant les performances de votre modèle par rapport à l'optimiseur `Adam` :
-
-```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 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/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 d'étiquettes, et retourne un dictionnaire avec les noms et les valeurs des métriques.
-
-Le *framework* 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 `evaluate.load()` comme nous l'avons fait dans le [chapitre 3](/course/fr/chapter3) :
-
-{:else}
-
-Le *framework* 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 `evaluate.load()` comme nous l'avons fait dans le [chapitre 3](/course/fr/chapter3) :
-
-{/if}
-
-```py
-import evaluate
-
-metric = evaluate.load("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 la fonction 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 objet `model` 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 et en laissant de côté 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 d'étiquettes 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 des étiquettes à la place.
-
-Elles devraient être définies par deux dictionnaires, `id2label` et `label2id`, qui contiennent les correspondances entre identifiants et étiquettes 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()`, ils seront définis dans la configuration du modèle puis 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()` (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 le taux de décroissance des poids), et nous spécifions `push_to_hub=True` pour indiquer que nous voulons sauvegarder le modèle, 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 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/bert-finetuned-ner"``TrainingArguments`. Par défaut, le dépôt 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 réutilisons 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 *finetuning* d'avant et que nous repartons bien du modèle pré-entraîné de BERT :
-
-```py
-model = AutoModelForTokenClassification.from_pretrained(
- model_checkpoint,
- id2label=id2label,
- label2id=label2id,
-)
-```
-
-Ensuite, nous avons besoin d'un optimiseur. Nous utilisons le classique `AdamW`, qui est comme `Adam`, mais avec un correctif dans la façon dont le taux de décroissance 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 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 avons 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éterminons le nom du dépôt à partir de l'identifiant 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 et 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 dépôt dans un dossier local. S'il existe déjà, ce dossier local doit être un clone existant du dépôt 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()
-
- # 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)
- ]
- 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, 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 batch : 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 *finetuné* 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* d'une phrase, tels que :
+
+- la **reconnaissance d'entités nommées (NER de l'anglais *Named Entity Recognition*)**, c'est-à-dire trouver les entités (telles que des personnes, des lieux ou des organisations) dans une phrase. Ce tâche peut être formulée comme l'attribution d'une étiquette à chaque *token* faisant parti d'une entité en ayant une classe spécifique par entité, et une classe pour les *tokens* ne faisant pas parti d'entité.
+- le ***part-of-speech tagging* (POS)**, c'est-à-dire marquer chaque mot dans une phrase comme correspondant à une partie particulière (comme un nom, un verbe, un adjectif, etc.).
+- le ***chunking***, c'est-à-dire 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 *finetuner* un modèle (BERT) sur la tâche de NER. Il sera alors capable de calculer des prédictions comme celle-ci :
+
+
+
+
+
+
+
+
+
+Vous pouvez trouver, télécharger et vérifier les précisions de ce modèle sur le [*Hub*](https://huggingface.co/huggingface-course/bert-finetuned-ner?text=My+nom+est+Sylvain+et+je+travaille+à+Hugging+Face+in+Brooklyn) les prédictions du modèle que nous allons entraîner.
+
+## 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 dans ce jeu de données 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 entrées textuelles 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 en 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 reconnaître 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 disponibles pour l'entraînement mais 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`. 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 au [chapitre 6](/course/fr/chapter6/3) lorsque nous nous sommes intéressés au pipeline `token-classification` mais nosu pouvons tout de même faire un rapide rappel :
+
+- `O` signifie que le mot ne correspond à aucune entité.
+- `B-PER`/`I-PER` signifie que le mot correspond au début/est à l'intérieur d'une entité *personne*.
+- `B-ORG`/`I-ORG` signifie que le mot correspond au début/est à l'intérieur d'une entité *organisation*.
+- `B-LOC`/`I-LOC` signifie que le mot correspond au début/est à l'intérieur d'une entité *location*.
+- `B-MISC`/`I-MISC` signifie que le mot correspond au début/est à 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 le quatrième élément du jeu d'entraînement :
+
+```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 « European Union » et « Werner Zwingmann », se voient attribuer une étiquette `B-` pour le premier mot et une étiquette `I-` pour le second.
+
+
+
+✏️ *A 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 au [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 BERT pré-entraîné, 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 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 é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 (l'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:
+ # Début d'un nouveau mot !
+ current_word = word_id
+ label = -100 if word_id is None else labels[word_id]
+ new_labels.append(label)
+ elif word_id is None:
+ # Token spécial
+ new_labels.append(-100)
+ else:
+ # Même mot que le token précédent
+ label = labels[word_id]
+ # Si l'étiquette est B-XXX, nous la changeons en 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é `-100` pour les deux *tokens* spéciaux du début et de fin, et un nouveau `0` pour notre mot qui a été divisé en deux *tokens*.
+
+
+
+✏️ *A votre tour !* Certains chercheurs préfèrent n'attribuer qu'une seule étiquette 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 identifiants d'entrée en suivant cette règle.
+
+
+Pour prétraiter notre jeu 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. Nous allons donc é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 identifiants 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 rembourré nos entrées. Nous le ferons plus tard lors de la création des batchs 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 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 utilisant `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 ainsi que la fonction de calcul de la métrique.
+
+{:else}
+
+## Finetuning du modèle avec Keras
+
+Le code 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 ainsi que la fonction de calcul de la métrique.
+
+{/if}
+
+
+### Collation des données
+
+Nous ne pouvons pas simplement utiliser un `DataCollatorWithPadding` comme dans le [chapitre 3](/course/fr/chapter3) car cela ne fait que rembourrer les entrées (identifiants d'entrée, masque d'attention et *token* de type identifiants). Ici, nos étiquettes doivent être rembourréés 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 des `-100`.
+
+{: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 d'étiquettes 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'identifiant à l'étiquette 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 d'étiquettes, vous obtiendrez plus tard une erreur obscure lors de l'appel de `model.fit()`. Cela peut être ennuyeux à déboguer 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 ! 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 le taux de décroissance des poids et le taux de décroissance de l'apprentissage, les deux améliorant les performances de votre modèle par rapport à l'optimiseur `Adam` :
+
+```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 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/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 d'étiquettes, et retourne un dictionnaire avec les noms et les valeurs des métriques.
+
+Le *framework* 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 `evaluate.load()` comme nous l'avons fait dans le [chapitre 3](/course/fr/chapter3) :
+
+{:else}
+
+Le *framework* 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 `evaluate.load()` comme nous l'avons fait dans le [chapitre 3](/course/fr/chapter3) :
+
+{/if}
+
+```py
+import evaluate
+
+metric = evaluate.load("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 la fonction 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 objet `model` 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 et en laissant de côté 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 d'étiquettes 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 des étiquettes à la place.
+
+Elles devraient être définies par deux dictionnaires, `id2label` et `label2id`, qui contiennent les correspondances entre identifiants et étiquettes 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()`, ils seront définis dans la configuration du modèle puis 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()` (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 le taux de décroissance des poids), et nous spécifions `push_to_hub=True` pour indiquer que nous voulons sauvegarder le modèle, 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 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/bert-finetuned-ner"``TrainingArguments`. Par défaut, le dépôt 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 réutilisons 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 *finetuning* d'avant et que nous repartons bien du modèle pré-entraîné de BERT :
+
+```py
+model = AutoModelForTokenClassification.from_pretrained(
+ model_checkpoint,
+ id2label=id2label,
+ label2id=label2id,
+)
+```
+
+Ensuite, nous avons besoin d'un optimiseur. Nous utilisons le classique `AdamW`, qui est comme `Adam`, mais avec un correctif dans la façon dont le taux de décroissance 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 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 avons 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éterminons le nom du dépôt à partir de l'identifiant 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 et 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 dépôt dans un dossier local. S'il existe déjà, ce dossier local doit être un clone existant du dépôt 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()
+
+ # 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)
+ ]
+ 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, 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 batch : 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 *finetuné* 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/3.mdx b/chapters/fr/chapter7/3.mdx
index 89cd6a843..96f3b04ff 100644
--- a/chapters/fr/chapter7/3.mdx
+++ b/chapters/fr/chapter7/3.mdx
@@ -1,1042 +1,1042 @@
-
-
-# Finetuner un modèle de langage masqué
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Pour de nombreuses applications de NLP impliquant des *transformers*, vous pouvez simplement prendre un modèle pré-entraîné du *Hub* et le *finetuner* directement sur vos données pour la tâche à accomplir. Pour autant que le corpus utilisé pour le pré-entraînement ne soit pas trop différent du corpus utilisé pour le *finetuning*. L'apprentissage par transfert produira généralement de bons résultats.
-
-Cependant, il existe quelques cas où vous voudrez d'abord *finetuner* les modèles de langue sur vos données, avant d'entraîner une tête spécifique à la tâche. Par exemple, si votre jeu de données contient des contrats légaux ou des articles scientifiques, un *transformer* classique comme BERT traitera généralement les mots spécifiques au domaine dans votre corpus comme des *tokens* rares et les performances résultantes peuvent être moins que satisfaisantes. En *finetunant* le modèle de langage sur les données du domaine, vous pouvez améliorer les performances de nombreuses tâches en aval, ce qui signifie que vous ne devez généralement effectuer cette étape qu'une seule fois !
-
-Ce processus de *finetuning* d'un modèle de langage pré-entraîné sur des données *dans le domaine* est généralement appelé _adaptation au domaine_. Il a été popularisé en 2018 par [ULMFiT](https://arxiv.org/abs/1801.06146) qui a été l'une des premières architectures neuronales (basées sur des LSTMs) à faire en sorte que l'apprentissage par transfert fonctionne réellement pour le NLP. Un exemple d'adaptation de domaine avec ULMFiT est présenté dans l'image ci-dessous. Dans cette section, nous ferons quelque chose de similaire mais avec un *transformer* au lieu d'une LSTM !
-
-
-
-
-
-
-À la fin de cette section, vous aurez un [modèle de langage masqué](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.) sur le *Hub* qui peut autocompléter des phrases comme indiqué ci-dessous :
-
-
-
-
-Allons-y !
-
-
-
-
-
-🙋 Si les termes « modélisation du langage masqué » et « modèle pré-entraîné » ne vous sont pas familiers, consultez le [chapitre 1](/course/fr/chapiter1), où nous expliquons tous ces concepts fondamentaux, vidéos à l'appui !
-
-
-
-## Choix d'un modèle pré-entraîné pour la modélisation du langage masqué
-
-Pour commencer, nous allons choisir un modèle pré-entraîné approprié pour la modélisation du langage masqué. Comme le montre la capture d'écran suivante, vous pouvez trouver une liste de candidats en appliquant le filtre « *Fill-Mask* » sur le [*Hub*](https://huggingface.co/models?pipeline_tag=fill-mask&sort=downloads) :
-
-
-
-
-
-Bien que les modèles de la famille BERT et RoBERTa soient les plus téléchargés, nous utiliserons un modèle appelé [DistilBERT](https://huggingface.co/distilbert-base-uncased) qui peut être entraîné beaucoup plus rapidement avec peu ou pas de perte de performance en aval. Ce modèle a été entraîné à l'aide d'une technique spéciale appelée [_distillation de connaissances_](https://en.wikipedia.org/wiki/Knowledge_distillation), où un grand modèle *enseignant* comme BERT est utilisé pour guider l'entraînement d'un modèle *étudiant* qui a beaucoup moins de paramètres. Une explication des détails de la distillation de connaissances nous mènerait trop loin dans cette section mais si vous êtes intéressé, vous pouvez lire tout cela dans le livre [_Natural Language Processing with Transformers_](https://learning.oreilly.com/library/view/natural-language-processing/9781098103231/ch05.html).
-
-{#if fw === 'pt'}
-
-Allons-y et téléchargeons DistilBERT en utilisant la classe `AutoModelForMaskedLM` :
-
-```python
-from transformers import AutoModelForMaskedLM
-
-model_checkpoint = "distilbert-base-uncased"
-model = AutoModelForMaskedLM.from_pretrained(model_checkpoint)
-```
-
-Nous pouvons voir combien de paramètres ce modèle possède en appelant la méthode `num_parameters()` :
-
-```python
-distilbert_num_parameters = model.num_parameters() / 1_000_000
-print(f"'>>> DistilBERT nombre de paramètres : {round(distilbert_num_parameters)}M'")
-print(f"'>>> BERT nombre de paramètres : 110M'")
-```
-
-```python out
-'>>> DistilBERT nombre de paramètres : 67M'
-'>>> BERT nombre de paramètres : 110M'
-```
-
-{:else}
-
-Allons-y et téléchargeons DistilBERT en utilisant la classe `AutoModelForMaskedLM` :
-
-```python
-from transformers import TFAutoModelForMaskedLM
-
-model_checkpoint = "distilbert-base-uncased"
-model = TFAutoModelForMaskedLM.from_pretrained(model_checkpoint)
-```
-
-Nous pouvons voir combien de paramètres ce modèle possède en appelant la méthode `summary()` :
-
-```python
-model(model.dummy_inputs) # Construire le modèle
-model.summary()
-```
-
-```python out
-Model: "tf_distil_bert_for_masked_lm"
-_________________________________________________________________
-Layer (type) Output Shape Param #
-=================================================================
-distilbert (TFDistilBertMain multiple 66362880
-_________________________________________________________________
-vocab_transform (Dense) multiple 590592
-_________________________________________________________________
-vocab_layer_norm (LayerNorma multiple 1536
-_________________________________________________________________
-vocab_projector (TFDistilBer multiple 23866170
-=================================================================
-Total params: 66,985,530
-Trainable params: 66,985,530
-Non-trainable params: 0
-_________________________________________________________________
-```
-
-{/if}
-
-Avec environ 67 millions de paramètres, DistilBERT est environ deux fois plus petit que le modèle de base de BERT, ce qui se traduit approximativement par une accélération de l'entraînement d'un facteur deux. Voyons maintenant quels types de *tokens* ce modèle prédit comme étant les compléments les plus probables d'un petit échantillon de texte :
-
-```python
-text = "This is a great [MASK]."
-```
-
-En tant qu'êtres humains, nous pouvons imaginer de nombreuses possibilités pour le *token* `[MASK]`, telles que « jour », « promenade » ou « peinture ». Pour les modèles pré-entraînés, les prédictions dépendent du corpus sur lequel le modèle a été entraîné puisqu'il apprend à détecter les modèles statistiques présents dans les données. Comme BERT, DistilBERT a été pré-entraîné sur les jeux de données [*English Wikipedia*](https://huggingface.co/datasets/wikipedia) et [*BookCorpus*](https://huggingface.co/datasets/bookcorpus), nous nous attendons donc à ce que les prédictions pour `[MASK]` reflètent ces domaines. Pour prédire le masque, nous avons besoin du *tokenizer* de DistilBERT pour produire les entrées du modèle, alors téléchargeons-le également depuis le *Hub* :
-
-```python
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-```
-
-Avec un *tokenizer* et un modèle, nous pouvons maintenant passer notre exemple de texte au modèle, extraire les logits, et afficher les 5 meilleurs candidats :
-
-{#if fw === 'pt'}
-
-```python
-import torch
-
-inputs = tokenizer(text, return_tensors="pt")
-token_logits = model(**inputs).logits
-# Trouve l'emplacement de [MASK] et extrait ses logits
-mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]
-mask_token_logits = token_logits[0, mask_token_index, :]
-# Choisissez les candidats [MASK] avec les logits les plus élevés
-top_5_tokens = torch.topk(mask_token_logits, 5, dim=1).indices[0].tolist()
-
-for token in top_5_tokens:
- print(f"'>>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}'")
-```
-
-{:else}
-
-```python
-import numpy as np
-import tensorflow as tf
-
-inputs = tokenizer(text, return_tensors="np")
-token_logits = model(**inputs).logits
-# Trouve l'emplacement de [MASK] et extrait ses logits
-mask_token_index = np.argwhere(inputs["input_ids"] == tokenizer.mask_token_id)[0, 1]
-mask_token_logits = token_logits[0, mask_token_index, :]
-# On choisit les candidats [MASK] avec les logits les plus élevés
-# Nous annulons le tableau avant argsort pour obtenir le plus grand, et non le plus petit, logits
-top_5_tokens = np.argsort(-mask_token_logits)[:5].tolist()
-
-for token in top_5_tokens:
- print(f">>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}")
-```
-
-{/if}
-
-```python out
-'>>> This is a great deal.' # C'est une bonne affaire
-'>>> This is a great success.' # C'est un grand succès
-'>>> This is a great adventure.' # C'est une grande aventure
-'>>> This is a great idea.' # C'est une bonne idée
-'>>> This is a great feat.' # C'est un grand exploit
-```
-
-Nous pouvons voir dans les sorties que les prédictions du modèle se réfèrent à des termes de tous les jours, ce qui n'est peut-être pas surprenant étant donné le fondement de Wikipédia. Voyons comment nous pouvons changer ce domaine pour quelque chose d'un peu plus spécialisé : des critiques de films !
-
-
-## Le jeu de données
-
-Pour illustrer l'adaptation au domaine, nous utiliserons le célèbre [*Large Movie Review Dataset*](https://huggingface.co/datasets/imdb) (ou IMDb en abrégé), qui est un corpus de critiques de films souvent utilisé pour évaluer les modèles d'analyse de sentiments. En *finetunant* DistilBERT sur ce corpus, nous espérons que le modèle de langage adaptera son vocabulaire des données factuelles de Wikipédia sur lesquelles il a été pré-entraîné aux éléments plus subjectifs des critiques de films. Nous pouvons obtenir les données du *Hub* avec la fonction `load_dataset()` de 🤗 *Datasets* :
-
-```python
-from datasets import load_dataset
-
-imdb_dataset = load_dataset("imdb")
-imdb_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['text', 'label'],
- num_rows: 25000
- })
- test: Dataset({
- features: ['text', 'label'],
- num_rows: 25000
- })
- unsupervised: Dataset({
- features: ['text', 'label'],
- num_rows: 50000
- })
-})
-```
-
-Nous pouvons voir que les parties `train` et `test` sont chacune composées de 25 000 critiques, alors qu'il y a une partie non étiquetée appelée `unsupervised` qui contient 50 000 critiques. Jetons un coup d'œil à quelques échantillons pour avoir une idée du type de texte auquel nous avons affaire. Comme nous l'avons fait dans les chapitres précédents du cours, nous allons enchaîner les fonctions `Dataset.shuffle()` et `Dataset.select()` pour créer un échantillon aléatoire :
-
-```python
-sample = imdb_dataset["train"].shuffle(seed=42).select(range(3))
-
-for row in sample:
- print(f"\n'>>> Review: {row['text']}'")
- print(f"'>>> Label: {row['label']}'")
-```
-
-```python out
-
-'>>> Review: This is your typical Priyadarshan movie--a bunch of loony characters out on some silly mission. His signature climax has the entire cast of the film coming together and fighting each other in some crazy moshpit over hidden money. Whether it is a winning lottery ticket in Malamaal Weekly, black money in Hera Pheri, "kodokoo" in Phir Hera Pheri, etc., etc., the director is becoming ridiculously predictable. Don\'t get me wrong; as clichéd and preposterous his movies may be, I usually end up enjoying the comedy. However, in most his previous movies there has actually been some good humor, (Hungama and Hera Pheri being noteworthy ones). Now, the hilarity of his films is fading as he is using the same formula over and over again.
Songs are good. Tanushree Datta looks awesome. Rajpal Yadav is irritating, and Tusshar is not a whole lot better. Kunal Khemu is OK, and Sharman Joshi is the best.'
-'>>> Label: 0'
-
-'>>> Review: Okay, the story makes no sense, the characters lack any dimensionally, the best dialogue is ad-libs about the low quality of movie, the cinematography is dismal, and only editing saves a bit of the muddle, but Sam" Peckinpah directed the film. Somehow, his direction is not enough. For those who appreciate Peckinpah and his great work, this movie is a disappointment. Even a great cast cannot redeem the time the viewer wastes with this minimal effort.
The proper response to the movie is the contempt that the director San Peckinpah, James Caan, Robert Duvall, Burt Young, Bo Hopkins, Arthur Hill, and even Gig Young bring to their work. Watch the great Peckinpah films. Skip this mess.'
-'>>> Label: 0'
-
-'>>> Review: I saw this movie at the theaters when I was about 6 or 7 years old. I loved it then, and have recently come to own a VHS version.
My 4 and 6 year old children love this movie and have been asking again and again to watch it.
I have enjoyed watching it again too. Though I have to admit it is not as good on a little TV.
I do not have older children so I do not know what they would think of it.
The songs are very cute. My daughter keeps singing them over and over.
Hope this helps.'
-'>>> Label: 1'
-```
-
-Oui, ce sont bien des critiques de films, et si vous êtes assez âgés, vous pouvez même comprendre le commentaire dans la dernière critique sur le fait de posséder une version VHS 😜 ! Bien que nous n'ayons pas besoin des étiquettes pour la modélisation du langage, nous pouvons déjà voir qu'un `0` dénote une critique négative, tandis qu'un `1` correspond à une critique positive.
-
-
-
-✏️ **Essayez !** Créez un échantillon aléatoire de la répartition `unsupervised` et vérifiez que les étiquettes ne sont ni `0` ni `1`. Pendant que vous y êtes, vous pouvez aussi vérifier que les étiquettes dans les échantillons `train` et `test` sont bien `0` ou `1`. C'est un contrôle utile que tout praticien en NLP devrait effectuer au début d'un nouveau projet !
-
-
-
-Maintenant que nous avons jeté un coup d'œil rapide aux données, plongeons dans leur préparation pour la modélisation du langage masqué. Comme nous allons le voir, il y a quelques étapes supplémentaires à suivre par rapport aux tâches de classification de séquences que nous avons vues au [chapitre 3](/course/fr/chapter3). Allons-y !
-
-## Prétraitement des données
-
-
-
-Pour la modélisation autorégressive et la modélisation du langage masqué, une étape commune de prétraitement consiste à concaténer tous les exemples, puis à diviser le corpus entier en morceaux de taille égale. C'est très différent de notre approche habituelle, où nous nous contentons de *tokenizer* les exemples individuels. Pourquoi tout concaténer ? La raison est que les exemples individuels peuvent être tronqués s'ils sont trop longs, ce qui entraînerait la perte d'informations qui pourraient être utiles pour la tâche de modélisation du langage !
-
-Donc pour commencer, nous allons d'abord tokeniser notre corpus comme d'habitude, mais _sans_ mettre l'option `truncation=True` dans notre *tokenizer*. Nous allons aussi récupérer les identifiants des mots s'ils sont disponibles (ce qui sera le cas si nous utilisons un *tokenizer* rapide, comme décrit dans le [chapitre 6](/course/fr/chapter6/3)), car nous en aurons besoin plus tard pour faire le masquage de mots entiers. Nous allons envelopper cela dans une simple fonction, et pendant que nous y sommes, nous allons supprimer les colonnes `text` et `label` puisque nous n'en avons plus besoin :
-
-```python
-def tokenize_function(examples):
- result = tokenizer(examples["text"])
- if tokenizer.is_fast:
- result["word_ids"] = [result.word_ids(i) for i in range(len(result["input_ids"]))]
- return result
-
-
-# Utilisation de batched=True pour activer le multithreading rapide !
-tokenized_datasets = imdb_dataset.map(
- tokenize_function, batched=True, remove_columns=["text", "label"]
-)
-tokenized_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['attention_mask', 'input_ids', 'word_ids'],
- num_rows: 25000
- })
- test: Dataset({
- features: ['attention_mask', 'input_ids', 'word_ids'],
- num_rows: 25000
- })
- unsupervised: Dataset({
- features: ['attention_mask', 'input_ids', 'word_ids'],
- num_rows: 50000
- })
-})
-```
-
-Comme DistilBERT est un modèle de type BERT, nous pouvons voir que les textes encodés sont constitués des `input_ids` et des `attention_mask` que nous avons vus dans d'autres chapitres, ainsi que des `word_ids` que nous avons ajoutés.
-
-Maintenant que nos critiques de films ont été tokenisées, l'étape suivante consiste à les regrouper et à diviser le résultat en chunks. Mais quelle taille doivent avoir ces *chunks* ? Cela sera finalement déterminé par la quantité de mémoire GPU dont vous disposez, mais un bon point de départ est de voir quelle est la taille maximale du contexte du modèle. Cela peut être déduit en inspectant l'attribut `model_max_length` du *tokenizer* :
-
-```python
-tokenizer.model_max_length
-```
-
-
-```python out
-512
-```
-
-Cette valeur est dérivée du fichier *tokenizer_config.json* associé à un *checkpoint*. Dans ce cas, nous pouvons voir que la taille du contexte est de 512 *tokens*, tout comme avec BERT.
-
-
-
-✏️ **Essayez !** Certains *transformers*, comme [BigBird](https://huggingface.co/google/bigbird-roberta-base) et [Longformer](hf.co/allenai/longformer-base-4096), ont une longueur de contexte beaucoup plus longue que BERT et les autres premiers *transformers*. Instanciez le *tokenizer* pour l'un de ces *checkpoints* et vérifiez que le `model_max_length` correspond à ce qui est indiqué sur sa carte.
-
-
-
-Ainsi, pour réaliser nos expériences sur des GPUs comme ceux disponibles sur Google Colab, nous choisirons quelque chose d'un peu plus petit qui peut tenir en mémoire :
-
-```python
-chunk_size = 128
-```
-
-
-
-Notez que l'utilisation d'une petite taille peut être préjudiciable dans les scénarios du monde réel. Vous devez donc utiliser une taille qui correspond au cas d'utilisation auquel vous appliquerez votre modèle.
-
-
-
-Maintenant vient la partie amusante. Pour montrer comment la concaténation fonctionne, prenons quelques commentaires de notre ensemble d'entraînement et affichons le nombre de *tokens* par commentaire :
-
-```python
-# Le découpage produit une liste de listes pour chaque caractéristique
-tokenized_samples = tokenized_datasets["train"][:3]
-
-for idx, sample in enumerate(tokenized_samples["input_ids"]):
- print(f"'>>> Review {idx} length: {len(sample)}'")
-```
-
-```python out
-'>>> Review 0 length: 200'
-'>>> Review 1 length: 559'
-'>>> Review 2 length: 192'
-```
-
-Nous pouvons ensuite concaténer tous ces exemples avec une simple compréhension du dictionnaire, comme suit :
-
-```python
-concatenated_examples = {
- k: sum(tokenized_samples[k], []) for k in tokenized_samples.keys()
-}
-total_length = len(concatenated_examples["input_ids"])
-print(f"'>>> Longueur des critiques concaténées : {total_length}'")
-```
-
-```python out
-'>>> Longueur des critiques concaténées : 951'
-```
-
-Super, la longueur totale est correcte. Donc maintenant, nous allons diviser les exemples concaténés en morceaux de la taille donnée par `block_size`. Pour ce faire, nous itérons sur les caractéristiques de `concatenated_examples` et utilisons une compréhension de liste pour créer des *chunks* de chaque caractéristique. Le résultat est un dictionnaire de *chunks* pour chaque caractéristique :
-
-```python
-chunks = {
- k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)]
- for k, t in concatenated_examples.items()
-}
-
-for chunk in chunks["input_ids"]:
- print(f"'>>> Chunk length: {len(chunk)}'")
-```
-
-```python out
-'>>> Chunk length: 128'
-'>>> Chunk length: 128'
-'>>> Chunk length: 128'
-'>>> Chunk length: 128'
-'>>> Chunk length: 128'
-'>>> Chunk length: 128'
-'>>> Chunk length: 128'
-'>>> Chunk length: 55'
-```
-
-Comme vous pouvez le voir dans cet exemple, le dernier *chunk* sera généralement plus petit que la taille maximale des morceaux. Il y a deux stratégies principales pour gérer cela :
-
-* Abandonner le dernier morceau s'il est plus petit que `chunk_size`.
-* Rembourrer le dernier morceau jusqu'à ce que sa longueur soit égale à `chunk_size`.
-
-Nous adopterons la première approche ici, donc nous allons envelopper toute la logique ci-dessus dans une seule fonction que nous pouvons appliquer à nos jeux de données tokenisés :
-
-```python
-def group_texts(examples):
- # Concaténation de tous les textes
- concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
- # Calcule la longueur des textes concaténés
- total_length = len(concatenated_examples[list(examples.keys())[0]])
- # Nous laissons tomber le dernier morceau s'il est plus petit que chunk_size
- total_length = (total_length // chunk_size) * chunk_size
- # Fractionnement par chunk de max_len
- result = {
- k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)]
- for k, t in concatenated_examples.items()
- }
- # Créer une nouvelle colonne d'étiquettes
- result["labels"] = result["input_ids"].copy()
- return result
-```
-
-Notez que dans la dernière étape de `group_texts()` nous créons une nouvelle colonne `labels` qui est une copie de la colonne `input_ids`. Comme nous le verrons bientôt, c'est parce que dans la modélisation du langage masqué, l'objectif est de prédire des *tokens* masqués aléatoirement dans le batch d'entrée, et en créant une colonne `labels`, nous fournissons la vérité de base pour notre modèle de langage à apprendre.
-
-Appliquons maintenant `group_texts()` à nos jeux de données tokenisés en utilisant notre fidèle fonction `Dataset.map()` :
-
-```python
-lm_datasets = tokenized_datasets.map(group_texts, batched=True)
-lm_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
- num_rows: 61289
- })
- test: Dataset({
- features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
- num_rows: 59905
- })
- unsupervised: Dataset({
- features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
- num_rows: 122963
- })
-})
-```
-
-Vous pouvez voir que le regroupement puis le découpage des textes a produit beaucoup plus d'exemples que nos 25 000 exemples initiaux pour les divisions `train` et `test`. C'est parce que nous avons maintenant des exemples impliquant des *tokens* contigus qui s'étendent sur plusieurs exemples du corpus original. Vous pouvez le voir explicitement en cherchant les *tokens* spéciaux `[SEP]` et `[CLS]` dans l'un des *chunks* :
-
-```python
-tokenizer.decode(lm_datasets["train"][1]["input_ids"])
-```
-
-```python out
-".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless"
-```
-
-Dans cet exemple, vous pouvez voir deux critiques de films qui se chevauchent, l'une sur un film de lycée et l'autre sur les sans-abri. Voyons également à quoi ressemblent les étiquettes pour la modélisation du langage masqué :
-
-```python out
-tokenizer.decode(lm_datasets["train"][1]["labels"])
-```
-
-```python out
-".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless"
-```
-
-Comme prévu par notre fonction `group_texts()` ci-dessus, cela semble identique aux `input_ids` décodés. Mais alors comment notre modèle peut-il apprendre quoi que ce soit ? Il nous manque une étape clé : insérer des *tokens* à des positions aléatoires dans les entrées ! Voyons comment nous pouvons le faire à la volée pendant le *finetuning* en utilisant un collateur de données spécial.
-
-## Finetuning de DistilBERT avec l'API `Trainer`
-
-Le *finetuning* d'un modèle de langage masqué est presque identique au *finetuning* d'un modèle de classification de séquences, comme nous l'avons fait dans le [chapitre 3](/course/fr/chapter3). La seule différence est que nous avons besoin d'un collecteur de données spécial qui peut masquer de manière aléatoire certains des *tokens* dans chaque batch de textes. Heureusement, 🤗 *Transformers* est livré préparé avec un `DataCollatorForLanguageModeling` dédié à cette tâche. Nous devons juste lui passer le *tokenizer* et un argument `mlm_probability` qui spécifie quelle fraction des *tokens* à masquer. Nous choisirons 15%, qui est la quantité utilisée pour BERT et un choix commun dans la littérature :
-
-```python
-from transformers import DataCollatorForLanguageModeling
-
-data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=0.15)
-```
-
-Pour voir comment le masquage aléatoire fonctionne, nous allons donner quelques exemples au collateur de données. Puisqu'il s'attend à une liste de `dict` où chaque `dict` représente un seul morceau de texte contigu, nous itérons d'abord sur le jeu de données avant de donner le batch au collateur. Nous supprimons la clé `"word_ids"` pour ce collateur de données car il ne l'attend pas :
-
-```python
-samples = [lm_datasets["train"][i] for i in range(2)]
-for sample in samples:
- _ = sample.pop("word_ids")
-
-for chunk in data_collator(samples)["input_ids"]:
- print(f"\n'>>> {tokenizer.decode(chunk)}'")
-```
-
-```python output
-'>>> [CLS] bromwell [MASK] is a cartoon comedy. it ran at the same [MASK] as some other [MASK] about school life, [MASK] as " teachers ". [MASK] [MASK] [MASK] in the teaching [MASK] lead [MASK] to believe that bromwell high\'[MASK] satire is much closer to reality than is " teachers ". the scramble [MASK] [MASK] financially, the [MASK]ful students whogn [MASK] right through [MASK] pathetic teachers\'pomp, the pettiness of the whole situation, distinction remind me of the schools i knew and their students. when i saw [MASK] episode in [MASK] a student repeatedly tried to burn down the school, [MASK] immediately recalled. [MASK]...'
-
-'>>> .... at.. [MASK]... [MASK]... high. a classic line plucked inspector : i\'[MASK] here to [MASK] one of your [MASK]. student : welcome to bromwell [MASK]. i expect that many adults of my age think that [MASK]mwell [MASK] is [MASK] fetched. what a pity that it isn\'t! [SEP] [CLS] [MASK]ness ( or [MASK]lessness as george 宇in stated )公 been an issue for years but never [MASK] plan to help those on the street that were once considered human [MASK] did everything from going to school, [MASK], [MASK] vote for the matter. most people think [MASK] the homeless'
-```
-
-Super, ça a marché ! Nous pouvons voir que le *token* `[MASK]` a été inséré de façon aléatoire à différents endroits dans notre texte. Ce seront les *tokens* que notre modèle devra prédire pendant l'entraînement. Et la beauté du collecteur de données est qu'il va rendre aléatoire l'insertion du `[MASK]` à chaque batch !
-
-
-
-✏️ **Essayez** Exécutez le code ci-dessus plusieurs fois pour voir le masquage aléatoire se produire sous vos yeux ! Remplacez aussi la méthode `tokenizer.decode()` par `tokenizer.convert_ids_to_tokens()` pour voir que parfois un seul *token* d'un mot donné est masqué et pas les autres.
-
-
-
-{#if fw === 'pt'}
-
-Un effet secondaire du masquage aléatoire est que nos métriques d'évaluation ne seront pas déterministes lorsque nous utilisons la fonction `Trainer` puisque nous utilisons le même collateur de données pour les échantillons d'entraînement et de test. Nous verrons plus tard, lorsque nous examinerons le *finetuning* avec 🤗 *Accelerate*, comment nous pouvons utiliser la flexibilité d'une boucle d'évaluation personnalisée pour geler le caractère aléatoire.
-
-{/if}
-
-Lors de l'entraînement des modèles pour la modélisation du langage masqué, une technique qui peut être utilisée est de masquer des mots entiers ensemble et pas seulement des *tokens* individuels. Cette approche est appelée _masquage de mots entiers_. Si nous voulons utiliser le masquage de mots entiers, nous devons construire nous-mêmes un collateur de données. Un collateur de données est simplement une fonction qui prend une liste d'échantillons et les convertit en un batch. Faisons-le ! Nous utiliserons les identifiants des mots calculés plus tôt pour faire une correspondance entre les indices des mots et les *tokens*, puis nous déciderons aléatoirement quels mots masquer et appliquerons ce masque sur les entrées. Notez que les étiquettes sont toutes `-100` sauf celles qui correspondent aux mots masqués.
-
-{#if fw === 'pt'}
-
-```py
-import collections
-import numpy as np
-
-from transformers import default_data_collator
-
-wwm_probability = 0.2
-
-
-def whole_word_masking_data_collator(features):
- for feature in features:
- word_ids = feature.pop("word_ids")
-
- # Création d'une correspondance entre les mots et les indices des tokens correspondants
- mapping = collections.defaultdict(list)
- current_word_index = -1
- current_word = None
- for idx, word_id in enumerate(word_ids):
- if word_id is not None:
- if word_id != current_word:
- current_word = word_id
- current_word_index += 1
- mapping[current_word_index].append(idx)
-
- # Masquer des mots de façon aléatoire
- mask = np.random.binomial(1, wwm_probability, (len(mapping),))
- input_ids = feature["input_ids"]
- labels = feature["labels"]
- new_labels = [-100] * len(labels)
- for word_id in np.where(mask)[0]:
- word_id = word_id.item()
- for idx in mapping[word_id]:
- new_labels[idx] = labels[idx]
- input_ids[idx] = tokenizer.mask_token_id
-
- return default_data_collator(features)
-```
-
-{:else}
-
-```py
-import collections
-import numpy as np
-
-from transformers.data.data_collator import tf_default_data_collator
-
-wwm_probability = 0.2
-
-
-def whole_word_masking_data_collator(features):
- for feature in features:
- word_ids = feature.pop("word_ids")
-
- # Création d'une correspondance entre les mots et les indices des tokens correspondants
- mapping = collections.defaultdict(list)
- current_word_index = -1
- current_word = None
- for idx, word_id in enumerate(word_ids):
- if word_id is not None:
- if word_id != current_word:
- current_word = word_id
- current_word_index += 1
- mapping[current_word_index].append(idx)
-
- # Masquer des mots de façon aléatoire
- mask = np.random.binomial(1, wwm_probability, (len(mapping),))
- input_ids = feature["input_ids"]
- labels = feature["labels"]
- new_labels = [-100] * len(labels)
- for word_id in np.where(mask)[0]:
- word_id = word_id.item()
- for idx in mapping[word_id]:
- new_labels[idx] = labels[idx]
- input_ids[idx] = tokenizer.mask_token_id
-
- return tf_default_data_collator(features)
-```
-
-{/if}
-
-Ensuite, nous pouvons l'essayer sur les mêmes échantillons que précédemment :
-
-```py
-samples = [lm_datasets["train"][i] for i in range(2)]
-batch = whole_word_masking_data_collator(samples)
-
-for chunk in batch["input_ids"]:
- print(f"\n'>>> {tokenizer.decode(chunk)}'")
-```
-
-```python out
-'>>> [CLS] bromwell high is a cartoon comedy [MASK] it ran at the same time as some other programs about school life, such as " teachers ". my 35 years in the teaching profession lead me to believe that bromwell high\'s satire is much closer to reality than is " teachers ". the scramble to survive financially, the insightful students who can see right through their pathetic teachers\'pomp, the pettiness of the whole situation, all remind me of the schools i knew and their students. when i saw the episode in which a student repeatedly tried to burn down the school, i immediately recalled.....'
-
-'>>> .... [MASK] [MASK] [MASK] [MASK]....... high. a classic line : inspector : i\'m here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn\'t! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless'
-```
-
-
-
-✏️ **Essayez** Exécutez le code ci-dessus plusieurs fois pour voir le masquage aléatoire se produire sous vos yeux ! Remplacez aussi la méthode `tokenizer.decode()` par `tokenizer.convert_ids_to_tokens()` pour voir que les *tokens* d'un mot donné sont toujours masqués ensemble.
-
-
-
-Maintenant que nous avons deux collateurs de données, les étapes restantes du *finetuning* sont standards. L'entraînement peut prendre un certain temps sur Google Colab si vous n'avez pas la chance de tomber sur un mythique GPU P100 😭. Ainsi nous allons d'abord réduire la taille du jeu d'entraînement à quelques milliers d'exemples. Ne vous inquiétez pas, nous obtiendrons quand même un modèle de langage assez décent ! Un moyen rapide de réduire la taille d'un jeu de données dans 🤗 *Datasets* est la fonction `Dataset.train_test_split()` que nous avons vue au [chapitre 5](/course/fr/chapter5) :
-
-```python
-train_size = 10_000
-test_size = int(0.1 * train_size)
-
-downsampled_dataset = lm_datasets["train"].train_test_split(
- train_size=train_size, test_size=test_size, seed=42
-)
-downsampled_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
- num_rows: 10000
- })
- test: Dataset({
- features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
- num_rows: 1000
- })
-})
-```
-
-Cela a automatiquement créé de nouvelles divisions `train` et `test` avec la taille du jeu d'entraînement fixée à 10.000 exemples et la validation fixée à 10% de cela. N'hésitez pas à augmenter la taille si vous avez un GPU puissant ! 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 suivante :
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-qui affichera un *widget* où vous pourrez saisir vos informations d'identification. Alternativement, vous pouvez exécuter :
-
-```
-huggingface-cli login
-```
-
-dans votre terminal préféré et connectez-vous là.
-
-{#if fw === 'tf'}
-
-Une fois que nous sommes connectés, nous pouvons créer nos jeux de données `tf.data`. Nous n'utiliserons ici que le collecteur de données standard, mais vous pouvez également essayer le collecteur de masquage de mots entiers et comparer les résultats à titre d'exercice :
-
-```python
-tf_train_dataset = downsampled_dataset["train"].to_tf_dataset(
- columns=["input_ids", "attention_mask", "labels"],
- collate_fn=data_collator,
- shuffle=True,
- batch_size=32,
-)
-
-tf_eval_dataset = downsampled_dataset["test"].to_tf_dataset(
- columns=["input_ids", "attention_mask", "labels"],
- collate_fn=data_collator,
- shuffle=False,
- batch_size=32,
-)
-```
-
-Ensuite, nous configurons nos hyperparamètres d'entraînement et compilons notre modèle. Nous utilisons la fonction `create_optimizer()` de la bibliothèque 🤗 *Transformers*, qui nous donne un optimiseur `AdamW` avec une décroissance linéaire du taux d'apprentissage. Nous utilisons également la perte intégrée au modèle, qui est la perte par défaut lorsqu'aucune perte n'est spécifiée comme argument de `compile()`, et nous définissons la précision d'entraînement à `"mixed_float16"`. Notez que si vous utilisez un GPU Colab ou un autre GPU qui n'a pas le support accéléré en float16, vous devriez probablement commenter cette ligne.
-
-De plus, nous mettons en place un `PushToHubCallback` qui sauvegardera le modèle sur le *Hub* après chaque époque. 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, pour pousser le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/distilbert-finetuned-imdb"`. 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, ce sera `"lewtun/distilbert-finetuned-imdb"`.
-
-```python
-from transformers import create_optimizer
-from transformers.keras_callbacks import PushToHubCallback
-import tensorflow as tf
-
-num_train_steps = len(tf_train_dataset)
-optimizer, schedule = create_optimizer(
- init_lr=2e-5,
- num_warmup_steps=1_000,
- 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")
-
-callback = PushToHubCallback(
- output_dir=f"{model_name}-finetuned-imdb", tokenizer=tokenizer
-)
-```
-
-Nous sommes maintenant prêts à exécuter `model.fit()`. Mais avant, regardons brièvement la _perplexité_ qui est une métrique commune pour évaluer la performance des modèles de langage.
-
-{:else}
-
-Une fois que nous sommes connectés, nous pouvons spécifier les arguments pour le `Trainer` :
-
-```python
-from transformers import TrainingArguments
-
-batch_size = 64
-# Montrer la perte d'entraînement à chaque époque
-logging_steps = len(downsampled_dataset["train"]) // batch_size
-model_name = model_checkpoint.split("/")[-1]
-
-training_args = TrainingArguments(
- output_dir=f"{model_name}-finetuned-imdb",
- overwrite_output_dir=True,
- evaluation_strategy="epoch",
- learning_rate=2e-5,
- weight_decay=0.01,
- per_device_train_batch_size=batch_size,
- per_device_eval_batch_size=batch_size,
- push_to_hub=True,
- fp16=True,
- logging_steps=logging_steps,
-)
-```
-
-Ici, nous avons modifié quelques options par défaut, y compris `logging_steps` pour s'assurer que nous suivons la perte d'entraînement à chaque époque. Nous avons également utilisé `fp16=True` pour activer l'entraînement en précision mixte, ce qui nous donne un autre gain de vitesse. Par défaut, `Trainer` va supprimer toutes les colonnes qui ne font pas partie de la méthode `forward()` du modèle. Cela signifie que si vous utilisez le collateur de masquage de mots entiers, vous devrez également définir `remove_unused_columns=False` pour vous assurer que nous ne perdons pas la colonne `word_ids` pendant l'entraînement.
-
-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/distilbert-finetuned-imdb"` `TrainingArguments`. 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 ce sera `"lewtun/distilbert-finetuned-imdb"`.
-
-Nous avons maintenant tous les ingrédients pour instancier le `Trainer`. Ici, nous utilisons juste le collateur standard `data_collator`, mais vous pouvez essayer le collateur de masquage de mots entiers et comparer les résultats comme exercice :
-
-```python
-from transformers import Trainer
-
-trainer = Trainer(
- model=model,
- args=training_args,
- train_dataset=downsampled_dataset["train"],
- eval_dataset=downsampled_dataset["test"],
- data_collator=data_collator,
-)
-```
-
-Nous sommes maintenant prêts à exécuter `trainer.train()`. Mais avant, regardons brièvement la _perplexité_ qui est une métrique commune pour évaluer la performance des modèles de langage.
-
-{/if}
-
-### Perplexité pour les modèles de langage
-
-
-
-Contrairement à d'autres tâches, comme la classification de textes ou la réponse à des questions, sur lesquelles nous disposons d'un corpus étiqueté pour entraîner, la modélisation du langage ne s'appuie sur aucune étiquette explicite. Alors comment déterminer ce qui fait un bon modèle de langage ? Comme pour la fonction de correction automatique de votre téléphone, un bon modèle de langage est celui qui attribue des probabilités élevées aux phrases grammaticalement correctes et des probabilités faibles aux phrases absurdes. Pour vous donner une meilleure idée de ce à quoi cela ressemble, vous pouvez trouver en ligne des séries entières de « ratés d'autocorrection » où le modèle d'un téléphone produit des compléments plutôt amusants (et souvent inappropriés) !
-
-{#if fw === 'pt'}
-
-En supposant que notre ensemble de test se compose principalement de phrases grammaticalement correctes, une façon de mesurer la qualité de notre modèle de langage est de calculer les probabilités qu'il attribue au mot suivant dans toutes les phrases de l'ensemble de test. Des probabilités élevées indiquent que le modèle n'est pas « surpris » ou « perplexe » vis-à-vis des exemples non vus, et suggèrent qu'il a appris les modèles de base de la grammaire de la langue. Il existe plusieurs définitions mathématiques de la perplexité. Celle que nous utiliserons la définit comme l'exponentielle de la perte d'entropie croisée. Ainsi, nous pouvons calculer la perplexité de notre modèle pré-entraîné en utilisant la fonction `Trainer.evaluate()` pour calculer la perte d'entropie croisée sur l'ensemble de test, puis en prenant l'exponentielle du résultat :
-
-```python
-import math
-
-eval_results = trainer.evaluate()
-print(f">>> Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
-```
-
-{:else}
-
-En supposant que notre ensemble de test se compose principalement de phrases grammaticalement correctes, une façon de mesurer la qualité de notre modèle de langage est de calculer les probabilités qu'il attribue au mot suivant dans toutes les phrases de l'ensemble de test. Des probabilités élevées indiquent que le modèle n'est pas « surpris » ou « perplexe » vis-à-vis des exemples non vus, et suggèrent qu'il a appris les modèles de base de la grammaire de la langue. Il existe plusieurs définitions mathématiques de la perplexité. Celle que nous utiliserons la définit comme l'exponentielle de la perte d'entropie croisée. Ainsi, nous pouvons calculer la perplexité de notre modèle pré-entraîné en utilisant la fonction `model.evaluate()` pour calculer la perte d'entropie croisée sur l'ensemble de test, puis en prenant l'exponentielle du résultat :
-
-```python
-import math
-
-eval_loss = model.evaluate(tf_eval_dataset)
-print(f"Perplexité : {math.exp(eval_loss):.2f}")
-```
-
-{/if}
-
-```python out
->>> Perplexité : 21.75
-```
-
-Un score de perplexité faible signifie un meilleur modèle de langue. Nous pouvons voir ici que notre modèle de départ a une valeur assez élevée. Voyons si nous pouvons la réduire en l'affinant ! Pour ce faire, nous commençons par exécuter la boucle d'entraînement :
-
-{#if fw === 'pt'}
-
-```python
-trainer.train()
-```
-
-{:else}
-
-```python
-model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback])
-```
-
-{/if}
-
-et ensuite calculer la perplexité résultante sur l'ensemble de test comme précédemment :
-
-{#if fw === 'pt'}
-
-```python
-eval_results = trainer.evaluate()
-print(f">>> Perplexité : {math.exp(eval_results['eval_loss']):.2f}")
-```
-
-{:else}
-
-```python
-eval_loss = model.evaluate(tf_eval_dataset)
-print(f"Perplexité : {math.exp(eval_loss):.2f}")
-```
-
-{/if}
-
-```python out
->>> Perplexité : 11.32
-```
-
-Joli. C'est une réduction considérable de la perplexité, ce qui nous indique que le modèle a appris quelque chose sur le domaine des critiques de films !
-
-{#if fw === 'pt'}
-
-Une fois l'entraînement terminé, nous pouvons pousser la carte de modèle avec les informations d'entraînement vers le *Hub* (les *checkpoints* sont sauvegardés pendant l'entraînement lui-même) :
-
-```python
-trainer.push_to_hub()
-```
-
-{/if}
-
-
-
-✏️ **A votre tour !** Exécutez l'entraînement ci-dessus après avoir remplacé le collecteur de données par le collecteur de mots entiers masqués. Obtenez-vous de meilleurs résultats ?
-
-
-
-{#if fw === 'pt'}
-
-Dans notre cas d'utilisation, nous n'avons pas eu besoin de faire quelque chose de spécial avec la boucle d'entraînement, mais dans certains cas, vous pourriez avoir besoin de mettre en œuvre une logique personnalisée. Pour ces applications, vous pouvez utiliser 🤗 *Accelerate*. Jetons un coup d'œil !
-
-## Finetuning de DistilBERT avec 🤗 Accelerate
-
-Comme nous l'avons vu, avec `Trainer` le *finetuning* d'un modèle de langage masqué est très similaire à l'exemple de classification de texte du [chapitre 3](/course/fr/chapter3). En fait, la seule subtilité est l'utilisation d'un collateur de données spécial, et nous l'avons déjà couvert plus tôt dans cette section !
-
-Cependant, nous avons vu que `DataCollatorForLanguageModeling` applique aussi un masquage aléatoire à chaque évaluation. Nous verrons donc quelques fluctuations dans nos scores de perplexité à chaque entrainement. Une façon d'éliminer cette source d'aléat est d'appliquer le masquage _une fois_ sur l'ensemble de test, puis d'utiliser le collateur de données par défaut dans 🤗 *Transformers* pour collecter les batchs pendant l'évaluation. Pour voir comment cela fonctionne, implémentons une fonction simple qui applique le masquage sur un batch, similaire à notre première rencontre avec `DataCollatorForLanguageModeling` :
-
-```python
-def insert_random_mask(batch):
- features = [dict(zip(batch, t)) for t in zip(*batch.values())]
- masked_inputs = data_collator(features)
- # Créer une nouvelle colonne "masquée" pour chaque colonne du jeu de données
- return {"masked_" + k: v.numpy() for k, v in masked_inputs.items()}
-```
-
-Ensuite, nous allons appliquer cette fonction à notre jeu de test et laisser tomber les colonnes non masquées afin de les remplacer par les colonnes masquées. Vous pouvez utiliser le masquage de mots entiers en remplaçant le `data_collator` ci-dessus par celui qui est approprié. Dans ce cas vous devez supprimer la première ligne ici :
-
-```py
-downsampled_dataset = downsampled_dataset.remove_columns(["word_ids"])
-eval_dataset = downsampled_dataset["test"].map(
- insert_random_mask,
- batched=True,
- remove_columns=downsampled_dataset["test"].column_names,
-)
-eval_dataset = eval_dataset.rename_columns(
- {
- "masked_input_ids": "input_ids",
- "masked_attention_mask": "attention_mask",
- "masked_labels": "labels",
- }
-)
-```
-
-Nous pouvons ensuite configurer les *dataloaders* comme d'habitude, mais nous utiliserons le `default_data_collator` de 🤗 *Transformers* pour le jeu d'évaluation :
-
-```python
-from torch.utils.data import DataLoader
-from transformers import default_data_collator
-
-batch_size = 64
-train_dataloader = DataLoader(
- downsampled_dataset["train"],
- shuffle=True,
- batch_size=batch_size,
- collate_fn=data_collator,
-)
-eval_dataloader = DataLoader(
- eval_dataset, batch_size=batch_size, collate_fn=default_data_collator
-)
-```
-
-Nous suivons les étapes standard avec 🤗 *Accelerate*. La première est de charger une version fraîche du modèle pré-entraîné :
-
-```
-model = AutoModelForMaskedLM.from_pretrained(model_checkpoint)
-```
-
-Ensuite, nous devons spécifier l'optimiseur. Nous utiliserons le standard `AdamW` :
-
-```python
-from torch.optim import AdamW
-
-optimizer = AdamW(model.parameters(), lr=5e-5)
-```
-
-Avec ces objets, nous pouvons maintenant tout préparer pour l'entraînement avec l'objet `Accelerator` :
-
-```python
-from accelerate import Accelerator
-
-accelerator = Accelerator()
-model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
- model, optimizer, train_dataloader, eval_dataloader
-)
-```
-
-Maintenant que notre modèle, notre optimiseur et nos chargeurs de données sont configurés, nous pouvons spécifier le planificateur du taux d'apprentissage comme suit :
-
-```python
-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,
-)
-```
-
-Il ne reste qu'une dernière chose à faire avant de s'entraîner : créer un dépôt de modèles sur le *Hub* d'Hugging Face ! Nous pouvons utiliser la bibliothèque 🤗 *Hub* pour générer d'abord le nom complet de notre dépôt :
-
-```python
-from huggingface_hub import get_full_repo_name
-
-model_name = "distilbert-base-uncased-finetuned-imdb-accelerate"
-repo_name = get_full_repo_name(model_name)
-repo_name
-```
-
-```python out
-'lewtun/distilbert-base-uncased-finetuned-imdb-accelerate'
-```
-
-puis créer et cloner le dépôt en utilisant la classe `Repository` du 🤗 *Hub* :
-
-```python
-from huggingface_hub import Repository
-
-output_dir = model_name
-repo = Repository(output_dir, clone_from=repo_name)
-```
-
-Une fois cela fait, il ne reste plus qu'à rédiger la boucle complète d'entraînement et d'évaluation :
-
-```python
-from tqdm.auto import tqdm
-import torch
-import math
-
-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()
- losses = []
- for step, batch in enumerate(eval_dataloader):
- with torch.no_grad():
- outputs = model(**batch)
-
- loss = outputs.loss
- losses.append(accelerator.gather(loss.repeat(batch_size)))
-
- losses = torch.cat(losses)
- losses = losses[: len(eval_dataset)]
- try:
- perplexity = math.exp(torch.mean(losses))
- except OverflowError:
- perplexity = float("inf")
-
- print(f">>> Epoch {epoch}: Perplexity: {perplexity}")
-
- # 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: Perplexity: 11.397545307900472
->>> Epoch 1: Perplexity: 10.904909330983092
->>> Epoch 2: Perplexity: 10.729503505340409
-```
-
-Cool, nous avons été en mesure d'évaluer la perplexité à chaque époque et de garantir la reproductibilité des entraînements multiples !
-
-{/if}
-
-### Utilisation de notre modèle finetuné
-
-Vous pouvez interagir avec votre modèle *finetuné* soit en utilisant son *widget* sur le *Hub*, soit localement avec le `pipeline` de 🤗 *Transformers*. Utilisons ce dernier pour télécharger notre modèle en utilisant le pipeline `fill-mask` :
-
-```python
-from transformers import pipeline
-
-mask_filler = pipeline(
- "fill-mask", model="huggingface-course/distilbert-base-uncased-finetuned-imdb"
-)
-```
-
-Nous pouvons ensuite donner au pipeline notre exemple de texte « this is a great [MASK] » et voir quelles sont les 5 premières prédictions :
-
-```python
-preds = mask_filler(text)
-
-for pred in preds:
- print(f">>> {pred['sequence']}")
-```
-
-```python out
-'>>> this is a great movie.'
-'>>> this is a great film.'
-'>>> this is a great story.'
-'>>> this is a great movies.'
-'>>> this is a great character.'
-```
-
-Notre modèle a clairement adapté ses pondérations pour prédire les mots qui sont plus fortement associés aux films !
-
-
-
-Ceci conclut notre première expérience d'entraînement d'un modèle de langage. Dans la [section 6](/course/fr/chapter7/section6), vous apprendrez comment entraîner à partir de zéro un modèle autorégressif comme GPT-2. Allez-y si vous voulez voir comment vous pouvez pré-entraîner votre propre *transformer* !
-
-
-
-✏️ **Essayez !** Pour quantifier les avantages de l'adaptation au domaine, finetunez un classifieur sur le jeu de données IMDb pour à la fois, le checkpoint de DistilBERT pré-entraîné et e checkpoint de DistilBERT finetuné. Si vous avez besoin d'un rafraîchissement sur la classification de texte, consultez le [chapitre 3](/course/fr/chapter3).
-
-
+
+
+# Finetuner un modèle de langage masqué
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Pour de nombreuses applications de NLP impliquant des *transformers*, vous pouvez simplement prendre un modèle pré-entraîné du *Hub* et le *finetuner* directement sur vos données pour la tâche à accomplir. Pour autant que le corpus utilisé pour le pré-entraînement ne soit pas trop différent du corpus utilisé pour le *finetuning*. L'apprentissage par transfert produira généralement de bons résultats.
+
+Cependant, il existe quelques cas où vous voudrez d'abord *finetuner* les modèles de langue sur vos données, avant d'entraîner une tête spécifique à la tâche. Par exemple, si votre jeu de données contient des contrats légaux ou des articles scientifiques, un *transformer* classique comme BERT traitera généralement les mots spécifiques au domaine dans votre corpus comme des *tokens* rares et les performances résultantes peuvent être moins que satisfaisantes. En *finetunant* le modèle de langage sur les données du domaine, vous pouvez améliorer les performances de nombreuses tâches en aval, ce qui signifie que vous ne devez généralement effectuer cette étape qu'une seule fois !
+
+Ce processus de *finetuning* d'un modèle de langage pré-entraîné sur des données *dans le domaine* est généralement appelé _adaptation au domaine_. Il a été popularisé en 2018 par [ULMFiT](https://arxiv.org/abs/1801.06146) qui a été l'une des premières architectures neuronales (basées sur des LSTMs) à faire en sorte que l'apprentissage par transfert fonctionne réellement pour le NLP. Un exemple d'adaptation de domaine avec ULMFiT est présenté dans l'image ci-dessous. Dans cette section, nous ferons quelque chose de similaire mais avec un *transformer* au lieu d'une LSTM !
+
+
+
+
+
+
+À la fin de cette section, vous aurez un [modèle de langage masqué](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.) sur le *Hub* qui peut autocompléter des phrases comme indiqué ci-dessous :
+
+
+
+
+Allons-y !
+
+
+
+
+
+🙋 Si les termes « modélisation du langage masqué » et « modèle pré-entraîné » ne vous sont pas familiers, consultez le [chapitre 1](/course/fr/chapiter1), où nous expliquons tous ces concepts fondamentaux, vidéos à l'appui !
+
+
+
+## Choix d'un modèle pré-entraîné pour la modélisation du langage masqué
+
+Pour commencer, nous allons choisir un modèle pré-entraîné approprié pour la modélisation du langage masqué. Comme le montre la capture d'écran suivante, vous pouvez trouver une liste de candidats en appliquant le filtre « *Fill-Mask* » sur le [*Hub*](https://huggingface.co/models?pipeline_tag=fill-mask&sort=downloads) :
+
+
+
+
+
+Bien que les modèles de la famille BERT et RoBERTa soient les plus téléchargés, nous utiliserons un modèle appelé [DistilBERT](https://huggingface.co/distilbert-base-uncased) qui peut être entraîné beaucoup plus rapidement avec peu ou pas de perte de performance en aval. Ce modèle a été entraîné à l'aide d'une technique spéciale appelée [_distillation de connaissances_](https://en.wikipedia.org/wiki/Knowledge_distillation), où un grand modèle *enseignant* comme BERT est utilisé pour guider l'entraînement d'un modèle *étudiant* qui a beaucoup moins de paramètres. Une explication des détails de la distillation de connaissances nous mènerait trop loin dans cette section mais si vous êtes intéressé, vous pouvez lire tout cela dans le livre [_Natural Language Processing with Transformers_](https://learning.oreilly.com/library/view/natural-language-processing/9781098103231/ch05.html).
+
+{#if fw === 'pt'}
+
+Allons-y et téléchargeons DistilBERT en utilisant la classe `AutoModelForMaskedLM` :
+
+```python
+from transformers import AutoModelForMaskedLM
+
+model_checkpoint = "distilbert-base-uncased"
+model = AutoModelForMaskedLM.from_pretrained(model_checkpoint)
+```
+
+Nous pouvons voir combien de paramètres ce modèle possède en appelant la méthode `num_parameters()` :
+
+```python
+distilbert_num_parameters = model.num_parameters() / 1_000_000
+print(f"'>>> DistilBERT nombre de paramètres : {round(distilbert_num_parameters)}M'")
+print(f"'>>> BERT nombre de paramètres : 110M'")
+```
+
+```python out
+'>>> DistilBERT nombre de paramètres : 67M'
+'>>> BERT nombre de paramètres : 110M'
+```
+
+{:else}
+
+Allons-y et téléchargeons DistilBERT en utilisant la classe `AutoModelForMaskedLM` :
+
+```python
+from transformers import TFAutoModelForMaskedLM
+
+model_checkpoint = "distilbert-base-uncased"
+model = TFAutoModelForMaskedLM.from_pretrained(model_checkpoint)
+```
+
+Nous pouvons voir combien de paramètres ce modèle possède en appelant la méthode `summary()` :
+
+```python
+model(model.dummy_inputs) # Construire le modèle
+model.summary()
+```
+
+```python out
+Model: "tf_distil_bert_for_masked_lm"
+_________________________________________________________________
+Layer (type) Output Shape Param #
+=================================================================
+distilbert (TFDistilBertMain multiple 66362880
+_________________________________________________________________
+vocab_transform (Dense) multiple 590592
+_________________________________________________________________
+vocab_layer_norm (LayerNorma multiple 1536
+_________________________________________________________________
+vocab_projector (TFDistilBer multiple 23866170
+=================================================================
+Total params: 66,985,530
+Trainable params: 66,985,530
+Non-trainable params: 0
+_________________________________________________________________
+```
+
+{/if}
+
+Avec environ 67 millions de paramètres, DistilBERT est environ deux fois plus petit que le modèle de base de BERT, ce qui se traduit approximativement par une accélération de l'entraînement d'un facteur deux. Voyons maintenant quels types de *tokens* ce modèle prédit comme étant les compléments les plus probables d'un petit échantillon de texte :
+
+```python
+text = "This is a great [MASK]."
+```
+
+En tant qu'êtres humains, nous pouvons imaginer de nombreuses possibilités pour le *token* `[MASK]`, telles que « jour », « promenade » ou « peinture ». Pour les modèles pré-entraînés, les prédictions dépendent du corpus sur lequel le modèle a été entraîné puisqu'il apprend à détecter les modèles statistiques présents dans les données. Comme BERT, DistilBERT a été pré-entraîné sur les jeux de données [*English Wikipedia*](https://huggingface.co/datasets/wikipedia) et [*BookCorpus*](https://huggingface.co/datasets/bookcorpus), nous nous attendons donc à ce que les prédictions pour `[MASK]` reflètent ces domaines. Pour prédire le masque, nous avons besoin du *tokenizer* de DistilBERT pour produire les entrées du modèle, alors téléchargeons-le également depuis le *Hub* :
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+```
+
+Avec un *tokenizer* et un modèle, nous pouvons maintenant passer notre exemple de texte au modèle, extraire les logits, et afficher les 5 meilleurs candidats :
+
+{#if fw === 'pt'}
+
+```python
+import torch
+
+inputs = tokenizer(text, return_tensors="pt")
+token_logits = model(**inputs).logits
+# Trouve l'emplacement de [MASK] et extrait ses logits
+mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]
+mask_token_logits = token_logits[0, mask_token_index, :]
+# Choisissez les candidats [MASK] avec les logits les plus élevés
+top_5_tokens = torch.topk(mask_token_logits, 5, dim=1).indices[0].tolist()
+
+for token in top_5_tokens:
+ print(f"'>>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}'")
+```
+
+{:else}
+
+```python
+import numpy as np
+import tensorflow as tf
+
+inputs = tokenizer(text, return_tensors="np")
+token_logits = model(**inputs).logits
+# Trouve l'emplacement de [MASK] et extrait ses logits
+mask_token_index = np.argwhere(inputs["input_ids"] == tokenizer.mask_token_id)[0, 1]
+mask_token_logits = token_logits[0, mask_token_index, :]
+# On choisit les candidats [MASK] avec les logits les plus élevés
+# Nous annulons le tableau avant argsort pour obtenir le plus grand, et non le plus petit, logits
+top_5_tokens = np.argsort(-mask_token_logits)[:5].tolist()
+
+for token in top_5_tokens:
+ print(f">>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}")
+```
+
+{/if}
+
+```python out
+'>>> This is a great deal.' # C'est une bonne affaire
+'>>> This is a great success.' # C'est un grand succès
+'>>> This is a great adventure.' # C'est une grande aventure
+'>>> This is a great idea.' # C'est une bonne idée
+'>>> This is a great feat.' # C'est un grand exploit
+```
+
+Nous pouvons voir dans les sorties que les prédictions du modèle se réfèrent à des termes de tous les jours, ce qui n'est peut-être pas surprenant étant donné le fondement de Wikipédia. Voyons comment nous pouvons changer ce domaine pour quelque chose d'un peu plus spécialisé : des critiques de films !
+
+
+## Le jeu de données
+
+Pour illustrer l'adaptation au domaine, nous utiliserons le célèbre [*Large Movie Review Dataset*](https://huggingface.co/datasets/imdb) (ou IMDb en abrégé), qui est un corpus de critiques de films souvent utilisé pour évaluer les modèles d'analyse de sentiments. En *finetunant* DistilBERT sur ce corpus, nous espérons que le modèle de langage adaptera son vocabulaire des données factuelles de Wikipédia sur lesquelles il a été pré-entraîné aux éléments plus subjectifs des critiques de films. Nous pouvons obtenir les données du *Hub* avec la fonction `load_dataset()` de 🤗 *Datasets* :
+
+```python
+from datasets import load_dataset
+
+imdb_dataset = load_dataset("imdb")
+imdb_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['text', 'label'],
+ num_rows: 25000
+ })
+ test: Dataset({
+ features: ['text', 'label'],
+ num_rows: 25000
+ })
+ unsupervised: Dataset({
+ features: ['text', 'label'],
+ num_rows: 50000
+ })
+})
+```
+
+Nous pouvons voir que les parties `train` et `test` sont chacune composées de 25 000 critiques, alors qu'il y a une partie non étiquetée appelée `unsupervised` qui contient 50 000 critiques. Jetons un coup d'œil à quelques échantillons pour avoir une idée du type de texte auquel nous avons affaire. Comme nous l'avons fait dans les chapitres précédents du cours, nous allons enchaîner les fonctions `Dataset.shuffle()` et `Dataset.select()` pour créer un échantillon aléatoire :
+
+```python
+sample = imdb_dataset["train"].shuffle(seed=42).select(range(3))
+
+for row in sample:
+ print(f"\n'>>> Review: {row['text']}'")
+ print(f"'>>> Label: {row['label']}'")
+```
+
+```python out
+
+'>>> Review: This is your typical Priyadarshan movie--a bunch of loony characters out on some silly mission. His signature climax has the entire cast of the film coming together and fighting each other in some crazy moshpit over hidden money. Whether it is a winning lottery ticket in Malamaal Weekly, black money in Hera Pheri, "kodokoo" in Phir Hera Pheri, etc., etc., the director is becoming ridiculously predictable. Don\'t get me wrong; as clichéd and preposterous his movies may be, I usually end up enjoying the comedy. However, in most his previous movies there has actually been some good humor, (Hungama and Hera Pheri being noteworthy ones). Now, the hilarity of his films is fading as he is using the same formula over and over again.
Songs are good. Tanushree Datta looks awesome. Rajpal Yadav is irritating, and Tusshar is not a whole lot better. Kunal Khemu is OK, and Sharman Joshi is the best.'
+'>>> Label: 0'
+
+'>>> Review: Okay, the story makes no sense, the characters lack any dimensionally, the best dialogue is ad-libs about the low quality of movie, the cinematography is dismal, and only editing saves a bit of the muddle, but Sam" Peckinpah directed the film. Somehow, his direction is not enough. For those who appreciate Peckinpah and his great work, this movie is a disappointment. Even a great cast cannot redeem the time the viewer wastes with this minimal effort.
The proper response to the movie is the contempt that the director San Peckinpah, James Caan, Robert Duvall, Burt Young, Bo Hopkins, Arthur Hill, and even Gig Young bring to their work. Watch the great Peckinpah films. Skip this mess.'
+'>>> Label: 0'
+
+'>>> Review: I saw this movie at the theaters when I was about 6 or 7 years old. I loved it then, and have recently come to own a VHS version.
My 4 and 6 year old children love this movie and have been asking again and again to watch it.
I have enjoyed watching it again too. Though I have to admit it is not as good on a little TV.
I do not have older children so I do not know what they would think of it.
The songs are very cute. My daughter keeps singing them over and over.
Hope this helps.'
+'>>> Label: 1'
+```
+
+Oui, ce sont bien des critiques de films, et si vous êtes assez âgés, vous pouvez même comprendre le commentaire dans la dernière critique sur le fait de posséder une version VHS 😜 ! Bien que nous n'ayons pas besoin des étiquettes pour la modélisation du langage, nous pouvons déjà voir qu'un `0` dénote une critique négative, tandis qu'un `1` correspond à une critique positive.
+
+
+
+✏️ **Essayez !** Créez un échantillon aléatoire de la répartition `unsupervised` et vérifiez que les étiquettes ne sont ni `0` ni `1`. Pendant que vous y êtes, vous pouvez aussi vérifier que les étiquettes dans les échantillons `train` et `test` sont bien `0` ou `1`. C'est un contrôle utile que tout praticien en NLP devrait effectuer au début d'un nouveau projet !
+
+
+
+Maintenant que nous avons jeté un coup d'œil rapide aux données, plongeons dans leur préparation pour la modélisation du langage masqué. Comme nous allons le voir, il y a quelques étapes supplémentaires à suivre par rapport aux tâches de classification de séquences que nous avons vues au [chapitre 3](/course/fr/chapter3). Allons-y !
+
+## Prétraitement des données
+
+
+
+Pour la modélisation autorégressive et la modélisation du langage masqué, une étape commune de prétraitement consiste à concaténer tous les exemples, puis à diviser le corpus entier en morceaux de taille égale. C'est très différent de notre approche habituelle, où nous nous contentons de *tokenizer* les exemples individuels. Pourquoi tout concaténer ? La raison est que les exemples individuels peuvent être tronqués s'ils sont trop longs, ce qui entraînerait la perte d'informations qui pourraient être utiles pour la tâche de modélisation du langage !
+
+Donc pour commencer, nous allons d'abord tokeniser notre corpus comme d'habitude, mais _sans_ mettre l'option `truncation=True` dans notre *tokenizer*. Nous allons aussi récupérer les identifiants des mots s'ils sont disponibles (ce qui sera le cas si nous utilisons un *tokenizer* rapide, comme décrit dans le [chapitre 6](/course/fr/chapter6/3)), car nous en aurons besoin plus tard pour faire le masquage de mots entiers. Nous allons envelopper cela dans une simple fonction, et pendant que nous y sommes, nous allons supprimer les colonnes `text` et `label` puisque nous n'en avons plus besoin :
+
+```python
+def tokenize_function(examples):
+ result = tokenizer(examples["text"])
+ if tokenizer.is_fast:
+ result["word_ids"] = [result.word_ids(i) for i in range(len(result["input_ids"]))]
+ return result
+
+
+# Utilisation de batched=True pour activer le multithreading rapide !
+tokenized_datasets = imdb_dataset.map(
+ tokenize_function, batched=True, remove_columns=["text", "label"]
+)
+tokenized_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'input_ids', 'word_ids'],
+ num_rows: 25000
+ })
+ test: Dataset({
+ features: ['attention_mask', 'input_ids', 'word_ids'],
+ num_rows: 25000
+ })
+ unsupervised: Dataset({
+ features: ['attention_mask', 'input_ids', 'word_ids'],
+ num_rows: 50000
+ })
+})
+```
+
+Comme DistilBERT est un modèle de type BERT, nous pouvons voir que les textes encodés sont constitués des `input_ids` et des `attention_mask` que nous avons vus dans d'autres chapitres, ainsi que des `word_ids` que nous avons ajoutés.
+
+Maintenant que nos critiques de films ont été tokenisées, l'étape suivante consiste à les regrouper et à diviser le résultat en chunks. Mais quelle taille doivent avoir ces *chunks* ? Cela sera finalement déterminé par la quantité de mémoire GPU dont vous disposez, mais un bon point de départ est de voir quelle est la taille maximale du contexte du modèle. Cela peut être déduit en inspectant l'attribut `model_max_length` du *tokenizer* :
+
+```python
+tokenizer.model_max_length
+```
+
+
+```python out
+512
+```
+
+Cette valeur est dérivée du fichier *tokenizer_config.json* associé à un *checkpoint*. Dans ce cas, nous pouvons voir que la taille du contexte est de 512 *tokens*, tout comme avec BERT.
+
+
+
+✏️ **Essayez !** Certains *transformers*, comme [BigBird](https://huggingface.co/google/bigbird-roberta-base) et [Longformer](hf.co/allenai/longformer-base-4096), ont une longueur de contexte beaucoup plus longue que BERT et les autres premiers *transformers*. Instanciez le *tokenizer* pour l'un de ces *checkpoints* et vérifiez que le `model_max_length` correspond à ce qui est indiqué sur sa carte.
+
+
+
+Ainsi, pour réaliser nos expériences sur des GPUs comme ceux disponibles sur Google Colab, nous choisirons quelque chose d'un peu plus petit qui peut tenir en mémoire :
+
+```python
+chunk_size = 128
+```
+
+
+
+Notez que l'utilisation d'une petite taille peut être préjudiciable dans les scénarios du monde réel. Vous devez donc utiliser une taille qui correspond au cas d'utilisation auquel vous appliquerez votre modèle.
+
+
+
+Maintenant vient la partie amusante. Pour montrer comment la concaténation fonctionne, prenons quelques commentaires de notre ensemble d'entraînement et affichons le nombre de *tokens* par commentaire :
+
+```python
+# Le découpage produit une liste de listes pour chaque caractéristique
+tokenized_samples = tokenized_datasets["train"][:3]
+
+for idx, sample in enumerate(tokenized_samples["input_ids"]):
+ print(f"'>>> Review {idx} length: {len(sample)}'")
+```
+
+```python out
+'>>> Review 0 length: 200'
+'>>> Review 1 length: 559'
+'>>> Review 2 length: 192'
+```
+
+Nous pouvons ensuite concaténer tous ces exemples avec une simple compréhension du dictionnaire, comme suit :
+
+```python
+concatenated_examples = {
+ k: sum(tokenized_samples[k], []) for k in tokenized_samples.keys()
+}
+total_length = len(concatenated_examples["input_ids"])
+print(f"'>>> Longueur des critiques concaténées : {total_length}'")
+```
+
+```python out
+'>>> Longueur des critiques concaténées : 951'
+```
+
+Super, la longueur totale est correcte. Donc maintenant, nous allons diviser les exemples concaténés en morceaux de la taille donnée par `block_size`. Pour ce faire, nous itérons sur les caractéristiques de `concatenated_examples` et utilisons une compréhension de liste pour créer des *chunks* de chaque caractéristique. Le résultat est un dictionnaire de *chunks* pour chaque caractéristique :
+
+```python
+chunks = {
+ k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)]
+ for k, t in concatenated_examples.items()
+}
+
+for chunk in chunks["input_ids"]:
+ print(f"'>>> Chunk length: {len(chunk)}'")
+```
+
+```python out
+'>>> Chunk length: 128'
+'>>> Chunk length: 128'
+'>>> Chunk length: 128'
+'>>> Chunk length: 128'
+'>>> Chunk length: 128'
+'>>> Chunk length: 128'
+'>>> Chunk length: 128'
+'>>> Chunk length: 55'
+```
+
+Comme vous pouvez le voir dans cet exemple, le dernier *chunk* sera généralement plus petit que la taille maximale des morceaux. Il y a deux stratégies principales pour gérer cela :
+
+* Abandonner le dernier morceau s'il est plus petit que `chunk_size`.
+* Rembourrer le dernier morceau jusqu'à ce que sa longueur soit égale à `chunk_size`.
+
+Nous adopterons la première approche ici, donc nous allons envelopper toute la logique ci-dessus dans une seule fonction que nous pouvons appliquer à nos jeux de données tokenisés :
+
+```python
+def group_texts(examples):
+ # Concaténation de tous les textes
+ concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
+ # Calcule la longueur des textes concaténés
+ total_length = len(concatenated_examples[list(examples.keys())[0]])
+ # Nous laissons tomber le dernier morceau s'il est plus petit que chunk_size
+ total_length = (total_length // chunk_size) * chunk_size
+ # Fractionnement par chunk de max_len
+ result = {
+ k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)]
+ for k, t in concatenated_examples.items()
+ }
+ # Créer une nouvelle colonne d'étiquettes
+ result["labels"] = result["input_ids"].copy()
+ return result
+```
+
+Notez que dans la dernière étape de `group_texts()` nous créons une nouvelle colonne `labels` qui est une copie de la colonne `input_ids`. Comme nous le verrons bientôt, c'est parce que dans la modélisation du langage masqué, l'objectif est de prédire des *tokens* masqués aléatoirement dans le batch d'entrée, et en créant une colonne `labels`, nous fournissons la vérité de base pour notre modèle de langage à apprendre.
+
+Appliquons maintenant `group_texts()` à nos jeux de données tokenisés en utilisant notre fidèle fonction `Dataset.map()` :
+
+```python
+lm_datasets = tokenized_datasets.map(group_texts, batched=True)
+lm_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
+ num_rows: 61289
+ })
+ test: Dataset({
+ features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
+ num_rows: 59905
+ })
+ unsupervised: Dataset({
+ features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
+ num_rows: 122963
+ })
+})
+```
+
+Vous pouvez voir que le regroupement puis le découpage des textes a produit beaucoup plus d'exemples que nos 25 000 exemples initiaux pour les divisions `train` et `test`. C'est parce que nous avons maintenant des exemples impliquant des *tokens* contigus qui s'étendent sur plusieurs exemples du corpus original. Vous pouvez le voir explicitement en cherchant les *tokens* spéciaux `[SEP]` et `[CLS]` dans l'un des *chunks* :
+
+```python
+tokenizer.decode(lm_datasets["train"][1]["input_ids"])
+```
+
+```python out
+".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless"
+```
+
+Dans cet exemple, vous pouvez voir deux critiques de films qui se chevauchent, l'une sur un film de lycée et l'autre sur les sans-abri. Voyons également à quoi ressemblent les étiquettes pour la modélisation du langage masqué :
+
+```python out
+tokenizer.decode(lm_datasets["train"][1]["labels"])
+```
+
+```python out
+".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless"
+```
+
+Comme prévu par notre fonction `group_texts()` ci-dessus, cela semble identique aux `input_ids` décodés. Mais alors comment notre modèle peut-il apprendre quoi que ce soit ? Il nous manque une étape clé : insérer des *tokens* à des positions aléatoires dans les entrées ! Voyons comment nous pouvons le faire à la volée pendant le *finetuning* en utilisant un collateur de données spécial.
+
+## Finetuning de DistilBERT avec l'API `Trainer`
+
+Le *finetuning* d'un modèle de langage masqué est presque identique au *finetuning* d'un modèle de classification de séquences, comme nous l'avons fait dans le [chapitre 3](/course/fr/chapter3). La seule différence est que nous avons besoin d'un collecteur de données spécial qui peut masquer de manière aléatoire certains des *tokens* dans chaque batch de textes. Heureusement, 🤗 *Transformers* est livré préparé avec un `DataCollatorForLanguageModeling` dédié à cette tâche. Nous devons juste lui passer le *tokenizer* et un argument `mlm_probability` qui spécifie quelle fraction des *tokens* à masquer. Nous choisirons 15%, qui est la quantité utilisée pour BERT et un choix commun dans la littérature :
+
+```python
+from transformers import DataCollatorForLanguageModeling
+
+data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=0.15)
+```
+
+Pour voir comment le masquage aléatoire fonctionne, nous allons donner quelques exemples au collateur de données. Puisqu'il s'attend à une liste de `dict` où chaque `dict` représente un seul morceau de texte contigu, nous itérons d'abord sur le jeu de données avant de donner le batch au collateur. Nous supprimons la clé `"word_ids"` pour ce collateur de données car il ne l'attend pas :
+
+```python
+samples = [lm_datasets["train"][i] for i in range(2)]
+for sample in samples:
+ _ = sample.pop("word_ids")
+
+for chunk in data_collator(samples)["input_ids"]:
+ print(f"\n'>>> {tokenizer.decode(chunk)}'")
+```
+
+```python output
+'>>> [CLS] bromwell [MASK] is a cartoon comedy. it ran at the same [MASK] as some other [MASK] about school life, [MASK] as " teachers ". [MASK] [MASK] [MASK] in the teaching [MASK] lead [MASK] to believe that bromwell high\'[MASK] satire is much closer to reality than is " teachers ". the scramble [MASK] [MASK] financially, the [MASK]ful students whogn [MASK] right through [MASK] pathetic teachers\'pomp, the pettiness of the whole situation, distinction remind me of the schools i knew and their students. when i saw [MASK] episode in [MASK] a student repeatedly tried to burn down the school, [MASK] immediately recalled. [MASK]...'
+
+'>>> .... at.. [MASK]... [MASK]... high. a classic line plucked inspector : i\'[MASK] here to [MASK] one of your [MASK]. student : welcome to bromwell [MASK]. i expect that many adults of my age think that [MASK]mwell [MASK] is [MASK] fetched. what a pity that it isn\'t! [SEP] [CLS] [MASK]ness ( or [MASK]lessness as george 宇in stated )公 been an issue for years but never [MASK] plan to help those on the street that were once considered human [MASK] did everything from going to school, [MASK], [MASK] vote for the matter. most people think [MASK] the homeless'
+```
+
+Super, ça a marché ! Nous pouvons voir que le *token* `[MASK]` a été inséré de façon aléatoire à différents endroits dans notre texte. Ce seront les *tokens* que notre modèle devra prédire pendant l'entraînement. Et la beauté du collecteur de données est qu'il va rendre aléatoire l'insertion du `[MASK]` à chaque batch !
+
+
+
+✏️ **Essayez** Exécutez le code ci-dessus plusieurs fois pour voir le masquage aléatoire se produire sous vos yeux ! Remplacez aussi la méthode `tokenizer.decode()` par `tokenizer.convert_ids_to_tokens()` pour voir que parfois un seul *token* d'un mot donné est masqué et pas les autres.
+
+
+
+{#if fw === 'pt'}
+
+Un effet secondaire du masquage aléatoire est que nos métriques d'évaluation ne seront pas déterministes lorsque nous utilisons la fonction `Trainer` puisque nous utilisons le même collateur de données pour les échantillons d'entraînement et de test. Nous verrons plus tard, lorsque nous examinerons le *finetuning* avec 🤗 *Accelerate*, comment nous pouvons utiliser la flexibilité d'une boucle d'évaluation personnalisée pour geler le caractère aléatoire.
+
+{/if}
+
+Lors de l'entraînement des modèles pour la modélisation du langage masqué, une technique qui peut être utilisée est de masquer des mots entiers ensemble et pas seulement des *tokens* individuels. Cette approche est appelée _masquage de mots entiers_. Si nous voulons utiliser le masquage de mots entiers, nous devons construire nous-mêmes un collateur de données. Un collateur de données est simplement une fonction qui prend une liste d'échantillons et les convertit en un batch. Faisons-le ! Nous utiliserons les identifiants des mots calculés plus tôt pour faire une correspondance entre les indices des mots et les *tokens*, puis nous déciderons aléatoirement quels mots masquer et appliquerons ce masque sur les entrées. Notez que les étiquettes sont toutes `-100` sauf celles qui correspondent aux mots masqués.
+
+{#if fw === 'pt'}
+
+```py
+import collections
+import numpy as np
+
+from transformers import default_data_collator
+
+wwm_probability = 0.2
+
+
+def whole_word_masking_data_collator(features):
+ for feature in features:
+ word_ids = feature.pop("word_ids")
+
+ # Création d'une correspondance entre les mots et les indices des tokens correspondants
+ mapping = collections.defaultdict(list)
+ current_word_index = -1
+ current_word = None
+ for idx, word_id in enumerate(word_ids):
+ if word_id is not None:
+ if word_id != current_word:
+ current_word = word_id
+ current_word_index += 1
+ mapping[current_word_index].append(idx)
+
+ # Masquer des mots de façon aléatoire
+ mask = np.random.binomial(1, wwm_probability, (len(mapping),))
+ input_ids = feature["input_ids"]
+ labels = feature["labels"]
+ new_labels = [-100] * len(labels)
+ for word_id in np.where(mask)[0]:
+ word_id = word_id.item()
+ for idx in mapping[word_id]:
+ new_labels[idx] = labels[idx]
+ input_ids[idx] = tokenizer.mask_token_id
+
+ return default_data_collator(features)
+```
+
+{:else}
+
+```py
+import collections
+import numpy as np
+
+from transformers.data.data_collator import tf_default_data_collator
+
+wwm_probability = 0.2
+
+
+def whole_word_masking_data_collator(features):
+ for feature in features:
+ word_ids = feature.pop("word_ids")
+
+ # Création d'une correspondance entre les mots et les indices des tokens correspondants
+ mapping = collections.defaultdict(list)
+ current_word_index = -1
+ current_word = None
+ for idx, word_id in enumerate(word_ids):
+ if word_id is not None:
+ if word_id != current_word:
+ current_word = word_id
+ current_word_index += 1
+ mapping[current_word_index].append(idx)
+
+ # Masquer des mots de façon aléatoire
+ mask = np.random.binomial(1, wwm_probability, (len(mapping),))
+ input_ids = feature["input_ids"]
+ labels = feature["labels"]
+ new_labels = [-100] * len(labels)
+ for word_id in np.where(mask)[0]:
+ word_id = word_id.item()
+ for idx in mapping[word_id]:
+ new_labels[idx] = labels[idx]
+ input_ids[idx] = tokenizer.mask_token_id
+
+ return tf_default_data_collator(features)
+```
+
+{/if}
+
+Ensuite, nous pouvons l'essayer sur les mêmes échantillons que précédemment :
+
+```py
+samples = [lm_datasets["train"][i] for i in range(2)]
+batch = whole_word_masking_data_collator(samples)
+
+for chunk in batch["input_ids"]:
+ print(f"\n'>>> {tokenizer.decode(chunk)}'")
+```
+
+```python out
+'>>> [CLS] bromwell high is a cartoon comedy [MASK] it ran at the same time as some other programs about school life, such as " teachers ". my 35 years in the teaching profession lead me to believe that bromwell high\'s satire is much closer to reality than is " teachers ". the scramble to survive financially, the insightful students who can see right through their pathetic teachers\'pomp, the pettiness of the whole situation, all remind me of the schools i knew and their students. when i saw the episode in which a student repeatedly tried to burn down the school, i immediately recalled.....'
+
+'>>> .... [MASK] [MASK] [MASK] [MASK]....... high. a classic line : inspector : i\'m here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn\'t! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless'
+```
+
+
+
+✏️ **Essayez** Exécutez le code ci-dessus plusieurs fois pour voir le masquage aléatoire se produire sous vos yeux ! Remplacez aussi la méthode `tokenizer.decode()` par `tokenizer.convert_ids_to_tokens()` pour voir que les *tokens* d'un mot donné sont toujours masqués ensemble.
+
+
+
+Maintenant que nous avons deux collateurs de données, les étapes restantes du *finetuning* sont standards. L'entraînement peut prendre un certain temps sur Google Colab si vous n'avez pas la chance de tomber sur un mythique GPU P100 😭. Ainsi nous allons d'abord réduire la taille du jeu d'entraînement à quelques milliers d'exemples. Ne vous inquiétez pas, nous obtiendrons quand même un modèle de langage assez décent ! Un moyen rapide de réduire la taille d'un jeu de données dans 🤗 *Datasets* est la fonction `Dataset.train_test_split()` que nous avons vue au [chapitre 5](/course/fr/chapter5) :
+
+```python
+train_size = 10_000
+test_size = int(0.1 * train_size)
+
+downsampled_dataset = lm_datasets["train"].train_test_split(
+ train_size=train_size, test_size=test_size, seed=42
+)
+downsampled_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
+ num_rows: 10000
+ })
+ test: Dataset({
+ features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
+ num_rows: 1000
+ })
+})
+```
+
+Cela a automatiquement créé de nouvelles divisions `train` et `test` avec la taille du jeu d'entraînement fixée à 10.000 exemples et la validation fixée à 10% de cela. N'hésitez pas à augmenter la taille si vous avez un GPU puissant ! 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 suivante :
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+qui affichera un *widget* où vous pourrez saisir vos informations d'identification. Alternativement, vous pouvez exécuter :
+
+```
+huggingface-cli login
+```
+
+dans votre terminal préféré et connectez-vous là.
+
+{#if fw === 'tf'}
+
+Une fois que nous sommes connectés, nous pouvons créer nos jeux de données `tf.data`. Nous n'utiliserons ici que le collecteur de données standard, mais vous pouvez également essayer le collecteur de masquage de mots entiers et comparer les résultats à titre d'exercice :
+
+```python
+tf_train_dataset = downsampled_dataset["train"].to_tf_dataset(
+ columns=["input_ids", "attention_mask", "labels"],
+ collate_fn=data_collator,
+ shuffle=True,
+ batch_size=32,
+)
+
+tf_eval_dataset = downsampled_dataset["test"].to_tf_dataset(
+ columns=["input_ids", "attention_mask", "labels"],
+ collate_fn=data_collator,
+ shuffle=False,
+ batch_size=32,
+)
+```
+
+Ensuite, nous configurons nos hyperparamètres d'entraînement et compilons notre modèle. Nous utilisons la fonction `create_optimizer()` de la bibliothèque 🤗 *Transformers*, qui nous donne un optimiseur `AdamW` avec une décroissance linéaire du taux d'apprentissage. Nous utilisons également la perte intégrée au modèle, qui est la perte par défaut lorsqu'aucune perte n'est spécifiée comme argument de `compile()`, et nous définissons la précision d'entraînement à `"mixed_float16"`. Notez que si vous utilisez un GPU Colab ou un autre GPU qui n'a pas le support accéléré en float16, vous devriez probablement commenter cette ligne.
+
+De plus, nous mettons en place un `PushToHubCallback` qui sauvegardera le modèle sur le *Hub* après chaque époque. 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, pour pousser le modèle vers l'organisation [`huggingface-course`](https://huggingface.co/huggingface-course), nous avons ajouté `hub_model_id="huggingface-course/distilbert-finetuned-imdb"`. 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, ce sera `"lewtun/distilbert-finetuned-imdb"`.
+
+```python
+from transformers import create_optimizer
+from transformers.keras_callbacks import PushToHubCallback
+import tensorflow as tf
+
+num_train_steps = len(tf_train_dataset)
+optimizer, schedule = create_optimizer(
+ init_lr=2e-5,
+ num_warmup_steps=1_000,
+ 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")
+
+callback = PushToHubCallback(
+ output_dir=f"{model_name}-finetuned-imdb", tokenizer=tokenizer
+)
+```
+
+Nous sommes maintenant prêts à exécuter `model.fit()`. Mais avant, regardons brièvement la _perplexité_ qui est une métrique commune pour évaluer la performance des modèles de langage.
+
+{:else}
+
+Une fois que nous sommes connectés, nous pouvons spécifier les arguments pour le `Trainer` :
+
+```python
+from transformers import TrainingArguments
+
+batch_size = 64
+# Montrer la perte d'entraînement à chaque époque
+logging_steps = len(downsampled_dataset["train"]) // batch_size
+model_name = model_checkpoint.split("/")[-1]
+
+training_args = TrainingArguments(
+ output_dir=f"{model_name}-finetuned-imdb",
+ overwrite_output_dir=True,
+ evaluation_strategy="epoch",
+ learning_rate=2e-5,
+ weight_decay=0.01,
+ per_device_train_batch_size=batch_size,
+ per_device_eval_batch_size=batch_size,
+ push_to_hub=True,
+ fp16=True,
+ logging_steps=logging_steps,
+)
+```
+
+Ici, nous avons modifié quelques options par défaut, y compris `logging_steps` pour s'assurer que nous suivons la perte d'entraînement à chaque époque. Nous avons également utilisé `fp16=True` pour activer l'entraînement en précision mixte, ce qui nous donne un autre gain de vitesse. Par défaut, `Trainer` va supprimer toutes les colonnes qui ne font pas partie de la méthode `forward()` du modèle. Cela signifie que si vous utilisez le collateur de masquage de mots entiers, vous devrez également définir `remove_unused_columns=False` pour vous assurer que nous ne perdons pas la colonne `word_ids` pendant l'entraînement.
+
+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/distilbert-finetuned-imdb"` `TrainingArguments`. 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 ce sera `"lewtun/distilbert-finetuned-imdb"`.
+
+Nous avons maintenant tous les ingrédients pour instancier le `Trainer`. Ici, nous utilisons juste le collateur standard `data_collator`, mais vous pouvez essayer le collateur de masquage de mots entiers et comparer les résultats comme exercice :
+
+```python
+from transformers import Trainer
+
+trainer = Trainer(
+ model=model,
+ args=training_args,
+ train_dataset=downsampled_dataset["train"],
+ eval_dataset=downsampled_dataset["test"],
+ data_collator=data_collator,
+)
+```
+
+Nous sommes maintenant prêts à exécuter `trainer.train()`. Mais avant, regardons brièvement la _perplexité_ qui est une métrique commune pour évaluer la performance des modèles de langage.
+
+{/if}
+
+### Perplexité pour les modèles de langage
+
+
+
+Contrairement à d'autres tâches, comme la classification de textes ou la réponse à des questions, sur lesquelles nous disposons d'un corpus étiqueté pour entraîner, la modélisation du langage ne s'appuie sur aucune étiquette explicite. Alors comment déterminer ce qui fait un bon modèle de langage ? Comme pour la fonction de correction automatique de votre téléphone, un bon modèle de langage est celui qui attribue des probabilités élevées aux phrases grammaticalement correctes et des probabilités faibles aux phrases absurdes. Pour vous donner une meilleure idée de ce à quoi cela ressemble, vous pouvez trouver en ligne des séries entières de « ratés d'autocorrection » où le modèle d'un téléphone produit des compléments plutôt amusants (et souvent inappropriés) !
+
+{#if fw === 'pt'}
+
+En supposant que notre ensemble de test se compose principalement de phrases grammaticalement correctes, une façon de mesurer la qualité de notre modèle de langage est de calculer les probabilités qu'il attribue au mot suivant dans toutes les phrases de l'ensemble de test. Des probabilités élevées indiquent que le modèle n'est pas « surpris » ou « perplexe » vis-à-vis des exemples non vus, et suggèrent qu'il a appris les modèles de base de la grammaire de la langue. Il existe plusieurs définitions mathématiques de la perplexité. Celle que nous utiliserons la définit comme l'exponentielle de la perte d'entropie croisée. Ainsi, nous pouvons calculer la perplexité de notre modèle pré-entraîné en utilisant la fonction `Trainer.evaluate()` pour calculer la perte d'entropie croisée sur l'ensemble de test, puis en prenant l'exponentielle du résultat :
+
+```python
+import math
+
+eval_results = trainer.evaluate()
+print(f">>> Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
+```
+
+{:else}
+
+En supposant que notre ensemble de test se compose principalement de phrases grammaticalement correctes, une façon de mesurer la qualité de notre modèle de langage est de calculer les probabilités qu'il attribue au mot suivant dans toutes les phrases de l'ensemble de test. Des probabilités élevées indiquent que le modèle n'est pas « surpris » ou « perplexe » vis-à-vis des exemples non vus, et suggèrent qu'il a appris les modèles de base de la grammaire de la langue. Il existe plusieurs définitions mathématiques de la perplexité. Celle que nous utiliserons la définit comme l'exponentielle de la perte d'entropie croisée. Ainsi, nous pouvons calculer la perplexité de notre modèle pré-entraîné en utilisant la fonction `model.evaluate()` pour calculer la perte d'entropie croisée sur l'ensemble de test, puis en prenant l'exponentielle du résultat :
+
+```python
+import math
+
+eval_loss = model.evaluate(tf_eval_dataset)
+print(f"Perplexité : {math.exp(eval_loss):.2f}")
+```
+
+{/if}
+
+```python out
+>>> Perplexité : 21.75
+```
+
+Un score de perplexité faible signifie un meilleur modèle de langue. Nous pouvons voir ici que notre modèle de départ a une valeur assez élevée. Voyons si nous pouvons la réduire en l'affinant ! Pour ce faire, nous commençons par exécuter la boucle d'entraînement :
+
+{#if fw === 'pt'}
+
+```python
+trainer.train()
+```
+
+{:else}
+
+```python
+model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback])
+```
+
+{/if}
+
+et ensuite calculer la perplexité résultante sur l'ensemble de test comme précédemment :
+
+{#if fw === 'pt'}
+
+```python
+eval_results = trainer.evaluate()
+print(f">>> Perplexité : {math.exp(eval_results['eval_loss']):.2f}")
+```
+
+{:else}
+
+```python
+eval_loss = model.evaluate(tf_eval_dataset)
+print(f"Perplexité : {math.exp(eval_loss):.2f}")
+```
+
+{/if}
+
+```python out
+>>> Perplexité : 11.32
+```
+
+Joli. C'est une réduction considérable de la perplexité, ce qui nous indique que le modèle a appris quelque chose sur le domaine des critiques de films !
+
+{#if fw === 'pt'}
+
+Une fois l'entraînement terminé, nous pouvons pousser la carte de modèle avec les informations d'entraînement vers le *Hub* (les *checkpoints* sont sauvegardés pendant l'entraînement lui-même) :
+
+```python
+trainer.push_to_hub()
+```
+
+{/if}
+
+
+
+✏️ **A votre tour !** Exécutez l'entraînement ci-dessus après avoir remplacé le collecteur de données par le collecteur de mots entiers masqués. Obtenez-vous de meilleurs résultats ?
+
+
+
+{#if fw === 'pt'}
+
+Dans notre cas d'utilisation, nous n'avons pas eu besoin de faire quelque chose de spécial avec la boucle d'entraînement, mais dans certains cas, vous pourriez avoir besoin de mettre en œuvre une logique personnalisée. Pour ces applications, vous pouvez utiliser 🤗 *Accelerate*. Jetons un coup d'œil !
+
+## Finetuning de DistilBERT avec 🤗 Accelerate
+
+Comme nous l'avons vu, avec `Trainer` le *finetuning* d'un modèle de langage masqué est très similaire à l'exemple de classification de texte du [chapitre 3](/course/fr/chapter3). En fait, la seule subtilité est l'utilisation d'un collateur de données spécial, et nous l'avons déjà couvert plus tôt dans cette section !
+
+Cependant, nous avons vu que `DataCollatorForLanguageModeling` applique aussi un masquage aléatoire à chaque évaluation. Nous verrons donc quelques fluctuations dans nos scores de perplexité à chaque entrainement. Une façon d'éliminer cette source d'aléat est d'appliquer le masquage _une fois_ sur l'ensemble de test, puis d'utiliser le collateur de données par défaut dans 🤗 *Transformers* pour collecter les batchs pendant l'évaluation. Pour voir comment cela fonctionne, implémentons une fonction simple qui applique le masquage sur un batch, similaire à notre première rencontre avec `DataCollatorForLanguageModeling` :
+
+```python
+def insert_random_mask(batch):
+ features = [dict(zip(batch, t)) for t in zip(*batch.values())]
+ masked_inputs = data_collator(features)
+ # Créer une nouvelle colonne "masquée" pour chaque colonne du jeu de données
+ return {"masked_" + k: v.numpy() for k, v in masked_inputs.items()}
+```
+
+Ensuite, nous allons appliquer cette fonction à notre jeu de test et laisser tomber les colonnes non masquées afin de les remplacer par les colonnes masquées. Vous pouvez utiliser le masquage de mots entiers en remplaçant le `data_collator` ci-dessus par celui qui est approprié. Dans ce cas vous devez supprimer la première ligne ici :
+
+```py
+downsampled_dataset = downsampled_dataset.remove_columns(["word_ids"])
+eval_dataset = downsampled_dataset["test"].map(
+ insert_random_mask,
+ batched=True,
+ remove_columns=downsampled_dataset["test"].column_names,
+)
+eval_dataset = eval_dataset.rename_columns(
+ {
+ "masked_input_ids": "input_ids",
+ "masked_attention_mask": "attention_mask",
+ "masked_labels": "labels",
+ }
+)
+```
+
+Nous pouvons ensuite configurer les *dataloaders* comme d'habitude, mais nous utiliserons le `default_data_collator` de 🤗 *Transformers* pour le jeu d'évaluation :
+
+```python
+from torch.utils.data import DataLoader
+from transformers import default_data_collator
+
+batch_size = 64
+train_dataloader = DataLoader(
+ downsampled_dataset["train"],
+ shuffle=True,
+ batch_size=batch_size,
+ collate_fn=data_collator,
+)
+eval_dataloader = DataLoader(
+ eval_dataset, batch_size=batch_size, collate_fn=default_data_collator
+)
+```
+
+Nous suivons les étapes standard avec 🤗 *Accelerate*. La première est de charger une version fraîche du modèle pré-entraîné :
+
+```
+model = AutoModelForMaskedLM.from_pretrained(model_checkpoint)
+```
+
+Ensuite, nous devons spécifier l'optimiseur. Nous utiliserons le standard `AdamW` :
+
+```python
+from torch.optim import AdamW
+
+optimizer = AdamW(model.parameters(), lr=5e-5)
+```
+
+Avec ces objets, nous pouvons maintenant tout préparer pour l'entraînement avec l'objet `Accelerator` :
+
+```python
+from accelerate import Accelerator
+
+accelerator = Accelerator()
+model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
+ model, optimizer, train_dataloader, eval_dataloader
+)
+```
+
+Maintenant que notre modèle, notre optimiseur et nos chargeurs de données sont configurés, nous pouvons spécifier le planificateur du taux d'apprentissage comme suit :
+
+```python
+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,
+)
+```
+
+Il ne reste qu'une dernière chose à faire avant de s'entraîner : créer un dépôt de modèles sur le *Hub* d'Hugging Face ! Nous pouvons utiliser la bibliothèque 🤗 *Hub* pour générer d'abord le nom complet de notre dépôt :
+
+```python
+from huggingface_hub import get_full_repo_name
+
+model_name = "distilbert-base-uncased-finetuned-imdb-accelerate"
+repo_name = get_full_repo_name(model_name)
+repo_name
+```
+
+```python out
+'lewtun/distilbert-base-uncased-finetuned-imdb-accelerate'
+```
+
+puis créer et cloner le dépôt en utilisant la classe `Repository` du 🤗 *Hub* :
+
+```python
+from huggingface_hub import Repository
+
+output_dir = model_name
+repo = Repository(output_dir, clone_from=repo_name)
+```
+
+Une fois cela fait, il ne reste plus qu'à rédiger la boucle complète d'entraînement et d'évaluation :
+
+```python
+from tqdm.auto import tqdm
+import torch
+import math
+
+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()
+ losses = []
+ for step, batch in enumerate(eval_dataloader):
+ with torch.no_grad():
+ outputs = model(**batch)
+
+ loss = outputs.loss
+ losses.append(accelerator.gather(loss.repeat(batch_size)))
+
+ losses = torch.cat(losses)
+ losses = losses[: len(eval_dataset)]
+ try:
+ perplexity = math.exp(torch.mean(losses))
+ except OverflowError:
+ perplexity = float("inf")
+
+ print(f">>> Epoch {epoch}: Perplexity: {perplexity}")
+
+ # 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: Perplexity: 11.397545307900472
+>>> Epoch 1: Perplexity: 10.904909330983092
+>>> Epoch 2: Perplexity: 10.729503505340409
+```
+
+Cool, nous avons été en mesure d'évaluer la perplexité à chaque époque et de garantir la reproductibilité des entraînements multiples !
+
+{/if}
+
+### Utilisation de notre modèle finetuné
+
+Vous pouvez interagir avec votre modèle *finetuné* soit en utilisant son *widget* sur le *Hub*, soit localement avec le `pipeline` de 🤗 *Transformers*. Utilisons ce dernier pour télécharger notre modèle en utilisant le pipeline `fill-mask` :
+
+```python
+from transformers import pipeline
+
+mask_filler = pipeline(
+ "fill-mask", model="huggingface-course/distilbert-base-uncased-finetuned-imdb"
+)
+```
+
+Nous pouvons ensuite donner au pipeline notre exemple de texte « this is a great [MASK] » et voir quelles sont les 5 premières prédictions :
+
+```python
+preds = mask_filler(text)
+
+for pred in preds:
+ print(f">>> {pred['sequence']}")
+```
+
+```python out
+'>>> this is a great movie.'
+'>>> this is a great film.'
+'>>> this is a great story.'
+'>>> this is a great movies.'
+'>>> this is a great character.'
+```
+
+Notre modèle a clairement adapté ses pondérations pour prédire les mots qui sont plus fortement associés aux films !
+
+
+
+Ceci conclut notre première expérience d'entraînement d'un modèle de langage. Dans la [section 6](/course/fr/chapter7/section6), vous apprendrez comment entraîner à partir de zéro un modèle autorégressif comme GPT-2. Allez-y si vous voulez voir comment vous pouvez pré-entraîner votre propre *transformer* !
+
+
+
+✏️ **Essayez !** Pour quantifier les avantages de l'adaptation au domaine, finetunez un classifieur sur le jeu de données IMDb pour à la fois, le checkpoint de DistilBERT pré-entraîné et e checkpoint de DistilBERT finetuné. Si vous avez besoin d'un rafraîchissement sur la classification de texte, consultez le [chapitre 3](/course/fr/chapter3).
+
+
diff --git a/chapters/fr/chapter7/4.mdx b/chapters/fr/chapter7/4.mdx
index e28cf05d5..8f0659328 100644
--- a/chapters/fr/chapter7/4.mdx
+++ b/chapters/fr/chapter7/4.mdx
@@ -1,998 +1,998 @@
-
-
-# Traduction
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Plongeons maintenant dans la traduction. Il s'agit d'une autre [tâche de séquence à séquence](/course/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é](/course/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** ? c'est-à-dire 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** c'est-à-dire créer un modèle qui génère des réponses à des questions compte tenu d'un contexte.
-
-
-
-Si vous disposez d'un corpus de textes suffisamment important en deux langues différentes (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](/course/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 jeu de données [KDE4](https://huggingface.co/datasets/kde4) qui est un jeu de données de fichiers localisés pour les applications [KDE](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. A noter que 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, télécharger et vérifier les précisions de ce modèle sur le [*Hub*](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 *finetuner* 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. Notez que vous pouvez adapter assez facilement le code pour utiliser vos propres données du moment que vous disposez de paires de phrases dans les deux langues que vous voulez traduire. 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
-
-raw_datasets = load_dataset("kde4", lang1="en", lang2="fr")
-```
-
-Si vous souhaitez travailler avec une autre paire de langues, 92 langues sont disponibles au total pour ce jeu de données. Vous pouvez les voir dans la [carte du jeu de données](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. Cependant regroupées dans un seul échantillon. Nous devrons donc créer notre propre jeu 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 qui nous intéresse.
-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 en « 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 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 francophones 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é, lui, s'en tient au mot anglais :
-
-```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 (alerte *spoiler* : il le fera).
-
-
-
-
-
-✏️ **A votre tour !** Un autre mot anglais souvent utilisé en français est « *email* ». Trouvez le premier échantillon dans l'échantillon d'entraînement qui utilise ce mot. Comment est-il traduit ? Comment le modèle pré-entraîné traduit-il cette même phrase ?
-
-
-
-### 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 remplacer le `model_checkpoint` par un tout autre modèle disponible sur le [*Hub*](https://huggingface.co/models) qui aurait votre préférence, ou par un dossier en 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 Marian, 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.). 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)
-
- # Configurer le tokenizer pour les cibles.
- 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 *checkpoints* `t5-xxx`), le modèle s'attendra à ce que les entrées aient un préfixe indiquant la tâche à accomplir, comme `translate: English to French:`.
-
-
-
-
-
-⚠️ 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 toutes les étiquettes qui correspondent au *token* de *padding* à `-100`.
-
-
-
-Nous pouvons maintenant appliquer ce prétraitement en une seule fois sur toutes les échantillons 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 `Trainer` sera le même que précédemment, avec juste un petit changement : nous utilisons ici [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer) qui est une sous-classe de `Trainer` qui nous permet 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 à *finetuner*. Nous allons utiliser l'API habituelle `AutoModel` :
-
-```py
-from transformers import AutoModelForSeq2SeqLM
-
-model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
-```
-
-{:else}
-
-## Finetuner du modèle avec Keras
-
-Tout d'abord, nous avons besoin d'un modèle à *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, vous aurez donc 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, c'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 batchs dynamique. Ici, nous ne pouvons pas simplement utiliser un `DataCollatorWithPadding` comme dans le [chapitre 3](/course/fr/chapter3) car cela ne rembourre que les entrées (identifiants d'entrée, masque d'attention, et *token* de type identifiants). 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 *token* de *padding* du *tokenizer* afin de s'assurer que ces valeurs soient 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 également le `model`. C'est parce que ce collateur de données est également responsable de la préparation des identifiants d'entrée du décodeur, qui sont des versions décalées des étiquettes avec un *token* 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 échantillon 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é rembourrées à la longueur maximale du batch, 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]])
-```
-
-Nous pouvons aussi jeter un coup d'œil aux identifiants 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. Ainsi 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. C'est quelque chose qui est implémenté en coulisses dans 🤗 *Transformers* par la méthode `generate()`. Le `Seq2SeqTrainer` nous laissera utiliser cette méthode pour l'évaluation si nous indiquons `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 charger ce score via `evaluate.load()` comme nous l'avons fait dans le [chapitre 3](/course/fr/chapter3) :
-
-```py
-import evaluate
-
-metric = evaluate.load("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 possibles d'une même phrase. Le jeu de données que nous utilisons n'en fournit qu'une seule, mais en NLP, il n'est pas rare de trouver des jeux de données ayant 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. A titre de comparaison, 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 (répétitions ou prédiction trop courte) 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. 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` dans les étiquettes. Le *tokenizer* fera automatiquement la même chose pour le *token* de *padding* :
-
-```py
-import numpy as np
-
-
-def compute_metrics(eval_preds):
- preds, labels = eval_preds
- # Dans le cas où le modèle retourne plus que les logits de prédiction
- if isinstance(preds, tuple):
- preds = preds[0]
-
- decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)
-
- # Remplacer les -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]
-
- result = metric.compute(predictions=decoded_preds, references=decoded_labels)
- return {"bleu": result["score"]}
-```
-
-{/if}
-
-Maintenant que c'est fait, nous sommes prêts à *finetuner* 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 le jeu 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 tf.data.Dataset,
-# et non le jeu de données original 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 la [section 2](/course/fr/chapter7/2), puis nous entraînons 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 dépôt vers lequel vous voulez pousser le modèle 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"` dans `Seq2SeqTrainingArguments`. Par défaut, le dépôt utilisé sera dans votre espace et nommé après le répertoire de sortie que vous avez défini. 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 métriques 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 des batchs et une le taux de 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 car elle prend du temps. Nous allons juste évaluer une fois notre modèle 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 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/marian-finetuned-kde4-en-to-fr"` à `Seq2SeqTrainingArguments`. Par défaut, le dépôt utilisé sera dans votre espace et nommé d'après le répertoire de sortie que vous avez défini. 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}
-```
-
-Un score BLEU de 39 n'est pas trop mauvais, ce qui reflète le fait que notre modèle est déjà bon pour traduire des phrases anglaises en phrases françaises.
-
-Vient ensuite l'entraînement, qui prendra également un peu de temps :
-
-```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. `Trainer` rédige également une carte de 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 l'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. Ainsi 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* 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 dans 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 le *finetuning* précédent et 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 entraîner sur des TPUs dans un *notebook* de Colab, vous devez déplacer tout ce code dans une fonction d'entraînement et 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 chargeur de données 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'identifiant 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 dépôt dans un dossier local. S'il existe déjà, ce dossier local doit être un clone du dépôt 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)
-
- # Remplace -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 la [section 2](/course/fr/chapter7/2) et du [chapitre 3](/course/fr/chapter3), avec quelques différences dans la partie évaluation. Donc concentrons-nous sur cela !
-
-La première chose à noter est que nous utilisons la méthode `generate()` pour calculer les prédictions. C'est une méthode sur notre modèle de base et non pas le modèle enveloppé 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 la classification de [*token*](/course/fr/chapter7/2), deux processus peuvent avoir rembourrés les entrées et les étiquettes à des formes différentes. Ainsi 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 `Seq2SeqTrainer`. Vous pouvez vérifier celui que nous avons entraîné en utilisant ce code sur [*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 *finetuné*. Et au lieu de laisser le mot anglais « *threads* », le modèle le traduit maintenant par la version française officielle. 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 !
-
-
-
-✏️ **A 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](/course/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é](/course/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** ? c'est-à-dire 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** c'est-à-dire créer un modèle qui génère des réponses à des questions compte tenu d'un contexte.
+
+
+
+Si vous disposez d'un corpus de textes suffisamment important en deux langues différentes (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](/course/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 jeu de données [KDE4](https://huggingface.co/datasets/kde4) qui est un jeu de données de fichiers localisés pour les applications [KDE](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. A noter que 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, télécharger et vérifier les précisions de ce modèle sur le [*Hub*](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 *finetuner* 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. Notez que vous pouvez adapter assez facilement le code pour utiliser vos propres données du moment que vous disposez de paires de phrases dans les deux langues que vous voulez traduire. 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
+
+raw_datasets = load_dataset("kde4", lang1="en", lang2="fr")
+```
+
+Si vous souhaitez travailler avec une autre paire de langues, 92 langues sont disponibles au total pour ce jeu de données. Vous pouvez les voir dans la [carte du jeu de données](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. Cependant regroupées dans un seul échantillon. Nous devrons donc créer notre propre jeu 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 qui nous intéresse.
+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 en « 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 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 francophones 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é, lui, s'en tient au mot anglais :
+
+```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 (alerte *spoiler* : il le fera).
+
+
+
+
+
+✏️ **A votre tour !** Un autre mot anglais souvent utilisé en français est « *email* ». Trouvez le premier échantillon dans l'échantillon d'entraînement qui utilise ce mot. Comment est-il traduit ? Comment le modèle pré-entraîné traduit-il cette même phrase ?
+
+
+
+### 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 remplacer le `model_checkpoint` par un tout autre modèle disponible sur le [*Hub*](https://huggingface.co/models) qui aurait votre préférence, ou par un dossier en 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 Marian, 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.). 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)
+
+ # Configurer le tokenizer pour les cibles.
+ 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 *checkpoints* `t5-xxx`), le modèle s'attendra à ce que les entrées aient un préfixe indiquant la tâche à accomplir, comme `translate: English to French:`.
+
+
+
+
+
+⚠️ 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 toutes les étiquettes qui correspondent au *token* de *padding* à `-100`.
+
+
+
+Nous pouvons maintenant appliquer ce prétraitement en une seule fois sur toutes les échantillons 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 `Trainer` sera le même que précédemment, avec juste un petit changement : nous utilisons ici [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer) qui est une sous-classe de `Trainer` qui nous permet 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 à *finetuner*. Nous allons utiliser l'API habituelle `AutoModel` :
+
+```py
+from transformers import AutoModelForSeq2SeqLM
+
+model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
+```
+
+{:else}
+
+## Finetuner du modèle avec Keras
+
+Tout d'abord, nous avons besoin d'un modèle à *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, vous aurez donc 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, c'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 batchs dynamique. Ici, nous ne pouvons pas simplement utiliser un `DataCollatorWithPadding` comme dans le [chapitre 3](/course/fr/chapter3) car cela ne rembourre que les entrées (identifiants d'entrée, masque d'attention, et *token* de type identifiants). 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 *token* de *padding* du *tokenizer* afin de s'assurer que ces valeurs soient 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 également le `model`. C'est parce que ce collateur de données est également responsable de la préparation des identifiants d'entrée du décodeur, qui sont des versions décalées des étiquettes avec un *token* 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 échantillon 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é rembourrées à la longueur maximale du batch, 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]])
+```
+
+Nous pouvons aussi jeter un coup d'œil aux identifiants 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. Ainsi 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. C'est quelque chose qui est implémenté en coulisses dans 🤗 *Transformers* par la méthode `generate()`. Le `Seq2SeqTrainer` nous laissera utiliser cette méthode pour l'évaluation si nous indiquons `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 charger ce score via `evaluate.load()` comme nous l'avons fait dans le [chapitre 3](/course/fr/chapter3) :
+
+```py
+import evaluate
+
+metric = evaluate.load("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 possibles d'une même phrase. Le jeu de données que nous utilisons n'en fournit qu'une seule, mais en NLP, il n'est pas rare de trouver des jeux de données ayant 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. A titre de comparaison, 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 (répétitions ou prédiction trop courte) 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. 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` dans les étiquettes. Le *tokenizer* fera automatiquement la même chose pour le *token* de *padding* :
+
+```py
+import numpy as np
+
+
+def compute_metrics(eval_preds):
+ preds, labels = eval_preds
+ # Dans le cas où le modèle retourne plus que les logits de prédiction
+ if isinstance(preds, tuple):
+ preds = preds[0]
+
+ decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)
+
+ # Remplacer les -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]
+
+ result = metric.compute(predictions=decoded_preds, references=decoded_labels)
+ return {"bleu": result["score"]}
+```
+
+{/if}
+
+Maintenant que c'est fait, nous sommes prêts à *finetuner* 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 le jeu 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 tf.data.Dataset,
+# et non le jeu de données original 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 la [section 2](/course/fr/chapter7/2), puis nous entraînons 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 dépôt vers lequel vous voulez pousser le modèle 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"` dans `Seq2SeqTrainingArguments`. Par défaut, le dépôt utilisé sera dans votre espace et nommé après le répertoire de sortie que vous avez défini. 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 métriques 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 des batchs et une le taux de 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 car elle prend du temps. Nous allons juste évaluer une fois notre modèle 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 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/marian-finetuned-kde4-en-to-fr"` à `Seq2SeqTrainingArguments`. Par défaut, le dépôt utilisé sera dans votre espace et nommé d'après le répertoire de sortie que vous avez défini. 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}
+```
+
+Un score BLEU de 39 n'est pas trop mauvais, ce qui reflète le fait que notre modèle est déjà bon pour traduire des phrases anglaises en phrases françaises.
+
+Vient ensuite l'entraînement, qui prendra également un peu de temps :
+
+```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. `Trainer` rédige également une carte de 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 l'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. Ainsi 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* 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 dans 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 le *finetuning* précédent et 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 entraîner sur des TPUs dans un *notebook* de Colab, vous devez déplacer tout ce code dans une fonction d'entraînement et 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 chargeur de données 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'identifiant 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 dépôt dans un dossier local. S'il existe déjà, ce dossier local doit être un clone du dépôt 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)
+
+ # Remplace -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 la [section 2](/course/fr/chapter7/2) et du [chapitre 3](/course/fr/chapter3), avec quelques différences dans la partie évaluation. Donc concentrons-nous sur cela !
+
+La première chose à noter est que nous utilisons la méthode `generate()` pour calculer les prédictions. C'est une méthode sur notre modèle de base et non pas le modèle enveloppé 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 la classification de [*token*](/course/fr/chapter7/2), deux processus peuvent avoir rembourrés les entrées et les étiquettes à des formes différentes. Ainsi 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 `Seq2SeqTrainer`. Vous pouvez vérifier celui que nous avons entraîné en utilisant ce code sur [*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 *finetuné*. Et au lieu de laisser le mot anglais « *threads* », le modèle le traduit maintenant par la version française officielle. 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 !
+
+
+
+✏️ **A 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 c2177fb07..d2d570667 100644
--- a/chapters/fr/chapter7/5.mdx
+++ b/chapters/fr/chapter7/5.mdx
@@ -1,1083 +1,1083 @@
-
-
-# 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 [*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é de critiques de produits Amazon en six langues et est généralement utilisé pour évaluer les classifieurs 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 critiques pour la partie entraînement et 5 000 critiques 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. 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 le compte des 20 premiers produits
-english_df["product_category"].value_counts()[:20]
-```
-
-```python out
-home 17679 # maison
-apparel 15951 # vêtements
-wireless 15717 # sans fil
-other 13418 # autres
-beauty 12091 # beauté
-drugstore 11730 # pharmacie
-kitchen 10382 # cuisine
-toy 8745 # jouets
-sports 8277 # sports
-automotive 7506 # automobile
-lawn_and_garden 7327 # pelouse_et_jardin
-home_improvement 7136 # amélioration_de_la_maison
-pet_products 7082 # produits_pour_animaux_de_compagnie
-digital_ebook_purchase 6749 # achat_de_livres_numériques
-pc 6401 # ordinateur_personnel
-electronics 6186 # électronique
-office_product 5521 # produits_de_bureau
-shoes 5197 # chaussures
-grocery 4730 # épicerie
-book 3756 # livre
-Name: product_category, dtype: int64
-```
-
-Les produits les plus populaires du jeu de données anglais 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 jeux 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 contient 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 *transformers* 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 le 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 *transformers* 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 *transformers* 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 d'être entraîné sur un corpus d'une seule langue, ils sont entraînés conjointement sur des textes dans plus de 50 langues !
-
-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 de NLP est formulée en termes d'un préfixe de *prompt* comme `summarize:` qui conditionne le modèle à adapter le texte généré au *prompt*. Comme le montre la figure ci-dessous, cela rend le 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 aurez terminé cette section, comparez le mT5 à mBART en *finetunant* 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 entrées 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 *checkpoint* du modèle pré-entraîné. Nous utiliserons `mt5-small` comme *checkpoint* afin de pouvoir *finetuner* 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 *checkpoint* 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 *finetuning* 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 Unicode spécial `▁` 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. En effet, 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"
-# "J'ai absolument adoré lire les Hunger Games"
-reference_summary = "I loved reading the Hunger Games"
-# "J'ai adoré lire les 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{Nombre\,de\,mots\,qui\,se\,chevauchent}}{\mathrm{Nombre\, total\, de\, mots\, dans\, le\, résumé\, de\, réference}} $$
-
-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 est pertinente :
-
-$$ \mathrm{Precision} = \frac{\mathrm{Nombre\,de\,mots\,qui\,se\,chevauchent}}{\mathrm{Nombre\, total\, de\, mots\, dans\, le\, résumé\, généré}} $$
-
-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 *package* `rouge_score` :
-
-```py
-!pip install rouge_score
-```
-
-et ensuite charger la métrique ROUGE comme suit :
-
-```python
-import evaluate
-
-rouge_score = evaluate.load("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 pas mal d'informations dans cette sortie. Qu'est-ce que ça veut dire ? Tout d'abord, 🤗 *Datasets* calcule 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 `mid` 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 (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. Le « sum » 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 *baseline* solide, mais simple !
-
-### Création d'une base de référence solide
-
-Une *baseline* commune pour le résumé de texte consiste à prendre simplement les trois premières phrases d'un article, souvent appelée la *baseline* _lead-3_. Nous pourrions utiliser les points pour tracker les limites des phrases 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 *package* 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 tester le tout 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 jeu 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 critiques sont typiquement concis et donc que la *baseline* *lead-3* est trop verbeuse. Maintenant que nous disposons d'une bonne *baseline*, concentrons-nous sur le *finetuning* du 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é à partir du *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 *checkpoint* `mt5-small`. Puisque la compression est une tâche de séquence à séquence, nous pouvons charger le modèle avec la classe `TFAutoModelForSeq2SeqLM`, 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 le *finetuning* 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 du [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é au [chapitre 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()`. 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'à trois *checkpoints* pendant l'entraînement. C'est parce que même la plus petite version de mT5 utilise environ 1 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 à `Seq2SeqTrainer` 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
- )
- # Extraire les scores médians
- 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 *transformer* encodeur-décodeur, une des subtilités de la préparation de nos batchs 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 *modèle* :
-
-{#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 batch 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`, où chaque `dict` représente un seul exemple du jeu 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 *token* `[PAD]` (dont l'identifiant est `0`). De même, nous pouvons voir que les `labels` ont été complétés par des `-100`, 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 *token* `[PAD]` dans la première entrée.
-
-{#if fw === 'pt'}
-
-Nous avons enfin tous les ingrédients dont nous avons besoin pour l'entraînement ! Nous devons maintenant simplement instancier le `Seq2SeqTrainer` avec les arguments :
-
-```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 *baseline* *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 *checkpoint* 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 *finetuner* 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` en utilisant le collateur de données que nous avons défini ci-dessus, puis utiliser `compile()` et `fit()`. 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 le jeu 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 tf.data.Dataset,
-# et non le jeu de données original 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 *finetunons* 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 le [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 échantillons. 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 programmeur du taux d'apprentissage,
-* implémenter une fonction pour post-traiter les résumés pour l'évaluation,
-* créer un dépôt sur le *Hub* vers lequel nous pouvons pousser notre modèle.
-
-Pour le programmeur de taux d'apprentissage, nous utiliserons le programmeur 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 dépôt, et la bibliothèque a une fonction utilitaire pour combiner l'identifiant du dépôt 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 dépôt 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)
-```
-
-Cela nous permettra de pousser les artefacts vers le *Hub* en appelant la méthode `repo.push_to_hub()` pendant l'entraînement ! Concluons maintenant notre analyse en écrivant la boucle d'entraînement.
-
-### 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 *checkpoints* 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 *checkpoints* 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'1 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é 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 [*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é de critiques de produits Amazon en six langues et est généralement utilisé pour évaluer les classifieurs 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 critiques pour la partie entraînement et 5 000 critiques 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. 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 le compte des 20 premiers produits
+english_df["product_category"].value_counts()[:20]
+```
+
+```python out
+home 17679 # maison
+apparel 15951 # vêtements
+wireless 15717 # sans fil
+other 13418 # autres
+beauty 12091 # beauté
+drugstore 11730 # pharmacie
+kitchen 10382 # cuisine
+toy 8745 # jouets
+sports 8277 # sports
+automotive 7506 # automobile
+lawn_and_garden 7327 # pelouse_et_jardin
+home_improvement 7136 # amélioration_de_la_maison
+pet_products 7082 # produits_pour_animaux_de_compagnie
+digital_ebook_purchase 6749 # achat_de_livres_numériques
+pc 6401 # ordinateur_personnel
+electronics 6186 # électronique
+office_product 5521 # produits_de_bureau
+shoes 5197 # chaussures
+grocery 4730 # épicerie
+book 3756 # livre
+Name: product_category, dtype: int64
+```
+
+Les produits les plus populaires du jeu de données anglais 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 jeux 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 contient 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 *transformers* 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 le 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 *transformers* 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 *transformers* 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 d'être entraîné sur un corpus d'une seule langue, ils sont entraînés conjointement sur des textes dans plus de 50 langues !
+
+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 de NLP est formulée en termes d'un préfixe de *prompt* comme `summarize:` qui conditionne le modèle à adapter le texte généré au *prompt*. Comme le montre la figure ci-dessous, cela rend le 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 aurez terminé cette section, comparez le mT5 à mBART en *finetunant* 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 entrées 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 *checkpoint* du modèle pré-entraîné. Nous utiliserons `mt5-small` comme *checkpoint* afin de pouvoir *finetuner* 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 *checkpoint* 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 *finetuning* 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 Unicode spécial `▁` 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. En effet, 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"
+# "J'ai absolument adoré lire les Hunger Games"
+reference_summary = "I loved reading the Hunger Games"
+# "J'ai adoré lire les 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{Nombre\,de\,mots\,qui\,se\,chevauchent}}{\mathrm{Nombre\, total\, de\, mots\, dans\, le\, résumé\, de\, réference}} $$
+
+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 est pertinente :
+
+$$ \mathrm{Precision} = \frac{\mathrm{Nombre\,de\,mots\,qui\,se\,chevauchent}}{\mathrm{Nombre\, total\, de\, mots\, dans\, le\, résumé\, généré}} $$
+
+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 *package* `rouge_score` :
+
+```py
+!pip install rouge_score
+```
+
+et ensuite charger la métrique ROUGE comme suit :
+
+```python
+import evaluate
+
+rouge_score = evaluate.load("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 pas mal d'informations dans cette sortie. Qu'est-ce que ça veut dire ? Tout d'abord, 🤗 *Datasets* calcule 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 `mid` 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 (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. Le « sum » 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 *baseline* solide, mais simple !
+
+### Création d'une base de référence solide
+
+Une *baseline* commune pour le résumé de texte consiste à prendre simplement les trois premières phrases d'un article, souvent appelée la *baseline* _lead-3_. Nous pourrions utiliser les points pour tracker les limites des phrases 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 *package* 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 tester le tout 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 jeu 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 critiques sont typiquement concis et donc que la *baseline* *lead-3* est trop verbeuse. Maintenant que nous disposons d'une bonne *baseline*, concentrons-nous sur le *finetuning* du 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é à partir du *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 *checkpoint* `mt5-small`. Puisque la compression est une tâche de séquence à séquence, nous pouvons charger le modèle avec la classe `TFAutoModelForSeq2SeqLM`, 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 le *finetuning* 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 du [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é au [chapitre 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()`. 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'à trois *checkpoints* pendant l'entraînement. C'est parce que même la plus petite version de mT5 utilise environ 1 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 à `Seq2SeqTrainer` 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
+ )
+ # Extraire les scores médians
+ 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 *transformer* encodeur-décodeur, une des subtilités de la préparation de nos batchs 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 *modèle* :
+
+{#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 batch 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`, où chaque `dict` représente un seul exemple du jeu 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 *token* `[PAD]` (dont l'identifiant est `0`). De même, nous pouvons voir que les `labels` ont été complétés par des `-100`, 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 *token* `[PAD]` dans la première entrée.
+
+{#if fw === 'pt'}
+
+Nous avons enfin tous les ingrédients dont nous avons besoin pour l'entraînement ! Nous devons maintenant simplement instancier le `Seq2SeqTrainer` avec les arguments :
+
+```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 *baseline* *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 *checkpoint* 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 *finetuner* 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` en utilisant le collateur de données que nous avons défini ci-dessus, puis utiliser `compile()` et `fit()`. 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 le jeu 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 tf.data.Dataset,
+# et non le jeu de données original 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 *finetunons* 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 le [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 échantillons. 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 programmeur du taux d'apprentissage,
+* implémenter une fonction pour post-traiter les résumés pour l'évaluation,
+* créer un dépôt sur le *Hub* vers lequel nous pouvons pousser notre modèle.
+
+Pour le programmeur de taux d'apprentissage, nous utiliserons le programmeur 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 dépôt, et la bibliothèque a une fonction utilitaire pour combiner l'identifiant du dépôt 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 dépôt 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)
+```
+
+Cela nous permettra de pousser les artefacts vers le *Hub* en appelant la méthode `repo.push_to_hub()` pendant l'entraînement ! Concluons maintenant notre analyse en écrivant la boucle d'entraînement.
+
+### 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 *checkpoints* 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 *checkpoints* 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'1 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é 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/6.mdx b/chapters/fr/chapter7/6.mdx
index a6c81f76d..0a3d395b4 100644
--- a/chapters/fr/chapter7/6.mdx
+++ b/chapters/fr/chapter7/6.mdx
@@ -1,905 +1,905 @@
-
-
-# Entraîner un modèle de langage causal à partir de zéro
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Jusqu'à présent, nous avons surtout réutilisé des modèles pré-entraînés et les avons *finetunés* sur de nouveaux cas d'usage. Comme nous l'avons vu dans le [chapitre 1](/course/fr/chapter1), ceci est communément appelé _apprentissage par transfert_, et il s'agit d'une stratégie très efficace pour appliquer les *transformers* à la plupart des applications du monde réel où les données étiquetées sont rares. Dans ce chapitre, nous allons adopter une approche différente consistant à entraîner un modèle complètement nouveau à partir de zéro. C'est une bonne démarche à adopter si vous avez beaucoup de données et qu'elles sont très différentes des données de pré-entraînement utilisées par les modèles disponibles. Cependant, le pré-entraînement d'un modèle de langue nécessite beaucoup plus de ressources informatiques que le simple *finetuning* d'un modèle existant. Parmi les exemples où il peut être utile d'entraîner un nouveau modèle, citons les jeux de données constitués de notes de musique, de séquences moléculaires telles que l'ADN, ou de langages de programmation. Ces derniers ont récemment gagné en popularité grâce à des outils tels que TabNine et Copilot de GitHub (alimentés par le modèle Codex d'OpenAI) qui peuvent générer de longues séquences de code. Cette tâche de génération de texte est mieux abordée avec des modèles de langage autorégressifs ou causaux tels que le GPT-2.
-
-Dans cette section, nous allons construire une version réduite d'un modèle de génération de code Python. Nous nous concentrerons sur la complétion d'une ligne de code au lieu de fonctions ou de classes complètes. Lorsque vous travaillez sur des projets de science des données en Python, vous êtes souvent en contact avec les bibliothèques `matplotlib`, `seaborn`, `pandas` et `scikit-learn`. Lors de l'utilisation de ces *frameworks*, il est fréquent d'avoir besoin de rechercher des commandes spécifiques. Il serait donc bien d'utiliser un modèle pour compléter ces appels pour nous.
-
-
-
-
-Dans le [chapitre 6](/course/fr/chapter6), nous avons créé un *tokenizer* efficace pour traiter du code Python. Nous avons besoin d'un jeu de données à grande échelle pour pré-entraîner un modèle. Ici, nous allons appliquer notre *tokenizer* à un corpus de code Python provenant des dépôts GitHub. Nous utiliserons ensuite l'API `Trainer` et 🤗 *Accelerate* pour entraîner le modèle. C'est parti !
-
-
-
-
-Il s'agit d'une présentation du modèle qui a été entraîné à l'aide du code présenté dans cette section et qui a ensuité été téléchargé sur le *Hub*. Vous pouvez le trouver [ici](https://huggingface.co/huggingface-course/codeparrot-ds?text=plt.imshow%28). Notez qu'étant donné qu'il y a un certains aléat dans la génération du texte, vous obtiendrez probablement un résultat légèrement différent.
-
-## Collecte des données
-
-On peut trouver du code Python en abondance dans les dépôts de code tels que GitHub, que nous pouvons utiliser pour créer un jeu de données en récupérant chaque dépôt Python. C'est l'approche adoptée dans le [livre *Natural Language Processing with Transformers*](https://learning.oreilly.com/library/view/natural-language-processing/9781098103231/) pour pré-entraîner un grand GPT-2. En utilisant un dépôt GitHub d'environ 180 Go contenant approximativement 20 millions de fichiers Python, les auteurs du livre ont construit un jeu de données appelé `codeparrot` qu'ils ont ensuite partagé sur le [*Hub*](https://huggingface.co/datasets/transformersbook/codeparrot).
-
-Cependant, entraîner sur l'ensemble du corpus prend beaucoup de temps et demande beaucoup de ressources de calculs. Dans notre cas, nous n'avons besoin que du sous-ensemble du jeu de données qui est relatif aux codes portant sur la science des données. Commençons donc par filtrer le jeu de données `codeparrot` en ne gardant que les fichiers incluant l'une des bibliothèques de science des données énumérées précédemment. En raison de la taille du jeu de données, nous voulons éviter de le télécharger. Nous utiliserons donc la fonctionnalité de *streaming* de 🤗 *Datasets* afin de le filtrer à la volée. Pour nous aider à filtrer les échantillons de code utilisant les bibliothèques que nous avons mentionnées précédemment, nous utilisons la fonction suivante :
-
-```py
-def any_keyword_in_string(string, keywords):
- for keyword in keywords:
- if keyword in string:
- return True
- return False
-```
-
-Testons-le sur deux exemples :
-
-```py
-filters = ["pandas", "sklearn", "matplotlib", "seaborn"]
-example_1 = "import numpy as np"
-example_2 = "import pandas as pd"
-
-print(
- any_keyword_in_string(example_1, filters), any_keyword_in_string(example_2, filters)
-)
-```
-
-```python out
-False True
-```
-
-Nous pouvons l'utiliser pour créer une fonction qui va *streamer* le jeu de donner et filtrer les éléments que nous voulons :
-
-```py
-def filter_streaming_dataset(dataset, filters):
- filtered_dict = defaultdict(list)
- total = 0
- for sample in tqdm(iter(dataset)):
- total += 1
- if any_keyword_in_string(sample["content"], filters):
- for k, v in sample.items():
- filtered_dict[k].append(v)
- print(f"{len(filtered_dict['content'])/total:.2%} of data after filtering.")
- return Dataset.from_dict(filtered_dict)
-```
-
-Ensuite, nous pouvons simplement appliquer cette fonction :
-
-```py
-# Cette cellule prendra beaucoup de temps à s'exécuter, donc vous devriez la sauter et aller à la suivante !
-from datasets import load_dataset
-
-split = "train" # "valid"
-filters = ["pandas", "sklearn", "matplotlib", "seaborn"]
-
-data = load_dataset(f"transformersbook/codeparrot-{split}", split=split, streaming=True)
-filtered_data = filter_streaming_dataset(data, filters)
-```
-
-```python out
-3.26% of data after filtering.
-```
-
-Cela nous laisse avec environ 3 % du jeu de données original, ce qui est tout de même assez important puisqu'il fait 6 Go et se compose de 600 000 scripts Python !
-
-Le filtrage peut prendre de 2 à 3 heures, selon votre machine et votre bande passante. Si vous ne voulez pas passer par ce long processus, nous fournissons sur le *Hub* le jeu de données filtré pour que vous puissiez le télécharger :
-
-```py
-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")
-
-raw_datasets = DatasetDict(
- {
- "train": ds_train, # .shuffle().select(range(50000)),
- "valid": ds_valid, # .shuffle().select(range(500))
- }
-)
-
-raw_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'],
- num_rows: 606720
- })
- valid: Dataset({
- features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'],
- num_rows: 3322
- })
-})
-```
-
-
-
-Le pré-entraînement du modèle de langue prendra un certain temps. Nous vous suggérons donc d'exécuter d'abord la boucle d'entraînement sur un petit échantillon des données en décommentant les deux lignes dans le code ci-dessus. Assurez-vous alors que l'entraînement se termine avec succès et que les modèles sont stockés. Rien n'est plus frustrant qu'un entraînement qui échoue à la dernière étape car vous avez oublié de créer un dossier ou parce qu'il y a une faute de frappe à la fin de la boucle d'entraînement !
-
-
-
-Examinons un exemple tiré du jeu de données. Nous ne montrerons que les 200 premiers caractères de chaque champ :
-
-```py
-for key in raw_datasets["train"][0]:
- print(f"{key.upper()}: {raw_datasets['train'][0][key][:200]}")
-```
-
-```python out
-'REPO_NAME: kmike/scikit-learn'
-'PATH: sklearn/utils/__init__.py'
-'COPIES: 3'
-'SIZE: 10094'
-'''CONTENT: """
-The :mod:`sklearn.utils` module includes various utilites.
-"""
-
-from collections import Sequence
-
-import numpy as np
-from scipy.sparse import issparse
-import warnings
-
-from .murmurhash import murm
-LICENSE: bsd-3-clause'''
-```
-
-Nous pouvons voir que le champ `content` contient le code sur lequel nous voulons que notre modèle s'entraîne. Maintenant que nous avons un jeu de données, nous devons préparer les textes afin qu'ils soient dans un format approprié pour le pré-entraînement.
-
-## Préparation du jeu de données
-
-
-
-La première étape est de tokeniser les données afin de pouvoir les utiliser pour l'entraînement. Puisque notre objectif est d'autocompléter de courts appels de fonctions, nous pouvons garder la taille du contexte relativement petite. L'avantage est que nous pouvons entraîner le modèle beaucoup plus rapidement et qu'il nécessite beaucoup moins de mémoire. Si c'est important pour votre application d'avoir davantage de contexte (par exemple, si vous voulez que le modèle écrive des tests unitaires basés sur un fichier avec la définition de la fonction), assurez-vous d'augmenter ce nombre. Gardez néanmoins à l'esprit que cela s'accompagne d'une plus grande empreinte mémoire du GPU. Pour l'instant, fixons la taille du contexte à 128 *tokens*, par opposition aux 1 024 ou 2 048 utilisés respectivement dans le GPT-2 et le GPT-3.
-
-La plupart des documents contiennent beaucoup plus de 128 *tokens*, donc le fait de tronquer les entrées à la longueur maximale éliminerait une grande partie de notre jeu de données. A la place, nous allons utiliser l'option `return_overflowing_tokens` pour tokeniser l'entrée entière et la diviser en plusieurs morceaux, comme nous l'avons fait dans le [chapitre 6](/course/fr/chapter6/4). Nous utiliserons également l'option `return_length` pour retourner automatiquement la longueur de chaque morceau créé. Souvent, le dernier morceau est plus petit que la taille du contexte et nous nous en débarrasserons pour éviter les problèmes de *padding*. Nous n'en avons pas vraiment besoin puisque de toute façon nous avons beaucoup de données.
-
-
-
-
-
-
-Voyons comment cela fonctionne en examinant les deux premiers exemples :
-
-```py
-from transformers import AutoTokenizer
-
-context_length = 128
-tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")
-
-outputs = tokenizer(
- raw_datasets["train"][:2]["content"],
- truncation=True,
- max_length=context_length,
- return_overflowing_tokens=True,
- return_length=True,
-)
-
-print(f"Input IDs length: {len(outputs['input_ids'])}")
-print(f"Input chunk lengths: {(outputs['length'])}")
-print(f"Chunk mapping: {outputs['overflow_to_sample_mapping']}")
-```
-
-```python out
-Input IDs length: 34
-Input chunk lengths: [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 117, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 41]
-Chunk mapping: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
-```
-
-Nous pouvons voir que nous obtenons 34 morceaux à partir de ces deux exemples. En regardant leurs longueurs, nous pouvons voir qu'ils se terminent avec moins de 128 *tokens* (117 et 41, respectivement). Ils ne représentent qu'une petite fraction du total des morceaux que nous avons (2/34), donc nous pouvons les jeter sans risque. Avec le champ `overflow_to_sample_mapping`, nous pouvons aussi reconstruire quels morceaux appartenaient à quels échantillons d'entrée.
-
-Avec cette opération, nous utilisons une fonctionnalité pratique de la fonction `Dataset.map()` de 🤗 *Datasets*. En effet, celle-ci ne nécessite pas une correspondance un à un comme nous l'avons vu dans la [section 3](/course/fr/chapter7/3). Nous pouvons créer des batchs avec plus ou moins d'éléments que le batch d'entrée. C'est utile lorsque l'on effectue des opérations telles que l'augmentation ou le filtrage des données qui modifient le nombre d'éléments. Dans notre cas, lors de la tokenisation de chaque élément en morceaux de longeur de la taille de contexte spécifiée, nous créons de nombreux échantillons de chaque document. Nous devons juste nous assurer de supprimer les colonnes existantes, car elles ont une taille conflictuelle. Si nous voulions les garder, nous pourrions les répéter de manière appropriée et les retourner dans l'appel `Dataset.map()` :
-
-```py
-def tokenize(element):
- outputs = tokenizer(
- element["content"],
- truncation=True,
- max_length=context_length,
- return_overflowing_tokens=True,
- return_length=True,
- )
- input_batch = []
- for length, input_ids in zip(outputs["length"], outputs["input_ids"]):
- if length == context_length:
- input_batch.append(input_ids)
- return {"input_ids": input_batch}
-
-
-tokenized_datasets = raw_datasets.map(
- tokenize, batched=True, remove_columns=raw_datasets["train"].column_names
-)
-tokenized_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['input_ids'],
- num_rows: 16702061
- })
- valid: Dataset({
- features: ['input_ids'],
- num_rows: 93164
- })
-})
-```
-
-Nous avons maintenant 16,7 millions d'exemples avec 128 *tokens* chacun, ce qui correspond à environ 2,1 milliards de *tokens* au total. A titre de comparaison, les modèles GPT-3 et Codex d'OpenAI sont entraînés sur 300 et 100 milliards de *tokens*, respectivement. Les modèles Codex étant initialisés à partir des *checkpoints* GPT-3. Notre objectif dans cette section n'est pas de rivaliser avec ces modèles, qui peuvent générer des textes longs et cohérents, mais de créer une version réduite fournissant une fonction d'autocomplétion rapide.
-
-Maintenant que le jeu de données est prêt, configurons le modèle !
-
-
-
-✏️ **Essayez !** Se débarrasser de tous les morceaux qui sont plus petits que la taille du contexte n'était pas un gros problème ici parce que nous utilisons de petites fenêtres de contexte. Si vous augmentez la taille du contexte (ou si vous avez un corpus de documents courts), la fraction des morceaux qui sont jetés augmentera. Une façon plus efficace de préparer les données est de joindre tous les échantillons dans un batch avec un *token* `eos_token_id` entre les deux, puis d'effectuer le découpage sur les séquences concaténées. Comme exercice, modifiez la fonction `tokenize()` pour utiliser cette approche. Notez que vous devrez mettre `truncation=False` et enlever les autres arguments du *tokenizer* pour obtenir la séquence complète des identifiants des *tokens*.
-
-
-
-
-## Initialisation d'un nouveau modèle
-
-Notre première étape consiste à initialiser un GPT-2. Pour notre modèle, nous utiliserons la même configuration que pour le petit modèle GPT-2. Ainsi nous chargeons la configuration pré-entraînée, nous nous assurons que la taille du *tokenizer* correspond à la taille du vocabulaire du modèle et nous passons les identifiants des *tokens* `bos` et `eos` (début et fin de séquence) :
-
-{#if fw === 'pt'}
-
-```py
-from transformers import AutoTokenizer, GPT2LMHeadModel, AutoConfig
-
-config = AutoConfig.from_pretrained(
- "gpt2",
- vocab_size=len(tokenizer),
- n_ctx=context_length,
- bos_token_id=tokenizer.bos_token_id,
- eos_token_id=tokenizer.eos_token_id,
-)
-```
-
-Avec cette configuration, nous pouvons charger un nouveau modèle. Notez que c'est la première fois que nous n'utilisons pas la fonction `from_pretrained()` puisque nous initialisons nous-mêmes un modèle :
-
-```py
-model = GPT2LMHeadModel(config)
-model_size = sum(t.numel() for t in model.parameters())
-print(f"GPT-2 size: {model_size/1000**2:.1f}M parameters")
-```
-
-```python out
-GPT-2 size: 124.2M parameters
-```
-
-{:else}
-
-```py
-from transformers import AutoTokenizer, TFGPT2LMHeadModel, AutoConfig
-
-config = AutoConfig.from_pretrained(
- "gpt2",
- vocab_size=len(tokenizer),
- n_ctx=context_length,
- bos_token_id=tokenizer.bos_token_id,
- eos_token_id=tokenizer.eos_token_id,
-)
-```
-
-Avec cette configuration, nous pouvons charger un nouveau modèle. Notez que c'est la première fois que nous n'utilisons pas la fonction `from_pretrained()` puisque nous initialisons nous-mêmes un modèle :
-
-```py
-model = TFGPT2LMHeadModel(config)
-model(model.dummy_inputs) # Construit le modèle
-model.summary()
-```
-
-```python out
-_________________________________________________________________
-Layer (type) Output Shape Param #
-=================================================================
-transformer (TFGPT2MainLayer multiple 124242432
-=================================================================
-Total params: 124,242,432
-Trainable params: 124,242,432
-Non-trainable params: 0
-_________________________________________________________________
-```
-
-{/if}
-
-Notre modèle comporte 124 millions de paramètres que nous devrons régler. Avant de commencer l'entraînement, nous devons configurer un collateur de données qui se chargera de créer les batchs. Nous pouvons utiliser le collateur `DataCollatorForLanguageModeling`, qui est conçu spécifiquement pour la modélisation du langage (comme son nom le suggère subtilement). En plus de l'empilage et du rembourrage des batchs, il s'occupe aussi de la création des étiquettes du modèle de langage. Dans la modélisation causale du langage, les entrées servent aussi d'étiquettes (juste décalées d'un élément) et que le collateur de données crée à la volée pendant l'entraînement pour ne pas avoir à dupliquer les `input_ids`.
-
-Notez que `DataCollatorForLanguageModeling` supporte à la fois la modélisation du langage masqué (MLM pour *masked language modeling*) et la modélisation du langage causal (CLM pour *causal language modeling*). Par défaut, il prépare les données pour la MLM mais nous pouvons passer à la CLM en définissant l'argument `mlm=False` :
-
-{#if fw === 'pt'}
-
-```py
-from transformers import DataCollatorForLanguageModeling
-
-tokenizer.pad_token = tokenizer.eos_token
-data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)
-```
-
-{:else}
-
-```py
-from transformers import DataCollatorForLanguageModeling
-
-tokenizer.pad_token = tokenizer.eos_token
-data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False, return_tensors="tf")
-```
-
-{/if}
-
-Prenons un exemple :
-
-```py
-out = data_collator([tokenized_dataset["train"][i] for i in range(5)])
-for key in out:
- print(f"{key} shape: {out[key].shape}")
-```
-
-{#if fw === 'pt'}
-
-```python out
-input_ids shape: torch.Size([5, 128])
-attention_mask shape: torch.Size([5, 128])
-labels shape: torch.Size([5, 128])
-```
-
-{:else}
-
-```python out
-input_ids shape: (5, 128)
-attention_mask shape: (5, 128)
-labels shape: (5, 128)
-```
-
-{/if}
-
-Nous pouvons voir que les exemples ont été empilés et que tous les tenseurs ont la même forme.
-
-{#if fw === 'tf'}
-
-Maintenant nous pouvons utiliser la méthode `to_tf_dataset()` pour convertir nos jeux de données en jeux de données TensorFlow avec le collateur de données que nous avons créé ci-dessus :
-
-```python
-tf_train_dataset = tokenized_dataset["train"].to_tf_dataset(
- columns=["input_ids", "attention_mask", "labels"],
- collate_fn=data_collator,
- shuffle=True,
- batch_size=32,
-)
-tf_eval_dataset = tokenized_dataset["valid"].to_tf_dataset(
- columns=["input_ids", "attention_mask", "labels"],
- collate_fn=data_collator,
- shuffle=False,
- batch_size=32,
-)
-```
-
-{/if}
-
-
-
-⚠️ Le déplacement des entrées et des étiquettes pour les aligner se fait à l'intérieur du modèle, de sorte que le collecteur de données ne fait que copier les entrées pour créer les étiquettes.
-
-
-
-
-Nous avons maintenant tout ce qu'il faut pour entraîner notre modèle. Ce n'était pas si compliqué ! Avant de commencer l'entraînement, nous devons nous connecter à Hugging Face. Si vous travaillez dans un *notebook*, vous pouvez le faire avec la fonction utilitaire suivante :
-
-```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 === 'pt'}
-
-Tout ce qu'il reste à faire est de configurer les arguments d'entraînement et de lancer la fonction `Trainer`. Nous utiliserons un programme de taux d'apprentissage de type cosinus avec un réchauffement et une taille de batch de 256 (`per_device_train_batch_size` x `gradient_accumulation_steps`). L'accumulation du gradient est utilisée lorsqu'un seul batch ne tient pas en mémoire, et construit le gradient de manière incrémentale à travers plusieurs passages en avant/en arrière. Nous verrons cela en action lorsque nous créerons la boucle d'entraînement avec 🤗 *Accelerate*.
-
-```py
-from transformers import Trainer, TrainingArguments
-
-args = TrainingArguments(
- output_dir="codeparrot-ds",
- per_device_train_batch_size=32,
- per_device_eval_batch_size=32,
- evaluation_strategy="steps",
- eval_steps=5_000,
- logging_steps=5_000,
- gradient_accumulation_steps=8,
- num_train_epochs=1,
- weight_decay=0.1,
- warmup_steps=1_000,
- lr_scheduler_type="cosine",
- learning_rate=5e-4,
- save_steps=5_000,
- fp16=True,
- push_to_hub=True,
-)
-
-trainer = Trainer(
- model=model,
- tokenizer=tokenizer,
- args=args,
- data_collator=data_collator,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["valid"],
-)
-```
-
-Maintenant, nous pouvons simplement lancer le `Trainer` et attendre que l'entraînement se termine. Selon que vous l'exécutez sur la totalité ou sur un sous-ensemble de l'échantillon d'entraînement, cela prendra respectivement 20 ou 2 heures. Alors prenez quelques cafés et un bon livre à lire !
-
-```py
-trainer.train()
-```
-
-Une fois l'entraînement terminé, nous pouvons pousser le modèle et le *tokenizer* vers le *Hub* :
-
-```py
-trainer.push_to_hub()
-```
-
-{:else}
-
-Tout ce qu'il reste à faire est de configurer les hyperparamètres d'entraînement et d'appeler `compile()` et `fit()`. Nous utiliserons un programme de taux d'apprentissage avec un réchauffement pour améliorer la stabilité de l'entraînement :
-
-```py
-from transformers import create_optimizer
-import tensorflow as tf
-
-num_train_steps = len(tf_train_dataset)
-optimizer, schedule = create_optimizer(
- init_lr=5e-5,
- num_warmup_steps=1_000,
- 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")
-```
-
-Maintenant, nous pouvons simplement appeler `model.fit()` et attendre que l'entraînement se termine. Selon que vous l'exécutez sur la totalité ou sur un sous-ensemble de l'échantillon d'entraînement, cela prendra respectivement 20 ou 2 heures. Alors prenez quelques cafés et un bon livre à lire ! Une fois l'entraînement terminé, nous pouvons pousser le modèle et le *tokenizer* vers le *Hub* :
-
-```py
-from transformers.keras_callbacks import PushToHubCallback
-
-callback = PushToHubCallback(output_dir="codeparrot-ds", tokenizer=tokenizer)
-
-model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback])
-```
-
-{/if}
-
-
-
-✏️ **Essayez !** Il ne nous a fallu qu'une trentaine de lignes de code en plus des `TrainingArguments` pour passer des textes bruts à l'entraînement du GPT-2. Essayez-le avec votre propre jeu de données et voyez si vous pouvez obtenir de bons résultats !
-
-
-
-
-
-{#if fw === 'pt'}
-
-💡 Si vous avez accès à une machine avec plusieurs GPUs, essayez d'y exécuter le code. `Trainer` gère automatiquement plusieurs machines ce qui peut accélérer considérablement l'entraînement.
-
-{:else}
-
-💡 Si vous avez accès à une machine avec plusieurs GPUs, vous pouvez essayer d'utiliser `MirroredStrategy` pour accélérer considérablement l'entraînement. Vous devrez créer un objet `tf.distribute.MirroredStrategy` et vous assurer que les commandes `to_tf_dataset` ainsi que la création du modèle et l'appel à `fit()` sont tous exécutés dans `scope()`. Vous pouvez consulter la documentation à ce sujet [ici](https://www.tensorflow.org/guide/distributed_training#use_tfdistributestrategy_with_keras_modelfit).
-
-{/if}
-
-
-
-## Génération de code avec le pipeline
-
-C'est maintenant le moment de vérité : voyons comment le modèle entraîné fonctionne réellement ! Nous pouvons voir dans les logs que la perte a diminué régulièrement, mais pour mettre le modèle à l'épreuve, regardons comment il fonctionne sur certains messages. Pour ce faire, nous allons envelopper le modèle dans un `pipeline` de génération de texte et, s'il y en a un de disponible, utiliser un GPU pour avoir des générations rapidement :
-
-{#if fw === 'pt'}
-
-```py
-import torch
-from transformers import pipeline
-
-device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-pipe = pipeline(
- "text-generation", model="huggingface-course/codeparrot-ds", device=device
-)
-```
-
-{:else}
-
-```py
-from transformers import pipeline
-
-course_model = TFGPT2LMHeadModel.from_pretrained("huggingface-course/codeparrot-ds")
-course_tokenizer = AutoTokenizer.from_pretrained("huggingface-course/codeparrot-ds")
-pipe = pipeline(
- "text-generation", model=course_model, tokenizer=course_tokenizer, device=0
-)
-```
-
-{/if}
-
-Let's start with the simple task of creating a scatter plot:
-
-```py
-txt = """\
-# créer des données
-x = np.random.randn(100)
-y = np.random.randn(100)
-
-# créer un nuage de points avec x, y
-"""
-print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
-```
-
-```python out
-# créer des données
-x = np.random.randn(100)
-y = np.random.randn(100)
-
-# créer un nuage de points avec x, y
-plt.scatter(x, y)
-```
-
-Le résultat semble correct. Est-ce que cela fonctionne aussi pour une opération `pandas` ? Voyons si nous pouvons créer un `DataFrame` à partir de deux tableaux :
-
-```py
-txt = """\
-# créer des données
-x = np.random.randn(100)
-y = np.random.randn(100)
-
-# créer un tableau de données à partir de x et y
-"""
-print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
-```
-
-```python out
-# créer des données
-x = np.random.randn(100)
-y = np.random.randn(100)
-
-# créer un tableau de données à partir de x et y
-df = pd.DataFrame({'x': x, 'y': y})
-df.insert(0,'x', x)
-for
-```
-
-Bien, c'est la bonne réponse. Bien qu'il insère ensuite la colonne `x` à nouveau. Comme le nombre de *tokens* générés est limité, la boucle `for` suivante est coupée. Voyons si nous pouvons faire quelque chose d'un peu plus complexe et faire en sorte que le modèle nous aide à utiliser l'opération `groupby` :
-
-```py
-txt = """\
-# tableau de données avec profession, revenu et nom
-df = pd.DataFrame({'profession': x, 'income':y, 'name': z})
-
-# calculer le revenu moyen par profession
-"""
-print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
-```
-
-```python out
-# tableau de données avec profession, revenu et nom
-df = pd.DataFrame({'profession': x, 'income':y, 'name': z})
-
-# calculer le revenu moyen par profession
-profession = df.groupby(['profession']).mean()
-```
-
-Pas mal, c'est la bonne façon de faire. Enfin, voyons si nous pouvons aussi l'utiliser pour `scikit-learn` et utiliser un modèle *Random Forest* :
-
-```py
-txt = """
-# import random forest regressor from scikit-learn
-from sklearn.ensemble import RandomForestRegressor
-
-# entraînement du modèle de forêt aléatoire avec 300 estimateurs sur X, y :
-"""
-print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
-```
-
-```python out
-# import random forest regressor from scikit-learn
-from sklearn.ensemble import RandomForestRegressor
-
-# entraînement du modèle de forêt aléatoire avec 300 estimateurs sur X, y :
-rf = RandomForestRegressor(n_estimators=300, random_state=random_state, max_depth=3)
-rf.fit(X, y)
-rf
-```
-
-{#if fw === 'tf'}
-
-Au vu de ces quelques exemples, il semble que le modèle ait appris une partie de la syntaxe des bibliothèques Python de science des données. Bien sûr, nous devrions évaluer le modèle de manière plus approfondie avant de le déployer dans le monde réel, mais il s'agit tout de même d'un prototype impressionnant.
-
-{:else}
-
-Au vu de ces quelques exemples, il semble que le modèle ait appris une partie de la syntaxe des bibliothèques Python de science des données. Bien sûr, nous devrions évaluer le modèle de manière plus approfondie avant de le déployer dans le monde réel, mais il s'agit tout de même d'un prototype impressionnant. Parfois, il est nécessaire de personnaliser davantage l'entraînement du modèle afin d'obtenir les performances nécessaires pour un cas d'utilisation donné. Par exemple, que se passe-t-il si l'on souhaite mettre à jour dynamiquement la taille du batch ou si l'on dispose d'une boucle d'entraînement conditionnelle qui ignore les mauvais exemples à la volée ? Une option serait de sous-classer le `Trainer` et d'ajouter les changements nécessaires, mais parfois il est plus simple d'écrire la boucle d'entraînement à partir de zéro. C'est là qu'intervient 🤗 *Accelerate*.
-
-{/if}
-
-{#if fw === 'pt'}
-
-## Entraîner avec 🤗 Accelerate
-
-Nous avons vu comment entraîner un modèle avec le `Trainer`, qui permet une certaine personnalisation. Cependant, parfois nous voulons un contrôle total sur la boucle d'entraînement ou nous souhaitons faire quelques changements exotiques. Dans ce cas, 🤗 *Accelerate* est un excellent choix, et dans cette section, nous allons suivre les étapes pour l'utiliser pour entraîner notre modèle. Pour rendre les choses plus intéressantes, nous allons également ajouter une touche à la boucle d'entraînement.
-
-
-
-Puisque nous sommes principalement intéressés par l'autocomplétion pour les bibliothèques de science des données, il est logique de donner plus de poids aux échantillons d'entraînement qui utilisent davantage ces bibliothèques. Nous pouvons facilement identifier ces exemples grâce à l'utilisation de mots-clés tels que `plt`, `pd`, `sk`, `fit`, et `predict`, qui sont les noms d'importation les plus fréquents pour `matplotlib.pyplot`, `pandas`, et `sklearn` ainsi que les fonctions `fit` et `predict` de cette dernière. Si chacun d'entre eux est représenté par un seul *token*, nous pouvons facilement vérifier s'ils apparaissent dans la séquence d'entrée. Les *tokens* peuvent avoir un préfixe d'espacement, donc nous vérifierons aussi ces versions dans le vocabulaire du *tokenizer*. Pour vérifier que cela fonctionne, nous ajouterons un *token* de test qui devrait être divisé en plusieurs *tokens* :
-
-```py
-keytoken_ids = []
-for keyword in [
- "plt",
- "pd",
- "sk",
- "fit",
- "predict",
- " plt",
- " pd",
- " sk",
- " fit",
- " predict",
- "testtest",
-]:
- ids = tokenizer([keyword]).input_ids[0]
- if len(ids) == 1:
- keytoken_ids.append(ids[0])
- else:
- print(f"Keyword has not single token: {keyword}")
-```
-
-```python out
-'Keyword has not single token: testtest'
-```
-
-Super, ça a l'air de bien fonctionner ! Nous pouvons maintenant écrire une fonction de perte personnalisée qui prend la séquence d'entrée, les logits et les *tokens* clés que nous venons de sélectionner comme entrées. Tout d'abord, nous devons aligner les logits et les entrées : la séquence d'entrée décalée d'une unité vers la droite forme les étiquettes, puisque le *token* suivant est l'étiquette du *token* actuel. Nous pouvons y parvenir en commençant les étiquettes à partir du deuxième *token* de la séquence d'entrée, puisque le modèle ne fait pas de prédiction pour le premier *token* de toute façon. Ensuite, nous coupons le dernier logit, car nous n'avons pas d'étiquette pour le *token* qui suit la séquence d'entrée complète. Avec cela, nous pouvons calculer la perte par échantillon et compter les occurrences de tous les mots-clés dans chaque échantillon. Enfin, nous calculons la moyenne pondérée sur tous les échantillons en utilisant les occurrences comme poids. Comme nous ne voulons pas rejeter tous les échantillons qui ne contiennent pas de mots-clés, nous ajoutons 1 aux poids :
-
-```py
-from torch.nn import CrossEntropyLoss
-import torch
-
-
-def keytoken_weighted_loss(inputs, logits, keytoken_ids, alpha=1.0):
- # Décalage pour que tokens < n prédisent n
- shift_labels = inputs[..., 1:].contiguous()
- shift_logits = logits[..., :-1, :].contiguous()
- # Calcul de la perte par token
- loss_fct = CrossEntropyLoss(reduce=False)
- loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
- # Redimensionnement et perte moyenne par échantillon
- loss_per_sample = loss.view(shift_logits.size(0), shift_logits.size(1)).mean(axis=1)
- # Calculer et échelonner la pondération
- weights = torch.stack([(inputs == kt).float() for kt in keytoken_ids]).sum(
- axis=[0, 2]
- )
- weights = alpha * (1.0 + weights)
- # Calculer la moyenne pondérée
- weighted_loss = (loss_per_sample * weights).mean()
- return weighted_loss
-```
-
-Avant de commencer à entraîner avec cette nouvelle fonction de perte géniale, nous devons préparer quelques éléments :
-
-- Nous avons besoin de chargeurs de données pour charger les données par batch.
-- Nous devons définir les paramètres de décroissance des poids.
-- De temps en temps, nous voulons évaluer, il est donc logique d'envelopper le code d'évaluation dans une fonction.
-
-Commençons par les chargeurs de données. Nous avons seulement besoin de définir le format du jeu de données à `"torch"` et ensuite nous pouvons le passer à un PyTorch `DataLoader` avec la taille de batch appropriée :
-
-```py
-from torch.utils.data.dataloader import DataLoader
-
-tokenized_dataset.set_format("torch")
-train_dataloader = DataLoader(tokenized_dataset["train"], batch_size=32, shuffle=True)
-eval_dataloader = DataLoader(tokenized_dataset["valid"], batch_size=32)
-```
-
-Ensuite, nous regroupons les paramètres de façon à ce que l'optimiseur sache lesquels bénéficieront d'une décroissance de poids supplémentaire. Habituellement, tous les termes de biais et les poids de la *LayerNorm* en sont exemptés. Voici comment nous pouvons le faire :
-
-```py
-weight_decay = 0.1
-
-
-def get_grouped_params(model, no_decay=["bias", "LayerNorm.weight"]):
- params_with_wd, params_without_wd = [], []
- for n, p in model.named_parameters():
- if any(nd in n for nd in no_decay):
- params_without_wd.append(p)
- else:
- params_with_wd.append(p)
- return [
- {"params": params_with_wd, "weight_decay": weight_decay},
- {"params": params_without_wd, "weight_decay": 0.0},
- ]
-```
-
-Puisque nous voulons évaluer le modèle régulièrement sur l'ensemble de validation pendant l'entraînement, écrivons une fonction pour cela aussi. Elle passe simplement par le *dataloader* d'évaluation et rassemble toutes les pertes à travers les processus :
-
-```py
-def evaluate():
- model.eval()
- losses = []
- for step, batch in enumerate(eval_dataloader):
- with torch.no_grad():
- outputs = model(batch["input_ids"], labels=batch["input_ids"])
-
- losses.append(accelerator.gather(outputs.loss))
- loss = torch.mean(torch.cat(losses))
- try:
- perplexity = torch.exp(loss)
- except OverflowError:
- perplexity = float("inf")
- return loss.item(), perplexity.item()
-```
-
-Avec la fonction `evaluate()` nous pouvons rapporter la perte et la [perplexité](/course/fr/chapter7/3) à intervalles réguliers. Ensuite, nous redéfinissons notre modèle pour nous assurer que nous entraînons à nouveau à partir de zéro :
-
-```py
-model = GPT2LMHeadModel(config)
-```
-
-Nous pouvons ensuite définir notre optimiseur, en utilisant la fonction précédente pour diviser les paramètres de décroissance des poids :
-
-```py
-from torch.optim import AdamW
-
-optimizer = AdamW(get_grouped_params(model), lr=5e-4)
-```
-
-Préparons maintenant le modèle, l'optimiseur et les chargeurs de données pour pouvoir commencer l'entraînement :
-
-```py
-from accelerate import Accelerator
-
-accelerator = Accelerator(fp16=True)
-
-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 commençant à 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 devons 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
-num_train_epochs = 1
-num_update_steps_per_epoch = len(train_dataloader)
-num_training_steps = num_train_epochs * num_update_steps_per_epoch
-
-lr_scheduler = get_scheduler(
- name="linear",
- optimizer=optimizer,
- num_warmup_steps=1_000,
- 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'identifiant 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 = "codeparrot-ds-accelerate"
-repo_name = get_full_repo_name(model_name)
-repo_name
-```
-
-```python out
-'sgugger/codeparrot-ds-accelerate'
-```
-
-Ensuite, nous pouvons cloner ce dépôt dans un dossier local. S'il existe déjà, ce dossier local doit être un clone existant du dépôt avec lequel nous travaillons :
-
-```py
-output_dir = "codeparrot-ds-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.
-
-Avant de nous entraîner, exécutons un test rapide pour voir si la fonction d'évaluation fonctionne correctement :
-
-```py
-evaluate()
-```
-
-```python out
-(10.934126853942871, 56057.14453125)
-```
-
-Ce sont des valeurs très élevées pour la perte et la perplexité, mais ce n'est pas surprenant puisque nous n'avons pas encore entraîné le modèle. Avec cela, nous avons tout préparé pour écrire la partie principale du script d'entraînement : la boucle d'entraînement. Dans celle-ci, nous itérons sur le chargeur de données et transmettons les batchs au modèle. Avec les logits, nous pouvons alors évaluer notre fonction de perte personnalisée. Nous mettons à l'échelle la perte par le nombre d'étapes d'accumulation du gradient afin de ne pas créer de plus grandes pertes en agrégeant plus d'étapes. Avant de procéder à l'optimisation, nous découpons également les gradients pour une meilleure convergence. Enfin, tous les quelques pas, nous évaluons le modèle sur l'ensemble d'évaluation avec notre nouvelle fonction `evaluate()` :
-
-```py
-from tqdm.notebook import tqdm
-
-gradient_accumulation_steps = 8
-eval_steps = 5_000
-
-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)
- ):
- logits = model(batch["input_ids"]).logits
- loss = keytoken_weighted_loss(batch["input_ids"], logits, keytoken_ids)
- if step % 100 == 0:
- accelerator.print(
- {
- "lr": get_lr(),
- "samples": step * samples_per_step,
- "steps": completed_steps,
- "loss/train": loss.item() * gradient_accumulation_steps,
- }
- )
- loss = loss / gradient_accumulation_steps
- accelerator.backward(loss)
- if step % gradient_accumulation_steps == 0:
- accelerator.clip_grad_norm_(model.parameters(), 1.0)
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- completed_steps += 1
- if (step % (eval_steps * gradient_accumulation_steps)) == 0:
- eval_loss, perplexity = evaluate()
- accelerator.print({"loss/eval": eval_loss, "perplexity": perplexity})
- model.train()
- 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 step {step}", blocking=False
- )
-```
-
-Et voilà, vous disposez maintenant de votre propre boucle d'entraînement personnalisée pour les modèles de langage causal tels que le GPT-2. Vous pouvez encore l'adapter à vos besoins.
-
-
-
-✏️ **Essayez !** Vous pouvez créer votre propre fonction de perte personnalisée, adaptée à votre cas d'utilisation, ou ajouter une autre étape personnalisée dans la boucle d'entraînement.
-
-
-
-
-
-✏️ **Essayez !** Lorsque vous effectuez de longues expériences d'entraînement, il est bon d'enregistrer les mesures importantes à l'aide d'outils tels que *TensorBoard* ou *Weights & Biases*. Ajoutez l'un d'eux à la boucle d'entraînement afin de pouvoir toujours vérifier comment se déroule l'entraînement.
-
-
-
-{/if}
+
+
+# Entraîner un modèle de langage causal à partir de zéro
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Jusqu'à présent, nous avons surtout réutilisé des modèles pré-entraînés et les avons *finetunés* sur de nouveaux cas d'usage. Comme nous l'avons vu dans le [chapitre 1](/course/fr/chapter1), ceci est communément appelé _apprentissage par transfert_, et il s'agit d'une stratégie très efficace pour appliquer les *transformers* à la plupart des applications du monde réel où les données étiquetées sont rares. Dans ce chapitre, nous allons adopter une approche différente consistant à entraîner un modèle complètement nouveau à partir de zéro. C'est une bonne démarche à adopter si vous avez beaucoup de données et qu'elles sont très différentes des données de pré-entraînement utilisées par les modèles disponibles. Cependant, le pré-entraînement d'un modèle de langue nécessite beaucoup plus de ressources informatiques que le simple *finetuning* d'un modèle existant. Parmi les exemples où il peut être utile d'entraîner un nouveau modèle, citons les jeux de données constitués de notes de musique, de séquences moléculaires telles que l'ADN, ou de langages de programmation. Ces derniers ont récemment gagné en popularité grâce à des outils tels que TabNine et Copilot de GitHub (alimentés par le modèle Codex d'OpenAI) qui peuvent générer de longues séquences de code. Cette tâche de génération de texte est mieux abordée avec des modèles de langage autorégressifs ou causaux tels que le GPT-2.
+
+Dans cette section, nous allons construire une version réduite d'un modèle de génération de code Python. Nous nous concentrerons sur la complétion d'une ligne de code au lieu de fonctions ou de classes complètes. Lorsque vous travaillez sur des projets de science des données en Python, vous êtes souvent en contact avec les bibliothèques `matplotlib`, `seaborn`, `pandas` et `scikit-learn`. Lors de l'utilisation de ces *frameworks*, il est fréquent d'avoir besoin de rechercher des commandes spécifiques. Il serait donc bien d'utiliser un modèle pour compléter ces appels pour nous.
+
+
+
+
+Dans le [chapitre 6](/course/fr/chapter6), nous avons créé un *tokenizer* efficace pour traiter du code Python. Nous avons besoin d'un jeu de données à grande échelle pour pré-entraîner un modèle. Ici, nous allons appliquer notre *tokenizer* à un corpus de code Python provenant des dépôts GitHub. Nous utiliserons ensuite l'API `Trainer` et 🤗 *Accelerate* pour entraîner le modèle. C'est parti !
+
+
+
+
+Il s'agit d'une présentation du modèle qui a été entraîné à l'aide du code présenté dans cette section et qui a ensuité été téléchargé sur le *Hub*. Vous pouvez le trouver [ici](https://huggingface.co/huggingface-course/codeparrot-ds?text=plt.imshow%28). Notez qu'étant donné qu'il y a un certains aléat dans la génération du texte, vous obtiendrez probablement un résultat légèrement différent.
+
+## Collecte des données
+
+On peut trouver du code Python en abondance dans les dépôts de code tels que GitHub, que nous pouvons utiliser pour créer un jeu de données en récupérant chaque dépôt Python. C'est l'approche adoptée dans le [livre *Natural Language Processing with Transformers*](https://learning.oreilly.com/library/view/natural-language-processing/9781098103231/) pour pré-entraîner un grand GPT-2. En utilisant un dépôt GitHub d'environ 180 Go contenant approximativement 20 millions de fichiers Python, les auteurs du livre ont construit un jeu de données appelé `codeparrot` qu'ils ont ensuite partagé sur le [*Hub*](https://huggingface.co/datasets/transformersbook/codeparrot).
+
+Cependant, entraîner sur l'ensemble du corpus prend beaucoup de temps et demande beaucoup de ressources de calculs. Dans notre cas, nous n'avons besoin que du sous-ensemble du jeu de données qui est relatif aux codes portant sur la science des données. Commençons donc par filtrer le jeu de données `codeparrot` en ne gardant que les fichiers incluant l'une des bibliothèques de science des données énumérées précédemment. En raison de la taille du jeu de données, nous voulons éviter de le télécharger. Nous utiliserons donc la fonctionnalité de *streaming* de 🤗 *Datasets* afin de le filtrer à la volée. Pour nous aider à filtrer les échantillons de code utilisant les bibliothèques que nous avons mentionnées précédemment, nous utilisons la fonction suivante :
+
+```py
+def any_keyword_in_string(string, keywords):
+ for keyword in keywords:
+ if keyword in string:
+ return True
+ return False
+```
+
+Testons-le sur deux exemples :
+
+```py
+filters = ["pandas", "sklearn", "matplotlib", "seaborn"]
+example_1 = "import numpy as np"
+example_2 = "import pandas as pd"
+
+print(
+ any_keyword_in_string(example_1, filters), any_keyword_in_string(example_2, filters)
+)
+```
+
+```python out
+False True
+```
+
+Nous pouvons l'utiliser pour créer une fonction qui va *streamer* le jeu de donner et filtrer les éléments que nous voulons :
+
+```py
+def filter_streaming_dataset(dataset, filters):
+ filtered_dict = defaultdict(list)
+ total = 0
+ for sample in tqdm(iter(dataset)):
+ total += 1
+ if any_keyword_in_string(sample["content"], filters):
+ for k, v in sample.items():
+ filtered_dict[k].append(v)
+ print(f"{len(filtered_dict['content'])/total:.2%} of data after filtering.")
+ return Dataset.from_dict(filtered_dict)
+```
+
+Ensuite, nous pouvons simplement appliquer cette fonction :
+
+```py
+# Cette cellule prendra beaucoup de temps à s'exécuter, donc vous devriez la sauter et aller à la suivante !
+from datasets import load_dataset
+
+split = "train" # "valid"
+filters = ["pandas", "sklearn", "matplotlib", "seaborn"]
+
+data = load_dataset(f"transformersbook/codeparrot-{split}", split=split, streaming=True)
+filtered_data = filter_streaming_dataset(data, filters)
+```
+
+```python out
+3.26% of data after filtering.
+```
+
+Cela nous laisse avec environ 3 % du jeu de données original, ce qui est tout de même assez important puisqu'il fait 6 Go et se compose de 600 000 scripts Python !
+
+Le filtrage peut prendre de 2 à 3 heures, selon votre machine et votre bande passante. Si vous ne voulez pas passer par ce long processus, nous fournissons sur le *Hub* le jeu de données filtré pour que vous puissiez le télécharger :
+
+```py
+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")
+
+raw_datasets = DatasetDict(
+ {
+ "train": ds_train, # .shuffle().select(range(50000)),
+ "valid": ds_valid, # .shuffle().select(range(500))
+ }
+)
+
+raw_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'],
+ num_rows: 606720
+ })
+ valid: Dataset({
+ features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'],
+ num_rows: 3322
+ })
+})
+```
+
+
+
+Le pré-entraînement du modèle de langue prendra un certain temps. Nous vous suggérons donc d'exécuter d'abord la boucle d'entraînement sur un petit échantillon des données en décommentant les deux lignes dans le code ci-dessus. Assurez-vous alors que l'entraînement se termine avec succès et que les modèles sont stockés. Rien n'est plus frustrant qu'un entraînement qui échoue à la dernière étape car vous avez oublié de créer un dossier ou parce qu'il y a une faute de frappe à la fin de la boucle d'entraînement !
+
+
+
+Examinons un exemple tiré du jeu de données. Nous ne montrerons que les 200 premiers caractères de chaque champ :
+
+```py
+for key in raw_datasets["train"][0]:
+ print(f"{key.upper()}: {raw_datasets['train'][0][key][:200]}")
+```
+
+```python out
+'REPO_NAME: kmike/scikit-learn'
+'PATH: sklearn/utils/__init__.py'
+'COPIES: 3'
+'SIZE: 10094'
+'''CONTENT: """
+The :mod:`sklearn.utils` module includes various utilites.
+"""
+
+from collections import Sequence
+
+import numpy as np
+from scipy.sparse import issparse
+import warnings
+
+from .murmurhash import murm
+LICENSE: bsd-3-clause'''
+```
+
+Nous pouvons voir que le champ `content` contient le code sur lequel nous voulons que notre modèle s'entraîne. Maintenant que nous avons un jeu de données, nous devons préparer les textes afin qu'ils soient dans un format approprié pour le pré-entraînement.
+
+## Préparation du jeu de données
+
+
+
+La première étape est de tokeniser les données afin de pouvoir les utiliser pour l'entraînement. Puisque notre objectif est d'autocompléter de courts appels de fonctions, nous pouvons garder la taille du contexte relativement petite. L'avantage est que nous pouvons entraîner le modèle beaucoup plus rapidement et qu'il nécessite beaucoup moins de mémoire. Si c'est important pour votre application d'avoir davantage de contexte (par exemple, si vous voulez que le modèle écrive des tests unitaires basés sur un fichier avec la définition de la fonction), assurez-vous d'augmenter ce nombre. Gardez néanmoins à l'esprit que cela s'accompagne d'une plus grande empreinte mémoire du GPU. Pour l'instant, fixons la taille du contexte à 128 *tokens*, par opposition aux 1 024 ou 2 048 utilisés respectivement dans le GPT-2 et le GPT-3.
+
+La plupart des documents contiennent beaucoup plus de 128 *tokens*, donc le fait de tronquer les entrées à la longueur maximale éliminerait une grande partie de notre jeu de données. A la place, nous allons utiliser l'option `return_overflowing_tokens` pour tokeniser l'entrée entière et la diviser en plusieurs morceaux, comme nous l'avons fait dans le [chapitre 6](/course/fr/chapter6/4). Nous utiliserons également l'option `return_length` pour retourner automatiquement la longueur de chaque morceau créé. Souvent, le dernier morceau est plus petit que la taille du contexte et nous nous en débarrasserons pour éviter les problèmes de *padding*. Nous n'en avons pas vraiment besoin puisque de toute façon nous avons beaucoup de données.
+
+
+
+
+
+
+Voyons comment cela fonctionne en examinant les deux premiers exemples :
+
+```py
+from transformers import AutoTokenizer
+
+context_length = 128
+tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")
+
+outputs = tokenizer(
+ raw_datasets["train"][:2]["content"],
+ truncation=True,
+ max_length=context_length,
+ return_overflowing_tokens=True,
+ return_length=True,
+)
+
+print(f"Input IDs length: {len(outputs['input_ids'])}")
+print(f"Input chunk lengths: {(outputs['length'])}")
+print(f"Chunk mapping: {outputs['overflow_to_sample_mapping']}")
+```
+
+```python out
+Input IDs length: 34
+Input chunk lengths: [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 117, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 41]
+Chunk mapping: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+```
+
+Nous pouvons voir que nous obtenons 34 morceaux à partir de ces deux exemples. En regardant leurs longueurs, nous pouvons voir qu'ils se terminent avec moins de 128 *tokens* (117 et 41, respectivement). Ils ne représentent qu'une petite fraction du total des morceaux que nous avons (2/34), donc nous pouvons les jeter sans risque. Avec le champ `overflow_to_sample_mapping`, nous pouvons aussi reconstruire quels morceaux appartenaient à quels échantillons d'entrée.
+
+Avec cette opération, nous utilisons une fonctionnalité pratique de la fonction `Dataset.map()` de 🤗 *Datasets*. En effet, celle-ci ne nécessite pas une correspondance un à un comme nous l'avons vu dans la [section 3](/course/fr/chapter7/3). Nous pouvons créer des batchs avec plus ou moins d'éléments que le batch d'entrée. C'est utile lorsque l'on effectue des opérations telles que l'augmentation ou le filtrage des données qui modifient le nombre d'éléments. Dans notre cas, lors de la tokenisation de chaque élément en morceaux de longeur de la taille de contexte spécifiée, nous créons de nombreux échantillons de chaque document. Nous devons juste nous assurer de supprimer les colonnes existantes, car elles ont une taille conflictuelle. Si nous voulions les garder, nous pourrions les répéter de manière appropriée et les retourner dans l'appel `Dataset.map()` :
+
+```py
+def tokenize(element):
+ outputs = tokenizer(
+ element["content"],
+ truncation=True,
+ max_length=context_length,
+ return_overflowing_tokens=True,
+ return_length=True,
+ )
+ input_batch = []
+ for length, input_ids in zip(outputs["length"], outputs["input_ids"]):
+ if length == context_length:
+ input_batch.append(input_ids)
+ return {"input_ids": input_batch}
+
+
+tokenized_datasets = raw_datasets.map(
+ tokenize, batched=True, remove_columns=raw_datasets["train"].column_names
+)
+tokenized_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['input_ids'],
+ num_rows: 16702061
+ })
+ valid: Dataset({
+ features: ['input_ids'],
+ num_rows: 93164
+ })
+})
+```
+
+Nous avons maintenant 16,7 millions d'exemples avec 128 *tokens* chacun, ce qui correspond à environ 2,1 milliards de *tokens* au total. A titre de comparaison, les modèles GPT-3 et Codex d'OpenAI sont entraînés sur 300 et 100 milliards de *tokens*, respectivement. Les modèles Codex étant initialisés à partir des *checkpoints* GPT-3. Notre objectif dans cette section n'est pas de rivaliser avec ces modèles, qui peuvent générer des textes longs et cohérents, mais de créer une version réduite fournissant une fonction d'autocomplétion rapide.
+
+Maintenant que le jeu de données est prêt, configurons le modèle !
+
+
+
+✏️ **Essayez !** Se débarrasser de tous les morceaux qui sont plus petits que la taille du contexte n'était pas un gros problème ici parce que nous utilisons de petites fenêtres de contexte. Si vous augmentez la taille du contexte (ou si vous avez un corpus de documents courts), la fraction des morceaux qui sont jetés augmentera. Une façon plus efficace de préparer les données est de joindre tous les échantillons dans un batch avec un *token* `eos_token_id` entre les deux, puis d'effectuer le découpage sur les séquences concaténées. Comme exercice, modifiez la fonction `tokenize()` pour utiliser cette approche. Notez que vous devrez mettre `truncation=False` et enlever les autres arguments du *tokenizer* pour obtenir la séquence complète des identifiants des *tokens*.
+
+
+
+
+## Initialisation d'un nouveau modèle
+
+Notre première étape consiste à initialiser un GPT-2. Pour notre modèle, nous utiliserons la même configuration que pour le petit modèle GPT-2. Ainsi nous chargeons la configuration pré-entraînée, nous nous assurons que la taille du *tokenizer* correspond à la taille du vocabulaire du modèle et nous passons les identifiants des *tokens* `bos` et `eos` (début et fin de séquence) :
+
+{#if fw === 'pt'}
+
+```py
+from transformers import AutoTokenizer, GPT2LMHeadModel, AutoConfig
+
+config = AutoConfig.from_pretrained(
+ "gpt2",
+ vocab_size=len(tokenizer),
+ n_ctx=context_length,
+ bos_token_id=tokenizer.bos_token_id,
+ eos_token_id=tokenizer.eos_token_id,
+)
+```
+
+Avec cette configuration, nous pouvons charger un nouveau modèle. Notez que c'est la première fois que nous n'utilisons pas la fonction `from_pretrained()` puisque nous initialisons nous-mêmes un modèle :
+
+```py
+model = GPT2LMHeadModel(config)
+model_size = sum(t.numel() for t in model.parameters())
+print(f"GPT-2 size: {model_size/1000**2:.1f}M parameters")
+```
+
+```python out
+GPT-2 size: 124.2M parameters
+```
+
+{:else}
+
+```py
+from transformers import AutoTokenizer, TFGPT2LMHeadModel, AutoConfig
+
+config = AutoConfig.from_pretrained(
+ "gpt2",
+ vocab_size=len(tokenizer),
+ n_ctx=context_length,
+ bos_token_id=tokenizer.bos_token_id,
+ eos_token_id=tokenizer.eos_token_id,
+)
+```
+
+Avec cette configuration, nous pouvons charger un nouveau modèle. Notez que c'est la première fois que nous n'utilisons pas la fonction `from_pretrained()` puisque nous initialisons nous-mêmes un modèle :
+
+```py
+model = TFGPT2LMHeadModel(config)
+model(model.dummy_inputs) # Construit le modèle
+model.summary()
+```
+
+```python out
+_________________________________________________________________
+Layer (type) Output Shape Param #
+=================================================================
+transformer (TFGPT2MainLayer multiple 124242432
+=================================================================
+Total params: 124,242,432
+Trainable params: 124,242,432
+Non-trainable params: 0
+_________________________________________________________________
+```
+
+{/if}
+
+Notre modèle comporte 124 millions de paramètres que nous devrons régler. Avant de commencer l'entraînement, nous devons configurer un collateur de données qui se chargera de créer les batchs. Nous pouvons utiliser le collateur `DataCollatorForLanguageModeling`, qui est conçu spécifiquement pour la modélisation du langage (comme son nom le suggère subtilement). En plus de l'empilage et du rembourrage des batchs, il s'occupe aussi de la création des étiquettes du modèle de langage. Dans la modélisation causale du langage, les entrées servent aussi d'étiquettes (juste décalées d'un élément) et que le collateur de données crée à la volée pendant l'entraînement pour ne pas avoir à dupliquer les `input_ids`.
+
+Notez que `DataCollatorForLanguageModeling` supporte à la fois la modélisation du langage masqué (MLM pour *masked language modeling*) et la modélisation du langage causal (CLM pour *causal language modeling*). Par défaut, il prépare les données pour la MLM mais nous pouvons passer à la CLM en définissant l'argument `mlm=False` :
+
+{#if fw === 'pt'}
+
+```py
+from transformers import DataCollatorForLanguageModeling
+
+tokenizer.pad_token = tokenizer.eos_token
+data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)
+```
+
+{:else}
+
+```py
+from transformers import DataCollatorForLanguageModeling
+
+tokenizer.pad_token = tokenizer.eos_token
+data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False, return_tensors="tf")
+```
+
+{/if}
+
+Prenons un exemple :
+
+```py
+out = data_collator([tokenized_dataset["train"][i] for i in range(5)])
+for key in out:
+ print(f"{key} shape: {out[key].shape}")
+```
+
+{#if fw === 'pt'}
+
+```python out
+input_ids shape: torch.Size([5, 128])
+attention_mask shape: torch.Size([5, 128])
+labels shape: torch.Size([5, 128])
+```
+
+{:else}
+
+```python out
+input_ids shape: (5, 128)
+attention_mask shape: (5, 128)
+labels shape: (5, 128)
+```
+
+{/if}
+
+Nous pouvons voir que les exemples ont été empilés et que tous les tenseurs ont la même forme.
+
+{#if fw === 'tf'}
+
+Maintenant nous pouvons utiliser la méthode `to_tf_dataset()` pour convertir nos jeux de données en jeux de données TensorFlow avec le collateur de données que nous avons créé ci-dessus :
+
+```python
+tf_train_dataset = tokenized_dataset["train"].to_tf_dataset(
+ columns=["input_ids", "attention_mask", "labels"],
+ collate_fn=data_collator,
+ shuffle=True,
+ batch_size=32,
+)
+tf_eval_dataset = tokenized_dataset["valid"].to_tf_dataset(
+ columns=["input_ids", "attention_mask", "labels"],
+ collate_fn=data_collator,
+ shuffle=False,
+ batch_size=32,
+)
+```
+
+{/if}
+
+
+
+⚠️ Le déplacement des entrées et des étiquettes pour les aligner se fait à l'intérieur du modèle, de sorte que le collecteur de données ne fait que copier les entrées pour créer les étiquettes.
+
+
+
+
+Nous avons maintenant tout ce qu'il faut pour entraîner notre modèle. Ce n'était pas si compliqué ! Avant de commencer l'entraînement, nous devons nous connecter à Hugging Face. Si vous travaillez dans un *notebook*, vous pouvez le faire avec la fonction utilitaire suivante :
+
+```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 === 'pt'}
+
+Tout ce qu'il reste à faire est de configurer les arguments d'entraînement et de lancer la fonction `Trainer`. Nous utiliserons un programme de taux d'apprentissage de type cosinus avec un réchauffement et une taille de batch de 256 (`per_device_train_batch_size` x `gradient_accumulation_steps`). L'accumulation du gradient est utilisée lorsqu'un seul batch ne tient pas en mémoire, et construit le gradient de manière incrémentale à travers plusieurs passages en avant/en arrière. Nous verrons cela en action lorsque nous créerons la boucle d'entraînement avec 🤗 *Accelerate*.
+
+```py
+from transformers import Trainer, TrainingArguments
+
+args = TrainingArguments(
+ output_dir="codeparrot-ds",
+ per_device_train_batch_size=32,
+ per_device_eval_batch_size=32,
+ evaluation_strategy="steps",
+ eval_steps=5_000,
+ logging_steps=5_000,
+ gradient_accumulation_steps=8,
+ num_train_epochs=1,
+ weight_decay=0.1,
+ warmup_steps=1_000,
+ lr_scheduler_type="cosine",
+ learning_rate=5e-4,
+ save_steps=5_000,
+ fp16=True,
+ push_to_hub=True,
+)
+
+trainer = Trainer(
+ model=model,
+ tokenizer=tokenizer,
+ args=args,
+ data_collator=data_collator,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["valid"],
+)
+```
+
+Maintenant, nous pouvons simplement lancer le `Trainer` et attendre que l'entraînement se termine. Selon que vous l'exécutez sur la totalité ou sur un sous-ensemble de l'échantillon d'entraînement, cela prendra respectivement 20 ou 2 heures. Alors prenez quelques cafés et un bon livre à lire !
+
+```py
+trainer.train()
+```
+
+Une fois l'entraînement terminé, nous pouvons pousser le modèle et le *tokenizer* vers le *Hub* :
+
+```py
+trainer.push_to_hub()
+```
+
+{:else}
+
+Tout ce qu'il reste à faire est de configurer les hyperparamètres d'entraînement et d'appeler `compile()` et `fit()`. Nous utiliserons un programme de taux d'apprentissage avec un réchauffement pour améliorer la stabilité de l'entraînement :
+
+```py
+from transformers import create_optimizer
+import tensorflow as tf
+
+num_train_steps = len(tf_train_dataset)
+optimizer, schedule = create_optimizer(
+ init_lr=5e-5,
+ num_warmup_steps=1_000,
+ 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")
+```
+
+Maintenant, nous pouvons simplement appeler `model.fit()` et attendre que l'entraînement se termine. Selon que vous l'exécutez sur la totalité ou sur un sous-ensemble de l'échantillon d'entraînement, cela prendra respectivement 20 ou 2 heures. Alors prenez quelques cafés et un bon livre à lire ! Une fois l'entraînement terminé, nous pouvons pousser le modèle et le *tokenizer* vers le *Hub* :
+
+```py
+from transformers.keras_callbacks import PushToHubCallback
+
+callback = PushToHubCallback(output_dir="codeparrot-ds", tokenizer=tokenizer)
+
+model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback])
+```
+
+{/if}
+
+
+
+✏️ **Essayez !** Il ne nous a fallu qu'une trentaine de lignes de code en plus des `TrainingArguments` pour passer des textes bruts à l'entraînement du GPT-2. Essayez-le avec votre propre jeu de données et voyez si vous pouvez obtenir de bons résultats !
+
+
+
+
+
+{#if fw === 'pt'}
+
+💡 Si vous avez accès à une machine avec plusieurs GPUs, essayez d'y exécuter le code. `Trainer` gère automatiquement plusieurs machines ce qui peut accélérer considérablement l'entraînement.
+
+{:else}
+
+💡 Si vous avez accès à une machine avec plusieurs GPUs, vous pouvez essayer d'utiliser `MirroredStrategy` pour accélérer considérablement l'entraînement. Vous devrez créer un objet `tf.distribute.MirroredStrategy` et vous assurer que les commandes `to_tf_dataset` ainsi que la création du modèle et l'appel à `fit()` sont tous exécutés dans `scope()`. Vous pouvez consulter la documentation à ce sujet [ici](https://www.tensorflow.org/guide/distributed_training#use_tfdistributestrategy_with_keras_modelfit).
+
+{/if}
+
+
+
+## Génération de code avec le pipeline
+
+C'est maintenant le moment de vérité : voyons comment le modèle entraîné fonctionne réellement ! Nous pouvons voir dans les logs que la perte a diminué régulièrement, mais pour mettre le modèle à l'épreuve, regardons comment il fonctionne sur certains messages. Pour ce faire, nous allons envelopper le modèle dans un `pipeline` de génération de texte et, s'il y en a un de disponible, utiliser un GPU pour avoir des générations rapidement :
+
+{#if fw === 'pt'}
+
+```py
+import torch
+from transformers import pipeline
+
+device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+pipe = pipeline(
+ "text-generation", model="huggingface-course/codeparrot-ds", device=device
+)
+```
+
+{:else}
+
+```py
+from transformers import pipeline
+
+course_model = TFGPT2LMHeadModel.from_pretrained("huggingface-course/codeparrot-ds")
+course_tokenizer = AutoTokenizer.from_pretrained("huggingface-course/codeparrot-ds")
+pipe = pipeline(
+ "text-generation", model=course_model, tokenizer=course_tokenizer, device=0
+)
+```
+
+{/if}
+
+Let's start with the simple task of creating a scatter plot:
+
+```py
+txt = """\
+# créer des données
+x = np.random.randn(100)
+y = np.random.randn(100)
+
+# créer un nuage de points avec x, y
+"""
+print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
+```
+
+```python out
+# créer des données
+x = np.random.randn(100)
+y = np.random.randn(100)
+
+# créer un nuage de points avec x, y
+plt.scatter(x, y)
+```
+
+Le résultat semble correct. Est-ce que cela fonctionne aussi pour une opération `pandas` ? Voyons si nous pouvons créer un `DataFrame` à partir de deux tableaux :
+
+```py
+txt = """\
+# créer des données
+x = np.random.randn(100)
+y = np.random.randn(100)
+
+# créer un tableau de données à partir de x et y
+"""
+print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
+```
+
+```python out
+# créer des données
+x = np.random.randn(100)
+y = np.random.randn(100)
+
+# créer un tableau de données à partir de x et y
+df = pd.DataFrame({'x': x, 'y': y})
+df.insert(0,'x', x)
+for
+```
+
+Bien, c'est la bonne réponse. Bien qu'il insère ensuite la colonne `x` à nouveau. Comme le nombre de *tokens* générés est limité, la boucle `for` suivante est coupée. Voyons si nous pouvons faire quelque chose d'un peu plus complexe et faire en sorte que le modèle nous aide à utiliser l'opération `groupby` :
+
+```py
+txt = """\
+# tableau de données avec profession, revenu et nom
+df = pd.DataFrame({'profession': x, 'income':y, 'name': z})
+
+# calculer le revenu moyen par profession
+"""
+print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
+```
+
+```python out
+# tableau de données avec profession, revenu et nom
+df = pd.DataFrame({'profession': x, 'income':y, 'name': z})
+
+# calculer le revenu moyen par profession
+profession = df.groupby(['profession']).mean()
+```
+
+Pas mal, c'est la bonne façon de faire. Enfin, voyons si nous pouvons aussi l'utiliser pour `scikit-learn` et utiliser un modèle *Random Forest* :
+
+```py
+txt = """
+# import random forest regressor from scikit-learn
+from sklearn.ensemble import RandomForestRegressor
+
+# entraînement du modèle de forêt aléatoire avec 300 estimateurs sur X, y :
+"""
+print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
+```
+
+```python out
+# import random forest regressor from scikit-learn
+from sklearn.ensemble import RandomForestRegressor
+
+# entraînement du modèle de forêt aléatoire avec 300 estimateurs sur X, y :
+rf = RandomForestRegressor(n_estimators=300, random_state=random_state, max_depth=3)
+rf.fit(X, y)
+rf
+```
+
+{#if fw === 'tf'}
+
+Au vu de ces quelques exemples, il semble que le modèle ait appris une partie de la syntaxe des bibliothèques Python de science des données. Bien sûr, nous devrions évaluer le modèle de manière plus approfondie avant de le déployer dans le monde réel, mais il s'agit tout de même d'un prototype impressionnant.
+
+{:else}
+
+Au vu de ces quelques exemples, il semble que le modèle ait appris une partie de la syntaxe des bibliothèques Python de science des données. Bien sûr, nous devrions évaluer le modèle de manière plus approfondie avant de le déployer dans le monde réel, mais il s'agit tout de même d'un prototype impressionnant. Parfois, il est nécessaire de personnaliser davantage l'entraînement du modèle afin d'obtenir les performances nécessaires pour un cas d'utilisation donné. Par exemple, que se passe-t-il si l'on souhaite mettre à jour dynamiquement la taille du batch ou si l'on dispose d'une boucle d'entraînement conditionnelle qui ignore les mauvais exemples à la volée ? Une option serait de sous-classer le `Trainer` et d'ajouter les changements nécessaires, mais parfois il est plus simple d'écrire la boucle d'entraînement à partir de zéro. C'est là qu'intervient 🤗 *Accelerate*.
+
+{/if}
+
+{#if fw === 'pt'}
+
+## Entraîner avec 🤗 Accelerate
+
+Nous avons vu comment entraîner un modèle avec le `Trainer`, qui permet une certaine personnalisation. Cependant, parfois nous voulons un contrôle total sur la boucle d'entraînement ou nous souhaitons faire quelques changements exotiques. Dans ce cas, 🤗 *Accelerate* est un excellent choix, et dans cette section, nous allons suivre les étapes pour l'utiliser pour entraîner notre modèle. Pour rendre les choses plus intéressantes, nous allons également ajouter une touche à la boucle d'entraînement.
+
+
+
+Puisque nous sommes principalement intéressés par l'autocomplétion pour les bibliothèques de science des données, il est logique de donner plus de poids aux échantillons d'entraînement qui utilisent davantage ces bibliothèques. Nous pouvons facilement identifier ces exemples grâce à l'utilisation de mots-clés tels que `plt`, `pd`, `sk`, `fit`, et `predict`, qui sont les noms d'importation les plus fréquents pour `matplotlib.pyplot`, `pandas`, et `sklearn` ainsi que les fonctions `fit` et `predict` de cette dernière. Si chacun d'entre eux est représenté par un seul *token*, nous pouvons facilement vérifier s'ils apparaissent dans la séquence d'entrée. Les *tokens* peuvent avoir un préfixe d'espacement, donc nous vérifierons aussi ces versions dans le vocabulaire du *tokenizer*. Pour vérifier que cela fonctionne, nous ajouterons un *token* de test qui devrait être divisé en plusieurs *tokens* :
+
+```py
+keytoken_ids = []
+for keyword in [
+ "plt",
+ "pd",
+ "sk",
+ "fit",
+ "predict",
+ " plt",
+ " pd",
+ " sk",
+ " fit",
+ " predict",
+ "testtest",
+]:
+ ids = tokenizer([keyword]).input_ids[0]
+ if len(ids) == 1:
+ keytoken_ids.append(ids[0])
+ else:
+ print(f"Keyword has not single token: {keyword}")
+```
+
+```python out
+'Keyword has not single token: testtest'
+```
+
+Super, ça a l'air de bien fonctionner ! Nous pouvons maintenant écrire une fonction de perte personnalisée qui prend la séquence d'entrée, les logits et les *tokens* clés que nous venons de sélectionner comme entrées. Tout d'abord, nous devons aligner les logits et les entrées : la séquence d'entrée décalée d'une unité vers la droite forme les étiquettes, puisque le *token* suivant est l'étiquette du *token* actuel. Nous pouvons y parvenir en commençant les étiquettes à partir du deuxième *token* de la séquence d'entrée, puisque le modèle ne fait pas de prédiction pour le premier *token* de toute façon. Ensuite, nous coupons le dernier logit, car nous n'avons pas d'étiquette pour le *token* qui suit la séquence d'entrée complète. Avec cela, nous pouvons calculer la perte par échantillon et compter les occurrences de tous les mots-clés dans chaque échantillon. Enfin, nous calculons la moyenne pondérée sur tous les échantillons en utilisant les occurrences comme poids. Comme nous ne voulons pas rejeter tous les échantillons qui ne contiennent pas de mots-clés, nous ajoutons 1 aux poids :
+
+```py
+from torch.nn import CrossEntropyLoss
+import torch
+
+
+def keytoken_weighted_loss(inputs, logits, keytoken_ids, alpha=1.0):
+ # Décalage pour que tokens < n prédisent n
+ shift_labels = inputs[..., 1:].contiguous()
+ shift_logits = logits[..., :-1, :].contiguous()
+ # Calcul de la perte par token
+ loss_fct = CrossEntropyLoss(reduce=False)
+ loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
+ # Redimensionnement et perte moyenne par échantillon
+ loss_per_sample = loss.view(shift_logits.size(0), shift_logits.size(1)).mean(axis=1)
+ # Calculer et échelonner la pondération
+ weights = torch.stack([(inputs == kt).float() for kt in keytoken_ids]).sum(
+ axis=[0, 2]
+ )
+ weights = alpha * (1.0 + weights)
+ # Calculer la moyenne pondérée
+ weighted_loss = (loss_per_sample * weights).mean()
+ return weighted_loss
+```
+
+Avant de commencer à entraîner avec cette nouvelle fonction de perte géniale, nous devons préparer quelques éléments :
+
+- Nous avons besoin de chargeurs de données pour charger les données par batch.
+- Nous devons définir les paramètres de décroissance des poids.
+- De temps en temps, nous voulons évaluer, il est donc logique d'envelopper le code d'évaluation dans une fonction.
+
+Commençons par les chargeurs de données. Nous avons seulement besoin de définir le format du jeu de données à `"torch"` et ensuite nous pouvons le passer à un PyTorch `DataLoader` avec la taille de batch appropriée :
+
+```py
+from torch.utils.data.dataloader import DataLoader
+
+tokenized_dataset.set_format("torch")
+train_dataloader = DataLoader(tokenized_dataset["train"], batch_size=32, shuffle=True)
+eval_dataloader = DataLoader(tokenized_dataset["valid"], batch_size=32)
+```
+
+Ensuite, nous regroupons les paramètres de façon à ce que l'optimiseur sache lesquels bénéficieront d'une décroissance de poids supplémentaire. Habituellement, tous les termes de biais et les poids de la *LayerNorm* en sont exemptés. Voici comment nous pouvons le faire :
+
+```py
+weight_decay = 0.1
+
+
+def get_grouped_params(model, no_decay=["bias", "LayerNorm.weight"]):
+ params_with_wd, params_without_wd = [], []
+ for n, p in model.named_parameters():
+ if any(nd in n for nd in no_decay):
+ params_without_wd.append(p)
+ else:
+ params_with_wd.append(p)
+ return [
+ {"params": params_with_wd, "weight_decay": weight_decay},
+ {"params": params_without_wd, "weight_decay": 0.0},
+ ]
+```
+
+Puisque nous voulons évaluer le modèle régulièrement sur l'ensemble de validation pendant l'entraînement, écrivons une fonction pour cela aussi. Elle passe simplement par le *dataloader* d'évaluation et rassemble toutes les pertes à travers les processus :
+
+```py
+def evaluate():
+ model.eval()
+ losses = []
+ for step, batch in enumerate(eval_dataloader):
+ with torch.no_grad():
+ outputs = model(batch["input_ids"], labels=batch["input_ids"])
+
+ losses.append(accelerator.gather(outputs.loss))
+ loss = torch.mean(torch.cat(losses))
+ try:
+ perplexity = torch.exp(loss)
+ except OverflowError:
+ perplexity = float("inf")
+ return loss.item(), perplexity.item()
+```
+
+Avec la fonction `evaluate()` nous pouvons rapporter la perte et la [perplexité](/course/fr/chapter7/3) à intervalles réguliers. Ensuite, nous redéfinissons notre modèle pour nous assurer que nous entraînons à nouveau à partir de zéro :
+
+```py
+model = GPT2LMHeadModel(config)
+```
+
+Nous pouvons ensuite définir notre optimiseur, en utilisant la fonction précédente pour diviser les paramètres de décroissance des poids :
+
+```py
+from torch.optim import AdamW
+
+optimizer = AdamW(get_grouped_params(model), lr=5e-4)
+```
+
+Préparons maintenant le modèle, l'optimiseur et les chargeurs de données pour pouvoir commencer l'entraînement :
+
+```py
+from accelerate import Accelerator
+
+accelerator = Accelerator(fp16=True)
+
+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 commençant à 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 devons 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
+num_train_epochs = 1
+num_update_steps_per_epoch = len(train_dataloader)
+num_training_steps = num_train_epochs * num_update_steps_per_epoch
+
+lr_scheduler = get_scheduler(
+ name="linear",
+ optimizer=optimizer,
+ num_warmup_steps=1_000,
+ 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'identifiant 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 = "codeparrot-ds-accelerate"
+repo_name = get_full_repo_name(model_name)
+repo_name
+```
+
+```python out
+'sgugger/codeparrot-ds-accelerate'
+```
+
+Ensuite, nous pouvons cloner ce dépôt dans un dossier local. S'il existe déjà, ce dossier local doit être un clone existant du dépôt avec lequel nous travaillons :
+
+```py
+output_dir = "codeparrot-ds-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.
+
+Avant de nous entraîner, exécutons un test rapide pour voir si la fonction d'évaluation fonctionne correctement :
+
+```py
+evaluate()
+```
+
+```python out
+(10.934126853942871, 56057.14453125)
+```
+
+Ce sont des valeurs très élevées pour la perte et la perplexité, mais ce n'est pas surprenant puisque nous n'avons pas encore entraîné le modèle. Avec cela, nous avons tout préparé pour écrire la partie principale du script d'entraînement : la boucle d'entraînement. Dans celle-ci, nous itérons sur le chargeur de données et transmettons les batchs au modèle. Avec les logits, nous pouvons alors évaluer notre fonction de perte personnalisée. Nous mettons à l'échelle la perte par le nombre d'étapes d'accumulation du gradient afin de ne pas créer de plus grandes pertes en agrégeant plus d'étapes. Avant de procéder à l'optimisation, nous découpons également les gradients pour une meilleure convergence. Enfin, tous les quelques pas, nous évaluons le modèle sur l'ensemble d'évaluation avec notre nouvelle fonction `evaluate()` :
+
+```py
+from tqdm.notebook import tqdm
+
+gradient_accumulation_steps = 8
+eval_steps = 5_000
+
+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)
+ ):
+ logits = model(batch["input_ids"]).logits
+ loss = keytoken_weighted_loss(batch["input_ids"], logits, keytoken_ids)
+ if step % 100 == 0:
+ accelerator.print(
+ {
+ "lr": get_lr(),
+ "samples": step * samples_per_step,
+ "steps": completed_steps,
+ "loss/train": loss.item() * gradient_accumulation_steps,
+ }
+ )
+ loss = loss / gradient_accumulation_steps
+ accelerator.backward(loss)
+ if step % gradient_accumulation_steps == 0:
+ accelerator.clip_grad_norm_(model.parameters(), 1.0)
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ completed_steps += 1
+ if (step % (eval_steps * gradient_accumulation_steps)) == 0:
+ eval_loss, perplexity = evaluate()
+ accelerator.print({"loss/eval": eval_loss, "perplexity": perplexity})
+ model.train()
+ 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 step {step}", blocking=False
+ )
+```
+
+Et voilà, vous disposez maintenant de votre propre boucle d'entraînement personnalisée pour les modèles de langage causal tels que le GPT-2. Vous pouvez encore l'adapter à vos besoins.
+
+
+
+✏️ **Essayez !** Vous pouvez créer votre propre fonction de perte personnalisée, adaptée à votre cas d'utilisation, ou ajouter une autre étape personnalisée dans la boucle d'entraînement.
+
+
+
+
+
+✏️ **Essayez !** Lorsque vous effectuez de longues expériences d'entraînement, il est bon d'enregistrer les mesures importantes à l'aide d'outils tels que *TensorBoard* ou *Weights & Biases*. Ajoutez l'un d'eux à la boucle d'entraînement afin de pouvoir toujours vérifier comment se déroule l'entraînement.
+
+
+
+{/if}
diff --git a/chapters/fr/chapter7/7.mdx b/chapters/fr/chapter7/7.mdx
index b703523bd..ab5fd9f75 100644
--- a/chapters/fr/chapter7/7.mdx
+++ b/chapters/fr/chapter7/7.mdx
@@ -1,1230 +1,1230 @@
-
-
-# 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 « d'étendue de texte » dans le document lui-même.
-
-
-
-Nous allons *finetuner* 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 celui-ci :
-
-
-
-
-Il s'agit d'une présentation du modèle qui a été entraîné à l'aide du code présenté dans cette section et qui a ensuité été téléchargé sur le *Hub*. Vous pouvez le trouver [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 basé que sur l'encodeur 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 le 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](/course/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 version 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 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`. Affichons-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é pour nous par une métrique de 🤗 *Datasets*. 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. Par exemple, si nous regardons l'échantillon de l'indice 2 :
-
-```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 est de générer des étiquettes pour la réponse à la question, c'est-à-dire 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, à l'aide d'un *tokenizer*, nous devons convertir le texte d'entrée en identifiants que le modèle peut comprendre :
-
-```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 un *tokenizer* rapide dans [ce 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
-```
-
-Nous pouvons transmettre à notre *tokenizer* la question et le contexte ensemble. 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 avec la légende " Venite Ad Me Omnes ". 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 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 sont l'index des *tokens* de début et de fin de la réponse. Le modèle sera chargé de prédire dans l'entrée un logit de début et de fin par *token*, les étiquettes théoriques étant les suivantes :
-
-
-
-
-
-
-Dans ce cas, le contexte n'est pas trop long, mais certains des exemples du jeu 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é le pipeline de `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 et avec une fenêtre glissante entre eux.
-
-Pour voir comment cela fonctionne sur notre exemple, 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 ? [SEP] 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 ? [SEP] 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 la 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.
-
-Le jeu 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 correspondances *offset* que nous avons étudiés au [chapitre 6](/course/fr/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 identifiants d'entrée, les *tokens* de type identifiant, le masque d'attention, ainsi que la correspondance *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 en 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` :
-
-```py
-inputs["overflow_to_sample_mapping"]
-```
-
-```python out
-[0, 0, 0, 0]
-```
-
-Mais si nous tokenisons davantage 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 identifiants d'entrée) au début de la réponse et `end_position` étant l'index du *token* (dans les identifiants 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 identifiants d'entrée. Nous pourrions utiliser les *tokens* de type identifiants 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 morceau de 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:
- # Sinon, ce sont les positions de début et de fin du token
- 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. Comparons alors 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'
-```
-
-Cela correspond ! Maintenant vérifions l'index 4, où nous avons mis les étiquettes à `(0, 0)`, signifiant que la réponse n'est pas dans le morceau 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:
- # Sinon, ce sont les positions de début et de fin du token
- 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 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 du jeu 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 elle ne nous aidera pas vraiment à comprendre la qualité du modèle). Le réel plaisir sera d'interpréter les prédictions du modèle dans des étendues du contexte original. Pour cela, il nous suffit de stocker les correspondances d'*offset* et un moyen de faire correspondre chaque caractéristique créée à l'exemple original dont elle provient. Puisqu'il y a une colonne identifiant dans le jeu de données original, nous l'utiliserons.
-
-La seule chose que nous allons ajouter ici est un petit nettoyage des correspondances d'*offset*. Elles contiendront les *offsets* pour la question et le contexte, mais une fois que nous serons à la phase de post-traitement, nous n'aurons aucun moyen de savoir quelle partie des identifiants 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 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 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 mais le calcul de la métrique avec la fonction `compute_metrics()` sera un défi unique. Puisque nous avons rembourré tous les échantillons à la longueur maximale que nous avons définie, il n'y a pas de collateur de données à définir. Ainsi 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 étendues 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 de la métrique sera un défi unique. Puisque nous avons rembourré tous les échantillons à la longueur maximale que nous avons définie, il n'y a pas de collateur de données à définir. Ainsi 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 étendues 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 identifiants d'entrée, comme nous l'avons vu lors de notre exploration du pipeline de `question-answering` [au chapitre 6](/course/fr/chapter6/3b). L'étape de post-traitement sera similaire à ce que nous avons fait à ce chapitre là. Voici un rapide rappel 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 une fonction 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 de la SoftMax. Pour aller plus vite, nous ne donnerons pas non plus un score à toutes les paires `(start_token, end_token)` possibles, mais seulement celles correspondant aux `n_best` logits les plus élevés (avec `n_best=20`). Puisque nous sautons la SoftMax, les scores seront des scores logi, 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 de `question-answering` 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 car elle repose sur la constante globale `tokenizer`, nous devons juste changer cet objet par 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 batch avec tout de ce petit ensemble de validation et 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 `Trainer` nous donne 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, nous pouvons vraiment nous mettre au travail en bouclant 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_best` 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
- # 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
-
- 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 la charger à l'aide de la bibliothèque 🤗 *Evaluate* :
-
-```python
-import evaluate
-
-metric = evaluate.load("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'identifiant 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'identifiant 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, d'après [le papier](https://arxiv.org/abs/1910.01108v2) de DistilBERT, *finetuné* sur SQuAD, ce modèle obtient 79,1 et 86,9 pour ces scores sur l'ensemble du jeu de 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 étiquettes. 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. Ainsi nous ne serons pas en mesure d'utiliser cette fonction pour obtenir des résultats d'évaluation standards 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 *finetuner* notre modèle.
-
-### Finetuning du modèle
-
-{#if fw === 'pt'}
-
-Nous sommes maintenant prêts à entraîner notre modèle. Créons-le 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 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 standard à 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 cela dans le paragraphe « Une boucle d'entraînement personnalisée » ci-dessous.
-
-C'est 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 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 d'entraînement, un taux de décroissance des 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 *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 jeux de données TensorFlow. 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. Notez que le jeu de données tf_train_dataset est ici un tf.data.Dataset,
-# et non le jeu de données original 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 à 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 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 indiqués dans l'article de BERT pour ce tâche 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 carte de 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 !
-
-
-
-✏️ **A votre tour** Essayez un autre modèle pour voir s'il est plus performant pour 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 celui 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 le *finetuning* précédent et que nous repartons du modèle BERT pré-entraîné :
-
-```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 le taux de décroissance des poids est appliqué :
-
-```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 entraîner sur des TPUs dans un *notebook* 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 l'argument `fp16=True` à `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 *Hub*, si vous n'êtes pas déjà connecté. Nous déterminerons le nom du dépôt à partir de l'identifiant 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 dépôt dans un dossier local. S'il existe déjà, ce dossier local doit être un clone du dépôt 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 car `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()` car 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 `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
-
-# Remplacez par votre propre 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 « d'étendue de texte » dans le document lui-même.
+
+
+
+Nous allons *finetuner* 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 celui-ci :
+
+
+
+
+Il s'agit d'une présentation du modèle qui a été entraîné à l'aide du code présenté dans cette section et qui a ensuité été téléchargé sur le *Hub*. Vous pouvez le trouver [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 basé que sur l'encodeur 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 le 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](/course/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 version 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 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`. Affichons-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é pour nous par une métrique de 🤗 *Datasets*. 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. Par exemple, si nous regardons l'échantillon de l'indice 2 :
+
+```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 est de générer des étiquettes pour la réponse à la question, c'est-à-dire 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, à l'aide d'un *tokenizer*, nous devons convertir le texte d'entrée en identifiants que le modèle peut comprendre :
+
+```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 un *tokenizer* rapide dans [ce 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
+```
+
+Nous pouvons transmettre à notre *tokenizer* la question et le contexte ensemble. 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 avec la légende " Venite Ad Me Omnes ". 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 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 sont l'index des *tokens* de début et de fin de la réponse. Le modèle sera chargé de prédire dans l'entrée un logit de début et de fin par *token*, les étiquettes théoriques étant les suivantes :
+
+
+
+
+
+
+Dans ce cas, le contexte n'est pas trop long, mais certains des exemples du jeu 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é le pipeline de `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 et avec une fenêtre glissante entre eux.
+
+Pour voir comment cela fonctionne sur notre exemple, 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 ? [SEP] 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 ? [SEP] 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 la 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.
+
+Le jeu 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 correspondances *offset* que nous avons étudiés au [chapitre 6](/course/fr/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 identifiants d'entrée, les *tokens* de type identifiant, le masque d'attention, ainsi que la correspondance *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 en 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` :
+
+```py
+inputs["overflow_to_sample_mapping"]
+```
+
+```python out
+[0, 0, 0, 0]
+```
+
+Mais si nous tokenisons davantage 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 identifiants d'entrée) au début de la réponse et `end_position` étant l'index du *token* (dans les identifiants 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 identifiants d'entrée. Nous pourrions utiliser les *tokens* de type identifiants 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 morceau de 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:
+ # Sinon, ce sont les positions de début et de fin du token
+ 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. Comparons alors 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'
+```
+
+Cela correspond ! Maintenant vérifions l'index 4, où nous avons mis les étiquettes à `(0, 0)`, signifiant que la réponse n'est pas dans le morceau 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:
+ # Sinon, ce sont les positions de début et de fin du token
+ 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 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 du jeu 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 elle ne nous aidera pas vraiment à comprendre la qualité du modèle). Le réel plaisir sera d'interpréter les prédictions du modèle dans des étendues du contexte original. Pour cela, il nous suffit de stocker les correspondances d'*offset* et un moyen de faire correspondre chaque caractéristique créée à l'exemple original dont elle provient. Puisqu'il y a une colonne identifiant dans le jeu de données original, nous l'utiliserons.
+
+La seule chose que nous allons ajouter ici est un petit nettoyage des correspondances d'*offset*. Elles contiendront les *offsets* pour la question et le contexte, mais une fois que nous serons à la phase de post-traitement, nous n'aurons aucun moyen de savoir quelle partie des identifiants 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 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 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 mais le calcul de la métrique avec la fonction `compute_metrics()` sera un défi unique. Puisque nous avons rembourré tous les échantillons à la longueur maximale que nous avons définie, il n'y a pas de collateur de données à définir. Ainsi 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 étendues 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 de la métrique sera un défi unique. Puisque nous avons rembourré tous les échantillons à la longueur maximale que nous avons définie, il n'y a pas de collateur de données à définir. Ainsi 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 étendues 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 identifiants d'entrée, comme nous l'avons vu lors de notre exploration du pipeline de `question-answering` [au chapitre 6](/course/fr/chapter6/3b). L'étape de post-traitement sera similaire à ce que nous avons fait à ce chapitre là. Voici un rapide rappel 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 une fonction 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 de la SoftMax. Pour aller plus vite, nous ne donnerons pas non plus un score à toutes les paires `(start_token, end_token)` possibles, mais seulement celles correspondant aux `n_best` logits les plus élevés (avec `n_best=20`). Puisque nous sautons la SoftMax, les scores seront des scores logi, 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 de `question-answering` 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 car elle repose sur la constante globale `tokenizer`, nous devons juste changer cet objet par 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 batch avec tout de ce petit ensemble de validation et 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 `Trainer` nous donne 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, nous pouvons vraiment nous mettre au travail en bouclant 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_best` 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
+ # 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
+
+ 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 la charger à l'aide de la bibliothèque 🤗 *Evaluate* :
+
+```python
+import evaluate
+
+metric = evaluate.load("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'identifiant 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'identifiant 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, d'après [le papier](https://arxiv.org/abs/1910.01108v2) de DistilBERT, *finetuné* sur SQuAD, ce modèle obtient 79,1 et 86,9 pour ces scores sur l'ensemble du jeu de 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 étiquettes. 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. Ainsi nous ne serons pas en mesure d'utiliser cette fonction pour obtenir des résultats d'évaluation standards 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 *finetuner* notre modèle.
+
+### Finetuning du modèle
+
+{#if fw === 'pt'}
+
+Nous sommes maintenant prêts à entraîner notre modèle. Créons-le 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 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 standard à 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 cela dans le paragraphe « Une boucle d'entraînement personnalisée » ci-dessous.
+
+C'est 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 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 d'entraînement, un taux de décroissance des 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 *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 jeux de données TensorFlow. 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. Notez que le jeu de données tf_train_dataset est ici un tf.data.Dataset,
+# et non le jeu de données original 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 à 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 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 indiqués dans l'article de BERT pour ce tâche 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 carte de 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 !
+
+
+
+✏️ **A votre tour** Essayez un autre modèle pour voir s'il est plus performant pour 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 celui 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 le *finetuning* précédent et que nous repartons du modèle BERT pré-entraîné :
+
+```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 le taux de décroissance des poids est appliqué :
+
+```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 entraîner sur des TPUs dans un *notebook* 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 l'argument `fp16=True` à `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 *Hub*, si vous n'êtes pas déjà connecté. Nous déterminerons le nom du dépôt à partir de l'identifiant 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 dépôt dans un dossier local. S'il existe déjà, ce dossier local doit être un clone du dépôt 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 car `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()` car 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 `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
+
+# Remplacez par votre propre 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/chapter7/8.mdx b/chapters/fr/chapter7/8.mdx
index aeea11abb..927345b35 100644
--- a/chapters/fr/chapter7/8.mdx
+++ b/chapters/fr/chapter7/8.mdx
@@ -1,17 +1,22 @@
-# Maîtriser le NLP
-
-Si vous êtes arrivé jusqu'ici dans le cours, félicitations ! Vous avez maintenant toutes les connaissances et les outils nécessaires pour aborder (presque) n'importe quelle tâche de *NLP* avec 🤗 *Transformers* et l'écosystème d'*Hugging Face* !
-
-Nous avons vu beaucoup de collecteurs de données différents, c'est pourquoi nous avons fait cette petite vidéo pour vous aider à trouver lequel utiliser pour chaque tâche :
-
-
-
-Après avoir terminé ce tour d'horizon des principales tâches de *NLP*, vous devriez :
-
-* savoir quelles architectures (encodeur, décodeur ou encodeur-décodeur) sont les mieux adaptées à chaque tâche,
-* comprendre la différence entre le pré-entraînement et le *finetuning* d'un modèle de langage,
-* savoir comment entraîner des *transformers* en utilisant soit l'API `Trainer` et les fonctionnalités d'entraînement distribué d' 🤗 *Accelerate* ou TensorFlow et Keras selon la piste que vous avez suivie,
-* comprendre la signification et les limites de métriques comme ROUGE et BLEU pour les tâches de génération de texte,
-* savoir comment interagir avec vos modèles *finetunés*, à la fois sur le *Hub* et en utilisant la `pipeline` de 🤗 *Transformers*.
-
+# Maîtriser le NLP
+
+
+
+Si vous êtes arrivé jusqu'ici dans le cours, félicitations ! Vous avez maintenant toutes les connaissances et les outils nécessaires pour aborder (presque) n'importe quelle tâche de *NLP* avec 🤗 *Transformers* et l'écosystème d'*Hugging Face* !
+
+Nous avons vu beaucoup de collecteurs de données différents, c'est pourquoi nous avons fait cette petite vidéo pour vous aider à trouver lequel utiliser pour chaque tâche :
+
+
+
+Après avoir terminé ce tour d'horizon des principales tâches de *NLP*, vous devriez :
+
+* savoir quelles architectures (encodeur, décodeur ou encodeur-décodeur) sont les mieux adaptées à chaque tâche,
+* comprendre la différence entre le pré-entraînement et le *finetuning* d'un modèle de langage,
+* savoir comment entraîner des *transformers* en utilisant soit l'API `Trainer` et les fonctionnalités d'entraînement distribué d' 🤗 *Accelerate* ou TensorFlow et Keras selon la piste que vous avez suivie,
+* comprendre la signification et les limites de métriques comme ROUGE et BLEU pour les tâches de génération de texte,
+* savoir comment interagir avec vos modèles *finetunés*, à la fois sur le *Hub* et en utilisant la `pipeline` de 🤗 *Transformers*.
+
Malgré toutes ces connaissances, il arrivera un moment où vous rencontrerez un *bug* difficile dans votre code ou aurez une question sur la façon de résoudre un problème de *NLP* particulier. Heureusement, la communauté d'*Hugging Face* est là pour vous aider ! Dans le dernier chapitre de cette partie du cours, nous allons explorer comment vous pouvez déboguer vos modèles et demander de l'aide efficacement.
\ No newline at end of file
diff --git a/chapters/fr/chapter7/9.mdx b/chapters/fr/chapter7/9.mdx
index ae063e9d0..c3f9e5934 100644
--- a/chapters/fr/chapter7/9.mdx
+++ b/chapters/fr/chapter7/9.mdx
@@ -1,324 +1,329 @@
-
-
-
-
-# Quiz de fin de chapitre
-
-Testons ce que vous avez appris dans ce chapitre !
-
-### 1. Laquelle des tâches suivantes peut être considérée comme un problème de classification de tokens ?
-
-
-
-### 2. Quelle partie du prétraitement pour la classification de tokens diffère des autres pipelines de prétraitement ?
-
--100 pour étiqueter les tokens spéciaux.",
- explain: "Ce n'est pas spécifique à la classification de tokens. Nous utilisons toujours -100
comme étiquette pour les tokens que nous voulons ignorer dans la perte."
- },
- {
- text: "Nous devons nous assurer que les étiquettes sont tronquées ou rembourrées à la même taille que les entrées, lorsque nous appliquons la troncature/le padding.",
- explain: "En effet mais ce n'est pas la seule différence.",
- correct: true
- }
- ]}
-/>
-
-### 3. Quel problème se pose lorsque nous tokenisons les mots dans un problème de classification de tokens et que nous voulons étiqueter les tokens ?
-
-tokenizer ajoute des tokens spéciaux et nous n'avons pas d'étiquettes pour eux.",
- explain: "Nous les étiquetons par -100
ils sont donc ignorés dans la perte."
- },
- {
- text: "Chaque mot peut produire plusieurs tokens, ce qui fait que nous nous retrouvons avec plus de tokens que d'étiquettes.",
- explain: "C'est le problème principal et nous devons aligner les étiquettes originales avec les tokens.",
- correct: true
- },
- {
- text: "Les tokens ajoutés n'ont pas d'étiquettes, il n'y a donc pas de problème.",
- explain: "Nous avons besoin d'autant d'étiquettes que de tokens, sinon nos modèles se tromperont."
- }
- ]}
-/>
-
-### 4. Que signifie « adaptation au domaine » ?
-
-finetunons un modèle pré-entraîné sur un nouveau jeu de données et qu'il donne des prédictions qui sont plus adaptées à ce nouveau jeu de données.",
- explain: "Le modèle a adapté ses connaissances au nouveau jeu de données.",
- correct: true
- },
- {
- text: "C'est lorsque nous ajoutons des échantillons mal classés à un jeu de données pour rendre notre modèle plus robuste.",
- explain: "C'est certainement quelque chose que vous devriez faire si vous réentraînez votre modèle régulièrement, mais ce n'est pas une adaptation au domaine."
- }
- ]}
-/>
-
-### 5. Quelles sont les étiquettes dans un problème de modélisation du langage masqué ?
-
-tokens de la phrase d'entrée sont masqués de manière aléatoire et les étiquettes sont les tokens d'entrée originaux.",
- explain: "C'est ça !",
- correct: true
- },
- {
- text: "Certains des tokens de la phrase d'entrée sont masqués de manière aléatoire et les étiquettes sont les tokens d'entrée originaux, décalés vers la gauche.",
- explain: "Non, le déplacement des étiquettes vers la gauche correspond à la prédiction du mot suivant, ce qui est une modélisation causale du langage."
- },
- {
- text: "Certains des tokens de la phrase d'entrée sont masqués de manière aléatoire et l'étiquette indique si la phrase est positive ou négative.",
- explain: "Il s'agit d'un problème de classification de séquences avec une certaine augmentation de données et non d'une modélisation du langage masqué."
- },
- {
- text: "Certains des tokens des deux phrases d'entrée sont masqués de manière aléatoire et l'étiquette indique si les deux phrases sont similaires ou non.",
- explain: "Il s'agit d'un problème de classification de séquences avec une certaine augmentation de données et non d'une modélisation du langage masqué."
- }
- ]}
-/>
-
-### 6. Laquelle de ces tâches peut être considérée comme un problème de séquence à séquence ?
-
-
-
-### 7. Quelle est la bonne façon de prétraiter les données pour un problème de séquence à séquence ?
-
-tokenizer avec les éléments suivants inputs=...
et targets=...
.",
- explain: "Nous pourrions ajouter cette API à l'avenir mais ce n'est pas possible pour le moment."
- },
- {
- text: "Les entrées et les cibles doivent être prétraitées, en deux appels séparés au tokenizer.",
- explain: "C'est vrai, mais incomplet. Il y a quelque chose que vous devez faire pour vous assurer que le tokenizer traite les deux correctement."
- },
- {
- text: "Comme d'habitude, nous devons simplement tokeniser les entrées.",
- explain: "Pas dans un problème de classification de séquences. Les cibles sont aussi des textes que nous devons convertir en chiffres !"
- },
- {
- text: "Les entrées doivent être envoyées au tokenizer, et les cibles aussi, mais sous un gestionnaire de contexte spécial.",
- explain: "C'est exact, le tokenizer doit être mis en mode cible par ce gestionnaire de contexte.",
- correct: true
- }
- ]}
-/>
-
-{#if fw === 'pt'}
-
-### 8. Pourquoi existe-t-il une sous-classe spécifique de Trainer
pour les problèmes de séquence à séquence ?
-
--100.",
- explain: "Ce n'est pas du tout une perte personnalisée mais la façon dont la perte est toujours calculée."
- },
- {
- text: "Parce que les problèmes de séquence à séquence nécessitent une boucle d'évaluation spéciale.",
- explain: "Les prédictions des modèles de séquence à séquence sont souvent exécutées en utilisant la méthode generate()
.",
- correct: true
- },
- {
- text: "Parce que les cibles sont des textes dans des problèmes de séquence à séquence.",
- explain: "Trainer
ne se soucie pas vraiment de cela puisqu'elles ont été prétraités auparavant."
- },
- {
- text: "Parce que nous utilisons deux modèles dans les problèmes de séquence à séquence.",
- explain: "Nous utilisons en quelque sorte deux modèles, un encodeur et un décodeur, mais ils sont regroupés dans un seul modèle."
- }
- ]}
-/>
-
-{:else}
-
-### 9. Pourquoi est-il souvent inutile de spécifier une perte quand on appelle compile()
sur un transformer ?
-
-tranformers sont entraînés avec un apprentissage autosupervisé.",
- explain: "Pas tout à fait. Même l'apprentissage autosupervisé a besoin d'une fonction de perte !"
- },
- {
- text: "Parce que la sortie de perte interne du modèle est utilisée par défaut.",
- explain: " ",
- correct: true
- },
- {
- text: "Parce que nous calculons les mesures après l'entraînement au lieu de le faire.",
- explain: "Nous le faisons souvent mais cela n'explique pas d'où vient la valeur de perte que nous optimisons dans l'entraînement."
- },
- {
- text: "Parce que la perte est spécifiée dans model.fit()
.",
- explain: "La fonction de perte est toujours fixée une fois que vous exécutez model.compile()
et ne peut pas être modifiée dans model.fit()
."
- }
- ]}
-/>
-
-{/if}
-
-### 10. Quand devez-vous pré-entraîner un nouveau modèle ?
-
-finetuner sur vos données afin d'éviter d'énormes coûts de calcul."
- },
- {
- text: "Lorsque vous avez des doutes sur le biais du modèle pré-entraîné que vous utilisez.",
- explain: "C'est vrai mais vous devez vous assurer que les données que vous utiliserez pour l'entraînement sont vraiment meilleures.",
- correct: true
- },
- {
- text: "Lorsque les modèles pré-entraînés disponibles ne sont tout simplement pas assez bons.",
- explain: "Vous êtes sûr d'avoir bien débogué votre entraînement ?"
- }
- ]}
-/>
-
-### 11. Pourquoi est-il facile de prétraîner un modèle de langage sur des batchs de textes ?
-
-Transformers ne nécessite que quelques lignes de code pour démarrer l'entraînement.",
- explain: "Bien que vrai, cela ne répond pas vraiment à la question posée. Essayez une autre réponse !"
- }
- ]}
-/>
-
-### 12. Quels sont les principaux défis lors du prétraitement des données pour une tâche de réponse à des questions ?
-
-
-
-### 13. Comment le post-traitement est-il généralement effectué dans les réponses aux questions ?
-
-tokens correspondant.",
- explain: "Ce pourrait être une façon de faire mais c'est un peu trop simpliste."
- },
- {
- text: "Le modèle vous donne les positions de début et de fin de la réponse pour chaque caractéristique créée par un exemple et il vous suffit de décoder la plage de tokens correspondant dans celui qui a le meilleur score.",
- explain: "C'est proche du post-traitement que nous avons étudié, mais ce n'est pas tout à fait exact."
- },
- {
- text: "Le modèle vous donne les positions de début et de fin de la réponse pour chaque caractéristique créée par un exemple et vous n'avez plus qu'à les faire correspondre à la portée dans le contexte de celui qui a le meilleur score.",
- explain: "C'est ça en résumé !",
- correct: true
- },
- {
- text: "Le modèle génère une réponse et il vous suffit de la décoder.",
- explain: "A moins que vous ne formuliez votre problème de réponse aux questions comme une tâche de séquence à séquence."
- }
- ]}
-/>
+
+
+
+
+# Quiz de fin de chapitre
+
+
+
+Testons ce que vous avez appris dans ce chapitre !
+
+### 1. Laquelle des tâches suivantes peut être considérée comme un problème de classification de tokens ?
+
+
+
+### 2. Quelle partie du prétraitement pour la classification de tokens diffère des autres pipelines de prétraitement ?
+
+-100 pour étiqueter les