From 61d1f47eaf478b9739b2e0db6bbaeee7b0256d42 Mon Sep 17 00:00:00 2001 From: burtenshaw Date: Wed, 1 Oct 2025 12:16:40 +0200 Subject: [PATCH] migrate tips --- chapters/bn/chapter1/audio_data.mdx | 16 +- chapters/bn/chapter1/preprocessing.mdx | 15 +- chapters/en/chapter0/get_ready.mdx | 17 +- chapters/en/chapter1/audio_data.mdx | 18 +- chapters/en/chapter1/preprocessing.mdx | 19 +- chapters/en/chapter3/classification.mdx | 5 +- chapters/en/chapter3/ctc.mdx | 10 +- chapters/en/chapter3/introduction.mdx | 5 +- chapters/en/chapter3/seq2seq.mdx | 15 +- chapters/en/chapter4/fine-tuning.mdx | 53 +- chapters/en/chapter5/evaluation.mdx | 14 +- chapters/en/chapter5/fine-tuning.mdx | 12 +- chapters/en/chapter6/fine-tuning.mdx | 9 +- chapters/en/chapter6/pre-trained_models.mdx | 93 +- chapters/en/chapter7/hands_on.mdx | 13 +- chapters/en/chapter7/speech-to-speech.mdx | 24 +- chapters/en/chapter7/transcribe-meeting.mdx | 9 +- chapters/en/chapter7/voice-assistant.mdx | 27 +- chapters/es/chapter1/audio_data.mdx | 21 +- chapters/es/chapter1/preprocessing.mdx | 19 +- chapters/fr/chapter1/audio_data.mdx | 444 ++++---- chapters/fr/chapter1/preprocessing.mdx | 405 ++++--- chapters/fr/chapter3/classification.mdx | 5 +- chapters/fr/chapter3/ctc.mdx | 10 +- chapters/fr/chapter3/introduction.mdx | 5 +- chapters/fr/chapter3/seq2seq.mdx | 30 +- chapters/fr/chapter4/fine-tuning.mdx | 938 +++++++++-------- chapters/fr/chapter5/evaluation.mdx | 642 ++++++------ chapters/fr/chapter5/fine-tuning.mdx | 990 +++++++++--------- chapters/fr/chapter6/fine-tuning.mdx | 7 +- chapters/fr/chapter6/pre-trained_models.mdx | 41 +- chapters/fr/chapter7/hands-on.mdx | 5 +- chapters/fr/chapter7/speech-to-speech.mdx | 12 +- chapters/fr/chapter7/transcribe-meeting.mdx | 5 +- chapters/fr/chapter7/voice-assistant.mdx | 10 +- chapters/ko/chapter1/audio_data.mdx | 9 +- chapters/ko/chapter1/preprocessing.mdx | 5 +- chapters/ko/chapter3/classification.mdx | 5 +- chapters/ko/chapter3/ctc.mdx | 10 +- chapters/ko/chapter3/introduction.mdx | 5 +- chapters/ko/chapter3/seq2seq.mdx | 15 +- chapters/pt-BR/chapter0/get_ready.mdx | 7 +- chapters/pt-BR/chapter1/audio_data.mdx | 20 +- chapters/pt-BR/chapter1/preprocessing.mdx | 5 +- chapters/ru/chapter1/audio_data.mdx | 16 +- chapters/ru/chapter1/preprocessing.mdx | 19 +- chapters/ru/chapter3/classification.mdx | 7 +- chapters/ru/chapter3/ctc.mdx | 20 +- chapters/ru/chapter3/introduction.mdx | 9 +- chapters/ru/chapter3/seq2seq.mdx | 19 +- chapters/ru/chapter4/fine-tuning.mdx | 49 +- chapters/ru/chapter5/evaluation.mdx | 14 +- chapters/ru/chapter5/fine-tuning.mdx | 12 +- chapters/ru/chapter6/fine-tuning.mdx | 9 +- chapters/ru/chapter6/pre-trained_models.mdx | 99 +- chapters/ru/chapter7/hands-on.mdx | 13 +- chapters/ru/chapter7/speech-to-speech.mdx | 22 +- chapters/ru/chapter7/transcribe-meeting.mdx | 9 +- chapters/ru/chapter7/voice-assistant.mdx | 25 +- chapters/tr/chapter0/get_ready.mdx | 7 +- chapters/tr/chapter1/audio_data.mdx | 10 +- chapters/tr/chapter1/preprocessing.mdx | 5 +- chapters/zh-CN/chapter1/audio_data.mdx | 10 +- chapters/zh-CN/chapter1/preprocessing.mdx | 5 +- chapters/zh-CN/chapter3/classification.mdx | 5 +- chapters/zh-CN/chapter3/ctc.mdx | 10 +- chapters/zh-CN/chapter3/introduction.mdx | 5 +- chapters/zh-CN/chapter3/seq2seq.mdx | 15 +- chapters/zh-CN/chapter5/evaluation.mdx | 10 +- chapters/zh-CN/chapter5/fine-tuning.mdx | 12 +- chapters/zh-CN/chapter6/fine-tuning.mdx | 7 +- .../zh-CN/chapter6/pre-trained_models.mdx | 75 +- 72 files changed, 2178 insertions(+), 2384 deletions(-) diff --git a/chapters/bn/chapter1/audio_data.mdx b/chapters/bn/chapter1/audio_data.mdx index 897d3fa3..44eae9df 100644 --- a/chapters/bn/chapter1/audio_data.mdx +++ b/chapters/bn/chapter1/audio_data.mdx @@ -167,9 +167,8 @@ DFT এর আউটপুট হল জটিল সংখ্যার এক আপনি amplitude মানগুলিকে ডেসিবেল স্কেলে রূপান্তর করতে `librosa.amplitude_to_db()` ব্যবহার করতে পারেন, এটি বর্ণালী মধ্যে সূক্ষ্ম বিবরণ টিকে দেখতে সহজ করে তোলে। কখনও কখনও লোকেরা **power বর্ণালী** ব্যবহার করে, যা amplitude এর পরিবর্তে শক্তি পরিমাপ করে; যা কেবলমাত্র amplitude এর মান এর বর্গাকার। - -💡 বাস্তবে, লোকেরা FFT শব্দটি DFT-এর সাথে বিনিময়যোগ্যভাবে ব্যবহার করে, কারণ FFT বা Fast Fourier Transform হলো কম্পিউটারে DFT গণনা করার একমাত্র কার্যকরী উপায়। - +> [!TIP] +> 💡 বাস্তবে, লোকেরা FFT শব্দটি DFT-এর সাথে বিনিময়যোগ্যভাবে ব্যবহার করে, কারণ FFT বা Fast Fourier Transform হলো কম্পিউটারে DFT গণনা করার একমাত্র কার্যকরী উপায়। একটি অডিও সিগন্যালের ফ্রিকোয়েন্সি বর্ণালীতে তার তরঙ্গরূপের মতো একই তথ্য থাকে - তারা কেবল দুটি ভিন্ন উপায়, একই ডেটাকে দেখার। যেখানে তরঙ্গরূপ amplitude প্লট করে সময়ের সাথে সাথে অডিও সিগন্যালের এবং বর্ণালী নির্দিষ্ট সময়ে পৃথক ফ্রিকোয়েন্সির amplitude কল্পনা করে। @@ -252,12 +251,11 @@ plt.colorbar() কারণ ডেসিবেলে রূপান্তর করার জন্যে লগারিদমিক অপারেশন প্রয়োগ করতে হয়। উপরের উদাহরণটি `librosa.power_to_db()` ব্যবহার করে `librosa.feature.melspectrogram()` একটি power spectrogram তৈরি করে। - -💡 সব mel spectrogram একই নয়! সাধারণ ব্যবহারে দুটি ভিন্ন মেল স্কেল আছে ("htk" এবং "slaney"), -এবং power spectrogram এর পরিবর্তে amplitude spectrogram ব্যবহার করা যেতে পারে। log-mel spectrogram গণনা করার সময় সর্বদা সত্য ডেসিবেল -গণনা করা হয় না, কিন্তু সহজভাবে `লগ` নেওয়া হতে পারে। অতএব, যদি একটি মেশিন লার্নিং মডেল ইনপুট হিসাবে একটি mel spectrogram আশা করে, -আপনি একই ভাবে কম্পিউট করছেন তা নিশ্চিত করতে দুবার চেক করুন। - +> [!TIP] +> 💡 সব mel spectrogram একই নয়! সাধারণ ব্যবহারে দুটি ভিন্ন মেল স্কেল আছে ("htk" এবং "slaney"), +> এবং power spectrogram এর পরিবর্তে amplitude spectrogram ব্যবহার করা যেতে পারে। log-mel spectrogram গণনা করার সময় সর্বদা সত্য ডেসিবেল +> গণনা করা হয় না, কিন্তু সহজভাবে `লগ` নেওয়া হতে পারে। অতএব, যদি একটি মেশিন লার্নিং মডেল ইনপুট হিসাবে একটি mel spectrogram আশা করে, +> আপনি একই ভাবে কম্পিউট করছেন তা নিশ্চিত করতে দুবার চেক করুন। একটি mel spectrogram তৈরি করা একটি ক্ষতিকর অপারেশন কারণ এতে সিগন্যাল ফিল্টার করা জড়িত। একটি mel spectrogram কে নিয়মিত তরঙ্গরূপ এ রূপান্তর করা খুবই কঠিন, এমনকি সাহারণ spectrogram কে নিয়মিত তরঙ্গরূপ এ রূপান্তর করা এর চেয়ে সহজ, কারণ এর জন্য যেই ফ্রিকোয়েন্সিগুলিকে ফেলে দেওয়া হয়েছিল সেগুলোকে diff --git a/chapters/bn/chapter1/preprocessing.mdx b/chapters/bn/chapter1/preprocessing.mdx index 2df30917..426c1fb8 100644 --- a/chapters/bn/chapter1/preprocessing.mdx +++ b/chapters/bn/chapter1/preprocessing.mdx @@ -60,14 +60,13 @@ minds[0] আপনি লক্ষ্য করতে পারেন যে অ্যারের মানগুলিও এখন ভিন্ন। এর কারণ হল আমরা এখন এর জন্য amplitude মানগুলির দ্বিগুণ সংখ্যা পেয়েছি প্রতিটি যে আমরা আগে ছিল. - -💡 Resampling সম্পর্কে কিছু তথ্য: যদি একটি অডিও সিগন্যাল ৮ kHz এ নমুনা নেওয়া হয়, যাতে প্রতি সেকেন্ডে ৮০০০ নমুনা রিডিং হয়, আমরা জানি যে অডিওতে -৪ kHz এর বেশি ফ্রিকোয়েন্সি নেই, এটি Nyquist sampling theorem দ্বারা নিশ্চিত করা হয়। এই কারণে, আমরা নিশ্চিত হতে পারি যে স্যাম্পলিং পয়েন্টগুলির মধ্যে -সর্বদা মূল অবিচ্ছিন্ন সংকেত থাকে যা একটি মসৃণ বক্ররেখা তৈরি করে। upsampling করার মানে তখন, অতিরিক্ত নমুনার মান এই বক্ররেখা আনুমান করে গণনা করা। -এই মানগুলি আগে থেকে উপস্থিত যমুনার মান এর সাহায্যে গণনা করা হয়। downsampling এর জন্যে আমরা প্রথমে যেকোনো ফ্রিকোয়েন্সি ফিল্টার আউট করি যা -Nyquist সীমার চেয়ে বেশি, তারপর নতুন নমুনা গণনা করি। অন্য কথায়, আপনি প্রতি দ্বিতীয় নমুনাকে ছুঁড়ে ফেলার মাধ্যমে ২x ফ্যাক্টর এ downsample করতে -পারবেন না - এটি সিগন্যালে বিকৃতি তৈরি করবে। resampling সঠিকভাবে করা কঠিন এবং ভাল-পরীক্ষিত লাইব্রেরি যেমন librosa বা 🤗 datasets এর উপর ছেড়ে দেওয়াই ভালো। - +> [!TIP] +> 💡 Resampling সম্পর্কে কিছু তথ্য: যদি একটি অডিও সিগন্যাল ৮ kHz এ নমুনা নেওয়া হয়, যাতে প্রতি সেকেন্ডে ৮০০০ নমুনা রিডিং হয়, আমরা জানি যে অডিওতে +> ৪ kHz এর বেশি ফ্রিকোয়েন্সি নেই, এটি Nyquist sampling theorem দ্বারা নিশ্চিত করা হয়। এই কারণে, আমরা নিশ্চিত হতে পারি যে স্যাম্পলিং পয়েন্টগুলির মধ্যে +> সর্বদা মূল অবিচ্ছিন্ন সংকেত থাকে যা একটি মসৃণ বক্ররেখা তৈরি করে। upsampling করার মানে তখন, অতিরিক্ত নমুনার মান এই বক্ররেখা আনুমান করে গণনা করা। +> এই মানগুলি আগে থেকে উপস্থিত যমুনার মান এর সাহায্যে গণনা করা হয়। downsampling এর জন্যে আমরা প্রথমে যেকোনো ফ্রিকোয়েন্সি ফিল্টার আউট করি যা +> Nyquist সীমার চেয়ে বেশি, তারপর নতুন নমুনা গণনা করি। অন্য কথায়, আপনি প্রতি দ্বিতীয় নমুনাকে ছুঁড়ে ফেলার মাধ্যমে ২x ফ্যাক্টর এ downsample করতে +> পারবেন না - এটি সিগন্যালে বিকৃতি তৈরি করবে। resampling সঠিকভাবে করা কঠিন এবং ভাল-পরীক্ষিত লাইব্রেরি যেমন librosa বা 🤗 datasets এর উপর ছেড়ে দেওয়াই ভালো। ## ডেটাসেট ফিল্টার করা diff --git a/chapters/en/chapter0/get_ready.mdx b/chapters/en/chapter0/get_ready.mdx index a064dc89..489c8db8 100644 --- a/chapters/en/chapter0/get_ready.mdx +++ b/chapters/en/chapter0/get_ready.mdx @@ -27,16 +27,13 @@ To go through the course materials you will need: - A computer with an internet connection - [Google Colab](https://colab.research.google.com) for hands-on exercises. The free version is enough. If you have never used Google Colab before, check out this [official introduction notebook](https://colab.research.google.com/notebooks/intro.ipynb). - - -As an alternative to the free tier of Google Colab, you can use your own local setup, or Kaggle Notebooks. Kaggle Notebooks -offer a fixed number of GPU hours and have similar functionality to Google Colab, however, there are differences when it -comes to sharing your models on 🤗 Hub (e.g. for completing assignments). If you decide to use Kaggle Notebooks as your -tool of choice, check out the [example Kaggle notebook](https://www.kaggle.com/code/michaelshekasta/test-notebook) created by -[@michaelshekasta](https://github.com/michaelshekasta). This notebook illustrates how you can train and share your -trained model on 🤗 Hub. - - +> [!TIP] +> As an alternative to the free tier of Google Colab, you can use your own local setup, or Kaggle Notebooks. Kaggle Notebooks +> offer a fixed number of GPU hours and have similar functionality to Google Colab, however, there are differences when it +> comes to sharing your models on 🤗 Hub (e.g. for completing assignments). If you decide to use Kaggle Notebooks as your +> tool of choice, check out the [example Kaggle notebook](https://www.kaggle.com/code/michaelshekasta/test-notebook) created by +> [@michaelshekasta](https://github.com/michaelshekasta). This notebook illustrates how you can train and share your +> trained model on 🤗 Hub. ## Step 5. Join the community diff --git a/chapters/en/chapter1/audio_data.mdx b/chapters/en/chapter1/audio_data.mdx index d1f5e9ad..28e18049 100644 --- a/chapters/en/chapter1/audio_data.mdx +++ b/chapters/en/chapter1/audio_data.mdx @@ -178,10 +178,9 @@ You used `librosa.amplitude_to_db()` to convert the amplitude values to the deci the finer details in the spectrum. Sometimes people use the **power spectrum**, which measures energy rather than amplitude; this is simply a spectrum with the amplitude values squared. - -💡 In practice, people use the term FFT interchangeably with DFT, as the FFT or Fast Fourier Transform is the only efficient -way to calculate the DFT on a computer. - +> [!TIP] +> 💡 In practice, people use the term FFT interchangeably with DFT, as the FFT or Fast Fourier Transform is the only efficient +> way to calculate the DFT on a computer. The frequency spectrum of an audio signal contains the exact same information as its waveform — they are simply two different ways of looking at the same data (here, the first 4096 samples from the trumpet sound). Where the waveform plots the amplitude @@ -276,12 +275,11 @@ Just as with a regular spectrogram, it's common practice to express the strength decibels. This is commonly referred to as a **log-mel spectrogram**, because the conversion to decibels involves a logarithmic operation. The above example used `librosa.power_to_db()` as `librosa.feature.melspectrogram()` creates a power spectrogram. - -💡 Not all mel spectrograms are the same! There are two different mel scales in common use ("htk" and "slaney"), -and instead of the power spectrogram the amplitude spectrogram may be used. The conversion to a log-mel spectrogram doesn't -always compute true decibels but may simply take the `log`. Therefore, if a machine learning model expects a mel spectrogram -as input, double check to make sure you're computing it the same way. - +> [!TIP] +> 💡 Not all mel spectrograms are the same! There are two different mel scales in common use ("htk" and "slaney"), +> and instead of the power spectrogram the amplitude spectrogram may be used. The conversion to a log-mel spectrogram doesn't +> always compute true decibels but may simply take the `log`. Therefore, if a machine learning model expects a mel spectrogram +> as input, double check to make sure you're computing it the same way. Creating a mel spectrogram is a lossy operation as it involves filtering the signal. Converting a mel spectrogram back into a waveform is more difficult than doing this for a regular spectrogram, as it requires estimating the frequencies diff --git a/chapters/en/chapter1/preprocessing.mdx b/chapters/en/chapter1/preprocessing.mdx index 5edd6a71..b0cc0292 100644 --- a/chapters/en/chapter1/preprocessing.mdx +++ b/chapters/en/chapter1/preprocessing.mdx @@ -61,16 +61,15 @@ minds[0] You may notice that the array values are now also different. This is because we've now got twice the number of amplitude values for every one that we had before. - -💡 Some background on resampling: If an audio signal has been sampled at 8 kHz, so that it has 8000 sample readings per -second, we know that the audio does not contain any frequencies over 4 kHz. This is guaranteed by the Nyquist sampling -theorem. Because of this, we can be certain that in between the sampling points the original continuous signal always -makes a smooth curve. Upsampling to a higher sampling rate is then a matter of calculating additional sample values that go in between -the existing ones, by approximating this curve. Downsampling, however, requires that we first filter out any frequencies -that would be higher than the new Nyquist limit, before estimating the new sample points. In other words, you can't -downsample by a factor 2x by simply throwing away every other sample — this will create distortions in the signal called -aliases. Doing resampling correctly is tricky and best left to well-tested libraries such as librosa or 🤗 Datasets. - +> [!TIP] +> 💡 Some background on resampling: If an audio signal has been sampled at 8 kHz, so that it has 8000 sample readings per +> second, we know that the audio does not contain any frequencies over 4 kHz. This is guaranteed by the Nyquist sampling +> theorem. Because of this, we can be certain that in between the sampling points the original continuous signal always +> makes a smooth curve. Upsampling to a higher sampling rate is then a matter of calculating additional sample values that go in between +> the existing ones, by approximating this curve. Downsampling, however, requires that we first filter out any frequencies +> that would be higher than the new Nyquist limit, before estimating the new sample points. In other words, you can't +> downsample by a factor 2x by simply throwing away every other sample — this will create distortions in the signal called +> aliases. Doing resampling correctly is tricky and best left to well-tested libraries such as librosa or 🤗 Datasets. ## Filtering the dataset diff --git a/chapters/en/chapter3/classification.mdx b/chapters/en/chapter3/classification.mdx index 0096414c..7d50dbe2 100644 --- a/chapters/en/chapter3/classification.mdx +++ b/chapters/en/chapter3/classification.mdx @@ -18,9 +18,8 @@ Just like ViT, the AST model splits the audio spectrogram into a sequence of par Image from the paper [AST: Audio Spectrogram Transformer](https://arxiv.org/pdf/2104.01778.pdf) - -💡 Even though here we pretend spectrograms are the same as images, there are important differences. For example, shifting the contents of an image up or down generally does not change the meaning of what is in the image. However, shifting a spectrogram up or down will change the frequencies that are in the sound and completely change its character. Images are invariant under translation but spectrograms are not. Treating spectrograms as images can work very well in practice, but keep in mind they are not really the same thing. - +> [!TIP] +> 💡 Even though here we pretend spectrograms are the same as images, there are important differences. For example, shifting the contents of an image up or down generally does not change the meaning of what is in the image. However, shifting a spectrogram up or down will change the frequencies that are in the sound and completely change its character. Images are invariant under translation but spectrograms are not. Treating spectrograms as images can work very well in practice, but keep in mind they are not really the same thing. ## Any transformer can be a classifier diff --git a/chapters/en/chapter3/ctc.mdx b/chapters/en/chapter3/ctc.mdx index dce2e8e0..2d2c514b 100644 --- a/chapters/en/chapter3/ctc.mdx +++ b/chapters/en/chapter3/ctc.mdx @@ -14,9 +14,8 @@ So far, this is very similar to what we do in NLP with a model such as BERT: an Here's the rub: In speech, we don't know the **alignment** of the audio inputs and text outputs. We know that the order the speech is spoken in is the same as the order that the text is transcribed in (the alignment is so-called monotonic), but we don't know how the characters in the transcription line up to the audio. This is where the CTC algorithm comes in. - -💡 In NLP models the vocabulary is usually made up of thousands of tokens that describe not just individual characters but parts of words or even complete words. For CTC, however, a small vocabulary works best and we generally try to keep it to less than 50 characters. We don't care about the casing of the letters, so only using uppercase (or only lowercase) is sufficient. Numbers are spelled out, e.g. `"20"` becomes `"twenty"`. In addition to the letters, we need at least a word separator token (space) and a padding token. Just as with an NLP model, the padding token allows us to combine multiple examples in a batch, but it's also the token the model will predict for silences. In English, it's also useful to keep the `'` character — after all, `"it's"` and `"its"` have very different meanings. - +> [!TIP] +> 💡 In NLP models the vocabulary is usually made up of thousands of tokens that describe not just individual characters but parts of words or even complete words. For CTC, however, a small vocabulary works best and we generally try to keep it to less than 50 characters. We don't care about the casing of the letters, so only using uppercase (or only lowercase) is sufficient. Numbers are spelled out, e.g. `"20"` becomes `"twenty"`. In addition to the letters, we need at least a word separator token (space) and a padding token. Just as with an NLP model, the padding token allows us to combine multiple examples in a batch, but it's also the token the model will predict for silences. In English, it's also useful to keep the `'` character — after all, `"it's"` and `"its"` have very different meanings. ## Dude, where's my alignment? @@ -92,9 +91,8 @@ BRION SAW SOMETHING CLOSE TO PANIC ON HIS OPPONENT'S FACE WHEN THE MAN FINALLY R To recap, the model predicts one token (character) for every 20 ms of (partially overlapping) audio from the input waveform. This gives a lot of duplicates. Thanks to the CTC blank token, we can easily remove these duplicates without destroying the proper spelling of the words. This is a very simple and convenient way to solve the problem of aligning the output text with the input audio. - -💡 In the actual Wav2Vec2 model, the CTC blank token is the same as the padding token ``. The model will predict many of these `` tokens, for example when there isn't a clear character to predict for the current 20 ms of audio. Using the same token for padding as for CTC blanking simplifies the decoding algorithm and it helps keep the vocab small. - +> [!TIP] +> 💡 In the actual Wav2Vec2 model, the CTC blank token is the same as the padding token ``. The model will predict many of these `` tokens, for example when there isn't a clear character to predict for the current 20 ms of audio. Using the same token for padding as for CTC blanking simplifies the decoding algorithm and it helps keep the vocab small. Adding CTC to a transformer encoder model is easy: the output sequence from the encoder goes into a linear layer that projects the acoustic features to the vocabulary. The model is trained with a special CTC loss. diff --git a/chapters/en/chapter3/introduction.mdx b/chapters/en/chapter3/introduction.mdx index bdf1551f..9fe5062b 100644 --- a/chapters/en/chapter3/introduction.mdx +++ b/chapters/en/chapter3/introduction.mdx @@ -92,9 +92,8 @@ In the **SpeechT5** TTS model, for example, the output from the transformer netw SpeechT5 outputs a spectrogram and uses a vocoder to create the waveform - -💡 If you take an existing waveform and apply the Short-Time Fourier Transform or STFT, it is possible to perform the inverse operation, the ISTFT, to get the original waveform again. This works because the spectrogram created by the STFT contains both amplitude and phase information, and both are needed to reconstruct the waveform. However, audio models that generate their output as a spectrogram typically only predict the amplitude information, not the phase. To turn such a spectrogram into a waveform, we have to somehow estimate the phase information. That's what a vocoder does. - +> [!TIP] +> 💡 If you take an existing waveform and apply the Short-Time Fourier Transform or STFT, it is possible to perform the inverse operation, the ISTFT, to get the original waveform again. This works because the spectrogram created by the STFT contains both amplitude and phase information, and both are needed to reconstruct the waveform. However, audio models that generate their output as a spectrogram typically only predict the amplitude information, not the phase. To turn such a spectrogram into a waveform, we have to somehow estimate the phase information. That's what a vocoder does. ### Waveform output diff --git a/chapters/en/chapter3/seq2seq.mdx b/chapters/en/chapter3/seq2seq.mdx index 07a534e3..881ce0c1 100644 --- a/chapters/en/chapter3/seq2seq.mdx +++ b/chapters/en/chapter3/seq2seq.mdx @@ -18,9 +18,8 @@ The architecture of **Whisper** is as follows (figure courtesy of [OpenAI Whispe This should look quite familiar. On the left is the **transformer encoder**. This takes as input a log-mel spectrogram and encodes that spectrogram to form a sequence of encoder hidden states that extract important features from the spoken speech. This hidden-states tensor represents the input sequence as a whole and effectively encodes the "meaning" of the input speech. - -💡 It's common for these seq2seq models to use spectrograms as input. However, a seq2seq model can also be designed to work directly on audio waveforms. - +> [!TIP] +> 💡 It's common for these seq2seq models to use spectrograms as input. However, a seq2seq model can also be designed to work directly on audio waveforms. The output of the encoder is then passed into the **transformer decoder**, shown on the right, using a mechanism called **cross-attention**. This is like self-attention but attends over the encoder output. From this point on, the encoder is no longer needed. @@ -33,9 +32,8 @@ While the architecture of the decoder is mostly identical to that of the encoder In this design, the decoder plays the role of a **language model**, processing the hidden-state representations from the encoder and generating the corresponding text transcriptions. This is a more powerful approach than CTC, even if the CTC model is combined with an external language model, as the seq2seq system can be trained end-to-end with the same training data and loss function, giving greater flexibility and generally superior performance. - -💡 Whereas a CTC model outputs a sequence of individual characters, the tokens predicted by Whisper are full words or portions of words. It uses the tokenizer from GPT-2 and has 50k+ unique tokens. A seq2seq model can therefore output a much shorter sequence than a CTC model for the same transcription. - +> [!TIP] +> 💡 Whereas a CTC model outputs a sequence of individual characters, the tokens predicted by Whisper are full words or portions of words. It uses the tokenizer from GPT-2 and has 50k+ unique tokens. A seq2seq model can therefore output a much shorter sequence than a CTC model for the same transcription. A typical loss function for a seq2seq ASR model is the cross-entropy loss, as the final layer of the model predicts a probability distribution over the possible tokens. This is usually combined with techniques such as [beam search to generate the final sequence](https://huggingface.co/blog/how-to-generate). The metric for speech recognition is WER or word error rate, which measures how many substitutions, insertions, and deletions are necessary to turn the predicted text into the target text — the fewer, the better the score. @@ -43,9 +41,8 @@ A typical loss function for a seq2seq ASR model is the cross-entropy loss, as th It may not surprise you: A seq2seq model for TTS works essentially the same as described above but with the inputs and outputs switched around! The transformer encoder takes in a sequence of text tokens and extracts a sequence of hidden-states that represent the input text. The transformer decoder applies cross-attention to the encoder output and predicts a spectrogram. - -💡 Recall that a spectrogram is made by taking the frequency spectrum of successive time slices of an audio waveform and stacking them together. In other words, a spectrogram is a sequence where the elements are (log-mel) frequency spectra, one for each timestep. - +> [!TIP] +> 💡 Recall that a spectrogram is made by taking the frequency spectrum of successive time slices of an audio waveform and stacking them together. In other words, a spectrogram is a sequence where the elements are (log-mel) frequency spectra, one for each timestep. With the ASR model, the decoder was kickstarted using a sequence that just has the special "start" token in it. For the TTS model, we can start the decoding with a spectrogram of length one that is all zeros that acts as the "start token". Given this initial spectrogram and the cross-attentions over the encoder's hidden-state representations, the decoder then predicts the next timeslice for this spectrogram, steadily growing the spectrogram one timestep at a time. diff --git a/chapters/en/chapter4/fine-tuning.mdx b/chapters/en/chapter4/fine-tuning.mdx index 496e4c7e..4b31aeaa 100644 --- a/chapters/en/chapter4/fine-tuning.mdx +++ b/chapters/en/chapter4/fine-tuning.mdx @@ -28,12 +28,9 @@ Dataset({ }) ``` - - -One of the recordings in GTZAN is corrupted, so it's been removed from the dataset. That's why we have 999 examples -instead of 1,000. - - +> [!WARNING] +> One of the recordings in GTZAN is corrupted, so it's been removed from the dataset. That's why we have 999 examples +> instead of 1,000. GTZAN doesn't provide a predefined validation set, so we'll have to create one ourselves. The dataset is balanced across @@ -279,14 +276,11 @@ different lengths. We can see that the mean value is now very much closer to zero, and the variance bang-on one! This is exactly the form we want our audio samples in prior to feeding them to the HuBERT model. - - -Note how we've passed the sampling rate of our audio data to our feature extractor. This is good practice, as the feature -extractor performs a check under-the-hood to make sure the sampling rate of our audio data matches the sampling rate -expected by the model. If the sampling rate of our audio data did not match the sampling rate of our model, we'd need to -up-sample or down-sample the audio data to the correct sampling rate. - - +> [!WARNING] +> Note how we've passed the sampling rate of our audio data to our feature extractor. This is good practice, as the feature +> extractor performs a check under-the-hood to make sure the sampling rate of our audio data matches the sampling rate +> expected by the model. If the sampling rate of our audio data did not match the sampling rate of our model, we'd need to +> up-sample or down-sample the audio data to the correct sampling rate. Great, so now we know how to process our resampled audio files, the last thing to do is define a function that we can apply to all the examples in the dataset. Since we expect the audio clips to be 30 seconds in length, we'll also @@ -345,12 +339,11 @@ DatasetDict({ }) ``` - - If you exhaust your device's RAM executing the above code, you can adjust the batch parameters to reduce the peak - RAM usage. In particular, the following two arguments can be modified: - * `batch_size`: defaults to 1000, but set to 100 above. Try reducing by a factor of 2 again to 50 - * `writer_batch_size`: defaults to 1000. Try reducing it to 500, and if that doesn't work, then reduce it by a factor of 2 again to 250 - +> [!WARNING] +> If you exhaust your device's RAM executing the above code, you can adjust the batch parameters to reduce the peak +> RAM usage. In particular, the following two arguments can be modified: +> * `batch_size`: defaults to 1000, but set to 100 above. Try reducing by a factor of 2 again to 50 +> * `writer_batch_size`: defaults to 1000. Try reducing it to 500, and if that doesn't work, then reduce it by a factor of 2 again to 250 To simplify the training, we've removed the `audio` and `file` columns from the dataset. The `input_values` column contains @@ -456,12 +449,9 @@ training_args = TrainingArguments( ) ``` - - - Here we have set `push_to_hub=True` to enable automatic upload of our fine-tuned checkpoints during training. Should you - not wish for your checkpoints to be uploaded to the Hub, you can set this to `False`. - - +> [!WARNING] +> Here we have set `push_to_hub=True` to enable automatic upload of our fine-tuned checkpoints during training. Should you +> not wish for your checkpoints to be uploaded to the Hub, you can set this to `False`. The last thing we need to do is define the metrics. Since the dataset is balanced, we'll use accuracy as our metric and load it using the 🤗 Evaluate library: @@ -496,13 +486,10 @@ trainer = Trainer( trainer.train() ``` - - -Depending on your GPU, it is possible that you will encounter a CUDA `"out-of-memory"` error when you start training. -In this case, you can reduce the `batch_size` incrementally by factors of 2 and employ [`gradient_accumulation_steps`](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments.gradient_accumulation_steps) -to compensate. - - +> [!WARNING] +> Depending on your GPU, it is possible that you will encounter a CUDA `"out-of-memory"` error when you start training. +> In this case, you can reduce the `batch_size` incrementally by factors of 2 and employ [`gradient_accumulation_steps`](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments.gradient_accumulation_steps) +> to compensate. **Output:** ```out diff --git a/chapters/en/chapter5/evaluation.mdx b/chapters/en/chapter5/evaluation.mdx index 6f9b50b2..a56b9704 100644 --- a/chapters/en/chapter5/evaluation.mdx +++ b/chapters/en/chapter5/evaluation.mdx @@ -279,10 +279,9 @@ common_voice_test = load_dataset( ) ``` - - If you face an authentication issue when loading the dataset, ensure that you have accepted the dataset's terms of use - on the Hugging Face Hub through the following link: https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 - +> [!TIP] +> If you face an authentication issue when loading the dataset, ensure that you have accepted the dataset's terms of use +> on the Hugging Face Hub through the following link: https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 Evaluating over an entire dataset can be done in much the same way as over a single example - all we have to do is **loop** over the input audios, rather than inferring just a single sample. To do this, we first transform our dataset into a @@ -310,10 +309,9 @@ for prediction in tqdm( all_predictions.append(prediction["text"]) ``` - - If you experience a CUDA out-of-memory (OOM) when running the above cell, incrementally reduce the `batch_size` by - factors of 2 until you find a batch size that fits your device. - +> [!TIP] +> If you experience a CUDA out-of-memory (OOM) when running the above cell, incrementally reduce the `batch_size` by +> factors of 2 until you find a batch size that fits your device. And finally, we can compute the WER. Let's first compute the orthographic WER, i.e. the WER without any post-processing: diff --git a/chapters/en/chapter5/fine-tuning.mdx b/chapters/en/chapter5/fine-tuning.mdx index a95e4420..7206bce8 100644 --- a/chapters/en/chapter5/fine-tuning.mdx +++ b/chapters/en/chapter5/fine-tuning.mdx @@ -79,10 +79,9 @@ DatasetDict({ }) ``` - - You can change the language identifier from `"dv"` to a language identifier of your choice. To see all possible languages - in Common Voice 13, check out the dataset card on the Hugging Face Hub: https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 - +> [!TIP] +> You can change the language identifier from `"dv"` to a language identifier of your choice. To see all possible languages +> in Common Voice 13, check out the dataset card on the Hugging Face Hub: https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 Most ASR datasets only provide input audio samples (`audio`) and the corresponding transcribed text (`sentence`). Common Voice contains additional metadata information, such as `accent` and `locale`, which we can disregard for ASR. @@ -451,9 +450,8 @@ training_args = Seq2SeqTrainingArguments( ) ``` - - If you do not want to upload the model checkpoints to the Hub, set `push_to_hub=False`. - +> [!TIP] +> If you do not want to upload the model checkpoints to the Hub, set `push_to_hub=False`. We can forward the training arguments to the 🤗 Trainer along with our model, dataset, data collator and `compute_metrics` function: diff --git a/chapters/en/chapter6/fine-tuning.mdx b/chapters/en/chapter6/fine-tuning.mdx index 8e7a3bbb..94e1ff21 100644 --- a/chapters/en/chapter6/fine-tuning.mdx +++ b/chapters/en/chapter6/fine-tuning.mdx @@ -10,12 +10,9 @@ Make sure that you have a GPU if you want to reproduce this example. In a notebo nvidia-smi ``` - - -In our example we will be using approximately 40 hours of training data. If you'd like to follow along using the Google Colab free tier GPU, -you will need to reduce the amount of training data to approximately 10-15 hours, and reduce the number of training steps. - - +> [!WARNING] +> In our example we will be using approximately 40 hours of training data. If you'd like to follow along using the Google Colab free tier GPU, +> you will need to reduce the amount of training data to approximately 10-15 hours, and reduce the number of training steps. You'll also need some additional dependencies: diff --git a/chapters/en/chapter6/pre-trained_models.mdx b/chapters/en/chapter6/pre-trained_models.mdx index ba076a32..5560580e 100644 --- a/chapters/en/chapter6/pre-trained_models.mdx +++ b/chapters/en/chapter6/pre-trained_models.mdx @@ -38,13 +38,10 @@ need the text encoder pre-net for the text inputs and the speech decoder pre- an This approach allows to obtain several models fine-tuned for different speech tasks that all benefit from the initial pre-training on unlabeled data. - - -Even though the fine-tuned models start out using the same set of weights from the shared pre-trained model, the -final versions are all quite different in the end. You can't take a fine-tuned ASR model and swap out the pre-nets and -post-net to get a working TTS model, for example. SpeechT5 is flexible, but not that flexible ;) - - +> [!TIP] +> Even though the fine-tuned models start out using the same set of weights from the shared pre-trained model, the +> final versions are all quite different in the end. You can't take a fine-tuned ASR model and swap out the pre-nets and +> post-net to get a working TTS model, for example. SpeechT5 is flexible, but not that flexible ;) Let's see what are the pre- and post-nets that SpeechT5 uses for the TTS task specifically: @@ -83,22 +80,19 @@ inputs = processor(text="Don't count the days, make the days count.", return_ten The SpeechT5 TTS model is not limited to creating speech for a single speaker. Instead, it uses so-called speaker embeddings that capture a particular speaker's voice characteristics. - - -Speaker embeddings is a method of representing a speaker's identity in a compact way, as a vector of -fixed size, regardless of the length of the utterance. These embeddings capture essential information about a speaker's -voice, accent, intonation, and other unique characteristics that distinguish one speaker from another. Such embeddings can -be used for speaker verification, speaker diarization, speaker identification, and more. -The most common techniques for generating speaker embeddings include: - -* I-Vectors (identity vectors): I-Vectors are based on a Gaussian mixture model (GMM). They represent speakers as low-dimensional fixed-length vectors derived from the statistics of a speaker-specific GMM, and are obtained in unsupervised manner. -* X-Vectors: X-Vectors are derived using deep neural networks (DNNs) and capture frame-level speaker information by incorporating temporal context. - -[X-Vectors](https://www.danielpovey.com/files/2018_icassp_xvectors.pdf) are a state-of-the-art method that shows superior performance -on evaluation datasets compared to I-Vectors. The deep neural network is used to obtain X-Vectors: it trains to discriminate -between speakers, and maps variable-length utterances to fixed-dimensional embeddings. You can also load an X-Vector speaker embedding that has been computed ahead of time, which will encapsulate the speaking characteristics of a particular speaker. - - +> [!TIP] +> Speaker embeddings is a method of representing a speaker's identity in a compact way, as a vector of +> fixed size, regardless of the length of the utterance. These embeddings capture essential information about a speaker's +> voice, accent, intonation, and other unique characteristics that distinguish one speaker from another. Such embeddings can +> be used for speaker verification, speaker diarization, speaker identification, and more. +> The most common techniques for generating speaker embeddings include: +> +> * I-Vectors (identity vectors): I-Vectors are based on a Gaussian mixture model (GMM). They represent speakers as low-dimensional fixed-length vectors derived from the statistics of a speaker-specific GMM, and are obtained in unsupervised manner. +> * X-Vectors: X-Vectors are derived using deep neural networks (DNNs) and capture frame-level speaker information by incorporating temporal context. +> +> [X-Vectors](https://www.danielpovey.com/files/2018_icassp_xvectors.pdf) are a state-of-the-art method that shows superior performance +> on evaluation datasets compared to I-Vectors. The deep neural network is used to obtain X-Vectors: it trains to discriminate +> between speakers, and maps variable-length utterances to fixed-dimensional embeddings. You can also load an X-Vector speaker embedding that has been computed ahead of time, which will encapsulate the speaking characteristics of a particular speaker. Let's load such a speaker embedding from a dataset on the Hub. The embeddings were obtained from the [CMU ARCTIC dataset](http://www.festvox.org/cmu_arctic/) using @@ -131,23 +125,20 @@ However, if we are looking to generate speech waveform, we need to specify a voc In theory, you can use any vocoder that works on 80-bin mel spectrograms. Conveniently, 🤗 Transformers offers a vocoder based on HiFi-GAN. Its weights were kindly provided by the original authors of SpeechT5. - - -[HiFi-GAN](https://arxiv.org/pdf/2010.05646v2.pdf) is a state-of-the-art generative adversarial network (GAN) designed -for high-fidelity speech synthesis. It is capable of generating high-quality and realistic audio waveforms from spectrogram inputs. - -On a high level, HiFi-GAN consists of one generator and two discriminators. The generator is a fully convolutional -neural network that takes a mel-spectrogram as input and learns to produce raw audio waveforms. The discriminators' -role is to distinguish between real and generated audio. The two discriminators focus on different aspects of the audio. - -HiFi-GAN is trained on a large dataset of high-quality audio recordings. It uses a so-called adversarial training, -where the generator and discriminator networks compete against each other. Initially, the generator produces low-quality -audio, and the discriminator can easily differentiate it from real audio. As training progresses, the generator improves -its output, aiming to fool the discriminator. The discriminator, in turn, becomes more accurate in distinguishing real -and generated audio. This adversarial feedback loop helps both networks improve over time. Ultimately, HiFi-GAN learns to -generate high-fidelity audio that closely resembles the characteristics of the training data. - - +> [!TIP] +> [HiFi-GAN](https://arxiv.org/pdf/2010.05646v2.pdf) is a state-of-the-art generative adversarial network (GAN) designed +> for high-fidelity speech synthesis. It is capable of generating high-quality and realistic audio waveforms from spectrogram inputs. +> +> On a high level, HiFi-GAN consists of one generator and two discriminators. The generator is a fully convolutional +> neural network that takes a mel-spectrogram as input and learns to produce raw audio waveforms. The discriminators' +> role is to distinguish between real and generated audio. The two discriminators focus on different aspects of the audio. +> +> HiFi-GAN is trained on a large dataset of high-quality audio recordings. It uses a so-called adversarial training, +> where the generator and discriminator networks compete against each other. Initially, the generator produces low-quality +> audio, and the discriminator can easily differentiate it from real audio. As training progresses, the generator improves +> its output, aiming to fool the discriminator. The discriminator, in turn, becomes more accurate in distinguishing real +> and generated audio. This adversarial feedback loop helps both networks improve over time. Ultimately, HiFi-GAN learns to +> generate high-fidelity audio that closely resembles the characteristics of the training data. Loading the vocoder is as easy as any other 🤗 Transformers model. @@ -334,11 +325,8 @@ Audio(speech_output[2], rate=sampling_rate) Your browser does not support the audio element. - - -Bark, like other 🤗 Transformers models, can be optimized in just a few lines of code regarding speed and memory impact. To find out how, click on [this colab demonstration notebook](https://colab.research.google.com/github/ylacombe/notebooks/blob/main/Benchmark_Bark_HuggingFace.ipynb). - - +> [!TIP] +> Bark, like other 🤗 Transformers models, can be optimized in just a few lines of code regarding speed and memory impact. To find out how, click on [this colab demonstration notebook](https://colab.research.google.com/github/ylacombe/notebooks/blob/main/Benchmark_Bark_HuggingFace.ipynb). ## Massive Multilingual Speech (MMS) @@ -355,15 +343,12 @@ generated. The waveform is then decoded using transposed convolutional layers ad During inference, the text encodings are upsampled and transformed into waveforms using the flow module and HiFi-GAN decoder. Like Bark, there's no need for a vocoder, as waveforms are generated directly. - - -MMS model has been added to 🤗 Transformers very recently, so you will have to install the library from source: - -```bash -pip install git+https://github.com/huggingface/transformers.git -``` - - +> [!WARNING] +> MMS model has been added to 🤗 Transformers very recently, so you will have to install the library from source: +> +> ```bash +> pip install git+https://github.com/huggingface/transformers.git +> ``` Let's give MMS a go, and see how we can synthesize speech in a language other than English, e.g. German. First, we'll load the model checkpoint and the tokenizer for the correct language: diff --git a/chapters/en/chapter7/hands_on.mdx b/chapters/en/chapter7/hands_on.mdx index 1486a5a9..d4899155 100644 --- a/chapters/en/chapter7/hands_on.mdx +++ b/chapters/en/chapter7/hands_on.mdx @@ -24,13 +24,12 @@ checkpoints, either the checkpoint [sanchit-gandhi/speecht5_tts_vox_nl](https:// which is a SpeechT5 checkpoint fine-tuned on the Dutch split of the [VoxPopuli](https://huggingface.co/datasets/facebook/voxpopuli) dataset, or an MMS TTS checkpoint (see section on [pretrained models for TTS](../chapter6/pre-trained_models)). - - In our experience experimenting with the Dutch language, using an MMS TTS checkpoint results in better performance than a - fine-tuned SpeechT5 one, but you might find that your fine-tuned TTS checkpoint is preferable in your language. - If you decide to use an MMS TTS checkpoint, you will need to update the requirements.txt - file of your demo to install transformers from the PR branch: -

git+https://github.com/hollance/transformers.git@6900e8ba6532162a8613d2270ec2286c3f58f57b

-
+> [!TIP] +> In our experience experimenting with the Dutch language, using an MMS TTS checkpoint results in better performance than a +> fine-tuned SpeechT5 one, but you might find that your fine-tuned TTS checkpoint is preferable in your language. +> If you decide to use an MMS TTS checkpoint, you will need to update the requirements.txt +> file of your demo to install transformers from the PR branch: +>

git+https://github.com/hollance/transformers.git@6900e8ba6532162a8613d2270ec2286c3f58f57b

Your demo should take as input an audio file, and return as output another audio file, matching the signature of the diff --git a/chapters/en/chapter7/speech-to-speech.mdx b/chapters/en/chapter7/speech-to-speech.mdx index 3a4ee424..b355722f 100644 --- a/chapters/en/chapter7/speech-to-speech.mdx +++ b/chapters/en/chapter7/speech-to-speech.mdx @@ -91,15 +91,12 @@ def translate(audio): return outputs["text"] ``` - - - Whisper can also be 'tricked' into translating from speech in any language X to any language Y. Simply set the task to - `"transcribe"` and the `"language"` to your target language in the generation key-word arguments, - e.g. for Spanish, one would set: - - `generate_kwargs={"task": "transcribe", "language": "es"}` - - +> [!TIP] +> Whisper can also be 'tricked' into translating from speech in any language X to any language Y. Simply set the task to +> `"transcribe"` and the `"language"` to your target language in the generation key-word arguments, +> e.g. for Spanish, one would set: +> +> `generate_kwargs={"task": "transcribe", "language": "es"}` Great! Let's quickly check that we get a sensible result from the model: @@ -144,11 +141,10 @@ model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts") vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan") ``` - - Here we're using SpeechT5 checkpoint trained specifically for English TTS. Should you wish to translate into a language - other than English, either swap the checkpoint for a SpeechT5 TTS model fine-tuned on your language of choice, or - use an MMS TTS checkpoint pre-trained in your target langauge. - +> [!TIP] +> Here we're using SpeechT5 checkpoint trained specifically for English TTS. Should you wish to translate into a language +> other than English, either swap the checkpoint for a SpeechT5 TTS model fine-tuned on your language of choice, or +> use an MMS TTS checkpoint pre-trained in your target langauge. As with the Whisper model, we'll place the SpeechT5 model and vocoder on our GPU accelerator device if we have one: ```python diff --git a/chapters/en/chapter7/transcribe-meeting.mdx b/chapters/en/chapter7/transcribe-meeting.mdx index 0f8a0118..340019f3 100644 --- a/chapters/en/chapter7/transcribe-meeting.mdx +++ b/chapters/en/chapter7/transcribe-meeting.mdx @@ -168,11 +168,10 @@ pipeline = ASRDiarizationPipeline( ) ``` - - You can also instantiate the ASRDiarizationPipeline directly from pre-trained by specifying the model id - of an ASR model on the Hub: -

pipeline = ASRDiarizationPipeline.from_pretrained("openai/whisper-base")

-
+> [!TIP] +> You can also instantiate the ASRDiarizationPipeline directly from pre-trained by specifying the model id +> of an ASR model on the Hub: +>

pipeline = ASRDiarizationPipeline.from_pretrained("openai/whisper-base")

Let's pass the audio file to the composite pipeline and see what we get out: diff --git a/chapters/en/chapter7/voice-assistant.mdx b/chapters/en/chapter7/voice-assistant.mdx index caeb61e2..ce279ce7 100644 --- a/chapters/en/chapter7/voice-assistant.mdx +++ b/chapters/en/chapter7/voice-assistant.mdx @@ -48,12 +48,11 @@ the device. Again, we've done this several times now, so the process will be very familiar! - - The following section requires the use of a microphone to record a voice input. Since Google Colab machines do not - have microphone compatibility, it is recommended to run this section locally, either on your CPU, or on a GPU if you - have local access. The checkpoint sizes have been selected as those small enough to run adequately fast on CPU, so - you will still get good performance without a GPU. - +> [!TIP] +> The following section requires the use of a microphone to record a voice input. Since Google Colab machines do not +> have microphone compatibility, it is recommended to run this section locally, either on your CPU, or on a GPU if you +> have local access. The checkpoint sizes have been selected as those small enough to run adequately fast on CPU, so +> you will still get good performance without a GPU. ## Wake word detection @@ -225,11 +224,10 @@ transcriber = pipeline( ) ``` - - If you're using a GPU, you can increase the checkpoint size to use the Whisper Small English - checkpoint, which will return better transcription accuracy and still be within the required latency threshold. Simply swap the - model id to: "openai/whisper-small.en". - +> [!TIP] +> If you're using a GPU, you can increase the checkpoint size to use the Whisper Small English +> checkpoint, which will return better transcription accuracy and still be within the required latency threshold. Simply swap the +> model id to: "openai/whisper-small.en". We can now define a function to record our microphone input and transcribe the corresponding text. With the `ffmpeg_microphone_live` helper function, we can control how 'real-time' our speech recognition model is. Using a smaller `stream_chunk_s` lends @@ -444,10 +442,9 @@ agent.run("Generate an image of a cat") - - Note that the first time calling this will trigger the model weights to be downloaded, which might take - some time depending on your Hub download speed. - +> [!TIP] +> Note that the first time calling this will trigger the model weights to be downloaded, which might take +> some time depending on your Hub download speed. Easy as that! The Agent interpreted our prompt, and used [Stable Diffusion](https://huggingface.co/docs/diffusers/using-diffusers/conditional_image_generation) under the hood to generate the image, without us having to worry about loading the model, writing the function or executing diff --git a/chapters/es/chapter1/audio_data.mdx b/chapters/es/chapter1/audio_data.mdx index 54f06511..4114ff82 100644 --- a/chapters/es/chapter1/audio_data.mdx +++ b/chapters/es/chapter1/audio_data.mdx @@ -187,10 +187,9 @@ Utilizamos `librosa.amplitude_to_db()` para convertir los valores de amplitud a sutiles en el espectro. A veces, las personas utilizan el **espectro de potencia**, que mide la energía en lugar de la amplitud; esto es simplemente un espectro con los valores de amplitud elevados al cuadrado.+ - -💡 En la práctica, las personas utilizan indistintamente los términos FFT (Transformada Rápida de Fourier) y DFT (Transformada Discreta de Fourier), -ya que la FFT es la única forma eficiente de calcular la DFT en una computadora. - +> [!TIP] +> 💡 En la práctica, las personas utilizan indistintamente los términos FFT (Transformada Rápida de Fourier) y DFT (Transformada Discreta de Fourier), +> ya que la FFT es la única forma eficiente de calcular la DFT en una computadora. El espectro de frecuencia de una señal de audio contiene exactamente la misma información que su representación en el dominio del tiempo(forma de onda); simplemente son dos formas diferentes de ver los mismos datos (en este caso, los primeros 4096 valores @@ -289,14 +288,12 @@ Al igual que con un espectrograma regular, es práctica común expresar la inten El ejemplo anterior se usó `librosa.power_to_db()` ya que la función `librosa.feature.melspectrogram()` crea un espectrograma de potencia. - - -💡 ¡No todos los espectrogramas mel son iguales! Existen dos variantes comumente usadas de las escalas de mel("htk" y "slaney"), -, y en lugar del espectrograma de potencia, se puede estar usando el espectrograma de amplitud. El cálculo de un espectrograma -logarítmico de mel no siempre usa decibelios reales, puede que se haya aplicado solamente la función `log`. Por lo tanto, -si un modelo de aprendizaje automático espera un espectrograma de mel como entrada, verifica que estés calculándolo de la misma manera - para asegurarte de que sea compatible. - +> [!TIP] +> 💡 ¡No todos los espectrogramas mel son iguales! Existen dos variantes comumente usadas de las escalas de mel("htk" y "slaney"), +> , y en lugar del espectrograma de potencia, se puede estar usando el espectrograma de amplitud. El cálculo de un espectrograma +> logarítmico de mel no siempre usa decibelios reales, puede que se haya aplicado solamente la función `log`. Por lo tanto, +> si un modelo de aprendizaje automático espera un espectrograma de mel como entrada, verifica que estés calculándolo de la misma manera +> para asegurarte de que sea compatible. La creación de un espectrograma mel es una operación con pérdidas, ya que implica filtrar la señal. Convertir un espectrograma de mel de nuevo en una forma de onda es más difícil que hacerlo para un espectrograma regular, ya que requiere estimar las frecuencias que se eliminaron. diff --git a/chapters/es/chapter1/preprocessing.mdx b/chapters/es/chapter1/preprocessing.mdx index 9dc79389..361949b7 100644 --- a/chapters/es/chapter1/preprocessing.mdx +++ b/chapters/es/chapter1/preprocessing.mdx @@ -60,16 +60,15 @@ minds[0] Puedes ver que los valores del array ahora son diferentes. Esto es porque ahora tenemos el doble de valores de amplitud de la longitud original. - -💡 Si una señal de audio ha sido grabada a una frecuencia de muestreo de 8kHz, de manera que cada segundo de la -señal esta representado por 8000 muestras, sabemos tambien que el audio no contiene ninguna frecuencia por encima -de 4kHz. Esto esta garantizado por el teorema de Nyquist. Resamplear a una frecuencia de muestro mayor(Upsampling) -consiste en estimar los puntos adicionales que irian entre las muestras existentes. El proceso de Downsampling, requiere en cambio, -que primero filtremos cualquier frecuencia que sea mayor al nuevo Limite de Nyquist antes de estimar las nuevas muestras. -En otras palabras, no puedes hacer downsampling por un facto de 2x solo descartando la mitad de muestras de la señal - Esto -crearía distorsiones en la señal llamadas alias. Hacer resampliing de la manera correcta es complejo por lo que es mejor -usar librerias que han sido probadas a lo largo de los años como lo son librosa o 🤗 Datasets. - +> [!TIP] +> 💡 Si una señal de audio ha sido grabada a una frecuencia de muestreo de 8kHz, de manera que cada segundo de la +> señal esta representado por 8000 muestras, sabemos tambien que el audio no contiene ninguna frecuencia por encima +> de 4kHz. Esto esta garantizado por el teorema de Nyquist. Resamplear a una frecuencia de muestro mayor(Upsampling) +> consiste en estimar los puntos adicionales que irian entre las muestras existentes. El proceso de Downsampling, requiere en cambio, +> que primero filtremos cualquier frecuencia que sea mayor al nuevo Limite de Nyquist antes de estimar las nuevas muestras. +> En otras palabras, no puedes hacer downsampling por un facto de 2x solo descartando la mitad de muestras de la señal - Esto +> crearía distorsiones en la señal llamadas alias. Hacer resampliing de la manera correcta es complejo por lo que es mejor +> usar librerias que han sido probadas a lo largo de los años como lo son librosa o 🤗 Datasets. ## Filtrando el conjunto de datos diff --git a/chapters/fr/chapter1/audio_data.mdx b/chapters/fr/chapter1/audio_data.mdx index 1456b8f2..04a45939 100644 --- a/chapters/fr/chapter1/audio_data.mdx +++ b/chapters/fr/chapter1/audio_data.mdx @@ -1,223 +1,221 @@ -# Introduction aux données audio - -Le **taux d'échantillonnage** (également appelé fréquence d'échantillonnage) est le nombre d'échantillons prélevés en une seconde et est mesuré en hertz (Hz). -Pour vous donner un point de référence, l'audio de qualité CD a un taux d'échantillonnage de 44 100 Hz, ce qui signifie que les échantillons sont prélevés 44 100 fois par seconde. À titre de comparaison, l'audio haute résolution a un taux d'échantillonnage de 192 000 Hz ou 192 kHz. -Un taux d'échantillonnage couramment utilisé dans les modèles vocaux d'apprentissage est de 16 000 Hz ou 16 kHz. - -Le choix de la fréquence d'échantillonnage détermine principalement la fréquence la plus élevée qui peut être capturée à partir du signal. -Ceci est également connu sous le nom de limite de Nyquist et correspond exactement à la moitié du taux d'échantillonnage. -Les fréquences audibles dans la parole humaine sont inférieures à 8 kHz et, par conséquent, l'échantillonnage de la parole à 16 kHz est suffisant. -L'utilisation d'un taux d'échantillonnage plus élevé ne permettra pas de capturer plus d'informations et ne fera qu'augmenter le coût de calcul du traitement de ces fichiers. -D'autre part, l'échantillonnage audio à un taux d'échantillonnage trop faible entraînera une perte d'informations. -La parole échantillonnée à 8 kHz sonnera étouffée car les fréquences plus élevées ne peuvent pas être capturées à ce rythme. - -Il est important de vous assurer que tous les exemples audio de votre jeu de données ont le même taux d'échantillonnage lorsque vous travaillez sur une tâche audio. -Si vous prévoyez d'utiliser des données audio personnalisées pour finetuner un modèle pré-entraîné, le taux d'échantillonnage de vos données doit correspondre au taux d'échantillonnage des données sur lesquelles le modèle a été pré-entraîné. -La fréquence d'échantillonnage détermine l'intervalle de temps entre les échantillons audio successifs, ce qui a un impact sur la résolution temporelle des données audio. -Prenons un exemple : un son de 5 secondes à une fréquence d'échantillonnage de 16 000 Hz sera représenté comme une série de 80 000 valeurs, tandis que le même son de 5 secondes à une fréquence d'échantillonnage de 8 000 Hz sera représenté comme une série de 40 000 valeurs. -Les *transformers* qui résolvent les tâches audio traitent les exemples comme des séquences et s'appuient sur des mécanismes d'attention pour apprendre l'audio ou la représentation multimodale. -Étant donné que les séquences sont différentes pour les exemples audio à des taux d'échantillonnage différents, il sera difficile pour les modèles de généraliser entre les taux d'échantillonnage. -**Le rééchantillonnage** est le processus de mise en correspondance des taux d'échantillonnage et fait partie du [prétraitement](preprocessing#resampling-the-audio-data) des données audio. - -## Amplitude et profondeur de bits - -Bien que le taux d'échantillonnage vous indique à quelle fréquence les échantillons sont prélevés, quelles sont exactement les valeurs de chaque échantillon? -Le son est produit par des changements de pression atmosphérique à des fréquences audibles pour les humains. -L'**amplitude** d'un son décrit le niveau de pression acoustique à un instant donné et est mesurée en décibels (dB). -Nous percevons l'amplitude comme un volume sonore. Pour vous donner un exemple, une voix normale est inférieure à 60 dB, et un concert de rock peut être autour de 125 dB, repoussant les limites de l'audition humaine. -En audio, chaque échantillon enregistre l'amplitude de l'onde à un moment donné. -La **profondeur de bits** de l'échantillon détermine avec quelle précision cette valeur d'amplitude peut être décrite. -Plus la profondeur de bits est élevée, plus la représentation numérique se rapproche fidèlement de l'onde sonore continue d'origine. -Les profondeurs de bits audio les plus courantes sont 16 bits et 24 bits. -Chacun est un terme binaire, représentant le nombre d'étapes possibles auxquelles la valeur d'amplitude peut être quantifiée lorsqu'elle est convertie de continue à discrète: 65 536 étapes pour l'audio 16 bits, 16 777 216 étapes pour l'audio 24 bits. -Étant donné que la quantification implique d'arrondir la valeur continue à une valeur discrète, le processus d'échantillonnage introduit du bruit. Plus la profondeur de bits est élevée, plus ce bruit de quantification est faible. -En pratique, le bruit de quantification de l'audio 16 bits est déjà suffisamment faible pour être inaudible et l'utilisation de profondeurs de bits plus élevées n'est généralement pas nécessaire. -Vous pouvez également rencontrer de l'audio 32 bits. -Cela stocke les échantillons sous forme de valeurs à virgule flottante, tandis que l'audio 16 bits et 24 bits utilise des échantillons entiers. -La précision d'une valeur à virgule flottante de 32 bits est de 24 bits, ce qui lui donne la même profondeur de bits que l'audio 24 bits. -Les échantillons audio en virgule flottante devraient se situer dans la plage [-1.0, 1.0]. -Étant donné que les modèles d'apprentissage automatique fonctionnent naturellement sur des données en virgule flottante, l'audio doit d'abord être converti au format à virgule flottante avant de pouvoir être utilisé pour entraîner le modèle. -Nous verrons comment faire cela dans la section suivante sur le [Prétraitement] (preprocessing). -Tout comme pour les signaux audio continus, l'amplitude de l'audio numérique est généralement exprimée en décibels (dB). -L'audition humaine étant de nature logarithmique - nos oreilles sont plus sensibles aux petites fluctuations des sons calmes qu'à celles des sons forts - l'intensité d'un son est plus facile à interpréter si les amplitudes sont exprimées en décibels, qui sont également logarithmiques. -L'échelle de décibels pour l'audio réel commence à 0 dB, ce qui représente le son le plus faible possible que les humains peuvent entendre, et les sons plus forts ont des valeurs plus importantes. -Cependant, pour les signaux audio numériques, 0 dB est l'amplitude la plus forte possible, tandis que toutes les autres amplitudes sont négatives. -En règle générale: chaque -6 dB est une réduction de moitié de l'amplitude, et tout ce qui est inférieur à -60 dB est généralement inaudible à moins que vous n'augmentiez vraiment le volume. - -## L'audio comme forme d'onde - -Vous avez peut-être vu des sons visualisés sous la **forme d'onde** qui traçant les valeurs de l'échantillon au fil du temps et illustrant les changements d'amplitude du son. Ceci est aussi connu sous le nom de représentation du *domaine temporel* du son. -Ce type de visualisation est utile pour identifier des caractéristiques spécifiques du signal audio telles que la synchronisation des événements sonores individuels, l'intensité sonore globale du signal et toute irrégularité ou bruit présent dans l'audio. - -Pour tracer la forme d'onde d'un signal audio, nous pouvons utiliser une bibliothèque Python `librosa`: - -```bash -pip install librosa -``` - -Prenons un exemple de son appelé « trompette » qui vient avec la bibliothèque: - -```py -import librosa - -array, sampling_rate = librosa.load(librosa.ex("trumpet")) -``` - -L'exemple est chargé sous la forme d'un tuple de séries temporelles audio (ici nous l'appelons `array`) et de taux d'échantillonnage (`sampling_rate`). -Jetons un coup d'œil à la forme d'onde de ce son en utilisant la fonction `waveshow()` de librosa: - -```py -import matplotlib.pyplot as plt -import librosa.display - -plt.figure().set_figwidth(12) -librosa.display.waveshow(array, sr=sampling_rate) -``` - -
- Waveform plot -
- -Cela trace l'amplitude du signal sur l'axe des y et le temps le long de l'axe des x. -En d'autres termes, chaque point correspond à une seule valeur d'échantillon qui a été prise lors de l'échantillonnage de ce son. -Notez également que librosa renvoie déjà l'audio sous forme de valeurs à virgule flottante et que les valeurs d'amplitude sont effectivement comprises dans la plage [-1.0, 1.0]. -Visualiser l'audio et l'écouter peut être un outil utile pour comprendre les données avec lesquelles vous travaillez. -Vous pouvez voir la forme du signal, observer des modèles, apprendre à repérer le bruit ou la distorsion. -Si vous prétraitez les données d'une manière ou d'une autre, telle que la normalisation, le rééchantillonnage ou le filtrage, vous pouvez confirmer visuellement que les étapes de prétraitement ont été appliquées comme prévu. -Après avoir entraîné un modèle, vous pouvez également visualiser des exemples où des erreurs se produisent (par exemple, dans la tâche de classification audio) pour déboguer le problème. - -## Le spectre de fréquences - -Une autre façon de visualiser les données audio consiste à tracer le **spectre de fréquences** d'un signal audio, également connu sous le nom de représentation du *domaine fréquentiel*. -Le spectre est calculé à l'aide de la transformée de Fourier discrète ou TFD. Il décrit les fréquences individuelles qui composent le signal et leur force. -Traçons le spectre de fréquences pour le même son de trompette en prenant la TFD en utilisant la fonction `rfft()` de numpy. Bien qu'il soit possible de tracer le spectre de l'ensemble du son, il est plus utile de regarder une petite région à la place. -Ici, nous allons prendre la TFD sur les 4096 premiers échantillons, ce qui correspond à peu près à la longueur de la première note jouée: - -```py -import numpy as np - -TFD_input = array[:4096] - -# calculer la TDF -window = np.hanning(len(TFD_input)) -windowed_input = TFD_input * window -TFD = np.fft.rfft(windowed_input) - -# obtenir le spectre d'amplitude en décibels -amplitude = np.abs(TFD) -amplitude_db = librosa.amplitude_to_db(amplitude, ref=np.max) - -# obtenir les bacs de fréquence -frequency = librosa.fft_frequencies(sr=sampling_rate, n_fft=len(TFD_input)) - -plt.figure().set_figwidth(12) -plt.plot(frequency, amplitude_db) -plt.xlabel("Frequency (Hz)") -plt.ylabel("Amplitude (dB)") -plt.xscale("log") -``` - -
- Spectrum plot -
- -Cela trace la force des différentes composantes de fréquence présentes dans ce segment audio. Les valeurs de fréquence sont sur l'axe des abscisses, généralement tracées sur une échelle logarithmique, tandis que leurs amplitudes sont sur l'axe des y. - -Le spectre de fréquences que nous avons tracé montre plusieurs pics. -Ces pics correspondent aux harmoniques de la note jouée, les harmoniques supérieures étant plus calmes. Puisque le premier pic est à environ 620 Hz, c'est le spectre de fréquence d'une♭ note Mi. - -La sortie de la TDF est un tableau de nombres complexes, composé de composants réels et imaginaires. -Prendre la magnitude avec ` np.abs(TFD)` extrait l'information d'amplitude du spectrogramme. -L'angle entre les composants réels et imaginaires fournit ce que l'on appelle le spectre de phase, mais il est souvent écarté dans les applications d'apprentissage automatique. -Nous utilions `librosa.amplitude_to_db()` pour convertir les valeurs d'amplitude en échelle de décibels, ce qui facilite la visualisation des détails les plus fins du spectre. -Parfois, les gens utilisent le **spectre de puissance**, qui mesure l'énergie plutôt que l'amplitude. Il s'agit simplement d'un spectre avec les valeurs d'amplitude au carré. - - -💡 En pratique, les gens utilisent le terme FFT de manière interchangeable avec TFD, car la FFT ou transformée de Fourier rapide est le seul moyen efficace de calculer la TFD sur un ordinateur. - - -Le spectre de fréquences d'un signal audio contient exactement la même information que sa forme d'onde. Ce sont simplement deux façons différentes de regarder les mêmes données (ici, les 4096 premiers échantillons du son de la trompette). -Là où la forme d'onde trace l'amplitude du signal audio au fil du temps, le spectre visualise les amplitudes des fréquences individuelles à un moment donné. - -## Spectrogramme - -Et si nous voulons voir comment les fréquences d'un signal audio changent ? La trompette joue plusieurs notes et elles ont toutes des fréquences différentes. -Le problème est que le spectre ne montre qu'un instantané figé des fréquences à un instant donné. -La solution consiste à prendre plusieurs TFD, chacune ne couvrant qu'une petite tranche de temps, et à empiler les spectres résultants dans un **spectrogramme**. - -Un spectrogramme trace le contenu en fréquence d'un signal audio au fil du temps. Il vous permet de voir le temps, la fréquence et l'amplitude sur un seul graphique. L'algorithme qui effectue ce calcul est la TFCT ou transformée de Fourier à court terme. -Le spectrogramme est l'un des outils audio les plus informatifs à notre disposition. -Par exemple, lorsque nous travaillons avec un enregistrement musical, nous pouvons voir les différents instruments et pistes vocales et comment ils contribuent au son global. Dans la parole, nous pouvons identifier différents sons de voyelles car chaque voyelle est caractérisée par des fréquences particulières. -Traçons un spectrogramme pour le même son de trompette, en utilisant les fonctions `stft()` et `specshow()` de librosa: - -```py -import numpy as np - -D = librosa.stft(array) -S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max) - -plt.figure().set_figwidth(12) -librosa.display.specshow(S_db, x_axis="time", y_axis="hz") -plt.colorbar() -``` - -
- Spectrogram plot -
- -Dans ce graphique, l'axe des x représente le temps comme dans la visualisation de la forme d'onde, mais maintenant l'axe des y représente la fréquence en Hz. -L'intensité de la couleur donne l'amplitude ou la puissance de la composante fréquence à chaque point dans le temps, mesurée en décibels (dB). - -Le spectrogramme est créé en prenant de courts segments du signal audio, généralement de quelques millisecondes, et en calculant la transformée de Fourier discrète de chaque segment pour obtenir son spectre de fréquences. -Les spectres résultants sont ensuite empilés sur l'axe temporel pour créer le spectrogramme. -Chaque tranche verticale de cette image correspond à un spectre de fréquences unique, vu du haut. -Par défaut, `librosa.stft()` divise le signal audio en segments de 2048 échantillons, ce qui donne un bon compromis entre la résolution de fréquence et la résolution temporelle. -Étant donné que le spectrogramme et la forme d'onde sont des vues différentes des mêmes données, il est possible de retourner le spectrogramme dans la forme d'onde d'origine en utilisant la TFCT inverse. -Cependant, cela nécessite les informations de phase en plus des informations d'amplitude. Si le spectrogramme a été généré par un modèle d'apprentissage automatique, il ne produit généralement que les amplitudes. -Dans ce cas, nous pouvons utiliser un algorithme de reconstruction de phase tel que l'algorithme classique de Griffin-Lim, ou en utilisant un réseau neuronal appelé vocodeur, pour reconstruire une forme d'onde à partir du spectrogramme. - -Les spectrogrammes ne sont pas seulement utilisés pour la visualisation. De nombreux modèles d'apprentissage automatique prendront des spectrogrammes en entrée, par opposition aux formes d'onde, et produiront des spectrogrammes en sortie. -Maintenant que nous savons ce qu'est un spectrogramme et comment il est fabriqué, jetons un coup d'œil à une variante de celui-ci largement utilisée pour le traitement de la parole: le spectrogramme mel. - -## Spectrogramme Mel - -Un spectrogramme mel est une variante du spectrogramme couramment utilisée dans les tâches de traitement de la parole et d'apprentissage automatique. -Il est similaire à un spectrogramme en ce sens qu'il montre le contenu en fréquence d'un signal audio au fil du temps, mais sur un axe de fréquence différent. -Dans un spectrogramme standard, l'axe de fréquence est linéaire et est mesuré en hertz (Hz). -Cependant, le système auditif humain est plus sensible aux changements dans les basses fréquences que dans les fréquences plus élevées, et cette sensibilité diminue logarithmiquement à mesure que la fréquence augmente. -L'échelle mel est une échelle perceptuelle qui se rapproche de la réponse en fréquence non linéaire de l'oreille humaine. -Pour créer un spectrogramme mel, le STFT est utilisé comme auparavant, divisant l'audio en segments courts pour obtenir une séquence de spectres de fréquence. -De plus, chaque spectre est envoyé à travers un ensemble de filtres, appelé *mel filterbank*, pour transformer les fréquences à l'échelle mel. -Voyons comment nous pouvons tracer un spectrogramme mel en utilisant la fonction `melspectrogram()` de librosa, qui effectue toutes ces étapes pour nous: - -```py -S = librosa.feature.melspectrogram(y=array, sr=sampling_rate, n_mels=128, fmax=8000) -S_dB = librosa.power_to_db(S, ref=np.max) - -plt.figure().set_figwidth(12) -librosa.display.specshow(S_dB, x_axis="time", y_axis="mel", sr=sampling_rate, fmax=8000) -plt.colorbar() -``` - -
- Mel spectrogram plot -
- -Dans l'exemple ci-dessus, `n_mels` représente le nombre de bandes mel à générer. -Les bandes mel définissent un ensemble de gammes de fréquences qui divisent le spectre en composantes perceptuellement significatives, en utilisant un ensemble de filtres dont la forme et l'espacement sont choisis pour imiter la façon dont l'oreille humaine répond à différentes fréquences. -Les valeurs courantes pour `n_mels` sont 40 ou 80. `fmax` indique la fréquence la plus élevée (en Hz) qui nous intéresse. -Tout comme avec un spectrogramme standard, il est courant d'exprimer la force des composantes de fréquence mel en décibels. -C'est ce qu'on appelle communément un **spectrogramme log-mel**, car la conversion en décibels implique une opération logarithmique. -L'exemple ci-dessus utilisé `librosa.power_to_db()` car `librosa.feature.melspectrogram()` crée un spectrogramme de puissance. - - -💡 Tous les spectrogrammes mel ne sont pas identiques ! Il existe deux échelles mel différentes d'usage courant (« htk » et « slaney »), et au lieu du spectrogramme de puissance, le spectrogramme d'amplitude peut être utilisé. -La conversion en spectrogramme log-mel ne calcule pas toujours les décibels vrais, mais peut simplement prendre le « log ». -Par conséquent, si un modèle d'apprentissage automatique attend un spectrogramme mel en entrée, vérifiez deux fois pour vous assurer que vous le calculez de la même manière. - - -La création d'un spectrogramme mel est une opération avec perte car elle implique le filtrage du signal. -La conversion d'un spectrogramme mel en une forme d'onde est plus difficile que de le faire pour un spectrogramme régulier, car cela nécessite d'estimer les fréquences qui ont été jetées. -C'est pourquoi des modèles d'apprentissage automatique tels que le vocodeur HiFiGAN sont nécessaires pour produire une forme d'onde à partir d'un spectrogramme mel. -Comparé à un spectrogramme standard, un spectrogramme mel peut capturer des caractéristiques plus significatives du signal audio pour la perception humaine, ce qui en fait un choix populaire dans des tâches telles que la reconnaissance vocale, l'identification du locuteur et la classification des genres musicaux. -Maintenant que vous savez comment visualiser des exemples de données audio, essayez de voir à quoi ressemblent vos sons préférés :) +# Introduction aux données audio + +Le **taux d'échantillonnage** (également appelé fréquence d'échantillonnage) est le nombre d'échantillons prélevés en une seconde et est mesuré en hertz (Hz). +Pour vous donner un point de référence, l'audio de qualité CD a un taux d'échantillonnage de 44 100 Hz, ce qui signifie que les échantillons sont prélevés 44 100 fois par seconde. À titre de comparaison, l'audio haute résolution a un taux d'échantillonnage de 192 000 Hz ou 192 kHz. +Un taux d'échantillonnage couramment utilisé dans les modèles vocaux d'apprentissage est de 16 000 Hz ou 16 kHz. + +Le choix de la fréquence d'échantillonnage détermine principalement la fréquence la plus élevée qui peut être capturée à partir du signal. +Ceci est également connu sous le nom de limite de Nyquist et correspond exactement à la moitié du taux d'échantillonnage. +Les fréquences audibles dans la parole humaine sont inférieures à 8 kHz et, par conséquent, l'échantillonnage de la parole à 16 kHz est suffisant. +L'utilisation d'un taux d'échantillonnage plus élevé ne permettra pas de capturer plus d'informations et ne fera qu'augmenter le coût de calcul du traitement de ces fichiers. +D'autre part, l'échantillonnage audio à un taux d'échantillonnage trop faible entraînera une perte d'informations. +La parole échantillonnée à 8 kHz sonnera étouffée car les fréquences plus élevées ne peuvent pas être capturées à ce rythme. + +Il est important de vous assurer que tous les exemples audio de votre jeu de données ont le même taux d'échantillonnage lorsque vous travaillez sur une tâche audio. +Si vous prévoyez d'utiliser des données audio personnalisées pour finetuner un modèle pré-entraîné, le taux d'échantillonnage de vos données doit correspondre au taux d'échantillonnage des données sur lesquelles le modèle a été pré-entraîné. +La fréquence d'échantillonnage détermine l'intervalle de temps entre les échantillons audio successifs, ce qui a un impact sur la résolution temporelle des données audio. +Prenons un exemple : un son de 5 secondes à une fréquence d'échantillonnage de 16 000 Hz sera représenté comme une série de 80 000 valeurs, tandis que le même son de 5 secondes à une fréquence d'échantillonnage de 8 000 Hz sera représenté comme une série de 40 000 valeurs. +Les *transformers* qui résolvent les tâches audio traitent les exemples comme des séquences et s'appuient sur des mécanismes d'attention pour apprendre l'audio ou la représentation multimodale. +Étant donné que les séquences sont différentes pour les exemples audio à des taux d'échantillonnage différents, il sera difficile pour les modèles de généraliser entre les taux d'échantillonnage. +**Le rééchantillonnage** est le processus de mise en correspondance des taux d'échantillonnage et fait partie du [prétraitement](preprocessing#resampling-the-audio-data) des données audio. + +## Amplitude et profondeur de bits + +Bien que le taux d'échantillonnage vous indique à quelle fréquence les échantillons sont prélevés, quelles sont exactement les valeurs de chaque échantillon? +Le son est produit par des changements de pression atmosphérique à des fréquences audibles pour les humains. +L'**amplitude** d'un son décrit le niveau de pression acoustique à un instant donné et est mesurée en décibels (dB). +Nous percevons l'amplitude comme un volume sonore. Pour vous donner un exemple, une voix normale est inférieure à 60 dB, et un concert de rock peut être autour de 125 dB, repoussant les limites de l'audition humaine. +En audio, chaque échantillon enregistre l'amplitude de l'onde à un moment donné. +La **profondeur de bits** de l'échantillon détermine avec quelle précision cette valeur d'amplitude peut être décrite. +Plus la profondeur de bits est élevée, plus la représentation numérique se rapproche fidèlement de l'onde sonore continue d'origine. +Les profondeurs de bits audio les plus courantes sont 16 bits et 24 bits. +Chacun est un terme binaire, représentant le nombre d'étapes possibles auxquelles la valeur d'amplitude peut être quantifiée lorsqu'elle est convertie de continue à discrète: 65 536 étapes pour l'audio 16 bits, 16 777 216 étapes pour l'audio 24 bits. +Étant donné que la quantification implique d'arrondir la valeur continue à une valeur discrète, le processus d'échantillonnage introduit du bruit. Plus la profondeur de bits est élevée, plus ce bruit de quantification est faible. +En pratique, le bruit de quantification de l'audio 16 bits est déjà suffisamment faible pour être inaudible et l'utilisation de profondeurs de bits plus élevées n'est généralement pas nécessaire. +Vous pouvez également rencontrer de l'audio 32 bits. +Cela stocke les échantillons sous forme de valeurs à virgule flottante, tandis que l'audio 16 bits et 24 bits utilise des échantillons entiers. +La précision d'une valeur à virgule flottante de 32 bits est de 24 bits, ce qui lui donne la même profondeur de bits que l'audio 24 bits. +Les échantillons audio en virgule flottante devraient se situer dans la plage [-1.0, 1.0]. +Étant donné que les modèles d'apprentissage automatique fonctionnent naturellement sur des données en virgule flottante, l'audio doit d'abord être converti au format à virgule flottante avant de pouvoir être utilisé pour entraîner le modèle. +Nous verrons comment faire cela dans la section suivante sur le [Prétraitement] (preprocessing). +Tout comme pour les signaux audio continus, l'amplitude de l'audio numérique est généralement exprimée en décibels (dB). +L'audition humaine étant de nature logarithmique - nos oreilles sont plus sensibles aux petites fluctuations des sons calmes qu'à celles des sons forts - l'intensité d'un son est plus facile à interpréter si les amplitudes sont exprimées en décibels, qui sont également logarithmiques. +L'échelle de décibels pour l'audio réel commence à 0 dB, ce qui représente le son le plus faible possible que les humains peuvent entendre, et les sons plus forts ont des valeurs plus importantes. +Cependant, pour les signaux audio numériques, 0 dB est l'amplitude la plus forte possible, tandis que toutes les autres amplitudes sont négatives. +En règle générale: chaque -6 dB est une réduction de moitié de l'amplitude, et tout ce qui est inférieur à -60 dB est généralement inaudible à moins que vous n'augmentiez vraiment le volume. + +## L'audio comme forme d'onde + +Vous avez peut-être vu des sons visualisés sous la **forme d'onde** qui traçant les valeurs de l'échantillon au fil du temps et illustrant les changements d'amplitude du son. Ceci est aussi connu sous le nom de représentation du *domaine temporel* du son. +Ce type de visualisation est utile pour identifier des caractéristiques spécifiques du signal audio telles que la synchronisation des événements sonores individuels, l'intensité sonore globale du signal et toute irrégularité ou bruit présent dans l'audio. + +Pour tracer la forme d'onde d'un signal audio, nous pouvons utiliser une bibliothèque Python `librosa`: + +```bash +pip install librosa +``` + +Prenons un exemple de son appelé « trompette » qui vient avec la bibliothèque: + +```py +import librosa + +array, sampling_rate = librosa.load(librosa.ex("trumpet")) +``` + +L'exemple est chargé sous la forme d'un tuple de séries temporelles audio (ici nous l'appelons `array`) et de taux d'échantillonnage (`sampling_rate`). +Jetons un coup d'œil à la forme d'onde de ce son en utilisant la fonction `waveshow()` de librosa: + +```py +import matplotlib.pyplot as plt +import librosa.display + +plt.figure().set_figwidth(12) +librosa.display.waveshow(array, sr=sampling_rate) +``` + +
+ Waveform plot +
+ +Cela trace l'amplitude du signal sur l'axe des y et le temps le long de l'axe des x. +En d'autres termes, chaque point correspond à une seule valeur d'échantillon qui a été prise lors de l'échantillonnage de ce son. +Notez également que librosa renvoie déjà l'audio sous forme de valeurs à virgule flottante et que les valeurs d'amplitude sont effectivement comprises dans la plage [-1.0, 1.0]. +Visualiser l'audio et l'écouter peut être un outil utile pour comprendre les données avec lesquelles vous travaillez. +Vous pouvez voir la forme du signal, observer des modèles, apprendre à repérer le bruit ou la distorsion. +Si vous prétraitez les données d'une manière ou d'une autre, telle que la normalisation, le rééchantillonnage ou le filtrage, vous pouvez confirmer visuellement que les étapes de prétraitement ont été appliquées comme prévu. +Après avoir entraîné un modèle, vous pouvez également visualiser des exemples où des erreurs se produisent (par exemple, dans la tâche de classification audio) pour déboguer le problème. + +## Le spectre de fréquences + +Une autre façon de visualiser les données audio consiste à tracer le **spectre de fréquences** d'un signal audio, également connu sous le nom de représentation du *domaine fréquentiel*. +Le spectre est calculé à l'aide de la transformée de Fourier discrète ou TFD. Il décrit les fréquences individuelles qui composent le signal et leur force. +Traçons le spectre de fréquences pour le même son de trompette en prenant la TFD en utilisant la fonction `rfft()` de numpy. Bien qu'il soit possible de tracer le spectre de l'ensemble du son, il est plus utile de regarder une petite région à la place. +Ici, nous allons prendre la TFD sur les 4096 premiers échantillons, ce qui correspond à peu près à la longueur de la première note jouée: + +```py +import numpy as np + +TFD_input = array[:4096] + +# calculer la TDF +window = np.hanning(len(TFD_input)) +windowed_input = TFD_input * window +TFD = np.fft.rfft(windowed_input) + +# obtenir le spectre d'amplitude en décibels +amplitude = np.abs(TFD) +amplitude_db = librosa.amplitude_to_db(amplitude, ref=np.max) + +# obtenir les bacs de fréquence +frequency = librosa.fft_frequencies(sr=sampling_rate, n_fft=len(TFD_input)) + +plt.figure().set_figwidth(12) +plt.plot(frequency, amplitude_db) +plt.xlabel("Frequency (Hz)") +plt.ylabel("Amplitude (dB)") +plt.xscale("log") +``` + +
+ Spectrum plot +
+ +Cela trace la force des différentes composantes de fréquence présentes dans ce segment audio. Les valeurs de fréquence sont sur l'axe des abscisses, généralement tracées sur une échelle logarithmique, tandis que leurs amplitudes sont sur l'axe des y. + +Le spectre de fréquences que nous avons tracé montre plusieurs pics. +Ces pics correspondent aux harmoniques de la note jouée, les harmoniques supérieures étant plus calmes. Puisque le premier pic est à environ 620 Hz, c'est le spectre de fréquence d'une♭ note Mi. + +La sortie de la TDF est un tableau de nombres complexes, composé de composants réels et imaginaires. +Prendre la magnitude avec ` np.abs(TFD)` extrait l'information d'amplitude du spectrogramme. +L'angle entre les composants réels et imaginaires fournit ce que l'on appelle le spectre de phase, mais il est souvent écarté dans les applications d'apprentissage automatique. +Nous utilions `librosa.amplitude_to_db()` pour convertir les valeurs d'amplitude en échelle de décibels, ce qui facilite la visualisation des détails les plus fins du spectre. +Parfois, les gens utilisent le **spectre de puissance**, qui mesure l'énergie plutôt que l'amplitude. Il s'agit simplement d'un spectre avec les valeurs d'amplitude au carré. + +> [!TIP] +> 💡 En pratique, les gens utilisent le terme FFT de manière interchangeable avec TFD, car la FFT ou transformée de Fourier rapide est le seul moyen efficace de calculer la TFD sur un ordinateur. + +Le spectre de fréquences d'un signal audio contient exactement la même information que sa forme d'onde. Ce sont simplement deux façons différentes de regarder les mêmes données (ici, les 4096 premiers échantillons du son de la trompette). +Là où la forme d'onde trace l'amplitude du signal audio au fil du temps, le spectre visualise les amplitudes des fréquences individuelles à un moment donné. + +## Spectrogramme + +Et si nous voulons voir comment les fréquences d'un signal audio changent ? La trompette joue plusieurs notes et elles ont toutes des fréquences différentes. +Le problème est que le spectre ne montre qu'un instantané figé des fréquences à un instant donné. +La solution consiste à prendre plusieurs TFD, chacune ne couvrant qu'une petite tranche de temps, et à empiler les spectres résultants dans un **spectrogramme**. + +Un spectrogramme trace le contenu en fréquence d'un signal audio au fil du temps. Il vous permet de voir le temps, la fréquence et l'amplitude sur un seul graphique. L'algorithme qui effectue ce calcul est la TFCT ou transformée de Fourier à court terme. +Le spectrogramme est l'un des outils audio les plus informatifs à notre disposition. +Par exemple, lorsque nous travaillons avec un enregistrement musical, nous pouvons voir les différents instruments et pistes vocales et comment ils contribuent au son global. Dans la parole, nous pouvons identifier différents sons de voyelles car chaque voyelle est caractérisée par des fréquences particulières. +Traçons un spectrogramme pour le même son de trompette, en utilisant les fonctions `stft()` et `specshow()` de librosa: + +```py +import numpy as np + +D = librosa.stft(array) +S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max) + +plt.figure().set_figwidth(12) +librosa.display.specshow(S_db, x_axis="time", y_axis="hz") +plt.colorbar() +``` + +
+ Spectrogram plot +
+ +Dans ce graphique, l'axe des x représente le temps comme dans la visualisation de la forme d'onde, mais maintenant l'axe des y représente la fréquence en Hz. +L'intensité de la couleur donne l'amplitude ou la puissance de la composante fréquence à chaque point dans le temps, mesurée en décibels (dB). + +Le spectrogramme est créé en prenant de courts segments du signal audio, généralement de quelques millisecondes, et en calculant la transformée de Fourier discrète de chaque segment pour obtenir son spectre de fréquences. +Les spectres résultants sont ensuite empilés sur l'axe temporel pour créer le spectrogramme. +Chaque tranche verticale de cette image correspond à un spectre de fréquences unique, vu du haut. +Par défaut, `librosa.stft()` divise le signal audio en segments de 2048 échantillons, ce qui donne un bon compromis entre la résolution de fréquence et la résolution temporelle. +Étant donné que le spectrogramme et la forme d'onde sont des vues différentes des mêmes données, il est possible de retourner le spectrogramme dans la forme d'onde d'origine en utilisant la TFCT inverse. +Cependant, cela nécessite les informations de phase en plus des informations d'amplitude. Si le spectrogramme a été généré par un modèle d'apprentissage automatique, il ne produit généralement que les amplitudes. +Dans ce cas, nous pouvons utiliser un algorithme de reconstruction de phase tel que l'algorithme classique de Griffin-Lim, ou en utilisant un réseau neuronal appelé vocodeur, pour reconstruire une forme d'onde à partir du spectrogramme. + +Les spectrogrammes ne sont pas seulement utilisés pour la visualisation. De nombreux modèles d'apprentissage automatique prendront des spectrogrammes en entrée, par opposition aux formes d'onde, et produiront des spectrogrammes en sortie. +Maintenant que nous savons ce qu'est un spectrogramme et comment il est fabriqué, jetons un coup d'œil à une variante de celui-ci largement utilisée pour le traitement de la parole: le spectrogramme mel. + +## Spectrogramme Mel + +Un spectrogramme mel est une variante du spectrogramme couramment utilisée dans les tâches de traitement de la parole et d'apprentissage automatique. +Il est similaire à un spectrogramme en ce sens qu'il montre le contenu en fréquence d'un signal audio au fil du temps, mais sur un axe de fréquence différent. +Dans un spectrogramme standard, l'axe de fréquence est linéaire et est mesuré en hertz (Hz). +Cependant, le système auditif humain est plus sensible aux changements dans les basses fréquences que dans les fréquences plus élevées, et cette sensibilité diminue logarithmiquement à mesure que la fréquence augmente. +L'échelle mel est une échelle perceptuelle qui se rapproche de la réponse en fréquence non linéaire de l'oreille humaine. +Pour créer un spectrogramme mel, le STFT est utilisé comme auparavant, divisant l'audio en segments courts pour obtenir une séquence de spectres de fréquence. +De plus, chaque spectre est envoyé à travers un ensemble de filtres, appelé *mel filterbank*, pour transformer les fréquences à l'échelle mel. +Voyons comment nous pouvons tracer un spectrogramme mel en utilisant la fonction `melspectrogram()` de librosa, qui effectue toutes ces étapes pour nous: + +```py +S = librosa.feature.melspectrogram(y=array, sr=sampling_rate, n_mels=128, fmax=8000) +S_dB = librosa.power_to_db(S, ref=np.max) + +plt.figure().set_figwidth(12) +librosa.display.specshow(S_dB, x_axis="time", y_axis="mel", sr=sampling_rate, fmax=8000) +plt.colorbar() +``` + +
+ Mel spectrogram plot +
+ +Dans l'exemple ci-dessus, `n_mels` représente le nombre de bandes mel à générer. +Les bandes mel définissent un ensemble de gammes de fréquences qui divisent le spectre en composantes perceptuellement significatives, en utilisant un ensemble de filtres dont la forme et l'espacement sont choisis pour imiter la façon dont l'oreille humaine répond à différentes fréquences. +Les valeurs courantes pour `n_mels` sont 40 ou 80. `fmax` indique la fréquence la plus élevée (en Hz) qui nous intéresse. +Tout comme avec un spectrogramme standard, il est courant d'exprimer la force des composantes de fréquence mel en décibels. +C'est ce qu'on appelle communément un **spectrogramme log-mel**, car la conversion en décibels implique une opération logarithmique. +L'exemple ci-dessus utilisé `librosa.power_to_db()` car `librosa.feature.melspectrogram()` crée un spectrogramme de puissance. + +> [!TIP] +> 💡 Tous les spectrogrammes mel ne sont pas identiques ! Il existe deux échelles mel différentes d'usage courant (« htk » et « slaney »), et au lieu du spectrogramme de puissance, le spectrogramme d'amplitude peut être utilisé. +> La conversion en spectrogramme log-mel ne calcule pas toujours les décibels vrais, mais peut simplement prendre le « log ». +> Par conséquent, si un modèle d'apprentissage automatique attend un spectrogramme mel en entrée, vérifiez deux fois pour vous assurer que vous le calculez de la même manière. + +La création d'un spectrogramme mel est une opération avec perte car elle implique le filtrage du signal. +La conversion d'un spectrogramme mel en une forme d'onde est plus difficile que de le faire pour un spectrogramme régulier, car cela nécessite d'estimer les fréquences qui ont été jetées. +C'est pourquoi des modèles d'apprentissage automatique tels que le vocodeur HiFiGAN sont nécessaires pour produire une forme d'onde à partir d'un spectrogramme mel. +Comparé à un spectrogramme standard, un spectrogramme mel peut capturer des caractéristiques plus significatives du signal audio pour la perception humaine, ce qui en fait un choix populaire dans des tâches telles que la reconnaissance vocale, l'identification du locuteur et la classification des genres musicaux. +Maintenant que vous savez comment visualiser des exemples de données audio, essayez de voir à quoi ressemblent vos sons préférés :) diff --git a/chapters/fr/chapter1/preprocessing.mdx b/chapters/fr/chapter1/preprocessing.mdx index 3ce4dcff..d133dbdc 100644 --- a/chapters/fr/chapter1/preprocessing.mdx +++ b/chapters/fr/chapter1/preprocessing.mdx @@ -1,203 +1,202 @@ -# Prétraitement d'un jeu de données audio - -Le chargement d'un jeu de données avec 🤗 *Datasets* n'est que la moitié du plaisir. -Si vous prévoyez de l'utiliser pour entraîner un modèle ou pour exécuter l'inférence, vous devrez d'abord prétraiter les données. En général, cela impliquera les étapes suivantes: -* Rééchantillonnage des données audio -* Filtrage du jeu de données -* Conversion des données audio en entrée attendue du modèle - -## Rééchantillonnage des données audio - -La fonction `load_dataset` télécharge des exemples audio avec le taux d'échantillonnage avec lequel ils ont été publiés. -Ce n'est pas toujours le taux d'échantillonnage attendu par un modèle que vous prévoyez d'entraîner ou d'utiliser pour l'inférence. -S'il existe un écart entre les taux d'échantillonnage, vous pouvez rééchantillonner l'audio à la fréquence d'échantillonnage attendue du modèle. -La plupart des modèles pré-entraînés disponibles ont été pré-entraînés sur des jeux de données audio à une fréquence d'échantillonnage de 16 kHz. -Lorsque nous avons exploré le jeu de données MINDS-14, vous avez peut-être remarqué qu'il est échantillonné à 8 kHz, ce qui signifie que nous devrons probablement le suréchantillonner. -Pour ce faire, utilisez la méthode `cast_column` de 🤗 Datasets. -Cette opération ne modifie pas l'audio sur place, mais plutôt les signaux aux jeux de données pour rééchantillonner les exemples audio à la volée lorsqu'ils sont chargés. -Le code suivant définira la fréquence d'échantillonnage à 16 kHz : - -```py -from datasets import Audio - -minds = minds.cast_column("audio", Audio(sampling_rate=16_000)) -``` - -Rechargez le premier exemple audio dans le jeu de données MINDS-14 et vérifiez qu'il a été rééchantillonné à la « fréquence d'échantillonnage » souhaitée : - -```py -minds[0] -``` - -**Sortie :** - -```out -{ - "path": "/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-AU~PAY_BILL/response_4.wav", - "audio": { - "path": "/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-AU~PAY_BILL/response_4.wav", - "array": array( - [ - 2.0634243e-05, - 1.9437837e-04, - 2.2419340e-04, - ..., - 9.3852862e-04, - 1.1302452e-03, - 7.1531429e-04, - ], - dtype=float32, - ), - "sampling_rate": 16000, - }, - "transcription": "I would like to pay my electricity bill using my card can you please assist", - "intent_class": 13, -} - -``` -Vous remarquerez peut-être que les valeurs de tableau sont maintenant également différentes. C'est parce que nous avons maintenant deux fois plus de valeurs d'amplitude pour chacune d'entre elles que nous avions auparavant. - - -💡 Quelques informations sur le rééchantillonnage: Si un signal audio a été échantillonné à 8 kHz, de sorte qu'il a 8000 lectures d'échantillon par seconde, nous savons que l'audio ne contient aucune fréquence supérieure à 4 kHz. -Ceci est garanti par le théorème d'échantillonnage de Nyquist. Pour cette raison, nous pouvons être certains qu'entre les points d'échantillonnage, le signal continu d'origine fait toujours une courbe lisse. -L'augmentation du prélèvement vers un taux d'échantillonnage plus élevé consiste alors à calculer des valeurs d'échantillonnage supplémentaires qui se situent entre les valeurs existantes, en approximant cette courbe. -Le sous-échantillonnage, cependant, nécessite que nous filtrons d'abord toutes les fréquences qui seraient supérieures à la nouvelle limite de Nyquist, avant d'estimer les nouveaux points d'échantillonnage. -En d'autres termes, vous ne pouvez pas sous-échantillonner d'un facteur 2x en jetant simplement tous les autres échantillons - cela créera des distorsions dans le signal appelées alias. -Faire un rééchantillonnage correct est délicat et mieux laissé à des bibliothèques bien testées telles que librosa ou 🤗 *Datasets*. - - -## Filtrage du jeu de données - -Vous devrez peut-être filtrer les données en fonction de certains critères. L'un des cas courants consiste à limiter les exemples audio à une certaine durée. -Par exemple, nous pourrions vouloir filtrer tous les exemples de plus de 20 secondes pour éviter les erreurs de mémoire insuffisante lors de l'entraînement d'un modèle. -Nous pouvons le faire en utilisant la méthode `filter` de 🤗 *Datasets* et en lui passant une fonction avec une logique de filtrage. Commençons par écrire une fonction qui indique quels exemples conserver et lesquels rejeter. -Cette fonction, `is_audio_length_in_range', renvoie `True` si un échantillon est inférieur à 20s et `False` s'il est plus long que 20s. - -```py -MAX_DURATION_IN_SECONDS = 20.0 - - -def is_audio_length_in_range(input_length): - return input_length < MAX_DURATION_IN_SECONDS -``` - -La fonction de filtrage peut être appliquée à la colonne d'un jeu de données, mais nous n'avons pas de colonne avec une durée de piste audio dans ce jeu de données. -Cependant, nous pouvons en créer un, filtrer en fonction des valeurs de cette colonne, puis le supprimer. - -```py -# Utilisez librosa pour obtenir la durée de l'exemple à partir du fichier audio -new_column = [librosa.get_duration(path=x) for x in minds["path"]] -minds = minds.add_column("duration", new_column) - -# utiliser la méthode `filter` de 🤗 Datasets pour appliquer la fonction de filtrage -minds = minds.filter(is_audio_length_in_range, input_columns=["duration"]) - -# supprimer la colonne d'assistance temporaire -minds = minds.remove_columns(["duration"]) -minds -``` - -**Sortie :** - -```out -Dataset({features: ["path", "audio", "transcription", "intent_class"], num_rows: 624}) -``` - -Nous pouvons vérifier que le jeu de données a été filtré de 654 exemples à 624. - -## Prétraitement des données audio - -L'un des aspects les plus difficiles de l'utilisation d'ensembles de données audio consiste à préparer les données dans le bon format pour la formation des modèles. -Comme vous l'avez vu, les données audio brutes se présentent sous la forme d'un tableau de valeurs d'échantillon. -Cependant, les modèles pré-entraînés, que vous les utilisiez pour l'inférence ou que vous souhaitiez les finetuner pour votre tâche, s'attendent à ce que les données brutes soient converties en fonctionnalités d'entrée. -Les exigences pour les caractéristiques d'entrée peuvent varier d'un modèle à l'autre. Elles dépendent de l'architecture du modèle et des données avec lesquelles il a été pré-entraîné. -La bonne nouvelle est que, pour chaque modèle audio pris en charge, 🤗 *Transformers* offrent une classe d'extracteur de caractéristiques qui peut convertir les données audio brutes en caractéristiques d'entrée attendues par le modèle. - -Alors, que fait un extracteur de caractéristiques avec les données audio brutes ? Jetons un coup d'œil à l'extracteur de caractéristiques de [Whisper](https://cdn.openai.com/papers/whisper.pdf) pour comprendre certaines transformations d'extraction de caractéristiques courantes. -Whisper est un modèle pré-entraîné pour la reconnaissance vocale automatique (ASR) publié en septembre 2022 par Alec Radford et al. d'OpenAI. -Tout d'abord, l'extracteur de fonction Whisper pave / tronque un batch d'exemples audio de sorte que tous les exemples ont une longueur d'entrée de 30s. -Les exemples plus courts sont complétés à 30s en ajoutant des zéros à la fin de la séquence (les zéros dans un signal audio correspondent à l'absence de signal ou au silence). Les exemples de plus de 30 ans sont tronqués à 30 s. -Étant donné que tous les éléments du batch sont rembourrés/tronqués à une longueur maximale dans l'espace d'entrée, il n'y a pas besoin d'un masque d'attention. -Whisper est unique à cet égard, la plupart des autres modèles audio nécessitent un masque d'attention qui détaille où les séquences ont été rembourrées, et donc où elles doivent être ignorées dans le mécanisme d'auto-attention. -Whisper est entraîné pour fonctionner sans masque d'attention et déduire directement des signaux vocaux où ignorer les entrées. -La deuxième opération effectuée par l'extracteur de fonctions Whisper consiste à convertir les matrices audio rembourrées en spectrogrammes log-mel. -Ces spectrogrammes décrivent comment les fréquences d'un signal changent au fil du temps, exprimées sur l'échelle mel et mesurées en décibels (la partie log) pour rendre les fréquences et les amplitudes plus représentatives de l'audition humaine. -Toutes ces transformations peuvent être appliquées à vos données audio brutes avec quelques lignes de code. -Allons de l'avant et chargeons l'extracteur de caractéristiques à partir du *checkpoint* de Whisper pré-entraîné pour être prêt pour nos données audio: - -```py -from transformers import WhisperFeatureExtractor - -feature_extractor = WhisperFeatureExtractor.from_pretrained("openai/whisper-small") -``` - -Ensuite, vous pouvez écrire une fonction pour pré-traiter un seul exemple audio en le faisant passer par le `feature_extractor`. - -```py -def prepare_dataset(example): - audio = example["audio"] - features = feature_extractor( - audio["array"], sampling_rate=audio["sampling_rate"], padding=True - ) - return features -``` - -Nous pouvons appliquer la fonction de préparation des données à tous nos exemples d’entraînement en utilisant la méthode `map` de 🤗 *Datasets* : - -```py -minds = minds.map(prepare_dataset) -minds -``` - -**Sortie :** - -```out -Dataset( - { - features: ["path", "audio", "transcription", "intent_class", "input_features"], - num_rows: 624, - } -) -``` - -Aussi simple que cela, nous avons maintenant des spectrogrammes log-mel comme `input_features` dans le jeu de données. -Visualisons-le pour l'un des exemples de `minds` : - -```py -import numpy as np - -example = minds[0] -input_features = example["input_features"] - -plt.figure().set_figwidth(12) -librosa.display.specshow( - np.asarray(input_features[0]), - x_axis="time", - y_axis="mel", - sr=feature_extractor.sampling_rate, - hop_length=feature_extractor.hop_length, -) -plt.colorbar() -``` - -
- Log mel spectrogram plot -
- -Vous pouvez maintenant voir à quoi ressemble l'entrée audio du modèle Whisper après le prétraitement. - -La classe d'extracteur de caractéristiques du modèle se charge de transformer les données audio brutes au format attendu par le modèle. Cependant, de nombreuses tâches impliquant l'audio sont multimodales, par exemple la reconnaissance vocale. -Dans de tels cas, 🤗 *Transformers* offrent également des tokeniseurs spécifiques au modèle pour traiter les entrées de texte. Pour une plongée approfondie dans les tokeniseurs, veuillez vous référer à notre [cours de NLP](https://huggingface.co/learn/nlp-course/fr/chapter2/4). - -Vous pouvez charger séparément l'extracteur de caractéristiques et le tokeniseur pour Whisper et d'autres modèles multimodaux, ou vous pouvez charger les deux via un processeur. -Pour rendre les choses encore plus simples, utilisez `AutoProcessor` pour charger l'extracteur de caractéristiques et le processeur d'un modèle à partir d'un *checkpoint*, comme ceci : - -```py -from transformers import AutoProcessor - -processor = AutoProcessor.from_pretrained("openai/whisper-small") -``` - -Nous avons illustré ici les étapes fondamentales de préparation des données. Bien entendu, les données personnalisées peuvent nécessiter un prétraitement plus complexe. -Dans ce cas, vous pouvez étendre la fonction `prepare_dataset` pour effectuer n'importe quel type de transformations de données personnalisées. -Avec 🤗 *Datasets*, si vous pouvez l'écrire en tant que fonction Python, vous pouvez l'appliquer à votre jeu de données ! +# Prétraitement d'un jeu de données audio + +Le chargement d'un jeu de données avec 🤗 *Datasets* n'est que la moitié du plaisir. +Si vous prévoyez de l'utiliser pour entraîner un modèle ou pour exécuter l'inférence, vous devrez d'abord prétraiter les données. En général, cela impliquera les étapes suivantes: +* Rééchantillonnage des données audio +* Filtrage du jeu de données +* Conversion des données audio en entrée attendue du modèle + +## Rééchantillonnage des données audio + +La fonction `load_dataset` télécharge des exemples audio avec le taux d'échantillonnage avec lequel ils ont été publiés. +Ce n'est pas toujours le taux d'échantillonnage attendu par un modèle que vous prévoyez d'entraîner ou d'utiliser pour l'inférence. +S'il existe un écart entre les taux d'échantillonnage, vous pouvez rééchantillonner l'audio à la fréquence d'échantillonnage attendue du modèle. +La plupart des modèles pré-entraînés disponibles ont été pré-entraînés sur des jeux de données audio à une fréquence d'échantillonnage de 16 kHz. +Lorsque nous avons exploré le jeu de données MINDS-14, vous avez peut-être remarqué qu'il est échantillonné à 8 kHz, ce qui signifie que nous devrons probablement le suréchantillonner. +Pour ce faire, utilisez la méthode `cast_column` de 🤗 Datasets. +Cette opération ne modifie pas l'audio sur place, mais plutôt les signaux aux jeux de données pour rééchantillonner les exemples audio à la volée lorsqu'ils sont chargés. +Le code suivant définira la fréquence d'échantillonnage à 16 kHz : + +```py +from datasets import Audio + +minds = minds.cast_column("audio", Audio(sampling_rate=16_000)) +``` + +Rechargez le premier exemple audio dans le jeu de données MINDS-14 et vérifiez qu'il a été rééchantillonné à la « fréquence d'échantillonnage » souhaitée : + +```py +minds[0] +``` + +**Sortie :** + +```out +{ + "path": "/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-AU~PAY_BILL/response_4.wav", + "audio": { + "path": "/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-AU~PAY_BILL/response_4.wav", + "array": array( + [ + 2.0634243e-05, + 1.9437837e-04, + 2.2419340e-04, + ..., + 9.3852862e-04, + 1.1302452e-03, + 7.1531429e-04, + ], + dtype=float32, + ), + "sampling_rate": 16000, + }, + "transcription": "I would like to pay my electricity bill using my card can you please assist", + "intent_class": 13, +} + +``` +Vous remarquerez peut-être que les valeurs de tableau sont maintenant également différentes. C'est parce que nous avons maintenant deux fois plus de valeurs d'amplitude pour chacune d'entre elles que nous avions auparavant. + +> [!TIP] +> 💡 Quelques informations sur le rééchantillonnage: Si un signal audio a été échantillonné à 8 kHz, de sorte qu'il a 8000 lectures d'échantillon par seconde, nous savons que l'audio ne contient aucune fréquence supérieure à 4 kHz. +> Ceci est garanti par le théorème d'échantillonnage de Nyquist. Pour cette raison, nous pouvons être certains qu'entre les points d'échantillonnage, le signal continu d'origine fait toujours une courbe lisse. +> L'augmentation du prélèvement vers un taux d'échantillonnage plus élevé consiste alors à calculer des valeurs d'échantillonnage supplémentaires qui se situent entre les valeurs existantes, en approximant cette courbe. +> Le sous-échantillonnage, cependant, nécessite que nous filtrons d'abord toutes les fréquences qui seraient supérieures à la nouvelle limite de Nyquist, avant d'estimer les nouveaux points d'échantillonnage. +> En d'autres termes, vous ne pouvez pas sous-échantillonner d'un facteur 2x en jetant simplement tous les autres échantillons - cela créera des distorsions dans le signal appelées alias. +> Faire un rééchantillonnage correct est délicat et mieux laissé à des bibliothèques bien testées telles que librosa ou 🤗 *Datasets*. + +## Filtrage du jeu de données + +Vous devrez peut-être filtrer les données en fonction de certains critères. L'un des cas courants consiste à limiter les exemples audio à une certaine durée. +Par exemple, nous pourrions vouloir filtrer tous les exemples de plus de 20 secondes pour éviter les erreurs de mémoire insuffisante lors de l'entraînement d'un modèle. +Nous pouvons le faire en utilisant la méthode `filter` de 🤗 *Datasets* et en lui passant une fonction avec une logique de filtrage. Commençons par écrire une fonction qui indique quels exemples conserver et lesquels rejeter. +Cette fonction, `is_audio_length_in_range', renvoie `True` si un échantillon est inférieur à 20s et `False` s'il est plus long que 20s. + +```py +MAX_DURATION_IN_SECONDS = 20.0 + + +def is_audio_length_in_range(input_length): + return input_length < MAX_DURATION_IN_SECONDS +``` + +La fonction de filtrage peut être appliquée à la colonne d'un jeu de données, mais nous n'avons pas de colonne avec une durée de piste audio dans ce jeu de données. +Cependant, nous pouvons en créer un, filtrer en fonction des valeurs de cette colonne, puis le supprimer. + +```py +# Utilisez librosa pour obtenir la durée de l'exemple à partir du fichier audio +new_column = [librosa.get_duration(path=x) for x in minds["path"]] +minds = minds.add_column("duration", new_column) + +# utiliser la méthode `filter` de 🤗 Datasets pour appliquer la fonction de filtrage +minds = minds.filter(is_audio_length_in_range, input_columns=["duration"]) + +# supprimer la colonne d'assistance temporaire +minds = minds.remove_columns(["duration"]) +minds +``` + +**Sortie :** + +```out +Dataset({features: ["path", "audio", "transcription", "intent_class"], num_rows: 624}) +``` + +Nous pouvons vérifier que le jeu de données a été filtré de 654 exemples à 624. + +## Prétraitement des données audio + +L'un des aspects les plus difficiles de l'utilisation d'ensembles de données audio consiste à préparer les données dans le bon format pour la formation des modèles. +Comme vous l'avez vu, les données audio brutes se présentent sous la forme d'un tableau de valeurs d'échantillon. +Cependant, les modèles pré-entraînés, que vous les utilisiez pour l'inférence ou que vous souhaitiez les finetuner pour votre tâche, s'attendent à ce que les données brutes soient converties en fonctionnalités d'entrée. +Les exigences pour les caractéristiques d'entrée peuvent varier d'un modèle à l'autre. Elles dépendent de l'architecture du modèle et des données avec lesquelles il a été pré-entraîné. +La bonne nouvelle est que, pour chaque modèle audio pris en charge, 🤗 *Transformers* offrent une classe d'extracteur de caractéristiques qui peut convertir les données audio brutes en caractéristiques d'entrée attendues par le modèle. + +Alors, que fait un extracteur de caractéristiques avec les données audio brutes ? Jetons un coup d'œil à l'extracteur de caractéristiques de [Whisper](https://cdn.openai.com/papers/whisper.pdf) pour comprendre certaines transformations d'extraction de caractéristiques courantes. +Whisper est un modèle pré-entraîné pour la reconnaissance vocale automatique (ASR) publié en septembre 2022 par Alec Radford et al. d'OpenAI. +Tout d'abord, l'extracteur de fonction Whisper pave / tronque un batch d'exemples audio de sorte que tous les exemples ont une longueur d'entrée de 30s. +Les exemples plus courts sont complétés à 30s en ajoutant des zéros à la fin de la séquence (les zéros dans un signal audio correspondent à l'absence de signal ou au silence). Les exemples de plus de 30 ans sont tronqués à 30 s. +Étant donné que tous les éléments du batch sont rembourrés/tronqués à une longueur maximale dans l'espace d'entrée, il n'y a pas besoin d'un masque d'attention. +Whisper est unique à cet égard, la plupart des autres modèles audio nécessitent un masque d'attention qui détaille où les séquences ont été rembourrées, et donc où elles doivent être ignorées dans le mécanisme d'auto-attention. +Whisper est entraîné pour fonctionner sans masque d'attention et déduire directement des signaux vocaux où ignorer les entrées. +La deuxième opération effectuée par l'extracteur de fonctions Whisper consiste à convertir les matrices audio rembourrées en spectrogrammes log-mel. +Ces spectrogrammes décrivent comment les fréquences d'un signal changent au fil du temps, exprimées sur l'échelle mel et mesurées en décibels (la partie log) pour rendre les fréquences et les amplitudes plus représentatives de l'audition humaine. +Toutes ces transformations peuvent être appliquées à vos données audio brutes avec quelques lignes de code. +Allons de l'avant et chargeons l'extracteur de caractéristiques à partir du *checkpoint* de Whisper pré-entraîné pour être prêt pour nos données audio: + +```py +from transformers import WhisperFeatureExtractor + +feature_extractor = WhisperFeatureExtractor.from_pretrained("openai/whisper-small") +``` + +Ensuite, vous pouvez écrire une fonction pour pré-traiter un seul exemple audio en le faisant passer par le `feature_extractor`. + +```py +def prepare_dataset(example): + audio = example["audio"] + features = feature_extractor( + audio["array"], sampling_rate=audio["sampling_rate"], padding=True + ) + return features +``` + +Nous pouvons appliquer la fonction de préparation des données à tous nos exemples d’entraînement en utilisant la méthode `map` de 🤗 *Datasets* : + +```py +minds = minds.map(prepare_dataset) +minds +``` + +**Sortie :** + +```out +Dataset( + { + features: ["path", "audio", "transcription", "intent_class", "input_features"], + num_rows: 624, + } +) +``` + +Aussi simple que cela, nous avons maintenant des spectrogrammes log-mel comme `input_features` dans le jeu de données. +Visualisons-le pour l'un des exemples de `minds` : + +```py +import numpy as np + +example = minds[0] +input_features = example["input_features"] + +plt.figure().set_figwidth(12) +librosa.display.specshow( + np.asarray(input_features[0]), + x_axis="time", + y_axis="mel", + sr=feature_extractor.sampling_rate, + hop_length=feature_extractor.hop_length, +) +plt.colorbar() +``` + +
+ Log mel spectrogram plot +
+ +Vous pouvez maintenant voir à quoi ressemble l'entrée audio du modèle Whisper après le prétraitement. + +La classe d'extracteur de caractéristiques du modèle se charge de transformer les données audio brutes au format attendu par le modèle. Cependant, de nombreuses tâches impliquant l'audio sont multimodales, par exemple la reconnaissance vocale. +Dans de tels cas, 🤗 *Transformers* offrent également des tokeniseurs spécifiques au modèle pour traiter les entrées de texte. Pour une plongée approfondie dans les tokeniseurs, veuillez vous référer à notre [cours de NLP](https://huggingface.co/learn/nlp-course/fr/chapter2/4). + +Vous pouvez charger séparément l'extracteur de caractéristiques et le tokeniseur pour Whisper et d'autres modèles multimodaux, ou vous pouvez charger les deux via un processeur. +Pour rendre les choses encore plus simples, utilisez `AutoProcessor` pour charger l'extracteur de caractéristiques et le processeur d'un modèle à partir d'un *checkpoint*, comme ceci : + +```py +from transformers import AutoProcessor + +processor = AutoProcessor.from_pretrained("openai/whisper-small") +``` + +Nous avons illustré ici les étapes fondamentales de préparation des données. Bien entendu, les données personnalisées peuvent nécessiter un prétraitement plus complexe. +Dans ce cas, vous pouvez étendre la fonction `prepare_dataset` pour effectuer n'importe quel type de transformations de données personnalisées. +Avec 🤗 *Datasets*, si vous pouvez l'écrire en tant que fonction Python, vous pouvez l'appliquer à votre jeu de données ! diff --git a/chapters/fr/chapter3/classification.mdx b/chapters/fr/chapter3/classification.mdx index 98544de1..f1604f88 100644 --- a/chapters/fr/chapter3/classification.mdx +++ b/chapters/fr/chapter3/classification.mdx @@ -15,9 +15,8 @@ Tout comme le ViT, le modèle AST divise le spectrogramme audio en une séquence Image tirée du papier [*AST: Audio Spectrogram Transformer*](https://arxiv.org/pdf/2104.01778.pdf) - -💡 Même si ici nous prétendons que les spectrogrammes sont comme les images, il existe des différences importantes. Par exemple, déplacer le contenu d'une image vers le haut ou vers le bas ne change généralement pas la signification de ce qui se trouve dans l'image. Cependant, déplacer un spectrogramme vers le haut ou vers le bas changera les fréquences qui sont dans le son et changera complètement son caractère. Les images sont invariantes sous translation mais les spectrogrammes ne le sont pas. Traiter les spectrogrammes comme des images peut très bien fonctionner dans la pratique, mais gardez à l'esprit que ce n'est pas vraiment la même chose. - +> [!TIP] +> 💡 Même si ici nous prétendons que les spectrogrammes sont comme les images, il existe des différences importantes. Par exemple, déplacer le contenu d'une image vers le haut ou vers le bas ne change généralement pas la signification de ce qui se trouve dans l'image. Cependant, déplacer un spectrogramme vers le haut ou vers le bas changera les fréquences qui sont dans le son et changera complètement son caractère. Les images sont invariantes sous translation mais les spectrogrammes ne le sont pas. Traiter les spectrogrammes comme des images peut très bien fonctionner dans la pratique, mais gardez à l'esprit que ce n'est pas vraiment la même chose. ## Tout transformer peut être un classifieur diff --git a/chapters/fr/chapter3/ctc.mdx b/chapters/fr/chapter3/ctc.mdx index 8604ece7..5712a73f 100644 --- a/chapters/fr/chapter3/ctc.mdx +++ b/chapters/fr/chapter3/ctc.mdx @@ -11,9 +11,8 @@ Avec un modèle avec CTC, nous appliquons un association linéaire supplémentai Jusqu'à présent, cela est très similaire à ce que nous faisons en NLP avec un modèle tel que BERT : un * transformer* encodeur associe nos *tokens* de texte dans une séquence d'états cachés de l'encodeur, puis nous appliquons une association linéaire pour obtenir une prédiction d'étiquette de classe pour chaque état caché. Voici le hic : dans la parole, nous ne connaissons pas l'alignement des entrées audio et des sorties de texte. Nous savons que l'ordre dans lequel le discours est prononcé est le même que l'ordre dans lequel le texte est transcrit (l'alignement est dit monotone), mais nous ne savons pas comment les caractères de la transcription s'alignent sur l'audio. C'est là qu'intervient l'algorithme CTC. - -💡 Dans les modèles de NLP, le vocabulaire est généralement composé de milliers de *tokens* qui décrivent non seulement des caractères individuels, mais des parties de mots ou même des mots complets. Pour la CTC, un petit vocabulaire fonctionne mieux et nous essayons généralement de le limiter à moins de 50 caractères. Nous ne nous soucions pas de la casse des lettres, donc seulement utiliser des majuscules (ou seulement des minuscules) est suffisant. Les chiffres sont épelés, par exemple « 20 » devient « vingt ». En plus des lettres, nous avons besoin d'au moins un *token* séparateur de mots (espace) et d'un *token* de rembourrage. Tout comme avec un modèle de NLP, le *token* de remplissage nous permet de combiner plusieurs exemples dans un batch, mais c'est aussi le *token* que le modèle prédira pour les silences. En anglais, il est également utile de garder le caractère `'` car `"it's"` et `"its"`ont des significations très différentes. - +> [!TIP] +> 💡 Dans les modèles de NLP, le vocabulaire est généralement composé de milliers de *tokens* qui décrivent non seulement des caractères individuels, mais des parties de mots ou même des mots complets. Pour la CTC, un petit vocabulaire fonctionne mieux et nous essayons généralement de le limiter à moins de 50 caractères. Nous ne nous soucions pas de la casse des lettres, donc seulement utiliser des majuscules (ou seulement des minuscules) est suffisant. Les chiffres sont épelés, par exemple « 20 » devient « vingt ». En plus des lettres, nous avons besoin d'au moins un *token* séparateur de mots (espace) et d'un *token* de rembourrage. Tout comme avec un modèle de NLP, le *token* de remplissage nous permet de combiner plusieurs exemples dans un batch, mais c'est aussi le *token* que le modèle prédira pour les silences. En anglais, il est également utile de garder le caractère `'` car `"it's"` et `"its"`ont des significations très différentes. ## Où est mon alignement? @@ -81,9 +80,8 @@ BRION SAW SOMETHING CLOSE TO PANIC ON HIS OPPONENT'S FACE WHEN THE MAN FINALLY R Pour récapituler, le modèle prédit un *token* (caractère) pour chaque 20 ms d'audio (partiellement chevauchant) à partir de la forme d'onde d'entrée. Cela donne beaucoup de doublons. Grâce au *token* blanc de la CTC, nous pouvons facilement supprimer ces doublons sans détruire la bonne l'orthographe des mots. C'est un moyen très simple et pratique de résoudre le problème de l'alignement du texte de sortie avec l'audio d'entrée. - -💡 Dans le modèle Wav2Vec2, le *token* blanc est le même que le *token* de remplissage ``. Le modèle prédira beaucoup de ces *tokens* ``, par exemple lorsqu'il n'y a pas de caractère clair à prédire pour les 20 ms actuelles d'audio. L'utilisation du même *token* pour le remplissage que pour les blancs simplifie l'algorithme de décodage et aide à garder le vocabulaire petit. - +> [!TIP] +> 💡 Dans le modèle Wav2Vec2, le *token* blanc est le même que le *token* de remplissage ``. Le modèle prédira beaucoup de ces *tokens* ``, par exemple lorsqu'il n'y a pas de caractère clair à prédire pour les 20 ms actuelles d'audio. L'utilisation du même *token* pour le remplissage que pour les blancs simplifie l'algorithme de décodage et aide à garder le vocabulaire petit. L'ajout de la CTC à un *transformer* encodeur est facile : la séquence de sortie de l’encodeur va dans une couche linéaire qui projette les caractéristiques acoustiques dans le vocabulaire. Le modèle est entraîné avec une perte de CTC spéciale. Un inconvénient de la CTC est qu'elle peut produire des mots qui *sonnent* corrects mais ne sont pas *orthographiés* correctement. Après tout, la tête de la CTC ne prend en compte que les caractères individuels, pas les mots complets. Une façon d'améliorer la qualité des transcriptions audio est d'utiliser un modèle de langage externe. Ce modèle de langage agit essentiellement comme un correcteur orthographique au-dessus de la sortie de la CTC. diff --git a/chapters/fr/chapter3/introduction.mdx b/chapters/fr/chapter3/introduction.mdx index 281840d2..5dccd4ad 100644 --- a/chapters/fr/chapter3/introduction.mdx +++ b/chapters/fr/chapter3/introduction.mdx @@ -82,9 +82,8 @@ Par exemple dans le modèle **[SpeechT5](https://arxiv.org/abs/2110.07205)** la SpeechT5 outputs a spectrogram and uses a vocoder to create the waveform - -💡 Si vous prenez une forme d'onde existante et appliquez la transformée de Fourier à court terme (TFCT), il est possible d'effectuer l'opération inverse pour obtenir à nouveau la forme d'onde d'origine. Cela fonctionne parce que le spectrogramme créé par la TFCT contient à la fois des informations d'amplitude et de phase, et les deux sont nécessaires pour reconstruire la forme d'onde. Cependant, les modèles audio qui génèrent leur sortie sous forme de spectrogramme ne prédisent généralement que les informations d'amplitude, pas la phase. Pour transformer un tel spectrogramme en une forme d'onde, nous devons en quelque sorte estimer l'information de phase. C'est ce que fait un vocodeur. - +> [!TIP] +> 💡 Si vous prenez une forme d'onde existante et appliquez la transformée de Fourier à court terme (TFCT), il est possible d'effectuer l'opération inverse pour obtenir à nouveau la forme d'onde d'origine. Cela fonctionne parce que le spectrogramme créé par la TFCT contient à la fois des informations d'amplitude et de phase, et les deux sont nécessaires pour reconstruire la forme d'onde. Cependant, les modèles audio qui génèrent leur sortie sous forme de spectrogramme ne prédisent généralement que les informations d'amplitude, pas la phase. Pour transformer un tel spectrogramme en une forme d'onde, nous devons en quelque sorte estimer l'information de phase. C'est ce que fait un vocodeur. ### Sortie sous forme de forme d'onde diff --git a/chapters/fr/chapter3/seq2seq.mdx b/chapters/fr/chapter3/seq2seq.mdx index fba2eebf..54ec370e 100644 --- a/chapters/fr/chapter3/seq2seq.mdx +++ b/chapters/fr/chapter3/seq2seq.mdx @@ -15,9 +15,8 @@ L'architecture de **Whisper** est la suivante (figure provenant du blog d’[Ope Cela devrait vous sembler assez familier. Sur la gauche se trouve l’**encodeur du *transformer***. Il prend comme entrée un spectrogramme log-mel et encode ce spectrogramme pour former une séquence d'états cachés de l’encodeur qui extraient des caractéristiques importantes de la parole. Ce tenseur à états cachés représente la séquence d'entrée dans son ensemble et code efficacement le « sens » du discours d'entrée. - -💡 Il est courant que ces modèles seq2seq utilisent des spectrogrammes comme entrée. Cependant, un modèle seq2seq peut également être conçu pour fonctionner directement sur les formes d'onde audio. - +> [!TIP] +> 💡 Il est courant que ces modèles seq2seq utilisent des spectrogrammes comme entrée. Cependant, un modèle seq2seq peut également être conçu pour fonctionner directement sur les formes d'onde audio. La sortie de l’encodeur est ensuite passée dans le **décodeur du *transformer***, illustré à droite, à l'aide d'un mécanisme d’**attention croisée**. C'est comme l'auto-attention, mais assiste la sortie de l'encodeur. À partir de ce moment, l'encodeur n'est plus nécessaire. Le décodeur prédit une séquence de *tokens* de texte de manière **autorégressive**, un seul *token* à la fois, à partir d'une séquence initiale qui contient juste un *token* « start » (`SOT` dans le cas de Whisper). À chaque pas de temps suivant, la séquence de sortie précédente est réintroduite dans le décodeur en tant que nouvelle séquence d'entrée. De cette manière, le décodeur émet un nouveau *token* à la fois, augmentant régulièrement la séquence de sortie, jusqu'à ce qu'il prédise qu'un *token* de fin ou un nombre maximum de pas de temps est atteint. @@ -26,19 +25,18 @@ Alors que l'architecture du décodeur est en grande partie identique à celle de 2. L'attention du décodeur est causale : le décodeur n'est pas autorisé à regarder vers l'avenir. Dans cette conception, le décodeur joue le rôle d'un **modèle de langage**, traitant les représentations à l'état caché de l'encodeur et générant les transcriptions de texte correspondantes. Il s'agit d'une approche plus puissante que la CTC (même si la CTC est combinée avec un modèle de langage externe), car le système seq2seq peut être entraîné de bout en bout avec les mêmes données d'apprentissage et la même fonction de perte, offrant une plus grande flexibilité et des performances généralement supérieures. - -💡 Alors qu'un modèle avec CTC produit une séquence de caractères individuels, les *tokens* prédits par Whisper sont des mots complets ou des portions de mots. Il utilise le tokenizer de GPT-2 et dispose d’environ 50 000 *tokens* uniques. Un modèle seq2seq peut donc produire une séquence beaucoup plus courte qu'un modèle CTC pour la même transcription. - - -Une fonction de perte typique pour un modèle d’ASR seq2seq est la perte d'entropie croisée car la dernière couche du modèle prédit une distribution de probabilité sur les *tokens* possibles. Ceci est généralement combiné avec des techniques telles que [recherche en faisceau pour générer la séquence finale](https://huggingface.co/blog/how-to-generate). La métrique de la reconnaissance vocale est le WER (*word error rate*) ou taux d'erreur de mots, qui mesure le nombre de substitutions, d'insertions et de suppressions nécessaires pour transformer le texte prédit en texte cible. Moins il y en a, meilleur est le score. - -## Synthèse vocale - -Cela ne vous surprendra peut-être pas : un modèle seq2seq pour la synthèse vocale fonctionne essentiellement de la même manière que décrit ci-dessus, mais avec les entrées et les sorties inversées ! L'encodeur du *transformer* prend une séquence de *tokens* de texte et extrait une séquence d'états masqués qui représentent le texte d'entrée. Le décodeur du *transformer* applique une attention croisée à la sortie de l’encodeur et prédit un spectrogramme. - - -💡 Rappelons qu'un spectrogramme est fabriqué en prenant le spectre de fréquences de tranches de temps successives d'une forme d'onde audio et en les empilant ensemble. En d'autres termes, un spectrogramme est une séquence où les éléments sont des spectres de fréquence (log-mel), un pour chaque pas de temps. - +> [!TIP] +> 💡 Alors qu'un modèle avec CTC produit une séquence de caractères individuels, les *tokens* prédits par Whisper sont des mots complets ou des portions de mots. Il utilise le tokenizer de GPT-2 et dispose d’environ 50 000 *tokens* uniques. Un modèle seq2seq peut donc produire une séquence beaucoup plus courte qu'un modèle CTC pour la même transcription. +> +> +> Une fonction de perte typique pour un modèle d’ASR seq2seq est la perte d'entropie croisée car la dernière couche du modèle prédit une distribution de probabilité sur les *tokens* possibles. Ceci est généralement combiné avec des techniques telles que [recherche en faisceau pour générer la séquence finale](https://huggingface.co/blog/how-to-generate). La métrique de la reconnaissance vocale est le WER (*word error rate*) ou taux d'erreur de mots, qui mesure le nombre de substitutions, d'insertions et de suppressions nécessaires pour transformer le texte prédit en texte cible. Moins il y en a, meilleur est le score. +> +> ## Synthèse vocale +> +> Cela ne vous surprendra peut-être pas : un modèle seq2seq pour la synthèse vocale fonctionne essentiellement de la même manière que décrit ci-dessus, mais avec les entrées et les sorties inversées ! L'encodeur du *transformer* prend une séquence de *tokens* de texte et extrait une séquence d'états masqués qui représentent le texte d'entrée. Le décodeur du *transformer* applique une attention croisée à la sortie de l’encodeur et prédit un spectrogramme. +> +> +> 💡 Rappelons qu'un spectrogramme est fabriqué en prenant le spectre de fréquences de tranches de temps successives d'une forme d'onde audio et en les empilant ensemble. En d'autres termes, un spectrogramme est une séquence où les éléments sont des spectres de fréquence (log-mel), un pour chaque pas de temps. Avec le modèle d’ASR, le décodeur a été démarré à l'aide d'une séquence qui contient simplement le *token* spécial « start ». Pour le modèle de synthèse vocale, on peut commencer le décodage avec un spectrogramme de longueur 1 et rempli de 0 qui agit comme le « *token* de départ ». Compte tenu de ce spectrogramme initial et des attentions croisées sur les représentations à l'état caché de l’encodeur, le décodeur prédit ensuite la prochaine tranche de temps pour ce spectrogramme, augmentant régulièrement le spectrogramme un pas de temps à la fois. diff --git a/chapters/fr/chapter4/fine-tuning.mdx b/chapters/fr/chapter4/fine-tuning.mdx index 0cdda837..6d071576 100644 --- a/chapters/fr/chapter4/fine-tuning.mdx +++ b/chapters/fr/chapter4/fine-tuning.mdx @@ -1,471 +1,467 @@ -# Finetuner un modèle de classification musicale - -Dans cette section, nous présenterons un guide étape par étape sur le *finetuning* d'un *transformer* encodeur pour la classification de la musique. -Nous utiliserons un modèle léger pour cette démonstration et un jeu de données assez petit, ce qui signifie que le code est exécutable de bout en bout sur n'importe quel GPU grand public, y compris le GPU T4 16GB fourni gratuitement par Google Colab. La section comprend divers conseils que vous pouvez essayer si vous avez un GPU plus petit et rencontrez des problèmes de mémoire en cours de route. - -## Le de données - -Pour entraîner notre modèle, nous utiliserons le jeu de données [GTZAN](https://huggingface.co/datasets/marsyas/gtzan), qui est un jeu de données populaire de 1 000 chansons pour la classification des genres musicaux. Chaque chanson dure 30 secondes et fait partie de l'un des 10 genres de musique disponible, allant du disco au métal. Nous pouvons obtenir les fichiers audio et leurs étiquettes correspondantes à partir du *Hub* avec la fonction `load_dataset()` de 🤗 *Datasets* : - -```python -from datasets import load_dataset - -gtzan = load_dataset("marsyas/gtzan", "all") -gtzan -``` - -**Sortie :** - -```out -Dataset({ - features: ['file', 'audio', 'genre'], - num_rows: 999 -}) -``` - - -L'un des enregistrements dans GTZAN est corrompu, il a donc été supprimé du jeu de données. C'est pourquoi nous avons 999 exemples au lieu de 1 000. - - -GTZAN ne fournit pas de jeu de validation prédéfini, nous devrons donc en créer un nous-mêmes. Le jeu de données est équilibré entre les genres, nous pouvons donc utiliser la méthode `train_test_split()` pour créer rapidement une répartition 90/10 comme suit : - -```python -gtzan = gtzan["train"].train_test_split(seed=42, shuffle=True, test_size=0.1) -gtzan -``` - -**Sortie :** - -```out -DatasetDict({ - train: Dataset({ - features: ['file', 'audio', 'genre'], - num_rows: 899 - }) - test: Dataset({ - features: ['file', 'audio', 'genre'], - num_rows: 100 - }) -}) -``` - -Maintenant que nous avons nos jeux d’entraînement et de validation, jetons un coup d'œil à l'un des fichiers audio : - -```python -gtzan["train"][0] -``` - -**Sortie :** - -```out -{ - "file": "~/.cache/huggingface/datasets/downloads/extracted/fa06ce46130d3467683100aca945d6deafb642315765a784456e1d81c94715a8/genres/pop/pop.00098.wav", - "audio": { - "path": "~/.cache/huggingface/datasets/downloads/extracted/fa06ce46130d3467683100aca945d6deafb642315765a784456e1d81c94715a8/genres/pop/pop.00098.wav", - "array": array( - [ - 0.10720825, - 0.16122437, - 0.28585815, - ..., - -0.22924805, - -0.20629883, - -0.11334229, - ], - dtype=float32, - ), - "sampling_rate": 22050, - }, - "genre": 7, -} -``` - -Comme nous l'avons vu dans [Unité 1](.. /chapter1/audio_data), les fichiers audio sont représentés sous forme de tableaux NumPy à 1 dimension, où la valeur du tableau représente l'amplitude à ce pas de temps. Pour ces chansons, la fréquence d'échantillonnage est de 22 050 Hz, ce qui signifie qu'il y a 22 050 valeurs d'amplitude échantillonnées par seconde. Nous devrons garder cela à l'esprit lorsque nous utiliserons un modèle pré-entraîné avec un taux d'échantillonnage différent, en convertissant nous-mêmes les taux d'échantillonnage pour nous assurer qu'ils correspondent. Nous pouvons également voir que le genre est représenté sous la forme d'un entier, ou _class label_, qui est le format dans lequel le modèle fera ses prédictions. Utilisons la méthode `int2str()` de la caractéristique `genre` pour associer ces entiers à des noms lisibles par l'homme : - -```python -id2label_fn = gtzan["train"].features["genre"].int2str -id2label_fn(gtzan["train"][0]["genre"]) -``` - -**Sortie :** - -```out -'pop' -``` - -Cette étiquette semble correcte, car elle correspond au nom de fichier du fichier audio. Écoutons maintenant quelques exemples supplémentaires en utilisant Gradio pour créer une interface simple avec l'API `Blocks` : - -```python -def generate_audio(): - example = gtzan["train"].shuffle()[0] - audio = example["audio"] - return ( - audio["sampling_rate"], - audio["array"], - ), id2label_fn(example["genre"]) - - -with gr.Blocks() as demo: - with gr.Column(): - for _ in range(4): - audio, label = generate_audio() - output = gr.Audio(audio, label=label) - -demo.launch(debug=True) -``` - - - -À partir de ces échantillons, nous pouvons certainement entendre la différence entre les genres, mais un *transformer* peut-il le faire aussi ? Entraînons un modèle pour le découvrir ! Tout d'abord, nous devrons trouver un modèle pré-entraîné approprié pour cette tâche. Voyons comment nous pouvons le faire. - -## Choisir un modèle pré-entraîné pour la classification audio - -Pour commencer, choisissons un modèle pré-entraîné approprié pour la classification audio. Dans ce domaine, le pré-entraînement est généralement effectuée sur de grandes quantités de données audio non étiquetées, en utilisant des jeux de données tels que [LibriSpeech](https://huggingface.co/datasets/librispeech_asr) et [Voxpopuli](https://huggingface.co/datasets/facebook/voxpopuli). La meilleure façon de trouver ces modèles sur le *Hub* est d'utiliser le filtre « classification audio », comme décrit dans la section précédente. Bien que des modèles comme Wav2Vec2 et HuBERT soient très populaires, nous utiliserons un modèle appelé _DistilHuBERT_. Il s'agit d'une version beaucoup plus petite (ou distillée) du modèle [HuBERT](https://huggingface.co/docs/transentraîners/model_doc/hubert), qui s’entraîne environ 73% plus vite, tout en préservant la plupart des performances. - - - -## De l'audio aux caractéristiques d'apprentissage automatique - -## Prétraitement des données - -À l'instar de la tokenisation en NLP, les modèles audio et vocaux nécessitent que l'entrée soit encodée dans un format que le modèle peut traiter. Dans 🤗 *Transformers*, la conversion de l'audio au format d'entrée est gérée par l’extracteur de caractéristique du modèle. 🤗 *Transformers* fournit une classe pratique `AutoFeatureExtractor` qui peut sélectionner automatiquement le bon extracteur de caractéristiques pour un modèle donné. Pour voir comment nous pouvons traiter nos fichiers audio, commençons par instancier l'extracteur de caractéristiques pour DistilHuBERT à partir du *checkpoint* pré-entraîné : - -```python -from transformers import AutoFeatureExtractor - -model_id = "ntu-spml/distilhubert" -feature_extractor = AutoFeatureExtractor.from_pretrained( - model_id, do_normalize=True, return_attention_mask=True -) -``` - -Comme le taux d'échantillonnage du modèle et de le jeu de données est différent, nous devons rééchantillonner le fichier audio à 16 000 Hz avant de le transmettre à l'extracteur de caractéristiques. Nous pouvons le faire en obtenant d'abord la fréquence d'échantillonnage du modèle à partir de l'extracteur de caractéristiques : - -```python -sampling_rate = feature_extractor.sampling_rate -sampling_rate -``` - -**Sortie :** - -```out -16000 -``` - -Ensuite, nous rééchantillonnons le jeu de données à l'aide de la méthode `cast_column()` et de la fonction `Audio` de 🤗 *Datasets* : - -```python -from datasets import Audio - -gtzan = gtzan.cast_column("audio", Audio(sampling_rate=sampling_rate)) -``` - -Nous pouvons maintenant vérifier le premier échantillon de l’échantillon d’entraînement de notre jeu de données pour vérifier qu'il est bien à 16 000 Hz. 🤗 *Datasets* rééchantillonnent le fichier audio *à la volée* lorsque nous chargeons chaque échantillon audio : - -```python -gtzan["train"][0] -``` - -**Sortie :** - -```out -{ - "file": "~/.cache/huggingface/datasets/downloads/extracted/fa06ce46130d3467683100aca945d6deafb642315765a784456e1d81c94715a8/genres/pop/pop.00098.wav", - "audio": { - "path": "~/.cache/huggingface/datasets/downloads/extracted/fa06ce46130d3467683100aca945d6deafb642315765a784456e1d81c94715a8/genres/pop/pop.00098.wav", - "array": array( - [ - 0.0873509, - 0.20183384, - 0.4790867, - ..., - -0.18743178, - -0.23294401, - -0.13517427, - ], - dtype=float32, - ), - "sampling_rate": 16000, - }, - "genre": 7, -} -``` - -Bien. Nous pouvons voir que le taux d'échantillonnage a été sous-échantillonné à 16kHz. Les valeurs de tableau sont également différentes car nous n'avons maintenant qu'environ une valeur d'amplitude pour chaque 1,5 pas que nous avions auparavant. -Une caractéristique de conception des modèles de type Wav2Vec2 et HuBERT est qu'ils acceptent un tableau de flottants correspondant à la forme d'onde brute du signal vocal comme entrée. Cela contraste avec d'autres modèles, comme Whisper, où nous prétraitons la forme d'onde audio brute au format spectrogramme. -Nous avons mentionné que les données audio sont représentées comme un tableau à 1 dimension, elles sont donc déjà dans le bon format pour être lues par le modèle (un ensemble d'entrées continues à pas de temps discrets). Alors, que fait exactement l'extracteur de caractéristiques ? -Eh bien, les données audio sont dans le bon format, mais nous n'avons imposé aucune restriction sur les valeurs qu'elles peuvent prendre. Pour que notre modèle fonctionne de manière optimale, nous voulons conserver toutes les entrées dans la même plage dynamique. Cela va nous assurer d'obtenir une plage similaire d'activations et de gradients pour nos échantillons, ce qui nous aidera à la stabilité et à la convergence pendant l'entraînement. -Pour ce faire, nous *normalisons* nos données audio, en redimensionnant chaque échantillon à une moyenne nulle et une variance unitaire pour avoir des variables centrées réduites. C'est exactement cette normalisation des caractéristiques que notre extracteur de caractéristiques effectue. -Nous pouvons jeter un coup d'œil à l'extracteur de caractéristiques en fonctionnement en l'appliquant à notre premier échantillon audio. Tout d'abord, calculons la moyenne et la variance de nos données audio brutes : - -```python -import numpy as np - -sample = gtzan["train"][0]["audio"] - -print(f"Mean: {np.mean(sample['array']):.3}, Variance: {np.var(sample['array']):.3}") -``` - -**Sortie :** - -```out -Mean: 0.000185, Variance: 0.0493 -``` - -Nous pouvons voir que la moyenne est déjà proche de 0, mais la variance est plus proche de 0,05. Si la variance de l'échantillon était plus grande, cela pourrait causer des problèmes à notre modèle, car la plage dynamique des données audio serait très petite et donc difficile à séparer. Appliquons l'extracteur de caractéristiques et voyons à quoi ressemblent les sorties: - -```python -inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) - -print(f"inputs keys: {list(inputs.keys())}") - -print( - f"Mean: {np.mean(inputs['input_values']):.3}, Variance: {np.var(inputs['input_values']):.3}" -) -``` - -**Sortie :** - -```out -inputs keys: ['input_values', 'attention_mask'] -Mean: -4.53e-09, Variance: 1.0 -``` - -Notre extracteur de caractéristiques renvoie un dictionnaire de deux tableaux : `input_values` et `attention_mask`. Les `input_values` sont les entrées audio prétraitées que nous passerons au modèle HuBERT. L’[`attention_mask`](https://huggingface.co/docs/transentraîners/glossary#attention-mask) est utilisé lorsque nous traitons un _batch_ d'entrées audio à la fois. Il est utilisé pour indiquer au modèle où nous avons des entrées rembourrées de différentes longueurs. -Nous pouvons voir que la valeur moyenne est maintenant beaucoup plus proche de 0, et la variance est à 1 ! C'est exactement la forme sous laquelle nous voulons que nos échantillons audio soient avant de les passer dans notre HuBERT. - - -Notez comment nous avons transmis le taux d'échantillonnage de nos données audio à notre extracteur de caractéristiques. Il s'agit d'une bonne pratique, car l'extracteur de caractéristiques effectue une vérification sous le capot pour s'assurer que le taux d'échantillonnage de nos données audio correspond au taux d'échantillonnage attendu par le modèle. Si le taux d'échantillonnage de nos données audio ne correspondait pas au taux d'échantillonnage de notre modèle, nous aurions besoin de suréchantillonner ou de sous-échantillonner les données audio au taux d'échantillonnage correct. - - -Alors maintenant que nous savons comment traiter nos fichiers audio rééchantillonnés, la dernière chose à faire est de définir une fonction que nous pouvons appliquer à tous les exemples du jeu de données. Étant donné que nous nous attendons à des échantillons de 30 secondes, nous tronquerons aussi les échantillons plus longs en utilisant les arguments `max_length` et `truncation` de l'extracteur de caractéristiques comme suit : - -```python -max_duration = 30.0 - - -def preprocess_function(examples): - audio_arrays = [x["array"] for x in examples["audio"]] - inputs = feature_extractor( - audio_arrays, - sampling_rate=feature_extractor.sampling_rate, - max_length=int(feature_extractor.sampling_rate * max_duration), - truncation=True, - return_attention_mask=True, - ) - return inputs -``` - -Une fois cette fonction définie, nous pouvons maintenant l'appliquer au jeu de données à l'aide de la méthode `map()`. - -```python -gtzan_encoded = gtzan.map( - preprocess_function, remove_columns=["audio", "file"], batched=True, num_proc=1 -) -gtzan_encoded -``` - -**Sortie :** - -```out -DatasetDict({ - train: Dataset({ - features: ['genre', 'input_values'], - num_rows: 899 - }) - test: Dataset({ - features: ['genre', 'input_values'], - num_rows: 100 - }) -}) -``` - -Pour simplifier l’entraînement, nous avons supprimé les colonnes `audio` et `file` du jeu de données. La colonne `input_values` contient les fichiers audio encodés, la colonne `attention_mask` est un masque binaire de valeurs 0/1 qui indique où nous avons rempli l'entrée audio, et la colonne `genre` contient les étiquettes (ou cibles) correspondantes. Pour permettre au `Trainer` de traiter les étiquettes de classe, nous devons renommer la colonne `genre` en `label` : - -```python -gtzan_encoded = gtzan_encoded.rename_column("genre", "label") -``` - -Enfin, nous devons obtenir les associations d'étiquettes à partir du jeu de données. L’association nous mènera des identifiants entiers (par exemple `7`) aux étiquettes de classe lisibles par l'homme (par exemple `pop`) et inversement. Ce faisant, nous pouvons convertir la prédiction de notre modèle dans un format lisible par l'homme, ce qui nous permet d'utiliser le modèle dans n'importe quelle application en aval. Nous pouvons le faire en utilisant la méthode `int2str()` comme suit: - -```python -id2label = { - str(i): id2label_fn(i) - for i in range(len(gtzan_encoded["train"].features["label"].names)) -} -label2id = {v: k for k, v in id2label.items()} - -id2label["7"] -``` - -**Sortie :** - -```out -'pop' -``` - -Nous avons maintenant un jeu de données prêt pour l’entraînement. Voyons comment nous pouvons entraîner un modèle sur ce jeu de données. - -## Finetuner le modèle - -Pour affiner le modèle, nous utiliserons la classe 'Trainer' de 🤗 *Transformers*. Comme nous l'avons vu dans d'autres chapitres, le « Trainer » est une API de haut niveau conçue pour gérer les scénarios de formation les plus courants. Dans ce cas, nous utiliserons le 'Trainer' pour affiner le modèle sur GTZAN. Pour ce faire, nous devons d'abord charger un modèle pour cette tâche. Nous pouvons le faire en utilisant la classe 'AutoModelForAudioClassification', qui ajoutera automatiquement la tête de classification appropriée à notre modèle DistilHuBERT préentraîné. Allons de l'avant et instancions le modèle : - -'''python -from transformers import AutoModelForAudioClassification - -num_labels = len(id2label) - -modèle = AutoModelForAudioClassification.from_pretrained( - model_id, - num_labels=num_labels, - label2id=label2id, - id2label=id2label, -) -``` - -Nous vous conseillons fortement de télécharger les *checkpoints* du modèle directement sur le [*Hub*](https://huggingface.co/) pendant l'entraînement. -Le *Hub* fournit : -- Un contrôle de version intégré : vous pouvez être sûr qu'aucun *checkpoint* du modèle n'est perdu pendant l’entraînement. -- Tensorboard : suivez les métriques importantes au cours de l’entraînement. -- La carte du modèle : documentant ce que fait un modèle et ses cas d'utilisation prévus. -- Communauté : un moyen facile de partager et de collaborer avec la communauté ! -Lier le *notebook* au *Hub* est simple. Il suffit d'entrer votre *token* d'authentification au *Hub* lorsque vous y êtes invité. -Vous pouvez trouver votre *token* d'authentification [ici](https://huggingface.co/settings/tokens) et le saisir dans - -```python -from huggingface_hub import notebook_login - -notebook_login() -``` - -**Sortie :** - -```bash -Login successful -Your token has been saved to /root/.huggingface/token -``` - -L'étape suivante consiste à définir les arguments d'apprentissage, y compris la taille du batch, les étapes d'accumulation du gradient, le nombre d'époques d'apprentissage et le taux d'apprentissage : - -```python -from transformers import TrainingArguments - -model_name = model_id.split("/")[-1] -batch_size = 8 -gradient_accumulation_steps = 1 -num_train_epochs = 10 - -training_args = TrainingArguments( - f"{model_name}-finetuned-gtzan", - evaluation_strategy="epoch", - save_strategy="epoch", - learning_rate=5e-5, - per_device_train_batch_size=batch_size, - gradient_accumulation_steps=gradient_accumulation_steps, - per_device_eval_batch_size=batch_size, - num_train_epochs=num_train_epochs, - warmup_ratio=0.1, - logging_steps=5, - load_best_model_at_end=True, - metric_for_best_model="accuracy", - fp16=True, - push_to_hub=True, -) -``` - - -Ici, nous avons défini `push_to_hub=True` pour activer le téléchargement automatique de nos *checkpoints* *finetunés* pendant l'entraînement. Si vous ne souhaitez pas que vos *checkpoints* soient téléchargés sur le *Hub*, vous pouvez définir cette option sur `False`. - - -La dernière chose que nous devons faire est de définir les métriques. Étant donné que le jeu de données est équilibré, nous utiliserons la précision comme métrique et le chargerons à l'aide de la bibliothèque 🤗 *Evaluate* : - -```python -import evaluate - -metric = evaluate.load("accuracy") - - -def compute_metrics(eval_pred): - """Computes accuracy on a batch of predictions""" - predictions = np.argmax(eval_pred.predictions, axis=1) - return metric.compute(predictions=predictions, references=eval_pred.label_ids) -``` - -Nous avons maintenant toutes les pièces ! Instancions le `Trainer` et entraînons le modèle : - -```python -from transformers import Trainer - -trainer = Trainer( - model, - training_args, - train_dataset=gtzan_encoded["train"], - eval_dataset=gtzan_encoded["test"], - tokenizer=feature_extractor, - compute_metrics=compute_metrics, -) - -trainer.train() -``` - - -Selon votre GPU, il est possible que vous rencontriez une erreur CUDA `"out-of-memory"` lorsque vous commencez à vous entraîner. -Dans ce cas, vous pouvez réduire la taille du batch de manière incrémentielle par des facteurs de 2 et utiliser ['gradient_accumulation_steps'](https://huggingface.co/docs/transentraîners/main_classes/trainer#transentraîners.TrainingArguments.gradient_accumulation_steps) pour compenser. - - -**Sortie :** - -```out -| Training Loss | Epoch | Step | Validation Loss | Accuracy | -|:-------------:|:-----:|:----:|:---------------:|:--------:| -| 1.7297 | 1.0 | 113 | 1.8011 | 0.44 | -| 1.24 | 2.0 | 226 | 1.3045 | 0.64 | -| 0.9805 | 3.0 | 339 | 0.9888 | 0.7 | -| 0.6853 | 4.0 | 452 | 0.7508 | 0.79 | -| 0.4502 | 5.0 | 565 | 0.6224 | 0.81 | -| 0.3015 | 6.0 | 678 | 0.5411 | 0.83 | -| 0.2244 | 7.0 | 791 | 0.6293 | 0.78 | -| 0.3108 | 8.0 | 904 | 0.5857 | 0.81 | -| 0.1644 | 9.0 | 1017 | 0.5355 | 0.83 | -| 0.1198 | 10.0 | 1130 | 0.5716 | 0.82 | -``` - -L’entraînement durera environ 1 heure en fonction de votre GPU ou de celui alloué par Google Colab. Notre meilleure précision d'évaluation est de 83%. Pas mal pour seulement 10 époques avec 899 exemples d'entraînement ! Nous pourrions certainement améliorer ce résultat en entraînant sur plus d'époques, en utilisant des techniques de régularisation telles que le *dropout*, ou en découpant chaque segment d’audio de 30s en segments de 15s pour utiliser une stratégie de prétraitement de données plus efficace. -La grande question est de savoir comment ce système se compare à d'autres systèmes de classification de musique 🤔 -Pour cela, nous pouvons afficher le [classement *autoevaluate*](https://huggingface.co/spaces/autoevaluate/leaderboards?dataset=marsyas%2Fgtzan&only_verified=0&task=audio-classification&config=all&split=train&metric=accuracy), un classement qui catégorise les modèles par langue et jeu de données, puis les classe en fonction de leur précision. -Nous pouvons automatiquement soumettre notre *checkpoint* au classement lorsque nous transmettons les résultats de l'entraînement au *Hub*. Nous devons simplement définir les arguments appropriés (kwargs). Vous pouvez modifier ces valeurs pour qu'elles correspondent à votre jeu de données, à votre langue et au nom de votre modèle en conséquence : - -```python -kwargs = { - "dataset_tags": "marsyas/gtzan", - "dataset": "GTZAN", - "model_name": f"{model_name}-finetuned-gtzan", - "finetuned_from": model_id, - "tasks": "audio-classification", -} -``` - -Les résultats de l’entraînement peuvent maintenant être téléchargés sur le *Hub*. Pour ce faire, exécutez la commande `.push_to_hub` : - -```python -trainer.push_to_hub(**kwargs) -``` - -Cela enregistrera les logs d'entraînement et les poids du modèle sous `"your-username/distilhubert-finetuned-gtzan"`. Pour cet exemple, consultez [`"sanchit-gandhi/distilhubert-finetuned-gtzan"`](https://huggingface.co/sanchit-gandhi/distilhubert-finetuned-gtzan). - -## Partager le modèle - -Vous pouvez désormais partager votre modèle avec n'importe qui en utilisant le lien sur le *Hub*. Il sera utilisable avec l'identifiant `"your-username/distilhubert-finetuned-gtzan"` directement dans la classe `pipeline()`. Par exemple, pour charger le *checkpoint* *finetuné* ['"sanchit-gandhi/distilhubert-finetuned-gtzan"'](https://huggingface.co/sanchit-gandhi/distilhubert-finetuned-gtzan): - -```python -from transformers import pipeline - -pipe = pipeline( - "audio-classification", model="sanchit-gandhi/distilhubert-finetuned-gtzan" -) -``` - -## Conclusion - -Dans cette section, nous avons couvert un guide étape par étape pour *finetuner* le modèle DistilHuBERT pour la tâche de classification de la musique. Bien que nous nous soyons concentrés sur cette tâche et le jeu de données GTZAN, les étapes présentées ici s'appliquent plus généralement à toute tâche de classification audio. Le même script peut être utilisé pour les tâches de détection de mots-clés ou l'identification de la langue. Il vous suffit de changer le jeu de données avec celui de votre tâche d'intérêt ! Si vous souhaitez *finetuner* d'autres modèles du *Hub* pour la classification d’audio, nous vous encourageons à consulter les [exemples](https://github.com/huggingface/transformers/tree/main/examples/pytorch/audio-classification) trouvables sur le dépôt 🤗 *Transformers*. -Dans la section suivante, nous prendrons le modèle que nous venons de *finetuner* et construirons une démo avec *Gradio* que nous pourrons partager sur le Hub. +# Finetuner un modèle de classification musicale + +Dans cette section, nous présenterons un guide étape par étape sur le *finetuning* d'un *transformer* encodeur pour la classification de la musique. +Nous utiliserons un modèle léger pour cette démonstration et un jeu de données assez petit, ce qui signifie que le code est exécutable de bout en bout sur n'importe quel GPU grand public, y compris le GPU T4 16GB fourni gratuitement par Google Colab. La section comprend divers conseils que vous pouvez essayer si vous avez un GPU plus petit et rencontrez des problèmes de mémoire en cours de route. + +## Le de données + +Pour entraîner notre modèle, nous utiliserons le jeu de données [GTZAN](https://huggingface.co/datasets/marsyas/gtzan), qui est un jeu de données populaire de 1 000 chansons pour la classification des genres musicaux. Chaque chanson dure 30 secondes et fait partie de l'un des 10 genres de musique disponible, allant du disco au métal. Nous pouvons obtenir les fichiers audio et leurs étiquettes correspondantes à partir du *Hub* avec la fonction `load_dataset()` de 🤗 *Datasets* : + +```python +from datasets import load_dataset + +gtzan = load_dataset("marsyas/gtzan", "all") +gtzan +``` + +**Sortie :** + +```out +Dataset({ + features: ['file', 'audio', 'genre'], + num_rows: 999 +}) +``` + +> [!WARNING] +> L'un des enregistrements dans GTZAN est corrompu, il a donc été supprimé du jeu de données. C'est pourquoi nous avons 999 exemples au lieu de 1 000. + +GTZAN ne fournit pas de jeu de validation prédéfini, nous devrons donc en créer un nous-mêmes. Le jeu de données est équilibré entre les genres, nous pouvons donc utiliser la méthode `train_test_split()` pour créer rapidement une répartition 90/10 comme suit : + +```python +gtzan = gtzan["train"].train_test_split(seed=42, shuffle=True, test_size=0.1) +gtzan +``` + +**Sortie :** + +```out +DatasetDict({ + train: Dataset({ + features: ['file', 'audio', 'genre'], + num_rows: 899 + }) + test: Dataset({ + features: ['file', 'audio', 'genre'], + num_rows: 100 + }) +}) +``` + +Maintenant que nous avons nos jeux d’entraînement et de validation, jetons un coup d'œil à l'un des fichiers audio : + +```python +gtzan["train"][0] +``` + +**Sortie :** + +```out +{ + "file": "~/.cache/huggingface/datasets/downloads/extracted/fa06ce46130d3467683100aca945d6deafb642315765a784456e1d81c94715a8/genres/pop/pop.00098.wav", + "audio": { + "path": "~/.cache/huggingface/datasets/downloads/extracted/fa06ce46130d3467683100aca945d6deafb642315765a784456e1d81c94715a8/genres/pop/pop.00098.wav", + "array": array( + [ + 0.10720825, + 0.16122437, + 0.28585815, + ..., + -0.22924805, + -0.20629883, + -0.11334229, + ], + dtype=float32, + ), + "sampling_rate": 22050, + }, + "genre": 7, +} +``` + +Comme nous l'avons vu dans [Unité 1](.. /chapter1/audio_data), les fichiers audio sont représentés sous forme de tableaux NumPy à 1 dimension, où la valeur du tableau représente l'amplitude à ce pas de temps. Pour ces chansons, la fréquence d'échantillonnage est de 22 050 Hz, ce qui signifie qu'il y a 22 050 valeurs d'amplitude échantillonnées par seconde. Nous devrons garder cela à l'esprit lorsque nous utiliserons un modèle pré-entraîné avec un taux d'échantillonnage différent, en convertissant nous-mêmes les taux d'échantillonnage pour nous assurer qu'ils correspondent. Nous pouvons également voir que le genre est représenté sous la forme d'un entier, ou _class label_, qui est le format dans lequel le modèle fera ses prédictions. Utilisons la méthode `int2str()` de la caractéristique `genre` pour associer ces entiers à des noms lisibles par l'homme : + +```python +id2label_fn = gtzan["train"].features["genre"].int2str +id2label_fn(gtzan["train"][0]["genre"]) +``` + +**Sortie :** + +```out +'pop' +``` + +Cette étiquette semble correcte, car elle correspond au nom de fichier du fichier audio. Écoutons maintenant quelques exemples supplémentaires en utilisant Gradio pour créer une interface simple avec l'API `Blocks` : + +```python +def generate_audio(): + example = gtzan["train"].shuffle()[0] + audio = example["audio"] + return ( + audio["sampling_rate"], + audio["array"], + ), id2label_fn(example["genre"]) + + +with gr.Blocks() as demo: + with gr.Column(): + for _ in range(4): + audio, label = generate_audio() + output = gr.Audio(audio, label=label) + +demo.launch(debug=True) +``` + + + +À partir de ces échantillons, nous pouvons certainement entendre la différence entre les genres, mais un *transformer* peut-il le faire aussi ? Entraînons un modèle pour le découvrir ! Tout d'abord, nous devrons trouver un modèle pré-entraîné approprié pour cette tâche. Voyons comment nous pouvons le faire. + +## Choisir un modèle pré-entraîné pour la classification audio + +Pour commencer, choisissons un modèle pré-entraîné approprié pour la classification audio. Dans ce domaine, le pré-entraînement est généralement effectuée sur de grandes quantités de données audio non étiquetées, en utilisant des jeux de données tels que [LibriSpeech](https://huggingface.co/datasets/librispeech_asr) et [Voxpopuli](https://huggingface.co/datasets/facebook/voxpopuli). La meilleure façon de trouver ces modèles sur le *Hub* est d'utiliser le filtre « classification audio », comme décrit dans la section précédente. Bien que des modèles comme Wav2Vec2 et HuBERT soient très populaires, nous utiliserons un modèle appelé _DistilHuBERT_. Il s'agit d'une version beaucoup plus petite (ou distillée) du modèle [HuBERT](https://huggingface.co/docs/transentraîners/model_doc/hubert), qui s’entraîne environ 73% plus vite, tout en préservant la plupart des performances. + + + +## De l'audio aux caractéristiques d'apprentissage automatique + +## Prétraitement des données + +À l'instar de la tokenisation en NLP, les modèles audio et vocaux nécessitent que l'entrée soit encodée dans un format que le modèle peut traiter. Dans 🤗 *Transformers*, la conversion de l'audio au format d'entrée est gérée par l’extracteur de caractéristique du modèle. 🤗 *Transformers* fournit une classe pratique `AutoFeatureExtractor` qui peut sélectionner automatiquement le bon extracteur de caractéristiques pour un modèle donné. Pour voir comment nous pouvons traiter nos fichiers audio, commençons par instancier l'extracteur de caractéristiques pour DistilHuBERT à partir du *checkpoint* pré-entraîné : + +```python +from transformers import AutoFeatureExtractor + +model_id = "ntu-spml/distilhubert" +feature_extractor = AutoFeatureExtractor.from_pretrained( + model_id, do_normalize=True, return_attention_mask=True +) +``` + +Comme le taux d'échantillonnage du modèle et de le jeu de données est différent, nous devons rééchantillonner le fichier audio à 16 000 Hz avant de le transmettre à l'extracteur de caractéristiques. Nous pouvons le faire en obtenant d'abord la fréquence d'échantillonnage du modèle à partir de l'extracteur de caractéristiques : + +```python +sampling_rate = feature_extractor.sampling_rate +sampling_rate +``` + +**Sortie :** + +```out +16000 +``` + +Ensuite, nous rééchantillonnons le jeu de données à l'aide de la méthode `cast_column()` et de la fonction `Audio` de 🤗 *Datasets* : + +```python +from datasets import Audio + +gtzan = gtzan.cast_column("audio", Audio(sampling_rate=sampling_rate)) +``` + +Nous pouvons maintenant vérifier le premier échantillon de l’échantillon d’entraînement de notre jeu de données pour vérifier qu'il est bien à 16 000 Hz. 🤗 *Datasets* rééchantillonnent le fichier audio *à la volée* lorsque nous chargeons chaque échantillon audio : + +```python +gtzan["train"][0] +``` + +**Sortie :** + +```out +{ + "file": "~/.cache/huggingface/datasets/downloads/extracted/fa06ce46130d3467683100aca945d6deafb642315765a784456e1d81c94715a8/genres/pop/pop.00098.wav", + "audio": { + "path": "~/.cache/huggingface/datasets/downloads/extracted/fa06ce46130d3467683100aca945d6deafb642315765a784456e1d81c94715a8/genres/pop/pop.00098.wav", + "array": array( + [ + 0.0873509, + 0.20183384, + 0.4790867, + ..., + -0.18743178, + -0.23294401, + -0.13517427, + ], + dtype=float32, + ), + "sampling_rate": 16000, + }, + "genre": 7, +} +``` + +Bien. Nous pouvons voir que le taux d'échantillonnage a été sous-échantillonné à 16kHz. Les valeurs de tableau sont également différentes car nous n'avons maintenant qu'environ une valeur d'amplitude pour chaque 1,5 pas que nous avions auparavant. +Une caractéristique de conception des modèles de type Wav2Vec2 et HuBERT est qu'ils acceptent un tableau de flottants correspondant à la forme d'onde brute du signal vocal comme entrée. Cela contraste avec d'autres modèles, comme Whisper, où nous prétraitons la forme d'onde audio brute au format spectrogramme. +Nous avons mentionné que les données audio sont représentées comme un tableau à 1 dimension, elles sont donc déjà dans le bon format pour être lues par le modèle (un ensemble d'entrées continues à pas de temps discrets). Alors, que fait exactement l'extracteur de caractéristiques ? +Eh bien, les données audio sont dans le bon format, mais nous n'avons imposé aucune restriction sur les valeurs qu'elles peuvent prendre. Pour que notre modèle fonctionne de manière optimale, nous voulons conserver toutes les entrées dans la même plage dynamique. Cela va nous assurer d'obtenir une plage similaire d'activations et de gradients pour nos échantillons, ce qui nous aidera à la stabilité et à la convergence pendant l'entraînement. +Pour ce faire, nous *normalisons* nos données audio, en redimensionnant chaque échantillon à une moyenne nulle et une variance unitaire pour avoir des variables centrées réduites. C'est exactement cette normalisation des caractéristiques que notre extracteur de caractéristiques effectue. +Nous pouvons jeter un coup d'œil à l'extracteur de caractéristiques en fonctionnement en l'appliquant à notre premier échantillon audio. Tout d'abord, calculons la moyenne et la variance de nos données audio brutes : + +```python +import numpy as np + +sample = gtzan["train"][0]["audio"] + +print(f"Mean: {np.mean(sample['array']):.3}, Variance: {np.var(sample['array']):.3}") +``` + +**Sortie :** + +```out +Mean: 0.000185, Variance: 0.0493 +``` + +Nous pouvons voir que la moyenne est déjà proche de 0, mais la variance est plus proche de 0,05. Si la variance de l'échantillon était plus grande, cela pourrait causer des problèmes à notre modèle, car la plage dynamique des données audio serait très petite et donc difficile à séparer. Appliquons l'extracteur de caractéristiques et voyons à quoi ressemblent les sorties: + +```python +inputs = feature_extractor(sample["array"], sampling_rate=sample["sampling_rate"]) + +print(f"inputs keys: {list(inputs.keys())}") + +print( + f"Mean: {np.mean(inputs['input_values']):.3}, Variance: {np.var(inputs['input_values']):.3}" +) +``` + +**Sortie :** + +```out +inputs keys: ['input_values', 'attention_mask'] +Mean: -4.53e-09, Variance: 1.0 +``` + +Notre extracteur de caractéristiques renvoie un dictionnaire de deux tableaux : `input_values` et `attention_mask`. Les `input_values` sont les entrées audio prétraitées que nous passerons au modèle HuBERT. L’[`attention_mask`](https://huggingface.co/docs/transentraîners/glossary#attention-mask) est utilisé lorsque nous traitons un _batch_ d'entrées audio à la fois. Il est utilisé pour indiquer au modèle où nous avons des entrées rembourrées de différentes longueurs. +Nous pouvons voir que la valeur moyenne est maintenant beaucoup plus proche de 0, et la variance est à 1 ! C'est exactement la forme sous laquelle nous voulons que nos échantillons audio soient avant de les passer dans notre HuBERT. + +> [!WARNING] +> Notez comment nous avons transmis le taux d'échantillonnage de nos données audio à notre extracteur de caractéristiques. Il s'agit d'une bonne pratique, car l'extracteur de caractéristiques effectue une vérification sous le capot pour s'assurer que le taux d'échantillonnage de nos données audio correspond au taux d'échantillonnage attendu par le modèle. Si le taux d'échantillonnage de nos données audio ne correspondait pas au taux d'échantillonnage de notre modèle, nous aurions besoin de suréchantillonner ou de sous-échantillonner les données audio au taux d'échantillonnage correct. + +Alors maintenant que nous savons comment traiter nos fichiers audio rééchantillonnés, la dernière chose à faire est de définir une fonction que nous pouvons appliquer à tous les exemples du jeu de données. Étant donné que nous nous attendons à des échantillons de 30 secondes, nous tronquerons aussi les échantillons plus longs en utilisant les arguments `max_length` et `truncation` de l'extracteur de caractéristiques comme suit : + +```python +max_duration = 30.0 + + +def preprocess_function(examples): + audio_arrays = [x["array"] for x in examples["audio"]] + inputs = feature_extractor( + audio_arrays, + sampling_rate=feature_extractor.sampling_rate, + max_length=int(feature_extractor.sampling_rate * max_duration), + truncation=True, + return_attention_mask=True, + ) + return inputs +``` + +Une fois cette fonction définie, nous pouvons maintenant l'appliquer au jeu de données à l'aide de la méthode `map()`. + +```python +gtzan_encoded = gtzan.map( + preprocess_function, remove_columns=["audio", "file"], batched=True, num_proc=1 +) +gtzan_encoded +``` + +**Sortie :** + +```out +DatasetDict({ + train: Dataset({ + features: ['genre', 'input_values'], + num_rows: 899 + }) + test: Dataset({ + features: ['genre', 'input_values'], + num_rows: 100 + }) +}) +``` + +Pour simplifier l’entraînement, nous avons supprimé les colonnes `audio` et `file` du jeu de données. La colonne `input_values` contient les fichiers audio encodés, la colonne `attention_mask` est un masque binaire de valeurs 0/1 qui indique où nous avons rempli l'entrée audio, et la colonne `genre` contient les étiquettes (ou cibles) correspondantes. Pour permettre au `Trainer` de traiter les étiquettes de classe, nous devons renommer la colonne `genre` en `label` : + +```python +gtzan_encoded = gtzan_encoded.rename_column("genre", "label") +``` + +Enfin, nous devons obtenir les associations d'étiquettes à partir du jeu de données. L’association nous mènera des identifiants entiers (par exemple `7`) aux étiquettes de classe lisibles par l'homme (par exemple `pop`) et inversement. Ce faisant, nous pouvons convertir la prédiction de notre modèle dans un format lisible par l'homme, ce qui nous permet d'utiliser le modèle dans n'importe quelle application en aval. Nous pouvons le faire en utilisant la méthode `int2str()` comme suit: + +```python +id2label = { + str(i): id2label_fn(i) + for i in range(len(gtzan_encoded["train"].features["label"].names)) +} +label2id = {v: k for k, v in id2label.items()} + +id2label["7"] +``` + +**Sortie :** + +```out +'pop' +``` + +Nous avons maintenant un jeu de données prêt pour l’entraînement. Voyons comment nous pouvons entraîner un modèle sur ce jeu de données. + +## Finetuner le modèle + +Pour affiner le modèle, nous utiliserons la classe 'Trainer' de 🤗 *Transformers*. Comme nous l'avons vu dans d'autres chapitres, le « Trainer » est une API de haut niveau conçue pour gérer les scénarios de formation les plus courants. Dans ce cas, nous utiliserons le 'Trainer' pour affiner le modèle sur GTZAN. Pour ce faire, nous devons d'abord charger un modèle pour cette tâche. Nous pouvons le faire en utilisant la classe 'AutoModelForAudioClassification', qui ajoutera automatiquement la tête de classification appropriée à notre modèle DistilHuBERT préentraîné. Allons de l'avant et instancions le modèle : + +'''python +from transformers import AutoModelForAudioClassification + +num_labels = len(id2label) + +modèle = AutoModelForAudioClassification.from_pretrained( + model_id, + num_labels=num_labels, + label2id=label2id, + id2label=id2label, +) +``` + +Nous vous conseillons fortement de télécharger les *checkpoints* du modèle directement sur le [*Hub*](https://huggingface.co/) pendant l'entraînement. +Le *Hub* fournit : +- Un contrôle de version intégré : vous pouvez être sûr qu'aucun *checkpoint* du modèle n'est perdu pendant l’entraînement. +- Tensorboard : suivez les métriques importantes au cours de l’entraînement. +- La carte du modèle : documentant ce que fait un modèle et ses cas d'utilisation prévus. +- Communauté : un moyen facile de partager et de collaborer avec la communauté ! +Lier le *notebook* au *Hub* est simple. Il suffit d'entrer votre *token* d'authentification au *Hub* lorsque vous y êtes invité. +Vous pouvez trouver votre *token* d'authentification [ici](https://huggingface.co/settings/tokens) et le saisir dans + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +**Sortie :** + +```bash +Login successful +Your token has been saved to /root/.huggingface/token +``` + +L'étape suivante consiste à définir les arguments d'apprentissage, y compris la taille du batch, les étapes d'accumulation du gradient, le nombre d'époques d'apprentissage et le taux d'apprentissage : + +```python +from transformers import TrainingArguments + +model_name = model_id.split("/")[-1] +batch_size = 8 +gradient_accumulation_steps = 1 +num_train_epochs = 10 + +training_args = TrainingArguments( + f"{model_name}-finetuned-gtzan", + evaluation_strategy="epoch", + save_strategy="epoch", + learning_rate=5e-5, + per_device_train_batch_size=batch_size, + gradient_accumulation_steps=gradient_accumulation_steps, + per_device_eval_batch_size=batch_size, + num_train_epochs=num_train_epochs, + warmup_ratio=0.1, + logging_steps=5, + load_best_model_at_end=True, + metric_for_best_model="accuracy", + fp16=True, + push_to_hub=True, +) +``` + +> [!WARNING] +> Ici, nous avons défini `push_to_hub=True` pour activer le téléchargement automatique de nos *checkpoints* *finetunés* pendant l'entraînement. Si vous ne souhaitez pas que vos *checkpoints* soient téléchargés sur le *Hub*, vous pouvez définir cette option sur `False`. + +La dernière chose que nous devons faire est de définir les métriques. Étant donné que le jeu de données est équilibré, nous utiliserons la précision comme métrique et le chargerons à l'aide de la bibliothèque 🤗 *Evaluate* : + +```python +import evaluate + +metric = evaluate.load("accuracy") + + +def compute_metrics(eval_pred): + """Computes accuracy on a batch of predictions""" + predictions = np.argmax(eval_pred.predictions, axis=1) + return metric.compute(predictions=predictions, references=eval_pred.label_ids) +``` + +Nous avons maintenant toutes les pièces ! Instancions le `Trainer` et entraînons le modèle : + +```python +from transformers import Trainer + +trainer = Trainer( + model, + training_args, + train_dataset=gtzan_encoded["train"], + eval_dataset=gtzan_encoded["test"], + tokenizer=feature_extractor, + compute_metrics=compute_metrics, +) + +trainer.train() +``` + +> [!WARNING] +> Selon votre GPU, il est possible que vous rencontriez une erreur CUDA `"out-of-memory"` lorsque vous commencez à vous entraîner. +> Dans ce cas, vous pouvez réduire la taille du batch de manière incrémentielle par des facteurs de 2 et utiliser ['gradient_accumulation_steps'](https://huggingface.co/docs/transentraîners/main_classes/trainer#transentraîners.TrainingArguments.gradient_accumulation_steps) pour compenser. + +**Sortie :** + +```out +| Training Loss | Epoch | Step | Validation Loss | Accuracy | +|:-------------:|:-----:|:----:|:---------------:|:--------:| +| 1.7297 | 1.0 | 113 | 1.8011 | 0.44 | +| 1.24 | 2.0 | 226 | 1.3045 | 0.64 | +| 0.9805 | 3.0 | 339 | 0.9888 | 0.7 | +| 0.6853 | 4.0 | 452 | 0.7508 | 0.79 | +| 0.4502 | 5.0 | 565 | 0.6224 | 0.81 | +| 0.3015 | 6.0 | 678 | 0.5411 | 0.83 | +| 0.2244 | 7.0 | 791 | 0.6293 | 0.78 | +| 0.3108 | 8.0 | 904 | 0.5857 | 0.81 | +| 0.1644 | 9.0 | 1017 | 0.5355 | 0.83 | +| 0.1198 | 10.0 | 1130 | 0.5716 | 0.82 | +``` + +L’entraînement durera environ 1 heure en fonction de votre GPU ou de celui alloué par Google Colab. Notre meilleure précision d'évaluation est de 83%. Pas mal pour seulement 10 époques avec 899 exemples d'entraînement ! Nous pourrions certainement améliorer ce résultat en entraînant sur plus d'époques, en utilisant des techniques de régularisation telles que le *dropout*, ou en découpant chaque segment d’audio de 30s en segments de 15s pour utiliser une stratégie de prétraitement de données plus efficace. +La grande question est de savoir comment ce système se compare à d'autres systèmes de classification de musique 🤔 +Pour cela, nous pouvons afficher le [classement *autoevaluate*](https://huggingface.co/spaces/autoevaluate/leaderboards?dataset=marsyas%2Fgtzan&only_verified=0&task=audio-classification&config=all&split=train&metric=accuracy), un classement qui catégorise les modèles par langue et jeu de données, puis les classe en fonction de leur précision. +Nous pouvons automatiquement soumettre notre *checkpoint* au classement lorsque nous transmettons les résultats de l'entraînement au *Hub*. Nous devons simplement définir les arguments appropriés (kwargs). Vous pouvez modifier ces valeurs pour qu'elles correspondent à votre jeu de données, à votre langue et au nom de votre modèle en conséquence : + +```python +kwargs = { + "dataset_tags": "marsyas/gtzan", + "dataset": "GTZAN", + "model_name": f"{model_name}-finetuned-gtzan", + "finetuned_from": model_id, + "tasks": "audio-classification", +} +``` + +Les résultats de l’entraînement peuvent maintenant être téléchargés sur le *Hub*. Pour ce faire, exécutez la commande `.push_to_hub` : + +```python +trainer.push_to_hub(**kwargs) +``` + +Cela enregistrera les logs d'entraînement et les poids du modèle sous `"your-username/distilhubert-finetuned-gtzan"`. Pour cet exemple, consultez [`"sanchit-gandhi/distilhubert-finetuned-gtzan"`](https://huggingface.co/sanchit-gandhi/distilhubert-finetuned-gtzan). + +## Partager le modèle + +Vous pouvez désormais partager votre modèle avec n'importe qui en utilisant le lien sur le *Hub*. Il sera utilisable avec l'identifiant `"your-username/distilhubert-finetuned-gtzan"` directement dans la classe `pipeline()`. Par exemple, pour charger le *checkpoint* *finetuné* ['"sanchit-gandhi/distilhubert-finetuned-gtzan"'](https://huggingface.co/sanchit-gandhi/distilhubert-finetuned-gtzan): + +```python +from transformers import pipeline + +pipe = pipeline( + "audio-classification", model="sanchit-gandhi/distilhubert-finetuned-gtzan" +) +``` + +## Conclusion + +Dans cette section, nous avons couvert un guide étape par étape pour *finetuner* le modèle DistilHuBERT pour la tâche de classification de la musique. Bien que nous nous soyons concentrés sur cette tâche et le jeu de données GTZAN, les étapes présentées ici s'appliquent plus généralement à toute tâche de classification audio. Le même script peut être utilisé pour les tâches de détection de mots-clés ou l'identification de la langue. Il vous suffit de changer le jeu de données avec celui de votre tâche d'intérêt ! Si vous souhaitez *finetuner* d'autres modèles du *Hub* pour la classification d’audio, nous vous encourageons à consulter les [exemples](https://github.com/huggingface/transformers/tree/main/examples/pytorch/audio-classification) trouvables sur le dépôt 🤗 *Transformers*. +Dans la section suivante, nous prendrons le modèle que nous venons de *finetuner* et construirons une démo avec *Gradio* que nous pourrons partager sur le Hub. diff --git a/chapters/fr/chapter5/evaluation.mdx b/chapters/fr/chapter5/evaluation.mdx index 104c9888..cd571a35 100644 --- a/chapters/fr/chapter5/evaluation.mdx +++ b/chapters/fr/chapter5/evaluation.mdx @@ -1,322 +1,320 @@ -# Évaluation et métriques pour la reconnaissance automatique de la parole - -Si vous connaissez la [distance de Levenshtein](https://fr.wikipedia.org/wiki/Distance_de_Levenshtein) en NLP, les métriques d'évaluation des systèmes de reconnaissance vocale vous seront familières ! Ne vous inquiétez pas si ce n'est pas le cas, nous allons passer en revue les explications afin de nous assurer que vous maîtrisez les différentes métriques et que vous comprenez ce qu'elles signifient. - -Lors de l'évaluation des systèmes de reconnaissance vocale, nous comparons les prédictions du système aux transcriptions du texte cible, en annotant toutes les erreurs présentes. Nous classons ces erreurs dans l'une des trois catégories suivantes : -1. Substitutions (Sub) : lorsque nous transcrivons le **mauvais mot** dans notre prédiction (par exemple "sit" au lieu de "sat"). -2. Insertions (Ins) : lorsque nous ajoutons un **mot supplémentaire** dans notre prédiction. -3. Suppressions (Sup) : lorsque nous **supprimons un mot** dans notre prédiction. - -Ces catégories d'erreurs sont les mêmes pour toutes les mesures de reconnaissance vocale. Ce qui diffère, c'est le niveau auquel nous calculons ces erreurs : nous pouvons les calculer soit au niveau du _mot_, soit au niveau du _caractère_. - -Nous utiliserons un exemple courant pour chacune des définitions des mesures. Nous avons ici une séquence de texte de _vérité de base_ ou de _référence_ : - -```python -reference = "the cat sat on the mat" -``` - -Et une séquence prédite par le système de reconnaissance vocale que nous essayons d'évaluer : - -```python -prediction = "the cat sit on the" -``` - -Nous pouvons constater que la prédiction est assez proche, mais que certains mots ne sont pas tout à fait corrects. Nous allons évaluer cette prédiction par rapport à la référence pour les trois mesures de reconnaissance vocale les plus populaires et voir quels types de chiffres nous obtenons pour chacune d'entre elles. - -## *Word Error Rate* (Taux d'erreur au niveau du mot) - -Le *word error rate (WER)* est la mesure de facto pour la reconnaissance vocale. Elle calcule les substitutions, les insertions et les suppressions au niveau du *mot*. Cela signifie que les erreurs sont annotées mot par mot. Prenons notre exemple : - - -| Réference : | the | cat | sat | on | the | mat | -|-------------|-----|-----|---------|-----|-----|-----| -| Prediction : | the | cat | **sit** | on | the | | | -| Étiquette : | ✅ | ✅ | Sub | ✅ | ✅ | Sup | - -Ici, nous avons -* 1 substitution ("sit" au lieu de "sat") -* 0 insertion -* 1 suppression ("mat" est manquant) - -Cela donne 2 erreurs au total. Pour obtenir notre taux d'erreur, nous divisons le nombre d'erreurs par le nombre total de mots de notre référence (N), qui est de 6 pour cet exemple : - -$$ -\begin{aligned} -WER &= \frac{Sub + Ins + Sup}{N} \\ -&= \frac{1 + 0 + 1}{6} \\ -&= 0.333 -\end{aligned} -$$ - -Ok, nous avons donc un WER de 0,333, soit 33,3 %. Remarquez que le mot "sit" ne comporte qu'un seul caractère erroné, mais que le mot entier est marqué comme incorrect. Il s'agit là d'une caractéristique essentielle du WER : les fautes d'orthographe sont lourdement pénalisées, même si elles sont mineures. - -Le WER est défini de telle sorte que *plus il est bas, mieux c'est* : un WER bas signifie qu'il y a moins d'erreurs dans notre prédiction, de sorte qu'un système de reconnaissance vocale parfait aurait un WER de zéro (pas d'erreurs). - -Voyons comment nous pouvons calculer le WER en utilisant 🤗 *Evaluate*. Nous aurons besoin de deux packages pour calculer notre métrique WER : 🤗 *Evaluate* pour l'interface API, et JIWER pour effectuer le gros du travail de calcul : - -``` -pip install --upgrade evaluate jiwer -``` - -Nous pouvons maintenant charger la métrique WER et calculer le résultat pour notre exemple : - -```python -from evaluate import load - -wer_metric = load("wer") - -wer = wer_metric.compute(references=[reference], predictions=[prediction]) - -print(wer) -``` - -**Sortie :** - -``` -0.3333333333333333 -``` - -0,33, soit 33,3 %, comme prévu ! Nous savons maintenant ce qui se passe sous le capot avec ce calcul du WER. - -Maintenant, voici quelque chose qui est assez déroutant... D'après vous, quelle est la limite supérieure du WER ? On pourrait s'attendre à ce qu'elle soit de 1 ou de 100 %, n'est-ce pas ? Le WER étant le rapport entre le nombre d'erreurs et le nombre de mots (N), il n'y a pas de limite supérieure au WER ! -Prenons un exemple dans lequel nous prédisons 10 mots et la cible n'en a que 2. Si toutes nos prédictions étaient fausses (10 erreurs), nous aurions un REE de 10 / 2 = 5, soit 500 % ! Il convient de garder cela à l'esprit si vous entraînez un système ASR et que vous obtenez un WER supérieur à 100 %. Toutefois, si vous obtenez ce résultat, il est probable que quelque chose n'a pas fonctionné... 😅 - -## *Word Accuracy* (Précision au niveau du mot) - -Nous pouvons inverser le WER pour obtenir une mesure où *plus c'est haut, mieux c'est*. Plutôt que de mesurer le taux d'erreurs au niveau du mot, nous pouvons mesurer la *précision au niveau du mot* (WAcc) de notre système : - -$$ -\begin{equation} -WAcc = 1 - WER \nonumber -\end{equation} -$$ - -Le WAcc est mesuré au niveau du mot, il s'agit simplement du WER reformulé en tant que mesure de précision plutôt qu'en tant que mesure d'erreur. Le WAcc est très rarement cité dans la littérature : nous pensons aux prédictions de notre système en termes d'erreurs de mots, et nous préférons donc les mesures de taux d'erreur qui sont plus associées à ces annotations de type d'erreur. - -## *Character Error Rate* (Taux d'erreur au niveau des caractères) - -Il semble un peu injuste que nous ayons marqué l'ensemble du mot "sit" comme erroné alors qu'en fait une seule lettre était incorrecte. -Le *Character Error Rate (CER)* évalue les systèmes au *niveau des caractères*. Cela signifie que nous divisons nos mots en caractères individuels et que nous annotons les erreurs caractère par caractère : - -| Réference : | t | h | e | | c | a | t | | s | a | t | | o | n | | t | h | e | | m | a | t | -|-------------|-----|-----|-----|-----|-----|-----|-----|-----|-----|-------|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----| -| Prediction : | t | h | e | | c | a | t | | s | **i** | t | | o | n | | t | h | e | | | | | -| Étiquette : | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | | ✅ | Sub | ✅ | | ✅ | ✅ | | ✅ | ✅ | ✅ | | D | D | D | - -Nous voyons maintenant que pour le mot "sit", le "s" et le "t" sont marqués comme corrects. Seul le "i" est étiqueté comme une erreur de substitution. Nous récompensons donc notre système pour la prédiction partiellement correcte 🤝 - -Dans notre exemple, nous avons 1 substitution de caractère, 0 insertion et 3 suppressions. Au total, nous avons 14 caractères. Notre CER est donc : - -$$ -\begin{aligned} -CER &= \frac{S + I + D}{N} \\ -&= \frac{1 + 0 + 3}{14} \\ -&= 0.286 -\end{aligned} -$$ - -Nous obtenons un CER de 0,286, soit 28,6 %. Remarquez que ce chiffre est inférieur à notre WER. Nous avons beaucoup moins pénalisé l'erreur d'orthographe. - -## Quelle mesure dois-je utiliser ? - -En général, le WER est beaucoup plus utilisé que le CER pour évaluer les systèmes vocaux. En effet, le WER exige des systèmes une meilleure compréhension du contexte des prédictions. Dans notre exemple, "sit" n'est pas au bon temps. Un système qui comprend la relation entre le verbe et le temps de la phrase aurait prédit le temps correct du verbe "sat". Nous voulons encourager ce niveau de compréhension de la part de nos systèmes vocaux. Ainsi, bien que le WER soit moins indulgent que le CER, il est également plus propice aux types de systèmes intelligibles que nous souhaitons développer. C'est pourquoi nous utilisons généralement le WER et nous vous encourageons à faire de même ! Toutefois, dans certaines circonstances, il n'est pas possible de l'utiliser. -En effet, certaines langues, comme le mandarin et le japonais, n'ont pas de notion de "mots", et le WER n'a donc pas de sens. Dans ce cas, nous revenons à l'utilisation du CER. - -Dans notre exemple, nous n'avons utilisé qu'une seule phrase pour calculer le WER. Lors de l'évaluation d'un système réel, nous utilisons généralement un ensemble de test complet composé de plusieurs milliers de phrases. Lorsque l'évaluation porte sur plusieurs phrases, nous agrégeons Sub, Inv, Sup et N pour toutes les phrases, puis nous calculons le WER à l'aide de la formule définie ci-dessus. Cela permet d'obtenir une meilleure estimation du WER pour des données inédites. - -## Normalisation - -Si nous entraînons un modèle d'ASR sur des données contenant de la ponctuation et de la casse, il apprendra à prédire la casse et la ponctuation dans ses transcriptions. C'est une bonne chose lorsque nous voulons utiliser notre modèle pour des applications réelles, telles que la transcription de réunions ou de dictées, car les transcriptions prédites seront entièrement formatées avec la casse et la ponctuation, un style appelé *orthographique*. - -Cependant, nous avons également la possibilité de *normaliser* le jeu de données afin d'en supprimer la casse et la ponctuation. La normalisation du jeu de données facilite la tâche de reconnaissance vocale : le modèle n'a plus besoin de faire la distinction entre les majuscules et les minuscules, ni de prédire la ponctuation à partir des seules données audio (par exemple, quel est le son d'un point-virgule ?). -De ce fait, les taux d'erreur sur les mots sont naturellement plus faibles (ce qui signifie que les résultats sont meilleurs). L'article de Whisper démontre -l'effet radical que la normalisation des transcriptions peut avoir sur les résultats du WER (*cf.* Section 4.4 du [papier] (https://cdn.openai.com/papers/whisper.pdf)). -Bien que nous obtenions des WER plus bas, le modèle n'est pas nécessairement meilleur pour la production. L'absence de casse et de ponctuation rend le texte prédit par le modèle nettement plus difficile à lire. Prenons l'exemple de la [section précédente](asr_models), où nous avons utilisé Wav2Vec2 et Whisper sur le même échantillon audio du jeu de données LibriSpeech. Le modèle Wav2Vec2 ne prédit ni la ponctuation ni la casse, alors que Whisper prédit les deux. En comparant les transcriptions côte à côte, nous constatons que la transcription de Whisper est beaucoup plus facile à lire : - -``` -Wav2Vec2: HE TELLS US THAT AT THIS FESTIVE SEASON OF THE YEAR WITH CHRISTMAUS AND ROSE BEEF LOOMING BEFORE US SIMALYIS DRAWN FROM EATING AND ITS RESULTS OCCUR MOST READILY TO THE MIND -Whisper: He tells us that at this festive season of the year, with Christmas and roast beef looming before us, similarly is drawn from eating and its results occur most readily to the mind. -``` - -La transcription Whisper est orthographique et donc prête à l'emploi. Au contraire, nous devrions utiliser un post-traitement supplémentaire pour restaurer la ponctuation et la casse dans nos prédictions Wav2Vec2 si nous voulions les utiliser pour des applications en aval. - -Il existe un juste milieu entre normaliser et ne pas normaliser : nous pouvons entraîner nos systèmes sur des transcriptions orthographiques, puis normaliser les prédictions et les cibles avant de calculer le WER. De cette manière, nous entraînons nos systèmes à prédire des textes entièrement formatés, mais nous bénéficions également des améliorations du WER que nous obtenons en normalisant les transcriptions. - -Whisper a été publié avec un normaliseur qui gère efficacement la normalisation de la casse, de la ponctuation et du formatage des nombres, entre autres. Appliquons le aux transcriptions de Whisper pour montrer comment nous pouvons les normaliser : - -```python -from transformers.models.whisper.english_normalizer import BasicTextNormalizer - -normalizer = BasicTextNormalizer() - -prediction = " He tells us that at this festive season of the year, with Christmas and roast beef looming before us, similarly is drawn from eating and its results occur most readily to the mind." -normalized_prediction = normalizer(prediction) - -normalized_prediction -``` - -**Sortie :** - -``` -' he tells us that at this festive season of the year with christmas and roast beef looming before us similarly is drawn from eating and its results occur most readily to the mind ' -``` - -Nous pouvons constater que le texte a été entièrement mis en minuscules et que toute la ponctuation a été supprimée. Définissons maintenant la transcription de référence et calculons le WER normalisé entre la référence et la cible : - -```python -reference = "HE TELLS US THAT AT THIS FESTIVE SEASON OF THE YEAR WITH CHRISTMAS AND ROAST BEEF LOOMING BEFORE US SIMILES DRAWN FROM EATING AND ITS RESULTS OCCUR MOST READILY TO THE MIND" -normalized_referece = normalizer(reference) - -wer = wer_metric.compute( - references=[normalized_referece], predictions=[normalized_prediction] -) -wer -``` - -**Sortie :** - -``` -0.0625 -``` - -6,25 % ; c'est à peu près ce à quoi nous nous attendions pour le modèle de base de Whisper sur l'ensemble de validation de LibriSpeech. Comme nous le voyons ici, nous avons prédit une transcription orthographique, mais nous avons bénéficié de l'augmentation du WER obtenue en normalisant la référence et la prédiction avant de calculer le WER. - -Le choix de la méthode de normalisation des transcriptions dépend en fin de compte de vos besoins. Nous recommandons d'entraîner sur du texte orthographique et d'évaluer sur du texte normalisé afin d'obtenir le meilleur des deux mondes. - -## Assembler le tout - -Nous avons couvert trois sujets jusqu'à présent dans cette unité : les modèles pré-entraînés, la sélection des jeux de données et l'évaluation. -Nous allons nous préparer pour la prochaine section sur le *finetuning* en évaluant le modèle Whisper pré-entraîné sur l'ensemble de test Common Voice 13 Dhivehi. Nous utiliserons le WER pour établir une _base_ pour la prochaine section où notre objectif sera d'essayer de le battre 🥊 - -Tout d'abord, nous allons charger le modèle Whisper pré-entraîné en utilisant la classe `pipeline()`. Ce processus vous est maintenant familier ! -La seule nouveauté est de charger le modèle en demi-précision (float16) s'il est exécuté sur un GPU. Cela permet d'accélérer l'inférence sans que le WER n'en souffre. - -```python -from transformers import pipeline -import torch - -if torch.cuda.is_available(): - device = "cuda:0" - torch_dtype = torch.float16 -else: - device = "cpu" - torch_dtype = torch.float32 - -pipe = pipeline( - "automatic-speech-recognition", - model="openai/whisper-small", - torch_dtype=torch_dtype, - device=device, -) -``` - -Ensuite, nous allons charger l'échantillon test Dhivehi de Common Voice 13. Nous avons vu dans la section précédente que Common Voice 13 est *sécurisé*, ce qui signifie que nous avons dû accepter les conditions d'utilisation du jeu de données avant d'y avoir accès. Nous pouvons maintenant relier notre compte Hugging Face à notre *notebook*, afin d'avoir accès au jeu de données à partir de la machine que nous utilisons actuellement. - -Lier le *notebook* au *Hub* est très simple, il suffit d'entrer votre *token* d'authentification au *Hub* lorsque l'on vous y invite. -Votre *token* d'authentification est trouvable [ici](https://huggingface.co/settings/tokens). - -```python -from huggingface_hub import notebook_login - -notebook_login() -``` - -Une fois que nous avons lié le *notebook* à notre compte Hugging Face, nous pouvons procéder au téléchargement du jeu de données Common Voice. Cela prendra quelques minutes de les télécharger et de les prétraiter automatiquement sur votre *notebook* : - -```python -from datasets import load_dataset - -common_voice_test = load_dataset( - "mozilla-foundation/common_voice_13_0", "dv", split="test" -) -``` - - - Si vous rencontrez un problème d'authentification lors du chargement du jeu de données, assurez-vous que vous avez accepté les conditions d'utilisation du jeu de données sur le *Hub* via le lien suivant : https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0. - - -L'évaluation sur un jeu de données entier peut être faite de la même manière que sur un seul exemple. Tout ce que nous avons à faire est de **boucler** sur les audios d'entrée, plutôt que d'inférer un seul échantillon. Pour ce faire, nous transformons d'abord notre jeu de données en un `KeyDataset`. Cela sélectionne la colonne particulière du jeu de données que nous voulons transmettre au modèle (dans notre cas, c'est la colonne `"audio"`), en ignorant le reste (comme les transcriptions cibles, que nous ne voulons pas utiliser pour l'inférence). Nous itérons ensuite sur ce jeu de données transformé, en ajoutant les sorties du modèle à une liste pour sauvegarder les prédictions. La cellule de code suivante prendra environ cinq minutes si elle est exécutée sur un GPU en demi-précision, avec un maximum de 12 Go de mémoire : - -```python -from tqdm import tqdm -from transformers.pipelines.pt_utils import KeyDataset - -all_predictions = [] - -# lancer l'inférence en streaming -for prediction in tqdm( - pipe( - KeyDataset(common_voice_test, "audio"), - max_new_tokens=128, - generate_kwargs={"task": "transcribe"}, - batch_size=32, - ), - total=len(common_voice_test), -): - all_predictions.append(prediction["text"]) -``` - - -Si vous rencontrez une erreur de mémoire CUDA lors de l'exécution de la cellule ci-dessus, réduisez progressivement la taille du batch par un facteur de 2 jusqu'à ce que vous trouviez une taille de batch qui convienne à votre appareil. - - -Enfin, nous pouvons calculer le WER. Calculons d'abord le WER orthographique, c'est-à-dire le WER sans post-traitement : - -```python -from evaluate import load - -wer_metric = load("wer") - -wer_ortho = 100 * wer_metric.compute( - references=common_voice_test["sentence"], predictions=all_predictions -) -wer_ortho -``` - -**Sortie :** - -``` -167.29577268612022 -``` - -D'accord... 167% signifie essentiellement que notre modèle produit n'imprte quoi 😜 Ne vous inquiétez pas, notre objectif est d'améliorer ce résultat en *fientunant* le modèle sur l'ensemble d'entraînement Dhivehi ! - -Ensuite, nous évaluons le WER normalisé, c'est-à-dire le WER avec post-traitement de normalisation. Nous devons filtrer nos échantillons qui seraient vides après normalisation, car sinon le nombre total de mots dans notre référence (N) serait nul, ce qui donnerait une erreur de division par zéro dans notre calcul : - -```python -from transformers.models.whisper.english_normalizer import BasicTextNormalizer - -normalizer = BasicTextNormalizer() - -# calculer le WER normalisé -all_predictions_norm = [normalizer(pred) for pred in all_predictions] -all_references_norm = [normalizer(label) for label in common_voice_test["sentence"]] - -# étape de filtrage pour n'évaluer que les échantillons qui correspondent à des références non nulles -all_predictions_norm = [ - all_predictions_norm[i] - for i in range(len(all_predictions_norm)) - if len(all_references_norm[i]) > 0 -] -all_references_norm = [ - all_references_norm[i] - for i in range(len(all_references_norm)) - if len(all_references_norm[i]) > 0 -] - -wer = 100 * wer_metric.compute( - references=all_references_norm, predictions=all_predictions_norm -) - -wer -``` - -**Sortie :** - -``` -125.69809089960707 -``` - -Une fois de plus, nous constatons la réduction drastique du WER obtenue en normalisant nos références et nos prédictions : le modèle de base obtient un WER de 168 % pour le test orthographique, alors que le WER normalisé est de 126 %. - -Voilà qui est clair ! Ce sont les chiffres que nous voulons essayer de battre lors du *finetuning* du modèle de reconnaissance de la parole en Dhivehi. Poursuivez votre lecture pour vous familiariser avec un exemple de *finetuning* 🚀 +# Évaluation et métriques pour la reconnaissance automatique de la parole + +Si vous connaissez la [distance de Levenshtein](https://fr.wikipedia.org/wiki/Distance_de_Levenshtein) en NLP, les métriques d'évaluation des systèmes de reconnaissance vocale vous seront familières ! Ne vous inquiétez pas si ce n'est pas le cas, nous allons passer en revue les explications afin de nous assurer que vous maîtrisez les différentes métriques et que vous comprenez ce qu'elles signifient. + +Lors de l'évaluation des systèmes de reconnaissance vocale, nous comparons les prédictions du système aux transcriptions du texte cible, en annotant toutes les erreurs présentes. Nous classons ces erreurs dans l'une des trois catégories suivantes : +1. Substitutions (Sub) : lorsque nous transcrivons le **mauvais mot** dans notre prédiction (par exemple "sit" au lieu de "sat"). +2. Insertions (Ins) : lorsque nous ajoutons un **mot supplémentaire** dans notre prédiction. +3. Suppressions (Sup) : lorsque nous **supprimons un mot** dans notre prédiction. + +Ces catégories d'erreurs sont les mêmes pour toutes les mesures de reconnaissance vocale. Ce qui diffère, c'est le niveau auquel nous calculons ces erreurs : nous pouvons les calculer soit au niveau du _mot_, soit au niveau du _caractère_. + +Nous utiliserons un exemple courant pour chacune des définitions des mesures. Nous avons ici une séquence de texte de _vérité de base_ ou de _référence_ : + +```python +reference = "the cat sat on the mat" +``` + +Et une séquence prédite par le système de reconnaissance vocale que nous essayons d'évaluer : + +```python +prediction = "the cat sit on the" +``` + +Nous pouvons constater que la prédiction est assez proche, mais que certains mots ne sont pas tout à fait corrects. Nous allons évaluer cette prédiction par rapport à la référence pour les trois mesures de reconnaissance vocale les plus populaires et voir quels types de chiffres nous obtenons pour chacune d'entre elles. + +## *Word Error Rate* (Taux d'erreur au niveau du mot) + +Le *word error rate (WER)* est la mesure de facto pour la reconnaissance vocale. Elle calcule les substitutions, les insertions et les suppressions au niveau du *mot*. Cela signifie que les erreurs sont annotées mot par mot. Prenons notre exemple : + + +| Réference : | the | cat | sat | on | the | mat | +|-------------|-----|-----|---------|-----|-----|-----| +| Prediction : | the | cat | **sit** | on | the | | | +| Étiquette : | ✅ | ✅ | Sub | ✅ | ✅ | Sup | + +Ici, nous avons +* 1 substitution ("sit" au lieu de "sat") +* 0 insertion +* 1 suppression ("mat" est manquant) + +Cela donne 2 erreurs au total. Pour obtenir notre taux d'erreur, nous divisons le nombre d'erreurs par le nombre total de mots de notre référence (N), qui est de 6 pour cet exemple : + +$$ +\begin{aligned} +WER &= \frac{Sub + Ins + Sup}{N} \\ +&= \frac{1 + 0 + 1}{6} \\ +&= 0.333 +\end{aligned} +$$ + +Ok, nous avons donc un WER de 0,333, soit 33,3 %. Remarquez que le mot "sit" ne comporte qu'un seul caractère erroné, mais que le mot entier est marqué comme incorrect. Il s'agit là d'une caractéristique essentielle du WER : les fautes d'orthographe sont lourdement pénalisées, même si elles sont mineures. + +Le WER est défini de telle sorte que *plus il est bas, mieux c'est* : un WER bas signifie qu'il y a moins d'erreurs dans notre prédiction, de sorte qu'un système de reconnaissance vocale parfait aurait un WER de zéro (pas d'erreurs). + +Voyons comment nous pouvons calculer le WER en utilisant 🤗 *Evaluate*. Nous aurons besoin de deux packages pour calculer notre métrique WER : 🤗 *Evaluate* pour l'interface API, et JIWER pour effectuer le gros du travail de calcul : + +``` +pip install --upgrade evaluate jiwer +``` + +Nous pouvons maintenant charger la métrique WER et calculer le résultat pour notre exemple : + +```python +from evaluate import load + +wer_metric = load("wer") + +wer = wer_metric.compute(references=[reference], predictions=[prediction]) + +print(wer) +``` + +**Sortie :** + +``` +0.3333333333333333 +``` + +0,33, soit 33,3 %, comme prévu ! Nous savons maintenant ce qui se passe sous le capot avec ce calcul du WER. + +Maintenant, voici quelque chose qui est assez déroutant... D'après vous, quelle est la limite supérieure du WER ? On pourrait s'attendre à ce qu'elle soit de 1 ou de 100 %, n'est-ce pas ? Le WER étant le rapport entre le nombre d'erreurs et le nombre de mots (N), il n'y a pas de limite supérieure au WER ! +Prenons un exemple dans lequel nous prédisons 10 mots et la cible n'en a que 2. Si toutes nos prédictions étaient fausses (10 erreurs), nous aurions un REE de 10 / 2 = 5, soit 500 % ! Il convient de garder cela à l'esprit si vous entraînez un système ASR et que vous obtenez un WER supérieur à 100 %. Toutefois, si vous obtenez ce résultat, il est probable que quelque chose n'a pas fonctionné... 😅 + +## *Word Accuracy* (Précision au niveau du mot) + +Nous pouvons inverser le WER pour obtenir une mesure où *plus c'est haut, mieux c'est*. Plutôt que de mesurer le taux d'erreurs au niveau du mot, nous pouvons mesurer la *précision au niveau du mot* (WAcc) de notre système : + +$$ +\begin{equation} +WAcc = 1 - WER \nonumber +\end{equation} +$$ + +Le WAcc est mesuré au niveau du mot, il s'agit simplement du WER reformulé en tant que mesure de précision plutôt qu'en tant que mesure d'erreur. Le WAcc est très rarement cité dans la littérature : nous pensons aux prédictions de notre système en termes d'erreurs de mots, et nous préférons donc les mesures de taux d'erreur qui sont plus associées à ces annotations de type d'erreur. + +## *Character Error Rate* (Taux d'erreur au niveau des caractères) + +Il semble un peu injuste que nous ayons marqué l'ensemble du mot "sit" comme erroné alors qu'en fait une seule lettre était incorrecte. +Le *Character Error Rate (CER)* évalue les systèmes au *niveau des caractères*. Cela signifie que nous divisons nos mots en caractères individuels et que nous annotons les erreurs caractère par caractère : + +| Réference : | t | h | e | | c | a | t | | s | a | t | | o | n | | t | h | e | | m | a | t | +|-------------|-----|-----|-----|-----|-----|-----|-----|-----|-----|-------|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----| +| Prediction : | t | h | e | | c | a | t | | s | **i** | t | | o | n | | t | h | e | | | | | +| Étiquette : | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | | ✅ | Sub | ✅ | | ✅ | ✅ | | ✅ | ✅ | ✅ | | D | D | D | + +Nous voyons maintenant que pour le mot "sit", le "s" et le "t" sont marqués comme corrects. Seul le "i" est étiqueté comme une erreur de substitution. Nous récompensons donc notre système pour la prédiction partiellement correcte 🤝 + +Dans notre exemple, nous avons 1 substitution de caractère, 0 insertion et 3 suppressions. Au total, nous avons 14 caractères. Notre CER est donc : + +$$ +\begin{aligned} +CER &= \frac{S + I + D}{N} \\ +&= \frac{1 + 0 + 3}{14} \\ +&= 0.286 +\end{aligned} +$$ + +Nous obtenons un CER de 0,286, soit 28,6 %. Remarquez que ce chiffre est inférieur à notre WER. Nous avons beaucoup moins pénalisé l'erreur d'orthographe. + +## Quelle mesure dois-je utiliser ? + +En général, le WER est beaucoup plus utilisé que le CER pour évaluer les systèmes vocaux. En effet, le WER exige des systèmes une meilleure compréhension du contexte des prédictions. Dans notre exemple, "sit" n'est pas au bon temps. Un système qui comprend la relation entre le verbe et le temps de la phrase aurait prédit le temps correct du verbe "sat". Nous voulons encourager ce niveau de compréhension de la part de nos systèmes vocaux. Ainsi, bien que le WER soit moins indulgent que le CER, il est également plus propice aux types de systèmes intelligibles que nous souhaitons développer. C'est pourquoi nous utilisons généralement le WER et nous vous encourageons à faire de même ! Toutefois, dans certaines circonstances, il n'est pas possible de l'utiliser. +En effet, certaines langues, comme le mandarin et le japonais, n'ont pas de notion de "mots", et le WER n'a donc pas de sens. Dans ce cas, nous revenons à l'utilisation du CER. + +Dans notre exemple, nous n'avons utilisé qu'une seule phrase pour calculer le WER. Lors de l'évaluation d'un système réel, nous utilisons généralement un ensemble de test complet composé de plusieurs milliers de phrases. Lorsque l'évaluation porte sur plusieurs phrases, nous agrégeons Sub, Inv, Sup et N pour toutes les phrases, puis nous calculons le WER à l'aide de la formule définie ci-dessus. Cela permet d'obtenir une meilleure estimation du WER pour des données inédites. + +## Normalisation + +Si nous entraînons un modèle d'ASR sur des données contenant de la ponctuation et de la casse, il apprendra à prédire la casse et la ponctuation dans ses transcriptions. C'est une bonne chose lorsque nous voulons utiliser notre modèle pour des applications réelles, telles que la transcription de réunions ou de dictées, car les transcriptions prédites seront entièrement formatées avec la casse et la ponctuation, un style appelé *orthographique*. + +Cependant, nous avons également la possibilité de *normaliser* le jeu de données afin d'en supprimer la casse et la ponctuation. La normalisation du jeu de données facilite la tâche de reconnaissance vocale : le modèle n'a plus besoin de faire la distinction entre les majuscules et les minuscules, ni de prédire la ponctuation à partir des seules données audio (par exemple, quel est le son d'un point-virgule ?). +De ce fait, les taux d'erreur sur les mots sont naturellement plus faibles (ce qui signifie que les résultats sont meilleurs). L'article de Whisper démontre +l'effet radical que la normalisation des transcriptions peut avoir sur les résultats du WER (*cf.* Section 4.4 du [papier] (https://cdn.openai.com/papers/whisper.pdf)). +Bien que nous obtenions des WER plus bas, le modèle n'est pas nécessairement meilleur pour la production. L'absence de casse et de ponctuation rend le texte prédit par le modèle nettement plus difficile à lire. Prenons l'exemple de la [section précédente](asr_models), où nous avons utilisé Wav2Vec2 et Whisper sur le même échantillon audio du jeu de données LibriSpeech. Le modèle Wav2Vec2 ne prédit ni la ponctuation ni la casse, alors que Whisper prédit les deux. En comparant les transcriptions côte à côte, nous constatons que la transcription de Whisper est beaucoup plus facile à lire : + +``` +Wav2Vec2: HE TELLS US THAT AT THIS FESTIVE SEASON OF THE YEAR WITH CHRISTMAUS AND ROSE BEEF LOOMING BEFORE US SIMALYIS DRAWN FROM EATING AND ITS RESULTS OCCUR MOST READILY TO THE MIND +Whisper: He tells us that at this festive season of the year, with Christmas and roast beef looming before us, similarly is drawn from eating and its results occur most readily to the mind. +``` + +La transcription Whisper est orthographique et donc prête à l'emploi. Au contraire, nous devrions utiliser un post-traitement supplémentaire pour restaurer la ponctuation et la casse dans nos prédictions Wav2Vec2 si nous voulions les utiliser pour des applications en aval. + +Il existe un juste milieu entre normaliser et ne pas normaliser : nous pouvons entraîner nos systèmes sur des transcriptions orthographiques, puis normaliser les prédictions et les cibles avant de calculer le WER. De cette manière, nous entraînons nos systèmes à prédire des textes entièrement formatés, mais nous bénéficions également des améliorations du WER que nous obtenons en normalisant les transcriptions. + +Whisper a été publié avec un normaliseur qui gère efficacement la normalisation de la casse, de la ponctuation et du formatage des nombres, entre autres. Appliquons le aux transcriptions de Whisper pour montrer comment nous pouvons les normaliser : + +```python +from transformers.models.whisper.english_normalizer import BasicTextNormalizer + +normalizer = BasicTextNormalizer() + +prediction = " He tells us that at this festive season of the year, with Christmas and roast beef looming before us, similarly is drawn from eating and its results occur most readily to the mind." +normalized_prediction = normalizer(prediction) + +normalized_prediction +``` + +**Sortie :** + +``` +' he tells us that at this festive season of the year with christmas and roast beef looming before us similarly is drawn from eating and its results occur most readily to the mind ' +``` + +Nous pouvons constater que le texte a été entièrement mis en minuscules et que toute la ponctuation a été supprimée. Définissons maintenant la transcription de référence et calculons le WER normalisé entre la référence et la cible : + +```python +reference = "HE TELLS US THAT AT THIS FESTIVE SEASON OF THE YEAR WITH CHRISTMAS AND ROAST BEEF LOOMING BEFORE US SIMILES DRAWN FROM EATING AND ITS RESULTS OCCUR MOST READILY TO THE MIND" +normalized_referece = normalizer(reference) + +wer = wer_metric.compute( + references=[normalized_referece], predictions=[normalized_prediction] +) +wer +``` + +**Sortie :** + +``` +0.0625 +``` + +6,25 % ; c'est à peu près ce à quoi nous nous attendions pour le modèle de base de Whisper sur l'ensemble de validation de LibriSpeech. Comme nous le voyons ici, nous avons prédit une transcription orthographique, mais nous avons bénéficié de l'augmentation du WER obtenue en normalisant la référence et la prédiction avant de calculer le WER. + +Le choix de la méthode de normalisation des transcriptions dépend en fin de compte de vos besoins. Nous recommandons d'entraîner sur du texte orthographique et d'évaluer sur du texte normalisé afin d'obtenir le meilleur des deux mondes. + +## Assembler le tout + +Nous avons couvert trois sujets jusqu'à présent dans cette unité : les modèles pré-entraînés, la sélection des jeux de données et l'évaluation. +Nous allons nous préparer pour la prochaine section sur le *finetuning* en évaluant le modèle Whisper pré-entraîné sur l'ensemble de test Common Voice 13 Dhivehi. Nous utiliserons le WER pour établir une _base_ pour la prochaine section où notre objectif sera d'essayer de le battre 🥊 + +Tout d'abord, nous allons charger le modèle Whisper pré-entraîné en utilisant la classe `pipeline()`. Ce processus vous est maintenant familier ! +La seule nouveauté est de charger le modèle en demi-précision (float16) s'il est exécuté sur un GPU. Cela permet d'accélérer l'inférence sans que le WER n'en souffre. + +```python +from transformers import pipeline +import torch + +if torch.cuda.is_available(): + device = "cuda:0" + torch_dtype = torch.float16 +else: + device = "cpu" + torch_dtype = torch.float32 + +pipe = pipeline( + "automatic-speech-recognition", + model="openai/whisper-small", + torch_dtype=torch_dtype, + device=device, +) +``` + +Ensuite, nous allons charger l'échantillon test Dhivehi de Common Voice 13. Nous avons vu dans la section précédente que Common Voice 13 est *sécurisé*, ce qui signifie que nous avons dû accepter les conditions d'utilisation du jeu de données avant d'y avoir accès. Nous pouvons maintenant relier notre compte Hugging Face à notre *notebook*, afin d'avoir accès au jeu de données à partir de la machine que nous utilisons actuellement. + +Lier le *notebook* au *Hub* est très simple, il suffit d'entrer votre *token* d'authentification au *Hub* lorsque l'on vous y invite. +Votre *token* d'authentification est trouvable [ici](https://huggingface.co/settings/tokens). + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +Une fois que nous avons lié le *notebook* à notre compte Hugging Face, nous pouvons procéder au téléchargement du jeu de données Common Voice. Cela prendra quelques minutes de les télécharger et de les prétraiter automatiquement sur votre *notebook* : + +```python +from datasets import load_dataset + +common_voice_test = load_dataset( + "mozilla-foundation/common_voice_13_0", "dv", split="test" +) +``` + +> [!TIP] +> Si vous rencontrez un problème d'authentification lors du chargement du jeu de données, assurez-vous que vous avez accepté les conditions d'utilisation du jeu de données sur le *Hub* via le lien suivant : https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0. + +L'évaluation sur un jeu de données entier peut être faite de la même manière que sur un seul exemple. Tout ce que nous avons à faire est de **boucler** sur les audios d'entrée, plutôt que d'inférer un seul échantillon. Pour ce faire, nous transformons d'abord notre jeu de données en un `KeyDataset`. Cela sélectionne la colonne particulière du jeu de données que nous voulons transmettre au modèle (dans notre cas, c'est la colonne `"audio"`), en ignorant le reste (comme les transcriptions cibles, que nous ne voulons pas utiliser pour l'inférence). Nous itérons ensuite sur ce jeu de données transformé, en ajoutant les sorties du modèle à une liste pour sauvegarder les prédictions. La cellule de code suivante prendra environ cinq minutes si elle est exécutée sur un GPU en demi-précision, avec un maximum de 12 Go de mémoire : + +```python +from tqdm import tqdm +from transformers.pipelines.pt_utils import KeyDataset + +all_predictions = [] + +# lancer l'inférence en streaming +for prediction in tqdm( + pipe( + KeyDataset(common_voice_test, "audio"), + max_new_tokens=128, + generate_kwargs={"task": "transcribe"}, + batch_size=32, + ), + total=len(common_voice_test), +): + all_predictions.append(prediction["text"]) +``` + +> [!TIP] +> Si vous rencontrez une erreur de mémoire CUDA lors de l'exécution de la cellule ci-dessus, réduisez progressivement la taille du batch par un facteur de 2 jusqu'à ce que vous trouviez une taille de batch qui convienne à votre appareil. + +Enfin, nous pouvons calculer le WER. Calculons d'abord le WER orthographique, c'est-à-dire le WER sans post-traitement : + +```python +from evaluate import load + +wer_metric = load("wer") + +wer_ortho = 100 * wer_metric.compute( + references=common_voice_test["sentence"], predictions=all_predictions +) +wer_ortho +``` + +**Sortie :** + +``` +167.29577268612022 +``` + +D'accord... 167% signifie essentiellement que notre modèle produit n'imprte quoi 😜 Ne vous inquiétez pas, notre objectif est d'améliorer ce résultat en *fientunant* le modèle sur l'ensemble d'entraînement Dhivehi ! + +Ensuite, nous évaluons le WER normalisé, c'est-à-dire le WER avec post-traitement de normalisation. Nous devons filtrer nos échantillons qui seraient vides après normalisation, car sinon le nombre total de mots dans notre référence (N) serait nul, ce qui donnerait une erreur de division par zéro dans notre calcul : + +```python +from transformers.models.whisper.english_normalizer import BasicTextNormalizer + +normalizer = BasicTextNormalizer() + +# calculer le WER normalisé +all_predictions_norm = [normalizer(pred) for pred in all_predictions] +all_references_norm = [normalizer(label) for label in common_voice_test["sentence"]] + +# étape de filtrage pour n'évaluer que les échantillons qui correspondent à des références non nulles +all_predictions_norm = [ + all_predictions_norm[i] + for i in range(len(all_predictions_norm)) + if len(all_references_norm[i]) > 0 +] +all_references_norm = [ + all_references_norm[i] + for i in range(len(all_references_norm)) + if len(all_references_norm[i]) > 0 +] + +wer = 100 * wer_metric.compute( + references=all_references_norm, predictions=all_predictions_norm +) + +wer +``` + +**Sortie :** + +``` +125.69809089960707 +``` + +Une fois de plus, nous constatons la réduction drastique du WER obtenue en normalisant nos références et nos prédictions : le modèle de base obtient un WER de 168 % pour le test orthographique, alors que le WER normalisé est de 126 %. + +Voilà qui est clair ! Ce sont les chiffres que nous voulons essayer de battre lors du *finetuning* du modèle de reconnaissance de la parole en Dhivehi. Poursuivez votre lecture pour vous familiariser avec un exemple de *finetuning* 🚀 diff --git a/chapters/fr/chapter5/fine-tuning.mdx b/chapters/fr/chapter5/fine-tuning.mdx index 4fd3c3b1..a480cab4 100644 --- a/chapters/fr/chapter5/fine-tuning.mdx +++ b/chapters/fr/chapter5/fine-tuning.mdx @@ -1,496 +1,494 @@ -# Comment finetuner un système de reconnaissance automatique de la parole avec l'API Trainer - -Dans cette section, nous allons couvrir un guide étape par étape pour *finetuner* Whisper pour la reconnaissance automatique de la parole sur le jeu de données Common Voice 13. Nous utiliserons la version "*small*" du modèle et un jeu de données relativement léger, vous permettant d'exécuter le *finetuning* assez rapidement sur n'importe quel GPU de plus de 16 Go avec de faibles besoins en espace disque, comme le GPU T4 de 16 Go fourni dans le volet gratuit de Google Colab. - -Si vous disposez d'un GPU plus petit ou si vous rencontrez des problèmes de mémoire pendant l'entraînement, vous pouvez suivre les suggestions fournies pour réduire l'utilisation de la mémoire. À l'inverse, si vous avez accès à un GPU plus puissant, vous pouvez modifier les arguments d'entraînement pour maximiser votre capacité de traitement. Ce guide est donc accessible quelles que soient les spécifications de votre GPU ! - -De même, ce guide porte plus précisément sur le *finetuning* sur la langue Dhivehi mais les étapes couvertes ici se généralisent à n'importe quelle langue du jeu de données Common Voice, et plus généralement à n'importe quel jeu de données d'ASR disponible sur le *Hub*. -Vous pouvez modifier le code pour passer rapidement à la langue de votre choix et *finetuner* un modèle Whisper dans votre langue maternelle 🌍. - -Commençons et donnons le coup d'envoi de notre pipeline de *finetuning* ! - -## Préparer l'environnement - -Nous vous conseillons vivement de télécharger les *checkpoints* du modèle directement sur le [*Hub*](https://huggingface.co/). -Le *Hub* offre les avantages suivants : -- Un contrôle de version intégré : vous pouvez être sûr qu'aucun *checkpoint* n'est perdu pendant l'entraînement. -- Tensorboard : suivi des mesures importantes au cours de l'entraînement. -- Cartes de modèle : documenter ce que fait un modèle et ses cas d'utilisation prévus. -- Communauté : un moyen facile de partager et de collaborer avec la communauté ! 🤗 - -Lier le *notebook* au *Hub* est très simple, il suffit d'entrer votre *token* d'authentification au *Hub* lorsque l'on vous y invite. -Votre *token* d'authentification est trouvable [ici](https://huggingface.co/settings/tokens). - -```python -from huggingface_hub import notebook_login - -notebook_login() -``` - -**Sortie :** - -```bash -Login successful -Your token has been saved to /root/.huggingface/token -``` - -## Charger le jeu de données - -[Common Voice 13](https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0) contient environ dix heures de données Dhivehi étiquetées, dont trois sont des données de test. Il s'agit de très peu de données pour un *finetuning*, nous nous appuierons donc sur la connaissance multilingue acquise par Whisper pendant le pré-entraînement. - -En utilisant les 🤗 *Datasets*, le téléchargement et la préparation des données sont extrêmement simples. Nous pouvons télécharger et préparer les ensembles de Common Voice 13 en une seule ligne de code. Puisque le Dhivehi est très pauvre en ressources, nous combinerons les splits `train` et `validation` pour obtenir environ sept heures de données d'entraînement. Nous utiliserons les trois heures de données `test` comme notre ensemble de test : - -```python -from datasets import load_dataset, DatasetDict - -common_voice = DatasetDict() - -common_voice["train"] = load_dataset( - "mozilla-foundation/common_voice_13_0", "dv", split="train+validation" -) -common_voice["test"] = load_dataset( - "mozilla-foundation/common_voice_13_0", "dv", split="test" -) - -print(common_voice) -``` - -**Sortie :** - -``` -DatasetDict({ - train: Dataset({ - features: ['client_id', 'path', 'audio', 'sentence', 'up_votes', 'down_votes', 'age', 'gender', 'accent', 'locale', 'segment', 'variant'], - num_rows: 4904 - }) - test: Dataset({ - features: ['client_id', 'path', 'audio', 'sentence', 'up_votes', 'down_votes', 'age', 'gender', 'accent', 'locale', 'segment', 'variant'], - num_rows: 2212 - }) -}) -``` - - - Vous pouvez changer l'identifiant de langue de `"dv"` pour un identifiant de langue de votre choix. Pour voir toutes les langues possibles dans Common Voice 13, consultez la carte du jeu de données sur le *Hub* : https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 - - -La plupart des jeux de données d'ASR ne fournissent que des échantillons audio en entrée (`audio`) et le texte transcrit correspondant (`sentence`). -Common Voice contient des métadonnées supplémentaires, telles que `accent` et `locale`, que nous pouvons ignorer pour l'ASR. -En gardant le *notebook* aussi général que possible, nous ne considérons que l'audio d'entrée et le texte transcrit en écartant les informations de métadonnées supplémentaires : - -```python -common_voice = common_voice.select_columns(["audio", "sentence"]) -``` - -## Extracteur de caractéristiques, tokeniser et processeur - -Le pipeline d'ASR peut être décomposé en trois étapes : - -1. L'extracteur de caractéristiques qui pré-traite les entrées audio brutes en spectrogrammes log-mél. -2. Le modèle qui effectue l'association séquence-séquence -3. Le tokenizer qui post-traite les *tokens* prédits en texte. - -Dans 🤗 *Transformers*, le modèle Whisper est associé à un extracteur de caractéristiques et à un tokenizer, appelés respectivement [WhisperFeatureExtractor](https://huggingface.co/docs/transformers/main/model_doc/whisper#transformers.WhisperFeatureExtractor) et [WhisperTokenizer](https://huggingface.co/docs/transformers/main/model_doc/whisper#transformers.WhisperTokenizer). -Pour nous simplifier la vie, ces deux objets sont regroupés dans une seule classe, appelée [WhisperProcessor](https://huggingface.co/docs/transformers/model_doc/whisper#transformers.WhisperProcessor). -Nous pouvons appeler le WhisperProcessor pour effectuer à la fois le prétraitement audio et le post-traitement des *tokens* de texte. Ce faisant, nous n'avons besoin de suivre que deux objets pendant l'entraîningment : le processeur et le modèle. - -Lors d'un *finetuné* multilingue, nous devons définir la `"language"` et la `"task"` lors de l'instanciation du processeur. -La `"language"` doit être fixée à la langue audio source, et la tâche à `"translate"` pour la reconnaissance vocale ou à `"translate"` pour la traduction vocale. Ces arguments modifient le comportement du *tokens*, et doivent être définis correctement pour s'assurer que les étiquettes cibles sont encodées correctement. - -Nous pouvons voir toutes les langues possibles supportées par Whisper en important la liste des langues : - -```python -from transformers.models.whisper.tokenization_whisper import TO_LANGUAGE_CODE - -TO_LANGUAGE_CODE -``` - -Si vous parcourez cette liste, vous remarquerez que de nombreuses langues sont présentes, mais que le dhivehi est l'une des rares à ne pas l'être ! -Cela signifie que Whisper n'a pas été pré-entraîné sur le dhivehi. Cependant, cela ne signifie pas que nous ne pouvons pas *finetuner* Whisper sur cette langue. -En faisant cela, nous allons entraîner Whisper dans une nouvelle langue, une langue non supportée par le *checkpoint* pré-entraîné. C'est plutôt cool, non ? - -Lorsque vous le *finetunez* sur une nouvelle langue, Whisper fait un bon travail en tirant parti de sa connaissance des 96 autres langues sur lesquelles il a été pré-entraîné. En général, toutes les langues modernes seront linguistiquement similaires à au moins l'une des 96 langues que Whisper connaît déjà, nous nous inscrivons donc dans ce paradigme de représentation des connaissances interlinguistiques. - -Ce que nous devons faire pour cette nouvelle langue, est de trouver la langue **la plus similaire** sur laquelle Whisper a été pré-entraîné. L'article de Wikipédia sur le dhivehi indique que cette langue est étroitement liée à la langue cinghalaise du Sri Lanka. -Si nous vérifions à nouveau les codes de langue, nous pouvons voir que le cinghalais est présent dans le jeu de langues de Whisper, nous pouvons donc en toute sécurité mettre notre argument de langue à `"sinhalese"`. - -Nous allons charger notre processeur à partir du *checkpoint* pré-entraîné, en fixant la langue à `"sinhalese"` et la tâche à `"transcribe"` comme expliqué ci-dessus : - -```python -from transformers import WhisperProcessor - -processor = WhisperProcessor.from_pretrained( - "openai/whisper-small", language="sinhalese", task="transcribe" -) -``` - -Il est utile de rappeler que dans la plupart des cas, vous constaterez que la langue sur laquelle vous souhaitez effectuer un *finetuning* se trouve dans l'ensemble des langues de pré-entraînement, auquel cas vous pouvez simplement définir cette langue directement comme langue audio source ! Notez que ces deux arguments doivent être omis pour le *finetuning* en anglais où il n'y a qu'une seule option pour la langue (`"English"`) et la tâche (`"transcribe"`). - -## Prétraitement des données - -Jetons un coup d'oeil aux caractéristiques du jeu de données. Portez une attention particulière à la colonne `"audio"`, elle détaille le taux d'échantillonnage de nos entrées audio : - -```python -common_voice["train"].features -``` - -**Sortie :** - -``` -{'audio': Audio(sampling_rate=48000, mono=True, decode=True, id=None), - 'sentence': Value(dtype='string', id=None)} -``` - -Puisque notre audio d'entrée est échantillonné à 48kHz, nous devons le sous-échantillonner à 16kHz avant de le passer à l'extracteur de caractéristiques de Whisper qui est la fréquence d'échantillonnage attendue par le modèle. - -Nous allons régler les entrées audio à la bonne fréquence d'échantillonnage en utilisant la méthode [`cast_column`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.cast_column) du jeu de données. Cette opération ne modifie pas l'audio sur place, mais signale aux jeux de données de rééchantillonner les échantillons audio à la volée lorsqu'ils sont chargés : - -```python -from datasets import Audio - -sampling_rate = processor.feature_extractor.sampling_rate -common_voice = common_voice.cast_column("audio", Audio(sampling_rate=sampling_rate)) -``` - -Nous pouvons maintenant écrire une fonction pour préparer nos données pour le modèle : - -1. Nous chargeons et rééchantillonnons les données audio échantillon par échantillon en appelant `sample["audio"]`. Comme expliqué ci-dessus, 🤗 *Datasets* effectue toutes les opérations de rééchantillonnage nécessaires à la volée. -2. Nous utilisons l'extracteur de caractéristiques pour calculer les caractéristiques d'entrée du spectrogramme log-mel à partir de notre tableau audio unidimensionnel. -3. Nous encodons les transcriptions en identifiants d'étiquettes à l'aide d'un *tokenizer*. - -```python -def prepare_dataset(example): - audio = example["audio"] - - example = processor( - audio=audio["array"], - sampling_rate=audio["sampling_rate"], - text=example["sentence"], - ) - - # compute input length of audio sample in seconds - example["input_length"] = len(audio["array"]) / audio["sampling_rate"] - - return example -``` - -Nous pouvons appliquer la fonction de préparation des données à tous nos exemples d'entraînement en utilisant la méthode `.map` de 🤗 *Datasets*. Nous allons supprimer les colonnes des données d'entraînement brutes (l'audio et le texte), en ne laissant que les colonnes renvoyées par la fonction `prepare_dataset` : - -```python -common_voice = common_voice.map( - prepare_dataset, remove_columns=common_voice.column_names["train"], num_proc=1 -) -``` - -Enfin, nous filtrons toutes les données d'entraînement contenant des échantillons audio de plus de 30 secondes. Ces échantillons seraient sinon tronqués par l'extracteur de caractéristiques de Whisper, ce qui pourrait affecter la stabilité de l'entraînement. Nous définissons une fonction qui renvoie `True` pour les échantillons de moins de 30 secondes, et `False` pour ceux qui sont plus longs : - -```python -max_input_length = 30.0 - - -def is_audio_in_length_range(length): - return length < max_input_length -``` - -Nous appliquons notre fonction de filtrage à tous les échantillons de notre jeu de données d'entraînement par le biais de la méthode `.filter` de 🤗 *Datasets* : - -```python -common_voice["train"] = common_voice["train"].filter( - is_audio_in_length_range, - input_columns=["input_length"], -) -``` - -Vérifions la quantité de données d'entraînement que nous avons supprimée grâce à cette étape de filtrage : - -```python -common_voice["train"] -``` - -**Sortie :** - -``` -Dataset({ - features: ['input_features', 'labels', 'input_length'], - num_rows: 4904 -}) -``` - -Dans ce cas, nous avons en fait le même nombre d'échantillons que précédemment, donc il n'y a pas d'échantillons de plus de 30s. -Cela pourrait ne pas être le cas si vous changez de langue, il est donc préférable de garder cette étape de filtrage en place pour plus de robustesse. Nos données sont maintenant prêtes à être entraînées ! Continuons et regardons comment nous pouvons utiliser ces données pour le *finetuning*. - -## Entraînement et évaluation - -Maintenant que nous avons préparé nos données, nous sommes prêts à plonger dans le pipeline d'entraînement. -[Trainer](https://huggingface.co/transformers/master/main_classes/trainer.html?highlight=trainer) -va faire le gros du travail à notre place. Tout ce que nous avons à faire est de : - -- Définir un assembleur de données qui prend nos données prétraitées et prépare des tenseurs PyTorch adaptés au modèle. - -- Métriques d'évaluation : lors de l'évaluation, nous voulons évaluer le modèle en utilisant la métrique du taux d'erreur au niveau du mot (WER). Nous devons définir une fonction `compute_metrics` qui gère ce calcul. - -- Charger un *checkpoint* pré-entraîné et le configurer correctement pour l'entraînement. - -- Définir les arguments d'entraînement : ils seront utilisés par le Trainer pour construire le plannificateur d'entraînement. - -Une fois le modèle *finetuné*, nous l'évaluerons sur les données de test pour vérifier que nous l'avons correctement entraîné à transcrire la parole en Dhivehi. - -### Définir un assembleur de données - -L'assembleur de données pour un modèle audio séquence-à-séquence est unique dans le sens où il traite les `input_features` et les `labels` indépendamment : les `input_features` doivent être traitées par l'extracteur de caractéristiques et les `labels` par le tokenizer. - -Les `input_features` sont déjà rembourrées à 30s et converties en un spectrogramme log-Mel de dimension fixe, donc tout ce que nous avons à faire est de les convertir en tenseurs PyTorch batchés. Nous le faisons en utilisant la méthode `.pad` de l'extracteur de caractéristiques avec `return_tensors=pt`. Notez qu'aucun rembourrage supplémentaire n'est appliqué ici puisque les entrées sont de dimension fixe, les `input_features` sont simplement converties en tenseurs PyTorch. - -D'un autre côté, les `labels` ne sont pas rembourrés. Les séquences sont d'abord remplacées par la longueur maximale du batch à l'aide de la méthode `.pad` du *tokenizer*. Les *tokens* de remplissage sont ensuite remplacés par `-100` de sorte que ces tokens ne sont **pas** pris en compte lors du calcul de la perte. Nous coupons ensuite le début du *token* de transcription du début de la séquence d'étiquettes comme nous l'ajouterons plus tard pendant l'entraînement. - -Nous pouvons utiliser le `WhisperProcessor` que nous avons défini plus tôt pour effectuer à la fois les opérations de l'extracteur de caractéristiques et du *tokenizer* : - -```python -import torch - -from dataclasses import dataclass -from typing import Any, Dict, List, Union - - -@dataclass -class DataCollatorSpeechSeq2SeqWithPadding: - processor: Any - - def __call__( - self, features: List[Dict[str, Union[List[int], torch.Tensor]]] - ) -> Dict[str, torch.Tensor]: - # diviser les entrées et les étiquettes car elles doivent être de longueurs différentes et nécessitent des méthodes de remplissage différentes - # traiter d'abord les entrées audio en renvoyant simplement des tenseurs Torch - input_features = [ - {"input_features": feature["input_features"][0]} for feature in features - ] - batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt") - - # obtenir les séquences d'étiquettes tokenisées - label_features = [{"input_ids": feature["labels"]} for feature in features] - # rembourrer les étiquettes à la longueur maximale - labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt") - - # remplacer le remplissage par -100 pour ignorer correctement les pertes - labels = labels_batch["input_ids"].masked_fill( - labels_batch.attention_mask.ne(1), -100 - ) - - # si le token bos est ajouté lors de l'étape de tokenisation précédente, couper le token bos ici puisqu'il sera de toute façon ajouté plus tard - if (labels[:, 0] == self.processor.tokenizer.bos_token_id).all().cpu().item(): - labels = labels[:, 1:] - - batch["labels"] = labels - - return batch -``` - -Nous pouvons maintenant initialiser l'assembleur de données que nous venons de définir : - -```python -data_collator = DataCollatorSpeechSeq2SeqWithPadding(processor=processor) -``` - -En avant ! - -### Métriques d'évaluation - -Ensuite, nous définissons la métrique d'évaluation que nous utiliserons sur notre ensemble d'évaluation. Nous utiliserons le taux d'erreur au niveaud du mot (WER) introduit dans la section [évaluation](évaluation), la métrique "de-facto" pour évaluer les systèmes d'ASR. - -Nous chargerons la métrique WER à partir d' 🤗 *Evaluate* : - -```python -import evaluate - -metric = evaluate.load("wer") -``` - -Il suffit ensuite de définir une fonction qui prend les prédictions de notre modèle et renvoie la métrique WER. Cette fonction, appelée `compute_metrics`, remplace d'abord `-100` par le `pad_token_id` dans `label_ids` (annulant l'étape que nous avons appliquée dans l'assembleur de données pour ignorer correctement les *tokens* rembourrés dans la perte). Il décode ensuite les identifiants prédits et les identifiants d'étiquettes en chaînes de caractères. Enfin, il calcule le WER entre les prédictions et les étiquettes de référence. Ici, nous avons la possibilité d'évaluer les transcriptions et les prédictions "normalisées", dont la ponctuation et la casse ont été supprimées. Nous vous recommandons de procéder ainsi pour bénéficier de l'amélioration du WER obtenue par la normalisation des transcriptions. - -```python -from transformers.models.whisper.english_normalizer import BasicTextNormalizer - -normalizer = BasicTextNormalizer() - - -def compute_metrics(pred): - pred_ids = pred.predictions - label_ids = pred.label_ids - - # remplacer -100 par pad_token_id - label_ids[label_ids == -100] = processor.tokenizer.pad_token_id - - # nous ne voulons pas grouper les *tokens* lors du calcul des métriques - pred_str = processor.batch_decode(pred_ids, skip_special_tokens=True) - label_str = processor.batch_decode(label_ids, skip_special_tokens=True) - - # calculer le Wer orthographique - wer_ortho = 100 * metric.compute(predictions=pred_str, references=label_str) - - # calculer le WER normalisé - pred_str_norm = [normalizer(pred) for pred in pred_str] - label_str_norm = [normalizer(label) for label in label_str] - # afin de n'évaluer que les échantillons correspondant à des références non nulles - pred_str_norm = [ - pred_str_norm[i] for i in range(len(pred_str_norm)) if len(label_str_norm[i]) > 0 - ] - label_str_norm = [ - label_str_norm[i] - for i in range(len(label_str_norm)) - if len(label_str_norm[i]) > 0 - ] - - wer = 100 * metric.compute(predictions=pred_str_norm, references=label_str_norm) - - return {"wer_ortho": wer_ortho, "wer": wer} -``` - -### Charger un *checkpoint* pré-entraîné - -Chargeons maintenant le *checkpoint* pré-entraîné de Whisper small. Encore une fois, ceci est trivial grâce à l'utilisation de 🤗 *Transformers* ! - -```python -from transformers import WhisperForConditionalGeneration - -model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-small") -``` - -Nous allons mettre `use_cache` à `False` pour l'entraînement puisque nous utilisons [*gradient checkpointing*] (https://huggingface.co/docs/transformers/v4.18.0/en/performance#gradient-checkpointing) et que les deux sont incompatibles. Nous allons aussi surcharger deux arguments de génération pour contrôler le comportement du modèle pendant l'inférence : nous allons forcer la langue et les *tokens* de tâche pendant la génération en définissant les arguments `language` et `task`, et aussi réactiver le cache pour la génération afin d'accélérer le temps d'inférence : - -```python -from functools import partial - -# désactiver le cache pendant l'entraînement car il est incompatible avec le checkpointing du gradient -model.config.use_cache = False - -# définir la langue et la tâche pour la génération et réactiver le cache -model.generate = partial( - model.generate, language="sinhalese", task="transcribe", use_cache=True -) -``` - -## Définir la configuration de l'entraînement - -Dans la dernière étape, nous définissons tous les paramètres liés à l'entraînement. Ici, nous fixons le nombre d'étapes d'entraînement à 500. -Cela représente suffisamment d'étapes pour voir une grande amélioration du WER par rapport au modèle pré-entraîné, tout en s'assurant que le *finetuning* peut être exécuté en environ 45 minutes sur un niveau gratuit de Google Colab. Pour plus de détails sur les arguments d'entraînement, reportez-vous à la documentation de [Seq2SeqTrainingArguments] (https://huggingface.co/docs/transformers/main_classes/trainer#transformers.Seq2SeqTrainingArguments). - -```python -from transformers import Seq2SeqTrainingArguments - -training_args = Seq2SeqTrainingArguments( - output_dir="./whisper-small-dv", # nom sur le Hub - per_device_train_batch_size=16, - gradient_accumulation_steps=1, # à x2 pour chaque diminution de 2x de la taille du batch - learning_rate=1e-5, - lr_scheduler_type="constant_with_warmup", - warmup_steps=50, - max_steps=500, # augmenter jusqu'à 4000 si vous disposez de votre propre GPU ou d'un plan Colab payant - gradient_checkpointing=True, - fp16=True, - fp16_full_eval=True, - evaluation_strategy="steps", - per_device_eval_batch_size=16, - predict_with_generate=True, - generation_max_length=225, - save_steps=500, - eval_steps=500, - logging_steps=25, - report_to=["tensorboard"], - load_best_model_at_end=True, - metric_for_best_model="wer", - greater_is_better=False, - push_to_hub=True, -) -``` - - - Si vous ne voulez pas télécharger les *checkpoints* du modèle vers le *Hub*, mettez `push_to_hub=False`. - - -Nous pouvons transmettre les arguments d'entraînement au *Trainer* avec notre modèle, notre jeu de données, notre assembleur de données et la fonction `compute_metrics` : - -```python -from transformers import Seq2SeqTrainer - -trainer = Seq2SeqTrainer( - args=training_args, - model=model, - train_dataset=common_voice["train"], - eval_dataset=common_voice["test"], - data_collator=data_collator, - compute_metrics=compute_metrics, - tokenizer=processor, -) -``` - -Et voilà, nous sommes prêts à entraîner ! - -#### Entraînement - -Pour lancer l'entraînement, il suffit d'exécuter : - -```python -trainer.train() -``` - -L'entraînement prendra environ 45 minutes en fonction de votre GPU ou de celui alloué par Google Colab. En fonction de votre GPU, il est possible que vous rencontriez une erreur CUDA `"out-of-memory"` lorsque vous commencez à entraîner. Dans ce cas, vous pouvez réduire le `per_device_train_batch_size` par incréments d'un facteur 2 et utiliser [`gradient_accumulation_steps`](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.Seq2SeqTrainingArguments.gradient_accumulation_steps) pour compenser. - -**Sortie :** - -| Training Loss | Epoch | Step | Validation Loss | Wer Ortho | Wer | -|:-------------:|:-----:|:----:|:---------------:|:---------:|:-------:| -| 0.136 | 1.63 | 500 | 0.1727 | 63.8972 | 14.0661 | - -Notre WER final est de 14,1 % ce qui n'est pas mal pour sept heures de données d'entraînement et seulement 500 étapes d'entraînement ! Cela représente une amélioration de 112 % par rapport au modèle pré-entraîné ! Cela signifie que nous avons pris un modèle qui n'avait aucune connaissance du dhivehi et que nous l'avons *finetuné* pour reconnaître l'dhivehi avec une précision adéquate en moins d'une heure 🤯. - -La grande question est de savoir comment cela se compare à d'autres systèmes ASR. Pour cela, nous pouvons consulter le [classement autoevaluate] (https://huggingface.co/spaces/autoevaluate/leaderboards?dataset=mozilla-foundation%2Fcommon_voice_13_0&only_verified=0&task=automatic-speech-recognition&config=dv&split=test&metric=wer) qui catégorise les modèles par langue et par jeu de données, et les classe ensuite en fonction de leur WER. - -En regardant le classement, nous voyons que notre modèle entraîné pour 500 étapes bat de manière convaincante le *checkpoint* [Whisper Small](https://huggingface.co/openai/whisper-small) pré-entraîné que nous avons évalué dans la section précédente. Bon travail 👏 - -Nous voyons qu'il y a quelques *checkpoints* qui font mieux que celui que nous avons entraîné. La beauté du *Hub* est qu'il s'agit d'une plateforme *collaborative*. Si nous n'avons pas le temps ou les ressources pour effectuer un entraînement plus long nous-mêmes, nous pouvons charger un *checkpoint* que quelqu'un d'autre dans la communauté a entraîné et a eu la gentillesse de partager (en s'assurant de le remercier pour cela !). -Vous pourrez charger ces *checkpoints* exactement de la même manière que les pré-entraînés en utilisant la classe `pipeline` comme nous l'avons fait précédemment ! Ainsi, rien ne vous empêche de sélectionner le meilleur modèle du leaderboard pour l'utiliser dans le cadre de votre tâche ! - -Nous pouvons automatiquement soumettre notre *checkpoint* au classement lorsque nous envoyons les résultats de l'entraînement au *Hub*. Nous devons simplement définir les arguments de mot-clé appropriés (kwargs). Vous pouvez modifier ces valeurs pour qu'elles correspondent à votre jeu de données, à votre langue et au nom de votre modèle en conséquence : - -```python -kwargs = { - "dataset_tags": "mozilla-foundation/common_voice_13_0", - "dataset": "Common Voice 13", # a 'pretty' name for the training dataset - "language": "dv", - "model_name": "Whisper Small Dv - Sanchit Gandhi", # a 'pretty' name for your model - "finetuned_from": "openai/whisper-small", - "tasks": "automatic-speech-recognition", -} -``` - -Les résultats de l'entraînement peuvent maintenant être téléchargés vers le *Hub*. Pour ce faire, exécutez la commande `push_to_hub` : - -```python -trainer.push_to_hub(**kwargs) -``` - -Ceci sauvegardera les logs d'entraînement et les poids des modèles sous `"votre-nom-d'utilisateur/le-nom-que-vous-avez-choisi"`. Pour cet exemple, regardez le téléchargement à `sanchit-gandhi/whisper-small-dv`. - -Bien que le modèle *finetuné* donne des résultats satisfaisants sur les données de test de Common Voice 13, il n'est en aucun cas optimal. -Le but de ce guide est de démontrer comment *finetuner* un modèle ASR en utilisant le *Trainer* pour la reconnaissance automatique de la parole multilingue. - -Si vous avez accès à votre propre GPU ou si vous êtes abonné à un plan payant de Google Colab, vous pouvez augmenter `max_pas` à 4000 pas pour améliorer davantage le WER en entraînant plus de pas. Entraîner 4000 pas prendra environ 3 à 5 heures en fonction de votre GPU et donnera des résultats WER inférieurs d'environ 3 % à l'entraînement de 500 pas. Si vous décidez d'entraîner sur 4000 pas, nous vous recommandons également de changer le planificateur de taux d'apprentissage pour un plan *linéaire* (set `lr_scheduler_type="linear"`), car cela permettra d'augmenter les performances sur de longues périodes d'entraînement. - -Les résultats pourraient probablement être améliorés en optimisant les hyperparamètres d'entraînement, tels que _learning rate_ et _dropout_, et en utilisant un *checkpoint* pré-entraîné plus grand (`medium` ou `large`). Nous laissons cet exercice au lecteur. - -## Partager votre modèle - -Vous pouvez maintenant partager ce modèle avec n'importe qui en utilisant le lien sur le *Hub*. Ils peuvent le charger avec l'identifiant `"votre-nom-d'utilisateur/le-nom-que-vous-avez-choisi"` directement dans l'objet `pipeline()`. Par exemple, pour charger le *checkpoint* *finetuné* ["sanchit-gandhi/whisper-small-dv"](https://huggingface.co/sanchit-gandhi/whisper-small-dv) : - -```python -from transformers import pipeline - -pipe = pipeline("automatic-speech-recognition", model="sanchit-gandhi/whisper-small-dv") -``` - -## Conclusion - -Dans cette section, nous avons couvert un guide étape par étape sur le *finetuning* du modèle Whisper pour la reconnaissance vocale en utilisants 🤗 *Jeux de données*, 🤗 *Transformers* et le *Hub*. Nous avons d'abord chargé le sous-ensemble Dhivehi du jeu de données Common Voice 13 et l'avons prétraité en calculant des spectrogrammes log-mel et en tokenisant le texte. Nous avons ensuite défini un assembleur de données, une métrique d'évaluation et des arguments d'entraînement, avant d'utiliser le *Trainer* pour entraîner et évaluer notre modèle. Nous avons terminé en téléchargeant le modèle *finetuné* sur le *Hub*, et nous avons montré comment le partager et l'utiliser avec la classe `pipeline()`. - -Si vous avez suivi jusqu'à ce point, vous devriez maintenant avoir un *checkpoint* *finetuné* pour la reconnaissance automatique de la parole, bien joué ! 🥳 -Plus important encore, vous êtes équipé de tous les outils nécessaires pour *finetuner* le modèle Whisper sur n'importe quel jeu de données ou domaine de reconnaissance vocale. Alors, qu'attendez-vous ? Choisissez l'un des jeux de données couverts dans la section [Choisir un jeu de données](choosing_dataset) ou sélectionnez un jeu de données de votre choix, et voyez si vous pouvez obtenir des performances de pointe ! Le classement vous attend... +# Comment finetuner un système de reconnaissance automatique de la parole avec l'API Trainer + +Dans cette section, nous allons couvrir un guide étape par étape pour *finetuner* Whisper pour la reconnaissance automatique de la parole sur le jeu de données Common Voice 13. Nous utiliserons la version "*small*" du modèle et un jeu de données relativement léger, vous permettant d'exécuter le *finetuning* assez rapidement sur n'importe quel GPU de plus de 16 Go avec de faibles besoins en espace disque, comme le GPU T4 de 16 Go fourni dans le volet gratuit de Google Colab. + +Si vous disposez d'un GPU plus petit ou si vous rencontrez des problèmes de mémoire pendant l'entraînement, vous pouvez suivre les suggestions fournies pour réduire l'utilisation de la mémoire. À l'inverse, si vous avez accès à un GPU plus puissant, vous pouvez modifier les arguments d'entraînement pour maximiser votre capacité de traitement. Ce guide est donc accessible quelles que soient les spécifications de votre GPU ! + +De même, ce guide porte plus précisément sur le *finetuning* sur la langue Dhivehi mais les étapes couvertes ici se généralisent à n'importe quelle langue du jeu de données Common Voice, et plus généralement à n'importe quel jeu de données d'ASR disponible sur le *Hub*. +Vous pouvez modifier le code pour passer rapidement à la langue de votre choix et *finetuner* un modèle Whisper dans votre langue maternelle 🌍. + +Commençons et donnons le coup d'envoi de notre pipeline de *finetuning* ! + +## Préparer l'environnement + +Nous vous conseillons vivement de télécharger les *checkpoints* du modèle directement sur le [*Hub*](https://huggingface.co/). +Le *Hub* offre les avantages suivants : +- Un contrôle de version intégré : vous pouvez être sûr qu'aucun *checkpoint* n'est perdu pendant l'entraînement. +- Tensorboard : suivi des mesures importantes au cours de l'entraînement. +- Cartes de modèle : documenter ce que fait un modèle et ses cas d'utilisation prévus. +- Communauté : un moyen facile de partager et de collaborer avec la communauté ! 🤗 + +Lier le *notebook* au *Hub* est très simple, il suffit d'entrer votre *token* d'authentification au *Hub* lorsque l'on vous y invite. +Votre *token* d'authentification est trouvable [ici](https://huggingface.co/settings/tokens). + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +**Sortie :** + +```bash +Login successful +Your token has been saved to /root/.huggingface/token +``` + +## Charger le jeu de données + +[Common Voice 13](https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0) contient environ dix heures de données Dhivehi étiquetées, dont trois sont des données de test. Il s'agit de très peu de données pour un *finetuning*, nous nous appuierons donc sur la connaissance multilingue acquise par Whisper pendant le pré-entraînement. + +En utilisant les 🤗 *Datasets*, le téléchargement et la préparation des données sont extrêmement simples. Nous pouvons télécharger et préparer les ensembles de Common Voice 13 en une seule ligne de code. Puisque le Dhivehi est très pauvre en ressources, nous combinerons les splits `train` et `validation` pour obtenir environ sept heures de données d'entraînement. Nous utiliserons les trois heures de données `test` comme notre ensemble de test : + +```python +from datasets import load_dataset, DatasetDict + +common_voice = DatasetDict() + +common_voice["train"] = load_dataset( + "mozilla-foundation/common_voice_13_0", "dv", split="train+validation" +) +common_voice["test"] = load_dataset( + "mozilla-foundation/common_voice_13_0", "dv", split="test" +) + +print(common_voice) +``` + +**Sortie :** + +``` +DatasetDict({ + train: Dataset({ + features: ['client_id', 'path', 'audio', 'sentence', 'up_votes', 'down_votes', 'age', 'gender', 'accent', 'locale', 'segment', 'variant'], + num_rows: 4904 + }) + test: Dataset({ + features: ['client_id', 'path', 'audio', 'sentence', 'up_votes', 'down_votes', 'age', 'gender', 'accent', 'locale', 'segment', 'variant'], + num_rows: 2212 + }) +}) +``` + +> [!TIP] +> Vous pouvez changer l'identifiant de langue de `"dv"` pour un identifiant de langue de votre choix. Pour voir toutes les langues possibles dans Common Voice 13, consultez la carte du jeu de données sur le *Hub* : https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 + +La plupart des jeux de données d'ASR ne fournissent que des échantillons audio en entrée (`audio`) et le texte transcrit correspondant (`sentence`). +Common Voice contient des métadonnées supplémentaires, telles que `accent` et `locale`, que nous pouvons ignorer pour l'ASR. +En gardant le *notebook* aussi général que possible, nous ne considérons que l'audio d'entrée et le texte transcrit en écartant les informations de métadonnées supplémentaires : + +```python +common_voice = common_voice.select_columns(["audio", "sentence"]) +``` + +## Extracteur de caractéristiques, tokeniser et processeur + +Le pipeline d'ASR peut être décomposé en trois étapes : + +1. L'extracteur de caractéristiques qui pré-traite les entrées audio brutes en spectrogrammes log-mél. +2. Le modèle qui effectue l'association séquence-séquence +3. Le tokenizer qui post-traite les *tokens* prédits en texte. + +Dans 🤗 *Transformers*, le modèle Whisper est associé à un extracteur de caractéristiques et à un tokenizer, appelés respectivement [WhisperFeatureExtractor](https://huggingface.co/docs/transformers/main/model_doc/whisper#transformers.WhisperFeatureExtractor) et [WhisperTokenizer](https://huggingface.co/docs/transformers/main/model_doc/whisper#transformers.WhisperTokenizer). +Pour nous simplifier la vie, ces deux objets sont regroupés dans une seule classe, appelée [WhisperProcessor](https://huggingface.co/docs/transformers/model_doc/whisper#transformers.WhisperProcessor). +Nous pouvons appeler le WhisperProcessor pour effectuer à la fois le prétraitement audio et le post-traitement des *tokens* de texte. Ce faisant, nous n'avons besoin de suivre que deux objets pendant l'entraîningment : le processeur et le modèle. + +Lors d'un *finetuné* multilingue, nous devons définir la `"language"` et la `"task"` lors de l'instanciation du processeur. +La `"language"` doit être fixée à la langue audio source, et la tâche à `"translate"` pour la reconnaissance vocale ou à `"translate"` pour la traduction vocale. Ces arguments modifient le comportement du *tokens*, et doivent être définis correctement pour s'assurer que les étiquettes cibles sont encodées correctement. + +Nous pouvons voir toutes les langues possibles supportées par Whisper en important la liste des langues : + +```python +from transformers.models.whisper.tokenization_whisper import TO_LANGUAGE_CODE + +TO_LANGUAGE_CODE +``` + +Si vous parcourez cette liste, vous remarquerez que de nombreuses langues sont présentes, mais que le dhivehi est l'une des rares à ne pas l'être ! +Cela signifie que Whisper n'a pas été pré-entraîné sur le dhivehi. Cependant, cela ne signifie pas que nous ne pouvons pas *finetuner* Whisper sur cette langue. +En faisant cela, nous allons entraîner Whisper dans une nouvelle langue, une langue non supportée par le *checkpoint* pré-entraîné. C'est plutôt cool, non ? + +Lorsque vous le *finetunez* sur une nouvelle langue, Whisper fait un bon travail en tirant parti de sa connaissance des 96 autres langues sur lesquelles il a été pré-entraîné. En général, toutes les langues modernes seront linguistiquement similaires à au moins l'une des 96 langues que Whisper connaît déjà, nous nous inscrivons donc dans ce paradigme de représentation des connaissances interlinguistiques. + +Ce que nous devons faire pour cette nouvelle langue, est de trouver la langue **la plus similaire** sur laquelle Whisper a été pré-entraîné. L'article de Wikipédia sur le dhivehi indique que cette langue est étroitement liée à la langue cinghalaise du Sri Lanka. +Si nous vérifions à nouveau les codes de langue, nous pouvons voir que le cinghalais est présent dans le jeu de langues de Whisper, nous pouvons donc en toute sécurité mettre notre argument de langue à `"sinhalese"`. + +Nous allons charger notre processeur à partir du *checkpoint* pré-entraîné, en fixant la langue à `"sinhalese"` et la tâche à `"transcribe"` comme expliqué ci-dessus : + +```python +from transformers import WhisperProcessor + +processor = WhisperProcessor.from_pretrained( + "openai/whisper-small", language="sinhalese", task="transcribe" +) +``` + +Il est utile de rappeler que dans la plupart des cas, vous constaterez que la langue sur laquelle vous souhaitez effectuer un *finetuning* se trouve dans l'ensemble des langues de pré-entraînement, auquel cas vous pouvez simplement définir cette langue directement comme langue audio source ! Notez que ces deux arguments doivent être omis pour le *finetuning* en anglais où il n'y a qu'une seule option pour la langue (`"English"`) et la tâche (`"transcribe"`). + +## Prétraitement des données + +Jetons un coup d'oeil aux caractéristiques du jeu de données. Portez une attention particulière à la colonne `"audio"`, elle détaille le taux d'échantillonnage de nos entrées audio : + +```python +common_voice["train"].features +``` + +**Sortie :** + +``` +{'audio': Audio(sampling_rate=48000, mono=True, decode=True, id=None), + 'sentence': Value(dtype='string', id=None)} +``` + +Puisque notre audio d'entrée est échantillonné à 48kHz, nous devons le sous-échantillonner à 16kHz avant de le passer à l'extracteur de caractéristiques de Whisper qui est la fréquence d'échantillonnage attendue par le modèle. + +Nous allons régler les entrées audio à la bonne fréquence d'échantillonnage en utilisant la méthode [`cast_column`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.cast_column) du jeu de données. Cette opération ne modifie pas l'audio sur place, mais signale aux jeux de données de rééchantillonner les échantillons audio à la volée lorsqu'ils sont chargés : + +```python +from datasets import Audio + +sampling_rate = processor.feature_extractor.sampling_rate +common_voice = common_voice.cast_column("audio", Audio(sampling_rate=sampling_rate)) +``` + +Nous pouvons maintenant écrire une fonction pour préparer nos données pour le modèle : + +1. Nous chargeons et rééchantillonnons les données audio échantillon par échantillon en appelant `sample["audio"]`. Comme expliqué ci-dessus, 🤗 *Datasets* effectue toutes les opérations de rééchantillonnage nécessaires à la volée. +2. Nous utilisons l'extracteur de caractéristiques pour calculer les caractéristiques d'entrée du spectrogramme log-mel à partir de notre tableau audio unidimensionnel. +3. Nous encodons les transcriptions en identifiants d'étiquettes à l'aide d'un *tokenizer*. + +```python +def prepare_dataset(example): + audio = example["audio"] + + example = processor( + audio=audio["array"], + sampling_rate=audio["sampling_rate"], + text=example["sentence"], + ) + + # compute input length of audio sample in seconds + example["input_length"] = len(audio["array"]) / audio["sampling_rate"] + + return example +``` + +Nous pouvons appliquer la fonction de préparation des données à tous nos exemples d'entraînement en utilisant la méthode `.map` de 🤗 *Datasets*. Nous allons supprimer les colonnes des données d'entraînement brutes (l'audio et le texte), en ne laissant que les colonnes renvoyées par la fonction `prepare_dataset` : + +```python +common_voice = common_voice.map( + prepare_dataset, remove_columns=common_voice.column_names["train"], num_proc=1 +) +``` + +Enfin, nous filtrons toutes les données d'entraînement contenant des échantillons audio de plus de 30 secondes. Ces échantillons seraient sinon tronqués par l'extracteur de caractéristiques de Whisper, ce qui pourrait affecter la stabilité de l'entraînement. Nous définissons une fonction qui renvoie `True` pour les échantillons de moins de 30 secondes, et `False` pour ceux qui sont plus longs : + +```python +max_input_length = 30.0 + + +def is_audio_in_length_range(length): + return length < max_input_length +``` + +Nous appliquons notre fonction de filtrage à tous les échantillons de notre jeu de données d'entraînement par le biais de la méthode `.filter` de 🤗 *Datasets* : + +```python +common_voice["train"] = common_voice["train"].filter( + is_audio_in_length_range, + input_columns=["input_length"], +) +``` + +Vérifions la quantité de données d'entraînement que nous avons supprimée grâce à cette étape de filtrage : + +```python +common_voice["train"] +``` + +**Sortie :** + +``` +Dataset({ + features: ['input_features', 'labels', 'input_length'], + num_rows: 4904 +}) +``` + +Dans ce cas, nous avons en fait le même nombre d'échantillons que précédemment, donc il n'y a pas d'échantillons de plus de 30s. +Cela pourrait ne pas être le cas si vous changez de langue, il est donc préférable de garder cette étape de filtrage en place pour plus de robustesse. Nos données sont maintenant prêtes à être entraînées ! Continuons et regardons comment nous pouvons utiliser ces données pour le *finetuning*. + +## Entraînement et évaluation + +Maintenant que nous avons préparé nos données, nous sommes prêts à plonger dans le pipeline d'entraînement. +[Trainer](https://huggingface.co/transformers/master/main_classes/trainer.html?highlight=trainer) +va faire le gros du travail à notre place. Tout ce que nous avons à faire est de : + +- Définir un assembleur de données qui prend nos données prétraitées et prépare des tenseurs PyTorch adaptés au modèle. + +- Métriques d'évaluation : lors de l'évaluation, nous voulons évaluer le modèle en utilisant la métrique du taux d'erreur au niveau du mot (WER). Nous devons définir une fonction `compute_metrics` qui gère ce calcul. + +- Charger un *checkpoint* pré-entraîné et le configurer correctement pour l'entraînement. + +- Définir les arguments d'entraînement : ils seront utilisés par le Trainer pour construire le plannificateur d'entraînement. + +Une fois le modèle *finetuné*, nous l'évaluerons sur les données de test pour vérifier que nous l'avons correctement entraîné à transcrire la parole en Dhivehi. + +### Définir un assembleur de données + +L'assembleur de données pour un modèle audio séquence-à-séquence est unique dans le sens où il traite les `input_features` et les `labels` indépendamment : les `input_features` doivent être traitées par l'extracteur de caractéristiques et les `labels` par le tokenizer. + +Les `input_features` sont déjà rembourrées à 30s et converties en un spectrogramme log-Mel de dimension fixe, donc tout ce que nous avons à faire est de les convertir en tenseurs PyTorch batchés. Nous le faisons en utilisant la méthode `.pad` de l'extracteur de caractéristiques avec `return_tensors=pt`. Notez qu'aucun rembourrage supplémentaire n'est appliqué ici puisque les entrées sont de dimension fixe, les `input_features` sont simplement converties en tenseurs PyTorch. + +D'un autre côté, les `labels` ne sont pas rembourrés. Les séquences sont d'abord remplacées par la longueur maximale du batch à l'aide de la méthode `.pad` du *tokenizer*. Les *tokens* de remplissage sont ensuite remplacés par `-100` de sorte que ces tokens ne sont **pas** pris en compte lors du calcul de la perte. Nous coupons ensuite le début du *token* de transcription du début de la séquence d'étiquettes comme nous l'ajouterons plus tard pendant l'entraînement. + +Nous pouvons utiliser le `WhisperProcessor` que nous avons défini plus tôt pour effectuer à la fois les opérations de l'extracteur de caractéristiques et du *tokenizer* : + +```python +import torch + +from dataclasses import dataclass +from typing import Any, Dict, List, Union + + +@dataclass +class DataCollatorSpeechSeq2SeqWithPadding: + processor: Any + + def __call__( + self, features: List[Dict[str, Union[List[int], torch.Tensor]]] + ) -> Dict[str, torch.Tensor]: + # diviser les entrées et les étiquettes car elles doivent être de longueurs différentes et nécessitent des méthodes de remplissage différentes + # traiter d'abord les entrées audio en renvoyant simplement des tenseurs Torch + input_features = [ + {"input_features": feature["input_features"][0]} for feature in features + ] + batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt") + + # obtenir les séquences d'étiquettes tokenisées + label_features = [{"input_ids": feature["labels"]} for feature in features] + # rembourrer les étiquettes à la longueur maximale + labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt") + + # remplacer le remplissage par -100 pour ignorer correctement les pertes + labels = labels_batch["input_ids"].masked_fill( + labels_batch.attention_mask.ne(1), -100 + ) + + # si le token bos est ajouté lors de l'étape de tokenisation précédente, couper le token bos ici puisqu'il sera de toute façon ajouté plus tard + if (labels[:, 0] == self.processor.tokenizer.bos_token_id).all().cpu().item(): + labels = labels[:, 1:] + + batch["labels"] = labels + + return batch +``` + +Nous pouvons maintenant initialiser l'assembleur de données que nous venons de définir : + +```python +data_collator = DataCollatorSpeechSeq2SeqWithPadding(processor=processor) +``` + +En avant ! + +### Métriques d'évaluation + +Ensuite, nous définissons la métrique d'évaluation que nous utiliserons sur notre ensemble d'évaluation. Nous utiliserons le taux d'erreur au niveaud du mot (WER) introduit dans la section [évaluation](évaluation), la métrique "de-facto" pour évaluer les systèmes d'ASR. + +Nous chargerons la métrique WER à partir d' 🤗 *Evaluate* : + +```python +import evaluate + +metric = evaluate.load("wer") +``` + +Il suffit ensuite de définir une fonction qui prend les prédictions de notre modèle et renvoie la métrique WER. Cette fonction, appelée `compute_metrics`, remplace d'abord `-100` par le `pad_token_id` dans `label_ids` (annulant l'étape que nous avons appliquée dans l'assembleur de données pour ignorer correctement les *tokens* rembourrés dans la perte). Il décode ensuite les identifiants prédits et les identifiants d'étiquettes en chaînes de caractères. Enfin, il calcule le WER entre les prédictions et les étiquettes de référence. Ici, nous avons la possibilité d'évaluer les transcriptions et les prédictions "normalisées", dont la ponctuation et la casse ont été supprimées. Nous vous recommandons de procéder ainsi pour bénéficier de l'amélioration du WER obtenue par la normalisation des transcriptions. + +```python +from transformers.models.whisper.english_normalizer import BasicTextNormalizer + +normalizer = BasicTextNormalizer() + + +def compute_metrics(pred): + pred_ids = pred.predictions + label_ids = pred.label_ids + + # remplacer -100 par pad_token_id + label_ids[label_ids == -100] = processor.tokenizer.pad_token_id + + # nous ne voulons pas grouper les *tokens* lors du calcul des métriques + pred_str = processor.batch_decode(pred_ids, skip_special_tokens=True) + label_str = processor.batch_decode(label_ids, skip_special_tokens=True) + + # calculer le Wer orthographique + wer_ortho = 100 * metric.compute(predictions=pred_str, references=label_str) + + # calculer le WER normalisé + pred_str_norm = [normalizer(pred) for pred in pred_str] + label_str_norm = [normalizer(label) for label in label_str] + # afin de n'évaluer que les échantillons correspondant à des références non nulles + pred_str_norm = [ + pred_str_norm[i] for i in range(len(pred_str_norm)) if len(label_str_norm[i]) > 0 + ] + label_str_norm = [ + label_str_norm[i] + for i in range(len(label_str_norm)) + if len(label_str_norm[i]) > 0 + ] + + wer = 100 * metric.compute(predictions=pred_str_norm, references=label_str_norm) + + return {"wer_ortho": wer_ortho, "wer": wer} +``` + +### Charger un *checkpoint* pré-entraîné + +Chargeons maintenant le *checkpoint* pré-entraîné de Whisper small. Encore une fois, ceci est trivial grâce à l'utilisation de 🤗 *Transformers* ! + +```python +from transformers import WhisperForConditionalGeneration + +model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-small") +``` + +Nous allons mettre `use_cache` à `False` pour l'entraînement puisque nous utilisons [*gradient checkpointing*] (https://huggingface.co/docs/transformers/v4.18.0/en/performance#gradient-checkpointing) et que les deux sont incompatibles. Nous allons aussi surcharger deux arguments de génération pour contrôler le comportement du modèle pendant l'inférence : nous allons forcer la langue et les *tokens* de tâche pendant la génération en définissant les arguments `language` et `task`, et aussi réactiver le cache pour la génération afin d'accélérer le temps d'inférence : + +```python +from functools import partial + +# désactiver le cache pendant l'entraînement car il est incompatible avec le checkpointing du gradient +model.config.use_cache = False + +# définir la langue et la tâche pour la génération et réactiver le cache +model.generate = partial( + model.generate, language="sinhalese", task="transcribe", use_cache=True +) +``` + +## Définir la configuration de l'entraînement + +Dans la dernière étape, nous définissons tous les paramètres liés à l'entraînement. Ici, nous fixons le nombre d'étapes d'entraînement à 500. +Cela représente suffisamment d'étapes pour voir une grande amélioration du WER par rapport au modèle pré-entraîné, tout en s'assurant que le *finetuning* peut être exécuté en environ 45 minutes sur un niveau gratuit de Google Colab. Pour plus de détails sur les arguments d'entraînement, reportez-vous à la documentation de [Seq2SeqTrainingArguments] (https://huggingface.co/docs/transformers/main_classes/trainer#transformers.Seq2SeqTrainingArguments). + +```python +from transformers import Seq2SeqTrainingArguments + +training_args = Seq2SeqTrainingArguments( + output_dir="./whisper-small-dv", # nom sur le Hub + per_device_train_batch_size=16, + gradient_accumulation_steps=1, # à x2 pour chaque diminution de 2x de la taille du batch + learning_rate=1e-5, + lr_scheduler_type="constant_with_warmup", + warmup_steps=50, + max_steps=500, # augmenter jusqu'à 4000 si vous disposez de votre propre GPU ou d'un plan Colab payant + gradient_checkpointing=True, + fp16=True, + fp16_full_eval=True, + evaluation_strategy="steps", + per_device_eval_batch_size=16, + predict_with_generate=True, + generation_max_length=225, + save_steps=500, + eval_steps=500, + logging_steps=25, + report_to=["tensorboard"], + load_best_model_at_end=True, + metric_for_best_model="wer", + greater_is_better=False, + push_to_hub=True, +) +``` + +> [!TIP] +> Si vous ne voulez pas télécharger les *checkpoints* du modèle vers le *Hub*, mettez `push_to_hub=False`. + +Nous pouvons transmettre les arguments d'entraînement au *Trainer* avec notre modèle, notre jeu de données, notre assembleur de données et la fonction `compute_metrics` : + +```python +from transformers import Seq2SeqTrainer + +trainer = Seq2SeqTrainer( + args=training_args, + model=model, + train_dataset=common_voice["train"], + eval_dataset=common_voice["test"], + data_collator=data_collator, + compute_metrics=compute_metrics, + tokenizer=processor, +) +``` + +Et voilà, nous sommes prêts à entraîner ! + +#### Entraînement + +Pour lancer l'entraînement, il suffit d'exécuter : + +```python +trainer.train() +``` + +L'entraînement prendra environ 45 minutes en fonction de votre GPU ou de celui alloué par Google Colab. En fonction de votre GPU, il est possible que vous rencontriez une erreur CUDA `"out-of-memory"` lorsque vous commencez à entraîner. Dans ce cas, vous pouvez réduire le `per_device_train_batch_size` par incréments d'un facteur 2 et utiliser [`gradient_accumulation_steps`](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.Seq2SeqTrainingArguments.gradient_accumulation_steps) pour compenser. + +**Sortie :** + +| Training Loss | Epoch | Step | Validation Loss | Wer Ortho | Wer | +|:-------------:|:-----:|:----:|:---------------:|:---------:|:-------:| +| 0.136 | 1.63 | 500 | 0.1727 | 63.8972 | 14.0661 | + +Notre WER final est de 14,1 % ce qui n'est pas mal pour sept heures de données d'entraînement et seulement 500 étapes d'entraînement ! Cela représente une amélioration de 112 % par rapport au modèle pré-entraîné ! Cela signifie que nous avons pris un modèle qui n'avait aucune connaissance du dhivehi et que nous l'avons *finetuné* pour reconnaître l'dhivehi avec une précision adéquate en moins d'une heure 🤯. + +La grande question est de savoir comment cela se compare à d'autres systèmes ASR. Pour cela, nous pouvons consulter le [classement autoevaluate] (https://huggingface.co/spaces/autoevaluate/leaderboards?dataset=mozilla-foundation%2Fcommon_voice_13_0&only_verified=0&task=automatic-speech-recognition&config=dv&split=test&metric=wer) qui catégorise les modèles par langue et par jeu de données, et les classe ensuite en fonction de leur WER. + +En regardant le classement, nous voyons que notre modèle entraîné pour 500 étapes bat de manière convaincante le *checkpoint* [Whisper Small](https://huggingface.co/openai/whisper-small) pré-entraîné que nous avons évalué dans la section précédente. Bon travail 👏 + +Nous voyons qu'il y a quelques *checkpoints* qui font mieux que celui que nous avons entraîné. La beauté du *Hub* est qu'il s'agit d'une plateforme *collaborative*. Si nous n'avons pas le temps ou les ressources pour effectuer un entraînement plus long nous-mêmes, nous pouvons charger un *checkpoint* que quelqu'un d'autre dans la communauté a entraîné et a eu la gentillesse de partager (en s'assurant de le remercier pour cela !). +Vous pourrez charger ces *checkpoints* exactement de la même manière que les pré-entraînés en utilisant la classe `pipeline` comme nous l'avons fait précédemment ! Ainsi, rien ne vous empêche de sélectionner le meilleur modèle du leaderboard pour l'utiliser dans le cadre de votre tâche ! + +Nous pouvons automatiquement soumettre notre *checkpoint* au classement lorsque nous envoyons les résultats de l'entraînement au *Hub*. Nous devons simplement définir les arguments de mot-clé appropriés (kwargs). Vous pouvez modifier ces valeurs pour qu'elles correspondent à votre jeu de données, à votre langue et au nom de votre modèle en conséquence : + +```python +kwargs = { + "dataset_tags": "mozilla-foundation/common_voice_13_0", + "dataset": "Common Voice 13", # a 'pretty' name for the training dataset + "language": "dv", + "model_name": "Whisper Small Dv - Sanchit Gandhi", # a 'pretty' name for your model + "finetuned_from": "openai/whisper-small", + "tasks": "automatic-speech-recognition", +} +``` + +Les résultats de l'entraînement peuvent maintenant être téléchargés vers le *Hub*. Pour ce faire, exécutez la commande `push_to_hub` : + +```python +trainer.push_to_hub(**kwargs) +``` + +Ceci sauvegardera les logs d'entraînement et les poids des modèles sous `"votre-nom-d'utilisateur/le-nom-que-vous-avez-choisi"`. Pour cet exemple, regardez le téléchargement à `sanchit-gandhi/whisper-small-dv`. + +Bien que le modèle *finetuné* donne des résultats satisfaisants sur les données de test de Common Voice 13, il n'est en aucun cas optimal. +Le but de ce guide est de démontrer comment *finetuner* un modèle ASR en utilisant le *Trainer* pour la reconnaissance automatique de la parole multilingue. + +Si vous avez accès à votre propre GPU ou si vous êtes abonné à un plan payant de Google Colab, vous pouvez augmenter `max_pas` à 4000 pas pour améliorer davantage le WER en entraînant plus de pas. Entraîner 4000 pas prendra environ 3 à 5 heures en fonction de votre GPU et donnera des résultats WER inférieurs d'environ 3 % à l'entraînement de 500 pas. Si vous décidez d'entraîner sur 4000 pas, nous vous recommandons également de changer le planificateur de taux d'apprentissage pour un plan *linéaire* (set `lr_scheduler_type="linear"`), car cela permettra d'augmenter les performances sur de longues périodes d'entraînement. + +Les résultats pourraient probablement être améliorés en optimisant les hyperparamètres d'entraînement, tels que _learning rate_ et _dropout_, et en utilisant un *checkpoint* pré-entraîné plus grand (`medium` ou `large`). Nous laissons cet exercice au lecteur. + +## Partager votre modèle + +Vous pouvez maintenant partager ce modèle avec n'importe qui en utilisant le lien sur le *Hub*. Ils peuvent le charger avec l'identifiant `"votre-nom-d'utilisateur/le-nom-que-vous-avez-choisi"` directement dans l'objet `pipeline()`. Par exemple, pour charger le *checkpoint* *finetuné* ["sanchit-gandhi/whisper-small-dv"](https://huggingface.co/sanchit-gandhi/whisper-small-dv) : + +```python +from transformers import pipeline + +pipe = pipeline("automatic-speech-recognition", model="sanchit-gandhi/whisper-small-dv") +``` + +## Conclusion + +Dans cette section, nous avons couvert un guide étape par étape sur le *finetuning* du modèle Whisper pour la reconnaissance vocale en utilisants 🤗 *Jeux de données*, 🤗 *Transformers* et le *Hub*. Nous avons d'abord chargé le sous-ensemble Dhivehi du jeu de données Common Voice 13 et l'avons prétraité en calculant des spectrogrammes log-mel et en tokenisant le texte. Nous avons ensuite défini un assembleur de données, une métrique d'évaluation et des arguments d'entraînement, avant d'utiliser le *Trainer* pour entraîner et évaluer notre modèle. Nous avons terminé en téléchargeant le modèle *finetuné* sur le *Hub*, et nous avons montré comment le partager et l'utiliser avec la classe `pipeline()`. + +Si vous avez suivi jusqu'à ce point, vous devriez maintenant avoir un *checkpoint* *finetuné* pour la reconnaissance automatique de la parole, bien joué ! 🥳 +Plus important encore, vous êtes équipé de tous les outils nécessaires pour *finetuner* le modèle Whisper sur n'importe quel jeu de données ou domaine de reconnaissance vocale. Alors, qu'attendez-vous ? Choisissez l'un des jeux de données couverts dans la section [Choisir un jeu de données](choosing_dataset) ou sélectionnez un jeu de données de votre choix, et voyez si vous pouvez obtenir des performances de pointe ! Le classement vous attend... diff --git a/chapters/fr/chapter6/fine-tuning.mdx b/chapters/fr/chapter6/fine-tuning.mdx index fd38536e..7636f3fe 100644 --- a/chapters/fr/chapter6/fine-tuning.mdx +++ b/chapters/fr/chapter6/fine-tuning.mdx @@ -9,11 +9,8 @@ Assurez-vous de disposer d'un GPU si vous souhaitez reproduire cet exemple. Dans nvidia-smi ``` - - -Dans notre exemple, nous utiliserons environ 40 heures de données d'entraînement. Si vous souhaitez suivre en utilisant la version gratuite de Google Colab, vous devrez réduire la quantité de données d'entraînement à environ 10-15 heures, ainsi que réduire le nombre d'étapes d'entraînement. - - +> [!WARNING] +> Dans notre exemple, nous utiliserons environ 40 heures de données d'entraînement. Si vous souhaitez suivre en utilisant la version gratuite de Google Colab, vous devrez réduire la quantité de données d'entraînement à environ 10-15 heures, ainsi que réduire le nombre d'étapes d'entraînement. Vous aurez également besoin de quelques dépendances supplémentaires : diff --git a/chapters/fr/chapter6/pre-trained_models.mdx b/chapters/fr/chapter6/pre-trained_models.mdx index dc05c88d..119ac28b 100644 --- a/chapters/fr/chapter6/pre-trained_models.mdx +++ b/chapters/fr/chapter6/pre-trained_models.mdx @@ -22,11 +22,8 @@ Après le pré-entraînement, le réseau encodeur-décodeur *finetuné* pour cha Cette approche permet d'obtenir plusieurs modèles *finetunés* pour différentes tâches vocales qui bénéficient tous du pré-entraînement initial sur des données non étiquetées. - - -Même si les modèles *finetunés* commencent par utiliser le même ensemble de poids provenant du modèle pré-entraîné partagé, les versions finales sont toutes très différentes au bout du compte. Vous ne pouvez pas prendre un modèle ASR *finetuné* et échanger les pré- et post-réseaux pour obtenir un modèle de TTS fonctionnel, par exemple. SpeechT5 est flexible, mais pas à ce point ;) - - +> [!TIP] +> Même si les modèles *finetunés* commencent par utiliser le même ensemble de poids provenant du modèle pré-entraîné partagé, les versions finales sont toutes très différentes au bout du compte. Vous ne pouvez pas prendre un modèle ASR *finetuné* et échanger les pré- et post-réseaux pour obtenir un modèle de TTS fonctionnel, par exemple. SpeechT5 est flexible, mais pas à ce point ;) Voyons quels sont les pré- et post-réseaux que SpeechT5 utilise pour la tâche de TTS : @@ -61,17 +58,14 @@ inputs = processor(text="Don't count the days, make the days count.", return_ten Le SpeechT5 TTS n'est pas limité à la création de discours pour un seul locuteur. Il utilise en effet ce que l'on appelle des " enchâssements de locuteurs, qui capturent les caractéristiques de la voix d'un locuteur particulier. - - -L'enchâssement des locuteurs est une méthode permettant de représenter l'identité d'un locuteur de manière compacte, sous la forme d'un vecteur de taille fixe, quelle que soit la longueur de l'énoncé. Ces enchâssements capturent des informations essentielles sur la voix d'un locuteur, son accent, son intonation et d'autres caractéristiques uniques qui distinguent un locuteur d'un autre. De tels enregistrements peuvent être utilisés pour la vérification du locuteur, la diarisation du locuteur, l'identification du locuteur, etc. -Les techniques les plus courantes pour générer des enchâssements de locuteurs sont les suivantes : - -* Les I-Vecteurs (vecteurs d'identité) : ils sont basés sur un modèle de mélange gaussien (GMM). Ils représentent les locuteurs sous la forme de vecteurs de faible dimension et de longueur fixe, dérivés des statistiques d'un GMM spécifique au locuteur, et sont obtenus de manière non supervisée. -* Les vecteurs X : ils sont dérivés à l'aide de réseaux neuronaux profonds (DNN) et capturent des informations sur le locuteur au niveau de l'image en incorporant le contexte temporel. - -[X-Vectors](https://www.danielpovey.com/files/2018_icassp_xvectors.pdf) est une méthode de pointe qui montre des performances supérieures sur des jeux de données d'évaluation par rapport aux I-Vectors. Le réseau neuronal profond est utilisé pour obtenir les X-Vecteurs : il est entraîné à discriminer les locuteurs, et fait correspondre des énoncés de longueur variable à des enchâssements de dimension fixe. Vous pouvez également charger un enchâssement de locuteur X-Vector qui a été calculé à l'avance et qui encapsule les caractéristiques d'élocution d'un locuteur particulier. - - +> [!TIP] +> L'enchâssement des locuteurs est une méthode permettant de représenter l'identité d'un locuteur de manière compacte, sous la forme d'un vecteur de taille fixe, quelle que soit la longueur de l'énoncé. Ces enchâssements capturent des informations essentielles sur la voix d'un locuteur, son accent, son intonation et d'autres caractéristiques uniques qui distinguent un locuteur d'un autre. De tels enregistrements peuvent être utilisés pour la vérification du locuteur, la diarisation du locuteur, l'identification du locuteur, etc. +> Les techniques les plus courantes pour générer des enchâssements de locuteurs sont les suivantes : +> +> * Les I-Vecteurs (vecteurs d'identité) : ils sont basés sur un modèle de mélange gaussien (GMM). Ils représentent les locuteurs sous la forme de vecteurs de faible dimension et de longueur fixe, dérivés des statistiques d'un GMM spécifique au locuteur, et sont obtenus de manière non supervisée. +> * Les vecteurs X : ils sont dérivés à l'aide de réseaux neuronaux profonds (DNN) et capturent des informations sur le locuteur au niveau de l'image en incorporant le contexte temporel. +> +> [X-Vectors](https://www.danielpovey.com/files/2018_icassp_xvectors.pdf) est une méthode de pointe qui montre des performances supérieures sur des jeux de données d'évaluation par rapport aux I-Vectors. Le réseau neuronal profond est utilisé pour obtenir les X-Vecteurs : il est entraîné à discriminer les locuteurs, et fait correspondre des énoncés de longueur variable à des enchâssements de dimension fixe. Vous pouvez également charger un enchâssement de locuteur X-Vector qui a été calculé à l'avance et qui encapsule les caractéristiques d'élocution d'un locuteur particulier. Chargeons un tel enchâssement de locuteur à partir d'un jeu de données sur le *Hub*. Les enchâssements ont été obtenus à partir du [jeu de données CMU ARCTIC](http://www.festvox.org/cmu_arctic/) en utilisant [ce script](https://huggingface.co/mechanicalsea/speecht5-vc/blob/main/manifest/utils/prep_cmu_arctic_spkemb.py), mais n'importe quel enchâssement X-Vector devrait fonctionner. @@ -98,15 +92,12 @@ Il en résulte un tenseur de forme (140, 80) contenant un log mel spectrogramme. Cependant, si nous cherchons à générer une forme d'onde de la parole, nous devons spécifier un vocodeur à utiliser pour la conversion du spectrogramme en forme d'onde. En théorie, vous pouvez utiliser n'importe quel vocodeur qui fonctionne sur des mel spectrogrammes 80-bin. De manière pratique, 🤗 *Transformers* propose un vocodeur basé sur HiFi-GAN. Ses poids ont été aimablement fournis par les auteurs originaux de SpeechT5. - - -[HiFi-GAN](https://arxiv.org/pdf/2010.05646v2.pdf) est un réseau antagoniste génératif (GAN) de pointe conçu pour de la synthèse vocale haute-fidélité. Il est capable de générer des formes d'ondes audio réalistes et de haute qualité à partir de spectrogrammes. - -À un niveau élevé, HiFi-GAN se compose d'un générateur et de deux discriminateurs. Le générateur est un réseau neuronal entièrement convolutionnel qui prend un mel spectrogramme en entrée et apprend à produire des formes d'ondes audio brutes. Les discriminateurs ont pour rôle de faire la distinction entre l'audio réel et l'audio généré. Les deux discriminateurs se concentrent sur des aspects différents de l'audio. - -HiFi-GAN est entraîné sur un grand jeu de données d'enregistrements audio de haute qualité. Il utilise un entraînement dit antagoniste, dans lequel les réseaux du générateur et du discriminateur sont en compétition l'un contre l'autre. Au départ, le générateur produit un son de faible qualité et le discriminateur peut facilement le différencier du son réel. Au fur et à mesure que l'entraînement progresse, le générateur améliore sa sortie, dans le but de tromper le discriminateur. Le discriminateur, à son tour, devient plus précis dans la distinction entre le son réel et le son généré. Cette boucle de rétroaction antagoniste permet aux deux réseaux de s'améliorer au fil du temps. En fin de compte, HiFi-GAN apprend à générer un son de haute fidélité qui ressemble étroitement aux caractéristiques des données entraînées. - - +> [!TIP] +> [HiFi-GAN](https://arxiv.org/pdf/2010.05646v2.pdf) est un réseau antagoniste génératif (GAN) de pointe conçu pour de la synthèse vocale haute-fidélité. Il est capable de générer des formes d'ondes audio réalistes et de haute qualité à partir de spectrogrammes. +> +> À un niveau élevé, HiFi-GAN se compose d'un générateur et de deux discriminateurs. Le générateur est un réseau neuronal entièrement convolutionnel qui prend un mel spectrogramme en entrée et apprend à produire des formes d'ondes audio brutes. Les discriminateurs ont pour rôle de faire la distinction entre l'audio réel et l'audio généré. Les deux discriminateurs se concentrent sur des aspects différents de l'audio. +> +> HiFi-GAN est entraîné sur un grand jeu de données d'enregistrements audio de haute qualité. Il utilise un entraînement dit antagoniste, dans lequel les réseaux du générateur et du discriminateur sont en compétition l'un contre l'autre. Au départ, le générateur produit un son de faible qualité et le discriminateur peut facilement le différencier du son réel. Au fur et à mesure que l'entraînement progresse, le générateur améliore sa sortie, dans le but de tromper le discriminateur. Le discriminateur, à son tour, devient plus précis dans la distinction entre le son réel et le son généré. Cette boucle de rétroaction antagoniste permet aux deux réseaux de s'améliorer au fil du temps. En fin de compte, HiFi-GAN apprend à générer un son de haute fidélité qui ressemble étroitement aux caractéristiques des données entraînées. Le chargement du vocodeur est aussi simple que n'importe quel autre modèle de 🤗 *Transformers*. diff --git a/chapters/fr/chapter7/hands-on.mdx b/chapters/fr/chapter7/hands-on.mdx index b017355e..fb0e145c 100644 --- a/chapters/fr/chapter7/hands-on.mdx +++ b/chapters/fr/chapter7/hands-on.mdx @@ -8,9 +8,8 @@ Des conseils pour mettre à jour la fonction de traduction vocale afin d'effectu Pour synthétiser du texte dans la langue Y vers la parole dans la langue Y, où Y est une langue multilingue, vous devrez utiliser un *checkpoint* TTS multilingue. Pour cela, vous pouvez utiliser soit celui de SpeechT5 TTS que vous avez *finetuné* dans l'exercice pratique précédent, soit un un *checkpoint* TTS multilingue pré-entraîné. Il y a alors deux options pour ce dernier cas : soit le *checkpoint* [sanchit-gandhi/speecht5_tts_vox_nl](https://huggingface.co/sanchit-gandhi/speecht5_tts_vox_nl), qui est un SpeechT5 *finetuné* sur la partie néerlandaise de [VoxPopuli](https://huggingface.co/datasets/facebook/voxpopuli), soit un *checkpoint* MMS TTS (voir la section [modèles pretrainés pour TTS](../chapter6/pre-trained_models)). - -D'après notre expérience avec le néerlandais, l'utilisation d'un *checkpoint* MMS TTS permet d'obtenir de meilleures performances qu'un *checkpoint* SpeechT5 *finetuné* mais il se peut que votre *checkpoint* TTS *fientuné* soit préférable dans votre langue. - +> [!TIP] +> D'après notre expérience avec le néerlandais, l'utilisation d'un *checkpoint* MMS TTS permet d'obtenir de meilleures performances qu'un *checkpoint* SpeechT5 *finetuné* mais il se peut que votre *checkpoint* TTS *fientuné* soit préférable dans votre langue. Votre démo doit prendre en entrée un fichier audio, et retourner en sortie un autre fichier audio, correspondant à la signature de la fonction [`speech_to_speech_translation`](https://huggingface.co/spaces/course-demos/speech-to-speech-translation/blob/3946ba6705a6632a63de8672ac52a482ab74b3fc/app.py#L35). Par conséquent, nous vous recommandons de laisser la fonction principale `speech_to_speech_translation` telle quelle, et de ne mettre à jour les fonctions [`translate`](https://huggingface.co/spaces/course-demos/speech-to-speech-translation/blob/a03175878f522df7445290d5508bfb5c5178f787/app.py#L24) et [`synthesise`](https://huggingface.co/spaces/course-demos/speech-to-speech-translation/blob/a03175878f522df7445290d5508bfb5c5178f787/app.py#L29) que si nécessaire. diff --git a/chapters/fr/chapter7/speech-to-speech.mdx b/chapters/fr/chapter7/speech-to-speech.mdx index bb84ff74..a5755871 100644 --- a/chapters/fr/chapter7/speech-to-speech.mdx +++ b/chapters/fr/chapter7/speech-to-speech.mdx @@ -65,11 +65,8 @@ def translate(audio): return outputs["text"] ``` - - -Whisper peut aussi être "trompé" pour traduire de la parole dans n'importe quelle langue X vers n'importe quelle langue Y. Il suffit de mettre la tâche à `"transcribe"` et `"language"` à votre langue cible en argument. Par exemple pour l'espagnol, on mettrait : `generate_kwargs={"task" : "transcribe", "language" : "es"}` - - +> [!TIP] +> Whisper peut aussi être "trompé" pour traduire de la parole dans n'importe quelle langue X vers n'importe quelle langue Y. Il suffit de mettre la tâche à `"transcribe"` et `"language"` à votre langue cible en argument. Par exemple pour l'espagnol, on mettrait : `generate_kwargs={"task" : "transcribe", "language" : "es"}` Bien, vérifions rapidement que nous obtenons un résultat raisonnable à partir du modèle : @@ -110,9 +107,8 @@ model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts") vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan") ``` - - Nous utilisons ici le *checkpoint* SpeechT5 entraîné spécifiquement pour le TTS anglais. Si vous souhaitez traduire dans une autre langue que l'anglais, remplacez le *checkpoint* par un modèle SpeechT5 TTS *finetuné* dans la langue de votre choix, ou utilisez un *checkpoint* MMS TTS pré-entraîné dans la langue cible. - +> [!TIP] +> Nous utilisons ici le *checkpoint* SpeechT5 entraîné spécifiquement pour le TTS anglais. Si vous souhaitez traduire dans une autre langue que l'anglais, remplacez le *checkpoint* par un modèle SpeechT5 TTS *finetuné* dans la langue de votre choix, ou utilisez un *checkpoint* MMS TTS pré-entraîné dans la langue cible. Comme pour le Whisper, nous placerons le SpeechT5 et le vocodeur sur notre GPU, si nous en avons un : diff --git a/chapters/fr/chapter7/transcribe-meeting.mdx b/chapters/fr/chapter7/transcribe-meeting.mdx index ae348be1..3bbf144a 100644 --- a/chapters/fr/chapter7/transcribe-meeting.mdx +++ b/chapters/fr/chapter7/transcribe-meeting.mdx @@ -141,9 +141,8 @@ pipeline = ASRDiarizationPipeline( ) ``` - - Vous pouvez également instancier le `ASRDiarizationPipeline` directement à partir d'un modèle pré-entraîné en spécifiant l'identifiant d'un modèle ASR sur le *Hub* : `pipeline = ASRDiarizationPipeline.from_pretrained("openai/whisper-base")` - +> [!TIP] +> Vous pouvez également instancier le `ASRDiarizationPipeline` directement à partir d'un modèle pré-entraîné en spécifiant l'identifiant d'un modèle ASR sur le *Hub* : `pipeline = ASRDiarizationPipeline.from_pretrained("openai/whisper-base")` Passons le fichier audio au pipeline et voyons ce que nous obtenons : diff --git a/chapters/fr/chapter7/voice-assistant.mdx b/chapters/fr/chapter7/voice-assistant.mdx index f8223950..2979b187 100644 --- a/chapters/fr/chapter7/voice-assistant.mdx +++ b/chapters/fr/chapter7/voice-assistant.mdx @@ -162,9 +162,8 @@ transcriber = pipeline( ) ``` - - Si vous utilisez un GPU, vous pouvez augmenter la taille du modèle à utiliser, ce qui permettra d'obtenir une meilleure précision de transcription tout en respectant le seuil de latence requis. Il suffit de remplacer l'identifiant du modèle par : "openai/whisper-small.en". - +> [!TIP] +> Si vous utilisez un GPU, vous pouvez augmenter la taille du modèle à utiliser, ce qui permettra d'obtenir une meilleure précision de transcription tout en respectant le seuil de latence requis. Il suffit de remplacer l'identifiant du modèle par : "openai/whisper-small.en". Nous pouvons maintenant définir une fonction pour enregistrer l'entrée de notre microphone et transcrire le texte correspondant. Avec la fonction d'aide `ffmpeg_microphone_live`, nous pouvons contrôler le degré de "temps réel" de notre modèle de reconnaissance vocale. L'utilisation d'un `stream_chunk_s` plus petit se prête à une reconnaissance vocale davantage en temps réel, puisque nous divisons notre audio d'entrée en plus petits morceaux et que nous les transcrivons à la volée. Cependant, cela se fait au détriment de la précision, puisqu'il y a moins de contexte pour le modèle à déduire. @@ -345,9 +344,8 @@ agent.run("Generate an image of a cat") - - Notez que la première fois que vous appelez cette fonction, les poids du modèle sont téléchargés, ce qui peut prendre un certain temps en fonction de votre connexion internet. - +> [!TIP] +> Notez que la première fois que vous appelez cette fonction, les poids du modèle sont téléchargés, ce qui peut prendre un certain temps en fonction de votre connexion internet. C'est aussi simple que cela ! L'agent a interprété notre prompt et a utilisé [Stable Diffusion](https://huggingface.co/docs/diffusers/using-diffusers/conditional_image_generation) pour générer l'image, sans que nous ayons à nous soucier du chargement du modèle, de l'écriture de la fonction ou de l'exécution du code. diff --git a/chapters/ko/chapter1/audio_data.mdx b/chapters/ko/chapter1/audio_data.mdx index 22a14bd5..2bf89e0c 100644 --- a/chapters/ko/chapter1/audio_data.mdx +++ b/chapters/ko/chapter1/audio_data.mdx @@ -128,8 +128,8 @@ DFT의 결과값은 복소수 배열입니다. `np.abs(dft)`로 그 크기를 `librosa.amplitude_to_db()`는 진폭값을 데시벨 스케일로 변환합니다. 이로 인해 스펙트럼의 더욱 세밀한 부분까지 쉽게 확인이 가능합니다. 때때로는 **파워 스펙트럼(power spectrum)**을 쓸 때도 있습니다. 진폭보다 에너지를 측정하기 위해 쓰는데, 이는 단지 진폭에 제곱을 취한 값으로 나타낸 스펙트럼입니다. - -💡 실무에서 사람들이 고속 푸리에 변환(FFT)이라는 용어와 DFT를 혼용해서 쓰는 경우를 볼 수 있을겁니다. 이는 DFT를 계산하는 유일한 효율적인 방법이 FFT를 사용하는것이기 때문입니다. +> [!TIP] +> 💡 실무에서 사람들이 고속 푸리에 변환(FFT)이라는 용어와 DFT를 혼용해서 쓰는 경우를 볼 수 있을겁니다. 이는 DFT를 계산하는 유일한 효율적인 방법이 FFT를 사용하는것이기 때문입니다. 오디오 신호의 파형과 주파수 스펙트럼은 동일한 정보를 지닙니다. 단지 같은 데이터(여기서는 트럼펫 소리의 첫 4096개의 샘플)를 바라보는 두 가지 방법일 뿐입니다. 파형은 시간에 따른 오디오 신호의 진폭을 표시하며, 스펙트럼은 고정된 시점의 개별 주파수들의 진폭을 시각화합니다. @@ -200,9 +200,8 @@ plt.colorbar() 일반적인 스펙트로그램과 마찬가지로 멜 스펙트로그램의 주파수 성분 역시 세기를 데시벨로 표현하는 것이 일반적입니다. 데시벨로의 변환이 로그 연산을 포함하기 때문에 이를 흔히 **로그-멜 스펙트로그램(log-mel spectrogram)**이라 합니다. 위 예제에선 `librosa.power_to_db()`를 썻는데, 이는 `librosa.feature.melspectrogram()`는 파워 스펙트로그램(power spectrogram)을 만들기 때문입니다. - -💡 모든 멜 스펙트로그램이 같은 것은 아닙니다! 일반적으로 사용되는 멜 스케일에는 두가지가 있으며("htk"와 "slaney"), 파워 스펙트로그램 대신 진폭 스펙트로그램(amplitude spectrogram)이 사용될 수도 있습니다. 로그-멜 스펙트로그램으로의 변환은 항상 실제 데시벨을 계산하는 것이 아니며 단순히 로그를 취할 수도 있습니다. 따라서, 머신러닝 모델이 멜 스펙트로그램을 입력으로 받는다면, 같은 방식으로 계산하고 있는지 다시 한번 확인하시길 바랍니다. - +> [!TIP] +> 💡 모든 멜 스펙트로그램이 같은 것은 아닙니다! 일반적으로 사용되는 멜 스케일에는 두가지가 있으며("htk"와 "slaney"), 파워 스펙트로그램 대신 진폭 스펙트로그램(amplitude spectrogram)이 사용될 수도 있습니다. 로그-멜 스펙트로그램으로의 변환은 항상 실제 데시벨을 계산하는 것이 아니며 단순히 로그를 취할 수도 있습니다. 따라서, 머신러닝 모델이 멜 스펙트로그램을 입력으로 받는다면, 같은 방식으로 계산하고 있는지 다시 한번 확인하시길 바랍니다. 멜 스펙트로그램은 신호를 필터링하여 만들기 때문에 정보의 손실이 일어납니다. 따라서 멜 스펙트로그램을 다시 원래의 파형으로 바꾸는 것은 일반적인 스펙트로그램을 다시 되돌리는것보다 힘든 일입니다. 버려진 주파수를 어떻게든 추정해야 하기 때문이죠. 멜 스펙트로그램을 다시 원래의 파형으로 되돌리기 위해 HiFiGAN vocoder같은 머신러닝 모델이 필요한 이유이기도 합니다. diff --git a/chapters/ko/chapter1/preprocessing.mdx b/chapters/ko/chapter1/preprocessing.mdx index 7293b12b..ddab3abd 100644 --- a/chapters/ko/chapter1/preprocessing.mdx +++ b/chapters/ko/chapter1/preprocessing.mdx @@ -54,9 +54,8 @@ minds[0] 여러분은 아마 배열의 값들 역시 달라졌음을 눈치채셨을 겁니다. 이는 기존에 비해 진폭값들의 갯수가 전부 두배로 늘어났기 때문입니다. - -💡 리샘플링에 대한 배경 정보: 만약 오디오 신호가 8 kHz로 샘플링 되었다면(즉, 초당 8000개의 샘플이 있다면) 4 kHz보다 높은 주파수는 없음을 알 수 있습니다. 나이퀴스트 샘플링 정리(Nyquist sampling theorem)에 의해서 말이죠. 이 덕분에 우린 샘플링 지점들간의 원래의 연속적인 신호는 항상 부드러운 커브임을 확신할 수 있는 것입니다. 더 높은 샘플링 속도로의 업샘플링은 이 커브를 근사하여 기존 점들 사이의 값을 찾아내면 됩니다. 그러나 다운샘플링 같은 경우, 새로운 샘플을 결정하기전에 새로운 나이퀴스트 한계보다 높은 주파수를 먼저 걸러내는 작업이 필요할 겁니다. 다시 말해, 2배의 다운샘플링 같은 경우 이에 맞춰 단순히 샘플들을 버리는 것으로는 왜곡이 생길 수 있습니다. 이 왜곡을 alias라고 합니다. 이렇듯 리샘플링을 올바르게 하기란 꽤 까다로우므로 librosa나 🤗 Datasets같은 잘 테스트된 라이브러리를 쓰는편이 낫습니다. - +> [!TIP] +> 💡 리샘플링에 대한 배경 정보: 만약 오디오 신호가 8 kHz로 샘플링 되었다면(즉, 초당 8000개의 샘플이 있다면) 4 kHz보다 높은 주파수는 없음을 알 수 있습니다. 나이퀴스트 샘플링 정리(Nyquist sampling theorem)에 의해서 말이죠. 이 덕분에 우린 샘플링 지점들간의 원래의 연속적인 신호는 항상 부드러운 커브임을 확신할 수 있는 것입니다. 더 높은 샘플링 속도로의 업샘플링은 이 커브를 근사하여 기존 점들 사이의 값을 찾아내면 됩니다. 그러나 다운샘플링 같은 경우, 새로운 샘플을 결정하기전에 새로운 나이퀴스트 한계보다 높은 주파수를 먼저 걸러내는 작업이 필요할 겁니다. 다시 말해, 2배의 다운샘플링 같은 경우 이에 맞춰 단순히 샘플들을 버리는 것으로는 왜곡이 생길 수 있습니다. 이 왜곡을 alias라고 합니다. 이렇듯 리샘플링을 올바르게 하기란 꽤 까다로우므로 librosa나 🤗 Datasets같은 잘 테스트된 라이브러리를 쓰는편이 낫습니다. ## 데이터셋 필터링하기[[filtering-the-dataset]] diff --git a/chapters/ko/chapter3/classification.mdx b/chapters/ko/chapter3/classification.mdx index 38f39e5f..980f6d3f 100644 --- a/chapters/ko/chapter3/classification.mdx +++ b/chapters/ko/chapter3/classification.mdx @@ -18,9 +18,8 @@ ViT와 마찬가지로 AST(Audio Spectrogram Transformer) 모델은 오디오 논문 [AST: 오디오 스펙트로그램 트랜스포머](https://arxiv.org/pdf/2104.01778.pdf)에서 가져온 이미지 - -💡 여기서는 스펙트로그램이 이미지와 동일하다고 가정하지만, 중요한 차이점이 있습니다. 예를 들어, 이미지의 내용을 위아래로 이동해도 일반적으로 이미지에 포함된 내용의 의미는 변하지 않습니다. 그러나 스펙트로그램을 위아래로 이동하면 소리에 포함된 주파수가 변경되어 소리의 성격이 완전히 달라집니다. 이미지는 변환 시에도 변하지 않지만 스펙트로그램은 그렇지 않습니다. 스펙트로그램을 이미지로 취급하는 것은 실제로는 매우 잘 작동할 수 있지만 실제로는 같은 것이 아니라는 점을 명심하세요. - +> [!TIP] +> 💡 여기서는 스펙트로그램이 이미지와 동일하다고 가정하지만, 중요한 차이점이 있습니다. 예를 들어, 이미지의 내용을 위아래로 이동해도 일반적으로 이미지에 포함된 내용의 의미는 변하지 않습니다. 그러나 스펙트로그램을 위아래로 이동하면 소리에 포함된 주파수가 변경되어 소리의 성격이 완전히 달라집니다. 이미지는 변환 시에도 변하지 않지만 스펙트로그램은 그렇지 않습니다. 스펙트로그램을 이미지로 취급하는 것은 실제로는 매우 잘 작동할 수 있지만 실제로는 같은 것이 아니라는 점을 명심하세요. ## 모든 트랜스포머는 분류기가 될 수 있습니다.[[any-transformer-can-be-a-classifier]] diff --git a/chapters/ko/chapter3/ctc.mdx b/chapters/ko/chapter3/ctc.mdx index ddd4867b..e52768f8 100644 --- a/chapters/ko/chapter3/ctc.mdx +++ b/chapters/ko/chapter3/ctc.mdx @@ -14,9 +14,8 @@ CTC 모델을 사용하면 은닉 상태 시퀀스에 추가 선형 매핑을 음성에서는 오디오 입력과 텍스트 출력의 '정렬(alignment)'을 알 수 없다는 점이 문제입니다. 음성이 말하는 순서와 텍스트를 필사(transcribe)하는 순서가 같다는 것은 알지만(소위 단조로운 정렬의 경우), 필사하는 텍스트의 문자가 오디오와 어떻게 일치하는지는 알 수 없습니다. 바로 이 부분에서 CTC 알고리즘이 등장합니다. - -💡 NLP 모델에서 어휘는 일반적으로 개별 문자뿐만 아니라 단어의 일부 또는 완전한 단어를 설명하는 수천 개의 토큰으로 구성됩니다. 그러나 CTC의 경우 작은 어휘가 가장 효과적이며 일반적으로 50자 미만으로 유지하려고 노력합니다. 트위터에서는 글자의 대소문자를 구분하지 않으므로 대문자(또는 소문자)만 사용해도 충분합니다. 숫자는 철자로 표기합니다(예: `"20"`은 `"twenty"`가 됩니다). 문자 외에도 최소한 단어 구분 토큰(공백)과 패딩 토큰이 필요합니다. 패딩 토큰은 자연어 처리 모델과 마찬가지로 여러 개의 예문을 일괄적으로 결합할 수 있게 해주지만, 모델이 무음을 예측할 때 사용하는 토큰이기도 합니다. 영어에서는 `'` 문자를 유지하는 것도 유용합니다. `it`s`와 `its`는 매우 다른 의미를 갖기 때문입니다. - +> [!TIP] +> 💡 NLP 모델에서 어휘는 일반적으로 개별 문자뿐만 아니라 단어의 일부 또는 완전한 단어를 설명하는 수천 개의 토큰으로 구성됩니다. 그러나 CTC의 경우 작은 어휘가 가장 효과적이며 일반적으로 50자 미만으로 유지하려고 노력합니다. 트위터에서는 글자의 대소문자를 구분하지 않으므로 대문자(또는 소문자)만 사용해도 충분합니다. 숫자는 철자로 표기합니다(예: `"20"`은 `"twenty"`가 됩니다). 문자 외에도 최소한 단어 구분 토큰(공백)과 패딩 토큰이 필요합니다. 패딩 토큰은 자연어 처리 모델과 마찬가지로 여러 개의 예문을 일괄적으로 결합할 수 있게 해주지만, 모델이 무음을 예측할 때 사용하는 토큰이기도 합니다. 영어에서는 `'` 문자를 유지하는 것도 유용합니다. `it`s`와 `its`는 매우 다른 의미를 갖기 때문입니다. ## 정렬을 어떻게 확인하지?[[dude-wheres-my-alignment]] @@ -92,9 +91,8 @@ BRION SAW SOMETHING CLOSE TO PANIC ON HIS OPPONENT'S FACE WHEN THE MAN FINALLY R 요약하자면, 모델은 입력 파형에서 (부분적으로 겹치는) 오디오의 20ms마다 하나의 토큰(문자)을 예측합니다. 이로 인해 많은 중복이 발생합니다. CTC 빈 토큰 덕분에 단어의 올바른 철자를 파괴하지 않고도 이러한 중복을 쉽게 제거할 수 있습니다. 이는 출력 텍스트를 입력 오디오와 정렬하는 문제를 해결하는 매우 간단하고 편리한 방법입니다. - -💡 실제 Wav2Vec2 모델에서 CTC 빈 토큰은 패딩 토큰 ``와 동일합니다. 이 모델은 예를 들어 현재 20ms의 오디오에 대해 예측할 명확한 문자가 없는 경우와 같이 이러한 `` 토큰을 많이 예측합니다. 패딩에 CTC 공백(blanking)과 동일한 토큰을 사용하면 디코딩 알고리즘이 단순화되고 어휘를 작게 유지하는 데 도움이 됩니다. - +> [!TIP] +> 💡 실제 Wav2Vec2 모델에서 CTC 빈 토큰은 패딩 토큰 ``와 동일합니다. 이 모델은 예를 들어 현재 20ms의 오디오에 대해 예측할 명확한 문자가 없는 경우와 같이 이러한 `` 토큰을 많이 예측합니다. 패딩에 CTC 공백(blanking)과 동일한 토큰을 사용하면 디코딩 알고리즘이 단순화되고 어휘를 작게 유지하는 데 도움이 됩니다. 인코더의 출력 시퀀스가 어휘에 음향 특징을 투영하는 선형 레이어로 이동하기 때문에 트랜스포머 인코더 모델에 CTC를 추가하는 것은 간단합니다.모델은 특수한 CTC 손실로 훈련됩니다. diff --git a/chapters/ko/chapter3/introduction.mdx b/chapters/ko/chapter3/introduction.mdx index 156b945e..0314edc5 100644 --- a/chapters/ko/chapter3/introduction.mdx +++ b/chapters/ko/chapter3/introduction.mdx @@ -92,9 +92,8 @@ SpeechT5 outputs a spectrogram and uses a vocoder to create the waveform - -💡 기존 파형을 가지고 단시간 푸리에 변환(STFT)을 적용하면 역연산인 ISFT를 수행하여 원래의 파형을 다시 얻을 수 있습니다. 이는 STFT로 생성된 스펙트로그램에 진폭과 위상 정보가 모두 포함되어 있고 파형을 재구성하는 데 두 가지 정보가 모두 필요하기 때문에 가능합니다. 그러나 스펙트로그램으로 출력을 생성하는 오디오 모델은 일반적으로 위상이 아닌 진폭 정보만 예측합니다. 이러한 스펙트로그램을 파형으로 변환하려면 어떻게든 위상 정보를 추정해야 합니다. 이것이 바로 보코더가 하는 일입니다. - +> [!TIP] +> 💡 기존 파형을 가지고 단시간 푸리에 변환(STFT)을 적용하면 역연산인 ISFT를 수행하여 원래의 파형을 다시 얻을 수 있습니다. 이는 STFT로 생성된 스펙트로그램에 진폭과 위상 정보가 모두 포함되어 있고 파형을 재구성하는 데 두 가지 정보가 모두 필요하기 때문에 가능합니다. 그러나 스펙트로그램으로 출력을 생성하는 오디오 모델은 일반적으로 위상이 아닌 진폭 정보만 예측합니다. 이러한 스펙트로그램을 파형으로 변환하려면 어떻게든 위상 정보를 추정해야 합니다. 이것이 바로 보코더가 하는 일입니다. ### 파형 출력[[waveform-output]] diff --git a/chapters/ko/chapter3/seq2seq.mdx b/chapters/ko/chapter3/seq2seq.mdx index fc5b6b98..0e673a1f 100644 --- a/chapters/ko/chapter3/seq2seq.mdx +++ b/chapters/ko/chapter3/seq2seq.mdx @@ -18,9 +18,8 @@ seq2seq 모델을 사용하면 이러한 일대일 대응이 없으며 입력 꽤 익숙하게 보일 것입니다. 왼쪽은 **트랜스포머 인코더**입니다. 이것은 로그 멜 스펙트로그램을 입력으로 받아 해당 스펙트로그램을 인코딩하여 음성에서 중요한 특징을 추출하는 인코더의 은닉 상태 시퀀스를 형성합니다. 이 은닉 상태 텐서는 입력 시퀀스를 전체적으로 나타내며 입력 음성의 '의미'를 효과적으로 인코딩합니다. - -💡 이러한 seq2seq 모델은 스펙트로그램을 입력으로 사용하는 것이 일반적입니다. 하지만 오디오 파형에서 직접 작동하도록 설계할 수도 있습니다. - +> [!TIP] +> 💡 이러한 seq2seq 모델은 스펙트로그램을 입력으로 사용하는 것이 일반적입니다. 하지만 오디오 파형에서 직접 작동하도록 설계할 수도 있습니다. 그런 다음 인코더의 출력은 **크로스 어텐션**이라는 메커니즘을 사용하여 오른쪽에 표시된 **트랜스포머 디코더**로 전달됩니다. 이는 셀프 어텐션과 비슷하지만 인코더 출력을 통해 이루어집니다. 이 시점부터 인코더는 더 이상 필요하지 않습니다. @@ -33,9 +32,8 @@ seq2seq 모델을 사용하면 이러한 일대일 대응이 없으며 입력 이 설계에서 디코더는 **언어 모델**의 역할을 수행하여 인코더의 은닉 상태 표현을 처리하고 해당 텍스트 트랜스크립션을 생성합니다. 이는 CTC 모델을 외부 언어 모델과 결합하더라도 동일한 훈련 데이터와 손실 함수로 seq2seq 시스템을 엔드 투 엔드 훈련할 수 있어 유연성이 뛰어나고 일반적으로 성능이 우수하기 때문에 CTC보다 강력한 접근 방식입니다. - -💡 CTC 모델은 개별 문자의 시퀀스를 출력하는 반면, Whisper가 예측하는 토큰은 전체 단어 또는 단어의 일부입니다. GPT-2의 토크나이저를 사용하며 5만 개 이상의 고유 토큰을 보유하고 있습니다. 따라서 seq2seq 모델은 동일한 트랜스크립션에 대해 CTC 모델보다 훨씬 짧은 시퀀스를 출력할 수 있습니다. - +> [!TIP] +> 💡 CTC 모델은 개별 문자의 시퀀스를 출력하는 반면, Whisper가 예측하는 토큰은 전체 단어 또는 단어의 일부입니다. GPT-2의 토크나이저를 사용하며 5만 개 이상의 고유 토큰을 보유하고 있습니다. 따라서 seq2seq 모델은 동일한 트랜스크립션에 대해 CTC 모델보다 훨씬 짧은 시퀀스를 출력할 수 있습니다. 모델의 최종 계층이 발생 가능한 토큰에 대한 확률 분포를 예측하기 때문에 seq2seq ASR 모델의 일반적인 손실 함수는 크로스 엔트로피 손실입니다. 이는 일반적으로 [최종 시퀀스 생성을 위한 빔 검색](https://huggingface.co/blog/how-to-generate)과 같은 기술과 결합됩니다. 음성 인식의 지표는 문자 오류율(WER, Word Error Rate)로, 예측된 텍스트를 대상 텍스트로 바꾸는 데 필요한 대체, 삽입, 삭제 횟수를 측정하며, 이 수치가 적을수록 좋은 점수를 받습니다. @@ -43,9 +41,8 @@ seq2seq 모델을 사용하면 이러한 일대일 대응이 없으며 입력 놀랍지 않으실 수도 있습니다: TTS용 seq2seq 모델은 위에서 설명한 것과 본질적으로 동일하게 작동하지만 입력과 출력의 위치가 바뀝니다! 트랜스포머 인코더는 일련의 텍스트 토큰을 받아 입력 텍스트를 나타내는 은닉 상태 시퀀스를 추출합니다. 트랜스포머 디코더는 인코더 출력에 크로스 어텐션을 적용하고 스펙트로그램을 예측합니다. - -💡 스펙트로그램은 오디오 파형의 연속적인 시간 조각의 주파수 스펙트럼을 가져와서 함께 쌓아서 만든다는 것을 기억하세요. 즉, 스펙트로그램은 각 타임스텝마다 하나씩의 로그 멜(log-mel) 주파수 스펙트럼이 요소로 구성된 시퀀스입니다. - +> [!TIP] +> 💡 스펙트로그램은 오디오 파형의 연속적인 시간 조각의 주파수 스펙트럼을 가져와서 함께 쌓아서 만든다는 것을 기억하세요. 즉, 스펙트로그램은 각 타임스텝마다 하나씩의 로그 멜(log-mel) 주파수 스펙트럼이 요소로 구성된 시퀀스입니다. ASR 모델에서는 특별한 "시작" 토큰이 포함된 시퀀스를 사용하여 디코더를 시작합니다. TTS 모델의 경우, '시작 토큰' 역할을 하는 길이가 1이고 모두 값이 0인 스펙트로그램으로 디코딩을 시작할 수 있습니다. 이 초기 스펙트로그램과 인코더의 은닉 상태 표현에 대한 크로스 어텐션이 주어지면 디코더는 이 스펙트로그램의 다음 타임슬라이스를 예측하여 스펙트로그램을 한 번에 한 타임스텝씩 꾸준히 증가시킵니다. diff --git a/chapters/pt-BR/chapter0/get_ready.mdx b/chapters/pt-BR/chapter0/get_ready.mdx index 243a2179..40dbdd95 100644 --- a/chapters/pt-BR/chapter0/get_ready.mdx +++ b/chapters/pt-BR/chapter0/get_ready.mdx @@ -24,11 +24,8 @@ Para acompanhar os materiais do curso, você precisará de: - Um computador com conexão à internet - [Google Colab](https://colab.research.google.com) para os exercícios práticos. A versão gratuita é suficiente. Se você nunca usou o Google Colab antes, confira este [caderno de introdução oficial](https://colab.research.google.com/notebooks/intro.ipynb?hl=pt-BR). - - -Como alternativa à versão gratuita do Google Colab, você pode usar seu próprio ambiente local ou o Kaggle. O Kaggle Notebooks oferece um número fixo de horas de GPU e têm funcionalidades semelhantes ao Google Colab, no entanto, existem diferenças quando se trata de compartilhar seus modelos no 🤗 Hub (para completar tarefas, por exemplo). Se você decidir usar o Kaggle como sua ferramenta de escolha, confira o [notebook de exemplo do Kaggle](https://www.kaggle.com/code/michaelshekasta/test-notebook) criado por [@michaelshekasta](https://github.com/michaelshekasta). Este notebook demonstra como você pode treinar e compartilhar seu modelo treinado no 🤗 Hub. - - +> [!TIP] +> Como alternativa à versão gratuita do Google Colab, você pode usar seu próprio ambiente local ou o Kaggle. O Kaggle Notebooks oferece um número fixo de horas de GPU e têm funcionalidades semelhantes ao Google Colab, no entanto, existem diferenças quando se trata de compartilhar seus modelos no 🤗 Hub (para completar tarefas, por exemplo). Se você decidir usar o Kaggle como sua ferramenta de escolha, confira o [notebook de exemplo do Kaggle](https://www.kaggle.com/code/michaelshekasta/test-notebook) criado por [@michaelshekasta](https://github.com/michaelshekasta). Este notebook demonstra como você pode treinar e compartilhar seu modelo treinado no 🤗 Hub. ## Passo 5. Junte-se à comunidade diff --git a/chapters/pt-BR/chapter1/audio_data.mdx b/chapters/pt-BR/chapter1/audio_data.mdx index fc225ba5..8b7010c4 100644 --- a/chapters/pt-BR/chapter1/audio_data.mdx +++ b/chapters/pt-BR/chapter1/audio_data.mdx @@ -116,9 +116,8 @@ O resultado da DFT é um array de números complexos, compostos de componentes r Você usou `librosa.amplitude_to_db()` para converter os valores de amplitude para a escala de decibéis, facilitando a visualização dos mínimos detalhes no espectro. Às vezes, as pessoas usam o **espectro de potência**, que mede a energia em vez da amplitude; isso é simplesmente um espectro com os valores de amplitude ao quadrado. - -💡 Na prática, as pessoas usam o termo FFT e DFT como sinônimos, já que a FFT, ou Fast Fouriter Transform (Transformada Rápida de Fourier), é a única maneira eficiente de calcular a DFT em um computador. - +> [!TIP] +> 💡 Na prática, as pessoas usam o termo FFT e DFT como sinônimos, já que a FFT, ou Fast Fouriter Transform (Transformada Rápida de Fourier), é a única maneira eficiente de calcular a DFT em um computador. O espectro de frequência de um sinal de áudio contém exatamente as mesmas informações que sua forma de onda — são simplesmente duas maneiras diferentes de olhar para os mesmos dados (no caso, as primeiras 4096 amostras do som de trompete). Enquanto a forma de onda traça a amplitude do sinal de áudio ao longo do tempo, o espectro visualiza as amplitudes de cada frequência em um ponto fixo no tempo. @@ -195,14 +194,13 @@ Assim como com um espectrograma regular, é prática comum expressar a intesidad decibéis. Isso é comumente referido como um **espectrograma log-mel**, porque a conversão para decibéis envolve uma operação logarítmica. O exemplo acima usou `librosa.power_to_db()` já que `librosa.feature.melspectrogram()` cria um espectrograma de potência. - -💡 Nem todos os espectrogramas mel são iguais! Existem duas escalas mel diferentes comumente usadas ("htk" e "slaney"), -e, ao invés do espectrograma de potência, pode ser usado o espectrograma de amplitude. - - A conversão para um espectrograma log-mel não -calcula sempre decibéis reais, mas pode simplesmente calcular `log`. Portanto, se um modelo de aprendizado de máquina espera um espectrograma mel -como entrada, certifique-se de garantir que você está calculando da mesma maneira. - +> [!TIP] +> 💡 Nem todos os espectrogramas mel são iguais! Existem duas escalas mel diferentes comumente usadas ("htk" e "slaney"), +> e, ao invés do espectrograma de potência, pode ser usado o espectrograma de amplitude. +> +> A conversão para um espectrograma log-mel não +> calcula sempre decibéis reais, mas pode simplesmente calcular `log`. Portanto, se um modelo de aprendizado de máquina espera um espectrograma mel +> como entrada, certifique-se de garantir que você está calculando da mesma maneira. Criar um espectrograma mel é uma operação com perda, pois envolve filtragem do sinal. Converter um espectrograma mel de volta em uma forma de onda é mais difícil do que fazer isso para um espectrograma regular, pois exige a estimação das frequências diff --git a/chapters/pt-BR/chapter1/preprocessing.mdx b/chapters/pt-BR/chapter1/preprocessing.mdx index d7b9609d..50599096 100644 --- a/chapters/pt-BR/chapter1/preprocessing.mdx +++ b/chapters/pt-BR/chapter1/preprocessing.mdx @@ -53,9 +53,8 @@ minds[0] Repare que os valores do array também estão diferentes agora. Isso ocorre porque agora temos o dobro do número de valores de amplitude para cada um que tínhamos antes. - -💡 Algumas informações sobre a reamostragem (resampling): Se um sinal de áudio foi amostrado a 8 kHz, de modo que possui 8000 amostras coletadas por segundo, sabemos que o áudio não contém frequências acima de 4 kHz. Isso é garantido pelo teorema de Nyquist. Com base nisso, podemos assumir que entre as amostras de um sinal contínuo, especificamente os pontos que existem entre elas, e não foram coletados, devido a amostragem, formam uma curva suave. Então, aumentar a taxa de amostragem para um valor mais alto é apenas uma questão de calcular valores de amostra adicionais que vão entre os existentes, formando essa curva suave. No entanto, diminuir a amostragem (downsampling) requer primeiro filtrar quaisquer frequências que seriam maiores que o novo limite de Nyquist, antes de estimar os novos pontos de amostra (pois diminuindo a amostragem, a frequência máxima também diminuiria). Em outras palavras, você não pode diminuir a amostragem por um fator de 2x simplesmente descartando todas as outras amostras - isso criará distorções no sinal chamadas de alias. Fazer a reamostragem corretamente é complicado e é melhor deixar para bibliotecas bem testadas como librosa ou 🤗 Datasets. - +> [!TIP] +> 💡 Algumas informações sobre a reamostragem (resampling): Se um sinal de áudio foi amostrado a 8 kHz, de modo que possui 8000 amostras coletadas por segundo, sabemos que o áudio não contém frequências acima de 4 kHz. Isso é garantido pelo teorema de Nyquist. Com base nisso, podemos assumir que entre as amostras de um sinal contínuo, especificamente os pontos que existem entre elas, e não foram coletados, devido a amostragem, formam uma curva suave. Então, aumentar a taxa de amostragem para um valor mais alto é apenas uma questão de calcular valores de amostra adicionais que vão entre os existentes, formando essa curva suave. No entanto, diminuir a amostragem (downsampling) requer primeiro filtrar quaisquer frequências que seriam maiores que o novo limite de Nyquist, antes de estimar os novos pontos de amostra (pois diminuindo a amostragem, a frequência máxima também diminuiria). Em outras palavras, você não pode diminuir a amostragem por um fator de 2x simplesmente descartando todas as outras amostras - isso criará distorções no sinal chamadas de alias. Fazer a reamostragem corretamente é complicado e é melhor deixar para bibliotecas bem testadas como librosa ou 🤗 Datasets. ## Filtragem do dataset diff --git a/chapters/ru/chapter1/audio_data.mdx b/chapters/ru/chapter1/audio_data.mdx index d05178cf..d38ad4fc 100644 --- a/chapters/ru/chapter1/audio_data.mdx +++ b/chapters/ru/chapter1/audio_data.mdx @@ -177,9 +177,8 @@ plt.xscale("log") более тонких деталей в спектре. Иногда используют **энергетический спектр**, который измеряет энергию, а не амплитуду; это просто спектр с квадратом амплитудных значений. - -💡 На практике термин БПФ (Fast Fourier Transform - FFT) используется как взаимозаменяемый с термином ДПФ, поскольку БПФ или быстрое преобразование Фурье является единственным эффективным способ вычисления ДПФ на компьютере. - +> [!TIP] +> 💡 На практике термин БПФ (Fast Fourier Transform - FFT) используется как взаимозаменяемый с термином ДПФ, поскольку БПФ или быстрое преобразование Фурье является единственным эффективным способ вычисления ДПФ на компьютере. Частотный спектр аудиосигнала содержит точно такую же информацию, как и его волновая форма, - это просто два разных способа взглянуть на одни и те же данные (здесь - первые 4096 выборок из звука трубы). Если волновая форма отображает амплитуду @@ -273,12 +272,11 @@ plt.colorbar() децибелах. Такую спектрограмму принято называть **лог-мэл-спектрограммой**, поскольку при переводе в децибелы выполняется логарифмическая операция. В приведенном выше примере использовалась функция `librosa.power_to_db()`, так как `librosa.feature.melspectrogram()` создает спектрограмму мощности. - -💡 Не все мэл спектрограммы одинаковы! Существуют две различные шкалы мэл, которые широко используются ("htk" и "slaney"), -а вместо спектрограммы мощности может использоваться амплитудная спектрограмма. При преобразовании спектрограммы в лог-мел не всегда -вычисляются истинные децибелы, а может просто браться `log`. Поэтому, если модель машинного обучения ожидает в качестве входных данных спектрограмму мел, -дважды проверьте, что она вычисляется тем же способом. - +> [!TIP] +> 💡 Не все мэл спектрограммы одинаковы! Существуют две различные шкалы мэл, которые широко используются ("htk" и "slaney"), +> а вместо спектрограммы мощности может использоваться амплитудная спектрограмма. При преобразовании спектрограммы в лог-мел не всегда +> вычисляются истинные децибелы, а может просто браться `log`. Поэтому, если модель машинного обучения ожидает в качестве входных данных спектрограмму мел, +> дважды проверьте, что она вычисляется тем же способом. Создание mel-спектрограммы - это операция с потерями, так как она связана с фильтрацией сигнала. Конвертировать mel-спектрограмму обратно в волновую форму сложнее, чем обычную спектрограмму, так как для этого необходимо оценить частоты, diff --git a/chapters/ru/chapter1/preprocessing.mdx b/chapters/ru/chapter1/preprocessing.mdx index f987ea96..5bbe25b2 100644 --- a/chapters/ru/chapter1/preprocessing.mdx +++ b/chapters/ru/chapter1/preprocessing.mdx @@ -61,16 +61,15 @@ minds[0] Вы можете заметить, что значения массива теперь также отличаются. Это связано с тем, что теперь для каждого значения амплитуды мы имеем в два раза больше значений чем раньше. - -💡 Некоторые сведения о передискретизации: Если аудиосигнал дискретизирован с частотой 8 кГц, т. е. имеет 8000 выборок в -секунду, то мы знаем, что он не содержит частот выше 4 кГц. Это гарантируется теоремой Найквиста -о дискретизации. Благодаря этому мы можем быть уверены, что между точками дискретизации исходный непрерывный сигнал всегда -имеет плавную кривую. Повышение частоты дискретизации до более высокой сводится к вычислению дополнительных значений выборки, которые находятся между -существующими, путем аппроксимации этой кривой. Однако понижающая дискретизация требует, чтобы мы сначала отфильтровали все частоты, -которые будут выше нового предела Найквиста, прежде чем оценивать новые точки дискретизации. Другими словами, нельзя -понизить дискретизацию в 2 раза, просто отбрасывая каждый второй сэмпл - это приведет к появлению искажений в сигнале, называемых -наложениями. Корректная передискретизация - дело непростое, и его лучше доверить проверенным библиотекам, таким как librosa или 🤗 Datasets. - +> [!TIP] +> 💡 Некоторые сведения о передискретизации: Если аудиосигнал дискретизирован с частотой 8 кГц, т. е. имеет 8000 выборок в +> секунду, то мы знаем, что он не содержит частот выше 4 кГц. Это гарантируется теоремой Найквиста +> о дискретизации. Благодаря этому мы можем быть уверены, что между точками дискретизации исходный непрерывный сигнал всегда +> имеет плавную кривую. Повышение частоты дискретизации до более высокой сводится к вычислению дополнительных значений выборки, которые находятся между +> существующими, путем аппроксимации этой кривой. Однако понижающая дискретизация требует, чтобы мы сначала отфильтровали все частоты, +> которые будут выше нового предела Найквиста, прежде чем оценивать новые точки дискретизации. Другими словами, нельзя +> понизить дискретизацию в 2 раза, просто отбрасывая каждый второй сэмпл - это приведет к появлению искажений в сигнале, называемых +> наложениями. Корректная передискретизация - дело непростое, и его лучше доверить проверенным библиотекам, таким как librosa или 🤗 Datasets. ## Фильтрация набора данных diff --git a/chapters/ru/chapter3/classification.mdx b/chapters/ru/chapter3/classification.mdx index 689c1ddd..aa33297a 100644 --- a/chapters/ru/chapter3/classification.mdx +++ b/chapters/ru/chapter3/classification.mdx @@ -23,10 +23,9 @@ AST является моделью трансформера состоящей Изображение из статьи [AST: Трансформер аудио спектрограммы](https://arxiv.org/pdf/2104.01778.pdf) - -💡 Хотя здесь мы делаем вид, что спектрограммы - это то же самое, что и изображения, есть существенные различия. Например, смещение содержимого изображения вверх или вниз, как правило, не меняет смысла того, что на нем изображено. Однако смещение спектрограммы вверх или вниз приводит к -изменению частот, присутствующих в звуке, и полностью меняет его характер. Изображения инвариантны при трансляции, а спектрограммы - нет. Обращение со спектрограммами как с изображениями может хорошо работать на практике, но следует помнить, что это не совсем одно и то же. - +> [!TIP] +> 💡 Хотя здесь мы делаем вид, что спектрограммы - это то же самое, что и изображения, есть существенные различия. Например, смещение содержимого изображения вверх или вниз, как правило, не меняет смысла того, что на нем изображено. Однако смещение спектрограммы вверх или вниз приводит к +> изменению частот, присутствующих в звуке, и полностью меняет его характер. Изображения инвариантны при трансляции, а спектрограммы - нет. Обращение со спектрограммами как с изображениями может хорошо работать на практике, но следует помнить, что это не совсем одно и то же. ## Любой трансформер может быть классификатором diff --git a/chapters/ru/chapter3/ctc.mdx b/chapters/ru/chapter3/ctc.mdx index 360afd0b..3205d42b 100644 --- a/chapters/ru/chapter3/ctc.mdx +++ b/chapters/ru/chapter3/ctc.mdx @@ -19,13 +19,12 @@ CTC (Connectionist Temporal Classification) или Коннекционистс Вот в чем загвоздка: в речи мы не знаем **соответствия** между входными аудио сигналами и текстовыми выходами. Мы знаем, что порядок произнесения речи совпадает с порядком транскрибирования текста (так называемое монотонное выравнивание), но мы не знаем, как символы в транскрипции соотносятся с аудиозаписью. В этом случае на помощь приходит алгоритм CTC. - -💡 В моделях NLP словарный запас обычно состоит из тысяч токенов, которые описывают не только отдельные символы, но и части слов или даже целые слова. Однако для CTC лучше всего -подходит небольшой словарь, и мы обычно стараемся, чтобы он не превышал 50 символов. Нам не важен регистр букв, поэтому достаточно использовать только верхний регистр (или только -нижний). Числа пишутся буквами, например, `"20"` становится `" twenty"`. Помимо букв, нам необходимы как минимум токен-разделитель слов (пробел) и токен-заполнитель. Как и в модели NLP, -токен-заполнитель позволяет объединить несколько примеров в батч, а также является токеном, который модель будет предсказывать в случае тишины. В английском языке также полезно сохранить -символ `'`' - в конце концов, `"it's"` и `"its"` имеют совершенно разные значения. - +> [!TIP] +> 💡 В моделях NLP словарный запас обычно состоит из тысяч токенов, которые описывают не только отдельные символы, но и части слов или даже целые слова. Однако для CTC лучше всего +> подходит небольшой словарь, и мы обычно стараемся, чтобы он не превышал 50 символов. Нам не важен регистр букв, поэтому достаточно использовать только верхний регистр (или только +> нижний). Числа пишутся буквами, например, `"20"` становится `" twenty"`. Помимо букв, нам необходимы как минимум токен-разделитель слов (пробел) и токен-заполнитель. Как и в модели NLP, +> токен-заполнитель позволяет объединить несколько примеров в батч, а также является токеном, который модель будет предсказывать в случае тишины. В английском языке также полезно сохранить +> символ `'`' - в конце концов, `"it's"` и `"its"` имеют совершенно разные значения. ## Дружище, где мое выравнивание? @@ -108,10 +107,9 @@ BRION SAW SOMETHING CLOSE TO PANIC ON HIS OPPONENT'S FACE WHEN THE MAN FINALLY R Напомним, что модель предсказывает один токен (символ) на каждые 20 мс (частично перекрывающихся) аудиоданных из входной формы сигнала. Это порождает большое количество дубликатов. Благодаря пустому токену CTC мы можем легко удалить эти дубликаты, не нарушая правильности написания слов. Это очень простой и удобный способ решения проблемы выравнивания выходного текста по входному звуку. - -💡 В реальной модели Wav2Vec2 пустой токен CTC совпадает с токеном заполнения ``. Модель будет прогнозировать много таких токенов ``, например, когда для текущих 20 мс звука нет четкого символа для прогнозирования. Использование одного и того же токена для дополнения, как и -пустой токен для CTC, упрощает алгоритм декодирования и позволяет сохранить небольшой объем словаря. - +> [!TIP] +> 💡 В реальной модели Wav2Vec2 пустой токен CTC совпадает с токеном заполнения ``. Модель будет прогнозировать много таких токенов ``, например, когда для текущих 20 мс звука нет четкого символа для прогнозирования. Использование одного и того же токена для дополнения, как и +> пустой токен для CTC, упрощает алгоритм декодирования и позволяет сохранить небольшой объем словаря. Добавить CTC в модель трансформера энкодера очень просто: выходная последовательность с энкодера поступает на линейный слой, который проецирует акустические признаки на словарь. Модель обучается с помощью специальной функции потерь CTC. diff --git a/chapters/ru/chapter3/introduction.mdx b/chapters/ru/chapter3/introduction.mdx index b4588e46..efdb67e4 100644 --- a/chapters/ru/chapter3/introduction.mdx +++ b/chapters/ru/chapter3/introduction.mdx @@ -109,11 +109,10 @@ SpeechT5 outputs a spectrogram and uses a vocoder to create the waveform - -💡 Если взять существующую форму сигнала и применить к ней Оконное преобразование Фурье или ОПФ, то можно выполнить обратную операцию, ООПФ, чтобы снова получить исходную форму сигнала. Это работает потому, что спектрограмма, -созданная в результате ОПФ, содержит информацию как об амплитуде, так и о фазе, а для восстановления формы волны необходимо и то, и другое. Однако аудиомодели, генерирующие выходной сигнал в виде спектрограммы, обычно -предсказывают только амплитудную информацию, но не фазовую. Чтобы превратить такую спектрограмму в форму волны, необходимо каким-то образом оценить фазовую информацию. Этим и занимается вокодер. - +> [!TIP] +> 💡 Если взять существующую форму сигнала и применить к ней Оконное преобразование Фурье или ОПФ, то можно выполнить обратную операцию, ООПФ, чтобы снова получить исходную форму сигнала. Это работает потому, что спектрограмма, +> созданная в результате ОПФ, содержит информацию как об амплитуде, так и о фазе, а для восстановления формы волны необходимо и то, и другое. Однако аудиомодели, генерирующие выходной сигнал в виде спектрограммы, обычно +> предсказывают только амплитудную информацию, но не фазовую. Чтобы превратить такую спектрограмму в форму волны, необходимо каким-то образом оценить фазовую информацию. Этим и занимается вокодер. ### Вывод формы волны diff --git a/chapters/ru/chapter3/seq2seq.mdx b/chapters/ru/chapter3/seq2seq.mdx index 6d607948..3043704c 100644 --- a/chapters/ru/chapter3/seq2seq.mdx +++ b/chapters/ru/chapter3/seq2seq.mdx @@ -23,9 +23,8 @@ Все это должно выглядеть довольно знакомо. Слева находится **энкодер трансформера**. В качестве входного сигнала принимается лог-мел спектрограмма, которая кодируется для формирования последовательности скрытых состояний энкодера, извлекающих важные признаки из произносимой речи. Этот тензор скрытых состояний представляет входную последовательность как единое целое и эффективно кодирует "смысл" поступившей на вход речи. - -💡 Обычно в таких seq2seq-моделях в качестве входных данных используются спектрограммы. Однако модель seq2seq может быть разработана и для работы непосредственно с формой волны звука. - +> [!TIP] +> 💡 Обычно в таких seq2seq-моделях в качестве входных данных используются спектрограммы. Однако модель seq2seq может быть разработана и для работы непосредственно с формой волны звука. Затем выход энкодера передается в **декодер трансформера**, показанный справа, с помощью механизма, называемого **перекрёстным вниманием (cross-attention)**. Это похоже на самовнимание (self-attention), но внимание направлено на выход энкодера. С этого момента энкодер больше не нужен. @@ -41,10 +40,9 @@ В этом случае декодер играет роль **языковой модели**, обрабатывая представления скрытых состояний, полученные от энкодера, и генерируя соответствующие текстовые транскрипции. Это более мощный подход, чем CTC, даже если модель CTC сочетается с внешней языковой моделью, так как система seq2seq может быть обучена от начала до конца с использованием одних и тех же обучающих данных и функции потерь, что обеспечивает большую гибкость и в целом более высокую производительность. - -💡 В то время как модель CTC выводит последовательность отдельных символов, токены, предсказываемые Whisper, представляют собой полные слова или фрагменты слов. Он использует токенизатор из GPT-2 и имеет 50k+ уникальных токенов. Поэтому модель seq2seq может выдать гораздо более короткую -последовательность, чем модель CTC для той же транскрипции. - +> [!TIP] +> 💡 В то время как модель CTC выводит последовательность отдельных символов, токены, предсказываемые Whisper, представляют собой полные слова или фрагменты слов. Он использует токенизатор из GPT-2 и имеет 50k+ уникальных токенов. Поэтому модель seq2seq может выдать гораздо более короткую +> последовательность, чем модель CTC для той же транскрипции. Типичной функцией потерь для seq2seq ASR-модели является функция кросс-энтропии, поскольку последний слой модели предсказывает распределение вероятностей по возможным токенам. Обычно это сочетается с такими методами, как [лучевой поиск для генерации конечной последовательности](https://huggingface.co/blog/how-to-generate). Метрикой распознавания речи является WER или word error rate, которая измеряет, сколько замен, вставок и удалений необходимо для превращения предсказанного текста в целевой - чем меньше, тем лучше результат. @@ -53,10 +51,9 @@ seq2seq может быть обучена от начала до конца с Возможно, это вас не удивит: Модель seq2seq для TTS работает по сути так же, как и описанная выше, но входы и выходы поменяны местами! Энкодер трансформера принимает последовательность текстовых токенов и извлекает из нее последовательность скрытых состояний, которые представляют собой входной текст. Декодер трансформера применяет перекрестное внимание к выходу энкодера и прогнозирует спектрограмму. - -💡 Напомним, что спектрограмма создается путем взятия частотного спектра последовательных временных отрезков звуковой волны и их суммирования. Другими словами, спектрограмма - это последовательность, элементами которой являются (лог-мел) частотные спектры, по одному на каждый временной -интервал. - +> [!TIP] +> 💡 Напомним, что спектрограмма создается путем взятия частотного спектра последовательных временных отрезков звуковой волны и их суммирования. Другими словами, спектрограмма - это последовательность, элементами которой являются (лог-мел) частотные спектры, по одному на каждый временной +> интервал. В ASR-модели декодер запускался с помощью последовательности, содержащей только специальный токен "start". Для модели TTS мы можем начать декодирование со спектрограммы длиной один, состоящей из одних нулей, которая выступает в качестве "стартового токена". Учитывая эту начальную спектрограмму и перекрестное внимание к представлениям скрытых состояний энкодера, декодер предсказывает следующий временной интервал для этой спектрограммы, постепенно увеличивая спектрограмму на один временной интервал. diff --git a/chapters/ru/chapter4/fine-tuning.mdx b/chapters/ru/chapter4/fine-tuning.mdx index 9c786f99..88a92c47 100644 --- a/chapters/ru/chapter4/fine-tuning.mdx +++ b/chapters/ru/chapter4/fine-tuning.mdx @@ -28,12 +28,9 @@ Dataset({ }) ``` - - -Одна из записей в GTZAN повреждена, поэтому она была удалена из набора данных. Поэтому мы имеем 999 примеров -вместо 1000. - - +> [!WARNING] +> Одна из записей в GTZAN повреждена, поэтому она была удалена из набора данных. Поэтому мы имеем 999 примеров +> вместо 1000. GTZAN не предоставляет предопределенного валидационного набора, поэтому нам придется создать его самостоятельно. @@ -279,14 +276,11 @@ Mean: -4.53e-09, Variance: 1.0 Мы видим, что среднее значение теперь очень сильно приближается к нулю, а дисперсия - к единице! Именно в таком виде мы хотим получить наши аудиосэмплы перед подачей их в модель HuBERT. - - -Обратите внимание, как мы передали частоту дискретизации наших аудиоданных нашему экстрактору признаков. Это хорошая практика, так как -экстрактор признаков выполняет проверку под капотом, чтобы убедиться, что частота дискретизации наших аудиоданных соответствует частоте -дискретизации, ожидаемой моделью. Если частота дискретизации аудиоданных не совпадает с частотой дискретизации нашей модели, то необходимо -увеличить или уменьшить частоту дискретизации аудиоданных до нужной. - - +> [!WARNING] +> Обратите внимание, как мы передали частоту дискретизации наших аудиоданных нашему экстрактору признаков. Это хорошая практика, так как +> экстрактор признаков выполняет проверку под капотом, чтобы убедиться, что частота дискретизации наших аудиоданных соответствует частоте +> дискретизации, ожидаемой моделью. Если частота дискретизации аудиоданных не совпадает с частотой дискретизации нашей модели, то необходимо +> увеличить или уменьшить частоту дискретизации аудиоданных до нужной. Отлично, теперь мы знаем, как обрабатывать наши ресэмплированные аудиофайлы, осталось определить функцию, которую мы можем применить ко всем примерам в наборе данных. Поскольку мы ожидаем, что длина аудиоклипов будет составлять 30 секунд, мы также будем обрезать все более длинные клипы, @@ -343,12 +337,11 @@ DatasetDict({ }) ``` - - Если при выполнении приведенного выше кода оперативная память устройства будет исчерпана, можно настроить параметры пакетной обработки, - чтобы уменьшить пиковое потребление оперативной памяти. В частности, можно модифицировать следующие два аргумента: - * `batch_size`: по умолчанию 1000, но выше было установлено значение 100. Попробуйте еще раз уменьшить в 2 раза до 50 - * `writer_batch_size`: по умолчанию равен 1000. Попробуйте уменьшить его до 500, а если это не сработает, то уменьшите его еще раз в 2 раза до 250 - +> [!WARNING] +> Если при выполнении приведенного выше кода оперативная память устройства будет исчерпана, можно настроить параметры пакетной обработки, +> чтобы уменьшить пиковое потребление оперативной памяти. В частности, можно модифицировать следующие два аргумента: +> * `batch_size`: по умолчанию 1000, но выше было установлено значение 100. Попробуйте еще раз уменьшить в 2 раза до 50 +> * `writer_batch_size`: по умолчанию равен 1000. Попробуйте уменьшить его до 500, а если это не сработает, то уменьшите его еще раз в 2 раза до 250 Для упрощения обучения мы удалили из набора данных столбцы `audio` и `file`. Столбец `input_values` содержит закодированные аудиофайлы, @@ -452,10 +445,9 @@ training_args = TrainingArguments( ) ``` - - Здесь мы установили значение `push_to_hub=True`, чтобы включить автоматическую загрузку настроенных контрольных точек во время обучения. - Если вы не хотите, чтобы ваши контрольные точки загружались на Hugging Face Hub, вы можете установить значение `False`. - +> [!WARNING] +> Здесь мы установили значение `push_to_hub=True`, чтобы включить автоматическую загрузку настроенных контрольных точек во время обучения. +> Если вы не хотите, чтобы ваши контрольные точки загружались на Hugging Face Hub, вы можете установить значение `False`. Последнее, что нам необходимо сделать, это определить метрики. Поскольку набор данных сбалансирован, в качестве метрики мы будем использовать accuracy и загружать ее с помощью библиотеки 🤗 Evaluate: @@ -490,12 +482,9 @@ trainer = Trainer( trainer.train() ``` - - - В зависимости от используемого графического процессора, при запуске обучения возможно возникновение ошибки CUDA `"out-of-memory"`. - В этом случае можно уменьшать `batch_size` постепенно в 2 раза, а для компенсации использовать [`gradient_accumulation_steps`](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments.gradient_accumulation_steps) - - +> [!WARNING] +> В зависимости от используемого графического процессора, при запуске обучения возможно возникновение ошибки CUDA `"out-of-memory"`. +> В этом случае можно уменьшать `batch_size` постепенно в 2 раза, а для компенсации использовать [`gradient_accumulation_steps`](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments.gradient_accumulation_steps) **Output:** ```out diff --git a/chapters/ru/chapter5/evaluation.mdx b/chapters/ru/chapter5/evaluation.mdx index d6d8f749..6fb6c71e 100644 --- a/chapters/ru/chapter5/evaluation.mdx +++ b/chapters/ru/chapter5/evaluation.mdx @@ -275,10 +275,9 @@ common_voice_test = load_dataset( ) ``` - - Если вы столкнетесь с проблемой аутентификации при загрузке набора данных, убедитесь, что вы приняли условия использования набора данных на - Hub Hugging Face по следующей ссылке: https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 - +> [!TIP] +> Если вы столкнетесь с проблемой аутентификации при загрузке набора данных, убедитесь, что вы приняли условия использования набора данных на +> Hub Hugging Face по следующей ссылке: https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 Оценка по всему набору данных может быть выполнена так же, как и для одного примера - все, что нам нужно сделать, это **циклически** пройти по входным аудиофайлам, вместо вывода только одного образца. Для этого мы сначала преобразуем наш набор данных в `KeyDataset`. При этом выбирается @@ -306,10 +305,9 @@ for prediction in tqdm( all_predictions.append(prediction["text"]) ``` - - Если вы столкнетесь с ошибкой "Out-of-Memory" (OOM) CUDA при выполнении вышеуказанной ячейки, уменьшайте размер пакета (`batch_size`) - последовательно вдвое, пока не найдете такой размер пакета, который подходит для вашего устройства. - +> [!TIP] +> Если вы столкнетесь с ошибкой "Out-of-Memory" (OOM) CUDA при выполнении вышеуказанной ячейки, уменьшайте размер пакета (`batch_size`) +> последовательно вдвое, пока не найдете такой размер пакета, который подходит для вашего устройства. И, наконец, мы можем вычислить WER. Давайте сначала вычислим орфографический WER, то есть WER без какой-либо дополнительной обработки: diff --git a/chapters/ru/chapter5/fine-tuning.mdx b/chapters/ru/chapter5/fine-tuning.mdx index 8f89e9b7..b9b180e9 100644 --- a/chapters/ru/chapter5/fine-tuning.mdx +++ b/chapters/ru/chapter5/fine-tuning.mdx @@ -79,10 +79,9 @@ DatasetDict({ }) ``` - - Вы можете изменить идентификатор языка с `"dv"` на идентификатор языка по вашему выбору. Чтобы увидеть все возможные - языки в Common Voice 13, ознакомьтесь с карточкой набора данных на Hub Hugging Face: https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 - +> [!TIP] +> Вы можете изменить идентификатор языка с `"dv"` на идентификатор языка по вашему выбору. Чтобы увидеть все возможные +> языки в Common Voice 13, ознакомьтесь с карточкой набора данных на Hub Hugging Face: https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 Большинство наборов данных для распознавания речи предоставляют только аудиофайлы для ввода (`audio`) и соответствующий транскрибированный текст (`sentence`). В наборе данных Common Voice есть дополнительная метаинформация, такая как `accent` @@ -449,9 +448,8 @@ training_args = Seq2SeqTrainingArguments( ) ``` - - Если вы не хотите загружать контрольные точки модели на Hugging Face Hub, установите `push_to_hub=False`. - +> [!TIP] +> Если вы не хотите загружать контрольные точки модели на Hugging Face Hub, установите `push_to_hub=False`. Мы можем передать аргументы обучения в 🤗 Trainer вместе с нашей моделью, набором данных, сборщиком данных и функцией `compute_metrics`: diff --git a/chapters/ru/chapter6/fine-tuning.mdx b/chapters/ru/chapter6/fine-tuning.mdx index bf7d805d..d6be8746 100644 --- a/chapters/ru/chapter6/fine-tuning.mdx +++ b/chapters/ru/chapter6/fine-tuning.mdx @@ -11,12 +11,9 @@ nvidia-smi ``` - - -В нашем примере мы будем использовать около 40 часов обучающих данных. Если вы хотите повторить этот процесс, используя бесплатный тарифный план Google Colab, -необходимо уменьшить объем обучающих данных примерно до 10-15 часов и сократить количество шагов обучения. - - +> [!WARNING] +> В нашем примере мы будем использовать около 40 часов обучающих данных. Если вы хотите повторить этот процесс, используя бесплатный тарифный план Google Colab, +> необходимо уменьшить объем обучающих данных примерно до 10-15 часов и сократить количество шагов обучения. Вам также понадобятся некоторые дополнительные зависимости: diff --git a/chapters/ru/chapter6/pre-trained_models.mdx b/chapters/ru/chapter6/pre-trained_models.mdx index 5c915816..a906ae4e 100644 --- a/chapters/ru/chapter6/pre-trained_models.mdx +++ b/chapters/ru/chapter6/pre-trained_models.mdx @@ -38,13 +38,10 @@ SpeechT5 сначала проходит предварительное обуч Такой подход позволяет получить несколько моделей, дообученных для различных речевых задач, все они выигрывают от первоначального предварительного обучения на немаркированных данных. - - -Несмотря на то что в начале работы дообученные модели используют один и тот же набор весов из общей предварительно обученной модели, конечные версии -в итоге оказываются совершенно разными. Например, вы не можете взять дообученную ASR-модель и поменять местами пред-сети и пост-сети, -чтобы получить рабочую TTS-модель. SpeechT5 гибкая, но не настолько ;) - - +> [!TIP] +> Несмотря на то что в начале работы дообученные модели используют один и тот же набор весов из общей предварительно обученной модели, конечные версии +> в итоге оказываются совершенно разными. Например, вы не можете взять дообученную ASR-модель и поменять местами пред-сети и пост-сети, +> чтобы получить рабочую TTS-модель. SpeechT5 гибкая, но не настолько ;) Посмотрим, какие пред- и пост-сети использует SpeechT5 для решения задачи TTS: @@ -83,23 +80,20 @@ inputs = processor(text="Don't count the days, make the days count.", return_ten Модель SpeechT5 TTS не ограничивается созданием речи для одного диктора. Вместо этого она использует так называемые эмбединги диктора, которые фиксируют голосовые характеристики конкретного диктора. - - -Эмбеддинги диктора - это метод компактного представления личности диктора в виде вектора -фиксированного размера, независимо от длины высказывания. Эти эмбеддинги фиксируют важную информацию о голосе, -акценте, интонации и других уникальных характеристиках, отличающих одного диктора от другого. Такие эмбеддинги могут быть -использованы для верификации диктора, диаризации, идентификации диктора и т.д. - -Наиболее распространенными методами генерации эмбеддингов диктора являются: - -* I-векторы (I-Vectors): I-векторы (векторы идентичности) основаны на смешанной модели Гаусса (Gaussian mixture model, GMM). Они представляют говорящих в виде низкоразмерных векторов фиксированной длины, полученных на основе статистики GMM, специфичной для конкретного говорящего, и получаются неконтролируемым (unsupervised) способом. -* X-векторы (X-Vectors): X-векторы создаются с помощью глубоких нейронных сетей (DNN) и захватывают информацию о дикторе на уровне фрейма с учетом временного контекста. - -[X-Vectors](https://www.danielpovey.com/files/2018_icassp_xvectors.pdf) это современный метод, который показывает более высокую эффективность -на оценочных наборах данных по сравнению с I-векторами. Для получения X-векторов используется глубокая нейронная сеть: она обучается различать -дикторов и сопоставляет высказывания переменной длины с эмбеддингами фиксированной размерности. Также можно загрузить X-вектор заранее вычисленных эмбеддингов диктора, в котором будут заключены речевые характеристики конкретного диктора. - - +> [!TIP] +> Эмбеддинги диктора - это метод компактного представления личности диктора в виде вектора +> фиксированного размера, независимо от длины высказывания. Эти эмбеддинги фиксируют важную информацию о голосе, +> акценте, интонации и других уникальных характеристиках, отличающих одного диктора от другого. Такие эмбеддинги могут быть +> использованы для верификации диктора, диаризации, идентификации диктора и т.д. +> +> Наиболее распространенными методами генерации эмбеддингов диктора являются: +> +> * I-векторы (I-Vectors): I-векторы (векторы идентичности) основаны на смешанной модели Гаусса (Gaussian mixture model, GMM). Они представляют говорящих в виде низкоразмерных векторов фиксированной длины, полученных на основе статистики GMM, специфичной для конкретного говорящего, и получаются неконтролируемым (unsupervised) способом. +> * X-векторы (X-Vectors): X-векторы создаются с помощью глубоких нейронных сетей (DNN) и захватывают информацию о дикторе на уровне фрейма с учетом временного контекста. +> +> [X-Vectors](https://www.danielpovey.com/files/2018_icassp_xvectors.pdf) это современный метод, который показывает более высокую эффективность +> на оценочных наборах данных по сравнению с I-векторами. Для получения X-векторов используется глубокая нейронная сеть: она обучается различать +> дикторов и сопоставляет высказывания переменной длины с эмбеддингами фиксированной размерности. Также можно загрузить X-вектор заранее вычисленных эмбеддингов диктора, в котором будут заключены речевые характеристики конкретного диктора. Загрузим такие эмбединги диктора из набора датсета в Hub. Эмбединги были получены из [датасета CMU ARCTIC](http://www.festvox.org/cmu_arctic/) с помощью [этого скрипта](https://huggingface.co/mechanicalsea/speecht5-vc/blob/main/manifest/utils/prep_cmu_arctic_spkemb.py), но любой Х-вектор эмбединг должен работать. @@ -130,22 +124,19 @@ spectrogram = model.generate_speech(inputs["input_ids"], speaker_embeddings) Теоретически можно использовать любой вокодер, работающий с 80-биновыми мел-спектрограммами. Удобно, что 🤗 Transformers предлагает вокодер, основанный на HiFi-GAN. Его весовые коэффициенты были любезно предоставлены авторами SpeechT5. - - -[HiFi-GAN](https://arxiv.org/pdf/2010.05646v2.pdf) представляет собой современную генеративную состязательную сеть (generative adversarial network, GAN), разработанную -для высокоточного синтеза речи. Она способна генерировать высококачественные и реалистичные формы волны звука на основе спектрограмм. - -В общем случае HiFi-GAN состоит из одного генератора и двух дискриминаторов. Генератор представляет собой полностью сверточную -нейронную сеть, которая принимает на вход mel-спектрограмму и учится генерировать исходные аудиосигналы. Роль дискриминаторов -заключается в том, чтобы различать реальный и сгенерированный звук. Оба дискриминатора фокусируются на различных аспектах звука. - -HiFi-GAN обучается на большом наборе данных высококачественных аудиозаписей. В нем используется так называемое состязательное обучение, -при котором сети генератора и дискриминатора соревнуются друг с другом. Вначале генератор выдает низкокачественный звук и дискриминатор легко -отличает его от реального звука. В ходе обучения генератор улучшает свой выход, пытаясь обмануть дискриминатор. Дискриминатор, в свою очередь, -становится более точным в различении реального и сгенерированного звука. Такая состязательная обратная связь помогает обеим сетям совершенствоваться -с течением времени. В конечном итоге HiFi-GAN учится генерировать звук высокой достоверности, близкий по характеристикам к обучающим данным. - - +> [!TIP] +> [HiFi-GAN](https://arxiv.org/pdf/2010.05646v2.pdf) представляет собой современную генеративную состязательную сеть (generative adversarial network, GAN), разработанную +> для высокоточного синтеза речи. Она способна генерировать высококачественные и реалистичные формы волны звука на основе спектрограмм. +> +> В общем случае HiFi-GAN состоит из одного генератора и двух дискриминаторов. Генератор представляет собой полностью сверточную +> нейронную сеть, которая принимает на вход mel-спектрограмму и учится генерировать исходные аудиосигналы. Роль дискриминаторов +> заключается в том, чтобы различать реальный и сгенерированный звук. Оба дискриминатора фокусируются на различных аспектах звука. +> +> HiFi-GAN обучается на большом наборе данных высококачественных аудиозаписей. В нем используется так называемое состязательное обучение, +> при котором сети генератора и дискриминатора соревнуются друг с другом. Вначале генератор выдает низкокачественный звук и дискриминатор легко +> отличает его от реального звука. В ходе обучения генератор улучшает свой выход, пытаясь обмануть дискриминатор. Дискриминатор, в свою очередь, +> становится более точным в различении реального и сгенерированного звука. Такая состязательная обратная связь помогает обеим сетям совершенствоваться +> с течением времени. В конечном итоге HiFi-GAN учится генерировать звук высокой достоверности, близкий по характеристикам к обучающим данным. Загрузить вокодер так же просто, как и любую другую модель 🤗 Transformers. @@ -330,11 +321,8 @@ Audio(speech_output[2], rate=sampling_rate) Your browser does not support the audio element. - - -Bark, как и другие 🤗 модели трансформеров, могут быть оптимизированы всего в нескольких строках кода в отношении скорости и потребления памяти. Чтобы узнать, как это сделать, нажмите на [этот демонстрационный блокнот Colab](https://colab.research.google.com/github/ylacombe/notebooks/blob/main/Benchmark_Bark_HuggingFace.ipynb). - - +> [!TIP] +> Bark, как и другие 🤗 модели трансформеров, могут быть оптимизированы всего в нескольких строках кода в отношении скорости и потребления памяти. Чтобы узнать, как это сделать, нажмите на [этот демонстрационный блокнот Colab](https://colab.research.google.com/github/ylacombe/notebooks/blob/main/Benchmark_Bark_HuggingFace.ipynb). ## Massive Multilingual Speech (MMS) @@ -351,18 +339,15 @@ VITS - это сеть генерации речи, преобразующая В процессе инференса кодировки текста подвергаются апсемплингу и преобразуются в волновые формы с помощью потокового модуля и декодера HiFi-GAN. Это означает, что не нужно добавлять вокодер для инференса, он уже "встроен". - - -Модель MMS была добавлена в 🤗 Transformers совсем недавно, поэтому ее API может немного измениться. На момент выхода этого раздела -MMS для TTS планируется интегрировать в Transformers `main` через пару дней. -После слияния вам придется установить библиотеку из исходного кода. Тем временем, если вы хотите попробовать следующий пример инференса, -вы можете установить модель из ветки PR: - -```bash -pip install git+https://github.com/hollance/transformers.git@6900e8ba6532162a8613d2270ec2286c3f58f57b -``` - - +> [!WARNING] +> Модель MMS была добавлена в 🤗 Transformers совсем недавно, поэтому ее API может немного измениться. На момент выхода этого раздела +> MMS для TTS планируется интегрировать в Transformers `main` через пару дней. +> После слияния вам придется установить библиотеку из исходного кода. Тем временем, если вы хотите попробовать следующий пример инференса, +> вы можете установить модель из ветки PR: +> +> ```bash +> pip install git+https://github.com/hollance/transformers.git@6900e8ba6532162a8613d2270ec2286c3f58f57b +> ``` Давайте попробуем использовать MMS и посмотрим, как можно синтезировать речь на языке, отличном от английского, например, на немецком. Сначала загрузим контрольную точку модели и токенизатор для нужного языка: diff --git a/chapters/ru/chapter7/hands-on.mdx b/chapters/ru/chapter7/hands-on.mdx index b4410094..9973990e 100644 --- a/chapters/ru/chapter7/hands-on.mdx +++ b/chapters/ru/chapter7/hands-on.mdx @@ -24,13 +24,12 @@ дообученную на голландском фрагменте набора данных [VoxPopuli](https://huggingface.co/datasets/facebook/voxpopuli) [NL], либо контрольная точка MMS TTS (смотрите раздел [предварительно обученные модели text-to-speech](../chapter6/pre-trained_models)). - - По нашему опыту экспериментов с голландским языком, использование контрольной точки MMS TTS дает лучшую производительность, чем - с дообученной SpeechT5, но вы можете обнаружить, что ваша дообученная контрольная точка TTS предпочтительнее для вашего языка. - Если вы решили использовать контрольную точку MMS TTS, вам необходимо обновить requirements.txt - файл вашей демонстрации для установки transformers из ветки PR: -

git+https://github.com/hollance/transformers.git@6900e8ba6532162a8613d2270ec2286c3f58f57b

-
+> [!TIP] +> По нашему опыту экспериментов с голландским языком, использование контрольной точки MMS TTS дает лучшую производительность, чем +> с дообученной SpeechT5, но вы можете обнаружить, что ваша дообученная контрольная точка TTS предпочтительнее для вашего языка. +> Если вы решили использовать контрольную точку MMS TTS, вам необходимо обновить requirements.txt +> файл вашей демонстрации для установки transformers из ветки PR: +>

git+https://github.com/hollance/transformers.git@6900e8ba6532162a8613d2270ec2286c3f58f57b

Ваше демо должно принимать на вход аудиофайл, а на выходе возвращать другой аудиофайл, соответствующий сигнатуре функции [`speech_to_speech_translation`](https://huggingface.co/spaces/course-demos/speech-to-speech-translation/blob/3946ba6705a6632a63de8672ac52a482ab74b3fc/app.py#L35) [NL] в шаблоне демо. diff --git a/chapters/ru/chapter7/speech-to-speech.mdx b/chapters/ru/chapter7/speech-to-speech.mdx index 39039d63..d3fbfc3f 100644 --- a/chapters/ru/chapter7/speech-to-speech.mdx +++ b/chapters/ru/chapter7/speech-to-speech.mdx @@ -86,14 +86,11 @@ def translate(audio): return outputs["text"] ``` - - - Whisper также можно "обманом" заставить перевести речь на любом языке X на любой язык Y. Просто задайте задачу `"transcribe"`, а `"language"` - целевым языком - в аргументах ключевых слов генерации, например, для испанского языка можно задать: - - `generate_kwargs={"task": "transcribe", "language": "es"}` - - +> [!TIP] +> Whisper также можно "обманом" заставить перевести речь на любом языке X на любой язык Y. Просто задайте задачу `"transcribe"`, а `"language"` - целевым языком +> в аргументах ключевых слов генерации, например, для испанского языка можно задать: +> +> `generate_kwargs={"task": "transcribe", "language": "es"}` Отлично! Давайте быстро проверим, что мы получаем разумный результат от модели: @@ -138,11 +135,10 @@ model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts") vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan") ``` - - Здесь мы используем контрольную точку SpeechT5, обученную специально для английского TTS. Если вы хотите перевести - на язык, отличный от английского, либо замените контрольную точку на модель SpeechT5 TTS, дообученную для выбранного - вами языка, либо используйте контрольную точку MMS TTS, предварительно обученную для вашего целевого языка. - +> [!TIP] +> Здесь мы используем контрольную точку SpeechT5, обученную специально для английского TTS. Если вы хотите перевести +> на язык, отличный от английского, либо замените контрольную точку на модель SpeechT5 TTS, дообученную для выбранного +> вами языка, либо используйте контрольную точку MMS TTS, предварительно обученную для вашего целевого языка. Как и в случае с моделью Whisper, модель SpeechT5 и вокодер мы поместим на GPU-ускоритель, если он у нас есть: diff --git a/chapters/ru/chapter7/transcribe-meeting.mdx b/chapters/ru/chapter7/transcribe-meeting.mdx index ccc83d20..7169f8c2 100644 --- a/chapters/ru/chapter7/transcribe-meeting.mdx +++ b/chapters/ru/chapter7/transcribe-meeting.mdx @@ -160,11 +160,10 @@ pipeline = ASRDiarizationPipeline( ) ``` - - Вы также можете инстанцировать ASRDiarizationPipeline directly непосредственно из предварительно обученных моделей, указав идентификатор - модели ASR на Hub: -

pipeline = ASRDiarizationPipeline.from_pretrained("openai/whisper-base")

-
+> [!TIP] +> Вы также можете инстанцировать ASRDiarizationPipeline directly непосредственно из предварительно обученных моделей, указав идентификатор +> модели ASR на Hub: +>

pipeline = ASRDiarizationPipeline.from_pretrained("openai/whisper-base")

Передадим аудиофайл в композитный конвейер и посмотрим, что получится в результате: diff --git a/chapters/ru/chapter7/voice-assistant.mdx b/chapters/ru/chapter7/voice-assistant.mdx index a5499520..d767a8be 100644 --- a/chapters/ru/chapter7/voice-assistant.mdx +++ b/chapters/ru/chapter7/voice-assistant.mdx @@ -47,11 +47,10 @@ Опять же, мы делали это уже несколько раз, так что процесс будет очень знакомым! - - Следующий раздел требует использования микрофона для записи голосового ввода. Поскольку машины Google Colab не совместимы с микрофоном, - рекомендуется выполнять этот раздел локально, либо на CPU, либо на GPU, если у вас есть доступ к локальным ресурсам. Размеры контрольных точек - были выбраны достаточно малыми для достаточно быстрой работы на CPU, поэтому Вы получите хорошую производительность и без GPU. - +> [!TIP] +> Следующий раздел требует использования микрофона для записи голосового ввода. Поскольку машины Google Colab не совместимы с микрофоном, +> рекомендуется выполнять этот раздел локально, либо на CPU, либо на GPU, если у вас есть доступ к локальным ресурсам. Размеры контрольных точек +> были выбраны достаточно малыми для достаточно быстрой работы на CPU, поэтому Вы получите хорошую производительность и без GPU. ## Обнаружение слова активации @@ -221,11 +220,10 @@ transcriber = pipeline( ) ``` - - Если вы используете GPU, вы можете увеличить размер контрольной точки, чтобы использовать контрольную точку Whisper Small English, - которая обеспечит более высокую точность транскрипции и при этом не превысит требуемый порог задержки. - Просто поменяйте id модели на: "openai/whisper-small.en". - +> [!TIP] +> Если вы используете GPU, вы можете увеличить размер контрольной точки, чтобы использовать контрольную точку Whisper Small English, +> которая обеспечит более высокую точность транскрипции и при этом не превысит требуемый порог задержки. +> Просто поменяйте id модели на: "openai/whisper-small.en". Теперь мы можем определить функцию для записи сигнала с микрофона и транскрипции соответствующего текста. С помощью вспомогательной функции `ffmpeg_microphone_live` мы можем управлять тем, насколько `в реальном времени` работает наша модель распознавания речи. @@ -439,10 +437,9 @@ agent.run("Generate an image of a cat") - - Обратите внимание, что при первом вызове будет произведена загрузка весов модели, что может занять некоторое время - в зависимости от скорости загрузки. - +> [!TIP] +> Обратите внимание, что при первом вызове будет произведена загрузка весов модели, что может занять некоторое время +> в зависимости от скорости загрузки. Все просто! Агент интерпретировал наш запрос и, используя [Stable Diffusion](https://huggingface.co/docs/diffusers/using-diffusers/conditional_image_generation) под капотом, сгенерировал изображение, при этом нам не пришлось беспокоиться о загрузке модели, написании функции или выполнении кода. diff --git a/chapters/tr/chapter0/get_ready.mdx b/chapters/tr/chapter0/get_ready.mdx index bd6951df..fccb0b78 100644 --- a/chapters/tr/chapter0/get_ready.mdx +++ b/chapters/tr/chapter0/get_ready.mdx @@ -24,11 +24,8 @@ Kurs materyallerini incelemek için ihtiyacınız olanlar şunlardır: - İnternet bağlantısı olan bir bilgisayar. - Uygulamalı eğitim egzersizleri için [Google Colab](https://colab.research.google.com) gereklidir. Ücretsiz sürüm yeterlidir. Daha önce Google Colab kullanmadıysanız, bu [resmi tanıtım not defterini](https://colab.research.google.com/notebooks/intro.ipynb) inceleyebilirsiniz. - - -Ücretsiz Google Colab seçeneği yerine, kendi yerel kurulumunuzu veya Kaggle Notebook'larını kullanabilirsiniz. Kaggle Notebook'lar, sabit bir GPU saat sayısı sunar ve Google Colab ile benzer işlevselliğe sahiptir, ancak 🤗 Hub'da modellerinizi paylaşma konusunda farklılıklar bulunmaktadır (örneğin, görevleri tamamlamak için). Kaggle Notebook'larınızı tercih etmeye karar verirseniz, [@michaelshekasta](https://github.com/michaelshekasta) tarafından oluşturulan bu [örnek Kaggle not defterine](https://www.kaggle.com/code/michaelshekasta/test-notebook) göz atabilirsiniz. Bu not defteri, nasıl model eğitebileceğinizi ve eğitilmiş modelinizi 🤗 Hub'da nasıl paylaşabileceğinizi göstermektedir. - - +> [!TIP] +> Ücretsiz Google Colab seçeneği yerine, kendi yerel kurulumunuzu veya Kaggle Notebook'larını kullanabilirsiniz. Kaggle Notebook'lar, sabit bir GPU saat sayısı sunar ve Google Colab ile benzer işlevselliğe sahiptir, ancak 🤗 Hub'da modellerinizi paylaşma konusunda farklılıklar bulunmaktadır (örneğin, görevleri tamamlamak için). Kaggle Notebook'larınızı tercih etmeye karar verirseniz, [@michaelshekasta](https://github.com/michaelshekasta) tarafından oluşturulan bu [örnek Kaggle not defterine](https://www.kaggle.com/code/michaelshekasta/test-notebook) göz atabilirsiniz. Bu not defteri, nasıl model eğitebileceğinizi ve eğitilmiş modelinizi 🤗 Hub'da nasıl paylaşabileceğinizi göstermektedir. ## 5.Adım Topluluğa katılın! diff --git a/chapters/tr/chapter1/audio_data.mdx b/chapters/tr/chapter1/audio_data.mdx index 8d31be5f..04028f00 100644 --- a/chapters/tr/chapter1/audio_data.mdx +++ b/chapters/tr/chapter1/audio_data.mdx @@ -125,9 +125,8 @@ DFT'nin çıktısı, gerçel ve sanal bileşenlerden oluşan karmaşık sayılar Genlik değerlerini desibel ölçeğine dönüştürmek için `librosa.amplitude_to_db()` kullandınız, böylece spektrumdaki daha ince ayrıntıları görmeyi kolaylaştırdınız. Bazen insanlar genlik yerine enerjiyi ölçen **güç spektrumunu** kullanır; bu basitçe genlik değerlerinin karesi olan bir spektrumdur. - -💡 Pratikte insanlar FFT terimini DFT ile birbirinin yerine kullanırlar çünkü FFT veya Hızlı Fourier Dönüşümü DFT'yi bilgisayarda hesaplamak için tek etkili yöntemdir. - +> [!TIP] +> 💡 Pratikte insanlar FFT terimini DFT ile birbirinin yerine kullanırlar çünkü FFT veya Hızlı Fourier Dönüşümü DFT'yi bilgisayarda hesaplamak için tek etkili yöntemdir. Bir ses sinyalinin frekans spektrumu, dalga formuyla aynı bilgiyi içerir. Bunlar aynı veriye (burada trompet sesinden ilk 4096 örnek) bakmanın iki farklı yoludur. Dalga formunun genliğe karşı zaman içinde çizdiği yerde, spektrum her bir frekansın genliğini sabit bir noktada görselleştirir. @@ -203,9 +202,8 @@ Yukarıdaki örnekte, `n_mels`, oluşturulacak mel bantlarının sayısını ifa Düzenli bir spektrogramda olduğu gibi, mel frekans bileşenlerinin gücünü desibel cinsinden ifade etmek yaygın bir uygulamadır. Bu genellikle bir **log-mel spektrogramu* olarak adlandırılır, çünkü desibellere dönüştürme işlemi logaritmik bir işlem içerir. Yukarıdaki örnek, `librosa.feature.melspectrogram()` olarak `librosa.power_to_db()` kullanarak güç spektrogramı oluşturur. - -💡 Tüm mel spektrogramları aynı değildir! Yaygın olarak kullanılan iki farklı mel ölçeği vardır ("htk" ve "slaney"), ve güç spektrogramı yerine genlik spektrogramı kullanılabilir. Log-mel spektrogramına dönüştürme işlemi her zaman gerçek desibelleri hesaplamaz, sadece `log`larını alabilir. Bu nedenle, bir makine öğrenme modeli girdi olarak bir mel spektrogramı bekliyorsa, bunu aynı şekilde hesapladığınızdan emin olmak için kontrol edin. - +> [!TIP] +> 💡 Tüm mel spektrogramları aynı değildir! Yaygın olarak kullanılan iki farklı mel ölçeği vardır ("htk" ve "slaney"), ve güç spektrogramı yerine genlik spektrogramı kullanılabilir. Log-mel spektrogramına dönüştürme işlemi her zaman gerçek desibelleri hesaplamaz, sadece `log`larını alabilir. Bu nedenle, bir makine öğrenme modeli girdi olarak bir mel spektrogramı bekliyorsa, bunu aynı şekilde hesapladığınızdan emin olmak için kontrol edin. Bir mel spektrogramı oluşturmak, sinyali filtrelemeyi içerdiği için kayıplı bir işlemdir. Bir mel spektrogramını bir dalga formuna dönüştürmek, bir düzenli spektrogram için bunu yapmaktan daha zordur, çünkü daha önce kaybedilen frekansları tahmin etmeyi gerektirir. Bu nedenle, mel spektrogramından bir dalga formu üretmek için HiFiGAN vokoder gibi makine öğrenme modellerine ihtiyaç vardır. diff --git a/chapters/tr/chapter1/preprocessing.mdx b/chapters/tr/chapter1/preprocessing.mdx index cb345b3d..20a2ea57 100644 --- a/chapters/tr/chapter1/preprocessing.mdx +++ b/chapters/tr/chapter1/preprocessing.mdx @@ -59,9 +59,8 @@ minds[0] Dizi değerlerinin de artık farklı olduğunu fark edebilirsiniz. Bunun nedeni artık iki kat daha fazla genlik değerine sahip olmamızdır. daha önce sahip olduğumuz her biri. - -💡 Örnekleme hakkında bazı temel bilgiler: Bir ses sinyali 8 kHz'de örneklenmişse, yani saniyede 8000 örnek okuma alıyorsak, sesin 4 kHz'nin üzerinde herhangi bir frekanseyi içermediğini biliyoruz. Bu, Nyquist örnekleme teoremi tarafından garanti edilir. Bu nedenle, örnekleme noktaları arasında, orijinal sürekli sinyalin her zaman düzgün bir eğri oluşturduğundan emin olabiliriz. Örnekleme hızını daha yüksek bir örnekleme hızına yükseltmek, bu eğriyi yaklaşıklayarak mevcut olanların arasına ek örnek değerlerini hesaplamakla ilgilidir. Bununla birlikte, örnekleme hızını düşürmek, önce yeni Nyquist sınırından daha yüksek frekanstaki herhangi bir frekansı filtrelemeyi gerektirir, ardından yeni örnek noktalarını tahmin etmeden önce. Yani, örnekleme hızını 2x faktörüyle sadece her diğer örneği atarak düşüremezsiniz - bu, sinyalde bozulmalara yol açan aliaslar adı verilen bozulmaları oluşturur. Örnekleme işlemini doğru bir şekilde yapmak karmaşıktır ve en iyi test edilmiş kütüphanelere, örneğin librosa veya 🤗 Datasets gibi kütüphanelere bırakılmalıdır. - +> [!TIP] +> 💡 Örnekleme hakkında bazı temel bilgiler: Bir ses sinyali 8 kHz'de örneklenmişse, yani saniyede 8000 örnek okuma alıyorsak, sesin 4 kHz'nin üzerinde herhangi bir frekanseyi içermediğini biliyoruz. Bu, Nyquist örnekleme teoremi tarafından garanti edilir. Bu nedenle, örnekleme noktaları arasında, orijinal sürekli sinyalin her zaman düzgün bir eğri oluşturduğundan emin olabiliriz. Örnekleme hızını daha yüksek bir örnekleme hızına yükseltmek, bu eğriyi yaklaşıklayarak mevcut olanların arasına ek örnek değerlerini hesaplamakla ilgilidir. Bununla birlikte, örnekleme hızını düşürmek, önce yeni Nyquist sınırından daha yüksek frekanstaki herhangi bir frekansı filtrelemeyi gerektirir, ardından yeni örnek noktalarını tahmin etmeden önce. Yani, örnekleme hızını 2x faktörüyle sadece her diğer örneği atarak düşüremezsiniz - bu, sinyalde bozulmalara yol açan aliaslar adı verilen bozulmaları oluşturur. Örnekleme işlemini doğru bir şekilde yapmak karmaşıktır ve en iyi test edilmiş kütüphanelere, örneğin librosa veya 🤗 Datasets gibi kütüphanelere bırakılmalıdır. ## Veri kümesini filtreleme diff --git a/chapters/zh-CN/chapter1/audio_data.mdx b/chapters/zh-CN/chapter1/audio_data.mdx index 86dd45ff..d6d79c14 100644 --- a/chapters/zh-CN/chapter1/audio_data.mdx +++ b/chapters/zh-CN/chapter1/audio_data.mdx @@ -117,9 +117,8 @@ plt.xscale("log") 我们使用了`librosa.amplitude_to_db()`函数将幅值转换为了分贝标度,方便我们观察频谱的细节。有时人们也使用测量能量而非幅值的**能量谱**(power spectrogram),其值为幅值的平方。 - -💡 在实践中,人们往往将快速傅里叶变换(Fast Fourier Transform, FFT)和离散傅里叶变换(Discrete Fourier Transform, DFT)这两个名词等价使用,这是因为FFT是在计算机中可以高效计算DFT的唯一方法。 - +> [!TIP] +> 💡 在实践中,人们往往将快速傅里叶变换(Fast Fourier Transform, FFT)和离散傅里叶变换(Discrete Fourier Transform, DFT)这两个名词等价使用,这是因为FFT是在计算机中可以高效计算DFT的唯一方法。 音频信号的频谱和其波形所包含的信息其实完全相同,他们只是相同数据的不同表示方法(这里均表示该小号音频的前4096个样本)。两者的区别在于波形表示的是幅值随着时间的变化,而频谱表示的是各个频率成分在该时间段内的强度。 @@ -186,9 +185,8 @@ plt.colorbar() 和标准频谱一样,我们也会将梅尔频率成分的强度转化为分贝标度。由于分贝的转化过程涉及到对数运算,转化后的梅尔谱通常被称为**对数梅尔时频谱**(log-mel spectrum)。在上面示例中,我们使用`librosa.power_to_db()`函数和`librosa.feature.melspectrogram()`来生成能量对数梅尔时频谱。 - -💡 梅尔视频谱间也有各种区别!有两种常用的mel计算标度("htk" 和 "slaney"),此外还有能量谱和幅度谱的区别。对数梅尔谱的转换有时仅仅是简单计算`对数`而不会完整转化为分贝标度。因此,在使用以梅尔谱作为输入的机器学习模型时,我们建议你检查梅尔谱的计算过程是否完全一致。 - +> [!TIP] +> 💡 梅尔视频谱间也有各种区别!有两种常用的mel计算标度("htk" 和 "slaney"),此外还有能量谱和幅度谱的区别。对数梅尔谱的转换有时仅仅是简单计算`对数`而不会完整转化为分贝标度。因此,在使用以梅尔谱作为输入的机器学习模型时,我们建议你检查梅尔谱的计算过程是否完全一致。 由于梅尔谱的计算过程中需要对信号进行滤波,梅尔谱的计算是一个有损过程。将梅尔谱转化回波形比将标准时频谱转化回波形更加困难,因为我们需要估计在滤波过程中丢失的频率成分。这就是为何我们需要HiFiGAN声码器等机器学习模型来将梅尔谱转化回波形。 diff --git a/chapters/zh-CN/chapter1/preprocessing.mdx b/chapters/zh-CN/chapter1/preprocessing.mdx index c047eda0..53d0cc67 100644 --- a/chapters/zh-CN/chapter1/preprocessing.mdx +++ b/chapters/zh-CN/chapter1/preprocessing.mdx @@ -53,9 +53,8 @@ minds[0] 你可能注意到数列的值也有所变化。这是因为我们在重采样后会获得两倍于原来采样点数量的数值。 - -💡 关于重采样的背景知识:当信号的原始采样率为8千赫兹时,信号每秒钟会包含8000个采样点,并且我们知道该信号不会包含高于4千赫兹的频率成分。奈奎斯特采样定理保证了这一点。也因此,我们可以确保在两个采样点中间的原始连续信号呈一条平滑的曲线。在这样的条件下,上采样所要做的就只是根据对曲线的估计而计算出两个采样点中间的额外数值。与之相反的是,下采样过程需要我们首先过滤掉所有高于奈奎斯特极限的频率成分,之后才能重新计算采样点。也就是说,我们不能通过简单的每隔一个采样点丢弃一个采样点来进行2倍的下采样:这会造成信号的失真,我们称之为混叠失真。重采样过程十分棘手,因此我们推荐使用经过测试的工具库,例如librosa或🤗 Datasets。 - +> [!TIP] +> 💡 关于重采样的背景知识:当信号的原始采样率为8千赫兹时,信号每秒钟会包含8000个采样点,并且我们知道该信号不会包含高于4千赫兹的频率成分。奈奎斯特采样定理保证了这一点。也因此,我们可以确保在两个采样点中间的原始连续信号呈一条平滑的曲线。在这样的条件下,上采样所要做的就只是根据对曲线的估计而计算出两个采样点中间的额外数值。与之相反的是,下采样过程需要我们首先过滤掉所有高于奈奎斯特极限的频率成分,之后才能重新计算采样点。也就是说,我们不能通过简单的每隔一个采样点丢弃一个采样点来进行2倍的下采样:这会造成信号的失真,我们称之为混叠失真。重采样过程十分棘手,因此我们推荐使用经过测试的工具库,例如librosa或🤗 Datasets。 ## 过滤数据集 diff --git a/chapters/zh-CN/chapter3/classification.mdx b/chapters/zh-CN/chapter3/classification.mdx index e99d1db5..578e0e8c 100644 --- a/chapters/zh-CN/chapter3/classification.mdx +++ b/chapters/zh-CN/chapter3/classification.mdx @@ -18,9 +18,8 @@ 图像来自论文[AST: Audio Spectrogram Transformer](https://arxiv.org/pdf/2104.01778.pdf) - -💡 尽管在这里我们假装时频谱与图像相同,但它们之间其实有重要的区别。例如,将图像的内容上下平移通常不会改变图像中的内容的含义。然而,将时频谱上下平移将改变声音中的频率,并完全改变其特性。图像具有一定的上下和左右平移不变性(shift-invariance),但时频谱仅具有一定的左右平移不变性,而几乎完全不具有上下平移不变性。尽管在实践中,将时频谱视为图像可以得到很好的效果,但请记住它们在原理上完全不同。 - +> [!TIP] +> 💡 尽管在这里我们假装时频谱与图像相同,但它们之间其实有重要的区别。例如,将图像的内容上下平移通常不会改变图像中的内容的含义。然而,将时频谱上下平移将改变声音中的频率,并完全改变其特性。图像具有一定的上下和左右平移不变性(shift-invariance),但时频谱仅具有一定的左右平移不变性,而几乎完全不具有上下平移不变性。尽管在实践中,将时频谱视为图像可以得到很好的效果,但请记住它们在原理上完全不同。 ## 任何Transformer都可以是分类器 diff --git a/chapters/zh-CN/chapter3/ctc.mdx b/chapters/zh-CN/chapter3/ctc.mdx index 96a320c1..d1672ad6 100644 --- a/chapters/zh-CN/chapter3/ctc.mdx +++ b/chapters/zh-CN/chapter3/ctc.mdx @@ -15,9 +15,8 @@ CTC结构(Connectionist Temporal Classification)是一种仅使用Transforme 棘手的问题在于:在语音识别中,我们不知道音频输入和文本输出的**对齐方式**(alignment)。我们知道语音的顺序与文本的转录顺序相同(对齐是单调的),但我们不知道转录文本中的字符如何与音频对齐。这就是CTC算法的用武之地。 - -💡 在NLP模型中,词汇表(vocabulary,也称字典)通常由数千个标记组成,这些标记不仅描述单个字符,还描述单词的部分甚至整个单词。然而对于CTC模型,较小的词汇表效果最好,我们一般会将其保持在50个字符以下。我们不关心字母的大小写,因此仅使用大写(或仅使用小写)就足够了。数字则被拼写为单词,例如`"20"`变成`"twenty"`。除了字母,我们还需要至少一个单词分隔符标记(空格,space)和一个填充标记(padding token)。与NLP模型一样,填充标记允许我们将多个示例组合成批次,但它也是模型将预测的沉默标记。在英语中,保留`'`字符也很有用——毕竟,`"it's"`和`"its"`的含义完全不同。 - +> [!TIP] +> 💡 在NLP模型中,词汇表(vocabulary,也称字典)通常由数千个标记组成,这些标记不仅描述单个字符,还描述单词的部分甚至整个单词。然而对于CTC模型,较小的词汇表效果最好,我们一般会将其保持在50个字符以下。我们不关心字母的大小写,因此仅使用大写(或仅使用小写)就足够了。数字则被拼写为单词,例如`"20"`变成`"twenty"`。除了字母,我们还需要至少一个单词分隔符标记(空格,space)和一个填充标记(padding token)。与NLP模型一样,填充标记允许我们将多个示例组合成批次,但它也是模型将预测的沉默标记。在英语中,保留`'`字符也很有用——毕竟,`"it's"`和`"its"`的含义完全不同。 ## 我们到底该怎么对齐呢? @@ -99,9 +98,8 @@ BRION SAW SOMETHING CLOSE TO PANIC ON HIS OPPONENT'S FACE WHEN THE MAN FINALLY R 总结一下,CTC模型对应每20毫秒的输入音频(包括部分重叠)会生成一个预测标记。这样的预测规则会生成很多重复的字母。利用CTC空白标记,我们可以轻松地移除这些重复的字母,而不会破坏单词的正确拼写。这是一种非常简单和方便的方法,可以解决输出文本与输入音频的对齐问题。 - -💡 在实际的Wav2Vec2模型中,CTC空白标记与填充标记``是相同的。模型会预测很多这样的``标记,例如当当前20毫秒的音频没有明确的字符可以预测时。使用相同的标记作为填充和CTC空白标记可以简化解码算法,并有助于保持词汇表的小规模。 - +> [!TIP] +> 💡 在实际的Wav2Vec2模型中,CTC空白标记与填充标记``是相同的。模型会预测很多这样的``标记,例如当当前20毫秒的音频没有明确的字符可以预测时。使用相同的标记作为填充和CTC空白标记可以简化解码算法,并有助于保持词汇表的小规模。 我们可以在Transomer编码模型简单地加入CTC:将编码器的输出序列进入一个线性层,该线性层将音频特征映射到词汇表。模型使用特殊的CTC损失进行训练。 diff --git a/chapters/zh-CN/chapter3/introduction.mdx b/chapters/zh-CN/chapter3/introduction.mdx index a5d0b9d5..2b5ebc37 100644 --- a/chapters/zh-CN/chapter3/introduction.mdx +++ b/chapters/zh-CN/chapter3/introduction.mdx @@ -89,9 +89,8 @@ Transformer架构会输出一个隐藏状态向量(hidden-state vectors)的 SpeechT5 outputs a spectrogram and uses a vocoder to create the waveform - -💡如果我们对波形进行短时傅里叶变换(STFT)得到其时频谱,我们可以通过其逆向过程(ISTFT)重新得到原始的波形。这是因为STFT生成的时频谱包含了幅值和相位两部分的信息,而这两部分信息都是重建波形所必需的。然而,音频深度学习模型所生成的时频谱往往仅包含了幅值信息。为了将这样的时频谱转化为波形,我们需要通过某种方式估计其相位信息,这便是声码器的功能。 - +> [!TIP] +> 💡如果我们对波形进行短时傅里叶变换(STFT)得到其时频谱,我们可以通过其逆向过程(ISTFT)重新得到原始的波形。这是因为STFT生成的时频谱包含了幅值和相位两部分的信息,而这两部分信息都是重建波形所必需的。然而,音频深度学习模型所生成的时频谱往往仅包含了幅值信息。为了将这样的时频谱转化为波形,我们需要通过某种方式估计其相位信息,这便是声码器的功能。 ### 波形输出 diff --git a/chapters/zh-CN/chapter3/seq2seq.mdx b/chapters/zh-CN/chapter3/seq2seq.mdx index a6c4f4b5..f4f50f54 100644 --- a/chapters/zh-CN/chapter3/seq2seq.mdx +++ b/chapters/zh-CN/chapter3/seq2seq.mdx @@ -19,9 +19,8 @@ Seq2Seq结构中的解码器(decoder)和编码器(encoder)非常类似 看起来十分熟悉。左边是**Transformer编码器**。它以对数梅尔时频谱(log-mel spectrogram)作为输入,并对其进行编码,输出一个编码器隐藏状态序列(sequence of encoder hidden states),从中提取出输入语音的重要特征。这个隐藏状态张量代表了整个输入序列,并高效地编码了输入语音的“含义”。 - -💡 这些seq2seq模型通常使用时频谱作为输入。不过,seq2seq模型也可以直接使用音频波形作为输入。 - +> [!TIP] +> 💡 这些seq2seq模型通常使用时频谱作为输入。不过,seq2seq模型也可以直接使用音频波形作为输入。 编码器的输出被传递到右边的**Transformer解码器**,使用一种称为**交叉注意力**(cross-attention)的机制。交叉注意力与自注意力类似,不过此处注意力的对象是编码器的输出。在这之后,我们就不再需要编码器了。 @@ -34,9 +33,8 @@ Seq2Seq结构中的解码器(decoder)和编码器(encoder)非常类似 在这个架构中,解码器的功能类似于**语言模型**(language model),它处理编码器的隐藏状态表示,并生成相应的文本转录。这种方法比CTC更强大,甚至CTC结合外部语言模型也无法相比。Seq2Seq结构可以使用相同的训练数据和损失函数进行端到端(end-to-end)训练,从而提供更好的灵活性和更好的性能。 - -💡 与CTC模型输出单个字母的序列不同,Whisper模型的输出是完整的单词或词素。它使用GPT-2的分词器,有50k+个不同的标记。因此,seq2seq模型可以输出比CTC模型更短的序列。 - +> [!TIP] +> 💡 与CTC模型输出单个字母的序列不同,Whisper模型的输出是完整的单词或词素。它使用GPT-2的分词器,有50k+个不同的标记。因此,seq2seq模型可以输出比CTC模型更短的序列。 Seq2Seq语音识别模型最常使用的损失函数为交叉熵损失(cross-entropy loss),因为我们通常把模型最终层的输出视为一个标记的概率分布。交叉熵损失通常与[束搜索(beam search)](https://huggingface.co/blog/how-to-generate)等技术结合使用,生成最终的序列。语音识别的度量标准是WER(word error rate),它衡量将预测文本转换为目标文本所需的替换、插入和删除的数量。所需的操作越少说明预测结果越好。 @@ -44,9 +42,8 @@ Seq2Seq语音识别模型最常使用的损失函数为交叉熵损失(cross-e 可能不会让你感到惊讶的是, Seq2Seq的TTS模型基本上和ASR模型相同,仅仅是将输入和输出的数据种类互换!Transformer编码器接收文本标记序列,并提取表示输入文本的隐藏状态序列。Transformer解码器使用交叉注意力机制,预测输出的时频谱序列。 - -💡 时频谱图是通过对音频波形的连续时间切片进行频谱分析得到的。换句话说,时频谱图是一个序列,其中的元素是(对数梅尔)频谱,每个时间步一个。 - +> [!TIP] +> 💡 时频谱图是通过对音频波形的连续时间切片进行频谱分析得到的。换句话说,时频谱图是一个序列,其中的元素是(对数梅尔)频谱,每个时间步一个。 在ASR模型里,解码器的初始输出序列是一个仅包含“起始”标记的序列。而在TTS模型里,我们可以使用一个长度为1,值全为0的时频谱序列来代替“起始”标记。有了这个初始时频谱序列和编码器隐藏状态的交叉注意力,解码器就可以预测下一个时间步的时频谱,逐步增长时频谱序列。 diff --git a/chapters/zh-CN/chapter5/evaluation.mdx b/chapters/zh-CN/chapter5/evaluation.mdx index d4c68bcf..d8b30077 100644 --- a/chapters/zh-CN/chapter5/evaluation.mdx +++ b/chapters/zh-CN/chapter5/evaluation.mdx @@ -243,9 +243,8 @@ common_voice_test = load_dataset( ) ``` - - 如果在加载数据集时遇到身份验证问题,请确保您已经通过以下链接在 Hugging Face Hub 上接受了数据集的使用条款:https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 - +> [!TIP] +> 如果在加载数据集时遇到身份验证问题,请确保您已经通过以下链接在 Hugging Face Hub 上接受了数据集的使用条款:https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 用与推理单个样本相同的方式评估整个数据集——我们所要做的就是**循环**处理输入的所有音频,而不是仅推理单个样本。为此,我们首先将我们的数据集转换为 `KeyDataset`。 我们要做的只是挑选出我们要传递给模型的特定的数据列(在我们的案例中,那是 `"audio"` 列),忽略其余的(如目标转写,在推理时用不上)。 @@ -270,9 +269,8 @@ for prediction in tqdm( all_predictions.append(prediction["text"]) ``` - - 如果在运行上述单元时遇到 CUDA 内存不足(out-of-memory, OOM)问题,请将 `batch_size` 逐渐减小 2 的倍数,直到找到适合您设备的批次大小。 - +> [!TIP] +> 如果在运行上述单元时遇到 CUDA 内存不足(out-of-memory, OOM)问题,请将 `batch_size` 逐渐减小 2 的倍数,直到找到适合您设备的批次大小。 最后,我们可以计算 WER。让我们首先计算正字法 WER,即没有任何后处理时的 WER: diff --git a/chapters/zh-CN/chapter5/fine-tuning.mdx b/chapters/zh-CN/chapter5/fine-tuning.mdx index b1a9f46c..ba705738 100644 --- a/chapters/zh-CN/chapter5/fine-tuning.mdx +++ b/chapters/zh-CN/chapter5/fine-tuning.mdx @@ -74,10 +74,9 @@ DatasetDict({ }) ``` - - 您可以将语言标识符从 `"dv"` 更改为您选择的语言标识符。要查看 Common Voice 13 中所有可选的语言, - 请查看 Hugging Face Hub 上的数据集卡片:https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 - +> [!TIP] +> 您可以将语言标识符从 `"dv"` 更改为您选择的语言标识符。要查看 Common Voice 13 中所有可选的语言, +> 请查看 Hugging Face Hub 上的数据集卡片:https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 大多数 ASR 数据集只提供输入音频样本(`audio`)和相应的转写文本(`sentence`)。Common Voice 包含额外的元数据,如 `accent` 和 `locale`, 我们做 ASR 时可以忽略这些信息。为了让代码尽可能通用,我们只用输入音频和转写文本来进行微调,丢弃额外的元数据信息: @@ -413,9 +412,8 @@ training_args = Seq2SeqTrainingArguments( ) ``` - - 如果您不想将模型检查点上传到 Hub,请设置 `push_to_hub=False`。 - +> [!TIP] +> 如果您不想将模型检查点上传到 Hub,请设置 `push_to_hub=False`。 我们可以将训练参数传递给 🤗 Trainer,连同我们的模型、数据集、数据整理器和 `compute_metrics` 函数一起: diff --git a/chapters/zh-CN/chapter6/fine-tuning.mdx b/chapters/zh-CN/chapter6/fine-tuning.mdx index 7cf5a8be..d091ba14 100644 --- a/chapters/zh-CN/chapter6/fine-tuning.mdx +++ b/chapters/zh-CN/chapter6/fine-tuning.mdx @@ -10,11 +10,8 @@ nvidia-smi ``` - - -在我们的示例中,我们将使用大约 40 小时的训练数据。如果您想使用 Google Colab 免费版的 GPU 复现,需要将训练数据量减少到大约 10-15 小时,并减少训练步骤的数量。 - - +> [!WARNING] +> 在我们的示例中,我们将使用大约 40 小时的训练数据。如果您想使用 Google Colab 免费版的 GPU 复现,需要将训练数据量减少到大约 10-15 小时,并减少训练步骤的数量。 您还需要一些额外的依赖: diff --git a/chapters/zh-CN/chapter6/pre-trained_models.mdx b/chapters/zh-CN/chapter6/pre-trained_models.mdx index 32c21d11..9917bf43 100644 --- a/chapters/zh-CN/chapter6/pre-trained_models.mdx +++ b/chapters/zh-CN/chapter6/pre-trained_models.mdx @@ -27,12 +27,9 @@ SpeechT5 首先使用大规模的未标注语音和文本数据进行预训练 这样就可以获得多个针对不同语音任务微调的模型,它们都受益于最开始在未标注数据上预训练时学到的知识。 - - -尽管模型开始时使用的是同一个预训练模型的相同权重集,但微调后的最终版本会大不相同。所以,您不能仅仅通过更换预处理和后处理网络, -就将一个微调后的 ASR 模型转换为一个可用的 TTS 模型。SpeechT5 很灵活,但还没有灵活到这个程度 QwQ - - +> [!TIP] +> 尽管模型开始时使用的是同一个预训练模型的相同权重集,但微调后的最终版本会大不相同。所以,您不能仅仅通过更换预处理和后处理网络, +> 就将一个微调后的 ASR 模型转换为一个可用的 TTS 模型。SpeechT5 很灵活,但还没有灵活到这个程度 QwQ 让我们具体看看 SpeechT5 在 TTS(文本到语音)任务中使用的预/后处理网络是什么: @@ -68,19 +65,16 @@ inputs = processor(text="Don't count the days, make the days count.", return_ten SpeechT5 TTS 模型不仅限于生成一种音色的语音。相反,它能够使用所谓的“说话人嵌入”(speaker embeddings)来模仿各种特定说话人的声音特征。 - - -说话人嵌入(Speaker embeddings)是一个固定大小的向量,不论音频的长度如何都能用一种紧凑的方式表示说话人的身份。这些向量捕获了关于说话人的音色、口音、 -语调以及其他独特特征的关键信息,这些特征区分了不同的说话人。这样的嵌入有说话人验证(speaker verification)、说话人分离(speaker diarization)、 -说话人识别(speaker identification)等多种应用。生成说话人嵌入时最常用的技术包括: - -* I-Vectors(身份向量):I-Vectors 基于高斯混合模型(Gaussian mixture model,GMM)。它属于无监督学习,用一个特定说话人的 GMM 的统计数据得出一个低维度、固定大小的向量来代表说话人。 -* X-Vectors:X-Vectors 利用了深度神经网络(DNN),通过整合时间维度的上下文来捕获帧级的说话人信息。 - -[X-Vectors](https://www.danielpovey.com/files/2018_icassp_xvectors.pdf) 是 SOTA(state-of-the-art)的方法,在测试集上的表现优于 I-Vectors。 -X-Vectors 是深度神经网络产生的:它被训练以区分不同说话人,并将长度不一的语音音频映射到固定维度的嵌入。您还可以加载提前计算好的 X-Vector 说话人嵌入,它封装好了某个特定说话人的说话特征。 - - +> [!TIP] +> 说话人嵌入(Speaker embeddings)是一个固定大小的向量,不论音频的长度如何都能用一种紧凑的方式表示说话人的身份。这些向量捕获了关于说话人的音色、口音、 +> 语调以及其他独特特征的关键信息,这些特征区分了不同的说话人。这样的嵌入有说话人验证(speaker verification)、说话人分离(speaker diarization)、 +> 说话人识别(speaker identification)等多种应用。生成说话人嵌入时最常用的技术包括: +> +> * I-Vectors(身份向量):I-Vectors 基于高斯混合模型(Gaussian mixture model,GMM)。它属于无监督学习,用一个特定说话人的 GMM 的统计数据得出一个低维度、固定大小的向量来代表说话人。 +> * X-Vectors:X-Vectors 利用了深度神经网络(DNN),通过整合时间维度的上下文来捕获帧级的说话人信息。 +> +> [X-Vectors](https://www.danielpovey.com/files/2018_icassp_xvectors.pdf) 是 SOTA(state-of-the-art)的方法,在测试集上的表现优于 I-Vectors。 +> X-Vectors 是深度神经网络产生的:它被训练以区分不同说话人,并将长度不一的语音音频映射到固定维度的嵌入。您还可以加载提前计算好的 X-Vector 说话人嵌入,它封装好了某个特定说话人的说话特征。 让我们从 Hub 上的数据集中加载一个说话人嵌入。这些嵌入是使用 [CMU ARCTIC 数据集](http://www.festvox.org/cmu_arctic/) 和 [这个脚本](https://huggingface.co/mechanicalsea/speecht5-vc/blob/main/manifest/utils/prep_cmu_arctic_spkemb.py) 获得的,这里使用任何 X-Vector 嵌入都可以。 @@ -108,18 +102,15 @@ spectrogram = model.generate_speech(inputs["input_ids"], speaker_embeddings) 如果我们想生成语音波形,我们还需要指定一个用于把频谱图转换为波形的声码器(vocoder)。理论上,您可以使用任何适用于 80-bin 梅尔谱的声码器。 🤗 Transformers 提供了一个基于 HiFi-GAN 的声码器,可以很方便地使用。其权重由 SpeechT5 的原作者友情提供。 - - -[HiFi-GAN](https://arxiv.org/pdf/2010.05646v2.pdf) 是一种用于高保真语音合成的 SOTA 生成对抗网络(GAN)。它能够根据输入的频谱图生成高品质且逼真的音频波形。 - -总的来说,HiFi-GAN 由一个生成器和两个鉴别器组成。生成器是一个全卷积神经网络,它以梅尔谱作为输入并学习产生原始音频波形。 -鉴别器负责区分真实音频和生成音频。这两个鉴别器关注音频的不同方面。 - -HiFi-GAN 在大量高品质音频数据上进行训练。它采用所谓的对抗训练,其中生成器和鉴别器网络相互竞争。最初,生成器产生的音频质量较低, -鉴别器可以轻松地将其与真实音频区分开。随着训练的进行,生成器改进其输出,目标是欺骗鉴别器。反过来,鉴别器也学会更准确地区分真实和生成的音频。 -这种对抗性反馈循环帮助两个网络随时间推移提升性能。最终,HiFi-GAN 学会生成高保真音频,精密地模仿训练数据的特征。 - - +> [!TIP] +> [HiFi-GAN](https://arxiv.org/pdf/2010.05646v2.pdf) 是一种用于高保真语音合成的 SOTA 生成对抗网络(GAN)。它能够根据输入的频谱图生成高品质且逼真的音频波形。 +> +> 总的来说,HiFi-GAN 由一个生成器和两个鉴别器组成。生成器是一个全卷积神经网络,它以梅尔谱作为输入并学习产生原始音频波形。 +> 鉴别器负责区分真实音频和生成音频。这两个鉴别器关注音频的不同方面。 +> +> HiFi-GAN 在大量高品质音频数据上进行训练。它采用所谓的对抗训练,其中生成器和鉴别器网络相互竞争。最初,生成器产生的音频质量较低, +> 鉴别器可以轻松地将其与真实音频区分开。随着训练的进行,生成器改进其输出,目标是欺骗鉴别器。反过来,鉴别器也学会更准确地区分真实和生成的音频。 +> 这种对抗性反馈循环帮助两个网络随时间推移提升性能。最终,HiFi-GAN 学会生成高保真音频,精密地模仿训练数据的特征。 加载声码器就像加载任何其他 🤗 Transformers 模型一样简单。 @@ -302,11 +293,8 @@ Audio(speech_output[2], rate=sampling_rate) Your browser does not support the audio element. - - -Bark 与其他 🤗 Transformers 模型一样,只需几行代码即可针对速度和内存影响进行优化。要了解具体操作方法,请单击 [此 colab 演示笔记本](https://colab.research.google.com/github/ylacombe/notebooks/blob/main/Benchmark_Bark_HuggingFace.ipynb)。 - - +> [!TIP] +> Bark 与其他 🤗 Transformers 模型一样,只需几行代码即可针对速度和内存影响进行优化。要了解具体操作方法,请单击 [此 colab 演示笔记本](https://colab.research.google.com/github/ylacombe/notebooks/blob/main/Benchmark_Bark_HuggingFace.ipynb)。 ## Massive Multilingual Speech (MMS) @@ -317,15 +305,12 @@ MMS 的语音合成是基于 [VITS Kim et al., 2021](https://arxiv.org/pdf/2106. VITS 是一个语音生成网络,可以将文本转换成语音波形。它的工作方式类似于条件变分自编码器(conditional variational auto-encoder),从输入文本估计音频特征。首先,生成用频谱图表示的声学特征。 然后,使用改编自 HiFi-GAN 的转置卷积层解码出波形。在推理过程中,文本编码被上采样并使用流模块(flow module)和 HiFi-GAN 解码器转换成波形。像 Bark 一样,这里不需要声码器(vocoder),因为已经直接生成波形了。 - - -MMS 模型最近才被添加到 🤗 Transformers 中,所以您需要从源代码安装该库: - -```bash -pip install git+https://github.com/huggingface/transformers.git -``` - - +> [!WARNING] +> MMS 模型最近才被添加到 🤗 Transformers 中,所以您需要从源代码安装该库: +> +> ```bash +> pip install git+https://github.com/huggingface/transformers.git +> ``` 让我们试用一下 MMS,看看如何合成非英语的语音,例如德语。首先,我们将加载特定语言的检查点和分词器: