From d15d46525bf7c0d75dfdbc83c2aa185906362960 Mon Sep 17 00:00:00 2001 From: Lewis Tunstall Date: Tue, 22 Nov 2022 12:42:09 +0100 Subject: [PATCH 1/4] Migrate Spaces URLs to new domain --- chapters/en/chapter7/2.mdx | 2 +- chapters/en/chapter7/3.mdx | 2 +- chapters/en/chapter7/4.mdx | 2 +- chapters/en/chapter7/5.mdx | 2 +- chapters/en/chapter7/6.mdx | 2 +- chapters/en/chapter7/7.mdx | 2 +- chapters/en/chapter9/1.mdx | 6 +- chapters/en/chapter9/2.mdx | 6 +- chapters/en/chapter9/3.mdx | 6 +- chapters/en/chapter9/4.mdx | 6 +- chapters/en/chapter9/5.mdx | 6 +- chapters/en/chapter9/6.mdx | 4 +- chapters/en/chapter9/7.mdx | 10 +- chapters/fr/chapter0/1.mdx | 220 ++-- chapters/fr/chapter7/2.mdx | 4 +- chapters/fr/chapter7/3.mdx | 4 +- chapters/fr/chapter7/4.mdx | 2022 +++++++++++++++--------------- chapters/fr/chapter7/5.mdx | 2206 ++++++++++++++++----------------- chapters/fr/chapter7/6.mdx | 4 +- chapters/fr/chapter7/7.mdx | 4 +- chapters/fr/chapter9/2.mdx | 6 +- chapters/fr/chapter9/3.mdx | 6 +- chapters/fr/chapter9/4.mdx | 6 +- chapters/fr/chapter9/5.mdx | 2 +- chapters/fr/chapter9/6.mdx | 4 +- chapters/fr/chapter9/7.mdx | 10 +- chapters/fr/events/1.mdx | 98 +- chapters/hi/chapter0/1.mdx | 220 ++-- chapters/it/chapter9/1.mdx | 6 +- chapters/it/chapter9/2.mdx | 6 +- chapters/it/chapter9/3.mdx | 6 +- chapters/ja/chapter7/2.mdx | 2 +- chapters/ja/chapter7/3.mdx | 2 +- chapters/ja/chapter7/4.mdx | 2 +- chapters/ja/chapter7/5.mdx | 2 +- chapters/ja/chapter7/6.mdx | 2 +- chapters/ja/chapter7/7.mdx | 2 +- chapters/vi/chapter7/2.mdx | 2 +- chapters/vi/chapter7/3.mdx | 2 +- chapters/vi/chapter7/4.mdx | 2 +- chapters/vi/chapter7/5.mdx | 2 +- chapters/vi/chapter7/6.mdx | 2 +- chapters/vi/chapter7/7.mdx | 2 +- chapters/vi/chapter9/1.mdx | 6 +- chapters/vi/chapter9/2.mdx | 6 +- chapters/vi/chapter9/3.mdx | 328 ++--- chapters/vi/chapter9/4.mdx | 6 +- chapters/vi/chapter9/5.mdx | 6 +- chapters/vi/chapter9/6.mdx | 4 +- chapters/vi/chapter9/7.mdx | 10 +- chapters/vi/chapter9/9.mdx | 468 +++---- chapters/zh-CN/chapter7/2.mdx | 4 +- chapters/zh-CN/chapter7/3.mdx | 4 +- chapters/zh-CN/chapter7/4.mdx | 4 +- chapters/zh-CN/chapter7/5.mdx | 4 +- chapters/zh-CN/chapter7/6.mdx | 4 +- chapters/zh-CN/chapter7/7.mdx | 4 +- chapters/zh-CN/chapter9/1.mdx | 6 +- chapters/zh-CN/chapter9/2.mdx | 6 +- chapters/zh-CN/chapter9/3.mdx | 6 +- chapters/zh-CN/chapter9/4.mdx | 6 +- chapters/zh-CN/chapter9/5.mdx | 6 +- chapters/zh-CN/chapter9/6.mdx | 4 +- chapters/zh-CN/chapter9/7.mdx | 10 +- 64 files changed, 2908 insertions(+), 2908 deletions(-) diff --git a/chapters/en/chapter7/2.mdx b/chapters/en/chapter7/2.mdx index 81ef920d6..23a7c153e 100644 --- a/chapters/en/chapter7/2.mdx +++ b/chapters/en/chapter7/2.mdx @@ -32,7 +32,7 @@ The first application we'll explore is token classification. This generic task e Of course, there are many other types of token classification problem; those are just a few representative examples. In this section, we will fine-tune a model (BERT) on a NER task, which will then be able to compute predictions like this one: - + One-hot encoded labels for question answering. diff --git a/chapters/en/chapter7/3.mdx b/chapters/en/chapter7/3.mdx index 982d8beba..032c5ac4b 100644 --- a/chapters/en/chapter7/3.mdx +++ b/chapters/en/chapter7/3.mdx @@ -35,7 +35,7 @@ This process of fine-tuning a pretrained language model on in-domain data is usu By the end of this section you'll have a [masked language model](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.) on the Hub that can autocomplete sentences as shown below: - + Let's dive in! diff --git a/chapters/en/chapter7/4.mdx b/chapters/en/chapter7/4.mdx index e7b2ad3fb..12c94051e 100644 --- a/chapters/en/chapter7/4.mdx +++ b/chapters/en/chapter7/4.mdx @@ -35,7 +35,7 @@ In this section, we will fine-tune a Marian model pretrained to translate from E Once we're finished, we will have a model able to make predictions like this one: - + One-hot encoded labels for question answering. diff --git a/chapters/en/chapter7/5.mdx b/chapters/en/chapter7/5.mdx index 6b93c1fea..723c5cbef 100644 --- a/chapters/en/chapter7/5.mdx +++ b/chapters/en/chapter7/5.mdx @@ -29,7 +29,7 @@ In this section we'll take a look at how Transformer models can be used to conde 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. diff --git a/chapters/en/chapter7/6.mdx b/chapters/en/chapter7/6.mdx index b5e48fdc8..a81c66be0 100644 --- a/chapters/en/chapter7/6.mdx +++ b/chapters/en/chapter7/6.mdx @@ -30,7 +30,7 @@ In this section we will build a scaled-down version of a code generation model: In [Chapter 6](/course/chapter6) we created an efficient tokenizer to process Python source code, but what we still need is a large-scale dataset to pretrain a model on. Here, we'll apply our tokenizer to a corpus of Python code derived from GitHub repositories. We will then use the `Trainer` API and 🤗 Accelerate to train the model. Let's get to it! - + This is actually showcasing the model that was trained and uploaded to the Hub using the code shown in this section. You can find it [here](https://huggingface.co/huggingface-course/codeparrot-ds?text=plt.imshow%28). Note that since there is some randomization happening in the text generation, you will probably get a slightly different result. diff --git a/chapters/en/chapter7/7.mdx b/chapters/en/chapter7/7.mdx index ed85f59c3..11ee8a7c9 100644 --- a/chapters/en/chapter7/7.mdx +++ b/chapters/en/chapter7/7.mdx @@ -28,7 +28,7 @@ Time to look at question answering! This task comes in many flavors, but the one We will fine-tune a BERT model on the [SQuAD dataset](https://rajpurkar.github.io/SQuAD-explorer/), which consists of questions posed by crowdworkers on a set of Wikipedia articles. This will give us a model able to compute predictions like this one: - + This is actually showcasing the model that was trained and uploaded to the Hub using the code shown in this section. You can find it and double-check the predictions [here](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). diff --git a/chapters/en/chapter9/1.mdx b/chapters/en/chapter9/1.mdx index 08e130eb2..fb2061904 100644 --- a/chapters/en/chapter9/1.mdx +++ b/chapters/en/chapter9/1.mdx @@ -20,15 +20,15 @@ Here are some examples of machine learning demos built with Gradio: * A **sketch recognition** model that takes in a sketch and outputs labels of what it thinks is being drawn: - + * An extractive **question answering** model that takes in a context paragraph and a quest and outputs a response and a probability score (we discussed this kind of model [in Chapter 7](/course/chapter7/7)): - + * A **background removal** model that takes in an image and outputs the image with the background removed: - + This chapter is broken down into sections which include both _concepts_ and _applications_. After you learn the concept in each section, you'll apply it to build a particular kind of demo, ranging from image classification to speech recognition. By the time you finish this chapter, you'll be able to build these demos (and many more!) in just a few lines of Python code. diff --git a/chapters/en/chapter9/2.mdx b/chapters/en/chapter9/2.mdx index 8dee73747..99f2abbad 100644 --- a/chapters/en/chapter9/2.mdx +++ b/chapters/en/chapter9/2.mdx @@ -37,7 +37,7 @@ Let's walk through the code above: If you run this code, the interface below will appear automatically within a Jupyter/Colab notebook, or pop in a browser on **[http://localhost:7860](http://localhost:7860/)** if running from a script. - + Try using this GUI right now with your own name or some other input! @@ -62,7 +62,7 @@ textbox = gr.Textbox(label="Type your name here:", placeholder="John Doe", lines gr.Interface(fn=greet, inputs=textbox, outputs="text").launch() ``` - + Here, we've created an input textbox with a label, a placeholder, and a set number of lines. You could do the same for the output textbox, but we'll leave that for now. @@ -113,6 +113,6 @@ gr.Interface(fn=predict, inputs="text", outputs="text").launch() That's it! You can now use this interface to generate text using the GPT-2 model as shown below 🤯. - + Keep reading to see how to build other kinds of demos with Gradio! \ No newline at end of file diff --git a/chapters/en/chapter9/3.mdx b/chapters/en/chapter9/3.mdx index b3f18a27a..73643bfc9 100644 --- a/chapters/en/chapter9/3.mdx +++ b/chapters/en/chapter9/3.mdx @@ -71,7 +71,7 @@ gr.Interface(reverse_audio, mic, "audio").launch() The code above will produce an interface like the one below (if your browser doesn't ask you for microphone permissions, open the demo in a separate tab.) - + You should now be able to record your voice and hear yourself speaking in reverse - spooky 👻! @@ -118,7 +118,7 @@ gr.Interface( ).launch() ``` - + ### The `launch()` method @@ -176,7 +176,7 @@ gr.Interface( If your browser doesn't ask you for microphone permissions, open the demo in a separate tab. - + That's it! You can now use this interface to transcribe audio. Notice here that diff --git a/chapters/en/chapter9/4.mdx b/chapters/en/chapter9/4.mdx index 2dcd458e6..5894eb189 100644 --- a/chapters/en/chapter9/4.mdx +++ b/chapters/en/chapter9/4.mdx @@ -32,7 +32,7 @@ Using the options above, we end up with a more complete interface. Run the code title = "Ask Rick a Question" description = """ The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything! - + """ article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of." @@ -50,7 +50,7 @@ gr.Interface( Using the options above, we end up with a more complete interface. Try the interface below: - + ### Sharing your demo with temporary links Now that we have a working demo of our machine learning model, let's learn how to easily share a link to our interface. @@ -132,7 +132,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + Notice the `live=True` parameter in `Interface`, which means that the sketch demo makes diff --git a/chapters/en/chapter9/5.mdx b/chapters/en/chapter9/5.mdx index b7d61d08a..2b1587332 100644 --- a/chapters/en/chapter9/5.mdx +++ b/chapters/en/chapter9/5.mdx @@ -41,7 +41,7 @@ gr.Interface.load( The code above will produce the interface below: - + Loading a model in this way uses Hugging Face's [Inference API](https://huggingface.co/inference-api), instead of loading the model in memory. This is ideal for huge models like GPT-J or T0pp which @@ -56,7 +56,7 @@ Remember the demo from section 1 that removes the background of an image? Let's gr.Interface.load("spaces/abidlabs/remove-bg").launch() ``` - + One of the cool things about loading demos from the Hub or Spaces is that you customize them by overriding any of the @@ -68,6 +68,6 @@ gr.Interface.load( ).launch() ``` - + Now that we've explored a few ways to integrate Gradio with the Hugging Face Hub, let's take a look at some advanced features of the `Interface` class. That's the topic of the next section! \ No newline at end of file diff --git a/chapters/en/chapter9/6.mdx b/chapters/en/chapter9/6.mdx index 74ba03a08..2ceb35237 100644 --- a/chapters/en/chapter9/6.mdx +++ b/chapters/en/chapter9/6.mdx @@ -53,7 +53,7 @@ iface = gr.Interface( iface.launch() ``` - + Notice how the state of the output component persists across submits. Note: you can pass in a default value to the state parameter, @@ -94,7 +94,7 @@ gr.Interface( Test the interpretation function by submitting an input then clicking Interpret under the output component. - + Besides the default interpretation method Gradio provides, you can also specify `shap` for the `interpretation` parameter and set the `num_shap` parameter. This uses Shapley-based interpretation, which you can read more about [here](https://christophm.github.io/interpretable-ml-book/shap.html). Lastly, you can also pass in your own interpretation function into the `interpretation` parameter. See an example in Gradio's getting started page [here](https://gradio.app/getting_started/). diff --git a/chapters/en/chapter9/7.mdx b/chapters/en/chapter9/7.mdx index 3dc2bf4ca..2847254da 100644 --- a/chapters/en/chapter9/7.mdx +++ b/chapters/en/chapter9/7.mdx @@ -56,7 +56,7 @@ with demo: demo.launch() ``` - + This simple example above introduces 4 concepts that underlie Blocks: @@ -121,7 +121,7 @@ with demo: demo.launch() ``` - + You'll notice that in this example, we've also created a `Button` component in each tab, and we've assigned a click event to each button, which is what actually runs the function. @@ -160,7 +160,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### Creating multi-step demos @@ -200,7 +200,7 @@ with demo: demo.launch() ``` - + ### Updating Component Properties @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + We just explored all the core concepts of `Blocks`! Just like with `Interfaces`, you can create cool demos that can be shared by using `share=True` in the `launch()` method or deployed on [Hugging Face Spaces](https://huggingface.co/spaces). \ No newline at end of file diff --git a/chapters/fr/chapter0/1.mdx b/chapters/fr/chapter0/1.mdx index 5a1be8e8a..47bfa9809 100644 --- a/chapters/fr/chapter0/1.mdx +++ b/chapters/fr/chapter0/1.mdx @@ -1,110 +1,110 @@ -# Introduction - -Bienvenue au cours d'Hugging Face ! Cette introduction est là pour vous guider dans la mise en place d'un environnement de travail. Si vous venez de commencer le cours, nous vous recommandons de consulter d'abord le [chapitre 1](/course/fr/chapter1) puis de revenir ici et de configurer votre environnement afin de pouvoir essayer le code vous-même. - -Toutes les bibliothèques que nous utiliserons dans ce cours sont disponibles sous forme de *packages* Python. Nous allons donc vous montrer comment configurer un environnement Python et installer les bibliothèques spécifiques dont vous aurez besoin. - -Nous aborderons deux façons de configurer votre environnement de travail : soit en utilisant un *notebook* Google Colab, soit en utilisant un environnement virtuel Python. N'hésitez pas à choisir celle qui vous convient le mieux. Pour les débutants, nous vous recommandons vivement de commencer en utilisant un *notebook* Google Colab. - -Notez que nous ne couvrirons pas le système Windows. Si vous travaillez sous Windows, nous vous recommandons de suivre le cours en utilisant un *notebook* Google Colab. Si vous utilisez une distribution Linux ou macOS, vous pouvez utiliser l'une des deux approches décrites ci-dessous. - -La plupart du cours repose sur le fait que vous ayez un compte Hugging Face. Si vous n'en disposez pas d'un, nous vous recommandons d'en créer un dès maintenant : [créer un compte](https://huggingface.co/join). - -## Utilisation d'un notebook Google Colab - -L'utilisation d'un *notebook* Google Colab est la configuration la plus simple possible. Démarrez un *notebook* dans votre navigateur et passez directement au codage ! - -Si vous n'êtes pas familier avec Colab, nous vous recommandons de commencer par suivre l'[introduction](https://colab.research.google.com/notebooks/intro.ipynb). Colab vous permet d'utiliser du matériel comme les GPUs ou les TPUs et est gratuit pour les petites charges de travail. - -Une fois que vous vous sentez suffisamment à l'aise avec Colab, créez un nouveau *notebook* et commencez à le configurer : - -
-An empty colab notebook -
- -L'étape suivante consiste à installer les bibliothèques que nous allons utiliser dans ce cours. Nous utiliserons `pip` pour l'installation qui est le gestionnaire de *packages* pour Python. Dans les *notebooks*, vous pouvez exécuter des commandes système en les faisant précéder du caractère `!`. Vous pouvez donc installer la bibliothèque 🤗 *Transformers* comme suit : - -``` -!pip install transformers -``` - -Vous pouvez vous assurer que le paquet a été correctement installé en l'important dans votre runtime Python : - -``` -import transformers -``` - -
-A gif showing the result of the two commands above: installation and import -
- -Cela installe une version très légère de 🤗 *Transformers*. En particulier, aucun *framework* d'apprentissage automatique spécifique (comme PyTorch ou TensorFlow) n'est installé. Comme nous utiliserons de nombreuses fonctionnalités différentes de la bibliothèque, nous recommandons d'installer la version de développement qui est livrée avec toutes les dépendances requises pour à peu près tous les cas d'utilisation imaginables : - -``` -!pip install transformers[sentencepiece] -``` - -Cela prendra un peu de temps, mais vous serez alors prêt pour le reste du cours ! - - -## Utilisation d'un environnement virtuel Python - -Si vous préférez utiliser un environnement virtuel Python, la première étape consiste à installer Python sur votre système. Nous vous recommandons de suivre [ce guide](https://realpython.com/installing-python/) pour commencer. - -Une fois Python installé, vous devriez être en mesure d'exécuter des commandes Python dans votre terminal. Vous pouvez commencer par exécuter la commande suivante pour vous assurer qu'il est correctement installé avant de passer aux étapes suivantes : `python --version`. Cette commande devrait vous indiquer la version de Python disponible sur votre système. - -Lorsque vous exécutez une commande Python dans votre terminal, comme `python --version`, vous devez considérer le programme qui exécute votre commande comme la fonction « main » Python sur votre système. Nous vous recommandons de garder cette installation principale libre de tout *package* et de l'utiliser pour créer des environnements séparés pour chaque application sur laquelle vous travaillez. De cette façon, chaque application peut avoir ses propres dépendances et *packages*, et vous n'aurez pas à vous soucier de problèmes potentiels de compatibilité avec d'autres applications. - -En Python, cela se fait avec les [*environnements virtuels*](https://docs.python.org/3/tutorial/venv.html), qui sont des arbres de répertoires autonomes contenant chacun une installation Python avec une version particulière de Python ainsi que tous les *packages* dont l'application a besoin. La création d'un tel environnement virtuel peut se faire à l'aide d'un certain nombre d'outils différents, mais nous utiliserons le *package* officiel de Python : [`venv`](https://docs.python.org/3/library/venv.html#module-venv). - -Tout d'abord, créez le répertoire dans lequel vous souhaitez que votre application se trouve. Par exemple, vous pouvez créer un nouveau répertoire appelé *transformers-course* à la racine de votre répertoire personnel : -``` -mkdir ~/transformers-course -cd ~/transformers-course -``` - -A l'intérieur de ce répertoire, créez un environnement virtuel en utilisant le module Python `venv` : - -``` -python -m venv .env -``` - -Vous devriez maintenant avoir un répertoire appelé *.env* dans votre dossier autrement vide : - -``` -ls -a -``` - -```out -. .. .env -``` - -Vous pouvez entrer et sortir de votre environnement virtuel avec les scripts `activate` et `deactivate` : - -``` -# Activate the virtual environment -source .env/bin/activate - -# Deactivate the virtual environment -source .env/bin/deactivate -``` - -Vous pouvez vous assurer que l'environnement est activé en exécutant la commande `which python` : si elle pointe vers l'environnement virtuel, alors vous l'avez activé avec succès ! - -``` -which python -``` - -```out -/home//transformers-course/.env/bin/python -``` - -### Installation des dépendances - -Comme dans la section précédente sur l'utilisation des instances Google Colab, vous devez maintenant installer les *packages* requis pour continuer. Encore une fois, vous pouvez installer la version de développement de 🤗 *Transformers* à l'aide du gestionnaire de packages `pip` : - -``` -pip install "transformers[sentencepiece]" -``` - -Vous êtes maintenant prêt ! +# Introduction + +Bienvenue au cours d'Hugging Face ! Cette introduction est là pour vous guider dans la mise en place d'un environnement de travail. Si vous venez de commencer le cours, nous vous recommandons de consulter d'abord le [chapitre 1](/course/fr/chapter1) puis de revenir ici et de configurer votre environnement afin de pouvoir essayer le code vous-même. + +Toutes les bibliothèques que nous utiliserons dans ce cours sont disponibles sous forme de *packages* Python. Nous allons donc vous montrer comment configurer un environnement Python et installer les bibliothèques spécifiques dont vous aurez besoin. + +Nous aborderons deux façons de configurer votre environnement de travail : soit en utilisant un *notebook* Google Colab, soit en utilisant un environnement virtuel Python. N'hésitez pas à choisir celle qui vous convient le mieux. Pour les débutants, nous vous recommandons vivement de commencer en utilisant un *notebook* Google Colab. + +Notez que nous ne couvrirons pas le système Windows. Si vous travaillez sous Windows, nous vous recommandons de suivre le cours en utilisant un *notebook* Google Colab. Si vous utilisez une distribution Linux ou macOS, vous pouvez utiliser l'une des deux approches décrites ci-dessous. + +La plupart du cours repose sur le fait que vous ayez un compte Hugging Face. Si vous n'en disposez pas d'un, nous vous recommandons d'en créer un dès maintenant : [créer un compte](https://huggingface.co/join). + +## Utilisation d'un notebook Google Colab + +L'utilisation d'un *notebook* Google Colab est la configuration la plus simple possible. Démarrez un *notebook* dans votre navigateur et passez directement au codage ! + +Si vous n'êtes pas familier avec Colab, nous vous recommandons de commencer par suivre l'[introduction](https://colab.research.google.com/notebooks/intro.ipynb). Colab vous permet d'utiliser du matériel comme les GPUs ou les TPUs et est gratuit pour les petites charges de travail. + +Une fois que vous vous sentez suffisamment à l'aise avec Colab, créez un nouveau *notebook* et commencez à le configurer : + +
+An empty colab notebook +
+ +L'étape suivante consiste à installer les bibliothèques que nous allons utiliser dans ce cours. Nous utiliserons `pip` pour l'installation qui est le gestionnaire de *packages* pour Python. Dans les *notebooks*, vous pouvez exécuter des commandes système en les faisant précéder du caractère `!`. Vous pouvez donc installer la bibliothèque 🤗 *Transformers* comme suit : + +``` +!pip install transformers +``` + +Vous pouvez vous assurer que le paquet a été correctement installé en l'important dans votre runtime Python : + +``` +import transformers +``` + +
+A gif showing the result of the two commands above: installation and import +
+ +Cela installe une version très légère de 🤗 *Transformers*. En particulier, aucun *framework* d'apprentissage automatique spécifique (comme PyTorch ou TensorFlow) n'est installé. Comme nous utiliserons de nombreuses fonctionnalités différentes de la bibliothèque, nous recommandons d'installer la version de développement qui est livrée avec toutes les dépendances requises pour à peu près tous les cas d'utilisation imaginables : + +``` +!pip install transformers[sentencepiece] +``` + +Cela prendra un peu de temps, mais vous serez alors prêt pour le reste du cours ! + + +## Utilisation d'un environnement virtuel Python + +Si vous préférez utiliser un environnement virtuel Python, la première étape consiste à installer Python sur votre système. Nous vous recommandons de suivre [ce guide](https://realpython.com/installing-python/) pour commencer. + +Une fois Python installé, vous devriez être en mesure d'exécuter des commandes Python dans votre terminal. Vous pouvez commencer par exécuter la commande suivante pour vous assurer qu'il est correctement installé avant de passer aux étapes suivantes : `python --version`. Cette commande devrait vous indiquer la version de Python disponible sur votre système. + +Lorsque vous exécutez une commande Python dans votre terminal, comme `python --version`, vous devez considérer le programme qui exécute votre commande comme la fonction « main » Python sur votre système. Nous vous recommandons de garder cette installation principale libre de tout *package* et de l'utiliser pour créer des environnements séparés pour chaque application sur laquelle vous travaillez. De cette façon, chaque application peut avoir ses propres dépendances et *packages*, et vous n'aurez pas à vous soucier de problèmes potentiels de compatibilité avec d'autres applications. + +En Python, cela se fait avec les [*environnements virtuels*](https://docs.python.org/3/tutorial/venv.html), qui sont des arbres de répertoires autonomes contenant chacun une installation Python avec une version particulière de Python ainsi que tous les *packages* dont l'application a besoin. La création d'un tel environnement virtuel peut se faire à l'aide d'un certain nombre d'outils différents, mais nous utiliserons le *package* officiel de Python : [`venv`](https://docs.python.org/3/library/venv.html#module-venv). + +Tout d'abord, créez le répertoire dans lequel vous souhaitez que votre application se trouve. Par exemple, vous pouvez créer un nouveau répertoire appelé *transformers-course* à la racine de votre répertoire personnel : +``` +mkdir ~/transformers-course +cd ~/transformers-course +``` + +A l'intérieur de ce répertoire, créez un environnement virtuel en utilisant le module Python `venv` : + +``` +python -m venv .env +``` + +Vous devriez maintenant avoir un répertoire appelé *.env* dans votre dossier autrement vide : + +``` +ls -a +``` + +```out +. .. .env +``` + +Vous pouvez entrer et sortir de votre environnement virtuel avec les scripts `activate` et `deactivate` : + +``` +# Activate the virtual environment +source .env/bin/activate + +# Deactivate the virtual environment +source .env/bin/deactivate +``` + +Vous pouvez vous assurer que l'environnement est activé en exécutant la commande `which python` : si elle pointe vers l'environnement virtuel, alors vous l'avez activé avec succès ! + +``` +which python +``` + +```out +/home//transformers-course/.env/bin/python +``` + +### Installation des dépendances + +Comme dans la section précédente sur l'utilisation des instances Google Colab, vous devez maintenant installer les *packages* requis pour continuer. Encore une fois, vous pouvez installer la version de développement de 🤗 *Transformers* à l'aide du gestionnaire de packages `pip` : + +``` +pip install "transformers[sentencepiece]" +``` + +Vous êtes maintenant prêt ! diff --git a/chapters/fr/chapter7/2.mdx b/chapters/fr/chapter7/2.mdx index 804e7fb82..bd7f1c0a6 100644 --- a/chapters/fr/chapter7/2.mdx +++ b/chapters/fr/chapter7/2.mdx @@ -32,8 +32,8 @@ La première application que nous allons explorer est la classification de *toke 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 : - - + + One-hot encoded labels for question answering. diff --git a/chapters/fr/chapter7/3.mdx b/chapters/fr/chapter7/3.mdx index 0579e6af9..55a91167e 100644 --- a/chapters/fr/chapter7/3.mdx +++ b/chapters/fr/chapter7/3.mdx @@ -35,8 +35,8 @@ Ce processus de *finetuning* d'un modèle de langage pré-entraîné sur des don À 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 ! diff --git a/chapters/fr/chapter7/4.mdx b/chapters/fr/chapter7/4.mdx index 28b04b436..236194972 100644 --- a/chapters/fr/chapter7/4.mdx +++ b/chapters/fr/chapter7/4.mdx @@ -1,1011 +1,1011 @@ - - -# 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 : - - - - - -One-hot encoded labels for question answering. - - - -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). - -Language available for the KDE4 dataset. - -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 assembleur 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. - -### Assemblage 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 cet assembleur 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 -model.prepare_tf_dataset( - tokenized_datasets["train"], - collate_fn=data_collator, - shuffle=True, - batch_size=32, -) -tf_eval_dataset = model.prepare_tf_dataset( - tokenized_datasets["validation"], - 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 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 remplissage. Définissons une fonction qui prend notre modèle et un jeu de données et calcule des métriques sur ceux-ci. Nous allons également utiliser une astuce qui augmente considérablement les performances : compiler notre code de génération avec [XLA](https://www.tensorflow.org/xla), le compilateur d'algèbre linéaire accéléré de TensorFlow. XLA applique diverses optimisations au graphe de calcul du modèle, ce qui permet d'améliorer considérablement la vitesse et l'utilisation de la mémoire. Comme décrit dans un article du [blog d’Hugging Face](https://huggingface.co/blog/tf-xla-generate), XLA fonctionne mieux lorsque nos formes d'entrée ne varient pas trop. Pour gérer cela, nous allons rembourrer nos entrées à des multiples de 128, et créer un nouveau jeu de données avec l’assembleur de rembourrage. Puis nous appliquerons le décorateur `@tf.function(jit_compile=True)` à notre fonction de génération, qui marque la fonction entière pour la compilation avec XLA. - -```py -import numpy as np -import tensorflow as tf -from tqdm import tqdm - -generation_data_collator = DataCollatorForSeq2Seq( - tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=128 -) -tf_generate_dataset = model.prepare_tf_dataset( - tokenized_datasets["validation"], - collate_fn=generation_data_collator, - shuffle=False, - batch_size=8, -) - - -@tf.function(jit_compile=True) -def generate_with_xla(batch): - return model.generate( - input_ids=batch["input_ids"], - attention_mask=batch["attention_mask"], - max_new_tokens=128, - ) - - -def compute_metrics(): - all_preds = [] - all_labels = [] - - for batch, labels in tqdm(tf_generate_dataset): - predictions = generate_with_xla(batch) - decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) - labels = 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 : + + + + + +One-hot encoded labels for question answering. + + + +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). + +Language available for the KDE4 dataset. + +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 assembleur 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. + +### Assemblage 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 cet assembleur 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 +model.prepare_tf_dataset( + tokenized_datasets["train"], + collate_fn=data_collator, + shuffle=True, + batch_size=32, +) +tf_eval_dataset = model.prepare_tf_dataset( + tokenized_datasets["validation"], + 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 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 remplissage. Définissons une fonction qui prend notre modèle et un jeu de données et calcule des métriques sur ceux-ci. Nous allons également utiliser une astuce qui augmente considérablement les performances : compiler notre code de génération avec [XLA](https://www.tensorflow.org/xla), le compilateur d'algèbre linéaire accéléré de TensorFlow. XLA applique diverses optimisations au graphe de calcul du modèle, ce qui permet d'améliorer considérablement la vitesse et l'utilisation de la mémoire. Comme décrit dans un article du [blog d’Hugging Face](https://huggingface.co/blog/tf-xla-generate), XLA fonctionne mieux lorsque nos formes d'entrée ne varient pas trop. Pour gérer cela, nous allons rembourrer nos entrées à des multiples de 128, et créer un nouveau jeu de données avec l’assembleur de rembourrage. Puis nous appliquerons le décorateur `@tf.function(jit_compile=True)` à notre fonction de génération, qui marque la fonction entière pour la compilation avec XLA. + +```py +import numpy as np +import tensorflow as tf +from tqdm import tqdm + +generation_data_collator = DataCollatorForSeq2Seq( + tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=128 +) +tf_generate_dataset = model.prepare_tf_dataset( + tokenized_datasets["validation"], + collate_fn=generation_data_collator, + shuffle=False, + batch_size=8, +) + + +@tf.function(jit_compile=True) +def generate_with_xla(batch): + return model.generate( + input_ids=batch["input_ids"], + attention_mask=batch["attention_mask"], + max_new_tokens=128, + ) + + +def compute_metrics(): + all_preds = [] + all_labels = [] + + for batch, labels in tqdm(tf_generate_dataset): + predictions = generate_with_xla(batch) + decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) + labels = 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 0e5e5bd61..cb24bcd28 100644 --- a/chapters/fr/chapter7/5.mdx +++ b/chapters/fr/chapter7/5.mdx @@ -1,1103 +1,1103 @@ - - -# 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 : - -
-Word count distributions for the review titles and texts. - -
- -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 ! - -
-Different tasks performed by the T5 architecture. - -
- -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, - ) - 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 assembleur 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 assembleur `DataCollatorForSeq2Seq` qui rembourrera dynamiquement les entrées et les étiquettes pour nous. Pour instancier ce assembleur, 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 assembleur 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 assembleur ne saura pas comment remplir ces éléments : - -```python -tokenized_datasets = tokenized_datasets.remove_columns( - books_dataset["train"].column_names -) -``` - -Comme le assembleur 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 assembleur 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 assembleur 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 = model.prepare_tf_dataset( - tokenized_datasets["train"], - collate_fn=data_collator, - shuffle=True, - batch_size=8, -) -tf_eval_dataset = model.prepare_tf_dataset( - tokenized_datasets["validation"], - 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 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 une liste d'étiquettes et une liste de prédictions pour la métrique ROUGE pour comparer (notez que si vous obtenez des erreurs d'importation pour cette section, vous pouvez avoir besoin de faire `pip install tqdm`). Nous allons également utiliser une astuce qui augmente considérablement les performances : compiler notre code de génération avec [XLA](https://www.tensorflow.org/xla), le compilateur d'algèbre linéaire accéléré de TensorFlow. XLA applique diverses optimisations au graphe de calcul du modèle, ce qui permet d'améliorer considérablement la vitesse et l'utilisation de la mémoire. Comme décrit dans un article du [blog d’Hugging Face](https://huggingface.co/blog/tf-xla-generate), XLA fonctionne mieux lorsque nos formes d'entrée ne varient pas trop. Pour gérer cela, nous allons rembourrer nos entrées à des multiples de 128, et créer un nouveau jeu de données avec l’assembleur de rembourrage. Puis nous appliquerons le décorateur `@tf.function(jit_compile=True)` à notre fonction de génération, qui marque la fonction entière pour la compilation avec XLA. - -```python -from tqdm import tqdm -import numpy as np - -generation_data_collator = DataCollatorForSeq2Seq( - tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=320 -) -tf_generate_dataset = model.prepare_tf_dataset( - tokenized_datasets["validation"], - collate_fn=generation_data_collator, - shuffle=False, - batch_size=8, - drop_remainder=True, -) - - -@tf.function(jit_compile=True) -def generate_with_xla(batch): - return model.generate( - input_ids=batch["input_ids"], - attention_mask=batch["attention_mask"], - max_new_tokens=32, - ) - - -all_preds = [] -all_labels = [] -for batch, labels in tqdm(tf_generate_dataset): - predictions = generate_with_xla(batch) - decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) - labels = 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 assembleur 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 planificateur 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 planificateur de taux d'apprentissage, nous utiliserons le planificateur 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 : + +
+Word count distributions for the review titles and texts. + +
+ +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 ! + +
+Different tasks performed by the T5 architecture. + +
+ +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, + ) + 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 assembleur 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 assembleur `DataCollatorForSeq2Seq` qui rembourrera dynamiquement les entrées et les étiquettes pour nous. Pour instancier ce assembleur, 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 assembleur 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 assembleur ne saura pas comment remplir ces éléments : + +```python +tokenized_datasets = tokenized_datasets.remove_columns( + books_dataset["train"].column_names +) +``` + +Comme le assembleur 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 assembleur 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 assembleur 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 = model.prepare_tf_dataset( + tokenized_datasets["train"], + collate_fn=data_collator, + shuffle=True, + batch_size=8, +) +tf_eval_dataset = model.prepare_tf_dataset( + tokenized_datasets["validation"], + 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 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 une liste d'étiquettes et une liste de prédictions pour la métrique ROUGE pour comparer (notez que si vous obtenez des erreurs d'importation pour cette section, vous pouvez avoir besoin de faire `pip install tqdm`). Nous allons également utiliser une astuce qui augmente considérablement les performances : compiler notre code de génération avec [XLA](https://www.tensorflow.org/xla), le compilateur d'algèbre linéaire accéléré de TensorFlow. XLA applique diverses optimisations au graphe de calcul du modèle, ce qui permet d'améliorer considérablement la vitesse et l'utilisation de la mémoire. Comme décrit dans un article du [blog d’Hugging Face](https://huggingface.co/blog/tf-xla-generate), XLA fonctionne mieux lorsque nos formes d'entrée ne varient pas trop. Pour gérer cela, nous allons rembourrer nos entrées à des multiples de 128, et créer un nouveau jeu de données avec l’assembleur de rembourrage. Puis nous appliquerons le décorateur `@tf.function(jit_compile=True)` à notre fonction de génération, qui marque la fonction entière pour la compilation avec XLA. + +```python +from tqdm import tqdm +import numpy as np + +generation_data_collator = DataCollatorForSeq2Seq( + tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=320 +) +tf_generate_dataset = model.prepare_tf_dataset( + tokenized_datasets["validation"], + collate_fn=generation_data_collator, + shuffle=False, + batch_size=8, + drop_remainder=True, +) + + +@tf.function(jit_compile=True) +def generate_with_xla(batch): + return model.generate( + input_ids=batch["input_ids"], + attention_mask=batch["attention_mask"], + max_new_tokens=32, + ) + + +all_preds = [] +all_labels = [] +for batch, labels in tqdm(tf_generate_dataset): + predictions = generate_with_xla(batch) + decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) + labels = 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 assembleur 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 planificateur 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 planificateur de taux d'apprentissage, nous utiliserons le planificateur 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 0ff1ce37b..f73f51eea 100644 --- a/chapters/fr/chapter7/6.mdx +++ b/chapters/fr/chapter7/6.mdx @@ -31,8 +31,8 @@ Dans cette section, nous allons construire une version réduite d'un modèle de 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. diff --git a/chapters/fr/chapter7/7.mdx b/chapters/fr/chapter7/7.mdx index 5ba430b64..885a0b126 100644 --- a/chapters/fr/chapter7/7.mdx +++ b/chapters/fr/chapter7/7.mdx @@ -28,8 +28,8 @@ Il est temps de s'intéresser à la réponse aux questions ! Cette tâche peut p 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) diff --git a/chapters/fr/chapter9/2.mdx b/chapters/fr/chapter9/2.mdx index c2579c3f4..a25253a1a 100644 --- a/chapters/fr/chapter9/2.mdx +++ b/chapters/fr/chapter9/2.mdx @@ -37,7 +37,7 @@ Parcourons le code ci-dessus : Si vous exécutez ce code, l'interface ci-dessous apparaîtra automatiquement dans un *notebook* Jupyter/Colab ou dans un navigateur sur **[http://localhost:7860](http://localhost:7860/)** si vous l'exécutez à partir d'un script. - + Essayez d'utiliser cette interface maintenant avec votre propre nom ou une autre entrée ! @@ -60,7 +60,7 @@ textbox = gr.Textbox(label="Type your name here:", placeholder="John Doe", lines gr.Interface(fn=greet, inputs=textbox, outputs="text").launch() ``` - + Ici, nous avons créé une zone de texte d'entrée avec une étiquette, un espace réservé et un nombre de lignes défini. Vous pourriez faire la même chose pour la zone de texte de sortie, mais nous allons laisser cela pour le moment. @@ -112,6 +112,6 @@ gr.Interface(fn=predict, inputs="text", outputs="text").launch() C'est fait ! Vous pouvez maintenant utiliser cette interface pour générer du texte en utilisant le modèle GPT-2 comme indiqué ci-dessous 🤯. - + Continuez votre lecture du cours pour voir comment construire d'autres types de démos avec *Gradio* ! \ No newline at end of file diff --git a/chapters/fr/chapter9/3.mdx b/chapters/fr/chapter9/3.mdx index 31e19722e..bede014c7 100644 --- a/chapters/fr/chapter9/3.mdx +++ b/chapters/fr/chapter9/3.mdx @@ -59,7 +59,7 @@ gr.Interface(reverse_audio, mic, "audio").launch() Le code ci-dessus produira une interface comme celle qui suit (si votre navigateur ne vous demande pas l'autorisation pour accéder au microphone, ouvrez la démo dans un onglet séparé). - + Vous devriez maintenant être capable d'enregistrer votre voix et de vous entendre parler à l'envers. Effrayant 👻 ! @@ -104,7 +104,7 @@ gr.Interface( ).launch() ``` - + ### La méthode `launch()` @@ -159,7 +159,7 @@ gr.Interface( Si votre navigateur ne vous demande pas l'autorisation pour accéder au microphone, ouvrez la démo dans un onglet séparé. - + Voilà, c'est fait ! Vous pouvez maintenant utiliser cette interface pour transcrire de l'audio. Remarquez ici qu'en passant le paramètre `optional` à `True`, nous permettons à l'utilisateur de soit fournir un microphone ou un fichier audio (ou aucun des deux, mais cela retournera un message d'erreur). diff --git a/chapters/fr/chapter9/4.mdx b/chapters/fr/chapter9/4.mdx index 2065d3589..125195301 100644 --- a/chapters/fr/chapter9/4.mdx +++ b/chapters/fr/chapter9/4.mdx @@ -33,7 +33,7 @@ description = """ The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything! # Le robot a été entraîné à répondre à des questions basées sur les dialogues de Rick et Morty. # Demandez à Rick ce que vous voulez ! - + """ article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of." @@ -53,7 +53,7 @@ gr.Interface( En utilisant les options ci-dessus, nous obtenons une interface plus complète. Essayez l'interface ci-dessous : - + ### Partager votre démo avec des liens temporaires Maintenant que nous avons une démo fonctionnelle de notre modèle d'apprentissage automatique, apprenons à partager facilement un lien vers notre interface. @@ -135,7 +135,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + Remarquez le paramètre `live=True` dans `Interface`, qui signifie que la démo de sketchs fait une prédiction chaque fois que quelqu'un dessine sur le bloc (pas de bouton de soumission !). diff --git a/chapters/fr/chapter9/5.mdx b/chapters/fr/chapter9/5.mdx index b6126213b..228eee18f 100644 --- a/chapters/fr/chapter9/5.mdx +++ b/chapters/fr/chapter9/5.mdx @@ -70,6 +70,6 @@ gr.Interface.load( ).launch() ``` - + Maintenant que nous avons exploré quelques façons d'intégrer *Gradio* avec le *Hub*, jetons un coup d'oeil à certaines fonctionnalités avancées de la classe `Interface`. C'est le sujet de la prochaine section ! \ No newline at end of file diff --git a/chapters/fr/chapter9/6.mdx b/chapters/fr/chapter9/6.mdx index 69e645d1c..8c89be5e9 100644 --- a/chapters/fr/chapter9/6.mdx +++ b/chapters/fr/chapter9/6.mdx @@ -51,7 +51,7 @@ iface = gr.Interface( iface.launch() ``` - + Remarquez comment l'état du composant de sortie persiste entre les soumissions. Remarque : vous pouvez transmettre une valeur par défaut au paramètre state, qui est utilisée comme valeur initiale de l'état. @@ -133,6 +133,6 @@ gr.Interface( ).launch(auth=("admin", "pass1234")) ``` - + Ceci conclut notre plongée dans la classe `Interface` de *Gradio*. Comme nous l'avons vu, cette classe permet de créer facilement des démos d'apprentissage automatique en quelques lignes de code Python. Cependant, vous voudrez parfois personnaliser votre démo en changeant la mise en page ou en enchaînant plusieurs fonctions de prédiction. Ne serait-il pas agréable de pouvoir diviser l'interface en blocs personnalisables ? Heureusement, c'est possible ! C'est le sujet de la dernière section. diff --git a/chapters/fr/chapter9/7.mdx b/chapters/fr/chapter9/7.mdx index c199b347d..963b89711 100644 --- a/chapters/fr/chapter9/7.mdx +++ b/chapters/fr/chapter9/7.mdx @@ -56,7 +56,7 @@ with demo: demo.launch() ``` - + Ce simple exemple ci-dessus introduit 4 concepts qui sous-tendent les *Blocks* : @@ -125,7 +125,7 @@ with demo: demo.launch() ``` - + Vous remarquerez que dans cet exemple, nous avons également créé un composant `Button` dans chaque onglet et avons assigné un événement de clic à chaque bouton qui est l'élément qui exécute réellement la fonction. @@ -164,7 +164,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### Création de démos multi-étapes @@ -204,7 +204,7 @@ with demo: demo.launch() ``` - + ### Mise à jour des propriétés des composants @@ -235,6 +235,6 @@ with gr.Blocks() as block: block.launch() ``` - + Nous venons d'explorer tous les concepts de base des `Blocks` ! Tout comme avec `Interface`, vous pouvez créer des démos sympas qui peuvent être partagées en utilisant `share=True` dans la méthode `launch()` ou déployées sur [*Spaces*](https://huggingface.co/spaces). \ No newline at end of file diff --git a/chapters/fr/events/1.mdx b/chapters/fr/events/1.mdx index 1526edf81..9f9cbbcf2 100644 --- a/chapters/fr/events/1.mdx +++ b/chapters/fr/events/1.mdx @@ -1,49 +1,49 @@ -# Sessions en direct et ateliers - -Pour la parution des parties 1 et 2 du cours, nous avons organisé plusieurs sessions et ateliers de codage en direct. Vous trouverez ci-dessous les liens vers les enregistrements de ces sessions et ateliers. - -## Sessions de codage en direct - -Lors de la première session, Sylvain parcourt avec vous le chapitre 1 du cours, en l'expliquant étape par étape : - -
- -
- -Lors de la deuxième session, c'est au tour de Lewis de présenter le chapitre 2 : - -
- -
- -Parce que le chapitre 2 est tellement cool, Sylvain a également fourni une présentation de ce chapitre ! - -
- -
- -Pour le chapitre 3, Lewis revient pour vous guider dans le code : - -
- -
- -Enfin, Omar conclut les sessions en direct liées à la première partie du cours en abordant le chapitre 4 : - -
- -
- -## Ateliers - -Dans le premier atelier, Merve accueille Lewis pour discuter de la section 7 du chapitre 7 sur le [*question answering*]( https://huggingface.co/course/chapter7/7?fw=pt). - -
- -
- -Pour le deuxième atelier, Merve reçoit Leandro pour parler de la section 6 du chapitre 7 sur [entraîner un modèle de langage causal à partir de zéro]( https://huggingface.co/course/chapter7/6?fw=pt) avec une application avec [CodeParrot](https://huggingface.co/codeparrot). - -
- -
+# Sessions en direct et ateliers + +Pour la parution des parties 1 et 2 du cours, nous avons organisé plusieurs sessions et ateliers de codage en direct. Vous trouverez ci-dessous les liens vers les enregistrements de ces sessions et ateliers. + +## Sessions de codage en direct + +Lors de la première session, Sylvain parcourt avec vous le chapitre 1 du cours, en l'expliquant étape par étape : + +
+ +
+ +Lors de la deuxième session, c'est au tour de Lewis de présenter le chapitre 2 : + +
+ +
+ +Parce que le chapitre 2 est tellement cool, Sylvain a également fourni une présentation de ce chapitre ! + +
+ +
+ +Pour le chapitre 3, Lewis revient pour vous guider dans le code : + +
+ +
+ +Enfin, Omar conclut les sessions en direct liées à la première partie du cours en abordant le chapitre 4 : + +
+ +
+ +## Ateliers + +Dans le premier atelier, Merve accueille Lewis pour discuter de la section 7 du chapitre 7 sur le [*question answering*]( https://huggingface.co/course/chapter7/7?fw=pt). + +
+ +
+ +Pour le deuxième atelier, Merve reçoit Leandro pour parler de la section 6 du chapitre 7 sur [entraîner un modèle de langage causal à partir de zéro]( https://huggingface.co/course/chapter7/6?fw=pt) avec une application avec [CodeParrot](https://huggingface.co/codeparrot). + +
+ +
diff --git a/chapters/hi/chapter0/1.mdx b/chapters/hi/chapter0/1.mdx index 9377795e8..6a9490ee0 100644 --- a/chapters/hi/chapter0/1.mdx +++ b/chapters/hi/chapter0/1.mdx @@ -1,110 +1,110 @@ -# परिचय - -हगिंग फेस में आपका स्वागत है! यह परिचय कार्य वातावरण स्थापित करने में आपका मार्गदर्शन करेगा। यदि आप अभी पाठ्यक्रम शुरू कर रहे हैं, तो हम अनुशंसा करते हैं कि आप पहले [अध्याय 1](course/chapter1) पर एक नज़र डालें, फिर वापस आएं और अपना वातावरण सेट करें ताकि आप कोड को स्वयं आज़मा सकें। - -इस पाठ्यक्रम में हम जिन सभी पुस्तकालयों का उपयोग करेंगे, वे पायथन पैकेज के रूप में उपलब्ध हैं, इसलिए यहां हम आपको दिखाएंगे कि पायथन वातावरण कैसे स्थापित करें और विशिष्ट पुस्तकालयों को स्थापित करें जिनकी आपको आवश्यकता होगी। - -हम आपके कार्य परिवेश को स्थापित करने के दो तरीकों को कवर करेंगे, एक Colab नोटबुक या एक पायथन आभासी वातावरण का उपयोग करके। बेझिझक वह चुनें जो आपके साथ सबसे अधिक प्रतिध्वनित हो। शुरुआती लोगों के लिए, हम दृढ़ता से अनुशंसा करते हैं कि आप Colab नोटबुक का उपयोग करके शुरुआत करें। - -ध्यान दें कि हम विंडोज सिस्टम को कवर नहीं करेंगे। यदि आप Windows पर चल रहे हैं, तो हम अनुशंसा करते हैं कि Colab नोटबुक का उपयोग करने के साथ-साथ अनुसरण करें। यदि आप Linux वितरण या macOS का उपयोग कर रहे हैं, तो आप यहाँ वर्णित किसी भी दृष्टिकोण का उपयोग कर सकते हैं। - -अधिकांश पाठ्यक्रम आपके हगिंग फेस खाते पर निर्भर करता है। हम अभी एक बनाने की सलाह देते हैं: [एक खाता बनाएँ](https://huggingface.co/join)। - -## Google Colab नोटबुक का उपयोग करना - -Colab नोटबुक का उपयोग करना सबसे आसान संभव सेटअप है; अपने ब्राउज़र में एक नोटबुक बूट करें और सीधे कोडिंग पर जाएं! - -यदि आप Colab से परिचित नहीं हैं, तो हम अनुशंसा करते हैं कि आप [परिचय](https://colab.research.google.com/notebooks/intro.ipynb) का पालन करके शुरुआत करें। Colab आपको GPU या TPU जैसे कुछ त्वरित हार्डवेयर का उपयोग करने की अनुमति देता है, और यह छोटे कार्यभार के लिए मुफ़्त है। - -एक बार जब आप Colab में घूमने में सहज हो जाएं, तो एक नई नोटबुक बनाएं और स्थापना के साथ आरंभ करें: -
- एक खाली Colab नोटबुक -
- -अगला चरण उन पुस्तकालयों को स्थापित करना है जिनका हम इस पाठ्यक्रम में उपयोग करेंगे। हम स्थापना के लिए `pip` का उपयोग करेंगे, जो कि पायथन के लिए पैकेज मैनेजर है। नोटबुक्स में, आप `!` वर्ण से पहले सिस्टम कमांड चला सकते हैं, इसलिए आप ट्रान्सफ़ॉर्मर लाइब्रेरी को निम्नानुसार स्थापित कर सकते हैं: -अगला चरण उन पुस्तकालयों को स्थापित करना है जिनका हम इस पाठ्यक्रम में उपयोग करेंगे। हम स्थापना के लिए `pip` का उपयोग करेंगे, जो कि पायथन के लिए पैकेज मैनेजर है। नोटबुक्स में, आप `!` वर्ण से पहले सिस्टम कमांड चला सकते हैं, इसलिए आप 🤗 ट्रान्सफ़ॉर्मर लाइब्रेरी को निम्नानुसार स्थापित कर सकते हैं: - -``` -!pip install transformers -``` - -आप यह सुनिश्चित कर सकते हैं कि पैकेज आपके पायथन रनटाइम के भीतर आयात करके सही ढंग से स्थापित किया गया है: - -``` -import transformers -``` - -
- उपरोक्त दो आदेशों का परिणाम दिखाने वाला एक GIF: स्थापना और आयात -
- -यह 🤗 ट्रांसफॉर्मर का एक बहुत हल्का संस्करण स्थापित करता है। विशेष रूप से, कोई विशिष्ट मशीन लर्निंग फ्रेमवर्क (जैसे PyTorch या TensorFlow) स्थापित नहीं हैं। चूंकि हम पुस्तकालय की कई अलग-अलग विशेषताओं का उपयोग करेंगे, हम विकास संस्करण को स्थापित करने की सलाह देते हैं, जो किसी भी कल्पनाशील उपयोग के मामले के लिए सभी आवश्यक निर्भरताओं के साथ आता है: - -``` -!pip install transformers[sentencepiece] -``` - -इसमें थोड़ा समय लगेगा, लेकिन फिर आप बाकी पाठ्यक्रम के लिए तैयार हो जाएंगे। - -## पायथन आभासी वातावरण का उपयोग करना - -यदि आप एक पायथन आभासी वातावरण का उपयोग करना पसंद करते हैं, तो पहला कदम आपके सिस्टम पर पायथन को स्थापित करना है। हम आरंभ करने के लिए [इस गाइड](https://realpython.com/installing-python/) का पालन करने की सलाह देते हैं। - -एक बार जब आप पायथन स्थापित कर लेते हैं, तो आपको अपने टर्मिनल में पायथन आदेश चलाने में सक्षम होना चाहिए। अगले चरण पर आगे बढ़ने से पहले यह सुनिश्चित करने के लिए कि यह सही ढंग से स्थापित है, आप निम्न आदेश चलाकर प्रारंभ कर सकते हैं: `python --version`. यह आपके सिस्टम पर अब उपलब्ध पायथन संस्करण को प्रिंट करना चाहिए। - -अपने टर्मिनल में पायथन आदेश चलाते समय, जैसे `python --version` आदेश को चलाने वाले प्रोग्राम को अपने सिस्टम में "main" पायथन के रूप में सोचना चाहिए। हम अनुशंसा करते हैं कि इस मुख्य स्थापना को किसी भी पैकेज से मुक्त रखें, और इसका उपयोग प्रत्येक एप्लिकेशन के लिए अलग वातावरण बनाने के लिए करें, जिस पर आप काम कर रहे हैं - इस तरह, प्रत्येक एप्लिकेशन की अपनी निर्भरताएं और पैकेज होंगे, और आपको अन्य एप्लिकेशन के साथ संभावित संगतता समस्याओं के बारे में चिंता करने की आवश्यकता नहीं होगी। - -पायथन में यह [आभासी वातावरण](https://docs.python.org/3/tutorial/venv.html) के साथ किया जाता है, जो स्व-निहित निर्देशिका ट्री हैं जिनमें से प्रत्येक में एक विशेष पायथन संस्करण के साथ एक पायथन स्थापना होती है, जिसमें सभी पैकेजों के साथ एप्लिकेशन की आवश्यकता होती है। इस तरह के आभासी वातावरण का निर्माण कई अलग-अलग उपकरणों के साथ किया जा सकता है, लेकिन हम उस उद्देश्य के लिए आधिकारिक पायथन पैकेज का उपयोग करेंगे, जिसे कहा जाता है [`venv`](https://docs.python.org/3/library/venv.html#module-venv)। - -सबसे पहले, एक निर्देशिका बनाएं जिसमें आप अपने आवेदन में रहना चाहते हैं - उदाहरण के लिए, आप अपनी होम निर्देशिका के मूल में *transformers-course* नामक एक नई निर्देशिका बनाना चाहेंगे: - -``` -mkdir ~/transformers-course -cd ~/transformers-course -``` - -इस निर्देशिका के अंदर, पायथन `venv` मॉड्यूल का उपयोग करके एक आभासी वातावरण बनाएं: - -``` -python3 -m venv .env -``` - -अब आपके पास आपके अन्यथा खाली फ़ोल्डर में *.env* नामक एक निर्देशिका होनी चाहिए: - -``` -ls -a -``` - -```out -. .. .env -``` - -आप 'activate' और 'deactivate' स्क्रिप्ट के साथ अपने आभासी वातावरण में और बाहर कूद सकते हैं: - -``` -# Activate the virtual environment -source .env/bin/activate - -# Deactivate the virtual environment -source .env/bin/deactivate -``` - -आप यह सुनिश्चित कर सकते हैं कि `which python` आदेश चलाकर कौन सा पर्यावरण सक्रिय है: यदि यह आभासी वातावरण की ओर इशारा करता है, तो आपने इसे सफलतापूर्वक सक्रिय कर दिया है! - -``` -which python -``` - -```out -/home//transformers-course/.env/bin/python -``` - -## निर्भरता स्थापित करना - -Google Colab इंस्टेंस का उपयोग करने पर पिछले अनुभाग की तरह, अब आपको जारी रखने के लिए आवश्यक पैकेजों को स्थापित करने की आवश्यकता होगी। फिर से, आप `pip` पैकेज मैनेजर का उपयोग करके 🤗 ट्रांसफॉर्मर के विकास संस्करण को स्थापित कर सकते हैं: - -``` -pip install "transformers[sentencepiece]" -``` - -अब आप पूरी तरह से तैयार हैं! +# परिचय + +हगिंग फेस में आपका स्वागत है! यह परिचय कार्य वातावरण स्थापित करने में आपका मार्गदर्शन करेगा। यदि आप अभी पाठ्यक्रम शुरू कर रहे हैं, तो हम अनुशंसा करते हैं कि आप पहले [अध्याय 1](course/chapter1) पर एक नज़र डालें, फिर वापस आएं और अपना वातावरण सेट करें ताकि आप कोड को स्वयं आज़मा सकें। + +इस पाठ्यक्रम में हम जिन सभी पुस्तकालयों का उपयोग करेंगे, वे पायथन पैकेज के रूप में उपलब्ध हैं, इसलिए यहां हम आपको दिखाएंगे कि पायथन वातावरण कैसे स्थापित करें और विशिष्ट पुस्तकालयों को स्थापित करें जिनकी आपको आवश्यकता होगी। + +हम आपके कार्य परिवेश को स्थापित करने के दो तरीकों को कवर करेंगे, एक Colab नोटबुक या एक पायथन आभासी वातावरण का उपयोग करके। बेझिझक वह चुनें जो आपके साथ सबसे अधिक प्रतिध्वनित हो। शुरुआती लोगों के लिए, हम दृढ़ता से अनुशंसा करते हैं कि आप Colab नोटबुक का उपयोग करके शुरुआत करें। + +ध्यान दें कि हम विंडोज सिस्टम को कवर नहीं करेंगे। यदि आप Windows पर चल रहे हैं, तो हम अनुशंसा करते हैं कि Colab नोटबुक का उपयोग करने के साथ-साथ अनुसरण करें। यदि आप Linux वितरण या macOS का उपयोग कर रहे हैं, तो आप यहाँ वर्णित किसी भी दृष्टिकोण का उपयोग कर सकते हैं। + +अधिकांश पाठ्यक्रम आपके हगिंग फेस खाते पर निर्भर करता है। हम अभी एक बनाने की सलाह देते हैं: [एक खाता बनाएँ](https://huggingface.co/join)। + +## Google Colab नोटबुक का उपयोग करना + +Colab नोटबुक का उपयोग करना सबसे आसान संभव सेटअप है; अपने ब्राउज़र में एक नोटबुक बूट करें और सीधे कोडिंग पर जाएं! + +यदि आप Colab से परिचित नहीं हैं, तो हम अनुशंसा करते हैं कि आप [परिचय](https://colab.research.google.com/notebooks/intro.ipynb) का पालन करके शुरुआत करें। Colab आपको GPU या TPU जैसे कुछ त्वरित हार्डवेयर का उपयोग करने की अनुमति देता है, और यह छोटे कार्यभार के लिए मुफ़्त है। + +एक बार जब आप Colab में घूमने में सहज हो जाएं, तो एक नई नोटबुक बनाएं और स्थापना के साथ आरंभ करें: +
+ एक खाली Colab नोटबुक +
+ +अगला चरण उन पुस्तकालयों को स्थापित करना है जिनका हम इस पाठ्यक्रम में उपयोग करेंगे। हम स्थापना के लिए `pip` का उपयोग करेंगे, जो कि पायथन के लिए पैकेज मैनेजर है। नोटबुक्स में, आप `!` वर्ण से पहले सिस्टम कमांड चला सकते हैं, इसलिए आप ट्रान्सफ़ॉर्मर लाइब्रेरी को निम्नानुसार स्थापित कर सकते हैं: +अगला चरण उन पुस्तकालयों को स्थापित करना है जिनका हम इस पाठ्यक्रम में उपयोग करेंगे। हम स्थापना के लिए `pip` का उपयोग करेंगे, जो कि पायथन के लिए पैकेज मैनेजर है। नोटबुक्स में, आप `!` वर्ण से पहले सिस्टम कमांड चला सकते हैं, इसलिए आप 🤗 ट्रान्सफ़ॉर्मर लाइब्रेरी को निम्नानुसार स्थापित कर सकते हैं: + +``` +!pip install transformers +``` + +आप यह सुनिश्चित कर सकते हैं कि पैकेज आपके पायथन रनटाइम के भीतर आयात करके सही ढंग से स्थापित किया गया है: + +``` +import transformers +``` + +
+ उपरोक्त दो आदेशों का परिणाम दिखाने वाला एक GIF: स्थापना और आयात +
+ +यह 🤗 ट्रांसफॉर्मर का एक बहुत हल्का संस्करण स्थापित करता है। विशेष रूप से, कोई विशिष्ट मशीन लर्निंग फ्रेमवर्क (जैसे PyTorch या TensorFlow) स्थापित नहीं हैं। चूंकि हम पुस्तकालय की कई अलग-अलग विशेषताओं का उपयोग करेंगे, हम विकास संस्करण को स्थापित करने की सलाह देते हैं, जो किसी भी कल्पनाशील उपयोग के मामले के लिए सभी आवश्यक निर्भरताओं के साथ आता है: + +``` +!pip install transformers[sentencepiece] +``` + +इसमें थोड़ा समय लगेगा, लेकिन फिर आप बाकी पाठ्यक्रम के लिए तैयार हो जाएंगे। + +## पायथन आभासी वातावरण का उपयोग करना + +यदि आप एक पायथन आभासी वातावरण का उपयोग करना पसंद करते हैं, तो पहला कदम आपके सिस्टम पर पायथन को स्थापित करना है। हम आरंभ करने के लिए [इस गाइड](https://realpython.com/installing-python/) का पालन करने की सलाह देते हैं। + +एक बार जब आप पायथन स्थापित कर लेते हैं, तो आपको अपने टर्मिनल में पायथन आदेश चलाने में सक्षम होना चाहिए। अगले चरण पर आगे बढ़ने से पहले यह सुनिश्चित करने के लिए कि यह सही ढंग से स्थापित है, आप निम्न आदेश चलाकर प्रारंभ कर सकते हैं: `python --version`. यह आपके सिस्टम पर अब उपलब्ध पायथन संस्करण को प्रिंट करना चाहिए। + +अपने टर्मिनल में पायथन आदेश चलाते समय, जैसे `python --version` आदेश को चलाने वाले प्रोग्राम को अपने सिस्टम में "main" पायथन के रूप में सोचना चाहिए। हम अनुशंसा करते हैं कि इस मुख्य स्थापना को किसी भी पैकेज से मुक्त रखें, और इसका उपयोग प्रत्येक एप्लिकेशन के लिए अलग वातावरण बनाने के लिए करें, जिस पर आप काम कर रहे हैं - इस तरह, प्रत्येक एप्लिकेशन की अपनी निर्भरताएं और पैकेज होंगे, और आपको अन्य एप्लिकेशन के साथ संभावित संगतता समस्याओं के बारे में चिंता करने की आवश्यकता नहीं होगी। + +पायथन में यह [आभासी वातावरण](https://docs.python.org/3/tutorial/venv.html) के साथ किया जाता है, जो स्व-निहित निर्देशिका ट्री हैं जिनमें से प्रत्येक में एक विशेष पायथन संस्करण के साथ एक पायथन स्थापना होती है, जिसमें सभी पैकेजों के साथ एप्लिकेशन की आवश्यकता होती है। इस तरह के आभासी वातावरण का निर्माण कई अलग-अलग उपकरणों के साथ किया जा सकता है, लेकिन हम उस उद्देश्य के लिए आधिकारिक पायथन पैकेज का उपयोग करेंगे, जिसे कहा जाता है [`venv`](https://docs.python.org/3/library/venv.html#module-venv)। + +सबसे पहले, एक निर्देशिका बनाएं जिसमें आप अपने आवेदन में रहना चाहते हैं - उदाहरण के लिए, आप अपनी होम निर्देशिका के मूल में *transformers-course* नामक एक नई निर्देशिका बनाना चाहेंगे: + +``` +mkdir ~/transformers-course +cd ~/transformers-course +``` + +इस निर्देशिका के अंदर, पायथन `venv` मॉड्यूल का उपयोग करके एक आभासी वातावरण बनाएं: + +``` +python3 -m venv .env +``` + +अब आपके पास आपके अन्यथा खाली फ़ोल्डर में *.env* नामक एक निर्देशिका होनी चाहिए: + +``` +ls -a +``` + +```out +. .. .env +``` + +आप 'activate' और 'deactivate' स्क्रिप्ट के साथ अपने आभासी वातावरण में और बाहर कूद सकते हैं: + +``` +# Activate the virtual environment +source .env/bin/activate + +# Deactivate the virtual environment +source .env/bin/deactivate +``` + +आप यह सुनिश्चित कर सकते हैं कि `which python` आदेश चलाकर कौन सा पर्यावरण सक्रिय है: यदि यह आभासी वातावरण की ओर इशारा करता है, तो आपने इसे सफलतापूर्वक सक्रिय कर दिया है! + +``` +which python +``` + +```out +/home//transformers-course/.env/bin/python +``` + +## निर्भरता स्थापित करना + +Google Colab इंस्टेंस का उपयोग करने पर पिछले अनुभाग की तरह, अब आपको जारी रखने के लिए आवश्यक पैकेजों को स्थापित करने की आवश्यकता होगी। फिर से, आप `pip` पैकेज मैनेजर का उपयोग करके 🤗 ट्रांसफॉर्मर के विकास संस्करण को स्थापित कर सकते हैं: + +``` +pip install "transformers[sentencepiece]" +``` + +अब आप पूरी तरह से तैयार हैं! diff --git a/chapters/it/chapter9/1.mdx b/chapters/it/chapter9/1.mdx index a478bea23..b1b73ae4b 100644 --- a/chapters/it/chapter9/1.mdx +++ b/chapters/it/chapter9/1.mdx @@ -20,15 +20,15 @@ Ecco alcuni esempi di demo di machine learning costruite con Gradio: * Un modello di **riconoscimento di disegni** che riceve uno schizzo di un disegno e restituisce il nome di ciò che pensa sia stato disegnato: - + * Un modello che estrae **risposte alle domande** che prende in considerazione un contesto e una domanda e produce una risposta e la sua probabilità (abbiamo discusso questo tipo di modello [nel capitolo 7](/course/chapter7/7)): - + * Un modello di **rimozione dello sfondo** che riceve un'immagine e la restituisce con lo sfondo rimosso: - + Questo capitolo è suddiviso in sezioni che comprendono sia _concetti_ che _applicazioni_. Dopo aver appreso il concetto in ogni sezione, lo applicherai per creare un particolare tipo di demo, dalla classificazione delle immagini al riconoscimento vocale. Al termine di questo capitolo, sarete in grado di creare queste demo (e molte altre!) con poche righe di Python. diff --git a/chapters/it/chapter9/2.mdx b/chapters/it/chapter9/2.mdx index 8ec5ce79e..8907fc06c 100644 --- a/chapters/it/chapter9/2.mdx +++ b/chapters/it/chapter9/2.mdx @@ -37,7 +37,7 @@ Analizziamo il codice qui sopra: Se si esegue questo codice, l'interfaccia qui sotto apparirà automaticamente all'interno di un Jupyter/Colab notebook, o comparirà in un browser **[http://localhost:7860](http://localhost:7860/)** se lanciato in uno script. - + Prova subito a utilizzare questa GUI con il tuo nome o con un altro input! @@ -62,7 +62,7 @@ textbox = gr.Textbox(label="Type your name here:", placeholder="John Doe", lines gr.Interface(fn=greet, inputs=textbox, outputs="text").launch() ``` - + Qui abbiamo creato una casella di testo di input con un'etichetta (`label`), un segnaposto (`placeholder`) e un numero di righe stabilito (`lines`). Si potrebbe fare lo stesso per la casella di testo in output, ma per ora ci fermiamo qui. @@ -113,6 +113,6 @@ gr.Interface(fn=predict, inputs="text", outputs="text").launch() Ecco fatto! Ora è possibile utilizzare questa interfaccia per generare testo utilizzando il modello GPT-2 come mostrato qui sotto 🤯. - + Continua a leggere per scoprire come costruire altri tipi di demo con Gradio! \ No newline at end of file diff --git a/chapters/it/chapter9/3.mdx b/chapters/it/chapter9/3.mdx index a36e67f20..9f4dbc7a4 100644 --- a/chapters/it/chapter9/3.mdx +++ b/chapters/it/chapter9/3.mdx @@ -71,7 +71,7 @@ gr.Interface(reverse_audio, mic, "audio").launch() Il codice precedente produrrà un'interfaccia come quella qui sotto (se il tuo browser non chiede il premesso per usare il microfono, apri il demo in una tab diversa.) - + A questo punto potresti registrare la tua voce e di sentirti parlare al contrario - spaventoso 👻! @@ -118,7 +118,7 @@ gr.Interface( ).launch() ``` - + ### Il metodo `launch()` @@ -176,7 +176,7 @@ gr.Interface( Se il tuo browser non ti chiede i permessi per il microfono, apri la demo in una scheda separata. - + Ecco fatto! Ora è possibile utilizzare questa interfaccia per trascrivere l'audio. Si osservi che diff --git a/chapters/ja/chapter7/2.mdx b/chapters/ja/chapter7/2.mdx index 5ad6ba398..7d235c0b7 100644 --- a/chapters/ja/chapter7/2.mdx +++ b/chapters/ja/chapter7/2.mdx @@ -32,7 +32,7 @@ もちろん、トークン分類問題には他にも多くの問題があり、これらは代表的な例に過ぎません。このセクションでは、NERタスクでモデル(BERT)を微調整し、以下のような予測計算ができるようにします。 - + One-hot encoded labels for question answering. diff --git a/chapters/ja/chapter7/3.mdx b/chapters/ja/chapter7/3.mdx index afdb30047..2e5ede2b7 100644 --- a/chapters/ja/chapter7/3.mdx +++ b/chapters/ja/chapter7/3.mdx @@ -36,7 +36,7 @@ Transformerモデルを含む多くのNLPアプリケーションでは、ハギ このセクションの終わりには、以下のような文章を自動補完できる[マスク言語モデル](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.)がHub上にできていることでしょう。 - + それでは始めましょう! diff --git a/chapters/ja/chapter7/4.mdx b/chapters/ja/chapter7/4.mdx index 11e5fb724..509fe100f 100644 --- a/chapters/ja/chapter7/4.mdx +++ b/chapters/ja/chapter7/4.mdx @@ -38,7 +38,7 @@ これが終われば、以下のような予測が可能なモデルが完成します。 - + One-hot encoded labels for question answering. diff --git a/chapters/ja/chapter7/5.mdx b/chapters/ja/chapter7/5.mdx index 8aa12a0b4..1c6d9e224 100644 --- a/chapters/ja/chapter7/5.mdx +++ b/chapters/ja/chapter7/5.mdx @@ -28,7 +28,7 @@ [ハギングフェイス ハブ](https://huggingface.co/models?pipeline_tag=summarization&sort=downloads)には、要約用に微調整されたさまざまなモデルがすでに存在しますが、これらのほとんどは英語のドキュメントにのみ適しています。 したがって、このセクションにひねりを加えるために、英語とスペイン語のバイリンガルモデルをトレーニングします。 このセクションの終わりまでに、ここに示すようなカスタマーレビューを要約できる[モデル](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es)ができあがります。 - + これから説明するように、これらの要約は、顧客が製品レビュー投稿時につけたタイトル文を使って学習されているため、簡潔です。 このタスクに適した多言語コーパスをまとめることから始めましょう。 diff --git a/chapters/ja/chapter7/6.mdx b/chapters/ja/chapter7/6.mdx index ded614470..34d0a71b6 100644 --- a/chapters/ja/chapter7/6.mdx +++ b/chapters/ja/chapter7/6.mdx @@ -30,7 +30,7 @@ [第6章](/course/ja/chapter6)では、Pythonソースコードを処理するための効率的なトークナイザーを作成しましたが、モデルを事前学習するためには、やはり大規模なデータセットが必要です。ここでは、GitHub リポジトリから得た Python コードのコーパスにトークナイザを適用します。そして、`Trainer` API と 🤗 Accelerate を使ってモデルを学習します。さあ、始めましょう - + これは実際に、このセクションで示したコードを使って学習し、ハブにアップロードしたモデルを紹介しているものです。[こちら](https://huggingface.co/huggingface-course/codeparrot-ds?text=plt.imshow%28)をご覧ください。なお、テキスト生成の際にランダム化が行われているので、おそらく少し異なる結果が得られると思います。 diff --git a/chapters/ja/chapter7/7.mdx b/chapters/ja/chapter7/7.mdx index a8aa16f24..4a1aaa4b6 100644 --- a/chapters/ja/chapter7/7.mdx +++ b/chapters/ja/chapter7/7.mdx @@ -28,7 +28,7 @@ 私達は、Wikipediaの記事に対してクラウドワーカーによって作成された質問からなる[SQuADデータセット](https://rajpurkar.github.io/SQuAD-explorer/)のBERTモデルを微調整する予定です。これにより、以下のような予測を実行できるモデルができるでしょう。 - + これは実際にこのセクションで示したコードを使って学習し、ハブにアップロードしたモデルを紹介しているものです。 貴方は[ここ](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)でモデルを見つけて、予測を再確認することができます。 diff --git a/chapters/vi/chapter7/2.mdx b/chapters/vi/chapter7/2.mdx index 619d3e932..5167b026f 100644 --- a/chapters/vi/chapter7/2.mdx +++ b/chapters/vi/chapter7/2.mdx @@ -51,7 +51,7 @@ Tất nhiên, có nhiều loại vấn đề phân loại token khác; đó chỉ là một vài ví dụ tiêu biểu. Trong phần này, chúng ta sẽ tinh chỉnh một mô hình (BERT) trên một tác vụ NER, sau đó sẽ có thể tính toán các dự đoán như sau: + Cùng đi sâu vào thôi! diff --git a/chapters/vi/chapter7/4.mdx b/chapters/vi/chapter7/4.mdx index c9a25dd1d..2456a58f6 100644 --- a/chapters/vi/chapter7/4.mdx +++ b/chapters/vi/chapter7/4.mdx @@ -35,7 +35,7 @@ Trong phần này, chúng ta sẽ tinh chỉnh mô hình Marian được huấn Sau khi hoàn thành, chúng ta sẽ có một mô hình có thể đưa ra các dự đoán như sau: - + One-hot encoded labels for question answering. diff --git a/chapters/vi/chapter7/5.mdx b/chapters/vi/chapter7/5.mdx index c72edb8d0..518c5217f 100644 --- a/chapters/vi/chapter7/5.mdx +++ b/chapters/vi/chapter7/5.mdx @@ -28,7 +28,7 @@ Trong phần này, chúng ta sẽ xem xét cách các mô hình Transformer có Mặc dù đã tồn tại nhiều mô hình được tinh chỉnh khác nhau để tóm tắt trên [Hugging Face Hub](https://huggingface.co/models?pipeline_tag=summarization&sort=downloads), hầu hết tất cả các mô hình này chỉ phù hợp với các tài liệu tiếng Anh. Vì vậy, để tạo thêm một điểm nhấn trong phần này, chúng tôi sẽ huấn luyện một mô hình song ngữ cho tiếng Anh và tiếng Tây Ban Nha. Đến cuối phần này, bạn sẽ có một [mô hình](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es) có thể tóm tắt các đánh giá của khách hàng như được hiển thị ở đây: - + Như chúng ta sẽ thấy, những bản tóm tắt này ngắn gọn vì chúng được học từ các tiêu đề mà khách hàng cung cấp trong các bài đánh giá sản phẩm của họ. Hãy bắt đầu bằng cách tập hợp một kho ngữ liệu song ngữ phù hợp cho tác vụ này. diff --git a/chapters/vi/chapter7/6.mdx b/chapters/vi/chapter7/6.mdx index 0dac41c13..0fc13dfd6 100644 --- a/chapters/vi/chapter7/6.mdx +++ b/chapters/vi/chapter7/6.mdx @@ -49,7 +49,7 @@ Trong phần này, chúng ta sẽ xây dựng một phiên bản thu nhỏ của Trong [Chương 6](/course/chapter6), chúng ta đã tạo một trình tokenize hiệu quả để xử lý mã nguồn Python, nhưng những gì chúng ta vẫn cần là một tập dữ liệu quy mô lớn để huấn luyện trước một mô hình. Ở đây, chúng ta sẽ áp dụng tokenizer cho một kho lưu trữ mã Python có nguồn gốc từ kho lưu trữ GitHub. Sau đó, chúng ta sẽ sử dụng API `Trainer` và 🤗 Accelerate để huấn luyện mô hình. Chúng ta hãy đi đến đó! + Đây thực sự cách mô hình đã được huấn luyện và tải lên Hub bằng cách sử dụng mã được hiển thị trong phần này. Bạn có thể tìm thấy nó và kiểm tra các dự đoạn [tại đây](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). diff --git a/chapters/vi/chapter9/1.mdx b/chapters/vi/chapter9/1.mdx index cf73b07a4..4bc7ab1a8 100644 --- a/chapters/vi/chapter9/1.mdx +++ b/chapters/vi/chapter9/1.mdx @@ -15,15 +15,15 @@ Dưới đây là một số ví dụ về demo học máy được xây dựng * Một mô hình **nhận dạng phác thảo** nhận bản phác thảo và xuất ra các nhãn của những gì nó cho là đang được vẽ: - + * Mô hình **hỏi đáp** khai thác lấy trong một đoạn ngữ cảnh và một câu hỏi và đưa ra một câu trả lời và điểm xác suất (chúng ta đã thảo luận về loại mô hình này [trong Chương 7](/course/chapter7/7)): - + * Một mô hình **xóa nền** nhận vào một hình ảnh và xuất ra hình ảnh với nền đã bị xóa: - + Chương này được chia thành các phần bao gồm cả _khái niệm_ và _ứng dụng_. Sau khi bạn tìm hiểu khái niệm trong mỗi phần, bạn sẽ áp dụng nó để xây dựng một loại bản demo cụ thể, từ phân loại hình ảnh đến nhận dạng giọng nói. Vào thời điểm bạn hoàn thành chương này, bạn sẽ có thể xây dựng các bản demo này (và nhiều hơn nữa!) Chỉ trong một vài dòng mã Python. diff --git a/chapters/vi/chapter9/2.mdx b/chapters/vi/chapter9/2.mdx index f70a32d2c..e71fd448d 100644 --- a/chapters/vi/chapter9/2.mdx +++ b/chapters/vi/chapter9/2.mdx @@ -37,7 +37,7 @@ Hãy xem qua đoạn mã trên: Nếu bạn chạy đoạn mã này, giao diện bên dưới sẽ tự động xuất hiện trong notebook Jupyter/Colab hoặc bật trong trình duyệt trên **[http://localhost:7860](http://localhost:7860/)** nếu đang chạy từ một tập lệnh. - + Hãy thử sử dụng GUI này ngay bây giờ với tên của chính bạn hoặc một số đầu vào khác! @@ -57,7 +57,7 @@ textbox = gr.Textbox(label="Type your name here:", placeholder="John Doe", lines gr.Interface(fn=greet, inputs=textbox, outputs="text").launch() ``` - + Ở đây, chúng ta đã tạo một hộp văn bản đầu vào với nhãn, trình giữ chỗ và một số dòng. Bạn có thể làm tương tự đối với hộp văn bản đầu ra, nhưng chúng ta sẽ để lại điều đó ngay bây giờ. @@ -104,6 +104,6 @@ gr.Interface(fn=predict, inputs="text", outputs="text").launch() Nó đó! Bây giờ bạn có thể sử dụng giao diện này để tạo văn bản bằng mô hình GPT-2 như hình bên dưới 🤯. - + Hãy tiếp tục đọc để biết cách tạo các loại demo khác với Gradio! diff --git a/chapters/vi/chapter9/3.mdx b/chapters/vi/chapter9/3.mdx index 1659fd788..79df19b79 100644 --- a/chapters/vi/chapter9/3.mdx +++ b/chapters/vi/chapter9/3.mdx @@ -1,164 +1,164 @@ -# Hiểu lớp Interface - - - -Trong phần này, chúng ta sẽ xem xét kỹ hơn về lớp `Interface` và hiểu các tham số chính được sử dụng để tạo ra nó. - -## Cách tạo một Interface - -Bạn sẽ nhận thấy rằng lớp `Interface` có 3 tham số bắt buộc: - -`Interface(fn, inputs, outputs, ...)` - -Các tham số này là: - - - `fn`: hàm dự đoán được bao bọc bởi giao diện Gradio. Hàm này có thể nhận một hoặc nhiều tham số và trả về một hoặc nhiều giá trị - - `inputs`: (các) loại thành phần đầu vào. Gradio cung cấp nhiều thành phần được tạo sẵn như`"image"` hay `"mic"`. - - `outputs`: (các) loại thành phần đầu ra. Một lần nữa, Gradio cung cấp nhiều thành phần được tạo sẵn, ví dụ: `"image"` hay `"label"`. - -Để có danh sách đầy đủ các thành phần, [xem tài liệu Gradio](https://gradio.app/docs). Mỗi thành phần được tạo sẵn có thể được tùy chỉnh bằng cách khởi tạo lớp tương ứng với thành phần. - -Ví dụ: như chúng ta đã thấy trong [phần trước](/course/chapter9/2), thay vì truyền tham số `input` vào trong `"textbox"`, bạn có thể truyền vào `Textbox(lines=7, label="Prompt")` để tạo một hộp văn bản có 7 dòng và một nhãn. - -Hãy xem một ví dụ khác, lần này với thành phần `Audio`. - -## Một ví dụ đơn giản với âm thanh - -Như đã đề cập trước đó, Gradio cung cấp nhiều đầu vào và đầu ra khác nhau. -Vì vậy, hãy xây dựng một `Interface` hoạt động với âm thanh. - -Trong ví dụ này, chúng tôi sẽ xây dựng một hàm chuyển đổi âm thanh sang âm thanh mà nhận tập tin âm thanh và chỉ cần đảo ngược nó. - -Chúng ta sẽ sử dụng thành phần `Audio` cho đầu vào. Khi sử dụng thành phần `Audio`, bạn có thể chỉ định xem bạn có muốn `source` của âm thanh là một tệp mà người dùng -tải lên hoặc micrô mà người dùng ghi lại giọng nói của họ. Trong trường hợp này, hãy đặt nó thành `"microphone"`. Chỉ cho vui thôi, chúng ta sẽ thêm một nhãn vào phần `Audio` của mình có nội dung "Speak here...", nghĩa là "Nói ở đây ...". - -Ngoài ra, chúng ta muốn nhận âm thanh dưới dạng mảng numpy để ta có thể dễ dàng "đảo ngược" nó lại. Vì vậy, chúng ta sẽ đặt `"type"` là `"numpy"`, chuyển đầu vào -dữ liệu dưới dạng một bộ (`sample_rate`, `data`) trong hàm của chúng ta. - -Chúng ta cũng sẽ sử dụng thành phần đầu ra `Audio` có thể tự động hiển thị một bộ tuple với tốc độ mẫu và mảng dữ liệu phức tạp dưới dạng tệp âm thanh có thể phát. Trong trường hợp này, chúng ta không cần thực hiện bất kỳ tùy chỉnh nào, vì vậy cta sẽ sử dụng chuỗi phím tắt `"audio"`. - - -```py -import numpy as np -import gradio as gr - - -def reverse_audio(audio): - sr, data = audio - reversed_audio = (sr, np.flipud(data)) - return reversed_audio - - -mic = gr.Audio(source="microphone", type="numpy", label="Speak here...") -gr.Interface(reverse_audio, mic, "audio").launch() -``` - -Đoạn mã trên sẽ tạo ra một giao diện giống như bên dưới (nếu trình duyệt của bạn không yêu cầu bạn cấp quyền đối với micrô, mở bản demo sang một tab khác.) - - - -Bây giờ bạn có thể ghi lại giọng nói của mình và nghe thấy chính mình đang nói ngược lại - thật ma quái 👻! - -## Xử lý nhiều đầu vào và đầu ra - -Giả sử chúng ta có một hàm phức tạp hơn, với nhiều đầu vào và đầu ra. Trong ví dụ dưới đây, chúng ta có một hàm lấy chỉ mục thả xuống, giá trị thanh trượt và số, và trả về một mẫu âm thanh của một giai điệu âm nhạc. - -Hãy xem cách chúng ta chuyển danh sách các thành phần đầu vào và đầu ra, và xem liệu bạn có thể theo dõi những gì đang xảy ra không. - -Chìa khóa ở đây là khi bạn truyền vào: -* danh sách các thành phần đầu vào, mỗi thành phần tương ứng với một tham số theo thứ tự. -* danh sách các thành phần đầu ra, mỗi thành phần tương ứng với một giá trị trả về. - -Đoạn mã bên dưới cho thấy cách ba thành phần đầu vào xếp hàng với ba tham số của hàm `generate_tone()`: - -```py -import numpy as np -import gradio as gr - -notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] - - -def generate_tone(note, octave, duration): - sr = 48000 - a4_freq, tones_from_a4 = 440, 12 * (octave - 4) + (note - 9) - frequency = a4_freq * 2 ** (tones_from_a4 / 12) - duration = int(duration) - audio = np.linspace(0, duration, duration * sr) - audio = (20000 * np.sin(audio * (2 * np.pi * frequency))).astype(np.int16) - return (sr, audio) - - -gr.Interface( - generate_tone, - [ - gr.Dropdown(notes, type="index"), - gr.Slider(minimum=4, maximum=6, step=1), - gr.Textbox(type="number", value=1, label="Duration in seconds"), - ], - "audio", -).launch() -``` - - - -### Phương thức `launch()` - -Cho đến nay, chúng tôi đã sử dụng phương thức `launch()` để khởi chạy giao diện, nhưng chúng ta chưa thực sự thảo luận về những gì nó làm. - -Theo mặc định, phương thức `launch()` sẽ khởi chạy bản demo trong một máy chủ web đang chạy cục bộ. Nếu bạn đang chạy mã của mình trong notebook Jupyter hoặc Colab, thì Gradio sẽ nhúng GUI demo vào notebook để bạn có thể dễ dàng sử dụng. - -Bạn có thể tùy chỉnh hành vi của `launch()` thông qua các tham số khác nhau: - - - `inline` - có hiển thị giao diện nội tuyến trên notebook Python hay không. - - `inbrowser` - có tự động khởi chạy giao diện trong tab mới trên trình duyệt mặc định hay không. - - `share` - có tạo một liên kết có thể chia sẻ công khai từ máy tính của bạn cho giao diện hay không. Giống như một liên kết Google Drive! - -Chúng tôi sẽ trình bày chi tiết hơn về tham số `share` trong phần tiếp theo! - -## ✏️ Hãy áp dụng nó! - -Hãy xây dựng một giao diện cho phép bạn giới thiệu mô hình **nhận dạng giọng nói**. Để làm cho nó thú vị, chúng ta sẽ chấp nhận hoặc đầu vào micrô hoặc một tệp đã tải lên. - -Như thường lệ, chúng ta sẽ tải mô hình nhận dạng giọng nói của mình bằng cách sử dụng hàm `pipeline()` từ 🤗 Transformers. -Nếu bạn cần cập nhật nhanh, bạn có thể quay lại [phần đó trong Chương 1](/course/chapter1/3). Tiếp theo, chúng ta sẽ triển khai một hàm `transcribe_audio()` để xử lý âm thanh và trả về phiên âm. Cuối cùng, chúng ta sẽ gói hàm này trong một `Interface` với các thành phần `Audio` cho đầu vào và chỉ văn bản cho đầu ra. Nhìn chung, mã cho ứng dụng này như sau: - -```py -from transformers import pipeline -import gradio as gr - -model = pipeline("automatic-speech-recognition") - - -def transcribe_audio(mic=None, file=None): - if mic is not None: - audio = mic - elif file is not None: - audio = file - else: - return "You must either provide a mic recording or a file" - transcription = model(audio)["text"] - return transcription - - -gr.Interface( - fn=transcribe_audio, - inputs=[ - gr.Audio(source="microphone", type="filepath", optional=True), - gr.Audio(source="upload", type="filepath", optional=True), - ], - outputs="text", -).launch() -``` - -Nếu trình duyệt của bạn không yêu cầu bạn cấp quyền đối với micrô, hãy mở bản demo trong một tab riêng. - - - -Nó đó! Bây giờ bạn có thể sử dụng giao diện này để phiên âm âm thanh. Chú ý ở đây rằng bằng cách đặt tham số `option` là `True`, chúng ta cho phép người dùng cung cấp micrô hoặc tệp âm thanh (hoặc không, nhưng điều đó sẽ trả lại thông báo lỗi). - -Tiếp tục xem cách chia sẻ giao diện của bạn với những người khác! +# Hiểu lớp Interface + + + +Trong phần này, chúng ta sẽ xem xét kỹ hơn về lớp `Interface` và hiểu các tham số chính được sử dụng để tạo ra nó. + +## Cách tạo một Interface + +Bạn sẽ nhận thấy rằng lớp `Interface` có 3 tham số bắt buộc: + +`Interface(fn, inputs, outputs, ...)` + +Các tham số này là: + + - `fn`: hàm dự đoán được bao bọc bởi giao diện Gradio. Hàm này có thể nhận một hoặc nhiều tham số và trả về một hoặc nhiều giá trị + - `inputs`: (các) loại thành phần đầu vào. Gradio cung cấp nhiều thành phần được tạo sẵn như`"image"` hay `"mic"`. + - `outputs`: (các) loại thành phần đầu ra. Một lần nữa, Gradio cung cấp nhiều thành phần được tạo sẵn, ví dụ: `"image"` hay `"label"`. + +Để có danh sách đầy đủ các thành phần, [xem tài liệu Gradio](https://gradio.app/docs). Mỗi thành phần được tạo sẵn có thể được tùy chỉnh bằng cách khởi tạo lớp tương ứng với thành phần. + +Ví dụ: như chúng ta đã thấy trong [phần trước](/course/chapter9/2), thay vì truyền tham số `input` vào trong `"textbox"`, bạn có thể truyền vào `Textbox(lines=7, label="Prompt")` để tạo một hộp văn bản có 7 dòng và một nhãn. + +Hãy xem một ví dụ khác, lần này với thành phần `Audio`. + +## Một ví dụ đơn giản với âm thanh + +Như đã đề cập trước đó, Gradio cung cấp nhiều đầu vào và đầu ra khác nhau. +Vì vậy, hãy xây dựng một `Interface` hoạt động với âm thanh. + +Trong ví dụ này, chúng tôi sẽ xây dựng một hàm chuyển đổi âm thanh sang âm thanh mà nhận tập tin âm thanh và chỉ cần đảo ngược nó. + +Chúng ta sẽ sử dụng thành phần `Audio` cho đầu vào. Khi sử dụng thành phần `Audio`, bạn có thể chỉ định xem bạn có muốn `source` của âm thanh là một tệp mà người dùng +tải lên hoặc micrô mà người dùng ghi lại giọng nói của họ. Trong trường hợp này, hãy đặt nó thành `"microphone"`. Chỉ cho vui thôi, chúng ta sẽ thêm một nhãn vào phần `Audio` của mình có nội dung "Speak here...", nghĩa là "Nói ở đây ...". + +Ngoài ra, chúng ta muốn nhận âm thanh dưới dạng mảng numpy để ta có thể dễ dàng "đảo ngược" nó lại. Vì vậy, chúng ta sẽ đặt `"type"` là `"numpy"`, chuyển đầu vào +dữ liệu dưới dạng một bộ (`sample_rate`, `data`) trong hàm của chúng ta. + +Chúng ta cũng sẽ sử dụng thành phần đầu ra `Audio` có thể tự động hiển thị một bộ tuple với tốc độ mẫu và mảng dữ liệu phức tạp dưới dạng tệp âm thanh có thể phát. Trong trường hợp này, chúng ta không cần thực hiện bất kỳ tùy chỉnh nào, vì vậy cta sẽ sử dụng chuỗi phím tắt `"audio"`. + + +```py +import numpy as np +import gradio as gr + + +def reverse_audio(audio): + sr, data = audio + reversed_audio = (sr, np.flipud(data)) + return reversed_audio + + +mic = gr.Audio(source="microphone", type="numpy", label="Speak here...") +gr.Interface(reverse_audio, mic, "audio").launch() +``` + +Đoạn mã trên sẽ tạo ra một giao diện giống như bên dưới (nếu trình duyệt của bạn không yêu cầu bạn cấp quyền đối với micrô, mở bản demo sang một tab khác.) + + + +Bây giờ bạn có thể ghi lại giọng nói của mình và nghe thấy chính mình đang nói ngược lại - thật ma quái 👻! + +## Xử lý nhiều đầu vào và đầu ra + +Giả sử chúng ta có một hàm phức tạp hơn, với nhiều đầu vào và đầu ra. Trong ví dụ dưới đây, chúng ta có một hàm lấy chỉ mục thả xuống, giá trị thanh trượt và số, và trả về một mẫu âm thanh của một giai điệu âm nhạc. + +Hãy xem cách chúng ta chuyển danh sách các thành phần đầu vào và đầu ra, và xem liệu bạn có thể theo dõi những gì đang xảy ra không. + +Chìa khóa ở đây là khi bạn truyền vào: +* danh sách các thành phần đầu vào, mỗi thành phần tương ứng với một tham số theo thứ tự. +* danh sách các thành phần đầu ra, mỗi thành phần tương ứng với một giá trị trả về. + +Đoạn mã bên dưới cho thấy cách ba thành phần đầu vào xếp hàng với ba tham số của hàm `generate_tone()`: + +```py +import numpy as np +import gradio as gr + +notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] + + +def generate_tone(note, octave, duration): + sr = 48000 + a4_freq, tones_from_a4 = 440, 12 * (octave - 4) + (note - 9) + frequency = a4_freq * 2 ** (tones_from_a4 / 12) + duration = int(duration) + audio = np.linspace(0, duration, duration * sr) + audio = (20000 * np.sin(audio * (2 * np.pi * frequency))).astype(np.int16) + return (sr, audio) + + +gr.Interface( + generate_tone, + [ + gr.Dropdown(notes, type="index"), + gr.Slider(minimum=4, maximum=6, step=1), + gr.Textbox(type="number", value=1, label="Duration in seconds"), + ], + "audio", +).launch() +``` + + + +### Phương thức `launch()` + +Cho đến nay, chúng tôi đã sử dụng phương thức `launch()` để khởi chạy giao diện, nhưng chúng ta chưa thực sự thảo luận về những gì nó làm. + +Theo mặc định, phương thức `launch()` sẽ khởi chạy bản demo trong một máy chủ web đang chạy cục bộ. Nếu bạn đang chạy mã của mình trong notebook Jupyter hoặc Colab, thì Gradio sẽ nhúng GUI demo vào notebook để bạn có thể dễ dàng sử dụng. + +Bạn có thể tùy chỉnh hành vi của `launch()` thông qua các tham số khác nhau: + + - `inline` - có hiển thị giao diện nội tuyến trên notebook Python hay không. + - `inbrowser` - có tự động khởi chạy giao diện trong tab mới trên trình duyệt mặc định hay không. + - `share` - có tạo một liên kết có thể chia sẻ công khai từ máy tính của bạn cho giao diện hay không. Giống như một liên kết Google Drive! + +Chúng tôi sẽ trình bày chi tiết hơn về tham số `share` trong phần tiếp theo! + +## ✏️ Hãy áp dụng nó! + +Hãy xây dựng một giao diện cho phép bạn giới thiệu mô hình **nhận dạng giọng nói**. Để làm cho nó thú vị, chúng ta sẽ chấp nhận hoặc đầu vào micrô hoặc một tệp đã tải lên. + +Như thường lệ, chúng ta sẽ tải mô hình nhận dạng giọng nói của mình bằng cách sử dụng hàm `pipeline()` từ 🤗 Transformers. +Nếu bạn cần cập nhật nhanh, bạn có thể quay lại [phần đó trong Chương 1](/course/chapter1/3). Tiếp theo, chúng ta sẽ triển khai một hàm `transcribe_audio()` để xử lý âm thanh và trả về phiên âm. Cuối cùng, chúng ta sẽ gói hàm này trong một `Interface` với các thành phần `Audio` cho đầu vào và chỉ văn bản cho đầu ra. Nhìn chung, mã cho ứng dụng này như sau: + +```py +from transformers import pipeline +import gradio as gr + +model = pipeline("automatic-speech-recognition") + + +def transcribe_audio(mic=None, file=None): + if mic is not None: + audio = mic + elif file is not None: + audio = file + else: + return "You must either provide a mic recording or a file" + transcription = model(audio)["text"] + return transcription + + +gr.Interface( + fn=transcribe_audio, + inputs=[ + gr.Audio(source="microphone", type="filepath", optional=True), + gr.Audio(source="upload", type="filepath", optional=True), + ], + outputs="text", +).launch() +``` + +Nếu trình duyệt của bạn không yêu cầu bạn cấp quyền đối với micrô, hãy mở bản demo trong một tab riêng. + + + +Nó đó! Bây giờ bạn có thể sử dụng giao diện này để phiên âm âm thanh. Chú ý ở đây rằng bằng cách đặt tham số `option` là `True`, chúng ta cho phép người dùng cung cấp micrô hoặc tệp âm thanh (hoặc không, nhưng điều đó sẽ trả lại thông báo lỗi). + +Tiếp tục xem cách chia sẻ giao diện của bạn với những người khác! diff --git a/chapters/vi/chapter9/4.mdx b/chapters/vi/chapter9/4.mdx index 0893954eb..bbe48eb07 100644 --- a/chapters/vi/chapter9/4.mdx +++ b/chapters/vi/chapter9/4.mdx @@ -31,7 +31,7 @@ Sử dụng các tùy chọn ở trên, chúng ta kết thúc với một giao d title = "Ask Rick a Question" description = """ The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything! - + """ article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of." @@ -49,7 +49,7 @@ gr.Interface( Sử dụng các tùy chọn ở trên, chúng ta kết thúc với một giao diện hoàn chỉnh hơn. Hãy thử giao diện bên dưới: - + ### Chia sẻ bản demo của bạn với các liên kết tạm thời @@ -131,7 +131,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + Lưu ý tham số `live=True` trong `Interface`, có nghĩa là bản demo phác thảo tạo ra dự đoán mỗi khi ai đó vẽ trên sketchpad (không có nút gửi!). diff --git a/chapters/vi/chapter9/5.mdx b/chapters/vi/chapter9/5.mdx index 8e793e25d..f3376e2a6 100644 --- a/chapters/vi/chapter9/5.mdx +++ b/chapters/vi/chapter9/5.mdx @@ -39,7 +39,7 @@ gr.Interface.load( Đoạn mã trên sẽ tạo ra giao diện bên dưới: - + Tải mô hình theo cách này sử dụng [API luận suy](https://huggingface.co/inference-api) của Hugging Face, thay vì tải mô hình trong bộ nhớ. Điều này lý tưởng cho các mô hình lớn như GPT-J hoặc T0pp, những mô hình yêu cầu nhiều RAM. @@ -53,7 +53,7 @@ Bạn có nhớ bản demo từ phần 1 xóa nền của hình ảnh không? H gr.Interface.load("spaces/abidlabs/remove-bg").launch() ``` - + Một trong những điều thú vị khi tải các bản demo từ Hub hoặc Spaces là bạn tùy chỉnh chúng bằng cách ghi đè bất kỳ thông số nào. Ở đây, chúng ta thêm tiêu đề và làm cho tiêu đề đó hoạt động với webcam: @@ -63,6 +63,6 @@ gr.Interface.load( ).launch() ``` - + Bây giờ chúng ta đã khám phá một số cách để tích hợp Gradio với Hugging Face Hub, hãy cùng xem xét một số tính năng nâng cao của lớp `Interface`. Đó là chủ đề của phần tiếp theo! diff --git a/chapters/vi/chapter9/6.mdx b/chapters/vi/chapter9/6.mdx index d09c7ba9b..b5a39870e 100644 --- a/chapters/vi/chapter9/6.mdx +++ b/chapters/vi/chapter9/6.mdx @@ -51,7 +51,7 @@ iface = gr.Interface( iface.launch() ``` - + Lưu ý trạng thái của thành phần đầu ra vẫn tồn tại qua các lần gửi. Lưu ý: bạn có thể chuyển giá trị mặc định vào tham số trạng thái, được sử dụng làm giá trị ban đầu của trạng thái. @@ -91,7 +91,7 @@ gr.Interface( Kiểm tra hàm thông dịch bằng cách gửi đầu vào, sau đó nhấp vào Interpret tương ứng diễn giải bên dưới thành phần đầu ra. - + Bên cạnh phương pháp diễn giải mặc định mà Gradio cung cấp, bạn cũng có thể chỉ định `shap` cho tham số `interpretation` và đặt tham số `num_shap`. Điều này sử dụng diễn giải dựa trên Shapley, bạn có thể đọc thêm về [tại đây](https://christophm.github.io/interpretable-ml-book/shap.html). Cuối cùng, bạn cũng có thể chuyển hàm thông dịch của riêng mình vào tham số `interpretation`. Xem ví dụ trong trang bắt đầu của Gradio [tại đây](https://gradio.app/getting_started/). diff --git a/chapters/vi/chapter9/7.mdx b/chapters/vi/chapter9/7.mdx index f7e723fe4..de6135500 100644 --- a/chapters/vi/chapter9/7.mdx +++ b/chapters/vi/chapter9/7.mdx @@ -55,7 +55,7 @@ with demo: demo.launch() ``` - + Ví dụ đơn giản ở trên giới thiệu 4 khái niệm làm nền tảng cho Blocks: @@ -122,7 +122,7 @@ with demo: demo.launch() ``` - + Bạn sẽ nhận thấy rằng trong ví dụ này, chúng ta cũng đã tạo ra một thành phần `Button` trong mỗi tab và chỉ định một sự kiện nhấp chuột cho mỗi nút, đó là những gì thực sự chạy hàm. @@ -160,7 +160,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### Tạo bản demo đa bước @@ -200,7 +200,7 @@ with demo: demo.launch() ``` - + ### Cập nhật Thuộc tính Thành phần @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + Chúng ta vừa khám phá tất cả các khái niệm cốt lõi của `Blocks`! Cũng giống như với `Interfaces`, bạn có thể tạo các bản demo thú vị có thể được chia sẻ bằng cách sử dụng `share=True` trong phương thức `launch()` hoặc triển khai trên [Hugging Face Spaces](https://huggingface.co/spaces). diff --git a/chapters/vi/chapter9/9.mdx b/chapters/vi/chapter9/9.mdx index 135bae491..7adeec64f 100644 --- a/chapters/vi/chapter9/9.mdx +++ b/chapters/vi/chapter9/9.mdx @@ -1,234 +1,234 @@ - - -# Đố vui cuối chương - -Hãy kiểm tra những gì bạn đã học được trong chương này! - -### 1. Bạn có thể sử dụng Gradio để làm gì? - -share=True trong phương thức khởi chạy, bạn có thể tạo liên kết chia sẻ để gửi cho bất kỳ ai.", - correct: true - }, - { - text: "Gỡ lỗi mô hình của bạn", - explain: "Một lợi thế của bản demo gradio là có thể kiểm tra mô hình của bạn với dữ liệu thực mà bạn có thể thay đổi và quan sát sự thay đổi dự đoán của mô hình trong thời gian thực, giúp bạn gỡ lỗi mô hình của mình.", - correct: true - }, - { - text: "Huấn luyện mô hình của bạn", - explain: "Gradio được thiết kể để sử dụng cho việc luận suy mô hình, SAU KHI mô hình của bạn đã được huấn luyện.", - } - ]} -/> - -### 2. Gradio CHỈ hoạt động với các mô hình PyTorch - - - -### 3. Bạn có thể khởi chạy bản demo Gradio từ đâu? - - - -### 4. Gradio được thiết kế chủ yếu cho các mô hình NLP - - - -### 5. Tính năng nào sau đây được hỗ trợ bởi Gradio? - -gr.Interface.load()", - correct: true - } - ]} -/> - -### 6. Cách nào sau đây là cách hợp lệ để tải mô hìnhHugging Face từ Hub hoặc Spaces? - - - -### 7. Chọn tất cả các bước cần thiết để thêm trạng thái vào giao diện Gradio của bạn - - - -### 8. Những thành phần nào sau đây có trong thư viện Gradio? - - - -### 9. Gradio `Blocks` cho phép bạn làm gì? - - - -### 10. Bạn có thể chia sẻ liên kết công khai tới `Blocks` demo và tổ chức lưu trữ `Blocks` demo trên Hugging Face Spaces. - - + + +# Đố vui cuối chương + +Hãy kiểm tra những gì bạn đã học được trong chương này! + +### 1. Bạn có thể sử dụng Gradio để làm gì? + +share=True trong phương thức khởi chạy, bạn có thể tạo liên kết chia sẻ để gửi cho bất kỳ ai.", + correct: true + }, + { + text: "Gỡ lỗi mô hình của bạn", + explain: "Một lợi thế của bản demo gradio là có thể kiểm tra mô hình của bạn với dữ liệu thực mà bạn có thể thay đổi và quan sát sự thay đổi dự đoán của mô hình trong thời gian thực, giúp bạn gỡ lỗi mô hình của mình.", + correct: true + }, + { + text: "Huấn luyện mô hình của bạn", + explain: "Gradio được thiết kể để sử dụng cho việc luận suy mô hình, SAU KHI mô hình của bạn đã được huấn luyện.", + } + ]} +/> + +### 2. Gradio CHỈ hoạt động với các mô hình PyTorch + + + +### 3. Bạn có thể khởi chạy bản demo Gradio từ đâu? + + + +### 4. Gradio được thiết kế chủ yếu cho các mô hình NLP + + + +### 5. Tính năng nào sau đây được hỗ trợ bởi Gradio? + +gr.Interface.load()", + correct: true + } + ]} +/> + +### 6. Cách nào sau đây là cách hợp lệ để tải mô hìnhHugging Face từ Hub hoặc Spaces? + + + +### 7. Chọn tất cả các bước cần thiết để thêm trạng thái vào giao diện Gradio của bạn + + + +### 8. Những thành phần nào sau đây có trong thư viện Gradio? + + + +### 9. Gradio `Blocks` cho phép bạn làm gì? + + + +### 10. Bạn có thể chia sẻ liên kết công khai tới `Blocks` demo và tổ chức lưu trữ `Blocks` demo trên Hugging Face Spaces. + + diff --git a/chapters/zh-CN/chapter7/2.mdx b/chapters/zh-CN/chapter7/2.mdx index c85dd51f8..b328bbc0d 100644 --- a/chapters/zh-CN/chapter7/2.mdx +++ b/chapters/zh-CN/chapter7/2.mdx @@ -32,8 +32,8 @@ 当然,还有很多其他类型的token分类问题;这些只是几个有代表性的例子。在本节中,我们将在 NER 任务上微调模型 (BERT),然后该模型将能够计算如下预测: - - + + One-hot encoded labels for question answering. diff --git a/chapters/zh-CN/chapter7/3.mdx b/chapters/zh-CN/chapter7/3.mdx index d95d76313..c67c1fec9 100644 --- a/chapters/zh-CN/chapter7/3.mdx +++ b/chapters/zh-CN/chapter7/3.mdx @@ -35,8 +35,8 @@ 在本节结束时, 你将在Hub上拥有一个[掩码语言模型(masked language model)](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.), 该模型可以自动完成句子, 如下所示: - - + + 让我们开始吧! diff --git a/chapters/zh-CN/chapter7/4.mdx b/chapters/zh-CN/chapter7/4.mdx index 07c79149c..32ea23dfa 100644 --- a/chapters/zh-CN/chapter7/4.mdx +++ b/chapters/zh-CN/chapter7/4.mdx @@ -35,8 +35,8 @@ 完成后,我们将拥有一个模型,可以进行这样的翻译: - - + + One-hot encoded labels for question answering. diff --git a/chapters/zh-CN/chapter7/5.mdx b/chapters/zh-CN/chapter7/5.mdx index 40c7111c5..e09659687 100644 --- a/chapters/zh-CN/chapter7/5.mdx +++ b/chapters/zh-CN/chapter7/5.mdx @@ -29,8 +29,8 @@ 尽管在[Hugging Face Hub](https://huggingface.co/models?pipeline_tag=summarization=downloads)上已经存在各种微调模型用于文本摘要,几乎所有这些都只适用于英文文档。因此,为了在本节中添加一些变化,我们将为英语和西班牙语训练一个双语模型。在本节结束时,您将有一个可以总结客户评论的[模型](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es)。 - - + + 如下所示:正如我们将看到的,这些摘要很简洁,因为它们是从客户在产品评论中提供的标题中学到的。让我们首先为这项任务准备一个合适的双语语料库。 diff --git a/chapters/zh-CN/chapter7/6.mdx b/chapters/zh-CN/chapter7/6.mdx index b8a5a8ede..494baba89 100644 --- a/chapters/zh-CN/chapter7/6.mdx +++ b/chapters/zh-CN/chapter7/6.mdx @@ -30,8 +30,8 @@ 在[第六章](/course/chapter6) 我们创建了一个高效的分词器来处理 Python 源代码,但我们仍然需要一个大规模数据集来预训练模型。在这里,我们将我们的分词器应用到源自 GitHub 存储库的 Python 代码语料库。然后我们将使用 `Trainer` API 和 🤗 Accelerate 来训练模型。让我们开始吧! - - + + 这实际上展示了使用本节中训练并上传到 Hub 的模型。你可以在[这里](https://huggingface.co/huggingface-course/codeparrot-ds?text=plt.imshow%28)找到。请注意,由于在文本生成过程中发生了一些随机化,您可能会得到略有不同的结果。 ## 收集数据 diff --git a/chapters/zh-CN/chapter7/7.mdx b/chapters/zh-CN/chapter7/7.mdx index 3874bc60f..8a823cadb 100644 --- a/chapters/zh-CN/chapter7/7.mdx +++ b/chapters/zh-CN/chapter7/7.mdx @@ -28,8 +28,8 @@ 我们将使用 [SQuAD 数据集](https://rajpurkar.github.io/SQuAD-explorer/) 微调一个BERT模型, 其中包括群众工作者对一组维基百科文章提出的问题。以下是一个小的测试样例: - - + + 本节使用的代码已经上传到了Hub。你可以在 [这里](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) 找到它并尝试用它进行预测。 diff --git a/chapters/zh-CN/chapter9/1.mdx b/chapters/zh-CN/chapter9/1.mdx index 2cb3dca85..88b53b355 100644 --- a/chapters/zh-CN/chapter9/1.mdx +++ b/chapters/zh-CN/chapter9/1.mdx @@ -15,15 +15,15 @@ * 一个**草图识别**模型,它接收草图并输出它认为正在绘制的标签: - + * 一个抽取式**问题回答**模型,它接受上下文段落和一个任务并输出一个结果和一个概率分数(我们在[第7章](/course/chapter7/7)中讨论了这种模型): - + * 一个**背景去除**模型,它接收图像并输出去除背景的图像: - + 本章分为两个部分,包括_概念_和_应用程序_。在您了解每个部分的概念后,您将应用它来构建特定类型的演示,范围从图像分类到语音识别。当你读完本章时,你将能够用几行 Python 代码构建这些演示(以及更多!)。 diff --git a/chapters/zh-CN/chapter9/2.mdx b/chapters/zh-CN/chapter9/2.mdx index 6c53eb619..f313f304d 100644 --- a/chapters/zh-CN/chapter9/2.mdx +++ b/chapters/zh-CN/chapter9/2.mdx @@ -37,7 +37,7 @@ demo.launch() 如果你运行这段代码,下面的界面会自动出现在 Jupyter/Colab notebook 中,或者在浏览器中弹出 **[http://localhost:7860](http://localhost:7860/)** 如果运行 从一个脚本。 - + 立即尝试使用您自己的姓名或其他输入来使用此 GUI! @@ -59,7 +59,7 @@ textbox = gr.Textbox(label="Type your name here:", placeholder="John Doe", lines gr.Interface(fn=greet, inputs=textbox, outputs="text").launch() ``` - + 在这里,我们创建了一个带有标签、占位符和一组行数的输入文本框。您可以对输出文本框执行相同的操作,但我们现在将其保留。 @@ -107,6 +107,6 @@ gr.Interface(fn=predict, inputs="text", outputs="text").launch() 就是这样! 您现在可以使用此接口使用 GPT-2 模型生成文本,如下所示 🤯. - + 继续阅读以了解如何使用 Gradio 构建其他类型的演示! \ No newline at end of file diff --git a/chapters/zh-CN/chapter9/3.mdx b/chapters/zh-CN/chapter9/3.mdx index 294b04a4b..1b9aa23ba 100644 --- a/chapters/zh-CN/chapter9/3.mdx +++ b/chapters/zh-CN/chapter9/3.mdx @@ -58,7 +58,7 @@ gr.Interface(reverse_audio, mic, "audio").launch() 上面的代码会产生一个类似下面的界面(如果你的浏览器没有 询问您的麦克风权限, open the demo in a separate tab.) - + 您现在应该能够录制自己的声音并听到自己在反向说话 - 怪异 👻! @@ -102,7 +102,7 @@ gr.Interface( ).launch() ``` - + ### `launch()` 方法 @@ -157,7 +157,7 @@ gr.Interface( 如果您的浏览器没有要求您提供麦克风权限,open the demo in a separate tab. - + 就是这样! 您现在可以使用此界面来转录音频。 注意这里 diff --git a/chapters/zh-CN/chapter9/4.mdx b/chapters/zh-CN/chapter9/4.mdx index 4e10fc77b..b9e444231 100644 --- a/chapters/zh-CN/chapter9/4.mdx +++ b/chapters/zh-CN/chapter9/4.mdx @@ -32,7 +32,7 @@ title = "Ask Rick a Question" description = """ The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything! - + """ article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of." @@ -50,7 +50,7 @@ gr.Interface( 使用上面的选项,我们最终得到了一个更完整的界面。 试试下面的界面: - + ### 使用临时链接分享您的演示 现在我们已经有了机器学习模型的工作演示,让我们学习如何轻松共享指向我们界面的链接。 @@ -132,7 +132,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + 注意 `Interface` 中的 `live=True` 参数,这意味着草图演示使 diff --git a/chapters/zh-CN/chapter9/5.mdx b/chapters/zh-CN/chapter9/5.mdx index 71bc125a0..4fc5a67a0 100644 --- a/chapters/zh-CN/chapter9/5.mdx +++ b/chapters/zh-CN/chapter9/5.mdx @@ -38,7 +38,7 @@ gr.Interface.load( 上述代码将生成以下界面: - + 以这种方式加载模型使用 Hugging Face 的 [Inference API](https://huggingface.co/inference-api),而不是将模型加载到内存中。这对于像 GPT-J 或 T0pp这样需要大量 RAM 的大型模型是理想的。 @@ -51,7 +51,7 @@ gr.Interface.load( gr.Interface.load("spaces/abidlabs/remove-bg").launch() ``` - + 从Hub或Spaces加载演示的一个很酷的地方是, 你可以通过覆盖任何参数来自定义它们。在这里, 我们添加一个标题并让它与网络摄像头一起使用: @@ -61,6 +61,6 @@ gr.Interface.load( ).launch() ``` - + 现在我们已经探索了几种将Gradio与hugs Face Hub集成的方法, 让我们来看看 `Interface` 类的一些高级功能。这就是下一节的主题! \ No newline at end of file diff --git a/chapters/zh-CN/chapter9/6.mdx b/chapters/zh-CN/chapter9/6.mdx index 51f6ca0a8..a2593e842 100644 --- a/chapters/zh-CN/chapter9/6.mdx +++ b/chapters/zh-CN/chapter9/6.mdx @@ -51,7 +51,7 @@ iface = gr.Interface( iface.launch() ``` - + 请注意输出组件的状态如何在提交之间保持不变。注意: 可以给 state 参数传入一个默认值, 作为 state 的初始值。 @@ -90,7 +90,7 @@ gr.Interface( 通过提交一个输入, 然后单击输出组件下的Interpret来测试解释功能。 - + 除了Gradio提供的默认解释方法之外, 你还可以为 `interpretation` 参数指定 `shap`, 并设置 `num_shap` 参数。这使用基于 Shapley 的解释, 你可以在 [here](https://christophm.github.io/interpretable-ml-book/shap.html) 阅读更多信息。最后, 还可以将自己的解释函数传入 `interpretation` 参数。在Gradio的入门页面 [here](https://gradio.app/getting_started/) 中可以看到一个例子。 diff --git a/chapters/zh-CN/chapter9/7.mdx b/chapters/zh-CN/chapter9/7.mdx index 56b9eed58..58b4441a5 100644 --- a/chapters/zh-CN/chapter9/7.mdx +++ b/chapters/zh-CN/chapter9/7.mdx @@ -56,7 +56,7 @@ with demo: demo.launch() ``` - + 上述简单示例介绍了块的4个基本概念: @@ -121,7 +121,7 @@ with demo: demo.launch() ``` - + 你会注意到, 在这个示例中, 我们还在每个选项卡中创建了一个 `Button` 组件, 并且我们为每个按钮分配了一个点击事件,这是实际运行该函数的事件。 @@ -160,7 +160,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### 创建多步demo @@ -200,7 +200,7 @@ with demo: demo.launch() ``` - + ### 更新组件属性 @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + 我们刚刚探索了`块`的所有核心概念! 就像 `参数一样`, 你可以创建很酷的demo, 可以通过在`launch()`方法中使用`share=True`来共享, 或者部署在[Hugging Face Spaces](https://huggingface.co/spaces)上。 \ No newline at end of file From 28d290d3a925fa6b74cfb88bfee61135bdf70a08 Mon Sep 17 00:00:00 2001 From: Lewis Tunstall Date: Tue, 22 Nov 2022 13:18:15 +0100 Subject: [PATCH 2/4] Revert broken links --- chapters/en/chapter9/4.mdx | 6 +++--- chapters/en/chapter9/7.mdx | 10 +++++----- chapters/fr/chapter9/4.mdx | 6 +++--- chapters/fr/chapter9/7.mdx | 10 +++++----- chapters/vi/chapter9/4.mdx | 6 +++--- chapters/vi/chapter9/7.mdx | 10 +++++----- chapters/zh-CN/chapter9/4.mdx | 6 +++--- chapters/zh-CN/chapter9/7.mdx | 10 +++++----- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/chapters/en/chapter9/4.mdx b/chapters/en/chapter9/4.mdx index 5894eb189..2dcd458e6 100644 --- a/chapters/en/chapter9/4.mdx +++ b/chapters/en/chapter9/4.mdx @@ -32,7 +32,7 @@ Using the options above, we end up with a more complete interface. Run the code title = "Ask Rick a Question" description = """ The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything! - + """ article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of." @@ -50,7 +50,7 @@ gr.Interface( Using the options above, we end up with a more complete interface. Try the interface below: - + ### Sharing your demo with temporary links Now that we have a working demo of our machine learning model, let's learn how to easily share a link to our interface. @@ -132,7 +132,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + Notice the `live=True` parameter in `Interface`, which means that the sketch demo makes diff --git a/chapters/en/chapter9/7.mdx b/chapters/en/chapter9/7.mdx index 2847254da..3dc2bf4ca 100644 --- a/chapters/en/chapter9/7.mdx +++ b/chapters/en/chapter9/7.mdx @@ -56,7 +56,7 @@ with demo: demo.launch() ``` - + This simple example above introduces 4 concepts that underlie Blocks: @@ -121,7 +121,7 @@ with demo: demo.launch() ``` - + You'll notice that in this example, we've also created a `Button` component in each tab, and we've assigned a click event to each button, which is what actually runs the function. @@ -160,7 +160,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### Creating multi-step demos @@ -200,7 +200,7 @@ with demo: demo.launch() ``` - + ### Updating Component Properties @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + We just explored all the core concepts of `Blocks`! Just like with `Interfaces`, you can create cool demos that can be shared by using `share=True` in the `launch()` method or deployed on [Hugging Face Spaces](https://huggingface.co/spaces). \ No newline at end of file diff --git a/chapters/fr/chapter9/4.mdx b/chapters/fr/chapter9/4.mdx index 125195301..2065d3589 100644 --- a/chapters/fr/chapter9/4.mdx +++ b/chapters/fr/chapter9/4.mdx @@ -33,7 +33,7 @@ description = """ The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything! # Le robot a été entraîné à répondre à des questions basées sur les dialogues de Rick et Morty. # Demandez à Rick ce que vous voulez ! - + """ article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of." @@ -53,7 +53,7 @@ gr.Interface( En utilisant les options ci-dessus, nous obtenons une interface plus complète. Essayez l'interface ci-dessous : - + ### Partager votre démo avec des liens temporaires Maintenant que nous avons une démo fonctionnelle de notre modèle d'apprentissage automatique, apprenons à partager facilement un lien vers notre interface. @@ -135,7 +135,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + Remarquez le paramètre `live=True` dans `Interface`, qui signifie que la démo de sketchs fait une prédiction chaque fois que quelqu'un dessine sur le bloc (pas de bouton de soumission !). diff --git a/chapters/fr/chapter9/7.mdx b/chapters/fr/chapter9/7.mdx index 963b89711..c199b347d 100644 --- a/chapters/fr/chapter9/7.mdx +++ b/chapters/fr/chapter9/7.mdx @@ -56,7 +56,7 @@ with demo: demo.launch() ``` - + Ce simple exemple ci-dessus introduit 4 concepts qui sous-tendent les *Blocks* : @@ -125,7 +125,7 @@ with demo: demo.launch() ``` - + Vous remarquerez que dans cet exemple, nous avons également créé un composant `Button` dans chaque onglet et avons assigné un événement de clic à chaque bouton qui est l'élément qui exécute réellement la fonction. @@ -164,7 +164,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### Création de démos multi-étapes @@ -204,7 +204,7 @@ with demo: demo.launch() ``` - + ### Mise à jour des propriétés des composants @@ -235,6 +235,6 @@ with gr.Blocks() as block: block.launch() ``` - + Nous venons d'explorer tous les concepts de base des `Blocks` ! Tout comme avec `Interface`, vous pouvez créer des démos sympas qui peuvent être partagées en utilisant `share=True` dans la méthode `launch()` ou déployées sur [*Spaces*](https://huggingface.co/spaces). \ No newline at end of file diff --git a/chapters/vi/chapter9/4.mdx b/chapters/vi/chapter9/4.mdx index bbe48eb07..0893954eb 100644 --- a/chapters/vi/chapter9/4.mdx +++ b/chapters/vi/chapter9/4.mdx @@ -31,7 +31,7 @@ Sử dụng các tùy chọn ở trên, chúng ta kết thúc với một giao d title = "Ask Rick a Question" description = """ The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything! - + """ article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of." @@ -49,7 +49,7 @@ gr.Interface( Sử dụng các tùy chọn ở trên, chúng ta kết thúc với một giao diện hoàn chỉnh hơn. Hãy thử giao diện bên dưới: - + ### Chia sẻ bản demo của bạn với các liên kết tạm thời @@ -131,7 +131,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + Lưu ý tham số `live=True` trong `Interface`, có nghĩa là bản demo phác thảo tạo ra dự đoán mỗi khi ai đó vẽ trên sketchpad (không có nút gửi!). diff --git a/chapters/vi/chapter9/7.mdx b/chapters/vi/chapter9/7.mdx index de6135500..f7e723fe4 100644 --- a/chapters/vi/chapter9/7.mdx +++ b/chapters/vi/chapter9/7.mdx @@ -55,7 +55,7 @@ with demo: demo.launch() ``` - + Ví dụ đơn giản ở trên giới thiệu 4 khái niệm làm nền tảng cho Blocks: @@ -122,7 +122,7 @@ with demo: demo.launch() ``` - + Bạn sẽ nhận thấy rằng trong ví dụ này, chúng ta cũng đã tạo ra một thành phần `Button` trong mỗi tab và chỉ định một sự kiện nhấp chuột cho mỗi nút, đó là những gì thực sự chạy hàm. @@ -160,7 +160,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### Tạo bản demo đa bước @@ -200,7 +200,7 @@ with demo: demo.launch() ``` - + ### Cập nhật Thuộc tính Thành phần @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + Chúng ta vừa khám phá tất cả các khái niệm cốt lõi của `Blocks`! Cũng giống như với `Interfaces`, bạn có thể tạo các bản demo thú vị có thể được chia sẻ bằng cách sử dụng `share=True` trong phương thức `launch()` hoặc triển khai trên [Hugging Face Spaces](https://huggingface.co/spaces). diff --git a/chapters/zh-CN/chapter9/4.mdx b/chapters/zh-CN/chapter9/4.mdx index b9e444231..4e10fc77b 100644 --- a/chapters/zh-CN/chapter9/4.mdx +++ b/chapters/zh-CN/chapter9/4.mdx @@ -32,7 +32,7 @@ title = "Ask Rick a Question" description = """ The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything! - + """ article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of." @@ -50,7 +50,7 @@ gr.Interface( 使用上面的选项,我们最终得到了一个更完整的界面。 试试下面的界面: - + ### 使用临时链接分享您的演示 现在我们已经有了机器学习模型的工作演示,让我们学习如何轻松共享指向我们界面的链接。 @@ -132,7 +132,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + 注意 `Interface` 中的 `live=True` 参数,这意味着草图演示使 diff --git a/chapters/zh-CN/chapter9/7.mdx b/chapters/zh-CN/chapter9/7.mdx index 58b4441a5..56b9eed58 100644 --- a/chapters/zh-CN/chapter9/7.mdx +++ b/chapters/zh-CN/chapter9/7.mdx @@ -56,7 +56,7 @@ with demo: demo.launch() ``` - + 上述简单示例介绍了块的4个基本概念: @@ -121,7 +121,7 @@ with demo: demo.launch() ``` - + 你会注意到, 在这个示例中, 我们还在每个选项卡中创建了一个 `Button` 组件, 并且我们为每个按钮分配了一个点击事件,这是实际运行该函数的事件。 @@ -160,7 +160,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### 创建多步demo @@ -200,7 +200,7 @@ with demo: demo.launch() ``` - + ### 更新组件属性 @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + 我们刚刚探索了`块`的所有核心概念! 就像 `参数一样`, 你可以创建很酷的demo, 可以通过在`launch()`方法中使用`share=True`来共享, 或者部署在[Hugging Face Spaces](https://huggingface.co/spaces)上。 \ No newline at end of file From 055d4ebc36c2499ffe2fc76532eb16b99c3c0dec Mon Sep 17 00:00:00 2001 From: Lewis Tunstall Date: Tue, 22 Nov 2022 13:28:12 +0100 Subject: [PATCH 3/4] Fix broken links --- chapters/en/chapter9/4.mdx | 4 ++-- chapters/en/chapter9/7.mdx | 10 +++++----- chapters/fr/chapter9/4.mdx | 4 ++-- chapters/fr/chapter9/7.mdx | 10 +++++----- chapters/vi/chapter9/4.mdx | 4 ++-- chapters/vi/chapter9/7.mdx | 10 +++++----- chapters/zh-CN/chapter9/4.mdx | 4 ++-- chapters/zh-CN/chapter9/7.mdx | 10 +++++----- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/chapters/en/chapter9/4.mdx b/chapters/en/chapter9/4.mdx index 2dcd458e6..0d11f7680 100644 --- a/chapters/en/chapter9/4.mdx +++ b/chapters/en/chapter9/4.mdx @@ -50,7 +50,7 @@ gr.Interface( Using the options above, we end up with a more complete interface. Try the interface below: - + ### Sharing your demo with temporary links Now that we have a working demo of our machine learning model, let's learn how to easily share a link to our interface. @@ -132,7 +132,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + Notice the `live=True` parameter in `Interface`, which means that the sketch demo makes diff --git a/chapters/en/chapter9/7.mdx b/chapters/en/chapter9/7.mdx index 3dc2bf4ca..1452c6867 100644 --- a/chapters/en/chapter9/7.mdx +++ b/chapters/en/chapter9/7.mdx @@ -56,7 +56,7 @@ with demo: demo.launch() ``` - + This simple example above introduces 4 concepts that underlie Blocks: @@ -121,7 +121,7 @@ with demo: demo.launch() ``` - + You'll notice that in this example, we've also created a `Button` component in each tab, and we've assigned a click event to each button, which is what actually runs the function. @@ -160,7 +160,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### Creating multi-step demos @@ -200,7 +200,7 @@ with demo: demo.launch() ``` - + ### Updating Component Properties @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + We just explored all the core concepts of `Blocks`! Just like with `Interfaces`, you can create cool demos that can be shared by using `share=True` in the `launch()` method or deployed on [Hugging Face Spaces](https://huggingface.co/spaces). \ No newline at end of file diff --git a/chapters/fr/chapter9/4.mdx b/chapters/fr/chapter9/4.mdx index 2065d3589..53e237e76 100644 --- a/chapters/fr/chapter9/4.mdx +++ b/chapters/fr/chapter9/4.mdx @@ -53,7 +53,7 @@ gr.Interface( En utilisant les options ci-dessus, nous obtenons une interface plus complète. Essayez l'interface ci-dessous : - + ### Partager votre démo avec des liens temporaires Maintenant que nous avons une démo fonctionnelle de notre modèle d'apprentissage automatique, apprenons à partager facilement un lien vers notre interface. @@ -135,7 +135,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + Remarquez le paramètre `live=True` dans `Interface`, qui signifie que la démo de sketchs fait une prédiction chaque fois que quelqu'un dessine sur le bloc (pas de bouton de soumission !). diff --git a/chapters/fr/chapter9/7.mdx b/chapters/fr/chapter9/7.mdx index c199b347d..66a3d838e 100644 --- a/chapters/fr/chapter9/7.mdx +++ b/chapters/fr/chapter9/7.mdx @@ -56,7 +56,7 @@ with demo: demo.launch() ``` - + Ce simple exemple ci-dessus introduit 4 concepts qui sous-tendent les *Blocks* : @@ -125,7 +125,7 @@ with demo: demo.launch() ``` - + Vous remarquerez que dans cet exemple, nous avons également créé un composant `Button` dans chaque onglet et avons assigné un événement de clic à chaque bouton qui est l'élément qui exécute réellement la fonction. @@ -164,7 +164,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### Création de démos multi-étapes @@ -204,7 +204,7 @@ with demo: demo.launch() ``` - + ### Mise à jour des propriétés des composants @@ -235,6 +235,6 @@ with gr.Blocks() as block: block.launch() ``` - + Nous venons d'explorer tous les concepts de base des `Blocks` ! Tout comme avec `Interface`, vous pouvez créer des démos sympas qui peuvent être partagées en utilisant `share=True` dans la méthode `launch()` ou déployées sur [*Spaces*](https://huggingface.co/spaces). \ No newline at end of file diff --git a/chapters/vi/chapter9/4.mdx b/chapters/vi/chapter9/4.mdx index 0893954eb..5be9bff24 100644 --- a/chapters/vi/chapter9/4.mdx +++ b/chapters/vi/chapter9/4.mdx @@ -49,7 +49,7 @@ gr.Interface( Sử dụng các tùy chọn ở trên, chúng ta kết thúc với một giao diện hoàn chỉnh hơn. Hãy thử giao diện bên dưới: - + ### Chia sẻ bản demo của bạn với các liên kết tạm thời @@ -131,7 +131,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + Lưu ý tham số `live=True` trong `Interface`, có nghĩa là bản demo phác thảo tạo ra dự đoán mỗi khi ai đó vẽ trên sketchpad (không có nút gửi!). diff --git a/chapters/vi/chapter9/7.mdx b/chapters/vi/chapter9/7.mdx index f7e723fe4..377ad66d9 100644 --- a/chapters/vi/chapter9/7.mdx +++ b/chapters/vi/chapter9/7.mdx @@ -55,7 +55,7 @@ with demo: demo.launch() ``` - + Ví dụ đơn giản ở trên giới thiệu 4 khái niệm làm nền tảng cho Blocks: @@ -122,7 +122,7 @@ with demo: demo.launch() ``` - + Bạn sẽ nhận thấy rằng trong ví dụ này, chúng ta cũng đã tạo ra một thành phần `Button` trong mỗi tab và chỉ định một sự kiện nhấp chuột cho mỗi nút, đó là những gì thực sự chạy hàm. @@ -160,7 +160,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### Tạo bản demo đa bước @@ -200,7 +200,7 @@ with demo: demo.launch() ``` - + ### Cập nhật Thuộc tính Thành phần @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + Chúng ta vừa khám phá tất cả các khái niệm cốt lõi của `Blocks`! Cũng giống như với `Interfaces`, bạn có thể tạo các bản demo thú vị có thể được chia sẻ bằng cách sử dụng `share=True` trong phương thức `launch()` hoặc triển khai trên [Hugging Face Spaces](https://huggingface.co/spaces). diff --git a/chapters/zh-CN/chapter9/4.mdx b/chapters/zh-CN/chapter9/4.mdx index 4e10fc77b..62c675d99 100644 --- a/chapters/zh-CN/chapter9/4.mdx +++ b/chapters/zh-CN/chapter9/4.mdx @@ -50,7 +50,7 @@ gr.Interface( 使用上面的选项,我们最终得到了一个更完整的界面。 试试下面的界面: - + ### 使用临时链接分享您的演示 现在我们已经有了机器学习模型的工作演示,让我们学习如何轻松共享指向我们界面的链接。 @@ -132,7 +132,7 @@ interface = gr.Interface( interface.launch(share=True) ``` - + 注意 `Interface` 中的 `live=True` 参数,这意味着草图演示使 diff --git a/chapters/zh-CN/chapter9/7.mdx b/chapters/zh-CN/chapter9/7.mdx index 56b9eed58..c09248999 100644 --- a/chapters/zh-CN/chapter9/7.mdx +++ b/chapters/zh-CN/chapter9/7.mdx @@ -56,7 +56,7 @@ with demo: demo.launch() ``` - + 上述简单示例介绍了块的4个基本概念: @@ -121,7 +121,7 @@ with demo: demo.launch() ``` - + 你会注意到, 在这个示例中, 我们还在每个选项卡中创建了一个 `Button` 组件, 并且我们为每个按钮分配了一个点击事件,这是实际运行该函数的事件。 @@ -160,7 +160,7 @@ with gr.Blocks() as demo: demo.launch() ``` - + ### 创建多步demo @@ -200,7 +200,7 @@ with demo: demo.launch() ``` - + ### 更新组件属性 @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + 我们刚刚探索了`块`的所有核心概念! 就像 `参数一样`, 你可以创建很酷的demo, 可以通过在`launch()`方法中使用`share=True`来共享, 或者部署在[Hugging Face Spaces](https://huggingface.co/spaces)上。 \ No newline at end of file From 981f18c3aaf5aa53c4c602a09809bff1b53af94a Mon Sep 17 00:00:00 2001 From: Lewis Tunstall Date: Tue, 22 Nov 2022 14:39:03 +0100 Subject: [PATCH 4/4] Fix domains --- chapters/en/chapter7/3.mdx | 2 +- chapters/en/chapter9/7.mdx | 2 +- chapters/fr/chapter7/3.mdx | 2 +- chapters/fr/chapter9/7.mdx | 2 +- chapters/ja/chapter7/3.mdx | 2 +- chapters/vi/chapter7/3.mdx | 2 +- chapters/vi/chapter9/7.mdx | 2 +- chapters/zh-CN/chapter7/3.mdx | 2 +- chapters/zh-CN/chapter9/7.mdx | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/chapters/en/chapter7/3.mdx b/chapters/en/chapter7/3.mdx index 032c5ac4b..059f73552 100644 --- a/chapters/en/chapter7/3.mdx +++ b/chapters/en/chapter7/3.mdx @@ -35,7 +35,7 @@ This process of fine-tuning a pretrained language model on in-domain data is usu By the end of this section you'll have a [masked language model](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.) on the Hub that can autocomplete sentences as shown below: - + Let's dive in! diff --git a/chapters/en/chapter9/7.mdx b/chapters/en/chapter9/7.mdx index 1452c6867..10654a24c 100644 --- a/chapters/en/chapter9/7.mdx +++ b/chapters/en/chapter9/7.mdx @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + We just explored all the core concepts of `Blocks`! Just like with `Interfaces`, you can create cool demos that can be shared by using `share=True` in the `launch()` method or deployed on [Hugging Face Spaces](https://huggingface.co/spaces). \ No newline at end of file diff --git a/chapters/fr/chapter7/3.mdx b/chapters/fr/chapter7/3.mdx index 55a91167e..87d31f385 100644 --- a/chapters/fr/chapter7/3.mdx +++ b/chapters/fr/chapter7/3.mdx @@ -35,7 +35,7 @@ Ce processus de *finetuning* d'un modèle de langage pré-entraîné sur des don À 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 ! diff --git a/chapters/fr/chapter9/7.mdx b/chapters/fr/chapter9/7.mdx index 66a3d838e..04a38803e 100644 --- a/chapters/fr/chapter9/7.mdx +++ b/chapters/fr/chapter9/7.mdx @@ -235,6 +235,6 @@ with gr.Blocks() as block: block.launch() ``` - + Nous venons d'explorer tous les concepts de base des `Blocks` ! Tout comme avec `Interface`, vous pouvez créer des démos sympas qui peuvent être partagées en utilisant `share=True` dans la méthode `launch()` ou déployées sur [*Spaces*](https://huggingface.co/spaces). \ No newline at end of file diff --git a/chapters/ja/chapter7/3.mdx b/chapters/ja/chapter7/3.mdx index 2e5ede2b7..e36e0d6cf 100644 --- a/chapters/ja/chapter7/3.mdx +++ b/chapters/ja/chapter7/3.mdx @@ -36,7 +36,7 @@ Transformerモデルを含む多くのNLPアプリケーションでは、ハギ このセクションの終わりには、以下のような文章を自動補完できる[マスク言語モデル](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.)がHub上にできていることでしょう。 - + それでは始めましょう! diff --git a/chapters/vi/chapter7/3.mdx b/chapters/vi/chapter7/3.mdx index bf61c2e4b..38e775401 100644 --- a/chapters/vi/chapter7/3.mdx +++ b/chapters/vi/chapter7/3.mdx @@ -35,7 +35,7 @@ Quá trình tinh chỉnh mô hình ngôn ngữ được huấn luyện trước Đến cuối phần này, bạn sẽ có một [mô hình ngôn ngữ bị ẩn đi](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.) trên Hub có thể tự động hoàn thiện câu như dưới đây: - + Cùng đi sâu vào thôi! diff --git a/chapters/vi/chapter9/7.mdx b/chapters/vi/chapter9/7.mdx index 377ad66d9..91ab3a275 100644 --- a/chapters/vi/chapter9/7.mdx +++ b/chapters/vi/chapter9/7.mdx @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + Chúng ta vừa khám phá tất cả các khái niệm cốt lõi của `Blocks`! Cũng giống như với `Interfaces`, bạn có thể tạo các bản demo thú vị có thể được chia sẻ bằng cách sử dụng `share=True` trong phương thức `launch()` hoặc triển khai trên [Hugging Face Spaces](https://huggingface.co/spaces). diff --git a/chapters/zh-CN/chapter7/3.mdx b/chapters/zh-CN/chapter7/3.mdx index c67c1fec9..a219af5f9 100644 --- a/chapters/zh-CN/chapter7/3.mdx +++ b/chapters/zh-CN/chapter7/3.mdx @@ -35,7 +35,7 @@ 在本节结束时, 你将在Hub上拥有一个[掩码语言模型(masked language model)](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.), 该模型可以自动完成句子, 如下所示: - + 让我们开始吧! diff --git a/chapters/zh-CN/chapter9/7.mdx b/chapters/zh-CN/chapter9/7.mdx index c09248999..68074009f 100644 --- a/chapters/zh-CN/chapter9/7.mdx +++ b/chapters/zh-CN/chapter9/7.mdx @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + 我们刚刚探索了`块`的所有核心概念! 就像 `参数一样`, 你可以创建很酷的demo, 可以通过在`launch()`方法中使用`share=True`来共享, 或者部署在[Hugging Face Spaces](https://huggingface.co/spaces)上。 \ No newline at end of file