diff --git a/README.md b/README.md index 7d253aa5c..b41485d8d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ This repo contains the content that's used to create the **[Hugging Face course] | [Bahasa Indonesia](https://huggingface.co/course/id/chapter1/1) (WIP) | [`chapters/id`](https://github.com/huggingface/course/tree/main/chapters/id) | [@gstdl](https://github.com/gstdl) | | [Italian](https://huggingface.co/course/it/chapter1/1) (WIP) | [`chapters/it`](https://github.com/huggingface/course/tree/main/chapters/it) | [@CaterinaBi](https://github.com/CaterinaBi), [@ClonedOne](https://github.com/ClonedOne), [@Nolanogenn](https://github.com/Nolanogenn), [@EdAbati](https://github.com/EdAbati), [@gdacciaro](https://github.com/gdacciaro) | | [Japanese](https://huggingface.co/course/ja/chapter1/1) (WIP) | [`chapters/ja`](https://github.com/huggingface/course/tree/main/chapters/ja) | [@hiromu166](https://github.com/@hiromu166), [@younesbelkada](https://github.com/@younesbelkada), [@HiromuHota](https://github.com/@HiromuHota) | -| [Korean](https://huggingface.co/course/ko/chapter1/1) (WIP) | [`chapters/ko`](https://github.com/huggingface/course/tree/main/chapters/ko) | [@Doohae](https://github.com/Doohae), [@wonhyeongseo](https://github.com/wonhyeongseo), [@dlfrnaos19](https://github.com/dlfrnaos19) | +| [Korean](https://huggingface.co/course/ko/chapter1/1) (WIP) | [`chapters/ko`](https://github.com/huggingface/course/tree/main/chapters/ko) | [@Doohae](https://github.com/Doohae), [@wonhyeongseo](https://github.com/wonhyeongseo), [@dlfrnaos19](https://github.com/dlfrnaos19), [@nsbg](https://github.com/nsbg) | | [Portuguese](https://huggingface.co/course/pt/chapter1/1) (WIP) | [`chapters/pt`](https://github.com/huggingface/course/tree/main/chapters/pt) | [@johnnv1](https://github.com/johnnv1), [@victorescosta](https://github.com/victorescosta), [@LincolnVS](https://github.com/LincolnVS) | | [Russian](https://huggingface.co/course/ru/chapter1/1) (WIP) | [`chapters/ru`](https://github.com/huggingface/course/tree/main/chapters/ru) | [@pdumin](https://github.com/pdumin), [@svv73](https://github.com/svv73) | | [Thai](https://huggingface.co/course/th/chapter1/1) (WIP) | [`chapters/th`](https://github.com/huggingface/course/tree/main/chapters/th) | [@peeraponw](https://github.com/peeraponw), [@a-krirk](https://github.com/a-krirk), [@jomariya23156](https://github.com/jomariya23156), [@ckingkan](https://github.com/ckingkan) | diff --git a/chapters/en/chapter4/3.mdx b/chapters/en/chapter4/3.mdx index a782152c6..6b27777f4 100644 --- a/chapters/en/chapter4/3.mdx +++ b/chapters/en/chapter4/3.mdx @@ -83,7 +83,7 @@ training_args = TrainingArguments( When you call `trainer.train()`, the `Trainer` will then upload your model to the Hub each time it is saved (here every epoch) in a repository in your namespace. That repository will be named like the output directory you picked (here `bert-finetuned-mrpc`) but you can choose a different name with `hub_model_id = "a_different_name"`. -To upload you model to an organization you are a member of, just pass it with `hub_model_id = "my_organization/my_repo_name"`. +To upload your model to an organization you are a member of, just pass it with `hub_model_id = "my_organization/my_repo_name"`. Once your training is finished, you should do a final `trainer.push_to_hub()` to upload the last version of your model. It will also generate a model card with all the relevant metadata, reporting the hyperparameters used and the evaluation results! Here is an example of the content you might find in a such a model card: diff --git a/chapters/en/chapter5/3.mdx b/chapters/en/chapter5/3.mdx index 3f9c37dc2..4a3ddc7b5 100644 --- a/chapters/en/chapter5/3.mdx +++ b/chapters/en/chapter5/3.mdx @@ -387,7 +387,7 @@ ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000 Oh no! That didn't work! Why not? Looking at the error message will give us a clue: there is a mismatch in the lengths of one of the columns, one being of length 1,463 and the other of length 1,000. If you've looked at the `Dataset.map()` [documentation](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasets.Dataset.map), you may recall that it's the number of samples passed to the function that we are mapping; here those 1,000 examples gave 1,463 new features, resulting in a shape error. -The problem is that we're trying to mix two different datasets of different sizes: the `drug_dataset` columns will have a certain number of examples (the 1,000 in our error), but the `tokenized_dataset` we are building will have more (the 1,463 in the error message). That doesn't work for a `Dataset`, so we need to either remove the columns from the old dataset or make them the same size as they are in the new dataset. We can do the former with the `remove_columns` argument: +The problem is that we're trying to mix two different datasets of different sizes: the `drug_dataset` columns will have a certain number of examples (the 1,000 in our error), but the `tokenized_dataset` we are building will have more (the 1,463 in the error message; it is more than 1,000 because we are tokenizing long reviews into more than one example by using `return_overflowing_tokens=True`). That doesn't work for a `Dataset`, so we need to either remove the columns from the old dataset or make them the same size as they are in the new dataset. We can do the former with the `remove_columns` argument: ```py tokenized_dataset = drug_dataset.map( diff --git a/chapters/en/chapter6/3.mdx b/chapters/en/chapter6/3.mdx index 62d143dcc..88250f6df 100644 --- a/chapters/en/chapter6/3.mdx +++ b/chapters/en/chapter6/3.mdx @@ -109,7 +109,7 @@ We can see that the tokenizer's special tokens `[CLS]` and `[SEP]` are mapped to -The notion of what a word is is complicated. For instance, does "I'll" (a contraction of "I will") count as one or two words? It actually depends on the tokenizer and the pre-tokenization operation it applies. Some tokenizers just split on spaces, so they will consider this as one word. Others use punctuation on top of spaces, so will consider it two words. +The notion of what a word is complicated. For instance, does "I'll" (a contraction of "I will") count as one or two words? It actually depends on the tokenizer and the pre-tokenization operation it applies. Some tokenizers just split on spaces, so they will consider this as one word. Others use punctuation on top of spaces, so will consider it two words. ✏️ **Try it out!** Create a tokenizer from the `bert-base-cased` and `roberta-base` checkpoints and tokenize "81s" with them. What do you observe? What are the word IDs? diff --git a/chapters/en/chapter7/3.mdx b/chapters/en/chapter7/3.mdx index a31bb432c..dc12fb8bc 100644 --- a/chapters/en/chapter7/3.mdx +++ b/chapters/en/chapter7/3.mdx @@ -35,7 +35,7 @@ This process of fine-tuning a pretrained language model on in-domain data is usu By the end of this section you'll have a [masked language model](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.) on the Hub that can autocomplete sentences as shown below: - + Let's dive in! @@ -1035,7 +1035,7 @@ Neat -- our model has clearly adapted its weights to predict words that are more -This wraps up our first experiment with training a language model. In [section 6](/course/chapter7/section6) you'll learn how to train an auto-regressive model like GPT-2 from scratch; head over there if you'd like to see how you can pretrain your very own Transformer model! +This wraps up our first experiment with training a language model. In [section 6](/course/en/chapter7/section6) you'll learn how to train an auto-regressive model like GPT-2 from scratch; head over there if you'd like to see how you can pretrain your very own Transformer model! diff --git a/chapters/en/chapter9/5.mdx b/chapters/en/chapter9/5.mdx index b32056e33..4e1797c7a 100644 --- a/chapters/en/chapter9/5.mdx +++ b/chapters/en/chapter9/5.mdx @@ -23,19 +23,13 @@ import gradio as gr title = "GPT-J-6B" description = "Gradio Demo for GPT-J 6B, a transformer model trained using Ben Wang's Mesh Transformer JAX. 'GPT-J' refers to the class of model, while '6B' represents the number of trainable parameters. To use it, simply add your text, or click one of the examples to load them. Read more at the links below." article = "

GPT-J-6B: A 6 Billion Parameter Autoregressive Language Model

" -examples = [ - ["The tower is 324 metres (1,063 ft) tall,"], - ["The Moon's orbit around Earth has"], - ["The smooth Borealis basin in the Northern Hemisphere covers 40%"], -] + gr.Interface.load( "huggingface/EleutherAI/gpt-j-6B", inputs=gr.Textbox(lines=5, label="Input Text"), title=title, description=description, article=article, - examples=examples, - enable_queue=True, ).launch() ``` diff --git a/chapters/en/chapter9/7.mdx b/chapters/en/chapter9/7.mdx index 3c74d8452..fce4f80fb 100644 --- a/chapters/en/chapter9/7.mdx +++ b/chapters/en/chapter9/7.mdx @@ -231,6 +231,6 @@ with gr.Blocks() as block: block.launch() ``` - + We just explored all the core concepts of `Blocks`! Just like with `Interfaces`, you can create cool demos that can be shared by using `share=True` in the `launch()` method or deployed on [Hugging Face Spaces](https://huggingface.co/spaces). \ No newline at end of file diff --git a/chapters/en/events/3.mdx b/chapters/en/events/3.mdx index b0770d38d..d74b9c206 100644 --- a/chapters/en/events/3.mdx +++ b/chapters/en/events/3.mdx @@ -6,4 +6,4 @@ You can find all the demos that the community created under the [`Gradio-Blocks` **Natural language to SQL** - + diff --git a/chapters/it/chapter2/2.mdx b/chapters/it/chapter2/2.mdx index 5260f62c4..1471eb014 100644 --- a/chapters/it/chapter2/2.mdx +++ b/chapters/it/chapter2/2.mdx @@ -257,7 +257,7 @@ outputs = model(inputs) ``` {/if} -Ora, se osserviamo la forma dei nostri input, la dimensionalità sarà molto più bassa: la model head prende in input i vettori ad alta dimensionalità che abbiamo visto prima e produce vettori contenenti due valori (uno per etichetta): +Ora, se osserviamo la forma dei nostri output, la dimensionalità sarà molto più bassa: la model head prende in input i vettori ad alta dimensionalità che abbiamo visto prima e produce vettori contenenti due valori (uno per etichetta): ```python print(outputs.logits.shape) diff --git a/chapters/ko/chapter2/8.mdx b/chapters/ko/chapter2/8.mdx index ddc6f4558..49a23d412 100644 --- a/chapters/ko/chapter2/8.mdx +++ b/chapters/ko/chapter2/8.mdx @@ -88,16 +88,16 @@ -Это установка самой базовой версии 🤗 Transformers. В частности, никаких библиотек машинного обучения (как PyTorch или TensorFloat) установлено не будет. Так как мы будем использовать множество различных возможностей библиотеки 🤗 Transformers, мы рекомендуем установить версию для разработчиков, в составе которой сразу инсталлируются все необходимые зависимости: +Это установка самой базовой версии 🤗 Transformers. В частности, никаких библиотек машинного обучения (например, PyTorch или TensorFloat) установлено не будет. Так как мы будем использовать множество различных возможностей библиотеки 🤗 Transformers, мы рекомендуем установить версию для разработчиков, в состав которой сразу входят все необходимые зависимости: ``` !pip install transformers[sentencepiece] @@ -52,11 +52,11 @@ import transformers После установки Python у вас появится возможность запускать Python-команды в терминале. Прежде чем переходить дальше, запустите в терминале команду `python --version`. В результате должна быть распечатана версия Python, доступная для работы. -Когда вы запускаете Python-команду в терминале (например, `python --version`), эту команду обрабатывает _оснвной_ Python-интерпретатор вашей системы. Мы не рекомендуем устанавливать в его окружение дополнительные библиотеки, лучше для каждого проекта создавать виртуальные окружения. Каждый проект будет обладать собственными зависимостями и пакетами, если проекты будут в разных окружениях, то вам меньше придется следить за совместимостью бибилиотек. +Когда вы запускаете Python-команду в терминале (например, `python --version`), эту команду обрабатывает _основной_ Python-интерпретатор вашей системы. Мы не рекомендуем устанавливать в его окружение дополнительные библиотеки, лучше для каждого проекта создавать отдельное виртуальное окружение. Каждый проект будет обладать собственными зависимостями и пакетами, если проекты будут в разных окружениях, то вам меньше придется следить за совместимостью библиотек. В Python такой подход можно реализовать с помощью разных библиотек, а подробнее об окружениях можно почитать [тут](https://docs.python.org/3/tutorial/venv.html). Каждое окружение будет содержать в себе необходимую версию языка и набор библиотек. Все эти окружения изолированы друг от друга. Среди самых популярных инструментов для работы с виртуальными окружениями можно отметить [`venv`](https://docs.python.org/3/library/venv.html#module-venv). -Для начала создайте папку в домашней директории, в которой будут храниться ваши файлы курса (ее можно назвать произвольным именем, например: *transformers-course*): +Для начала создайте папку в домашней директории, в которой будут храниться ваши файлы курса (ее можно назвать произвольным именем, например, *transformers-course*): ``` mkdir ~/transformers-course @@ -84,7 +84,7 @@ ls -a # Активировать виртуальное окружение source .env/bin/activate -# Деактивировать окржуение +# Деактивировать окружение source .env/bin/deactivate ``` diff --git a/chapters/ru/chapter1/1.mdx b/chapters/ru/chapter1/1.mdx index 3c10ca68f..8d5b8a560 100644 --- a/chapters/ru/chapter1/1.mdx +++ b/chapters/ru/chapter1/1.mdx @@ -9,7 +9,7 @@ -В этом курсе вы научитесь основам обработки естесственного языка (NLP) с использованием библиотек от [Hugging Face](https://huggingface.co/). Экосистема состоит из: моделей ([🤗 Transformers](https://github.com/huggingface/transformers)), датасетов ([🤗 Datasets](https://github.com/huggingface/datasets)), вспомогательных бибилиотек ([🤗 Accelerate](https://github.com/huggingface/accelerate), [🤗 Tokenizers](https://github.com/huggingface/tokenizers)), а также репозитория [Hugging Face Hub](https://huggingface.co/models). Это полностью бесплатно! +В этом курсе вы научитесь основам обработки естественного языка (NLP) с использованием библиотек от [Hugging Face](https://huggingface.co/). Экосистема состоит из: моделей ([🤗 Transformers](https://github.com/huggingface/transformers)), датасетов ([🤗 Datasets](https://github.com/huggingface/datasets)), вспомогательных библиотек ([🤗 Accelerate](https://github.com/huggingface/accelerate), [🤗 Tokenizers](https://github.com/huggingface/tokenizers)), а также репозитория [Hugging Face Hub](https://huggingface.co/models). Это полностью бесплатно! ## Чего ожидать от курса? @@ -21,8 +21,8 @@ - Главы 1-4 содержат в себе введение в главные концепции библиотеки 🤗 Transformers. К концу этой части курса вы будете знакомы с тем, как функционируют трансформеры, как применять модели из репозитория [Hugging Face Hub](https://huggingface.co/models), как дообучить модели на собственных данных и опубликовать результаты на Hugging Face Hub! -- Главы 5-8 научат вас основам разделов 🤗 Datasets и 🤗 Tokenizers (датасеты и токенизаторы), это необходимо для дальнейшего погружения в область обработки естесственного языка. К концу этой части вы научитесь решать наиболее распространенные задачи в  NLP самостоятельно! -- Главы 9-12 выходят за рамки NLP, в них описано, как можно применять трансформеры в задачах обработки речи и компьютерном зрении. Также вы узнаете, как создавать и демонстрировать свои модели, оптимизировать их для промышленного использования. После изучения этой части вы будете в силах применить 🤗 трансформеры к (почти) любой задаче машинного обучения! +- Главы 5-8 научат вас основам библиотек 🤗 Datasets и 🤗 Tokenizers (датасеты и токенизаторы); это необходимо для дальнейшего погружения в область обработки естественного языка. К концу этой части вы научитесь решать наиболее распространенные задачи в  NLP самостоятельно! +- Главы 9-12 выходят за рамки NLP, в них описано, как можно применять трансформеры в задачах обработки речи и компьютерном зрении. Также вы узнаете, как создавать и демонстрировать свои модели, оптимизировать их для промышленного использования. После изучения этой части вы будете в силах применить 🤗 Transformers к (почти) любой задаче машинного обучения! Этот курс: @@ -37,23 +37,70 @@ Об авторах: -**Matthew Carrigan** - ML-инженер в Hugging Face. Живет в Дублине, Ирландия, и ранее работал инженером по машинному обучению в Parse.ly, а до этого — научным сотрудником в Тринити-колледже в Дублине. Он не верит, что мы сможем достичь реализовать теорию сильного искусственного интеллекта за счет масштабирования существующих архитектур, но все равно возлагает большие надежды на бессмертие роботов. +[**Abubakar Abid**](https://huggingface.co/abidlabs) окончил PhD в области прикладного машинного обучения в Стэндфордском университете. Во время PhD, он основал [Gradio](https://github.com/gradio-app/gradio) - свободная библиотека для Python, с помощью которой увидели свет свыше 600000 тысяч демоверсий моделей машинного обучения. Hugging Face приобрел Gradio, и теперь Abubakar работает с нами в качестве руководителя разработки машинного обучения. +[**Matthew Carrigan**](https://huggingface.co/Rocketknight1) - ML-инженер в Hugging Face. Живет в Дублине, Ирландия, и ранее работал инженером по машинному обучению в Parse.ly, а до этого — научным сотрудником в Тринити-колледже в Дублине. Он не верит, что мы сможем реализовать теорию сильного искусственного интеллекта за счет масштабирования существующих архитектур, но все равно возлагает большие надежды на бессмертие роботов. -**Lysandre Debut** - ML-инженер в Hugging Face, работает над библиотекой 🤗 Transformers с самых ранних этапов разработки. Его цель — сделать NLP доступным для всех, разработав инструменты с очень простым API. +[**Lysandre Debut**](https://huggingface.co/lysandre) - ML-инженер в Hugging Face, работает над библиотекой 🤗 Transformers с самых ранних этапов разработки. Его цель — сделать NLP доступным для всех, разработав инструменты с очень простым API. -**Sylvain Gugger** – инженер-исследователь в Hugging Face и один из ключевых участников разработки библиотеки 🤗 Transformers. Ранее работал научным сотрудником в fast.ai и написал книгу в соавторстве с Jeremy Howard: _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_. Основное внимание в его исследованиях уделяется тому, чтобы сделать глубокое обучение более доступным путем разработки и улучшения методов, позволяющих моделям быстро обучаться с ограниченными ресурсами. +[**Sylvain Gugger**](https://huggingface.co/sgugger) – инженер-исследователь в Hugging Face и один из ключевых участников разработки библиотеки 🤗 Transformers. Ранее работал научным сотрудником в fast.ai и написал книгу _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ в соавторстве с Jeremy Howard. Основное внимание в его исследованиях уделяется тому, чтобы сделать глубокое обучение более доступным путем разработки и улучшения методов, позволяющих моделям быстро обучаться при ограниченных ресурсах. -**Merve Noyan** - developer advocate в Hugging Face, работает над разработкой инструментов и созданием контента на их основе, чтобы машинное обучение более доступным. +[**Dawood Khan**](https://huggingface.co/dawoodkhan82) - ML-инженер в Hugging Face. Dawood из Нью-Йорка, где он окончил Нью-Йоркский университет и получил степень бакалавра компьютерных наук. Проработав несколько лет iOS инженером, Dawood решил сменить работу и стал сооснователем Gradio. Позднее Hugging Face приобрел Gradio. -**Lucile Saulnier** - ML-инженер в Hugging Face, разрабатывающая и поддерживающая использование инструментов с открытым исходным кодом. Она также активно участвует во многих исследовательских проектах в области NLP, таких как совместное обучение и BigScience. +[**Merve Noyan**](https://huggingface.co/merve) - developer advocate в Hugging Face, работает над разработкой инструментов и созданием контента на их основе, чтобы машинное обучение более доступным. -**Lewis Tunstall** - ML-инженер в Hugging Face, сосредоточен на разработке инструментов с открытым исходным кодом и обеспечении их доступности для более широкого сообщества. Соавтор будущей книги [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). +[**Lucile Saulnier**](https://huggingface.co/SaulLu) - ML-инженер в Hugging Face, разрабатывающая и поддерживающая использование инструментов с открытым исходным кодом. Она также активно участвует во многих исследовательских проектах в области NLP, таких как совместное обучение и BigScience. -**Leandro von Werra** - ML-инженер в команде, работающей над открытым исходным кодом Hugging Face и соавтор будушей будущей книги [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). Обладает большим опытом реализации NLP-проектов в промышленности. +[**Lewis Tunstall**](https://huggingface.co/lewtun) - ML-инженер в Hugging Face, сосредоточен на разработке инструментов с открытым исходным кодом и обеспечении их доступности для более широкого сообщества. Соавтор будущей книги [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). +[**Leandro von Werra**](https://huggingface.co/lvwerra) - ML-инженер в команде, работающей над открытым исходным кодом Hugging Face и соавтор будущей будущей книги [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). Обладает большим опытом реализации NLP-проектов в промышленности. + +## ЧАВО + +Мы собрали ответы на несколько часто задаваемых вопросов: + +- **Получу ли я сертификат после прохождения этого курса?** +На данный момент у нас нет сертификации для этого курса. Мы работаем над получением сертификации для экосистемы Hugging Face. Следите за новостями! + +- **Сколько времени мне нужно будет потратить на прохождение этого курса?** +Каждая глава этого курса рассчитана на неделю работы, то есть примерно 6-8 часов в неделю. Однако, вы можете проходить курс в любом удобном для вас ритме. + +- **Где я могу задать вопрос по материалам курса?** +Если у вас возникли какие-либо вопросы по поводу любой части курса, просто нажмите на "*Ask a question*" наверху страницы, и вы будете автоматически перенаправлены в соответствующий раздел [форума Hugging Face](https://discuss.huggingface.co/) (форум на английском языке): + +Link to the Hugging Face forums + +Обратите внимание, что на форуме также доступен список [идей для проектов](https://discuss.huggingface.co/c/course/course-event/25), если вы хотите применить полученные знания на практике после прохождения курса. + +- **Где я могу посмотреть на код, используемый в этом курсе?** +Внутри каждого раздела наверху страницы есть баннер, который позволит запустить код в Google Colab или Amazon SageMaker Studio Lab: + +Link to the Hugging Face course notebooks + +Блокноты Jupyter со всем кодом, используемом в материалах курса, доступны в репозитории [`huggingface/notebooks`](https://github.com/huggingface/notebooks). Если вы хотите сгенерировать их на своем компьютере, вы можете найти инструкцию в репозитории [`course`](https://github.com/huggingface/course#-jupyter-notebooks) на GitHub. + +- **Как я могу внести свой вклад в развитие курса?** +Существует множество способов внести свой вклад в наш курс! Если вы найдете опечатку или баг, пожалуйста, откройте вопрос (issue) в репозитории [`course`](https://github.com/huggingface/course). Если вы хотите помочь с переводом на ваш родной язык, вы можете найти инструкцию [здесь](https://github.com/huggingface/course#translating-the-course-into-your-language). + +- **Какие стандарты использовались при переводе?** +Каждый перевод содержит глоссарий и файл `TRANSLATING.txt`, в которых описаны стандарты, используемые для перевода терминов и т.д. Вы можете посмотреть на пример для немецкого языка [здесь](https://github.com/huggingface/course/blob/main/chapters/de/TRANSLATING.txt). + +- **Могу ли я использовать этот курс в своих целях?** +Конечно! Этот курс распространяется по либеральной лицензии [Apache 2 license](https://www.apache.org/licenses/LICENSE-2.0.html). Это означает, что вы должны упомянуть создателей этого курса, предоставить ссылку на лицензию и обозначить все изменения. Все это может быть сделано любым приемлемым способов, который, однако, не подразумевает, что правообладатель поддерживает вас или ваши действия по отношению этого курса. Если вы хотите процитировать этот курс, пожалуйста, используйте следующий BibTex: + +``` +@misc{huggingfacecourse, + author = {Hugging Face}, + title = {The Hugging Face Course, 2022}, + howpublished = "\url{https://huggingface.co/course}", + year = {2022}, + note = "[Online; accessed ]" +} +``` + +## Поехали! Вы готовы начать? В этой главе вы узнаете: -* Как испольовать `pipeline()` для решения NLP-задач генерации и классификации текста +* Как использовать `pipeline()` для решения NLP-задач генерации и классификации текста * Об архитектуре трансформеров -* Как различать архитектуры кодировщика, декодера и кодировщика-декодера и варианты их использования +* Как различать архитектуры кодировщика, декодировщика и кодировщика-декодировщика и варианты их использования diff --git a/chapters/ru/chapter1/10.mdx b/chapters/ru/chapter1/10.mdx new file mode 100644 index 000000000..124998f73 --- /dev/null +++ b/chapters/ru/chapter1/10.mdx @@ -0,0 +1,258 @@ + + +# Проверка знаний[[end-of-chapter-quiz]] + + + +В этой главе было много материала! Если вы чувствуете, что все еще всецело не познали все премудрости трансформеров - не переживайте! В следующих главах мы детально расскажем, как все устроено "под капотом". + +Сперва, однако, давайте проверим, что вы узнали в этой главе! + + +### 1. Зайдите на Hub и найдите чекпоинт модели `roberta-large-mnli`. Какую задачу она решает? + + +странице roberta-large-mnli." + }, + { + text: "Классификация текстов", + explain: "В частности, модель определяет, являются ли два предложения логически связанными и присваивает одну из трех меток: противопоставление, нейтральная связь, импликация (англ. contradiction, neutral, entailment). Эта задача называется автоматическое определение логической связи между текстами (англ. natural language inference).", + correct: true + }, + { + text: "Генерация текста", + explain: "Посмотрите получше на странице roberta-large-mnli." + } + ]} +/> + +### 2. Какой будет результат выполнения данного кода? + +```py +from transformers import pipeline + +ner = pipeline("ner", grouped_entities=True) +ner("My name is Sylvain and I work at Hugging Face in Brooklyn.") +``` + +sentiment-analysis." + }, + { + text: "Пайплайн вернет текст, сгенерированный на основе данного предложения.", + explain: "Неверно — для этого используется пайплайн text-generation.", + }, + { + text: "Пайплайн вернет слова, обозначающие персон, организаций или географических локаций.", + explain: "Кроме того, с аргументом grouped_entities=True, пайплайн сгруппирует слова, принадлежащие одной и той же сущности, например, \"Hugging Face\".", + correct: true + } + ]} +/> + +### 3. Чем нужно заменить ... в данном коде? + +```py +from transformers import pipeline + +filler = pipeline("fill-mask", model="bert-base-cased") +result = filler("...") +``` + + has been waiting for you.", + explain: "Неверно. Прочитайте карточку модели bert-base-cased и попробуйте найти, где вы ошиблись." + }, + { + text: "This [MASK] has been waiting for you.", + explain: "Верно! Токен-маска для этой модели - [MASK].", + correct: true + }, + { + text: "This man has been waiting for you.", + explain: "Неверно. Этот пайплайн предсказывает замаскированный токен, а для этого нужно предоставить токен-маску." + } + ]} +/> + +### 4. Почему этот код выдаст ошибку? + +```py +from transformers import pipeline + +classifier = pipeline("zero-shot-classification") +result = classifier("This is a course about the Transformers library") +``` + +candidate_labels=[...].", + correct: true + }, + { + text: "Этому пайплайну требуются несколько предложений, а не одно.", + explain: "Неверно. Хотя, если использовать этот пайплайн правильно, он может принимать на вход массив предложений (как и все остальные пайплайны)." + }, + { + text: "Опять библиотека 🤗 Transformers не работает как положено.", + explain: "Мы даже не будем комментировать этот ответ!" + }, + { + text: "Этому пайплайну требуются более длинные предложения - это слишком короткое.", + explain: "Неверно. Однако, стоит отметить, что этот пайплайн обрежет очень длинный текст, для того, чтобы его корректно обработать." + } + ]} +/> + +### 5. Что такое «трансферное обучение»? + +получит знания предобученной модели. Другими словами, предобученная модель передаст свои знания новой.", + correct: true + }, + { + text: "Передача знаний от предобученной модели к новой модели путем проектирования новой модели с той же самой архитектурой, что и у предобученной.", + explain: "Архитектура - это лишь «скелет» модели; в этом случае никой передачи знаний не происходит." + } + ]} +/> + +### 6. Правда или ложь? Для предобучения языковой модели обычно не требуются метки классов. + +самостоятельно (англ. self-supervised). Это означает, что метки классов создаются автоматически на основе входных данных (например, предсказание следующего или замаскированного слова).", + correct: true + }, + { + text: "Ложь", + explain: "Это неверный ответ." + } + ]} +/> + +### 7. Выберите предложение, которое наилучшим способом описывает следующие термины: «модель», «архитектура» и «веса». + + + + +### 8. Какую из этих моделей вы выберете для дополнения текста по введенной его части? + + + +### 9. Какую из этих моделей вы выберете для автоматического реферирования? + + + +### 10. Какую из этих моделей вы выберете для классификации текстов путем присвоения им определенных меток? + + + +### 11. Что может быть одной из причин предвзятости модели? + + diff --git a/chapters/ru/chapter1/2.mdx b/chapters/ru/chapter1/2.mdx index f265cabca..3afb88da3 100644 --- a/chapters/ru/chapter1/2.mdx +++ b/chapters/ru/chapter1/2.mdx @@ -1,25 +1,25 @@ -# Обработка естесственного языка +# Обработка естественного языка -Прежде, чем перейти к трансформерам, сделаем быстрый обзор того, что такое обработка естесственного языка (NLP) и почему мы заинтересованы в этой сфере. +Прежде, чем перейти к трансформерам, сделаем быстрый обзор того, что такое обработка естественного языка (NLP), и почему мы заинтересованы в этой сфере. ## Что такое NLP? -NLP - область лингвистики и машинного обучения, сосредоточенная на изучении всего, что связано с человеческим языком. Главная цель NLP не просто понимать отдельные слова, но и иметь возможность понимать конекст, в котором эти слова находятся. +NLP - область лингвистики и машинного обучения, которая изучает все, что связано с естественными языками. Главная цель NLP не просто понимать отдельные слова, но и иметь возможность понимать контекст, в котором эти слова находятся. -Список общих NLP-задач с некоторыми примерами: +Список типичных NLP-задач с некоторыми примерами: -- **Классификация предложений**: определить эмоциональную окраску отзыва, детектировать среди входящий писем спам, определить грамматическую корректность предложения или даже проверить, являются ли два предложения связанными между собой логически +- **Классификация предложений**: определить эмоциональную окраску отзыва, детектировать среди входящих писем спам, определить грамматическую правильность предложения или даже проверить, являются ли два предложения связанными между собой логически - **Классификация каждого слова в предложении**: вычленить грамматические составляющие предложения (существительное, глагол, прилагательное) или определить именованные сущности (персона, локация, организация) -- **Генерация текста**: закончить предложение на основе некоторого вводного фрагмента, заполнить пропуски в тексте, содержащем замаскированные слова +- **Генерация текста**: закончить предложение на основе некоторого запроса, заполнить пропуски в тексте, содержащем замаскированные слова - **Сформулировать ответ на вопрос**: получить ответ на заданный по тексту вопрос -- **Сгенерировать новое предложение исходя из предложенного**: перевести текст с одного языка на другой, аннотировать/саммаризовать текст +- **Сгенерировать новое предложение исходя из предложенного**: перевести текст с одного языка на другой, выполнить автоматическое реферирование текста -NLP не ограничивается только письменным текстом. Есть множество сложных задач, связанных с распознаванием речи, компьютерным зрением, таких как расшифровка аудио-сигнала или описания изображений. +NLP не ограничивается только письменным текстом. Есть множество сложных задач, связанных с распознаванием речи и компьютерным зрением, таких как транскрибирование аудио или описание изображений. ## Почему это сложно? diff --git a/chapters/ru/chapter1/3.mdx b/chapters/ru/chapter1/3.mdx index 2f418a6fb..ed4fb86d4 100644 --- a/chapters/ru/chapter1/3.mdx +++ b/chapters/ru/chapter1/3.mdx @@ -1,4 +1,4 @@ -# Трансформеры, на что они способны? +# Трансформеры: на что они способны? Библиотека [🤗 Transformers](https://github.com/huggingface/transformers) предоставляет различную функциональность для создания и использования этих моделей. [Model Hub](https://huggingface.co/models) содержит тысячи предобученных моделей, которые может скачать и использовать любой. Вы также можете загружать свои модели на Model Hub! -⚠️ Hugging Face Hub не ограничивается только моделями. Любой человек может поделиться своими моделями или датасетами! Для этого нужно создать аккаунт: Create a huggingface.co +⚠️ Hugging Face Hub не ограничивается только моделями. Любой человек может поделиться своими моделями или датасетами! Для этого нужно создать учетную запись: Create a huggingface.co @@ -62,7 +62,7 @@ classifier( {'label': 'NEGATIVE', 'score': 0.9994558095932007}] ``` -По умолчанию этот пайплайн выбирает специальную модель, которая была предобучена для оценки эмоциональной окаски предложений на английском языке. Модель загружается и кэшируется когда вы создадите объект `classifier`. Если вы перезапустите команду, будет использована кэшированная модель, загрузки новой модели не произойдет. +По умолчанию этот пайплайн выбирает специальную модель, которая была предобучена для оценки тональности предложений на английском языке. Модель загружается и кэшируется когда вы создадаете объект `classifier`. Если вы перезапустите команду, будет использована кэшированная модель, т.е. загрузки новой модели не произойдет. В процессе обработки пайплайном текста, который вы ему передали, есть три главных шага: @@ -115,7 +115,7 @@ classifier( ## Генерация текста -Теперь давайте взглянем на пайплайн генерации текста. Главная идея заключается в следующем: вы передаете на вход модели небольшой фрагмент текста, а модель будет продолжать его. Это похоже на предсказание следующего слова в клавиатурах различных смартфонов. Генерация текста содержит в себе элемент случайности, поэтому ваш результат может отличаться от того, который приведен ниже в примере. +Теперь давайте взглянем на пайплайн генерации текста (англ. text generation). Главная идея заключается в следующем: вы передаете на вход модели небольшой фрагмент текста, а модель будет продолжать его. Это похоже на предсказание следующего слова в клавиатурах различных смартфонов. Генерация текста содержит в себе элемент случайности, поэтому ваш результат может отличаться от того, который приведен ниже в примере. ```python from transformers import pipeline @@ -141,7 +141,7 @@ generator("In this course, we will teach you how to")
-## Использование произвольной модлеи из Hub в пайплайне +## Использование произвольной модели из Hub в пайплайне Предыдущие примеры использовали модель по умолчанию для решения конкретной задачи, но у вас есть возможность выбрать произвольную модель из Hub и передать ее в пайплайн для конкретной задачи. Например, для генерации текста. Перейдите по ссылке [Model Hub](https://huggingface.co/models) и кликните на соответствующий тег слева, чтобы получить список доступных для этой задачи моделей. Вы должны увидеть страницу, подобную [этой](https://huggingface.co/models?pipeline_tag=text-generation). @@ -167,7 +167,7 @@ generator( 'time and real'}] ``` -Вы можете уточнить, для какого языка вам нужна модель, щелкнув на языковые теги, и выбрать ту, которая будет генерировать текст на другом языке. Model Hub предобученные модели для многоязычных задач. +Вы можете уточнить, для какого языка вам нужна модель, щелкнув на языковые теги, и выбрать ту, которая будет генерировать текст на другом языке. На Model Hub даже есть предобученные модели для многоязычных задач. Как только вы выберете модель, вы увидите, что есть виджет, позволяющий вам попробовать ее прямо на сайте. Таким образом, вы можете быстро протестировать возможности модели перед ее загрузкой. @@ -179,7 +179,7 @@ generator( ### The Inference API -Все модели могут быть протестированы прямо на сайте с использованием inference api, доступнго по адресу [https://huggingface.co/](https://huggingface.co/). Вы можете попробовать применить модель вводя различный текст и сразу же получая результат. +Все модели могут быть протестированы прямо на сайте с использованием inference API, доступного по адресу [https://huggingface.co/](https://huggingface.co/). Вы можете попробовать применить модель, вводя различный текст и сразу же получая результат. Inference API также представляется как платный продукт, что пригодится для интегрирования моделей в свои рабочие процессы. Подробнее можно узнать на странице с [ценами](https://huggingface.co/pricing). @@ -187,7 +187,7 @@ Inference API также представляется как платный пр ## Заполнение пропусков -Следующая задача, на которую мы обратим внимание, связана с заполнением пропусков в тексте. Идея очень проста, мы продемонстрируем ее на простом тексте: +Следующая задача, на которую мы обратим внимание, связана с заполнением пропусков в тексте (англ. mask filling). Идея очень проста, мы продемонстрируем ее на простом тексте: ```python @@ -208,19 +208,17 @@ unmasker("This course will teach you all about models.", top_k=2) 'token_str': ' computational'}] ``` -Аргумент `top_k` указывает, сколько вариантов для пропущенного слова будет отображено. Обратите внимание, что модель заполнит пропуск на месте слова ``, которое часто интерпретируют как *mask token*. Другие модели могут использовать другие токены для обозначения пропуска, всегда лучше проверять это. Один из способов сделать это - обратить внимание на виджет для соответствующей модели. - -The `top_k` argument controls how many possibilities you want to be displayed. Note that here the model fills in the special `` word, which is often referred to as a *mask token*. Other mask-filling models might have different mask tokens, so it's always good to verify the proper mask word when exploring other models. One way to check it is by looking at the mask word used in the widget. +Аргумент `top_k` указывает, сколько вариантов для пропущенного слова будет отображено. Обратите внимание, что модель заполнит пропуск на месте слова ``, которое часто интерпретируют как *mask token (токен-маска)*. Другие модели могут использовать другие токены для обозначения пропуска, всегда лучше проверять это. Один из способов сделать это - обратить внимание на виджет для соответствующей модели. -✏️ **Попробуйте!** Найдите в поиске модель `bert-based-cased` и обратите внимание на его mask token в виджете. Что эта модель будет предсказывать для нашего пайплайна выше? +✏️ **Попробуйте!** Найдите в поиске модель `bert-based-cased` и обратите внимание на его токен-маску в виджете. Что эта модель предскажет, если применить ее в предыдущем примере? ## Распознавание именованных сущностей (NER) -Распознавание именованных сущностей - это задача, в которой модели необходимо найти части текста, соответствующие некоторым сущностям, например: персонам, местам, организациям. Давайте посмотрим на пример: +Распознавание именованных сущностей (англ. named entity recognition) - это задача, в которой модели необходимо найти части текста, соответствующие некоторым сущностям, например: персонам, местам, организациям. Давайте посмотрим на пример: ```python from transformers import pipeline @@ -236,20 +234,19 @@ ner("My name is Sylvain and I work at Hugging Face in Brooklyn.") ] ``` -В этом примере модель корректно обозначила Сильвен как персону (PER), Hugging Face как организацию (ORG) и Бруклин как локацию (LOC). +В этом примере модель корректно обозначила Sylvain как персону (PER), Hugging Face как организацию (ORG) и Brooklyn как локацию (LOC). Мы передали в пайплайн аргумент `grouped_entities=True` для того, чтобы модель сгруппировала части предложения, соответствующие одной сущности: в данном случае модель объединила "Hugging" и "Face" несмотря на то, что название организации состоит из двух слов. На самом деле, как мы увидим в следующей главе, препроцессинг делит даже отдельные слова на несколько частей. Например, `Sylvain` будет разделено на 4 части: `S`, `##yl`, `##va`, and `##in`. На этапе постпроцессинга пайплайн успешно объединит эти части. -✏️ **Попробуйте!** Найдите на Hub модель, позволяющую решать задачу определения частей речи в предложении (part of speech tagging, POS). Что модель предскажет для предложения из примера выше? +✏️ **Попробуйте!** Найдите на Model Hub модель, позволяющую решать задачу определения частей речи в предложении (part of speech tagging, POS). Что модель предскажет для предложения из примера выше? -## Ответы на вопросы - -Пайплан `question-answering` позволяет сгенерировать ответ на вопрос по данному контексту: +## Вопросно-ответные системы +Пайплайн `question-answering` позволяет сгенерировать ответ на вопрос по данному контексту: ```python from transformers import pipeline @@ -268,9 +265,9 @@ question_answerer( Обратите внимание, что пайплайн извлекает информацию для ответа из переданного ему контекста -## Саммаризация +## Автоматическое реферирование (саммаризация) -Саммаризация - задача, в которой необходимо сократить объем текста, но при этом сохранить все (или большинство) важных аспектов изначального текста. Вот пример: +Автоматическое реферирование (англ. summarization) - задача, в которой необходимо сократить объем текста, но при этом сохранить все важные аспекты (или большинство из них) изначального текста. Вот пример: ```python from transformers import pipeline @@ -329,7 +326,7 @@ translator("Ce cours est produit par Hugging Face.") [{'translation_text': 'This course is produced by Hugging Face.'}] ``` -Так же, как и в задачах генерации и саммаризации текста, вы можете указать максимальную длину `max_length` или минимальную длину `min_length` результата. +Так же, как и в задачах генерации и автоматического реферирования текста, вы можете указать максимальную длину `max_length` или минимальную длину `min_length` результата. @@ -338,5 +335,5 @@ translator("Ce cours est produit par Hugging Face.") -Показанные пайплайн в основном носят демонстрационный характер, потому что настроены на решение конкретных задач. В следующей главе вы узнаете, как изменить поведение функции `pipeline()`. +Показанные пайплайны в основном носят демонстрационный характер, потому что настроены на решение конкретных задач. В следующей главе вы узнаете, как изменить поведение функции `pipeline()`. diff --git a/chapters/ru/chapter1/4.mdx b/chapters/ru/chapter1/4.mdx index a817d124c..52072be87 100644 --- a/chapters/ru/chapter1/4.mdx +++ b/chapters/ru/chapter1/4.mdx @@ -5,7 +5,7 @@ classNames="absolute z-10 right-0 top-0" /> -В этом разделе мы посмотрим в общих чертах на то, как работают трансфореры. +В этом разделе мы посмотрим в общих чертах на то, как работают трансформеры. ## Немного истории @@ -18,9 +18,9 @@ [Архитектура трансформеров](https://arxiv.org/abs/1706.03762) была опубликована в июне 2017. Основной фокус оригинального исследования был сосредоточен на задачах перевода. Эта публикация повлекла за собой несколько влиятельных моделей: -- **Июнь 2018**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), первая предобученная модель, часто используется процедура тонкой настройки (fine-tuning) и применение для различных NLP-задач с последующим получением результатов высокого качества. +- **Июнь 2018**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), первая предобученная модель для тонкой настройки или дообучения (fine-tuning), которая показала результаты высокого качества для многих NLP-задач. -- **Октябрь 2018**: [BERT](https://arxiv.org/abs/1810.04805), другая большая предобученная модель, была разработана для для получения хороших саммаризаций предложений (больше мы узнаем об этом в следующей главе!) +- **Октябрь 2018**: [BERT](https://arxiv.org/abs/1810.04805), другая большая предобученная модель, была разработана для извлечения более точного содержания из предложений (больше мы узнаем об этом в следующей главе!) - **Февраль 2019**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), улучшенная (и более объемная) версия GPT, которая не была сразу опубликована по этическим соображениям @@ -34,17 +34,17 @@ В широком смысле трансформеры могут быть классифицированы на три типа: - GPT-подобные модели (также часто называемые _авторегрессионные_ трансформеры) - BERT-подобные модели (также часто называемые _автокодирующие_ трансформеры (_auto-encoding_)) -- тип BART/T5 модели (также часто называются модели класса _последовательность-последовательность_ (_sequence2sequence, seq2seq_)) +- тип BART/T5 модели (также часто называются модели класса _последовательность-последовательность_ (_sequence-to-sequence, seq2seq_)) -Мы рассмотри эти семейства более глубоко позже. +Мы рассмотрим эти семейства более детально позже. ## Трансформеры - языковые модели -Все модели трансформеров, упомянутые выше (GPT, BERT, BART, T5, etc.) обучены как *языковые модели*. Это означает, что они обучены на огромном количестве текста в технике самостоятельного обучения (self-supervised learning). Самостоятельное обучение - это такой способ обучения, в котором цель обучения автоматически вычислятся на основе входных данных. Это означает, что люди не должны размечать данные! +Все модели трансформеров, упомянутые выше (GPT, BERT, BART, T5, etc.) обучены как *языковые модели (англ. language models)*. Это означает, что они обучены на огромном количестве текста, используя технику самостоятельного обучения (англ. self-supervised learning). Самостоятельное обучение - это такой способ обучения, в котором цель обучения автоматически вычисляется на основе входных данных. Это означает, что люди не должны размечать данные! -Такой тип моделей реализует статистическое понимание языка, на котором он был обучен, но он не очень полезен для конкретных практических задач. Из-за этого базовая предварительно обученная модель потом подвергается процедуре, называемой *трансферным обучением*. В ходе этого процесса модель настраивается под конкретные наблюдения, т. е. с размеченными человеком данными конкретной задачи. +Такой тип моделей реализует статистическое понимание языка, на котором он был обучен, но он не очень полезен для конкретных практических задач. Из-за этого базовая предварительно обученная модель потом подвергается процедуре, называемой *трансферным обучением (англ. transfer learning)*. В ходе этого процесса модель настраивается под конкретные наблюдения, т.е. размеченными человеком данными для конкретной задачи. -В качестве примера можно привести предсказание следующего слова в предложении на основе *n* предыдущих слов. Это называется *каузальным языковым моделированием*, потому что модель зависит от прошлых и текущих слов, но не от будущих. +В качестве примера можно привести предсказание следующего слова в предложении на основе *n* предыдущих слов. Это называется *каузальным языковым моделированием (англ. causal language modeling)*, потому что модель зависит от прошлых и текущих слов, но не от будущих.
@@ -52,7 +52,7 @@
-Другой пример - *максированная языковая модель*, которая предсказывает замаскированное слово в предложении. +Другой пример - *маскированная языковая модель (англ. masked language modeling)*, которая предсказывает замаскированное слово в предложении.
Example of masked language modeling in which a masked word from a sentence is predicted. @@ -76,17 +76,19 @@ -Это продемонстрировано командой разработчиков на примере очень большой модели. Команда сознательно пытающется уменьшить воздействие предобучения на окружающую среду. Углеродный следу от проведения множества экспериментов для получения лучших гиперпараметров будет еще выше. +И это наглядная демонстрация проекта по разработке (очень большой) модели, которым руководит команда, _сознательно_ пытающаяся уменьшить воздействие предобучения на окружающую среду. Углеродный след от проведения множества экспериментов для получения лучших гиперпараметров будет еще выше. -Представьте себе, что каждый раз, когда исследовательская группа, студенческая организация или компания хотят обучить модель, они делают это с нуля. Это привело бы к огромным, ненужным глобальным затратам! +Представьте себе, что каждый раз, когда исследовательская группа, студенческая организация или компания хотели бы обучить модель, они делали бы это с нуля. Это привело бы к огромным, ненужным глобальным затратам! -Вот почему совместное использование языковых моделей имеет первостепенное значение: совместное использование обученных весов и построение на основе уже обученных весов снижает общую стоимость вычислений и углеродный след сообщества. +Вот почему распространение языковых моделей имеет первостепенное значение: распространение обученных весов и построение новых моделей на их основе снижает общую стоимость вычислений и углеродный след сообщества. + +Кстати, вы можете измерить углеродный след, который оставят ваши модели, при помощи нескольких инструментов. Например, интегрированные в 🤗 Transformers [ML CO2 Impact](https://mlco2.github.io/impact/) или [Code Carbon](https://codecarbon.io/). Чтобы узнать больше о этом, вы можете прочитать этот [блог-пост](https://huggingface.co/blog/carbon-emissions-on-the-hub), в котором мы рассказываем как сгенерировать файл `emissions.csv`, содержащий прогноз объемов выброса углерода во время обучения модели, а также [документацию](https://huggingface.co/docs/hub/model-cards-co2) 🤗 Transformers, в которой затрагивается эта тема. ## Трансферное обучение -*Предобучение* - это процесс обучения модели с нуля: веса модели случайным образом инициализируются, после начинается обучение без предварительных настроек. +*Предобучение (англ. pretraining)* - это процесс обучения модели с нуля: веса модели случайным образом инициализируются, после начинается обучение без предварительных настроек.
The pretraining of a language model is costly in both time and money. @@ -95,22 +97,22 @@ Предобучение обычно происходит на огромных наборах данных, сам процесс может занять несколько недель. -*Fine-tuning*, с другой стороны, это обучение, проведенной *после* того, как модель была предобучена. Для проведения fine-tuning вы сначала должны выбрать предобученную языковую модель, а после провести обучение на данных собственной задачи. Стойте -- почему не обучить модель сразу же на данных конкретной задачи? Этому есть несколько причин: +*Дообучение (англ. fine-tuning)*, с другой стороны, это обучение *после* того, как модель была предобучена. Для дообучения вы сначала должны выбрать предобученную языковую модель, а после продолжить ее обучение ее на данных собственной задачи. Стойте -- почему не обучить модель сразу же на данных конкретной задачи? Этому есть несколько причин: -* Предобученная модель уже обучена на датасете, который имеет много сходств с датасетом для fine-tuning. Процесс тонкой настройки может использовать знания, которые были получены моделью в процессе предобучения (например, в задачах NLP предварительно обученная модель будет иметь представление о статистических закономерностях языка, который вы используете в своей задаче). +* Предобученная модель уже обучена на датасете, который имеет много сходств с датасетом для дообучения. Процесс дообучения может использовать знания, которые были получены моделью в процессе предобучения (например, в задачах NLP предварительно обученная модель будет иметь представление о статистических закономерностях языка, который вы используете в своей задаче). -* Так как предобученная модель уже "видела" много данных, процесс тонкой настройки требует меньшего количества данных для получения приемлемых результатов. +* Так как предобученная модель уже "видела" много данных, процесс дообучения требует меньшего количества данных для получения приемлемых результатов. * По этой же причине требуется и намного меньше времени для получения хороших результатов. -Например, можно использовать предварительно обученную на английском языке модель, а затем провести ее fine-tuning на корпусе arXiv, в результате чего получится научно-исследовательская модель. Для тонкой настройки потребуется лишь ограниченный объем данных: знания, которые приобрела предварительно обученная модель, «передаются» (осуществляют трансфер), отсюда и термин «трансферное обучение». +Например, можно использовать предварительно обученную на английском языке модель, а затем дообучить ее на корпусе arXiv, в результате чего получится научно-исследовательская модель. Для дообучения потребуется лишь ограниченный объем данных: знания, которые приобрела предварительно обученная модель, «передаются» (осуществляют трансфер), отсюда и термин «трансферное обучение».
The fine-tuning of a language model is cheaper than pretraining in both time and money.
-Таким образом, тонкая настройка модели требует меньше времени, данных, финансовых и экологических затрат. Также быстрее и проще перебирать различные схемы тонкой настройки, поскольку обучение требует меньше усилий, чем полное предварительное обучение. +Таким образом, дообучение модели требует меньше времени, данных, финансовых и экологических затрат. Также быстрее и проще перебирать различные схемы дообучения, поскольку оно требует меньше усилий, чем полное предварительное обучение. Этот процесс также даст лучшие результаты, чем обучение с нуля (если только у вас нет большого количества данных), поэтому вы всегда должны пытаться использовать предобученную модель — модель, максимально приближенную к поставленной задаче, а потом дообучить ее. @@ -124,8 +126,8 @@ Модель состоит из двух блоков: -* **Encoder (слева)** (кодировщик, энкодер): энкодер получает входные данные и строит их репрезентацию (формирует признаки). Это означает, модель нацелена на "понимание" входных данных. -* **Декодер (справа)** (декодировщик, декодер): декодер использует репрезентации (признаки) энкодера с другими входными данными для создания нужной последовательности. Это означает, что модель нацелена на генерацию выходных данных. +* **Кодировщик (слева)** (англ. encoder): кодировщик получает входные данные и строит их репрезентацию (формирует признаки). Это означает, модель нацелена на "понимание" входных данных. +* **Декодировщик (справа)** (англ. decoder): декодировщик использует репрезентации (признаки) кодировщика с другими входными данными для создания нужной последовательности. Это означает, что модель нацелена на генерацию выходных данных.
Architecture of a Transformers models @@ -134,45 +136,45 @@ Каждая из этих частей может быть использована отдельно, это зависит от задачи: -* **Encoder-модели**: полезны для задач, требющих понимания входных данных, таких как классификация предложений и распознавание именованных сущностей. -* **Decoder-модели**: полезны для генеративных задач, таких как генерация текста. -* **Encoder-decoder модели** или **seq2seq-модели**: полезны в генеративных задачах, требущих входных данных. Например: перевод или саммаризация текста. +* **Модели-кодировщики**: полезны для задач, требующих понимания входных данных, таких как классификация предложений и распознавание именованных сущностей. +* **Модели-декодировщики**: полезны для генеративных задач, таких как генерация текста. +* **Модели типа "кодировщик-декодировщик"** или **seq2seq-модели**: полезны в генеративных задачах, требующих входных данных. Например: перевод или автоматическое реферирование текста. -Мы изучим эти архитектуры глубже в следующих разделах. +Мы изучим эти архитектуры подробнее в следующих разделах. -## Слой внимания или attention +## Слой внимания -Ключевой особенностью трансформеров является наличие в архитектуре специального слоя, называемого слоем внимания или attention'ом. Статья, в которой была описана архитектура трансформера, называлась["Attention Is All You Need"](https://arxiv.org/abs/1706.03762) ("Внимание - все, что вам нужно")! Мы изучим детали этого слоя позже. На текущий момент мы сформулируем механизм его работы так: attention-слой помогает модели "обращать внимание" на одни слова в поданном на вход предложении, а другие слова в той или иной степени игнорировать. И это происходит в процессе анализа каждого слова. +Ключевой особенностью трансформеров является наличие в архитектуре специального слоя, называемого *слоем внимания (англ. attention layer)*. Статья, в которой была впервые представлена архитектура трансформера, называется ["Attention Is All You Need"](https://arxiv.org/abs/1706.03762) ("Внимание - все, что вам нужно")! Мы изучим детали этого слоя позже. На текущий момент мы сформулируем механизм его работы так: слой внимания помогает модели "обращать внимание" на одни слова в поданном на вход предложении, а другие слова в той или иной степени игнорировать. И это происходит в процессе анализа каждого слова. Чтобы поместить это в контекст, рассмотрим задачу перевода текста с английского на французский язык. Для предложения "You like this course", модель должна будет также учитывать соседнее слово "You", чтобы получить правильный перевод слова "like", потому что во французском языке глагол "like" спрягается по-разному в зависимости от подлежащего. Однако остальная часть предложения бесполезна для перевода этого слова. В том же духе при переводе "like" также необходимо будет обратить внимание на слово "course", потому что "this" переводится по-разному в зависимости от того, стоит ли ассоциированное существительное в мужском или женском роде. Опять же, другие слова в предложении не будут иметь значения для перевода "this". С более сложными предложениями (и более сложными грамматическими правилами) модели потребуется уделять особое внимание словам, которые могут оказаться дальше в предложении, чтобы правильно перевести каждое слово. -Такая же концепция применима к любой задаче, связанной с обработкой естесственного языка: слово само по себе имеет некоторое значение, однако значение очень часто зависит от контекста, которым может являться слово (или слова), стоящие вокруг искомого слова. +Такая же концепция применима к любой задаче, связанной с обработкой естественного языка: слово само по себе имеет некоторое значение, однако значение очень часто зависит от контекста, которым может являться слово (или слова), стоящие вокруг искомого слова. -Теперь, когда вы знакомы с идеей attention в целом, посмотрим поближе на архитектуру всего трансформера. +Теперь, когда вы знакомы с идеей внимания в целом, посмотрим поближе на архитектуру всего трансформера. ## Первоначальная архитектура -Архитектура трансформера изначально была разработана для перевода. Во время обучения энкодер получает входные данные (предложения) на определенном языке, а декодер получает те же предложения на желаемом целевом языке. В энкодере слои внимания могут использовать все слова в предложении (поскольку, как мы только что видели, перевод данного слова может зависеть от того, что в предложении находится после и перед ним). Декодер, в свою очерель, работает последовательно и может обращать внимание только на слова в предложении, которые он уже перевел (то есть только на слова перед генерируемым в данный момент словом). Например, когда мы предсказали первые три слова переведенной цели, мы передаем их декодеру, который затем использует все входные данные энкодера, чтобы попытаться предсказать четвертое слово. +Архитектура трансформера изначально была разработана для перевода. Во время обучения кодировщик получает входные данные (предложения) на определенном языке, а декодировщик получает те же предложения на желаемом целевом языке. В кодировщике слои внимания могут использовать все слова в предложении (поскольку, как мы только что видели, перевод данного слова может зависеть от того, что в предложении находится после и перед ним). Декодировщик, в свою очередь, работает последовательно и может обращать внимание только на слова в предложении, которые он уже перевел (то есть только на слова перед генерируемым в данный момент словом). Например, когда мы предсказали первые три слова переведенной цели, мы передаем их декодировщику, который затем использует все входные данные кодировщика, чтобы попытаться предсказать четвертое слово. -Чтобы ускорить процесс во время обучения (когда модель имеет доступ к целевым предложениям), декодер получает целевое предложение полностью, но ему не разрешается использовать будущие слова (если он имел доступ к слову в позиции 2 при попытке предсказать слово на позиции 2, задача не будет сложной!). Например, при попытке предсказать четвертое слово уровень внимания будет иметь доступ только к словам в позициях с 1 по 3. +Чтобы ускорить процесс во время обучения (когда модель имеет доступ к целевым предложениям), декодировщик получает целевое предложение полностью, но ему не разрешается использовать будущие слова (если он имел доступ к слову в позиции 2 при попытке предсказать слово на позиции 2, задача не будет сложной!). Например, при попытке предсказать четвертое слово слой внимания будет иметь доступ только к словам в позициях с 1 по 3. -Первоначальная архитектура Transformer выглядела так: энкодер слева и декодер справа: +Первоначальная архитектура Transformer выглядела так: кодировщик слева и декодировщик справа:
Architecture of a Transformers models
-Обратите внимание, что первый уровень внимания в блоке декодера обращает внимание на все (прошлые) входные данные декодера, а второй уровень внимания использует выходные данные первого энкодера. Таким образом, он может получить доступ ко всему входному предложению, чтобы наилучшим образом предсказать текущее слово. Это очень полезно, так как разные языки могут иметь грамматические правила, которые располагают слова в разном порядке, или некоторый контекст, предоставленный в предложении далеко от текущего слова. Конекст может быть полезен для определения наилучшего перевода данного слова. +Обратите внимание, что первый слой внимания в блоке декодировщика обращает внимание на все (прошлые) входные данные декодировщика, а второй слой внимания использует выходные данные кодировщика. Таким образом, он может получить доступ ко всему входному предложению, чтобы наилучшим образом предсказать текущее слово. Это очень полезно, так как разные языки могут иметь грамматические правила, которые располагают слова в разном порядке, или некоторый контекст, предоставленный в предложении далеко от текущего слова. Контекст может быть полезен для определения наилучшего перевода данного слова. -*Attention-mask* (маска внимания) также может использоваться в энкодере/декодере, чтобы модель не обращала внимания на некоторые специальные слова — например, специальное несуществующее слово-заполнитель (служебный токен), используемое для придания всем входным данным одинаковой длины при группировке предложений. +*Маска внимания (англ. attention mask)* также может использоваться в кодировщике/декодировщике, чтобы модель не обращала внимания на некоторые специальные слова — например, специальное несуществующее слово-заполнитель (англ. padding), используемое для придания всем входным данным одинаковой длины при группировке предложений. -## Архитектуры и контрольные точки +## Архитектуры и чекпоинты -По мере погружения в трансформеры, вы будете встречать термины *архитектуры* и *контрольные точки* (checkpoints) в смысле *модели*. Эти термины имеют разный смысл: +По мере погружения в трансформеры, вы будете встречать термины *архитектуры* и *чекпоинты* (англ. checkpoints) в смысле *модели*. Эти термины имеют разный смысл: **Архитектура** - скелет модели -- слои, связи и операции, которые выполняются в модели. -**Контрольная точка** - веса модели, которые могут быть загружены для конкретной архитектуры. +**Чекпоинт** - веса модели, которые могут быть загружены для конкретной архитектуры. **Модель** - зонтичный термин, который может означать и архитектуру, и веса для конкретной архитектуры. В этом курсе мы будем точнее использовать термины *архитектуры* и *чекпоинт*, если это будет важно для лучшего понимания. Например, BERT - это архитектура, а `bert-base-cased` - набор весов, подготовленный Google к первому выпуску BERT'а, - это чекпоинт. Однако можно сказать и "модель BERT", и "модель bert-base-cased". diff --git a/chapters/ru/chapter1/5.mdx b/chapters/ru/chapter1/5.mdx index 1cc0839a3..f4b28c5c8 100644 --- a/chapters/ru/chapter1/5.mdx +++ b/chapters/ru/chapter1/5.mdx @@ -1,4 +1,4 @@ -# Модели энкодеров +# Модели-кодировщики -Энкодеры используют только компонент кодировщика трансформера. На каждом этапе слой внимания может использовать все слова исходного предложения. Эти модели часто характеризуют как имеющие двунаправленное внимание ("bi-directional attention"), и часто называют моделями *автоэнкодеров*. +Кодировщики используют только компонент кодировщика трансформера. На каждом этапе слой внимания может использовать все слова исходного предложения. Эти модели часто характеризуют как имеющие двунаправленное внимание (англ. bi-directional attention), и часто называют моделями *автокодировщиками*. -Предварительное обучение этих моделей обычно заключаетс в том, чтобы как-то исказить данное предложение (например, путем маскировки в нем случайных слов) и поставить перед моделью задачу найти или восстановить исходное предложение. +Предварительное обучение этих моделей обычно заключается в том, чтобы как-то исказить предложение (например, путем маскировки в нем случайных слов) и поставить перед моделью задачу найти или восстановить исходное предложение. -Энкодеры лучше всего подходят для задач, требующих _понимания_ всего предложения, таких как классификация предложений, распознавание именованных сущностей (и, в более общем смысле, классификация слов) и ответы на вопросы с извлечением информации из контекста. +Кодировщики лучше всего подходят для задач, требующих _понимания_ всего предложения, таких как классификация предложений, распознавание именованных сущностей (и, в более общем смысле, классификация слов) и ответы на вопросы с извлечением информации из контекста (выделительные вопросно-ответные системы). К представителям этого семейства моделей относятся: diff --git a/chapters/ru/chapter1/6.mdx b/chapters/ru/chapter1/6.mdx index 815f267d5..b75dd4ea4 100644 --- a/chapters/ru/chapter1/6.mdx +++ b/chapters/ru/chapter1/6.mdx @@ -1,4 +1,4 @@ -# Модели декодеров +# Модели-декодировщики -Декодировщики используют только компонент декодер трансформера. На каждом этапе для текущего слова слой внимания может получить доступ только к словам, которые были расположены до текущего в предложении. Такие модели часто называются *авторегрессионными моделями*. +Декодировщики используют только компонент декодирования трансформера. На каждом этапе для текущего слова слой внимания может получить доступ только к словам, которые были расположены до него в предложении. Такие модели часто называются *авторегрессионными моделями*. -Процесс предобучения декодеров обычно заключается в предсказании следующего слова в предложении. ё +Процесс предобучения декодировщиков обычно заключается в предсказании следующего слова в предложении. Такие модели лучше всего подходят для задач, связанных с генерацией текста. diff --git a/chapters/ru/chapter1/7.mdx b/chapters/ru/chapter1/7.mdx index 86be51145..6f13b4683 100644 --- a/chapters/ru/chapter1/7.mdx +++ b/chapters/ru/chapter1/7.mdx @@ -7,11 +7,11 @@ -Энкодер-декодер модели (также называемые *sequence-to-sequence models*) используют обе части трансформера. На каждом этапе слой внимания энкодера получает доступ ко всем словам в исходной последовательности, тогда как слой внимания декодера получает доступ только к тем словам, которые позиционированы до текущего слова. +Модели типа кодировщик-декодировщик (также называемые *sequence-to-sequence models*) используют обе части трансформера. На каждом этапе слой внимания кодировщика получает доступ ко всем словам в исходной последовательности, тогда как слой внимания декодировщика получает доступ только к тем словам, которые расположены до текущего слова. -Предобучение таких моделей может быть проведено по аналогии с процессом предобучения энкодера или декодера, но обычно это происходит сложнее. Например, модель [T5](https://huggingface.co/t5-base) была предобучена путем замены случайных фрагментов текста (фрагменты могут содержать несколько слов) на специальную маску, цель модели - предсказать текст, который заменила маска. +Предобучение таких моделей может быть выполнено на задачах, используемых для предобучения моделей кодировщиков или декодировщиков, но обычно все немного сложнее. Например, модель [T5](https://huggingface.co/t5-base) была предобучена путем замены случайных фрагментов текста (фрагменты могут содержать несколько слов) на специальную маску, цель модели - предсказать текст, который заменила маска. -Модели seq2seq лучше всего подходят для задач генерации новых предложений, зависящих от входного массива данных, например: саммаризация текста, перевод или генерация ответов на вопросы. +Модели seq2seq лучше всего подходят для задач генерации новых предложений, зависящих от входного массива данных, например: автоматическое реферирование текста, перевод или в генеративных вопросно-ответных системах. Представителями этого семейства являются: diff --git a/chapters/ru/chapter1/8.mdx b/chapters/ru/chapter1/8.mdx index fe5db9f6b..edc759829 100644 --- a/chapters/ru/chapter1/8.mdx +++ b/chapters/ru/chapter1/8.mdx @@ -7,7 +7,7 @@ {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/ru/chapter1/section8.ipynb"}, ]} /> -Если вы намерены использовать предварительно обученную модель или точно настроенную версию в рабочей среде, имейте в виду, что, хотя эти модели являются мощными инструментами, они имеют ограничения. Самая большая из них заключается в том, что для предварительной подготовки на больших объемах данных исследователи часто очищают весь контент, который они могут найти, беря как лучшее, так и худшее из того, что доступно в Интернете. +Если вы намерены использовать предварительно обученную модель или ее дообученную версию в рабочей среде, имейте в виду, что, хотя эти модели являются мощными инструментами, они имеют ограничения. Самое большое из них заключается в том, что для предварительного обучения на больших объемах данных исследователи часто очищают весь контент, который они могут найти, беря как лучшее, так и худшее из того, что доступно в Интернете. Для иллюстрации вернемся к примеру пайплайна `fill-mask` с моделью BERT: @@ -29,5 +29,5 @@ print([r["token_str"] for r in result]) На просьбу вставить пропущенное слово в этих двух предложениях модель дает только один ответ без гендерной принадлежности (официант/официантка). Другие рабочие профессии обычно ассоциируются с одним конкретным полом — и да, проститутка попала в топ-5 вариантов, которые модель ассоциирует с "женщиной" и "работой". Это происходит даже несмотря на то, что BERT — одна из редких моделей трансформеров, созданная не путем сбора данных со всего Интернета, а с использованием явно нейтральных данных (он обучен на [английской Википедии](https://huggingface.co/datasets/wikipedia) и наборе данных [BookCorpus](https://huggingface.co/datasets/bookcorpus). -Поэтому, когда вы используете эти инструменты, вам нужно помнить, что исходная модель, которую вы используете, может очень легко генерировать сексистский, расистский или гомофобный контент. Тонкая настройка модели на ваших данных не избавит вас от этой внутренней предвзятости. +Поэтому, когда вы используете эти инструменты, вам нужно помнить, что исходная модель, которую вы используете, может очень легко генерировать сексистский, расистский или гомофобный контент. Дообучение модели на ваших данных не сможет устранить эту внутреннюю предвзятость. diff --git a/chapters/ru/chapter1/9.mdx b/chapters/ru/chapter1/9.mdx index f808b04d0..98c0abaac 100644 --- a/chapters/ru/chapter1/9.mdx +++ b/chapters/ru/chapter1/9.mdx @@ -7,11 +7,11 @@ В этой главе вы увидели, как подходить к различным задачам NLP, используя высокоуровневую функцию `pipeline()` из библиотеки 🤗 Transformers. Вы также увидели, как искать и использовать модели в Hub, а также как использовать Inference API для тестирования моделей прямо в браузере. -Мы обсудили, как трансформеры работают на высоком уровне, и поговорили о важности трансферного обучения и тонкой настройки. Ключевым аспектом является то, что вы можете использовать всю архитектуру или только энкодер или декодер, в зависимости от того, какую задачу вы хотите решить. Следующая таблица резюмирует это: +Мы обсудили, как трансформеры работают на высоком уровне, и поговорили о важности трансферного обучения и дообучения. Ключевым аспектом является то, что вы можете использовать всю архитектуру или только кодировщик или декодировщик, в зависимости от того, какую задачу вы хотите решить. Следующая таблица резюмирует это: -| Модель | Примеры | Задачи | -|-----------------|--------------------------------------------|----------------------------------------------------------------------------------| -| Энкодер | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa | Классификация предложений, распознавание именованных сущностей, генерация ответов на вопросы с извлечением информации | -| Декодер | CTRL, GPT, GPT-2, Transformer XL | Генерация текста | -| Энкодер-декодер | BART, T5, Marian, mBART | Саммаризация, перевод, генеративный подход к ответам на вопросы | +| Модель | Примеры | Задачи | +|-------------------------|--------------------------------------------|---------------------------------------------------------------------------------------------------------| +| Кодировщик | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa | Классификация предложений, распознавание именованных сущностей, выделительные вопросно-ответные системы | +| Декодировщик | CTRL, GPT, GPT-2, Transformer XL | Генерация текста | +| Кодировщик-декодировщик | BART, T5, Marian, mBART | Автоматическое реферирование, перевод, генеративные вопросно-ответные системы | diff --git a/chapters/ru/glossary/1.mdx b/chapters/ru/glossary/1.mdx new file mode 100644 index 000000000..00e1217c8 --- /dev/null +++ b/chapters/ru/glossary/1.mdx @@ -0,0 +1,146 @@ +# Глоссарий + +| Оригинал | Перевод | +|---------------------------------|-----------------------------------------| +| Abstraction | абстракция | +| Account | учетная запись | +| Accuracy | accuracy | +| Artificial General Intelligence | сильный искусственный интеллект | +| Attention | внимание | +| Attention mask (layer) | маска внимания (слой) | +| Backward Pass\* | обратный проход | +| Batch | батч | +| Bias | смещение | +| Causal Language Modeling | каузальное языковое моделирование | +| Chapter | глава | +| Checkpoint(s) | чекпоинт | +| Class | класс | +| Classification | классификация | +| Code | код | +| Colab Notebook | блокнот Colab | +| Command | команда | +| Computer Vision | компьютерное зрение | +| Configuration | конфигурация | +| Course | курс | +| Decoder | декодировщик / декодер | +| Dependency | зависимость | +| Deployment | развертывание (программного обеспечения)| +| Development | разработка | +| Dictionary | dictionary | +| Distribution | распределение | +| Download | download | +| Encoder | кодировщик / энкодер | +| Extractive question answering | выделительная вопросно-ответная система | +| F1 score | F1-мера | +| Feature | признак | +| Fine-tune | дообучать | +| Fine-tuning | дообучение | +| Folder | папка / директория | +| Forward Pass\* | прямой проход | +| Function | функция | +| Generative question answering | генеративная вопросно-ответная система | +| Google | Google | +| Hugging Face | Hugging Face | +| Incompatibility | несовместимость | +| Inference | инференс | +| Input | вход | +| Input data | входные данные | +| Label (verb) | размечать | +| Label (subj) | метка класса | +| Layer | слой | +| Library | библиотека | +| Linux | Linux | +| Load | загружать | +| Loss function | функция потерь | +| Machine Learning | машинное обучение | +| macOS | macOS | +| Mask | маска | +| Mask Filling | предсказание замаскированного токена | +| Mask Token | токен-маска | +| Masked Language Modeling | маскированное языковое моделирование | +| Model | модель | +| Model Hub | Model Hub | +| Module | модуль | +| Named Entities | именованные сущности | +| Named Entity Recognition | распознавание именованных сущностей | +| Natural Language Processing | обработка естественного языка | +| Output | выход | +| Package | пакет | +| Package Manager | менеджер пакетов | +| Padding (объект) | padding | +| Padding (действие) | дополнение | +| Parameter | параметр | +| Postprocessing | постобработка / последующая обработка | +| Preprocessing | предобработка / предварительная обработка| +| Pretraining | предварительное обучение / предобучение | +| Pretrained model | предварительно обученная модель | +| Pretrained model | предобученная модель | +| Prompt | начальный текст | +| Python | Python | +| Pytorch | Pytorch | +| Question Answering | вопросно-ответная система | +| Save | сохранять | +| Sample | пример | +| Script | скрипт | +| Self-Attention | самовнимание | +| Self-Contained | самостоятельный | +| Sentiment analysis | анализ тональности текста (сентимент-анализ)| +| Sequence-to-sequence models | sequence-to-sequence модель | +| Setup | установка (программы) / настройка (среды)| +| Speech Processing | обработка речи | +| Speech Recognition | распознавание речи | +| Summarization | суммаризация | +| Target | целевая переменная | +| Task | задача | +| TensorFlow | Tensorflow | +| Terminal | терминал | +| Text generation | генерация текста | +| Tokenizer | Tokenizer (библиотека) / токенизатор | +| Train | обучение (обучать) | +| Transfer Learning | Transfer Learning / трансферное обучение| +| Transformer | трансформер | +| Transformer models | архитектура трансформер | +| Translation | (машинный) перевод | +| Virtual Environment | виртуальное окружение | +| Weight | вес | +| Weights | веса | +| Windows | Windows | +| Working Environment | рабочее окружение | +| Workload | нагрузка | +| Workspace | Workspace | +| Zero-shot classification | zero-shot классификация | +======= + +\* Данные термины могут употребляться взаимозаменяемо с их английской версией + +## Сокращения + +| Оригинал | Перевод | +|-----------|-------------| +| NLP | NLP | +| API | API | +| GPU | GPU | +| TPU | TPU | +| ML | ML | + +## Notes + +Please refer to [TRANSLATING.txt](/chapters/ru/TRANSLATING.txt) for a translation guide. Here are some excerpts relevant to the glossary: + +- Refer and contribute to the glossary frequently to stay on top of the latest + choices we make. This minimizes the amount of editing that is required. + Add new terms alphabetically sorted. + +- The Russian language accepts English words especially in modern contexts more + than many other languages (i.e. Anglicisms). Check for the correct usage of + terms in computer science and commonly used terms in other publications. + +- Don't translate industry-accepted acronyms. e.g. TPU or GPU. + +- If translating a technical word, keep the choice of Russian translation consistent. + This does not apply for non-technical choices, as in those cases variety actually + helps keep the text engaging. + +- Be exact when choosing equivalents for technical words. Package is package. + Library is library. Don't mix and match. + diff --git a/chapters/zh-CN/_toctree.yml b/chapters/zh-CN/_toctree.yml index ff3aaaa6d..08335aaf7 100644 --- a/chapters/zh-CN/_toctree.yml +++ b/chapters/zh-CN/_toctree.yml @@ -69,7 +69,7 @@ - local: chapter4/1 title: The Hugging Face Hub - local: chapter4/2 - title: 使用预训练的模型 + title: 使用预训练模型 - local: chapter4/3 title: 分享预训练的模型 - local: chapter4/4 diff --git a/chapters/zh-CN/chapter0/1.mdx b/chapters/zh-CN/chapter0/1.mdx index 5e46295d1..45af8dfc9 100644 --- a/chapters/zh-CN/chapter0/1.mdx +++ b/chapters/zh-CN/chapter0/1.mdx @@ -1,4 +1,4 @@ -# 课程简介 +# 课程简介 [[课程简介]] 欢迎来到拥抱脸课程!本介绍将指导您设置工作环境。如果您刚开始学习本课程,我们建议您先阅读[第一章](/course/chapter1), 然后再回来设置您的环境,以便您可以自己尝试运行代码。 @@ -10,7 +10,7 @@ 大多数课程和服务都依赖于您拥有 Hugging Face 帐户。我们建议现在创建一个:[创建一个账号](https://huggingface.co/join). -## 使用 Google Colab 笔记本 +## 使用 Google Colab 笔记本 [[使用 Google Colab 笔记本]] 使用 Colab notebook 是最简单的设置;可以在浏览器中启动Notebook并直接开始编写自己的代码! @@ -46,7 +46,7 @@ import transformers 这将需要一些时间,但当完成之后您就做好学习剩下的课程的环境准备了! -## 使用 Python 虚拟环境 +## 使用 Python 虚拟环境 [[使用 Python 虚拟环境]] 如果您更喜欢使用 Python 虚拟环境,那么第一步是在您的系统上安装 Python。我们建议您按照[这个教程](https://realpython.com/installing-python/)进行配置。 @@ -99,7 +99,7 @@ which python /home//transformers-course/.env/bin/python ``` -### 安装依赖 +### 安装依赖 [[安装依赖]] 与使用 Google Colab 实例的上一节一样,您现在需要安装继续所需的包。 同样,您可以使用 `pip` 包管理器安装 🤗 Transformers 的开发版本: diff --git a/chapters/zh-CN/chapter1/1.mdx b/chapters/zh-CN/chapter1/1.mdx index fb0ccc351..18d638949 100644 --- a/chapters/zh-CN/chapter1/1.mdx +++ b/chapters/zh-CN/chapter1/1.mdx @@ -1,18 +1,18 @@ -# 本章简介 +# 本章简介 [[本章简介]] -## 欢迎来到🤗课程 +## 欢迎来到🤗课程 [[欢迎来到🤗课程]] 本课程将使用 Hugging Face 生态系统中的库——🤗 Transformers、🤗 Datasets、🤗 Tokenizers 和 🤗 Accelerate——以及 Hugging Face Hub 教你自然语言处理 (NLP)。它是完全免费的,并且没有广告。 -## 有什么是值得期待的? +## 有什么是值得期待的?[[有什么是值得期待的?]] 以下是课程的简要概述: @@ -33,23 +33,74 @@ 完成本课程后,我们建议您查看 [DeepLearning.AI的自然语言处理系列课程](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh),其中涵盖了广泛的传统 NLP 模型,如朴素贝叶斯和 LSTM,这些模型非常值得了解! -## 我们是谁? +## 我们是谁?[[我们是谁?]] 关于作者: -**Matthew Carrigan** 是 Hugging Face 的机器学习工程师。他住在爱尔兰都柏林,之前在 Parse.ly 担任机器学习工程师,在此之前,他在Trinity College Dublin担任博士后研究员。他不相信我们会通过扩展现有架构来实现 AGI,但无论如何都对机器人充满希望。 +[**Abubakar Abid**](https://huggingface.co/abidlabs) 在斯坦福大学获得应用机器学习博士学位。 在攻读博士学位期间,他创立了 [Gradio](https://github.com/gradio-app/gradio),这是一个开源 Python 库,已用于构建超过 600,000 个机器学习演示。 Gradio 被 Hugging Face 收购,Abubakar 现在是该公司的机器学习团队负责人。 -**Lysandre Debut** 是 Hugging Face 的机器学习工程师,从早期的开发阶段就一直致力于 🤗 Transformers 库。他的目标是通过使用非常简单的 API 开发工具,让每个人都可以使用 NLP。 +[**Matthew Carrigan**](https://huggingface.co/Rocketknight1) 是 Hugging Face 的机器学习工程师。他住在爱尔兰都柏林,之前在 Parse.ly 担任机器学习工程师,在此之前,他在Trinity College Dublin担任博士后研究员。他不相信我们会通过扩展现有架构来实现 AGI,但无论如何都对机器人充满希望。 -**Sylvain Gugger** 是 Hugging Face 的一名研究工程师,也是 🤗Transformers库的核心维护者之一。此前,他是 fast.ai 的一名研究科学家,他与Jeremy Howard 共同编写了[Deep Learning for Coders with fastai and Py Torch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)。他的主要研究重点是通过设计和改进允许模型在有限资源上快速训练的技术,使深度学习更容易普及。 +[**Lysandre Debut**](https://huggingface.co/lysandre) 是 Hugging Face 的机器学习工程师,从早期的开发阶段就一直致力于 🤗 Transformers 库。他的目标是通过使用非常简单的 API 开发工具,让每个人都可以使用 NLP。 -**Merve Noyan** 是 Hugging Face 的开发者倡导者,致力于开发工具并围绕它们构建内容,以使每个人的机器学习平民化。 +[**Sylvain Gugger**](https://huggingface.co/sgugger) 是 Hugging Face 的一名研究工程师,也是 🤗Transformers库的核心维护者之一。此前,他是 fast.ai 的一名研究科学家,他与Jeremy Howard 共同编写了[Deep Learning for Coders with fastai and Py Torch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)。他的主要研究重点是通过设计和改进允许模型在有限资源上快速训练的技术,使深度学习更容易普及。 -**Lucile Saulnier** 是 Hugging Face 的机器学习工程师,负责开发和支持开源工具的使用。她还积极参与了自然语言处理领域的许多研究项目,例如协作训练和 BigScience。 +[**Dawood Khan**](https://huggingface.co/dawoodkhan82) 是 Hugging Face 的机器学习工程师。 他来自纽约,毕业于纽约大学计算机科学专业。 在担任 iOS 工程师几年后,Dawood 辞职并与其他联合创始人一起创办了 Gradio。 Gradio 最终被 Hugging Face 收购。 -**Lewis Tunstall** 是 Hugging Face 的机器学习工程师,专注于开发开源工具并使更广泛的社区可以使用它们。他也是即将出版的一本书[O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/)的作者之一。 +[**Merve Noyan**](https://huggingface.co/sgugger) 是 Hugging Face 的开发者倡导者,致力于开发工具并围绕它们构建内容,以使每个人的机器学习平民化。 -**Leandro von Werra** 是 Hugging Face 开源团队的机器学习工程师,也是即将出版的一本书[O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/)的作者之一。他拥有多年的行业经验,通过在整个机器学习堆栈中工作,将 NLP 项目投入生产。 +[**Lucile Saulnier**](https://huggingface.co/SaulLu) 是 Hugging Face 的机器学习工程师,负责开发和支持开源工具的使用。她还积极参与了自然语言处理领域的许多研究项目,例如协作训练和 BigScience。 + +[**Lewis Tunstall**](https://huggingface.co/lewtun) 是 Hugging Face 的机器学习工程师,专注于开发开源工具并使更广泛的社区可以使用它们。他也是即将出版的一本书[O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/)的作者之一。 + +[**Leandro von Werra**](https://huggingface.co/lvwerra) 是 Hugging Face 开源团队的机器学习工程师,也是即将出版的一本书[O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/)的作者之一。他拥有多年的行业经验,通过在整个机器学习堆栈中工作,将 NLP 项目投入生产。 + +## FAQ[[faq]] + +这里有一些经常被提到的问题: + +- **参加本课程是否会获得认证?** +目前,我们没有获得此课程的任何认证。 但是,我们正在为 Hugging Face 生态系统制定认证计划——敬请期待! + +- **我应该在这门课程上花多少时间?** +本课程的每一章都设计为在 1 周内完成,每周大约需要 6-8 小时的学习时间。 但是,您可以花尽可能多的时间来完成课程。 + +- **如果我有问题,我可以在哪里提问?** +如果您对课程的任何部分有疑问,只需单击页面顶部的“*提问*”横幅,系统就会自动重定向到 [Hugging Face 论坛](https:// discuss.huggingface.co/): + +拥抱脸论坛链接 + +请注意,如果您想在完成课程后进行更多练习,论坛上还提供了[项目灵感](https://discuss.huggingface.co/c/course/course-event/25) 列表。 + +- **我在哪里可以获得课程的代码?** +对于每个部分,单击页面顶部的横幅可以在 Google Colab 或 Amazon SageMaker Studio Lab 中运行代码: + +拥抱脸课程笔记本链接 + +包含课程所有代码的 Jupyter 笔记本托管在 [`huggingface/notebooks`](https://github.com/huggingface/notebooks) 仓库中。 如果您希望在本地生成它们,请查看 GitHub 上 [`course`](https://github.com/huggingface/course#-jupyter-notebooks) 仓库中的说明。 + + +- **我如何为课程做出贡献?** +有很多方法可以为课程做出贡献! 如果您发现拼写错误或错误,请在 [`course`](https://github.com/huggingface/course) 仓库中提出问题。 如果您想帮助将课程翻译成您的母语,请在[此处](https://github.com/huggingface/course#translating-the-course-into-your-language) 查看说明。 + +- ** 每个翻译的选择是什么?** +每个翻译都有一个词汇表和“TRANSLATING.txt”文件,其中详细说明了为机器学习术语等所做的选择。您可以在 [此处](https://github.com/huggingface/course/blob/main/chapters/de/TRANSLATING.txt)。 + + +- **我可以使用这门课程再次进行创作吗?** +当然! 该课程是根据宽松的 [Apache 2 许可证](https://www.apache.org/licenses/LICENSE-2.0.html) 发布的。 这意味着您必须按照诚信的原则,提供许可证的链接,并指出是否进行了更改。您可以以任何合理的方式这样做,但不能以任何表明许可方认可您或您的使用的方式。 如果您想引用该课程,请使用以下 BibTeX: + +``` +@misc{huggingfacecourse, + author = {Hugging Face}, + title = {The Hugging Face Course, 2022}, + howpublished = "\url{https://huggingface.co/course}", + year = {2022}, + note = "[Online; accessed ]" +} +``` + +## 让我们开始吧![[让我们开始吧!]] 你准备好了吗?在本章中,您将学习: * 如何使用 `pipeline()` 函数解决文本生成、分类等NLP任务 diff --git a/chapters/zh-CN/chapter1/10.mdx b/chapters/zh-CN/chapter1/10.mdx index 3e2951036..84a5cb639 100644 --- a/chapters/zh-CN/chapter1/10.mdx +++ b/chapters/zh-CN/chapter1/10.mdx @@ -1,6 +1,6 @@ -# 章末小测试 +# 章末小测试 [[章末小测试]] 准备. -## Transformer被应用于各个方面! +## Transformer被应用于各个方面! [[Transformer被应用于各个方面!]] Transformer 模型用于解决各种 NLP 任务,就像上一节中提到的那样。以下是一些使用 Hugging Face 和 Transformer 模型的公司和组织,他们也通过分享他们的模型回馈社区: ![使用 Hugging Face 的公司](https://huggingface.co/course/static/chapter1/companies.PNG) @@ -26,7 +26,7 @@ Transformer 模型用于解决各种 NLP 任务,就像上一节中提到的那 在深入研究 Transformer 模型的底层工作原理之前,让我们先看几个示例,看看它们如何用于解决一些有趣的 NLP 问题。 -## 使用pipelines +## 使用pipelines [[使用pipelines]] (这里有一个视频,但是国内可能打不开,译者注) @@ -77,7 +77,7 @@ classifier( 让我们来看看其中的一些吧! -## 零样本分类 +## 零样本分类 [[零样本分类]] 我们将首先处理一项非常具挑战性的任务,我们需要对尚未标记的文本进行分类。这是实际项目中的常见场景,因为注释文本通常很耗时并且需要领域专业知识。对于这项任务**zero-shot-classification**pipeline非常强大:它允许您直接指定用于分类的标签,因此您不必依赖预训练模型的标签。下面的模型展示了如何使用这两个标签将句子分类为正面或负面——但也可以使用您喜欢的任何其他标签集对文本进行分类。 ```python @@ -100,7 +100,7 @@ classifier( ✏️**快来试试吧!**使用您自己的序列和标签,看看模型的行为。 -## 文本生成 +## 文本生成 [[文本生成]] 现在让我们看看如何使用pipeline来生成一些文本。这里的主要使用方法是您提供一个提示,模型将通过生成剩余的文本来自动完成整段话。这类似于许多手机上的预测文本功能。文本生成涉及随机性,因此如果您没有得到相同的如下所示的结果,这是正常的。 ```python @@ -122,7 +122,7 @@ generator("In this course, we will teach you how to") ✏️**快来试试吧!**使用 num_return_sequences 和 max_length 参数生成两个句子,每个句子 15 个单词。 -## 在pipeline中使用 Hub 中的其他模型 +## 在pipeline中使用 Hub 中的其他模型 [[在pipeline中使用 Hub 中的其他模型]] 前面的示例使用了默认模型,但您也可以从 Hub 中选择特定模型以在特定任务的pipeline中使用 - 例如,文本生成。转到[模型中心(hub)](https://huggingface.co/models)并单击左侧的相应标签将会只显示该任务支持的模型。[例如这样](https://huggingface.co/models?pipeline_tag=text-generation)。 让我们试试 [**distilgpt2**](https://huggingface.co/distilgpt2) 模型吧!以下是如何在与以前相同的pipeline中加载它: @@ -151,12 +151,12 @@ generator( ✏️**快来试试吧!**使用标签筛选查找另一种语言的文本生成模型。使用小组件测试并在pipeline中使用它! -## 推理 API +## 推理 API [[推理 API]] 所有模型都可以使用 Inference API 直接通过浏览器进行测试,该 API 可在 [Hugging Face 网站](https://huggingface.co/)上找到。通过输入自定义文本并观察模型的输出,您可以直接在此页面上使用模型。 小组件形式的推理 API 也可作为付费产品使用,如果您的工作流程需要它,它会派上用场。有关更多详细信息,请参阅[定价页面](https://huggingface.co/pricing)。 -## Mask filling +## Mask filling [[Mask filling]] 您将尝试的下一个pipeline是 **fill-mask**。此任务的想法是填充给定文本中的空白: ```python from transformers import pipeline @@ -180,7 +180,7 @@ unmasker("This course will teach you all about models.", top_k=2) ✏️**快来试试吧!**在 Hub 上搜索基于 bert 的模型并在推理 API 小组件中找到它的掩码。这个模型对上面pipeline示例中的句子预测了什么? -## 命名实体识别 +## 命名实体识别 [[命名实体识别]] 命名实体识别 (NER) 是一项任务,其中模型必须找到输入文本的哪些部分对应于诸如人员、位置或组织之类的实体。让我们看一个例子: ```python from transformers import pipeline @@ -202,7 +202,7 @@ ner("My name is Sylvain and I work at Hugging Face in Brooklyn.") ✏️**快来试试吧!**在模型中心(hub)搜索能够用英语进行词性标注(通常缩写为 POS)的模型。这个模型对上面例子中的句子预测了什么? -## 问答系统 +## 问答系统 [[问答系统]] 问答pipeline使用来自给定上下文的信息回答问题: ```python from transformers import pipeline @@ -221,7 +221,7 @@ klyn", ``` 请注意,此pipeline通过从提供的上下文中提取信息来工作;它不会凭空生成答案。 -## 文本摘要 +## 文本摘要 [[文本摘要]] 文本摘要是将文本缩减为较短文本的任务,同时保留文本中的主要(重要)信息。下面是一个例子: ```python @@ -262,7 +262,7 @@ summarizer( ``` 与文本生成一样,您指定结果的 **max_length** 或 **min_length**。 -## 翻译 +## 翻译 [[翻译]] 对于翻译,如果您在任务名称中提供语言对(例如“**translation_en_to_fr**”),则可以使用默认模型,但最简单的方法是在[模型中心(hub)](https://huggingface.co/models)选择要使用的模型。在这里,我们将尝试从法语翻译成英语: ```python diff --git a/chapters/zh-CN/chapter1/4.mdx b/chapters/zh-CN/chapter1/4.mdx index a2f8a839f..c056b2b10 100644 --- a/chapters/zh-CN/chapter1/4.mdx +++ b/chapters/zh-CN/chapter1/4.mdx @@ -1,4 +1,4 @@ -# Transformers 是如何工作的? +# Transformers 是如何工作的? [[Transformers 是如何工作的?]]
-## Transformer是大模型 +## Transformer是大模型 [[Transformer是大模型]] 除了一些特例(如 DistilBERT)外,实现更好性能的一般策略是增加模型的大小以及预训练的数据量。 @@ -81,8 +81,9 @@ Transformers是由一个团队领导的(非常大的)模型项目,该团 这就是为什么共享语言模型至关重要:共享经过训练的权重,当遇见新的需求时在预训练的权重之上进行微调,可以降低训练模型训练的算力和时间消耗,降低全球的总体计算成本和碳排放。 +顺便说一句,您可以通过多种工具评估模型训练的碳排放。 例如 [ML CO2 Impact](https://mlco2.github.io/impact/) 或集成在 🤗 Transformers 中的 [Code Carbon]( https://codecarbon.io/)。 要了解更多相关信息,您可以阅读这篇[博客文章](https://huggingface.co/blog/carbon-emissions-on-the-hub),其中将向您展示如何生成 `emissions.csv` 文件估计训练的碳足迹,这里还有 🤗 Transformers 关于碳足迹的[文档](https://huggingface.co/docs/hub/model-cards-co2)。 -## 迁移学习 +## 迁移学习 [[迁移学习]] @@ -112,12 +113,12 @@ Transformers是由一个团队领导的(非常大的)模型项目,该团 这个过程也会比从头开始的训练(除非你有很多数据)取得更好的效果,这就是为什么你应该总是尝试利用一个预训练的模型--一个尽可能接近你手头的任务的模型--并对其进行微调。 -## 一般的体系结构 +## 一般的体系结构 [[一般的体系结构]] 在这一部分,我们将介绍Transformer模型的一般架构。如果你不理解其中的一些概念,不要担心;下文将详细介绍每个组件。 -## 介绍 +## 介绍 [[介绍]] 该模型主要由两个块组成: @@ -137,7 +138,7 @@ Transformers是由一个团队领导的(非常大的)模型项目,该团 在后面的部分中,我们将单独地深入研究这些体系结构。 -## 注意力层 +## 注意力层 [[注意力层]] Transformer模型的一个关键特性是*注意力层*。事实上,介绍Transformer架构的文章的标题是[“注意力就是你所需要的”](https://arxiv.org/abs/1706.03762)! 我们将在课程的后面更加深入地探索注意力层;现在,您需要知道的是,这一层将告诉模型在处理每个单词的表示时,要特别重视您传递给它的句子中的某些单词(并且或多或少地忽略其他单词)。 @@ -147,7 +148,7 @@ Transformer模型的一个关键特性是*注意力层*。事实上,介绍Tran 现在您已经了解了注意力层的含义,让我们更仔细地了解Transformer架构。 -## 原始的结构 +## 原始的结构 [[原始的结构]] Transformer架构最初是为翻译而设计的。在训练期间,编码器接收特定语言的输入(句子),而解码器需要输出对应语言的翻译。在编码器中,注意力层可以使用一个句子中的所有单词(正如我们刚才看到的,给定单词的翻译可以取决于它在句子中的其他单词)。然而,解码器是按顺序工作的,并且只能注意它已经翻译过的句子中的单词。例如,当我们预测了翻译目标的前三个单词时,我们将它们提供给解码器,然后解码器使用编码器的所有输入来尝试预测第四个单词。 @@ -164,7 +165,7 @@ Transformer架构最初是为翻译而设计的。在训练期间,编码器接 也可以在编码器/解码器中使用*注意力遮罩层*,以防止模型注意某些特殊单词。例如,在批处理句子时,填充特殊词使所有句子的长度一致。 -## 架构与参数 +## 架构与参数 [[架构与参数]] 在本课程中,当我们深入探讨Transformers模型时,您将看到 架构、参数和模型 diff --git a/chapters/zh-CN/chapter1/5.mdx b/chapters/zh-CN/chapter1/5.mdx index 7c235523d..64b30d2d7 100644 --- a/chapters/zh-CN/chapter1/5.mdx +++ b/chapters/zh-CN/chapter1/5.mdx @@ -1,4 +1,4 @@ -# “编码器”模型 +# “编码器”模型 [[“编码器”模型]] -# 管道的内部 +# 管道的内部 [[管道的内部]] {#if fw === 'pt'} @@ -62,7 +62,7 @@ classifier( 让我们快速浏览一下这些内容。 -## 使用分词器进行预处理 +## 使用分词器进行预处理 [[使用分词器进行预处理]] 与其他神经网络一样,Transformer模型无法直接处理原始文本, 因此我们管道的第一步是将文本输入转换为模型能够理解的数字。 为此,我们使用*tokenizer*(标记器),负责: @@ -121,7 +121,7 @@ print(inputs) ]), 'attention_mask': tensor([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] + [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] ]) } ``` @@ -139,7 +139,7 @@ print(inputs) 'attention_mask': } ``` @@ -147,7 +147,7 @@ print(inputs) 输出本身是一个包含两个键的字典,`input_ids`和`attention_mask`。`input_ids`包含两行整数(每个句子一行),它们是每个句子中标记的唯一标记(token)。我们将在本章后面解释什么是`attention_mask`。 -## 浏览模型 +## 浏览模型 [[浏览模型]] {#if fw === 'pt'} 我们可以像使用标记器一样下载预训练模型。🤗 Transformers提供了一个`AutoModel`类,该类还具有`from_pretrained()`方法: @@ -177,7 +177,7 @@ model = TFAutoModel.from_pretrained(checkpoint) 虽然这些隐藏状态本身可能很有用,但它们通常是模型另一部分(称为*头部(head)*)的输入。 在[Chapter 1](/course/chapter1)中,可以使用相同的体系结构执行不同的任务,但这些任务中的每个任务都有一个与之关联的不同头。 -### 高维向量? +### 高维向量? [[高维向量?]] Transformers模块的矢量输出通常较大。它通常有三个维度: @@ -211,7 +211,7 @@ print(outputs.last_hidden_state.shape) 注意🤗 Transformers模型的输出与`namedtuple`或词典相似。您可以通过属性(就像我们所做的那样)或键(`输出["last_hidden_state"]`)访问元素,甚至可以通过索引访问元素,前提是您确切知道要查找的内容在哪里(`outputs[0]`)。 -### 模型头:数字的意义 +### 模型头:数字的意义 [[模型头:数字的意义]] 模型头将隐藏状态的高维向量作为输入,并将其投影到不同的维度。它们通常由一个或几个线性层组成: @@ -259,7 +259,7 @@ outputs = model(inputs) ``` {/if} -现在,如果我们观察输入的形状,维度将低得多:模型头将我们之前看到的高维向量作为输入,并输出包含两个值的向量(每个标签一个): +现在,如果我们观察输出的形状,维度将低得多:模型头将我们之前看到的高维向量作为输入,并输出包含两个值的向量(每个标签一个): ```python print(outputs.logits.shape) @@ -281,7 +281,7 @@ torch.Size([2, 2]) 因为我们只有两个句子和两个标签,所以我们从模型中得到的结果是2 x 2的形状。 -## 对输出进行后处理 +## 对输出进行后处理 [[对输出进行后处理]] 我们从模型中得到的输出值本身并不一定有意义。我们来看看, diff --git a/chapters/zh-CN/chapter2/3.mdx b/chapters/zh-CN/chapter2/3.mdx index e840668d5..67946ab1a 100644 --- a/chapters/zh-CN/chapter2/3.mdx +++ b/chapters/zh-CN/chapter2/3.mdx @@ -1,6 +1,6 @@ -# 模型 +# 模型 [[模型]] {#if fw === 'pt'} @@ -44,7 +44,7 @@ AutoModel类,当您希望从检查点实例化任何模型时,这非常方 但是,如果您知道要使用的模型类型,则可以使用直接定义其体系结构的类。让我们看看这是如何与BERT模型一起工作的。 -## 创建转换器 +## 创建转换器 [[创建转换器]] 初始化BERT模型需要做的第一件事是加载配置对象: @@ -90,7 +90,7 @@ BertConfig { 虽然您还没有看到所有这些属性都做了什么,但您应该认识到其中的一些属性:hidden_size属性定义了hidden_状态向量的大小,num_hidden_layers定义了Transformer模型的层数。 -### 不同的加载方式 +### 不同的加载方式 [[不同的加载方式]] 从默认配置创建模型会使用随机值对其进行初始化: @@ -166,7 +166,7 @@ HF_HOME 用于加载模型的标识符可以是模型中心Hub上任何模型的标识符,只要它与BERT体系结构兼容。可以找到可用的BERT检查点的完整列表 [here](https://huggingface.co/models?filter=bert) . -### 保存模型 +### 保存模型 [[保存模型]] 保存模型和加载模型一样简单--我们使用 save_pretrained() @@ -206,7 +206,7 @@ config.json {/if} -### 使用Transformers模型进行推理 +### 使用Transformers模型进行推理 [[使用Transformers模型进行推理]] 既然您知道了如何加载和保存模型,那么让我们尝试使用它进行一些预测。Transformer模型只能处理数字——分词器生成的数字。但在我们讨论标记化器之前,让我们先探讨模型接受哪些输入。 @@ -246,7 +246,7 @@ model_inputs = tf.constant(encoded_sequences) ``` {/if} -### 使用张量作为模型的输入 +### 使用张量作为模型的输入 [[使用张量作为模型的输入]] diff --git a/chapters/zh-CN/chapter2/4.mdx b/chapters/zh-CN/chapter2/4.mdx index 4190508b3..f211b3db4 100644 --- a/chapters/zh-CN/chapter2/4.mdx +++ b/chapters/zh-CN/chapter2/4.mdx @@ -1,6 +1,6 @@ -# 标记器(Tokenizer) +# 标记器(Tokenizer) [[标记器(Tokenizer)]] {#if fw === 'pt'} @@ -36,7 +36,7 @@ Jim Henson was a puppeteer 让我们看一下标记化算法的一些示例,并尝试回答您可能对标记化提出的一些问题。 -## 基于词的(Word-based) +## 基于词的(Word-based) [[基于词的(Word-based)]] @@ -68,7 +68,7 @@ print(tokenized_text) 减少未知标记数量的一种方法是使用更深一层的标记器(tokenizer),即基于字符的(_character-based_)标记器(tokenizer)。 -## 基于字符(Character-based) +## 基于字符(Character-based) [[基于字符(Character-based)]] @@ -90,7 +90,7 @@ print(tokenized_text) 为了两全其美,我们可以使用结合这两种方法的第三种技术:*子词标记化(subword tokenization)*。 -## 子词标记化 +## 子词标记化 [[子词标记化]] @@ -109,7 +109,7 @@ print(tokenized_text) 这种方法在土耳其语等粘着型语言(agglutinative languages)中特别有用,您可以通过将子词串在一起来形成(几乎)任意长的复杂词。 -### 还有更多! +### 还有更多! [[还有更多!]] 不出所料,还有更多的技术。仅举几例: @@ -119,7 +119,7 @@ print(tokenized_text) 您现在应该对标记器(tokenizers)的工作原理有足够的了解,以便开始使用 API。 -## 加载和保存 +## 加载和保存 [[加载和保存]] 加载和保存标记器(tokenizer)就像使用模型一样简单。实际上,它基于相同的两种方法: `from_pretrained()` 和 `save_pretrained()` 。这些方法将加载或保存标记器(tokenizer)使用的算法(有点像*建筑学(architecture)*的模型)以及它的词汇(有点像*权重(weights)*模型)。 @@ -165,7 +165,7 @@ tokenizer.save_pretrained("directory_on_my_computer") 我们在[Chapter 3](/Couse/chapter3)中将更多地谈论`token_type_ids`,稍后我们将解释 `attention_mask` 键。首先,让我们看看 `input_ids` 如何生成。为此,我们需要查看标记器(tokenizer)的中间方法。 -## 编码 +## 编码 [[编码]] @@ -177,7 +177,7 @@ tokenizer.save_pretrained("directory_on_my_computer") 为了更好地理解这两个步骤,我们将分别探讨它们。请注意,我们将使用一些单独执行部分标记化管道的方法来向您展示这些步骤的中间结果,但实际上,您应该直接在您的输入上调用标记器(tokenizer)(如第 2 部分所示)。 -### 标记化 +### 标记化 [[标记化]] 标记化过程由标记器(tokenizer)的`tokenize()` 方法实现: @@ -200,7 +200,7 @@ print(tokens) 这个标记器(tokenizer)是一个子词标记器(tokenizer):它对词进行拆分,直到获得可以用其词汇表表示的标记(token)。`transformer` 就是这种情况,它分为两个标记:`transform` 和 `##er`。 -### 从词符(token)到输入 ID +### 从词符(token)到输入 ID [[从词符(token)到输入 ID]] 输入 ID 的转换由标记器(tokenizer)的`convert_tokens_to_ids()`方法实现: ```py @@ -221,7 +221,7 @@ print(ids) -## 解码 +## 解码 [[解码]] *解码(Decoding)* 正好相反:从词汇索引中,我们想要得到一个字符串。这可以通过 `decode()` 方法实现,如下: diff --git a/chapters/zh-CN/chapter2/5.mdx b/chapters/zh-CN/chapter2/5.mdx index fc1e62c43..2f35dbcb9 100644 --- a/chapters/zh-CN/chapter2/5.mdx +++ b/chapters/zh-CN/chapter2/5.mdx @@ -1,6 +1,6 @@ -# 处理多个序列 +# 处理多个序列 [[处理多个序列]] {#if fw === 'pt'} @@ -43,7 +43,7 @@ 让我们看看这些问题会带来什么样的问题,以及如何使用🤗 Transformers API解决它们 -## 模型需要一批输入 +## 模型需要一批输入 [[模型需要一批输入]] 在上一个练习中,您看到了序列如何转换为数字列表。让我们将此数字列表转换为张量,并将其发送到模型: @@ -93,7 +93,7 @@ InvalidArgumentError: Input to reshape is a tensor with 14 values, but the reque 哦,不!为什么失败了?“我们遵循了第2节中管道的步骤。 -问题是我们向模型发送了一个序列,而🤗 Transformers模型默认情况下需要多个句子。在这里,当我们将分词器应用于一个应用程序时,我们尝试在幕后完成分词器所做的一切,但如果仔细观察,您会发现它不仅将输入ID列表转换为张量,还在其顶部添加了一个维度: +问题是我们向模型发送了一个序列,而🤗 Transformers模型默认情况下需要多个句子。在这里,当我们将分词器应用于一个应用程序时,我们尝试在幕后完成分词器所做的一切,但如果仔细观察,您会发现tokenizer不仅将输入ID列表转换为张量,还在其顶部添加了一个维度: {#if fw === 'pt'} ```py @@ -194,7 +194,7 @@ batched_ids = [ids, ids] 批处理允许模型在输入多个句子时工作。使用多个序列就像使用单个序列构建批一样简单。不过,还有第二个问题。当你试图将两个(或更多)句子组合在一起时,它们的长度可能不同。如果您以前使用过张量,那么您知道它们必须是矩形,因此无法将输入ID列表直接转换为张量。为了解决这个问题,我们通常填充输入。 -## 填充输入 +## 填充输入 [[填充输入]] 以下列表不能转换为张量: @@ -271,7 +271,7 @@ tf.Tensor( 这是因为Transformer模型的关键特性是关注层,它将每个标记上下文化。这些将考虑填充标记,因为它们涉及序列中的所有标记。为了在通过模型传递不同长度的单个句子时,或者在传递一批应用了相同句子和填充的句子时获得相同的结果,我们需要告诉这些注意层忽略填充标记。这是通过使用 attention mask来实现的。 -## 注意力面具 +## 注意力面具 [[注意力面具]] *Attention masks*是与输入ID张量形状完全相同的张量,用0和1填充:1s表示应注意相应的标记,0s表示不应注意相应的标记(即,模型的注意力层应忽略它们)。 @@ -330,7 +330,7 @@ tf.Tensor( -## 长序列 +## 长序列 [[长序列]] 对于Transformers模型,我们可以通过模型的序列长度是有限的。大多数模型处理多达512或1024个令牌的序列,当要求处理更长的序列时,会崩溃。此问题有两种解决方案: diff --git a/chapters/zh-CN/chapter2/6.mdx b/chapters/zh-CN/chapter2/6.mdx index 7c38c2c8d..17dd3c2c8 100644 --- a/chapters/zh-CN/chapter2/6.mdx +++ b/chapters/zh-CN/chapter2/6.mdx @@ -1,6 +1,6 @@ -# 把它们放在一起 +# 把它们放在一起 [[把它们放在一起]] {#if fw === 'pt'} @@ -98,7 +98,7 @@ model_inputs = tokenizer(sequences, padding=True, return_tensors="tf") model_inputs = tokenizer(sequences, padding=True, return_tensors="np") ``` -## 特殊词符(token) +## 特殊词符(token) [[特殊词符(token)]] 如果我们看一下标记器返回的输入 ID,我们会发现它们与之前的略有不同: @@ -132,7 +132,7 @@ print(tokenizer.decode(ids)) 标记器在开头添加了特殊单词`[CLS]`,在结尾添加了特殊单词`[SEP]`。这是因为模型是用这些数据预训练的,所以为了得到相同的推理结果,我们还需要添加它们。请注意,有些模型不添加特殊单词,或者添加不同的单词;模型也可能只在开头或结尾添加这些特殊单词。在任何情况下,标记器都知道需要哪些词符,并将为您处理这些词符。 -## 结束:从标记器到模型 +## 结束:从标记器到模型 [[结束:从标记器到模型]] 现在我们已经看到了标记器对象在应用于文本时使用的所有单独步骤,让我们最后一次看看它如何处理多个序列(填充!),非常长的序列(截断!),以及多种类型的张量及其主要API: diff --git a/chapters/zh-CN/chapter2/7.mdx b/chapters/zh-CN/chapter2/7.mdx index ef205f164..b3924309b 100644 --- a/chapters/zh-CN/chapter2/7.mdx +++ b/chapters/zh-CN/chapter2/7.mdx @@ -1,4 +1,4 @@ -# 基本用法完成! +# 基本用法完成! [[基本用法完成!]] -# 章末小测试 +# 章末小测试 [[章末小测验]] AutoNLP 产品相混淆了?" + explain: "错误。您可能把AutoModel与我们的AutoTrain 产品相混淆了?" }, { text: "一个根据Checkpoint(检查点)返回模型体系结构的对象", @@ -125,7 +125,7 @@ choices={[ { text: "根据您的数据自动进行训练的模型", - explain: "错误。您可能把TFAutoModel与我们的AutoNLP 产品相混淆了?" + explain: "错误。您可能把TFAutoModel与我们的AutoTrain 产品相混淆了?" }, { text: "一个根据Checkpoint(检查点)返回模型体系结构的对象", diff --git a/chapters/zh-CN/chapter3/1.mdx b/chapters/zh-CN/chapter3/1.mdx index e744bf78e..2e1c920e1 100644 --- a/chapters/zh-CN/chapter3/1.mdx +++ b/chapters/zh-CN/chapter3/1.mdx @@ -1,6 +1,6 @@ -# 本章简介 +# 本章简介 [[本章简介]] -# 处理数据 +# 处理数据 [[处理数据]] {#if fw === 'pt'} @@ -76,7 +76,7 @@ model.train_on_batch(batch, labels) 在本节中,我们将使用MRPC(微软研究释义语料库)数据集作为示例,该数据集由威廉·多兰和克里斯·布罗克特在[这篇文章](https://www.aclweb.org/anthology/I05-5002.pdf)发布。该数据集由5801对句子组成,每个句子对带有一个标签,指示它们是否为同义(即,如果两个句子的意思相同)。我们在本章中选择了它,因为它是一个小数据集,所以很容易对它进行训练。 -### 从模型中心(Hub)加载数据集 +### 从模型中心(Hub)加载数据集 [[从模型中心(Hub)加载数据集]] {#if fw === 'pt'} @@ -114,7 +114,7 @@ DatasetDict({ 正如你所看到的,我们获得了一个**DatasetDict**对象,其中包含训练集、验证集和测试集。每一个集合都包含几个列(**sentence1**, **sentence2**, **label**, and **idx**)以及一个代表行数的变量,即每个集合中的行的个数(因此,训练集中有3668对句子,验证集中有408对,测试集中有1725对)。 -默认情况下,此命令在下载数据集并缓存到 **~/.cache/huggingface/dataset**. 回想一下第2章,您可以通过设置**HF_HOME**环境变量来自定义缓存的文件夹。 +默认情况下,此命令在下载数据集并缓存到 **~/.cache/huggingface/datasets**. 回想一下第2章,您可以通过设置**HF_HOME**环境变量来自定义缓存的文件夹。 我们可以访问我们数据集中的每一个**raw_train_dataset**对象,如使用字典: @@ -151,7 +151,7 @@ raw_train_dataset.features -### 预处理数据集 +### 预处理数据集 [[预处理数据集]] {#if fw === 'pt'} @@ -278,7 +278,7 @@ DatasetDict({ 最后一件我们需要做的事情是,当我们一起批处理元素时,将所有示例填充到最长元素的长度——我们称之为动态填充。 -### 动态填充 +### 动态填充 [[动态填充]] diff --git a/chapters/zh-CN/chapter3/3.mdx b/chapters/zh-CN/chapter3/3.mdx index f00d71e12..efcdb0916 100644 --- a/chapters/zh-CN/chapter3/3.mdx +++ b/chapters/zh-CN/chapter3/3.mdx @@ -1,6 +1,6 @@ -# 使用 Trainer API 微调模型 +# 使用 Trainer API 微调模型 [[使用 Trainer API 微调模型]] diff --git a/chapters/zh-CN/chapter3/3_tf.mdx b/chapters/zh-CN/chapter3/3_tf.mdx index c369f06d2..936c81e76 100644 --- a/chapters/zh-CN/chapter3/3_tf.mdx +++ b/chapters/zh-CN/chapter3/3_tf.mdx @@ -1,6 +1,6 @@ -# 使用 Keras 微调一个模型 +# 使用 Keras 微调一个模型 [[使用 Keras 微调一个模型]] -### 提升训练的效果 +### 提升训练的效果 [[提升训练的效果]] @@ -150,7 +150,7 @@ model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3) -### 模型预测 +### 模型预测 [[模型预测]] @@ -187,4 +187,4 @@ metric.compute(predictions=class_preds, references=raw_datasets["validation"]["l 您获得的确切结果可能会有所不同,因为模型头的随机初始化可能会改变它获得的指标。 在这里,我们可以看到我们的模型在验证集上的准确率为 85.78%,F1 得分为 89.97。 这些是用于评估 GLUE 基准的 MRPC 数据集结果的两个指标。 [BERT 论文](https://arxiv.org/pdf/1810.04805.pdf) 中的表格报告了基本模型的 F1 分数为 88.9。 那是 `uncased` 模型,而我们目前使用的是 `cased` 模型,这解释了为什么我们会获得更好的结果。 -使用 Keras API 进行微调的介绍到此结束。 第 7 章将给出对大多数常见 NLP 任务执行此操作的示例。如果您想在 Keras API 上磨练自己的技能,请尝试使第二节所使用的的数据处理在 GLUE SST-2 数据集上微调模型。 \ No newline at end of file +使用 Keras API 进行微调的介绍到此结束。 [第 7 章](/course/chapter7)将给出对大多数常见 NLP 任务执行此操作的示例。如果您想在 Keras API 上磨练自己的技能,请尝试使第二节所使用的的数据处理在 GLUE SST-2 数据集上微调模型。 \ No newline at end of file diff --git a/chapters/zh-CN/chapter3/4.mdx b/chapters/zh-CN/chapter3/4.mdx index 99aa4a19e..bc8921f13 100644 --- a/chapters/zh-CN/chapter3/4.mdx +++ b/chapters/zh-CN/chapter3/4.mdx @@ -1,4 +1,4 @@ -# 一个完整的训练 +# 一个完整的训练 [[一个完整的训练]] -### S使用🤗 Accelerate加速您的训练循环 +### S使用🤗 Accelerate加速您的训练循环 [[S使用🤗 Accelerate加速您的训练循环]] diff --git a/chapters/zh-CN/chapter3/5.mdx b/chapters/zh-CN/chapter3/5.mdx index 51edbadeb..ee69b3a3a 100644 --- a/chapters/zh-CN/chapter3/5.mdx +++ b/chapters/zh-CN/chapter3/5.mdx @@ -1,6 +1,6 @@ -# 微调,检查! +# 微调,检查! [[微调,检查!]] -# End-of-chapter quiz +# 章末小测验 [[章末小测验]] TPUStrategy scope 中的所有内容,包括模型的初始化。" }, { - text: "您可以利用现有的方法,如 < code > compile () 、 < code > fit () < c/ode > 和 < code > predict () 。", + text: "您可以利用现有的方法,如 < code > compile () 、 < code > fit () 和 < code > predict () 。", explain: "正确! 一旦你有了这些数据,在这些数据上进行培训只需要很少的工作。", correct: true }, diff --git a/chapters/zh-CN/chapter4/1.mdx b/chapters/zh-CN/chapter4/1.mdx index ea639b48c..32c8ef87e 100644 --- a/chapters/zh-CN/chapter4/1.mdx +++ b/chapters/zh-CN/chapter4/1.mdx @@ -1,4 +1,4 @@ -# The Hugging Face Hub +# The Hugging Face Hub [[The Hugging Face Hub]] -# 使用预训练的模型 +# 使用预训练模型 [[使用预训练模型]] {#if fw === 'pt'} @@ -22,15 +22,15 @@ {/if} -模型中心使选择合适的模型变得简单,因此只需几行代码即可在任何下游库中使用它。让我们来看看如何实际使用这些模型之一,以及如何回馈社区。 +模型中心使选择合适的模型变得简单,因此只需几行代码即可在任何下游库中使用它。让我们来看看如何使用这些模型,以及如何将模型贡献到社区。 -假设我们正在寻找一种可以执行**mask**填充的French-based模型。 +假设我们正在寻找一种可以执行 mask 填充的 French-based 模型。
Selecting the Camembert model.
-我们选择 **camembert-base** 检查点来尝试一下。我们需要做的仅仅是输入 `camembert-base`标识符!正如您在前几章中看到的,我们可以使用 **pipeline()** 功能: +我们选择 `camembert-base` 检查点来尝试一下。我们需要做的仅仅是输入 `camembert-base` 标识符!正如你在前几章中看到的,我们可以使用 `pipeline()` 功能: ```py from transformers import pipeline @@ -49,13 +49,13 @@ results = camembert_fill_mask("Le camembert est :)") ] ``` -如您所见,在管道中加载模型非常简单。您唯一需要注意的是所选检查点是否适合它将用于的任务。例如,这里我们正在加载 **camembert-base** 检查点在 **fill-mask** 管道,这完全没问题。但是如果我们要在 **text-classification** 管道,结果没有任何意义,因为 **camembert-base** 不适合这个任务!我们建议使用 Hugging Face Hub 界面中的任务选择器来选择合适的检查点: +如你所见,在管道中加载模型非常简单。你唯一需要注意的是所选检查点是否适合它将用于的任务。例如,这里我们正在将 `camembert-base` 检查点加载在 `fill-mask` 管道,这完全没问题。但是如果我们在 `text-classification` 管道加载检查点,结果没有任何意义,因为 `camembert-base` 不适合这个任务!我们建议使用 Hugging Face Hub 界面中的任务选择器来选择合适的检查点:
The task selector on the web interface.
-您还可以直接使用模型架构实例化检查点: +你还可以直接使用模型架构实例化检查点: {#if fw === 'pt'} ```py @@ -65,7 +65,7 @@ tokenizer = CamembertTokenizer.from_pretrained("camembert-base") model = CamembertForMaskedLM.from_pretrained("camembert-base") ``` -然而,我们建议使用[Auto* 类](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes),因为Auto* 类设计与架构无关。前面的代码示例将只能在 CamemBERT 架构中加载可用的检查点,但使用 **Auto*** 类使切换检查点变得简单: +然而,我们建议使用 [`Auto*` 类](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes),因为 `Auto*` 类设计与架构无关。前面的代码示例将只能在 CamemBERT 架构中加载可用的检查点,但使用 `Auto*` 类使切换不同的检查点变得简单: ```py from transformers import AutoTokenizer, AutoModelForMaskedLM @@ -81,8 +81,7 @@ tokenizer = CamembertTokenizer.from_pretrained("camembert-base") model = TFCamembertForMaskedLM.from_pretrained("camembert-base") ``` -However, we recommend using the [`TFAuto*` classes](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes) instead, as these are by design architecture-agnostic. While the previous code sample limits users to checkpoints loadable in the CamemBERT architecture, using the `TFAuto*` classes makes switching checkpoints simple: -然而,我们建议使用[`TFAuto*` 类](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes),因为`TFAuto*`类设计与架构无关。前面的代码示例将只能在 CamemBERT 架构中加载可用的检查点,但使用 `TFAuto*` 类使切换检查点变得简单: +然而,我们建议使用 [`TFAuto*` 类](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes),因为 `TFAuto*` 类设计与架构无关。前面的代码示例将只能在 CamemBERT 架构中加载可用的检查点,但使用 `TFAuto*` 类使切换不同的检查点变得简单: ```py from transformers import AutoTokenizer, TFAutoModelForMaskedLM @@ -93,5 +92,5 @@ model = TFAutoModelForMaskedLM.from_pretrained("camembert-base") {/if} -使用预训练模型时,一定要检查它是如何训练的,在哪些数据集上,它的限制和它的偏差。所有这些信息都应在其模型卡片上注明。 +使用预训练模型时,一定要检查它是如何训练的、在哪些数据集上训练的、它的局限性和偏差。所有这些信息都应在其模型卡片上注明。 diff --git a/chapters/zh-CN/chapter4/3.mdx b/chapters/zh-CN/chapter4/3.mdx index c9c920dc5..9be4ff273 100644 --- a/chapters/zh-CN/chapter4/3.mdx +++ b/chapters/zh-CN/chapter4/3.mdx @@ -1,6 +1,6 @@ -# 共享预训练模型 +# 共享预训练模型 [[共享预训练模型]] {#if fw === 'pt'} @@ -37,7 +37,7 @@ 创建存储库后,您可以通过 git 和 git-lfs 将文件上传到其中。我们将在以下部分引导您创建模型存储库并将文件上传到它们 -## 使用 push_to_hub API +## 使用 push_to_hub API [[使用 push_to_hub API]] {#if fw === 'pt'} @@ -190,7 +190,7 @@ tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token= 跳到最后一部分,了解如何将文件上传到新创建的存储库! -## 使用 huggingface_hub python库 +## 使用 huggingface_hub python库 [[使用 huggingface_hub python库]] 这 **huggingface_hub** Python 库是一个包,它为模型和数据集中心提供了一组工具。它为常见任务提供了简单的方法和类,例如 获取有关集线器上存储库的信息并对其进行管理。它提供了在 git 之上工作的简单 API 来管理这些存储库的内容并集成 Hub @@ -256,7 +256,7 @@ create_repo("dummy-model", organization="huggingface") 创建存储库后,我们应该向其中添加文件!跳到下一部分以查看可以处理此问题的三种方法。 -## 使用网络界面 +## 使用网络界面 [[使用网络界面]] Web 界面提供了直接在 Hub 中管理存储库的工具。使用该界面,您可以轻松创建存储库、添加文件(甚至是大文件!)、探索模型、可视化差异等等。 @@ -292,13 +292,13 @@ README 文件在 Markdown 中 - 随意使用它!本章的第三部分致力于 接下来我们将看看如何添加一些新文件。 -## 上传模型文件 +## 上传模型文件 [[上传模型文件]] Hugging Face Hub 上的文件管理系统基于用于常规文件的 git 和 git-lfs(代表[Git Large File Storage](https://git-lfs.github.com/)) 对于较大的文件。 在下一节中,我们将介绍将文件上传到 Hub 的三种不同方式:通过 **huggingface_hub** 并通过 git 命令。 -### The `upload_file` approach +### The `upload_file` approach [[The `upload_file` approach]] 使用 **upload_file** 不需要在您的系统上安装 git 和 git-lfs。它使用 HTTP POST 请求将文件直接推送到 🤗 Hub。这种方法的一个限制是它不能处理大于 5GB 的文件。 如果您的文件大于 5GB,请按照下面详述的另外两种方法进行操作。API 可以按如下方式使用: @@ -320,7 +320,7 @@ upload_file( - repo_type, 如果你想要上传一个 `dataset` 或一个 `space` 而不是模型。 接受的值为 `"dataset"` 和 `"space"`. -### The `Repository` class +### The `Repository` class [[The `Repository` class]] 以类似 git 的方式管理本地存储库。它抽象了 git 可能遇到的大部分痛点,以提供我们需要的所有功能。 @@ -373,7 +373,7 @@ repo.git_push() 恭喜!您刚刚将第一个文件推送到hub上。 -### The git-based approach +### The git-based approach [[The git-based approach]] 这是上传文件的非常简单的方法:我们将直接使用 git 和 git-lfs 来完成。大多数困难都被以前的方法抽象掉了,但是下面的方法有一些警告,所以我们将遵循一个更复杂的用例。 diff --git a/chapters/zh-CN/chapter4/4.mdx b/chapters/zh-CN/chapter4/4.mdx index 694f4ace2..e844ac18a 100644 --- a/chapters/zh-CN/chapter4/4.mdx +++ b/chapters/zh-CN/chapter4/4.mdx @@ -1,4 +1,4 @@ -# 构建模型卡片 +# 构建模型卡片 [[构建模型卡片]] -# 章末小测试 +# 章末小测试 [[章末小测验]] -## 使用本地和远程数据集 +## 使用本地和远程数据集 [[使用本地和远程数据集]] 🤗 Datasets 提供了加载脚本来加载本地和远程数据集。它支持几种常见的数据格式,例如: @@ -24,7 +24,7 @@ 如表所示, 对于每种数据格式, 我们只需要使用 `load_dataset()` 函数, 使用 `data_files` 指定一个或多个文件的路径的参数。 让我们从本地文件加载数据集开始;稍后我们将看到如何对远程文件执行相同的操作。 -## 加载本地数据集 +## 加载本地数据集 [[加载本地数据集]] 对于这个例子,我们将使用 [SQuAD-it dataset](https://github.com/crux82/squad-it/), 这是一个大规模的意大利语问答数据集。 @@ -46,7 +46,7 @@ SQuAD_it-test.json.gz: 87.4% -- replaced with SQuAD_it-test.json SQuAD_it-train.json.gz: 82.2% -- replaced with SQuAD_it-train.json ``` -我们可以看到压缩文件已经被替换为SQuAD_it-train.json和SQuAD_it-text.json,并且数据以 JSON 格式存储。 +我们可以看到压缩文件已经被替换为SQuAD_it-train.json和SQuAD_it-test.json,并且数据以 JSON 格式存储。 @@ -143,7 +143,7 @@ squad_it_dataset = load_dataset("json", data_files=data_files, field="data") 现在你知道如何在笔记本电脑或台式机上加载本地文件,让我们来看看加载远程文件。 -## 加载远程数据集 +## 加载远程数据集 [[加载远程数据集]] 如果你在公司担任数据研究员或编码员,那么你要分析的数据集很有可能存储在某个远程服务器上。幸运的是,加载远程文件就像加载本地文件一样简单!我们没有提供本地文件的路径, 而是将`load_dataset()`的`data_files`参数指向存储远程文件的一个或多个URL。例如, 对于托管在 GitHub 上的 SQuAD-it 数据集, 我们可以将 `data_files` 指向 _SQuAD_it-*.json.gz_ 的网址,如下所示: diff --git a/chapters/zh-CN/chapter5/3.mdx b/chapters/zh-CN/chapter5/3.mdx index 5d92ed61b..239780da7 100644 --- a/chapters/zh-CN/chapter5/3.mdx +++ b/chapters/zh-CN/chapter5/3.mdx @@ -1,4 +1,4 @@ -# 是时候来学一下切片了 +# 是时候来学一下切片了 [[是时候来学一下切片了]] -## 切片与切分我们的数据 +## 切片与切分我们的数据 [[切片与切分我们的数据]] 与 Pandas 类似,🤗 Datasets 提供了几个函数来操作 **Dataset** 和 **DatasetDict** 对象。我们在[第三章](/course/chapter3)已经遇到了 **Dataset.map()** 方法,在本节中,我们将探索我们可以使用的其他功能。 @@ -168,7 +168,7 @@ drug_dataset["train"]["condition"][:3] 有用!现在我们已经清理了标签,让我们来看看清洗后的评论文本。 -## Creating new columns +## 创建新的数据列 [[创建新的数据列]] 每当您处理客户评论时,一个好的做法是检查每个评论中的字数。评论可能只是一个词,比如“太棒了!”或包含数千字的完整文章,根据实际的情况,您需要以不同的方式处理这些极端情况。为了计算每条评论中的单词数,我们将使用基于空格分割每个文本的粗略方法。 @@ -263,7 +263,7 @@ drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])} 如您所见, **Dataset.map()** 方法对于处理数据非常有用——在示例中仅仅是浅尝辄止就有很大的收获! -## map() 方法的超级加速 +## map() 方法的超级加速 [[map() 方法的超级加速]] **Dataset.map()** 方法有一个 **batched** 参数,如果设置为 **True** , map 函数将会分批执行所需要进行的操作(批量大小是可配置的,但默认为 1,000)。例如,之前对所有 HTML 进行转义的 map 函数运行需要一些时间(您可以从进度条中读取所用时间)。我们可以通过使用列表推导同时处理多个元素来加快速度。 @@ -446,7 +446,7 @@ DatasetDict({ 您现在已经了解了 🤗 Datasets如何以各种方式用于预处理数据集。虽然🤗 Datasets 的处理功能会覆盖你大部分的模型训练需求,有时您可能需要切换到 Pandas 以使用更强大的功能,例如 **DataFrame.groupby()** 或用于可视化的高级 API。幸运的是,🤗 Datasets旨在与 Pandas、NumPy、PyTorch、TensorFlow 和 JAX 等库可以相互转换。让我们来看看这是如何工作的。 -## `🤗 Datasets 和 DataFrames 的相互转换 +## `🤗 Datasets 和 DataFrames 的相互转换 [[`🤗 Datasets 和 DataFrames 的相互转换]] @@ -606,7 +606,7 @@ Dataset({ drug_dataset.reset_format() ``` -## 创建验证集 +## 创建验证集 [[创建验证集]] 尽管我们有一个可以用于评估的测试集,但在开发过程中保持测试集不变并创建一个单独的验证集是一个很好的做法。一旦您对模型在测试集上的表现感到满意,您就可以对验证集进行最终的检查。此过程有助于降低您过拟合测试集并部署在现实世界数据上失败的模型的风险。 @@ -640,7 +640,7 @@ DatasetDict({ 太好了,我们现在已经准备好了一个数据集,可以用来训练一些模型了!在[第五节]](/course/chapter5/5)我们将向您展示如何将数据集上传到 Hugging Face Hub,但现在让我们查看在本地计算机上保存数据集的几种方法。 -## 保存数据集 +## 保存数据集 [[保存数据集]] diff --git a/chapters/zh-CN/chapter5/4.mdx b/chapters/zh-CN/chapter5/4.mdx index f5675110c..70ef713ca 100644 --- a/chapters/zh-CN/chapter5/4.mdx +++ b/chapters/zh-CN/chapter5/4.mdx @@ -1,4 +1,4 @@ -# 大数据? 🤗 Datasets 来救援! +# 大数据? 🤗 Datasets 来救援! [[大数据? 🤗 Datasets 来救援!]] -## 流式数据集 +## 流式数据集 [[流式数据集]] 要使用数据集流, 你只需要将 `streaming=True` 参数传递给 `load_dataset()` 函数。接下来, 让我们再次加载 PubMed Abstracts 数据集, 但是采用流模式: diff --git a/chapters/zh-CN/chapter5/5.mdx b/chapters/zh-CN/chapter5/5.mdx index 055f79ea1..a18ee94f4 100644 --- a/chapters/zh-CN/chapter5/5.mdx +++ b/chapters/zh-CN/chapter5/5.mdx @@ -1,4 +1,4 @@ -# 创建自己的数据集 +# 创建自己的数据集 [[创建自己的数据集]] -现在我们有了我们的增强数据集,是时候将它推送到 Hub 并且与社区共享它!要上传数据集,我们将使用[🤗 Hub 库](https://github.com/huggingface/huggingface_hub),它允许我们通过 Python API 与 Hugging Face Hub 进行交互。 🤗 Hub 预装了🤗 Transformers,所以我们可以直接使用它。例如,我们可以使用 **list_datasets()** 获取有关当前托管在 Hub 上的所有公共数据集的信息的函数: - -```py -from huggingface_hub import list_datasets - -all_datasets = list_datasets() -print(f"Number of datasets on Hub: {len(all_datasets)}") -print(all_datasets[0]) -``` - -```python out -Number of datasets on Hub: 1487 -Dataset Name: acronym_identification, Tags: ['annotations_creators:expert-generated', 'language_creators:found', 'languages:en', 'licenses:mit', 'multilinguality:monolingual', 'size_categories:10K - -✏️ 试试看!使用您的 Hugging Face Hub 用户名和密码获取令牌并创建一个名为 github-issues.请记住永远不要将您的凭据保存在 Colab 或任何其他存储库中,因为这些信息可能会被不法分子利用。 - - - -接下来,让我们将存储库从 Hub 克隆到我们的本地机器,并将我们的数据集文件复制到其中。 🤗 Hub 提供了一个方便的 **Repository** 类,它包含许多常见 Git 命令的类,因此要克隆远程存储库,我们只需要提供我们要克隆的 URL 和本地路径: - -```py -from huggingface_hub import Repository - -repo = Repository(local_dir="github-issues", clone_from=repo_url) -!cp datasets-issues-with-comments.jsonl github-issues/ -``` - -默认情况下,使用Git LFS跟踪各种文件扩展名(如.bin、.gz和.zip),以便在同一Git工作流中对大型文件进行版本控制。您可以在存储库的.gitattributes文件找到跟踪文件扩展名的列表。要在列表中包含JSON行格式,我们可以运行以下命令: - -```py -repo.lfs_track("*.jsonl") -``` - -然后我们可以使用 **Repository.push_to_hub()** 将数据集推送到 Hub: - -```py -repo.push_to_hub() -``` - -如果我们导航到包含在 **repo_url** ,我们现在应该看到我们的数据集文件已经上传。 - -
-Our dataset repository on the Hugging Face Hub. -
- -从这里,任何人都可以通过简单地提供来下载数据集 **load_dataset()** 以存储库 ID 作为 **path** 争论: +之后,任何人都可以通过便捷地提供带有存储库 ID 作为 path 参数的 load_dataset() 来下载数据集: ```py remote_dataset = load_dataset("lewtun/github-issues", split="train") @@ -427,7 +364,7 @@ Dataset({ -## 创建数据集卡片 +## 创建数据集卡片 [[创建数据集卡片]] 有据可查的数据集更有可能对其他人(包括你未来的自己!)有用,因为它们提供了上下文,使用户能够决定数据集是否与他们的任务相关,并评估任何潜在的偏见或与使用相关的风险。在 Hugging Face Hub 上,此信息存储在每个数据集存储库的自述文件文件。在创建此文件之前,您应该执行两个主要步骤: diff --git a/chapters/zh-CN/chapter5/6.mdx b/chapters/zh-CN/chapter5/6.mdx index 429881676..8de5fb335 100644 --- a/chapters/zh-CN/chapter5/6.mdx +++ b/chapters/zh-CN/chapter5/6.mdx @@ -1,6 +1,6 @@ -# 使用 FAISS 进行语义搜索 +# 使用 FAISS 进行语义搜索 [[使用 FAISS 进行语义搜索]] {#if fw === 'pt'} @@ -26,7 +26,7 @@ -## 使用嵌入进行语义搜索 +## 使用嵌入进行语义搜索 [[使用嵌入进行语义搜索]] 正如我们在[第一章](/course/chapter1),学习的, 基于 Transformer 的语言模型会将文本中的每个标记转换为嵌入向量.事实证明,可以“汇集”各个嵌入向量来创建整个句子、段落或文档(在某些情况下)的向量表示。然后,通过计算每个嵌入之间的点积相似度(或其他一些相似度度量)并返回相似度最大的文档,这些嵌入可用于在语料库中找到相似的文档。在本节中,我们将使用嵌入来开发语义搜索引擎。与基于将查询中的关键字的传统方法相比,这些搜索引擎具有多种优势。 @@ -35,26 +35,15 @@
-## ## 加载和准备数据集 +## 加载和准备数据集 [[加载和准备数据集]] -我们需要做的第一件事是下载我们的 GitHub 问题数据集,所以让我们使用 🤗 Hub 库来解析我们的文件在 Hugging Face Hub 上存储的数据: - -```py -from huggingface_hub import hf_hub_url - -data_files = hf_hub_url( - repo_id="lewtun/github-issues", - filename="datasets-issues-with-comments.jsonl", - repo_type="dataset", -) -``` - -将 URL 存储在 **data_files** ,然后我们可以使用[第二小节](/course/chapter5/2)介绍的方法加载远程数据集: +我们需要做的第一件事是下载我们的 GitHub issues 数据集,所以让我们像往常那样使用 `load_dataset()`函数: ```py from datasets import load_dataset -issues_dataset = load_dataset("json", data_files=data_files, split="train") +issues_dataset = load_dataset("lewtun/github-issues", split="train") + issues_dataset ``` @@ -232,7 +221,7 @@ comments_dataset = comments_dataset.map(concatenate_text) 我们终于准备好创建一些嵌入了!让我们来看看。 -## 创建文本嵌入 +## 创建文本嵌入 [[创建文本嵌入]] 我们在[第二章](/course/chapter2) 学过,我们可以通过使用 **AutoModel** 类来完成词嵌入。我们需要做的就是选择一个合适的检查点来加载模型。幸运的是,有一个名为 **sentence-transformers** 专门用于创建词嵌入。如库中[文档](https://www.sbert.net/examples/applications/semantic-search/README.html#symmetric-vs-asymmetric-semantic-search), 所述的,我们这次要实现的是非对称语义搜索,因为我们有一个简短的查询,我们希望在比如问题评论等更长的文档中找到其答案。通过查看[模型概述表](https://www.sbert.net/docs/pretrained_models.html#model-overview) 我们可以发现 **multi-qa-mpnet-base-dot-v1** 检查点在语义搜索方面具有最佳性能,因此我们将在我们的应用程序中使用它。我们还将使用相同的检查点加载标记器: @@ -345,7 +334,7 @@ embeddings_dataset = comments_dataset.map( 请注意,我们已经将嵌入转换为 NumPy 数组——这是因为当我们尝试使用 FAISS 索引它们时,🤗 Datasets需要这种格式,我们接下来会这样做。 -## 使用 FAISS 进行高效的相似性搜索 +## 使用 FAISS 进行高效的相似性搜索 [[使用 FAISS 进行高效的相似性搜索]] 现在我们有了一个词嵌入数据集,我们需要一些方法来搜索它们。为此,我们将在 🤗 Datasets中使用一种特殊的数据结构,称为 FAISS指数.[FAISS](https://faiss.ai/) (short for Facebook AI Similarity Search) (Facebook AI Similarity Search 的缩写)是一个提供高效算法来快速搜索和聚类嵌入向量的库。FAISS 背后的基本思想是创建一个特殊的数据结构,称为指数。这允许人们找到哪些嵌入词与输入的词嵌入相似。在 🤗 Datasets中创建一个 FAISS 索引很简单——我们使用 **Dataset.add_faiss_index()** 函数并指定我们要索引的数据集的哪一列: diff --git a/chapters/zh-CN/chapter5/7.mdx b/chapters/zh-CN/chapter5/7.mdx index fc9f5e4b4..9f42af397 100644 --- a/chapters/zh-CN/chapter5/7.mdx +++ b/chapters/zh-CN/chapter5/7.mdx @@ -1,4 +1,4 @@ -# 🤗 Datasets,回顾! +# 🤗 Datasets,回顾! [[🤗 Datasets,回顾!]] -# 章末小测试 +# 章末小测试 [[章末小测验]] -# 章末小测验 +# 章末小测验 [[章末小测验]] -## 准备语料库 +## 准备语料库 [[准备语料库]] 🤗 Transformers 中有一个非常简单的 API,你可以用它来训练一个新的标记器,使它与现有标记器相同的特征: **AutoTokenizer.train_new_from_iterator()** .为了复现这一点,假设我们想从头开始训练 GPT-2,但使用英语以外的语言。我们的首要任务是在训练语料库中收集该语言的大量数据。为了提供每个人都能理解的示例,我们在这里不会使用俄语或中文之类的语言,而是使用在特定领域的英语语言:Python 代码。 @@ -129,7 +129,7 @@ def get_training_corpus(): 这将产生与以前完全相同的生成器,但允许您使用比列表生成式中更复杂的逻辑。 -## 训练一个新的标记器 +## 训练一个新的标记器 [[训练一个新的标记器]] 现在我们的语料库是文本批量迭代器的形式,我们准备训练一个新的标记器。为此,我们首先需要加载要与模型配对的标记器(此处为 GPT-2): @@ -218,7 +218,7 @@ tokenizer.tokenize(example) 除了一个缩进对应的token,这里我们还可以看到一个双缩进的token: **ĊĠĠĠĠĠĠĠ** .特殊的 Python 词如 **class** , **init** , **call** , **self** , 和 **return** 每个都被标记为一个标记,我们可以看到,以及分裂 **_** 和 **.** 标记器甚至可以正确拆分驼峰式名称: **LinearLayer** 被标记为 **[ĠLinear, Layer]** . -## 保存标记器 +## 保存标记器 [[保存标记器]] 为了确保我们以后可以使用它,我们需要保存我们的新标记器。就像模型一样,是通过 **save_pretrained()** 方法: diff --git a/chapters/zh-CN/chapter6/3.mdx b/chapters/zh-CN/chapter6/3.mdx index 22db0d122..2cdcc95e3 100644 --- a/chapters/zh-CN/chapter6/3.mdx +++ b/chapters/zh-CN/chapter6/3.mdx @@ -1,6 +1,6 @@ -# 快速标记器的特殊能力 +# 快速标记器的特殊能力 [[快速标记器的特殊能力]] {#if fw === 'pt'} @@ -28,7 +28,7 @@ 在接下来的讨论中,我们会经常区分“慢”和“快”分词器。慢速分词器是在 🤗 Transformers 库中用 Python 编写的,而快速版本是由 🤗 分词器提供的,它们是用 Rust 编写的。如果你还记得在[Chapter 5](/course/chapter5/3)中报告了快速和慢速分词器对药物审查数据集进行分词所需的时间的这张表,您应该知道为什么我们称它们为“快”和“慢”: - | Fast tokenizer | Slow tokenizer +| | Fast tokenizer | Slow tokenizer :--------------:|:--------------:|:-------------: `batched=True` | 10.8s | 4min41s `batched=False` | 59.2s | 5min3s @@ -39,7 +39,7 @@ -## 批量编码 +## 批量编码 [[批量编码]] @@ -134,7 +134,7 @@ Sylvain -## 在令牌分类管道内 +## 在令牌分类管道内 [[在令牌分类管道内]] 在[Chapter 1](/course/chapter1)我们第一次尝试使用 NER——任务是识别文本的哪些部分对应于个人、地点或组织等实体——使用 🤗 Transformers **pipeline()** 功能。然后,在[Chapter 2](/course/chapter2),我们看到了管道如何将从原始文本中获取预测所需的三个阶段组合在一起:标记化、通过模型传递输入和后处理。前两步 **token-classification** 管道与任何其他管道相同,但后处理稍微复杂一些 - 让我们看看如何! @@ -148,7 +148,7 @@ Sylvain {/if} -### 通过管道获得基本结果 +### 通过管道获得基本结果 [[通过管道获得基本结果]] 首先,让我们获取一个标记分类管道,以便我们可以手动比较一些结果。默认使用的模型是[dbmdz/bert-large-cased-finetuned-conll03-english](https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english);它对句子执行 NER: @@ -195,7 +195,7 @@ token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.") 现在让我们看看如何在不使用pipeline()函数的情况下获得这些结果! -### 从输入到预测 +### 从输入到预测 [[从输入到预测]] {#if fw === 'pt'} @@ -402,7 +402,7 @@ print(results) 这和我们从第一个管道中得到的一样! -### 分组实体 +### 分组实体 [[分组实体]] 使用偏移量来确定每个实体的开始和结束键很方便,但该信息并不是绝对必要的。然而,当我们想要将实体组合在一起时,偏移量将为我们节省大量混乱的代码。例如,如果我们想将令牌组合在一起 **Hu** , **##gging** , 和 **Face** ,我们可以制定特殊的规则,说前两个应该附加,同时删除 **##** ,以及 **Face** 应该添加一个空格,因为它不以 **##** — 但这仅适用于这种特定类型的标记器。我们必须为 SentencePiece 或 Byte-Pair-Encoding 分词器(本章稍后讨论)。 diff --git a/chapters/zh-CN/chapter6/3b.mdx b/chapters/zh-CN/chapter6/3b.mdx index 4c4b95d7f..1c23ad523 100644 --- a/chapters/zh-CN/chapter6/3b.mdx +++ b/chapters/zh-CN/chapter6/3b.mdx @@ -1,6 +1,6 @@ -# QA 管道中的快速标记器 +# QA 管道中的快速标记器 [[QA 管道中的快速标记器]] {#if fw === 'pt'} @@ -34,7 +34,7 @@ {/if} -## 使用 `question-answering` 管道 +## 使用 `question-answering` 管道 [[使用 `question-answering` 管道]] 正如我们在[Chapter 1](/course/chapter1),我们可以使用 **question-answering** 像这样的管道以获得问题的答案: @@ -109,7 +109,7 @@ question_answerer(question=question, context=long_context) 让我们看看它是如何做到这一切的! -## 使用模型进行问答 +## 使用模型进行问答 [[使用模型进行问答]] 与任何其他管道一样,我们首先对输入进行标记化,然后通过模型将其发送。默认情况下用于的检查点 **question-answering** 管道是[distilbert-base-cased-distilled-squad](https://huggingface.co/distilbert-base-cased-distilled-squad)(名称中的“squad”来自模型微调的数据集;我们将在[Chapter 7](/course/chapter7/7)): @@ -244,6 +244,8 @@ scores = start_probabilities[:, None] * end_probabilities[None, :] 然后我们将屏蔽这些值 **start_index > end_index** 通过将它们设置为 **0** (其他概率都是正数)。这 **torch.triu()** 函数返回作为参数传递的 2D 张量的上三角部分,因此它会为我们做屏蔽: ```py +import numpy as np + scores = torch.triu(scores) ``` @@ -315,7 +317,7 @@ print(result) -## 处理长上下文 +## 处理长上下文 [[处理长上下文]] 如果我们尝试对我们之前作为示例使用的问题和长上下文进行标记化,我们将获得比在 **question-answering** 管道(即 384): diff --git a/chapters/zh-CN/chapter6/4.mdx b/chapters/zh-CN/chapter6/4.mdx index b43fcbb31..137aba74c 100644 --- a/chapters/zh-CN/chapter6/4.mdx +++ b/chapters/zh-CN/chapter6/4.mdx @@ -1,4 +1,4 @@ -# 标准化和预标记化 +# 标准化和预标记化 [[标准化和预标记化]] @@ -54,7 +54,7 @@ print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü? -## 预标记化 +## 预标记化 [[预标记化]] @@ -103,13 +103,13 @@ tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you? 现在我们已经了解了一些不同的标记器如何处理文本,我们可以开始探索底层算法本身。我们首先快速浏览一下广泛适用的 SentencePiece;然后,在接下来的三个部分中,我们将研究用于子词标记化的三种主要算法是如何工作的。 -## 句子 +## 句子 [[句子]] [SentencePiece](https://github.com/google/sentencepiece) 是一种用于文本预处理的标记化算法,您可以将其与我们将在接下来的三个部分中看到的任何模型一起使用。它将文本视为 Unicode 字符序列,并用特殊字符替换空格, **▁** .与 Unigram 算法结合使用(参见[section 7](/course/chapter7/7)), 它甚至不需要预标记化步骤,这对于不使用空格字符的语言(如中文或日语)非常有用。 SentencePiece 的另一个主要特点是可逆标记化:由于没有对空格进行特殊处理,因此只需通过将它们连接起来并替换 **_** s 带空格——这会导致标准化的文本。正如我们之前看到的,BERT 分词器删除了重复的空格,因此它的分词是不可逆的。 -## 算法概述 +## 算法概述 [[算法概述]] 在下面的部分中,我们将深入研究三种主要的子词标记化算法:BPE(由 GPT-2 和其他人使用)、WordPiece(例如由 BERT 使用)和 Unigram(由 T5 和其他人使用)。在我们开始之前,这里是它们各自工作原理的快速概述。如果您还没有理解,请在阅读下一节后立即回到此表。 diff --git a/chapters/zh-CN/chapter6/5.mdx b/chapters/zh-CN/chapter6/5.mdx index af1170c06..e923db49b 100644 --- a/chapters/zh-CN/chapter6/5.mdx +++ b/chapters/zh-CN/chapter6/5.mdx @@ -1,4 +1,4 @@ -# 字节对编码标记化 +# 字节对编码标记化 [[字节对编码标记化]] -## 训练算法 +## 训练算法 [[训练算法]] BPE 训练首先计算语料库中使用的唯一单词集(在完成标准化和预标记化步骤之后),然后通过获取用于编写这些单词的所有符号来构建词汇表。一个非常简单的例子,假设我们的语料库使用了这五个词: @@ -80,7 +80,7 @@ Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5 -## 标记化算法 +## 标记化算法 [[标记化算法]] 标记化紧跟训练过程,从某种意义上说,通过应用以下步骤对新输入进行标记: @@ -105,7 +105,7 @@ Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5 -## 实现 BPE +## 实现 BPE [[实现 BPE]] 现在让我们看一下 BPE 算法的实现。这不会是你可以在大型语料库上实际使用的优化版本;我们只是想向你展示代码,以便你可以更好地理解算法 @@ -113,7 +113,7 @@ Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5 ```python corpus = [ - "This is the Hugging Face course.", + "This is the Hugging Face Course.", "This chapter is about tokenization.", "This section shows several tokenizer algorithms.", "Hopefully, you will be able to understand how they are trained and generate tokens.", diff --git a/chapters/zh-CN/chapter6/6.mdx b/chapters/zh-CN/chapter6/6.mdx index 898e065ee..8f3b746ba 100644 --- a/chapters/zh-CN/chapter6/6.mdx +++ b/chapters/zh-CN/chapter6/6.mdx @@ -1,4 +1,4 @@ -# WordPiece 标记化 +# WordPiece 标记化 [[WordPiece 标记化]] -## 训练算法 +## 训练算法 [[训练算法]] @@ -81,7 +81,7 @@ Corpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "## ✏️ **现在轮到你了!** 下一个合并规则是什么? -## 标记化算法 +## 标记化算法 [[标记化算法]] WordPiece 和 BPE 中的标记化的不同在于 WordPiece 只保存最终词汇,而不是学习的合并规则。从要标记的单词开始,WordPiece 找到词汇表中最长的子词,然后对其进行拆分。例如,如果我们使用上面例子中学到的词汇,对于单词 `"hugs"`,词汇表中从头开始的最长子词是 `"hug"`,所以我们在那里拆分并得到 `["hug", "##s"]`。 然后我们继续使用词汇表中的 `"##s"`,因此 `"hugs"` 的标记化是 `["hug", "##s"]`. @@ -97,7 +97,7 @@ WordPiece 和 BPE 中的标记化的不同在于 WordPiece 只保存最终词汇 -## 实现 WordPiece +## 实现 WordPiece [[实现 WordPiece]] 现在让我们看一下 WordPiece 算法的实现。与 BPE 一样,这只是教学,你将无法在大型语料库中使用它。 @@ -105,7 +105,7 @@ WordPiece 和 BPE 中的标记化的不同在于 WordPiece 只保存最终词汇 ```python corpus = [ - "This is the Hugging Face course.", + "This is the Hugging Face Course.", "This chapter is about tokenization.", "This section shows several tokenizer algorithms.", "Hopefully, you will be able to understand how they are trained and generate tokens.", @@ -306,7 +306,7 @@ print(vocab) ```python out ['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', '##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', '##l', '##m', '##n', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', - 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', '##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully', + 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', 'ab','##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully', 'Th', 'ch', '##hm', 'cha', 'chap', 'chapt', '##thm', 'Hu', 'Hug', 'Hugg', 'sh', 'th', 'is', '##thms', '##za', '##zat', '##ut'] ``` diff --git a/chapters/zh-CN/chapter6/7.mdx b/chapters/zh-CN/chapter6/7.mdx index b5803646c..95202c1e3 100644 --- a/chapters/zh-CN/chapter6/7.mdx +++ b/chapters/zh-CN/chapter6/7.mdx @@ -1,4 +1,4 @@ -# Unigram标记化 +# Unigram标记化 [[Unigram标记化]] -## 训练算法 +## 训练算法 [[训练算法]] 与 BPE 和 WordPiece 相比,Unigram 在另一个方向上工作:它从一个较大的词汇表开始,然后从中删除标记,直到达到所需的词汇表大小。有多种选项可用于构建基本词汇表:例如,我们可以采用预标记化单词中最常见的子串,或者在具有大词汇量的初始语料库上应用 BPE。 @@ -41,7 +41,7 @@ ["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"] ``` -## 标记化算法 +## 标记化算法 [[标记化算法]] Unigram 模型是一种语言模型,它认为每个标记都独立于它之前的标记。它是最简单的语言模型,从某种意义上说, 给定先前上下文的标记 X 的概率就是标记 X 的概率。因此,如果我们使用 Unigram 语言模型生成文本,我们将始终预测最常见的标记。 @@ -104,7 +104,7 @@ Character 4 (g): "un" "hug" (score 0.005442) -## 回到训练 +## 回到训练 [[回到训练]] 现在我们已经了解了标记化的工作原理,我们可以更深入地研究训练期间使用的损失。在任何给定的阶段,这个损失是通过对语料库中的每个单词进行标记来计算的,使用当前词汇表和由语料库中每个标记的频率确定的 Unigram 模型(如前所述)。 @@ -149,7 +149,7 @@ Character 4 (g): "un" "hug" (score 0.005442) 因此, 标记 `"pu"`可能会从词汇表中删除,但不会删除 `"hug"`. -## 实现 Unigram +## 实现 Unigram [[实现 Unigram]] 现在让我们在代码中实现我们迄今为止看到的所有内容。与 BPE 和 WordPiece 一样,这不是 Unigram 算法的有效实现(恰恰相反),但它应该可以帮助你更好地理解它。 @@ -157,7 +157,7 @@ Character 4 (g): "un" "hug" (score 0.005442) ```python corpus = [ - "This is the Hugging Face course.", + "This is the Hugging Face Course.", "This chapter is about tokenization.", "This section shows several tokenizer algorithms.", "Hopefully, you will be able to understand how they are trained and generate tokens.", diff --git a/chapters/zh-CN/chapter6/8.mdx b/chapters/zh-CN/chapter6/8.mdx index 43ce8d72f..7e6802bdc 100644 --- a/chapters/zh-CN/chapter6/8.mdx +++ b/chapters/zh-CN/chapter6/8.mdx @@ -1,4 +1,4 @@ -# 逐块地构建标记器 +# 逐块地构建标记器 [[逐块地构建标记器]] -# 章节简介 +# 章节简介 [[章节简介]] -在[第三章](/course/chapter3),您了解了如何微调文本分类的模型。在本章中,我们将处理以下常见NLP任务: +在[第三章](/course/chapter3),您了解了如何微调文本分类的模型。在本章中,我们将处理以下常见的 NLP 任务: -- 标记(token)分类 -- 遮罩语言建模(如BERT) -- 提取文本摘要 +- 词元(token)分类 +- 掩码语言建模(如 BERT) +- 文本摘要 - 翻译 -- 因果语言建模预训练(如GPT-2) +- 因果语言建模预训练(如 GPT-2) - 问答 {#if fw === 'pt'} -为此,您需要利用[第三章](/course/chapter3)中学到的`Trainer` API 和🤗Accelerate 库、[第五章](/course/chapter5)中的 🤗 Datasets 库以及[第六章](/course/chapter6)中的 🤗 Tokenizers 库的所有知识。我们还会将结果上传到模型中心,就像我们在[第四章](/course/chapter4)中所做的那样,所以这确实是将之前所有内容汇集在一起的章节! +为此,您需要利用[第三章](/course/chapter3)中学到的 `Trainer` API 和 🤗 Accelerate 库、[第五章](/course/chapter5)中的 🤗 Datasets 库以及[第六章](/course/chapter6)中的 🤗 Tokenizers 库的所有知识。我们同样会将结果上传到模型中心,就像我们在[第四章](/course/chapter4)中所做的那样,所以这确实是融会贯通的一章! -每个部分都可以独立阅读,并将向您展示如何使用API或按照您自己的训练循环训练模型,使用🤗 Accelerate 加速。你可以随意跳过其中一部分,把注意力集中在你最感兴趣的那一部分:API可以优化或训练您的模型而无需担心幕后发生了什么,而训练循环使用可以让您更轻松地自定义所需的任何结构。 +每个部分都可以独立阅读,并将向您展示如何使用 `Trainer` API 或按照您自己的训练循环训练模型,并采用 🤗 Accelerate 加速。你可以随意跳过任何一部分,专注于您最感兴趣的部分:`Trainer` API 非常适用于微调(fine-tuning)或训练您的模型,且无需担心幕后发生的事情;而采用 `Accelerate` 的训练循环可以让您更轻松地自定义所需的任何结构。 {:else} -为此,您需要利用[第三章](/course/chapter3)中学到的有关Keras API、[第五章](/course/chapter5)中的 🤗 Datasets 库以及[第六章](/course/chapter6)中的 🤗 Tokenizers 库的所有知识。我们还会将结果上传到模型中心,就像我们在[第四章](/course/chapter4)中所做的那样,所以这确实是将之前所有内容汇集在一起的章节! +为此,您需要利用[第三章](/course/chapter3)中学到的有关 Keras API、[第五章](/course/chapter5)中的 🤗 Datasets 库以及[第六章](/course/chapter6)中的 🤗 Tokenizers 库的所有知识。我们同样会将结果上传到模型中心,就像我们在[第四章](/course/chapter4)中所做的那样,所以这确实是融会贯通的一章! 每个部分都可以独立阅读。 diff --git a/chapters/zh-CN/chapter7/2.mdx b/chapters/zh-CN/chapter7/2.mdx index b328bbc0d..e9f663157 100644 --- a/chapters/zh-CN/chapter7/2.mdx +++ b/chapters/zh-CN/chapter7/2.mdx @@ -1,6 +1,6 @@ -# Token 分类 +# Token 分类 [[Token 分类]] {#if fw === 'pt'} @@ -33,7 +33,6 @@ 当然,还有很多其他类型的token分类问题;这些只是几个有代表性的例子。在本节中,我们将在 NER 任务上微调模型 (BERT),然后该模型将能够计算如下预测: - One-hot encoded labels for question answering. @@ -42,7 +41,7 @@ 您可以[在这里](https://huggingface.co/huggingface-course/bert-finetuned-ner?text=My+name+is+Sylvain+and+I+work+at+Hugging+Face+in+Brooklyn).找到我们将训练并上传到 Hub的模型,可以尝试输入一些句子看看模型的预测结果。 -## 准备数据 +## 准备数据 [[准备数据]] 首先,我们需要一个适合标记分类的数据集。在本节中,我们将使用[CoNLL-2003 数据集](https://huggingface.co/datasets/conll2003), 其中包含来自路透社的新闻报道。 @@ -52,7 +51,7 @@ -### CoNLL-2003 数据集 +### CoNLL-2003 数据集 [[CoNLL-2003 数据集]] 要加载 CoNLL-2003 数据集,我们使用 来自 🤗 Datasets 库的**load_dataset()** 方法: @@ -174,7 +173,7 @@ print(line2) -### 处理数据 +### 处理数据 [[处理数据]] @@ -302,20 +301,20 @@ tokenized_datasets = raw_datasets.map( {#if fw === 'pt'} -## 使用 Trainer API 微调模型 +## 使用 Trainer API 微调模型 [[使用 Trainer API 微调模型]] 使用 `Trainer` 的实际代码会和以前一样;唯一的变化是数据整理成时批处理的方式和度量计算函数。 {:else} -## 使用 Keras 微调模型 +## 使用 Keras 微调模型 [[使用 Keras 微调模型]] 使用Keras的实际代码将与之前非常相似;唯一的变化是将数据整理成批处理的方式和指标计算函数。 {/if} -### 数据排序 +### 数据排序 [[数据排序]] 我们不能像[第三章](/course/chapter3)那样只使用一个 `DataCollatorWithPadding `因为这只会填充输入(输入 ID、注意掩码和标记类型 ID)。在这里我们的标签应该以与输入完全相同的方式填充,以便它们保持长度相同,使用 `-100 ` ,这样在损失计算中就可以忽略相应的预测。 @@ -371,8 +370,7 @@ for i in range(2): {:else} -我们的数据整理器已准备就绪!现在,让我们用它来制作一个带有`to_tf_dataset()`方法的`tf.data.Dataset`。 - +我们的数据整理器已准备就绪! 现在让我们使用它通过 `to_tf_dataset()` 方法制作一个 `tf.data.Dataset`。 您还可以使用 model.prepare_tf_dataset() 来使用更少的样板代码来执行此操作——您将在本章的其他部分中看到这一点。 ```py tf_train_dataset = tokenized_datasets["train"].to_tf_dataset( columns=["attention_mask", "input_ids", "labels", "token_type_ids"], @@ -403,7 +401,7 @@ tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset( 它们应该由两个字典设置, `id2label` 和 `label2id` ,其中包含从 ID 到标签的映射,反之亦然: ```py -id2label = {i: label for i, label in enumerate(label_names)} +id2label = {str(i): label for i, label in enumerate(label_names)} label2id = {v: k for k, v in id2label.items()} ``` @@ -460,7 +458,6 @@ from transformers import create_optimizer import tensorflow as tf # Train in mixed-precision float16 -# Comment this line out if you're using a GPU that will not benefit from this tf.keras.mixed_precision.set_global_policy("mixed_float16") # The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied @@ -510,7 +507,7 @@ model.fit( {/if} -### 评估指标 +### 评估指标 [[评估指标]] {#if fw === 'pt'} @@ -522,7 +519,7 @@ model.fit( !pip install seqeval ``` -然后我们可以通过加载它 `load_metric()` 函数就像我们在[第三章](/course/chapter3)做的那样: +然后我们可以通过加载它 `evaluate.load()` 函数就像我们在[第三章](/course/chapter3)做的那样: {:else} @@ -532,14 +529,14 @@ model.fit( !pip install seqeval ``` -然后我们可以通过加载它 `load_metric()` 函数就像我们在[第三章](/course/chapter3)做的那样: +然后我们可以通过`evaluate.load()` 函数加载它就像我们在[第三章](/course/chapter3)做的那样: {/if} ```py -from datasets import load_metric +import evaluate -metric = load_metric("seqeval") +metric = evaluate.load("seqeval") ``` 这个评估方式与标准精度不同:它实际上将标签列表作为字符串,而不是整数,因此在将预测和标签传递给它之前,我们需要完全解码它们。让我们看看它是如何工作的。首先,我们将获得第一个训练示例的标签: @@ -616,7 +613,7 @@ import numpy as np all_predictions = [] all_labels = [] for batch in tf_eval_dataset: - logits = model.predict(batch)["logits"] + logits = model.predict_on_batch(batch)["logits"] labels = batch["labels"] predictions = np.argmax(logits, axis=-1) for prediction, label in zip(predictions, labels): @@ -653,7 +650,7 @@ metric.compute(predictions=[all_predictions], references=[all_labels]) 它们应该由两个字典设置, `id2label` 和 `label2id` ,其中包含从 ID 到标签的映射,反之亦然: ```py -id2label = {i: label for i, label in enumerate(label_names)} +id2label = {str(i): label for i, label in enumerate(label_names)} label2id = {v: k for k, v in id2label.items()} ``` @@ -759,11 +756,11 @@ This command returns the URL of the commit it just did, if you want to inspect i 如果您想更深入地了解训练循环,我们现在将向您展示如何使用 🤗 Accelerate 做同样的事情。 -## 自定义训练循环 +## 自定义训练循环 [[自定义训练循环]] 现在让我们看一下完整的训练循环,这样您可以轻松定义所需的部分。它看起来很像我们在[第三章](/course/chapter3/4), 所做的,对评估进行了一些更改。 -### 做好训练前的准备 +### 做好训练前的准备 [[做好训练前的准备]] 首先我们需要为我们的数据集构建 `DataLoader` 。我们将重用我们的 `data_collator` 作为一个 `collate_fn` 并打乱训练集,但不打乱验证集: ```py @@ -845,18 +842,16 @@ repo_name 'sgugger/bert-finetuned-ner-accelerate' ``` -Then we can clone that repository in a local folder. If it already exists, this local folder should be an existing clone of the repository we are working with: +然后我们可以将该存储库克隆到本地文件夹中。 如果它已经存在,这个本地文件夹应该是我们正在使用的存储库的现有克隆: ```py output_dir = "bert-finetuned-ner-accelerate" repo = Repository(output_dir, clone_from=repo_name) ``` -We can now upload anything we save in `output_dir` by calling the `repo.push_to_hub()` method. This will help us upload the intermediate models at the end of each epoch. - -### Training loop +我们现在可以通过调用 `repo.push_to_hub()` 方法上传保存在 `output_dir` 中的任何内容。 这将帮助我们在每个训练周期结束时上传中间模型。 -### 训练循环 +### 训练循环 [[训练循环]] 我们现在准备编写完整的训练循环。为了简化它的评估部分,我们定义了这个 `postprocess()` 接受预测和标签并将它们转换为字符串列表的函数,也就是 `metric`对象需要的输入格式: ```py @@ -954,7 +949,7 @@ unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) {/if} -## 使用微调模型 +## 使用微调模型 [[使用微调模型]] 我们已经向您展示了如何使用我们在模型中心微调的模型和推理小部件。在本地使用它 `pipeline` ,您只需要指定正确的模型标识符: diff --git a/chapters/zh-CN/chapter7/3.mdx b/chapters/zh-CN/chapter7/3.mdx index b5c410d23..4b4c8232e 100644 --- a/chapters/zh-CN/chapter7/3.mdx +++ b/chapters/zh-CN/chapter7/3.mdx @@ -1,6 +1,6 @@ -# 微调掩码语言模型 +# 微调掩码语言模型 [[微调掩码语言模型]] {#if fw === 'pt'} @@ -36,7 +36,6 @@ 在本节结束时, 你将在Hub上拥有一个[掩码语言模型(masked language model)](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.), 该模型可以自动完成句子, 如下所示: - 让我们开始吧! @@ -48,7 +47,7 @@ -## 选择用于掩码语言建模的预训练模型 +## 选择用于掩码语言建模的预训练模型 [[选择用于掩码语言建模的预训练模型]] 首先, 让我们为掩码语言建模选择一个合适的预训练模型。如以下屏幕截图所示, 你可以通过在[Hugging Face Hub](https://huggingface.co/models?pipeline_tag=fill-mask&sort=downloads)上应用"Fill-Mask"过滤器找到: @@ -187,7 +186,7 @@ for token in top_5_tokens: 我们可以从输出中看到模型的预测是指日常用语, 鉴于英语维基百科的基础, 这也许并不奇怪。让我们看看我们如何将这个领域改变为更小众的东西 -- 高度两极分化的电影评论! -## 数据集 +## 数据集 [[数据集]] 为了展示域适配, 我们将使用著名的[大型电影评论数据集(Large Movie Review Dataset)](https://huggingface.co/datasets/imdb) (或者简称为IMDb), 这是一个电影评论语料库, 通常用于对情感分析模型进行基准测试。通过在这个语料库上对 DistilBERT 进行微调, 我们预计语言模型将根据维基百科的事实数据调整其词汇表, 这些数据已经预先训练到电影评论中更主观的元素。我们可以使用🤗 Datasets中的`load_dataset()`函数从Hugging Face 中获取数据: @@ -247,7 +246,7 @@ for row in sample: 现在我们已经快速浏览了数据, 让我们深入研究为掩码语言建模做准备。正如我们将看到的, 与我们在[第三章](/course/chapter3)中看到的序列分类任务相比, 还需要采取一些额外的步骤。让我们继续! -## 预处理数据 +## 预处理数据 [[预处理数据]] @@ -445,7 +444,7 @@ tokenizer.decode(lm_datasets["train"][1]["labels"]) 正如前面的 `group_texts()` 函数所期望的那样, 这看起来与解码后的 `input_ids` 相同 -- 但是我们的模型怎么可能学到任何东西呢? 我们错过了一个关键步骤: 在输入中的随机位置插入 `[MASK]` 标记! 让我们看看如何使用特殊的数据整理器在微调期间即时执行此操作。 -## 使用 `Trainer` API 微调DistilBERT +## 使用 `Trainer` API 微调DistilBERT [[使用 `Trainer` API 微调DistilBERT]] 微调屏蔽语言模型几乎与微调序列分类模型相同, 就像我们在 [第三章](/course/chapter3)所作的那样。 唯一的区别是我们需要一个特殊的数据整理器, 它可以随机屏蔽每批文本中的一些标记。幸运的是, 🤗 Transformers 为这项任务准备了专用的 `DataCollatorForLanguageModeling` 。我们只需要将它转递给标记器和一个 `mlm_probability` 参数, 该参数指定要屏蔽的标记的分数。我们将选择 15%, 这是 BERT 使用的数量也是文献中的常见选择: @@ -535,7 +534,7 @@ def whole_word_masking_data_collator(features): import collections import numpy as np -from transformers.data import tf_default_data_collator +from transformers.data.data_collator import tf_default_data_collator wwm_probability = 0.2 @@ -637,18 +636,18 @@ huggingface-cli login {#if fw === 'tf'} -登陆后, 我们就可以创建我们的 `tf.data` 数据集。我们将在这里只使用标准数据整理器, 但你也可以尝试使用整个单词掩码整理器并将结果作为练习进行比较: +登录后,我们可以创建我们的“tf.data”数据集。 为此,我们将使用 `prepare_tf_dataset()` 方法,该方法使用我们的模型自动推断哪些列应进入数据集。 如果您想准确控制要使用的列,可以改用“Dataset.to_tf_dataset()”方法。 为了简单起见,我们在这里只使用标准数据整理器,但您也可以尝试整个单词屏蔽整理器并将结果作为练习进行比较: ```python -tf_train_dataset = downsampled_dataset["train"].to_tf_dataset( - columns=["input_ids", "attention_mask", "labels"], +tf_train_dataset = model.prepare_tf_dataset( + downsampled_dataset["train"], collate_fn=data_collator, shuffle=True, batch_size=32, ) -tf_eval_dataset = downsampled_dataset["test"].to_tf_dataset( - columns=["input_ids", "attention_mask", "labels"], +tf_eval_dataset = model.prepare_tf_dataset( + downsampled_dataset["test"], collate_fn=data_collator, shuffle=False, batch_size=32, @@ -676,6 +675,7 @@ model.compile(optimizer=optimizer) # Train in mixed-precision float16 tf.keras.mixed_precision.set_global_policy("mixed_float16") +model_name = model_checkpoint.split("/")[-1] callback = PushToHubCallback( output_dir=f"{model_name}-finetuned-imdb", tokenizer=tokenizer ) @@ -732,7 +732,7 @@ trainer = Trainer( {/if} -### 语言模型的perplexity +### 语言模型的perplexity [[语言模型的perplexity]] @@ -826,7 +826,7 @@ trainer.push_to_hub() 在我们的用例中, 我们不需要对训练循环做任何特别的事情, 但在某些情况下, 你可能需要实现一些自定义逻辑。对于这些应用, 你可以使用 🤗 Accelerate -- 让我们来看看吧! -## 使用 🤗 Accelerate 微调 DistilBERT +## 使用 🤗 Accelerate 微调 DistilBERT [[使用 🤗 Accelerate 微调 DistilBERT]] 正如我们在 `Trainer` 中看到的, 对掩码语言模型的微调与 [第三章](/course/chapter3) 中的文本分类示例非常相似。事实上, 唯一的微妙之处是使用特殊的数据整理器, 我们已经在本节的前面介绍过了! @@ -1003,7 +1003,7 @@ for epoch in range(num_train_epochs): {/if} -## 使用我们微调的模型 +## 使用我们微调的模型 [[使用我们微调的模型]] 你可以通过在Hub上使用其他小部件或在本地使用🤗 Transformers 的`管道`于微调模型进行交互。让我们使用后者来下载我们的模型, 使用 `fill-mask` 管道: diff --git a/chapters/zh-CN/chapter7/4.mdx b/chapters/zh-CN/chapter7/4.mdx index 32ea23dfa..a88dc2777 100644 --- a/chapters/zh-CN/chapter7/4.mdx +++ b/chapters/zh-CN/chapter7/4.mdx @@ -1,6 +1,6 @@ -# 翻译 +# 翻译 [[翻译]] {#if fw === 'pt'} @@ -36,7 +36,6 @@ 完成后,我们将拥有一个模型,可以进行这样的翻译: - One-hot encoded labels for question answering. @@ -45,16 +44,16 @@ 与前面的部分一样,您可以使用以下代码找到我们将训练并上传到 Hub 的实际模型,并[在这里](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr?text=This+plugin+allows+you+to+automatically+translate+web+pages+between+several+languages.)查看模型输出的结果 -## 准备数据 +## 准备数据 [[准备数据]] 为了从头开始微调或训练翻译模型,我们需要一个适合该任务的数据集。如前所述,我们将使用[KDE4 数据集](https://huggingface.co/datasets/kde4)在本节中,但您可以很容易地调整代码以使用您自己的数据,只要您有要互译的两种语言的句子对。如果您需要复习如何将自定义数据加载到 **Dataset** 可以复习一下[第五章](/course/chapter5). -### KDE4 数据集 +### KDE4 数据集 [[KDE4 数据集]] 像往常一样,我们使用 **load_dataset()** 函数: ```py -from datasets import load_dataset, load_metric +from datasets import load_dataset raw_datasets = load_dataset("kde4", lang1="en", lang2="fr") ``` @@ -162,7 +161,7 @@ translator( -### 处理数据 +### 处理数据 [[处理数据]] @@ -172,7 +171,7 @@ translator( from transformers import AutoTokenizer model_checkpoint = "Helsinki-NLP/opus-mt-en-fr" -tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="tf") +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="pt") ``` 您可以将 **model_checkpoint** 更换为[Hub](https://huggingface.co/models)上你喜欢的任何其他型号,或本地保存的预训练模型和标记器。 @@ -183,36 +182,28 @@ tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="tf") -我们的数据准备非常简单。 只需要记住一件事:您照常处理输入,但对于这次的输出目标,您需要将标记器包装在上下文管理器“as_target_tokenizer()”中。 +我们的数据准备非常简单。 只有一件事要记住; 您需要确保分词器以输出语言(此处为法语)处理目标。 您可以通过将目标传递给分词器的 __call__ 方法的 text_targets 参数来完成此操作。 -Python 中的上下文管理器引入了 **with** 语句,当您有两个相关的操作要成对执行时很有用。最常见的例子是当您写入或读取文件时,下面是一个例子: - -``` -with open(file_path) as f: - content = f.read() -``` - -这里成对执行的两个相关操作是打开和关闭文件的操作。打开的文件f对应的对象只在with下的缩进块内有效;在该块之前打开,在该块的末尾关闭。 - -在本例中,上下文管理器 as_target_tokenizer() 将在执行缩进块之前将标记器设置为输出语言(此处为法语),然后将其设置回输入语言(此处为英语)。 - -因此,预处理一个样本如下所示: +为了了解这是如何工作的,让我们处理训练集中每种语言的一个样本: ```python en_sentence = split_datasets["train"][1]["translation"]["en"] fr_sentence = split_datasets["train"][1]["translation"]["fr"] -inputs = tokenizer(en_sentence) -with tokenizer.as_target_tokenizer(): - targets = tokenizer(fr_sentence) +inputs = tokenizer(en_sentence, text_target=fr_sentence) +inputs ``` -如果我们忘记在上下文管理器中标记目标,它们将被输入标记器标记,在Marian模型的情况下,会导致输出的异常: +```python out +{'input_ids': [47591, 12, 9842, 19634, 9, 0], 'attention_mask': [1, 1, 1, 1, 1, 1], 'labels': [577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]} +``` + +正如我们所见,输出包含与英语句子关联的输入 ID,而与法语句子关联的 ID 存储在“labels”字段中。 如果您忘记表明您正在对标签进行分词,它们将由输入分词器进行分词,这在 Marian 模型的情况下根本不会顺利进行: ```python wrong_targets = tokenizer(fr_sentence) print(tokenizer.convert_ids_to_tokens(wrong_targets["input_ids"])) -print(tokenizer.convert_ids_to_tokens(targets["input_ids"])) +print(tokenizer.convert_ids_to_tokens(inputs["labels"])) ``` ```python out @@ -222,24 +213,18 @@ print(tokenizer.convert_ids_to_tokens(targets["input_ids"])) 正如我们所看到的,使用英语标记器来预处理法语句子会产生更多的标记,因为标记器不知道任何法语单词(除了那些也出现在英语语言中的单词,比如“discussion”)。 -`inputs` 和 `targets` 都是带有我们常用键(输入 ID、注意掩码等)的字典,所以最后一步是在输入中设置一个 `"labels"` 键。 我们在数据集的预处理函数中执行此操作: +由于“inputs”是一个包含我们常用键(输入 ID、注意掩码等)的字典,最后一步是定义我们将应用于数据集的预处理函数: ```python -max_input_length = 128 -max_target_length = 128 +max_length = 128 def preprocess_function(examples): inputs = [ex["en"] for ex in examples["translation"]] targets = [ex["fr"] for ex in examples["translation"]] - model_inputs = tokenizer(inputs, max_length=max_input_length, truncation=True) - - # Set up the tokenizer for targets - with tokenizer.as_target_tokenizer(): - labels = tokenizer(targets, max_length=max_target_length, truncation=True) - - model_inputs["labels"] = labels["input_ids"] - return model_inputs + model_inputs = tokenizer( + inputs, text_target=targets, max_length=max_length, truncation=True + ) ``` 请注意,我们为输入和输出设置了相同的最大长度。由于我们处理的文本看起来很短,我们使用 128。 @@ -270,7 +255,7 @@ tokenized_datasets = split_datasets.map( {#if fw === 'pt'} -## 使用 Trainer API 微调模型 +## 使用 Trainer API 微调模型 [[使用 Trainer API 微调模型]] 使用 `Trainer` 的实际代码将与以前相同,只是稍作改动:我们在这里使用 [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer), 它是 `Trainer` 的子类,它可以正确处理这种序列到序列的评估,并使用 `generate()` 方法来预测输入的输出。 当我们讨论评估指标时,我们将更详细地探讨这一点。 @@ -284,7 +269,7 @@ model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint) {:else} -## 使用 Keras 微调模型 +## 使用 Keras 微调模型 [[使用 Keras 微调模型]] 首先,我们需要一个实际的模型来进行微调。 我们将使用通常的 `AutoModel` API: @@ -304,7 +289,7 @@ model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint, from_pt=True) 请注意,这次我们使用的是在翻译任务上训练过的模型,并且实际上已经可以使用,因此没有关于丢失权重或新初始化权重的警告。 -### 数据整理 +### 数据整理 [[数据整理]] 我们需要一个数据整理器来处理动态批处理的填充。在本例中,我们不能像[第3章](/course/chapter3)那样使用带填充的**DataCollatorWithPadding**,因为它只填充输入(输入ID、注意掩码和令牌类型ID)。我们的标签也应该填充到标签中遇到的最大长度。而且,如前所述,用于填充标签的填充值应为-100,而不是标记器的填充标记,以确保在损失计算中忽略这些填充值。 @@ -386,14 +371,14 @@ for i in range(1, 3): 我们现在可以使用 `data_collator` 将我们的每个数据集转换为 `tf.data.Dataset`,准备好进行训练: ```python -tf_train_dataset = tokenized_datasets["train"].to_tf_dataset( - columns=["input_ids", "attention_mask", "labels"], +tf_train_dataset = model.prepare_tf_dataset( + tokenized_datasets["train"], collate_fn=data_collator, shuffle=True, batch_size=32, ) -tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset( - columns=["input_ids", "attention_mask", "labels"], +tf_eval_dataset = model.prepare_tf_dataset( + tokenized_datasets["validation"], collate_fn=data_collator, shuffle=False, batch_size=16, @@ -403,7 +388,7 @@ tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset( {/if} -### 评估指标 +### 评估指标 [[评估指标]] @@ -424,12 +409,12 @@ BLEU 的一个缺点是它需要文本已经被分词,这使得比较使用不 !pip install sacrebleu ``` -然后我们可以就像我们在[第三章](/course/chapter3)那样通过 **load_metric()** 加载它 : +然后我们可以就像我们在[第三章](/course/chapter3)那样通过 **evaluate.load()** 加载它 : ```py -from datasets import load_metric +import evaluate -metric = load_metric("sacrebleu") +metric = evaluate.load("sacrebleu") ``` 该指标将文本作为输入和目标结果。它旨在接受多个可接受的目标,因为同一个句子通常有多个可接受的翻译——我们使用的数据集只提供一个,但在 NLP 中找到将多个句子作为标签的数据集不是一个难题。因此,预测结果应该是一个句子列表,而参考应该是一个句子列表的列表。 @@ -504,10 +489,32 @@ metric.compute(predictions=predictions, references=references) {#if fw === 'tf'} -为了从模型输出可以被评估基准可以使用的文本,我们将使用 **tokenizer.batch_decode()** 方法。我们只需要清理标签中所有 **-100** (标记器会自动为填充标记做同样的事情): +为了将模型输出的向量转换为可以使用的文本,我们将使用 `tokenizer.batch_decode()` 方法。 我们只需要清除标签中的所有“-100”; tokenizer 将自动对填充令牌执行相同的操作。 让我们定义一个函数,它接受我们的模型和数据集并计算其指标。 我们还将使用一个可以显着提高性能的技巧 - 使用 TensorFlow 的加速线性代数编译器 [XLA](https://www.tensorflow.org/xla) 编译我们的生成代码。 XLA 对模型的计算图应用了各种优化,并显着提高了速度和内存使用率。 正如 Hugging Face [博客](https://huggingface.co/blog/tf-xla-generate) 中所述,当我们的输入形状变化不大时,XLA 效果最佳。 为了处理这个问题,我们将输入填充为 128 的倍数,并使用填充整理器创建一个新数据集,然后我们将 @tf.function(jit_compile=True) 装饰器应用于我们的生成函数,将整个函数标记为使用 XLA 进行编译。 ```py import numpy as np +import tensorflow as tf +from tqdm import tqdm + +generation_data_collator = DataCollatorForSeq2Seq( + tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=128 +) + +tf_generate_dataset = model.prepare_tf_dataset( + tokenized_datasets["validation"], + collate_fn=generation_data_collator, + shuffle=False, + batch_size=8, +) + + +@tf.function(jit_compile=True) +def generate_with_xla(batch): + return model.generate( + input_ids=batch["input_ids"], + attention_mask=batch["attention_mask"], + max_new_tokens=128, + ) def compute_metrics(): @@ -520,12 +527,10 @@ def compute_metrics(): shuffle=False, batch_size=4, ) - for batch in tf_generate_dataset: - predictions = model.generate( - input_ids=batch["input_ids"], attention_mask=batch["attention_mask"] - ) + for batch, labels in tqdm(tf_generate_dataset): + predictions = generate_with_xla(batch) decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) - labels = batch["labels"].numpy() + labels = labels.numpy() labels = np.where(labels != -100, labels, tokenizer.pad_token_id) decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True) decoded_preds = [pred.strip() for pred in decoded_preds] @@ -569,7 +574,7 @@ def compute_metrics(eval_preds): 现在这已经完成了,我们已经准备好微调我们的模型了! -### 微调模型 +### 微调模型 [[微调模型]] 第一步是登录 Hugging Face,这样您就可以将结果上传到模型中心。有一个方便的功能可以帮助您在notebook中完成此操作: @@ -719,7 +724,7 @@ trainer = Seq2SeqTrainer( 在训练之前,我们将首先查看我们的模型获得的分数,以仔细检查我们的微调没有让事情变得更糟。此命令需要一些时间,因此您可以在执行时喝杯咖啡: ```python -trainer.evaluate(max_length=max_target_length) +trainer.evaluate(max_length=max_length) ``` ```python out @@ -743,7 +748,7 @@ trainer.train() 训练完成后,我们再次评估我们的模型——希望我们会看到 BLEU 分数有所改善! ```py -trainer.evaluate(max_length=max_target_length) +trainer.evaluate(max_length=max_length) ``` ```python out @@ -777,11 +782,11 @@ trainer.push_to_hub(tags="translation", commit_message="Training complete") {#if fw === 'pt'} -## 自定义训练循环 +## 自定义训练循环 [[自定义训练循环]] 现在让我们看一下完整的训练循环,以便您可以轻松自定义所需的部分。它看起来很像我们在[本章第二节](/course/chapter7/2)和[第三章第四小节](/course/chapter3/4)所做的。 -### 准备训练所需的一切 +### 准备训练所需的一切 [[准备训练所需的一切]] 您已经多次看到所有这些,因此这一块会简略进行。首先我们将构建我们的数据集的**DataLoader** ,在将数据集设置为 **torch** 格式,我们就得到了 PyTorch 张量: @@ -865,7 +870,7 @@ repo = Repository(output_dir, clone_from=repo_name) 我们现在可以通过调用 **repo.push_to_hub()** 方法上传我们保存的任何内容 **output_dir** 。这将帮助我们在每个 epoch 结束时上传过程中的模型。 -### 训练循环 +### 训练循环 [[训练循环]] 我们现在准备编写完整的训练循环。为了简化它的评估部分,我们定义了这个 **postprocess()** 函数接收预测结果和正确标签并将它们转换为我们 **metric** 对象所需要的字符串列表: @@ -958,7 +963,7 @@ epoch 2, BLEU score: 54.44 {/if} -## 使用微调后的模型 +## 使用微调后的模型 [[使用微调后的模型]] 我们已经向您展示了如何将我们在模型中心微调的模型与推理小部件一起使用。 要在“管道”中本地使用它,我们只需要指定正确的模型标识符: diff --git a/chapters/zh-CN/chapter7/5.mdx b/chapters/zh-CN/chapter7/5.mdx index e09659687..0fbba71a7 100644 --- a/chapters/zh-CN/chapter7/5.mdx +++ b/chapters/zh-CN/chapter7/5.mdx @@ -1,6 +1,6 @@ -# 提取文本摘要 +# 提取文本摘要 [[提取文本摘要]] {#if fw === 'pt'} @@ -30,11 +30,10 @@ 尽管在[Hugging Face Hub](https://huggingface.co/models?pipeline_tag=summarization=downloads)上已经存在各种微调模型用于文本摘要,几乎所有这些都只适用于英文文档。因此,为了在本节中添加一些变化,我们将为英语和西班牙语训练一个双语模型。在本节结束时,您将有一个可以总结客户评论的[模型](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es)。 - 如下所示:正如我们将看到的,这些摘要很简洁,因为它们是从客户在产品评论中提供的标题中学到的。让我们首先为这项任务准备一个合适的双语语料库。 -## 准备多语言语料库 +## 准备多语言语料库 [[准备多语言语料库]] 我们将使用[多语言亚马逊评论语料库](https://huggingface.co/datasets/amazon_reviews_multi)创建我们的双语摘要器。该语料库由六种语言的亚马逊产品评论组成,通常用于对多语言分类器进行基准测试。然而,由于每条评论都附有一个简短的标题,我们可以使用标题作为我们模型学习的目标摘要!首先,让我们从 Hugging Face Hub 下载英语和西班牙语子集: @@ -204,7 +203,7 @@ books_dataset = books_dataset.filter(lambda x: len(x["review_title"].split()) > 现在我们已经准备好了我们的语料库,让我们来看看一些可以对其进行微调的可能的 Transformer 模型! -## 文本摘要模型 +## 文本摘要模型 [[文本摘要模型]] 如果你仔细想想,文本摘要是一种类似于机器翻译的任务:我们有一个像评论这样的文本正文,我们希望将其“翻译”成一个较短的版本,以捕捉输入的显着特征。因此,大多数用于文本摘要的 Transformer 模型采用了我们在[第一章](/course/chapter1)遇到的编码器-解码器架构。尽管有一些例外,例如 GPT 系列模型,它们在few-shot(少量微调)之后也可以提取摘要。下表列出了一些流行的预训练模型,可以对其进行微调以进行汇总。 @@ -235,7 +234,7 @@ mT5 不使用前缀,但具有 T5 的大部分功能,并且具有多语言的 -## 预处理数据 +## 预处理数据 [[预处理数据]] @@ -277,7 +276,7 @@ tokenizer.convert_ids_to_tokens(inputs.input_ids) 特殊的 Unicode 字符 `▁` 和序列结束标记 `` 表明我们正在处理 SentencePiece 分词器,它基于在[第六章](/course/chapter6)中讨论的Unigram分词算法. Unigram 对多语言语料库特别有用,因为它允许 SentencePiece 不知道重音、标点符号以及许多语言(如日语)没有空格字符。 -为了标记我们的语料库,我们必须处理与摘要相关的细节:因为我们的标签也是文本,它们可能会超过模型的最大上下文大小。这意味着我们需要对评论及其标题进行截断,以确保我们不会将过长的输入传递给我们的模型。 🤗 Transformers 中的分词器提供了一个漂亮的 **as_target_tokenizer()** 函数,它允许您并行分词并标记标签的函数。这通常是使用预处理函数内的上下文管理器完成的,该函数首先对输入进行编码,然后将标签编码为单独的列。以下是 mT5 的此函数的示例: +为了标记我们的语料库,我们必须处理与摘要相关的微妙之处:因为我们的标签也是文本,所以它们可能超过模型的最大上下文大小。 这意味着我们需要对评论及其标题进行截断,以确保我们不会将过长的输入传递给我们的模型。 🤗 Transformers 中的分词器提供了一个绝妙的 `text_target` 参数,允许您将标签与输入并行分词。 以下是如何为 mT5 处理输入和目标的示例: ```python max_input_length = 512 @@ -286,19 +285,18 @@ max_target_length = 30 def preprocess_function(examples): model_inputs = tokenizer( - examples["review_body"], max_length=max_input_length, truncation=True + examples["review_body"], + max_length=max_input_length, + truncation=True, + ) + labels = tokenizer( + examples["review_title"], max_length=max_target_length, truncation=True ) - # Set up the tokenizer for targets - with tokenizer.as_target_tokenizer(): - labels = tokenizer( - examples["review_title"], max_length=max_target_length, truncation=True - ) - model_inputs["labels"] = labels["input_ids"] return model_inputs ``` -让我们通过这段代码来了解发生了什么。我们做的第一件事是定义值 **max_input_length** 和 **max_target_length** ,它为我们的评论和标题的长度设置了上限。由于评论正文通常比标题大得多,我们相应地调整了这些值。然后,在 **preprocess_function()** 我们可以看到评论首先被标记化,然后是标题在 **as_target_tokenizer()** 函数里也做了相同的处理. +让我们通过这段代码来了解发生了什么。我们做的第一件事是定义值 **max_input_length** 和 **max_target_length** ,它为我们的评论和标题的长度设置了上限。由于评论正文通常比标题大得多,我们相应地调整了这些值。 有了 `preprocess_function()`,我们在整个课程中广泛使用的方便的 `Dataset.map()` 函数来标记整个语料库是一件简单的事情: @@ -315,7 +313,7 @@ tokenized_datasets = books_dataset.map(preprocess_function, batched=True) -## 文本摘要的指标 +## 文本摘要的指标 [[文本摘要的指标]] @@ -352,9 +350,9 @@ $$ \mathrm{Precision} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{ 然后按如下方式加载 ROUGE 指标: ```python -from datasets import load_metric +import evaluate -rouge_score = load_metric("rouge") +rouge_score = evaluate.load("rouge") ``` 然后我们可以使用 **rouge_score.compute()** 一次性计算所有指标的函数: @@ -392,7 +390,7 @@ Score(precision=0.86, recall=1.0, fmeasure=0.92) 我们将使用这些 ROUGE 分数来跟踪我们模型的性能,但在此之前,让我们做每个优秀的 NLP 从业者都应该做的事情:创建一个强大而简单的baseline! -### 创建强大的baseline +### 创建强大的baseline [[创建强大的baseline]] 文本摘要的一个常见基线是简单地取一篇文章的前三个句子,通常称为 _lead-3_ 基线。 我们可以使用句号(英文使用.)来跟踪句子边界,但这在"U.S." or "U.N."之类的首字母缩略词上会失败。所以我们将使用 `nltk` 库,它包含一个更好的算法来处理这些情况。 您可以使用 `pip` 安装软件包,如下所示: @@ -453,7 +451,7 @@ rouge_dict {#if fw === 'pt'} -## 使用 `Trainer` API微调mT5 +## 使用 `Trainer` API微调mT5 [[使用 `Trainer` API微调mT5]] 微调模型以进行提取摘要与我们在本章中介绍的其他任务非常相似。 我们需要做的第一件事是从`mt5-small`检查点加载预训练模型。 由于摘要提取是一个序列到序列的任务,我们可以使用 AutoModelForSeq2SeqLM 类加载模型,该类会自动下载并缓存权重: @@ -465,9 +463,9 @@ model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint) {:else} -## 使用 `Keras` API微调mT5 +## 使用 `Keras` API微调mT5 [[使用 `Keras` API微调mT5]] -微调模型以进行提取摘要与我们在本章中介绍的其他任务非常相似。 我们需要做的第一件事是从`mt5-small`检查点加载预训练模型。 由于摘要提取是一个序列到序列的任务,我们可以使用 AutoModelForSeq2SeqLM 类加载模型,该类会自动下载并缓存权重: +微调模型以进行提取摘要与我们在本章中介绍的其他任务非常相似。 我们需要做的第一件事是从`mt5-small`检查点加载预训练模型。 由于摘要提取是一个序列到序列的任务,我们可以使用 `TFAutoModelForSeq2SeqLM` 类加载模型,该类会自动下载并缓存权重: ```python from transformers import TFAutoModelForSeq2SeqLM @@ -673,14 +671,14 @@ trainer.push_to_hub(commit_message="Training complete", tags="summarization") 我们几乎准备好训练了! 我们只需要使用我们上面定义的数据整理器将我们的数据集转换为 tf.data.Dataset ,然后 `compile()` 和 `fit()` 模型。 首先,转换数据集: ```python -tf_train_dataset = tokenized_datasets["train"].to_tf_dataset( - columns=["input_ids", "attention_mask", "labels"], +tf_train_dataset = model.prepare_tf_dataset( + tokenized_datasets["train"], collate_fn=data_collator, shuffle=True, batch_size=8, ) -tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset( - columns=["input_ids", "attention_mask", "labels"], +tf_eval_dataset = model.prepare_tf_dataset( + tokenized_datasets["validation"], collate_fn=data_collator, shuffle=False, batch_size=8, @@ -726,18 +724,40 @@ model.fit( ) ``` -我们在训练期间输出了一些loss,但实际上我们希望看到我们之前计算的 ROUGE 指标。 要获得这些指标,我们需要从模型生成输出并将它们转换为字符串。 让我们为 ROUGE 指标构建一些标签和预测列表以进行比较(请注意,如果您在本节中遇到import的错误,您可能需要`!pip install tqdm`): +我们在训练期间得到了一些loss,但实际上我们希望看到我们之前计算的 ROUGE 指标。 要获得这些指标,我们需要从模型生成输出并将它们转换为字符串。 让我们为 ROUGE 指标构建一些标签和预测列表以进行比较(请注意,如果您遇到此部分的导入错误,您可能需要`!pip install tqdm`)。 我们还将使用一个可以显着提高性能的技巧 - 使用 TensorFlow 的加速线性代数编译器 [XLA](https://www.tensorflow.org/xla) 编译我们的生成代码。 XLA 对模型的计算图应用了各种优化,并显着提高了速度和内存使用率。 正如 Hugging Face [博客](https://huggingface.co/blog/tf-xla-generate) 中所述,当我们的输入形状变化不大时,XLA 效果最佳。 为了处理这个问题,我们将输入填充为 128 的倍数,并使用填充整理器创建一个新数据集,然后我们将 @tf.function(jit_compile=True) 装饰器应用于我们的生成函数,它会将整个函数标记为使用 XLA 进行编译。 ```python from tqdm import tqdm import numpy as np +generation_data_collator = DataCollatorForSeq2Seq( + tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=320 +) + +tf_generate_dataset = model.prepare_tf_dataset( + tokenized_datasets["validation"], + collate_fn=generation_data_collator, + shuffle=False, + batch_size=8, + drop_remainder=True, +) + + +@tf.function(jit_compile=True) +def generate_with_xla(batch): + return model.generate( + input_ids=batch["input_ids"], + attention_mask=batch["attention_mask"], + max_new_tokens=32, + ) + + all_preds = [] all_labels = [] -for batch in tqdm(tf_eval_dataset): - predictions = model.generate(**batch) +for batch, labels in tqdm(tf_generate_dataset): + predictions = generate_with_xla(batch) decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) - labels = batch["labels"].numpy() + labels = labels.numpy() labels = np.where(labels != -100, labels, tokenizer.pad_token_id) decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True) decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds] @@ -765,11 +785,11 @@ result = {key: value.mid.fmeasure * 100 for key, value in result.items()} {#if fw === 'pt'} -## 使用 🤗 Accelerate 微调 mT5 +## 使用 🤗 Accelerate 微调 mT5 [[使用 🤗 Accelerate 微调 mT5]] 使用 🤗 Accelerate 微调我们的模型与我们在 [Chapter 3](/course/chapter3) 中遇到的文本分类示例非常相似。 主要区别在于需要在训练期间显式生成摘要并定义我们如何计算 ROUGE 分数(回想一下,`Seq2SeqTrainer` 为我们生成了摘要)。 让我们看看我们如何在 🤗 Accelerate 中实现这两个要求! -### 为训练做好一切准备 +### 为训练做好一切准备 [[为训练做好一切准备]] The first thing we need to do is create a `DataLoader` for each of our splits. Since the PyTorch dataloaders expect batches of tensors, we need to set the format to `"torch"` in our datasets: 我们需要做的第一件事是为每个数据集的每一个拆分创建一个`DataLoader`。 由于 PyTorch 数据加载器需要成批的张量,我们需要在数据集中将格式设置为`torch`: @@ -888,7 +908,7 @@ repo = Repository(output_dir, clone_from=repo_name) ``` 这将允许我们在训练期间通过调用 `repo.push_to_hub()` 方法将模型推送到 Hub! 现在让我们通过写出完整的训练循环来结束我们的分析。 -### 训练循环 +### 训练循环 [[训练循环]] 文本摘要的训练循环与我们遇到的其他 🤗 Accelerate 示例非常相似,大致分为四个主要步骤:这 @@ -991,7 +1011,7 @@ Epoch 9: {'rouge1': 14.1192, 'rouge2': 7.0059, 'rougeL': 14.1172, 'rougeLsum': 1 {/if} -## 使用您微调的模型 +## 使用您微调的模型 [[使用您微调的模型]] 将模型推送到 Hub 后,您可以通过推理小部件或“管道”对象来使用它,如下所示: diff --git a/chapters/zh-CN/chapter7/6.mdx b/chapters/zh-CN/chapter7/6.mdx index 494baba89..6cf1a67b6 100644 --- a/chapters/zh-CN/chapter7/6.mdx +++ b/chapters/zh-CN/chapter7/6.mdx @@ -1,6 +1,6 @@ -# 从头开始训练因果语言模型 +# 从头开始训练因果语言模型 [[从头开始训练因果语言模型]] {#if fw === 'pt'} @@ -31,12 +31,11 @@ 在[第六章](/course/chapter6) 我们创建了一个高效的分词器来处理 Python 源代码,但我们仍然需要一个大规模数据集来预训练模型。在这里,我们将我们的分词器应用到源自 GitHub 存储库的 Python 代码语料库。然后我们将使用 `Trainer` API 和 🤗 Accelerate 来训练模型。让我们开始吧! - 这实际上展示了使用本节中训练并上传到 Hub 的模型。你可以在[这里](https://huggingface.co/huggingface-course/codeparrot-ds?text=plt.imshow%28)找到。请注意,由于在文本生成过程中发生了一些随机化,您可能会得到略有不同的结果。 -## 收集数据 +## 收集数据 [[收集数据]] -Python 代码可以从 GitHub 等代码存储库中获得,我们可以通过抓取每个 Python 存储库来使用它们来创建数据集。这是在[Transformers textbook](https://learning.oreilly.com/library/view/natural-language-processing/9781098103231/)预训练大型的GPT-2 模型。使用大约 180 GB 的 GitHub 转储,其中包含大约 2000 万个 Python 文件,称为 `codeparrot` ,作者构建了一个数据集,然后在[Hugging Face Hub](https://huggingface.co/datasets/transformersbook/codeparrot)上分享出来了. +Python 代码可以从 GitHub 等代码存储库中获得,我们可以通过抓取每个 Python 存储库来使用它们来创建数据集。这是在[Transformers textbook](https://learning.oreilly.com/library/view/natural-language-processing/9781098136789/)预训练大型的GPT-2 模型。使用大约 180 GB 的 GitHub 转储,其中包含大约 2000 万个 Python 文件,称为 `codeparrot` ,作者构建了一个数据集,然后在[Hugging Face Hub](https://huggingface.co/datasets/transformersbook/codeparrot)上分享出来了. 然而,对完整语料库的训练既耗时又费力,我们只需要与 Python 数据科学堆栈相关的数据集子集。所以,让我们开始过滤 `codeparrot` 包含此堆栈中任何库的所有文件的数据集。由于数据集的太大,我们希望避免下载它;因此反,我们将使用流功能来动态过滤它。为了使用前面提到的库过滤代码示例,我们将使用以下函数: @@ -67,6 +66,11 @@ False True 我们可以使用它来创建一个函数来流式传输数据集并过滤我们想要的元素: ```py +from collections import defaultdict +from tqdm import tqdm +from datasets import Dataset + + def filter_streaming_dataset(dataset, filters): filtered_dict = defaultdict(list) total = 0 @@ -103,7 +107,7 @@ filtered_data = filter_streaming_dataset(data, filters) from datasets import load_dataset, DatasetDict ds_train = load_dataset("huggingface-course/codeparrot-ds-train", split="train") -ds_valid = load_dataset("huggingface-course/codeparrot-ds-valid", split="train") +ds_valid = load_dataset("huggingface-course/codeparrot-ds-valid", split="validation") raw_datasets = DatasetDict( { @@ -162,7 +166,7 @@ LICENSE: bsd-3-clause''' 我们可以看到 `content` 字段包含我们希望我们的模型训练的代码。现在我们有了一个数据集,我们需要预处理文本,使其采用适合预训练的格式。 -## 准备数据集 +## 准备数据集 [[准备数据集]] @@ -254,7 +258,7 @@ DatasetDict({ -## 初始化新模型 +## 初始化新模型 [[初始化新模型]] 我们的第一步是新初始化一个 GPT-2 模型。我们将对我们的模型使用与小型 GPT-2 模型相同的配置,因此我们加载预训练配置,确保分词器大小与模型词汇量大小匹配并设置 `bos` 和 `eos` (序列的开始和结束)令牌 ID: @@ -374,17 +378,17 @@ labels shape: (5, 128) {#if fw === 'tf'} -现在,我们可以使用`to_tf_dataset()`方法,使用上面创建的数据整理器将数据集转换为TensorFlow数据集: +现在,我们可以使用`prepare_tf_dataset()`方法,使用上面创建的数据整理器将数据集转换为TensorFlow数据集: ```python -tf_train_dataset = tokenized_dataset["train"].to_tf_dataset( - columns=["input_ids", "attention_mask", "labels"], +tf_train_dataset = model.prepare_tf_dataset( + tokenized_dataset["train"], collate_fn=data_collator, shuffle=True, batch_size=32, ) -tf_eval_dataset = tokenized_dataset["valid"].to_tf_dataset( - columns=["input_ids", "attention_mask", "labels"], +tf_eval_dataset = model.prepare_tf_dataset( + tokenized_dataset["valid"], collate_fn=data_collator, shuffle=False, batch_size=32, @@ -510,13 +514,13 @@ model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback {:else} -💡 如果您有权访问具有多个 GPU 的计算机,则可以尝试使用 `MirroredStrategy` 上下文来大幅加快训练速度。您需要创建一个`tf.distribute.MirroredStrategy`对象,并确保 `to_tf_dataset` 命令以及模型创建和对 `fit()`的调用都在其 `scope()` context. 上下文中运行。您可以查看有关此内容的文档[here](https://www.tensorflow.org/guide/distributed_training#use_tfdistributestrategy_with_keras_modelfit). +💡 如果您正在使用具有多个 GPU 的计算机,则可以尝试使用 `MirroredStrategy` 上下文来大幅加快训练速度。您需要创建一个`tf.distribute.MirroredStrategy`对象,并确保所有的 `to_tf_dataset` 或 `prepare_tf_dataset()` 方法以及模型创建和对 `fit()`的调用都在其 `scope()` 上下文中运行。您可以查看有关此内容的文档[here](https://www.tensorflow.org/guide/distributed_training#use_tfdistributestrategy_with_keras_modelfit). {/if} -## 使用管道生成代码 +## 使用管道生成代码 [[使用管道生成代码]] 现在是关键的部分:让我们看看经过训练的模型的实际效果如何!我们可以在日志中看到损失稳步下降,但为了让模型进行测试,让我们看看它在某些测试上的表现如何。为此,我们将模型包装在文本生成中的`pipeline` ,如果有可用的,我们会将它放在 GPU 上进行快速生成: @@ -650,7 +654,7 @@ rf {#if fw === 'pt'} -## 用 🤗 Accelerate 训练 +## 用 🤗 Accelerate 训练 [[用 🤗 Accelerate 训练]] 我们已经看到了如何使用 `Trainer` ,这可以允许一些自定义。然而,有时我们想要完全控制训练循环,或者我们想要进行一些奇特的更改。在这种情况下 🤗 Accelerate 是一个不错的选择,在本节中,我们将逐步介绍使用它来训练我们的模型的步骤。为了让事情变得更有趣,我们还将在训练循环中添加一些修改。 @@ -854,7 +858,7 @@ model.train() completed_steps = 0 for epoch in range(num_train_epochs): for step, batch in tqdm( - enumerate(train_dataloader, start=1), total=len(train_dataloader) + enumerate(train_dataloader, start=1), total=num_training_steps ): logits = model(batch["input_ids"]).logits loss = keytoken_weighted_loss(batch["input_ids"], logits, keytoken_ids) diff --git a/chapters/zh-CN/chapter7/7.mdx b/chapters/zh-CN/chapter7/7.mdx index 8a823cadb..4d1fdfb96 100644 --- a/chapters/zh-CN/chapter7/7.mdx +++ b/chapters/zh-CN/chapter7/7.mdx @@ -1,6 +1,6 @@ -# 问答 +# 问答 [[问答]] {#if fw === 'pt'} @@ -29,7 +29,6 @@ 我们将使用 [SQuAD 数据集](https://rajpurkar.github.io/SQuAD-explorer/) 微调一个BERT模型, 其中包括群众工作者对一组维基百科文章提出的问题。以下是一个小的测试样例: - 本节使用的代码已经上传到了Hub。你可以在 [这里](https://huggingface.co/huggingface-course/bert-finetuned-squad?context=%F0%9F%A4%97+Transformers+is+backed+by+the+three+most+popular+deep+learning+libraries+%E2%80%94+Jax%2C+PyTorch+and+TensorFlow+%E2%80%94+with+a+seamless+integration+between+them.+It%27s+straightforward+to+train+your+models+with+one+before+loading+them+for+inference+with+the+other.&question=Which+deep+learning+libraries+back+%F0%9F%A4%97+Transformers%3F) 找到它并尝试用它进行预测。 @@ -39,11 +38,11 @@ -## 准备数据 +## 准备数据 [[准备数据]] 最常用作抽取式问答的学术基准的数据集是 [SQuAD](https://rajpurkar.github.io/SQuAD-explorer/), 所以这就是我们将在这里使用的。还有一个更难的 [SQuAD v2](https://huggingface.co/datasets/squad_v2) 基准, 其中包括没有答案的问题。只要你自己的数据集包含上下文列、问题列和答案列, 你就应该能够调整以下步骤。 -### SQuAD 数据集 +### SQuAD 数据集 [[SQuAD 数据集]] 像往常一样, 我们只需一步就可以下载和缓存数据集, 这要归功于 `load_dataset()`: @@ -127,7 +126,7 @@ print(raw_datasets["validation"][2]["question"]) 我们可以看到, 答案确实可以是我们之前看到的三种可能性之一。 -### 处理训练数据 +### 处理训练数据 [[处理训练数据]] @@ -448,7 +447,7 @@ len(raw_datasets["train"]), len(train_dataset) 正如我们所见, 预处理增加了大约 1,000 个特征。我们的训练集现在可以使用了-- 让我们深入研究验证集的预处理! -### 处理验证数据 +### 处理验证数据 [[处理验证数据]] 预处理验证数据会稍微容易一些, 因为我们不需要生成标签(除非我们想计算验证损失, 但这个数字并不能真正帮助我们理解模型有多好)。真正的乐趣是将模型的预测解释为原始上下文的跨度。为此, 我们只需要存储偏移映射和某种方式来将每个创建的特征与它来自的原始示例相匹配。由于原始数据集中有一个 ID 列, 我们将使用该 ID。 @@ -506,19 +505,19 @@ I在这种情况下, 我们只添加了几百个样本, 因此验证数据集中 {#if fw === 'pt'} -## 使用 `Trainer` API 微调模型 +## 使用 `Trainer` API 微调模型 [[使用 `Trainer` API 微调模型]] 这个例子的训练代码看起来很像前面几节中的代码 -- 最难的是编写 `compute_metrics()` 函数。由于我们将所有样本填充到我们设置的最大长度, 因此没有数据整理器要定义, 所以这个度量计算真的是我们唯一需要担心的事情。困难的部分是将模型预测后处理为原始示例中的文本范围; 一旦我们这样做了, 🤗 Datasets 库中的指标将为我们完成大部分工作。 {:else} -## 使用 Keras 微调模型 +## 使用 Keras 微调模型 [[使用 Keras 微调模型]] 这个示例的训练代码看起来很像前几节中的代码, 但是计算指标将是唯一的挑战。因为我们将所有的样本填充到我们设置的最大长度, 所以不需要定义数据整理器, 所以这个度量计算实际上是我们唯一需要担心的事情。困难的部分是将模型预测后处理成原始例子中的文本范围; 一旦我们完成了这些, 🤗 Datasets 库中的指标将为我们完成大部分工作。 {/if} -### 后处理 +### 后处理 [[后处理]] {#if fw === 'pt'} @@ -530,7 +529,7 @@ I在这种情况下, 我们只添加了几百个样本, 因此验证数据集中 {/if} -该模型将在输入ID中为答案的开始和结束位置输出Logit, 正如我们在探索 [`question-answering` pipeline](/course/chapter6/4) 时看到的那样。后处理步骤将类似于我们在那里所做的, 所以这里是我们采取的行动的快速提醒: +该模型将在输入ID中为答案的开始和结束位置输出Logit, 正如我们在探索 [`question-answering` pipeline](/course/chapter6/3b) 时看到的那样。后处理步骤将类似于我们在那里所做的, 所以这里是我们采取的行动的快速提醒: - 我们屏蔽了与上下文之外的标记相对应的开始和结束 logits。 - 然后, 我们使用 softmax 将开始和结束 logits 转换为概率。 @@ -671,12 +670,12 @@ for example in small_eval_set: predicted_answers.append({"id": example_id, "prediction_text": best_answer["text"]}) ``` -预测答案的最终格式是我们将使用的度量标准所期望的格式。像往常一样, 我们可以在 🤗 Datasets 库的帮助下加载它: +预测答案的最终格式是我们将使用的度量标准所期望的格式。像往常一样, 我们可以在 🤗 Evaluate 库的帮助下加载它: ```python -from datasets import load_metric +import evaluate -metric = load_metric("squad") +metric = evaluate.load("squad") ``` 该指标期望我们上面看到的格式的预测答案 (一个字典列表, 其中一个键用于示例 ID, 一个键用于预测文本) 和以下格式的理论答案 (一个字典列表, 一个键示例的 ID 和可能答案的一键): @@ -789,7 +788,7 @@ compute_metrics(start_logits, end_logits, eval_set, small_eval_set) 看起来不错! 现在让我们用它来微调我们的模型。 -### 微调模型 +### 微调模型 [[微调模型]] {#if fw === 'pt'} @@ -863,20 +862,14 @@ data_collator = DefaultDataCollator(return_tensors="tf") 现在我们像往常一样创建数据集。 ```python -tf_train_dataset = train_dataset.to_tf_dataset( - columns=[ - "input_ids", - "start_positions", - "end_positions", - "attention_mask", - "token_type_ids", - ], +tf_train_dataset = model.prepare_tf_dataset( + train_dataset, collate_fn=data_collator, shuffle=True, batch_size=16, ) -tf_eval_dataset = validation_dataset.to_tf_dataset( - columns=["input_ids", "attention_mask", "token_type_ids"], +tf_eval_dataset = model.prepare_tf_dataset( + validation_dataset, collate_fn=data_collator, shuffle=False, batch_size=16, @@ -956,7 +949,7 @@ model.fit(tf_train_dataset, callbacks=[callback], epochs=num_train_epochs) 一旦训练完成, 我们终于可以评估我们的模型(并祈祷我们没有把所有的计算时间都花在任何事情上)。`Trainer` 的 `predict()` 方法将返回一个元组, 其中第一个元素将是模型的预测 (这里是带有开始和结束 logits 的对)。我们将其发送给 `compute_metrics()` 函数: ```python -predictions, _ = trainer.predict(validation_dataset) +predictions, _, _ = trainer.predict(validation_dataset) start_logits, end_logits = predictions compute_metrics(start_logits, end_logits, validation_dataset, raw_datasets["validation"]) ``` @@ -1013,11 +1006,11 @@ trainer.push_to_hub(commit_message="Training complete") 如果你想更深入地了解训练循环, 我们现在将向你展示如何使用 🤗 Accelerate 来做同样的事情。 -## 自定义训练循环 +## 自定义训练循环 [[自定义训练循环]] 现在让我们来看一下完整的训练循环, 这样您就可以轻松地自定义所需的部分。它看起来很像 [第三章](/course/chapter3/4) 中的训练循环, 除了评估循环。我们将能够定期评估模型, 因为我们不再受 `Trainer` 类的限制。 -### 为训练做准备 +### 为训练做准备 [[为训练做准备]] 首先, 我们需要从我们的数据集中构建 `DataLoader`。我们将这些数据集的格式设置为 `"torch"`, 并删除模型未使用的验证集中的列。然后, 我们可以使用 Transformers 提供的 `default_data_collator` 作为 `collate_fn`, 并打乱训练集, 但不打乱验证集58: @@ -1105,7 +1098,7 @@ repo = Repository(output_dir, clone_from=repo_name) 我们现在可以通过调用 `repo.push_to_hub()` 方法上传我们保存在 `output_dir` 中的任何内容。这将帮助我们在每个 epoch 结束时上传中间模型。 -## 训练循环 +## 训练循环 [[训练循环]] 我们现在准备编写完整的训练循环。在定义了一个进度条来跟踪训练进行后, 循环分为三个部分: @@ -1181,7 +1174,7 @@ unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) {/if} -## 使用微调模型 +## 使用微调模型 [[使用微调模型]] 我们已经向您展示了如何将我们在模型中心微调的模型与推理小部件一起使用。要在 `pipeline` 中本地使用它, 你只需要指定模型标识符: diff --git a/chapters/zh-CN/chapter7/8.mdx b/chapters/zh-CN/chapter7/8.mdx index be36949af..6d691a65a 100644 --- a/chapters/zh-CN/chapter7/8.mdx +++ b/chapters/zh-CN/chapter7/8.mdx @@ -1,4 +1,4 @@ -# 精通自然语言处理 +# 精通自然语言处理 [[精通自然语言处理]] 如果你在课程中做到了这一步,恭喜你——你现在拥有了用 🤗 Transformers 和 Hugging Face 生态系统解决(几乎)任何 NLP 任务所需的所有知识和工具! diff --git a/chapters/zh-CN/chapter7/9.mdx b/chapters/zh-CN/chapter7/9.mdx index 3e4841372..0ab1edce2 100644 --- a/chapters/zh-CN/chapter7/9.mdx +++ b/chapters/zh-CN/chapter7/9.mdx @@ -2,7 +2,7 @@ -# End-of-chapter quiz +# 章末小测验 [[章末小测验]] Let's test what you learned in this chapter! diff --git a/chapters/zh-CN/chapter8/1.mdx b/chapters/zh-CN/chapter8/1.mdx index 0505bbb4d..7f2e8f531 100644 --- a/chapters/zh-CN/chapter8/1.mdx +++ b/chapters/zh-CN/chapter8/1.mdx @@ -1,4 +1,4 @@ -# 介绍 +# 介绍 [[介绍]] 既然您知道如何使用🤗 Transformers处理最常见的NLP任务,您应该能够开始自己的项目了!在本章中,我们将探讨遇到问题时该怎么做。您将学习如何成功调试代码或训练,以及如果自己无法解决问题,如何向社区寻求帮助。如果您认为您在其中一个拥抱人脸库中发现了错误,我们将向您展示报告它的最佳方式,以便尽快解决问题。 diff --git a/chapters/zh-CN/chapter8/2.mdx b/chapters/zh-CN/chapter8/2.mdx index 311b09e6b..bcab43e75 100644 --- a/chapters/zh-CN/chapter8/2.mdx +++ b/chapters/zh-CN/chapter8/2.mdx @@ -1,4 +1,4 @@ -# 出现错误时该怎么办 +# 出现错误时该怎么办 [[出现错误时该怎么办]] -# 调试训练管道 +# 调试训练管道 [[调试训练管道]] @@ -22,7 +22,7 @@ 为了证明这一点,我们将使用以下脚本(尝试)在 [MNLI 数据集](https://huggingface.co/datasets/glue)上微调 DistilBERT 模型: ```py -from datasets import load_dataset, load_metric +from datasets import load_dataset from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, @@ -30,7 +30,7 @@ from transformers import ( Trainer, ) -raw_datasets = load_dataset("glue", "mnli") +raw_datasets = evaluate.load("glue", "mnli") model_checkpoint = "distilbert-base-uncased" tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) @@ -52,7 +52,7 @@ args = TrainingArguments( weight_decay=0.01, ) -metric = load_metric("glue", "mnli") +metric = evaluate.load("glue", "mnli") def compute_metrics(eval_pred): @@ -76,7 +76,7 @@ trainer.train() 'ValueError: You have to specify either input_ids or inputs_embeds' ``` -### 检查数据 +### 检查数据 [[检查数据]] 这是不言而喻的,如果你的数据被破坏,“Trainer”将无法形成批次,更不用说训练你的模型了。 所以首先,你需要看看你的训练集中有什么。 @@ -98,7 +98,7 @@ trainer.train_dataset[0] 为什么没有处理数据生成标记呢? 我们确实在数据集上使用了“Dataset.map()”方法来对每个样本应用标记器。 但是如果你仔细看代码,你会发现我们在将训练和评估集传递给`Trainer`时犯了一个错误。 我们在这里没有使用 `tokenized_datasets`,而是使用了 `raw_datasets` 🤦。 所以让我们解决这个问题! ```py -from datasets import load_dataset, load_metric +from datasets import load_dataset from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, @@ -106,7 +106,7 @@ from transformers import ( Trainer, ) -raw_datasets = load_dataset("glue", "mnli") +raw_datasets = evaluate.load("glue", "mnli") model_checkpoint = "distilbert-base-uncased" tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) @@ -128,7 +128,7 @@ args = TrainingArguments( weight_decay=0.01, ) -metric = load_metric("glue", "mnli") +metric = evaluate.load("glue", "mnli") def compute_metrics(eval_pred): @@ -200,7 +200,7 @@ transformers.models.distilbert.modeling_distilbert.DistilBertForSequenceClassifi 我们通过解码检查了输入 ID 是否正确。 接下来是检查 `attention_mask`: ```py -tokenizer.decode(trainer.train_dataset[0]["attention_mask"]) +trainer.train_dataset[0]["attention_mask"] ``` ```python out @@ -253,7 +253,7 @@ trainer.train_dataset.features["label"].names 现在我们知道我们的数据集看起来不错,是时候检查训练管道的下一步了。 -### 从 datasets 到 dataloaders +### 从 datasets 到 dataloaders [[从 datasets 到 dataloaders]] 训练管道中可能出错的下一件事是当“Trainer”尝试从训练或验证集形成批次时。 一旦你确定 `Trainer` 的数据集是正确的,你可以尝试通过执行以下操作手动形成一个批次(可以将 `train` 替换为 `eval` 用于验证数据加载器): @@ -291,7 +291,8 @@ data_collator 答案是因为我们没有将 `tokenizer` 传递给 `Trainer`,所以它无法创建我们想要的 `DataCollatorWithPadding`。 在实践中,您应该明确地传递您想要使用的数据整理器,以确保避免这些类型的错误。 让我们调整我们的代码来做到这一点: ```py -from datasets import load_dataset, load_metric +from datasets import load_dataset +import evaluate from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, @@ -300,7 +301,7 @@ from transformers import ( Trainer, ) -raw_datasets = load_dataset("glue", "mnli") +raw_datasets = evaluate.load("glue", "mnli") model_checkpoint = "distilbert-base-uncased" tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) @@ -322,7 +323,7 @@ args = TrainingArguments( weight_decay=0.01, ) -metric = load_metric("glue", "mnli") +metric = evaluate.load("glue", "mnli") def compute_metrics(eval_pred): @@ -371,7 +372,7 @@ batch = data_collator([actual_train_set[i] for i in range(4)]) 现在我们已经调试了批处理创建过程,是时候将数据传递给模型了! -### 检查模型 +### 检查模型 [[检查模型]] 您应该能够通过执行以下命令来获得一个批次的数据: @@ -416,7 +417,8 @@ trainer.model.config.num_labels 有两个标签,只允许 0 和 1 作为目标,但是根据错误信息我们得到一个 2。得到一个 2 实际上是正常的:如果我们记得我们之前提取的标签名称,有三个,所以我们有索引 0 , 1 和 2 在我们的数据集中。 问题是我们没有告诉我们的模型,它应该创建三个标签。 所以让我们解决这个问题! ```py -from datasets import load_dataset, load_metric +from datasets import load_dataset +import evaluate from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, @@ -425,7 +427,7 @@ from transformers import ( Trainer, ) -raw_datasets = load_dataset("glue", "mnli") +raw_datasets = evaluate.load("glue", "mnli") model_checkpoint = "distilbert-base-uncased" tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) @@ -447,7 +449,7 @@ args = TrainingArguments( weight_decay=0.01, ) -metric = load_metric("glue", "mnli") +metric = evaluate.load("glue", "mnli") def compute_metrics(eval_pred): @@ -490,7 +492,7 @@ outputs = trainer.model.to(device)(**batch) 如果仍然出现错误,请确保重新启动notebook并仅执行脚本的最后一个版本。 -### 执行一个优化器步骤 +### 执行一个优化器步骤 [[执行一个优化器步骤]] 现在我们知道我们可以构建实际通过模型检查的成批次的数据,我们已经为训练管道的下一步做好准备:计算梯度并执行优化步骤。 @@ -512,7 +514,7 @@ trainer.optimizer.step() 同样,如果您在 `Trainer` 中使用默认优化器,则在此阶段您不应该收到错误,但如果您有自定义优化器,则可能会出现一些问题需要在这里调试。 如果您在此阶段遇到奇怪的 CUDA 错误,请不要忘记返回 CPU。 说到 CUDA 错误,前面我们提到了一个特殊情况。 现在让我们来看看。 -### 处理 CUDA out-of-memory错误 +### 处理 CUDA out-of-memory错误 [[处理 CUDA out-of-memory错误]] 每当您收到以`RuntimeError: CUDA out of memory`开头的错误消息时,这表明您的 GPU 内存不足。 这与您的代码没有直接关联,并且它可能发生在运行良好的代码中。 此错误意味着您试图在 GPU 的内部存储器中放入太多东西,这导致了错误。 与其他 CUDA 错误一样,您需要重新启动内核才能再次运行训练。 @@ -524,7 +526,7 @@ trainer.optimizer.step() -### 评估模型 +### 评估模型 [[评估模型]] 现在我们已经解决了代码的所有问题,一切都很完美,训练应该可以顺利进行,对吧? 没那么快! 如果你运行 `trainer.train()` 命令,一开始一切看起来都不错,但过一会儿你会得到以下信息: @@ -626,7 +628,8 @@ compute_metrics((predictions, labels)) ```py import numpy as np -from datasets import load_dataset, load_metric +from datasets import load_dataset +import evaluate from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, @@ -635,7 +638,7 @@ from transformers import ( Trainer, ) -raw_datasets = load_dataset("glue", "mnli") +raw_datasets = evaluate.load("glue", "mnli") model_checkpoint = "distilbert-base-uncased" tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) @@ -657,7 +660,7 @@ args = TrainingArguments( weight_decay=0.01, ) -metric = load_metric("glue", "mnli") +metric = evaluate.load("glue", "mnli") def compute_metrics(eval_pred): @@ -688,11 +691,11 @@ trainer.train() -## 在训练期间调试静默(没有任何错误提示)错误 +## 在训练期间调试静默(没有任何错误提示)错误 [[在训练期间调试静默(没有任何错误提示)错误]] 我们可以做些什么来调试一个没有错误地完成但没有得到好的结果的训练? 我们会在这里给你一些提示,但请注意,这种调试是机器学习中最难的部分,并且没有神奇的答案。 -### 检查您的数据(再次!) +### 检查您的数据(再次!) [[检查您的数据(再次!)]] 只有在理论上可以从您的数据中学到任何东西时,您的模型才会学到一些东西。 如果存在损坏数据的错误或标签是随机属性的,那么您很可能不会在数据集上获得任何知识。 因此,始终首先仔细检查您的解码输入和标签,然后问自己以下问题: @@ -713,7 +716,7 @@ trainer.train() 当您确定您的数据是完美的时,您可以通过一个简单的测试来查看模型是否能够对其进行训练。 -### 在一批上过度拟合你的模型 +### 在一批上过度拟合你的模型 [[在一批上过度拟合你的模型]] 过度拟合通常是我们在训练时尽量避免的事情,因为这意味着模型没有学习识别我们想要的一般特征,而只是记住了训练样本。 在这种情况下,一遍又一遍地尝试在一个批次上训练您的模型是一个很好的测试,可以检查您的问题是否可以通过您尝试训练的模型来解决。 它还将帮助您查看您的初始学习率是否太高。 @@ -765,7 +768,7 @@ compute_metrics((preds.cpu().numpy(), labels.cpu().numpy())) -### 在你有第一个基线之前不要调整任何东西 +### 在你有第一个基线之前不要调整任何东西 [[在你有第一个基线之前不要调整任何东西]] 超参数调优总是被强调为机器学习中最难的部分,但这只是帮助您在指标上有所收获的最后一步。 大多数情况下,`Trainer` 的默认超参数可以很好地为您提供良好的结果,因此在您获得超出数据集基线的东西之前,不要开始进行耗时且昂贵的超参数搜索 . @@ -773,7 +776,7 @@ compute_metrics((preds.cpu().numpy(), labels.cpu().numpy())) 如果您正在调整模型本身,不要尝试任何您无法合理证明的事情。 始终确保您返回过拟合测试以验证您的更改没有产生任何意外后果。 -### 请求帮忙 +### 请求帮忙 [[请求帮忙]] 希望您会在本节中找到一些可以帮助您解决问题的建议,但如果不是这样,请记住您可以随时在 [论坛](https://discuss.huggingface.co/) 上向社区提问。 diff --git a/chapters/zh-CN/chapter8/4_tf.mdx b/chapters/zh-CN/chapter8/4_tf.mdx index 21a4e57b5..b23b67fa9 100644 --- a/chapters/zh-CN/chapter8/4_tf.mdx +++ b/chapters/zh-CN/chapter8/4_tf.mdx @@ -1,6 +1,6 @@ -# Debugging the training pipeline +# Debugging the training pipeline [[Debugging the training pipeline]] -### TensorFlow 🦛饿饿 +### TensorFlow 🦛饿饿 [[TensorFlow 🦛饿饿]] 您应该注意的 TensorFlow 的一个特殊怪癖是,它会在您加载模型或进行任何训练后立即为自己分配 *所有 * GPU 内存,然后根据需要分配该内存。这与其他框架的行为不同,例如 PyTorch,后者根据 CUDA 的需要分配内存,而不是在内部进行。 TensorFlow 方法的一个优点是,当您耗尽内存时,它通常会给出有用的错误,并且它可以从该状态恢复而不会导致整个 CUDA 内核崩溃。但也有一个重要的缺点:如果你同时运行两个 TensorFlow 进程,那么**你将度过一段糟糕的时光**。 @@ -407,7 +407,7 @@ model.fit(train_dataset) 如果您开始运行之前正确的代码却收到有关 CUDA、BLAS 或 cuBLAS 的错误,这通常是罪魁祸首。您可以使用类似 `nvidia-smi` 的命令来检查 - 当您关闭或重新启动当前笔记本时,您的大部分内存是否空闲,或者是否仍在使用中?如果它仍在使用中,则有其他东西在占用它! -### 检查您的数据(再次!) +### 检查您的数据(再次!) [[检查您的数据(再次!)]] 只有在理论上可以从您的数据中学到任何东西时,您的模型才会学到一些东西。 如果存在损坏数据的错误或标签是随机属性的,那么您很可能不会在数据集上获得任何知识。这里一个有用的工具是`tokenizer.decode()`。 这会将 `input_ids` 转换回字符串,因此您可以查看数据并查看您的训练数据是否正在教授您希望它教授的内容。 例如,像我们上面所做的那样从 `tf.data.Dataset` 中获取 `batch` 后,您可以像这样解码第一个元素: @@ -435,7 +435,7 @@ label = labels[0] 当您确定您的数据是完美的时,您可以通过一个简单的测试来查看模型是否能够对其进行训练。 -### 在一批上过度拟合你的模型 +### 在一批上过度拟合你的模型 [[在一批上过度拟合你的模型]] 过度拟合通常是我们在训练时尽量避免的事情,因为这意味着模型没有学习识别我们想要的一般特征,而只是记住了训练样本。 但是,一遍又一遍地尝试在一个批次上训练您的模型是一个很好的测试,可以检查您构建的问题是否可以通过您尝试训练的模型来解决。 它还将帮助您查看您的初始学习率是否太高。 @@ -445,8 +445,8 @@ label = labels[0] for batch in train_dataset: break -# Make sure you have run model.compile() and set your optimizer, -# and your loss/metrics if you're using them +# Make sure you have run model.compile() and set your optimizer, [[Make sure you have run model.compile() and set your optimizer,]] +# and your loss/metrics if you're using them [[and your loss/metrics if you're using them]] model.fit(batch, epochs=20) ``` @@ -467,7 +467,7 @@ model.fit(batch, epochs=20) -### 在你有第一个基线之前不要调整任何东西 +### 在你有第一个基线之前不要调整任何东西 [[在你有第一个基线之前不要调整任何东西]] 超参数调整总是被强调为机器学习中最难的部分,但这只是帮助您在指标上获得一点点提升的最后一步。 例如将默认的 Adam 学习率 1e-3 与 Transformer 模型一起使用,当然会使学习进行得非常缓慢或完全停止,但大多数时候“合理”的超参数,例如从 1e-5 到 5e-5 的学习率,会很好地给你带来好的结果。因此,在您获得超出数据集基线的东西之前,不要开始进行耗时且昂贵的超参数搜索。 @@ -475,7 +475,7 @@ model.fit(batch, epochs=20) 如果您正在调整模型本身,不要尝试任何您无法合理证明的事情。 始终确保您返回过拟合测试以验证您的更改没有产生任何意外后果。 -### 请求帮忙 +### 请求帮忙 [[请求帮忙]] 希望您会在本节中找到一些可以帮助您解决问题的建议,但如果不是这样,请记住您可以随时在 [论坛](https://discuss.huggingface.co/) 上向社区提问。 diff --git a/chapters/zh-CN/chapter8/5.mdx b/chapters/zh-CN/chapter8/5.mdx index c620cc9a7..e7164be66 100644 --- a/chapters/zh-CN/chapter8/5.mdx +++ b/chapters/zh-CN/chapter8/5.mdx @@ -1,4 +1,4 @@ -# 如何写一个好问题 +# 如何写一个好问题 [[如何写一个好问题]] 当您确定手头有错误时,第一步是构建一个最小的可重现示例。 -## 创建一个最小的可重现示例 +## 创建一个最小的可重现示例 [[创建一个最小的可重现示例]] 隔离产生错误的代码段非常重要,因为 Hugging Face 团队中没有人是魔术师(目前),他们无法修复他们看不到的东西。顾名思义,最小的可重现示例应该是可重现的。这意味着它不应依赖于您可能拥有的任何外部文件或数据。尝试用一些看起来像真实值的虚拟值替换您正在使用的数据,但仍然会产生相同的错误。 @@ -26,13 +26,13 @@ 如果您觉得足够舒服,请检查发生错误的源代码。您可能会找到问题的解决方案(在这种情况下,您甚至可以提出拉取请求来修复它),但更一般地说,这可以帮助维护人员在阅读您的报告时更好地理解来源。 -## 填写问题模板 +## 填写问题模板 [[填写问题模板]] 当您提交问题时,您会注意到有一个模板需要填写。我们将按照[🤗 Transformers issues](https://github.com/huggingface/transformers/issues/new/choose)在这里,但是如果您在另一个存储库中报告问题,则需要相同类型的信息。不要将模板留空:花时间填写它可以最大限度地提高您获得答案和解决问题的机会。 通常,在提交问题时,请始终保持礼貌。这是一个开源项目,因此您使用的是免费软件,没有人有任何义务帮助您。您可能会在您的问题中包含您认为合理的批评,但是维护人员很可能会认为它很糟糕并且不会急于帮助您。确保你阅读了[code of conduct](https://github.com/huggingface/transformers/blob/master/CODE_OF_CONDUCT.md)项目的。 -### 包括您的环境信息 +### 包括您的环境信息 [[包括您的环境信息]] 🤗 Transformers 提供了一个实用程序来获取我们需要的关于您的环境的所有信息。只需在终端中输入以下内容: @@ -59,13 +59,13 @@ Copy-and-paste the text below in your GitHub issue and FILL OUT the two last poi 您还可以添加一个 **!** 在开始的时候 **transformers-cli env** 命令从笔记本单元执行它,然后在问题的开头复制并粘贴结果。 -### 标记人员 +### 标记人员 [[标记人员]] 通过输入标记人员 **@** 其次是他们的 GitHub 句柄将向他们发送通知,以便他们会看到您的问题并可能会更快地回复。适度使用它,因为如果您标记的人没有直接链接,他们可能不喜欢收到通知。如果您查看了与您的错误相关的源文件,您应该在您认为对您的问题负责的行中标记最后一个进行更改的人(您可以通过查看 GitHub 上的所述行找到此信息,选择它,然后单击“查看 git blame”)。 否则,模板会提供要标记的人的建议。一般来说,不要标记超过三个人! -### 包含一格可重复的示例 +### 包含一格可重复的示例 [[包含一格可重复的示例]] 如果您已经设法创建了一个产生错误的独立示例,那么现在是包含它的时候了!键入一行包含三个反引号,后跟 **python** , 像这样: @@ -75,11 +75,11 @@ Copy-and-paste the text below in your GitHub issue and FILL OUT the two last poi 然后粘贴您的最小可重现示例并键入一个带有三个反引号的新行。这将确保您的代码格式正确。如果您没有设法创建可重现的示例,请以清晰的步骤解释您是如何解决问题的。如果可以,请包含指向错误所在的 Google Colab 笔记本的链接。你分享的信息越多,维护者就越有能力回复你。在所有情况下,您都应该复制并粘贴您收到的整个错误消息。如果您在 Colab 中工作,请记住,堆栈跟踪中的某些帧可能会自动折叠,因此请确保在复制之前展开它们。与代码示例一样,将该错误消息放在两行之间,并带有三个反引号,因此格式正确。 -### 描述预期行为 +### 描述预期行为 [[描述预期行为]] 用几行解释你期望得到什么,以便维护人员完全掌握问题。这部分通常很明显,所以应该用一句话来形容,但在某些情况下,您可能有很多话要说。 -## 然后什么? +## 然后什么? [[然后什么?]] 提交您的问题后,请确保快速检查一切是否正常。如果您犯了错误,您可以编辑问题,或者如果您发现问题与您最初的想法不同,甚至可以更改其标题。如果你没有得到答案,就没有必要对人进行 ping 操作。如果几天内没有人帮助您,很可能没有人能理解您的问题。不要犹豫,回到可重现的例子。你能不能让它更短更切题?如果您在一周内没有得到答复,您可以留言温和地寻求帮助,特别是如果您已编辑问题以包含有关该问题的更多信息。 diff --git a/chapters/zh-CN/chapter8/6.mdx b/chapters/zh-CN/chapter8/6.mdx index a13a9b23c..f33712406 100644 --- a/chapters/zh-CN/chapter8/6.mdx +++ b/chapters/zh-CN/chapter8/6.mdx @@ -1,4 +1,4 @@ -# 第2部分完成! +# 第2部分完成! [[第2部分完成!]] 恭喜,您已经完成了课程的第二部分!我们正在积极研究第三个,所以订阅我们的[newsletter](https://huggingface.curated.co/)以确保您不会错过它的发布。 diff --git a/chapters/zh-CN/chapter8/7.mdx b/chapters/zh-CN/chapter8/7.mdx index 45f4c7ee7..9bfb36bc0 100644 --- a/chapters/zh-CN/chapter8/7.mdx +++ b/chapters/zh-CN/chapter8/7.mdx @@ -1,6 +1,6 @@ -# 章末测评 +# 章末小测验 [[章末小测验]] 让我们测试一下你在本章学到的东西! @@ -184,7 +184,8 @@ from transformers import GPT3ForSequenceClassification }, { text: "它允许维护人员知道你是在 GPU 还是 CPU 上运行代码。", - explain: "正确! 正如我们在本章中看到的, gpu 和 cpu 上的错误在风格上可能有很大的不同, 了解您正在使用的硬件可以帮助集中维护人员的注意力。但这不是唯一的好处...", + explain: "正确的! 正如我们在本章中所见,在 GPU 或 CPU 上运行的代码可能会产生不同的结果或错误,了解您使用的是哪种硬件有助于吸引维护人员的注意力。 但这不是唯一的好处...", + correct: true } ]} /> \ No newline at end of file diff --git a/chapters/zh-CN/chapter9/1.mdx b/chapters/zh-CN/chapter9/1.mdx index 88b53b355..f438d58aa 100644 --- a/chapters/zh-CN/chapter9/1.mdx +++ b/chapters/zh-CN/chapter9/1.mdx @@ -1,4 +1,4 @@ -# Gradio 简介 +# Gradio 简介 [[Gradio 简介]] 在本章中,我们将学习如何为您的机器学习构建**交互式演示**模型。 @@ -28,9 +28,3 @@ 本章分为两个部分,包括_概念_和_应用程序_。在您了解每个部分的概念后,您将应用它来构建特定类型的演示,范围从图像分类到语音识别。当你读完本章时,你将能够用几行 Python 代码构建这些演示(以及更多!)。 👀 点击 Hugging Face Spaces 以查看机器学习社区构建的许多机器学习演示的最新示例! - -## Gradio 方块派对🥳 - -如果你想充分利用本章中的知识,就加入 Gradio 积木派对吧!这是由 Hugging Face 于**5 月 16 日至 31 日**举办的社区活动。在此活动中,您将使用 Gradio 构建酷炫的机器学习演示,并参与赢取 Hugging Face 礼物和奖品! - -查看 [活动描述](https://github.com/AK391/community-events/blob/main/gradio-blocks/README.md) 可以了解如何参与的详细信息 - 我们迫不及待地想看看你构建的🤗演示! diff --git a/chapters/zh-CN/chapter9/2.mdx b/chapters/zh-CN/chapter9/2.mdx index f313f304d..9910c483c 100644 --- a/chapters/zh-CN/chapter9/2.mdx +++ b/chapters/zh-CN/chapter9/2.mdx @@ -1,4 +1,4 @@ -# 构建你的第一个演示 +# 构建你的第一个演示 [[构建你的第一个演示]] -### `launch()` 方法 +### `launch()` 方法 [[`launch()` 方法]] 到目前为止,我们已经使用了`launch()`方法来启动界面,但是我们 还没有真正讨论过它的作用。 @@ -120,7 +120,7 @@ gr.Interface( 我们将在下一节中更详细地介绍 `share` 参数! -## ✏️ 让我们应用它! +## ✏️ 让我们应用它! [[✏️ 让我们应用它!]] 让我们构建一个界面,让您演示 **speech-recognition** 模型。 为了让它变得有趣,我们将接受 *or* 麦克风输入或上传的文件。 diff --git a/chapters/zh-CN/chapter9/4.mdx b/chapters/zh-CN/chapter9/4.mdx index 62c675d99..676084ce1 100644 --- a/chapters/zh-CN/chapter9/4.mdx +++ b/chapters/zh-CN/chapter9/4.mdx @@ -1,4 +1,4 @@ -# 与他人分享演示 +# 与他人分享演示 [[与他人分享演示]] Overview of a gradio interface @@ -52,7 +52,7 @@ gr.Interface( -### 使用临时链接分享您的演示 +### 使用临时链接分享您的演示 [[使用临时链接分享您的演示]] 现在我们已经有了机器学习模型的工作演示,让我们学习如何轻松共享指向我们界面的链接。 通过在 `launch()` 方法中设置 `share=True` 可以轻松地公开共享接口: @@ -64,7 +64,7 @@ gr.Interface(classify_image, "image", "label").launch(share=True) 但是请记住,这些链接是可公开访问的,这意味着任何人都可以使用您的模型进行预测! 因此,请确保不要通过您编写的函数公开任何敏感信息,或允许在您的设备上发生任何关键更改。 如果设置 `share=False`(默认值),则仅创建本地链接。 -### 在 Hugging Face Spaces 上托管您的演示 +### 在 Hugging Face Spaces 上托管您的演示 [[在 Hugging Face Spaces 上托管您的演示]] 可以传递给同事的共享链接很酷,但是如何永久托管您的演示并让它存在于互联网上自己的“空间”中? @@ -74,7 +74,7 @@ Hugging Face Spaces 提供了在互联网上永久托管 Gradio 模型的基础 -## ✏️ 让我们应用它! +## ✏️ 让我们应用它! [[✏️ 让我们应用它!]] 使用到目前为止我们在各节中学到的知识,让我们创建我们在[本章第一节](/course/chapter9/1)中看到的草图识别演示。 让我们为我们的界面添加一些自定义并设置 `share=True` 以创建一个我们可以传递的公共链接。 diff --git a/chapters/zh-CN/chapter9/5.mdx b/chapters/zh-CN/chapter9/5.mdx index 4fc5a67a0..77200b830 100644 --- a/chapters/zh-CN/chapter9/5.mdx +++ b/chapters/zh-CN/chapter9/5.mdx @@ -1,4 +1,4 @@ -# 与 Hugging Face Hub 整合 +# 与 Hugging Face Hub 整合 [[与 Hugging Face Hub 整合]] -### 创建多步demo +### 创建多步demo [[创建多步demo]] 在某些情况下, 您可能需要一个 _多步骤的demo_, 其中重用一个函数的输出作为下一个函数的输入。使用 `块` 很容易做到这一点, 因为你可以使用组件作为一个事件触发器的输入, 但作为另一个事件触发器的输出。看看下面示例中的文本组件, 它的值是语音到文本模型的结果, 但也被传递到情感分析模型: @@ -202,7 +202,7 @@ demo.launch() -### 更新组件属性 +### 更新组件属性 [[更新组件属性]] 到目前为止, 我们已经了解了如何创建事件来更新另一个组件的值。但是, 如果您想更改组件的其他属性, 例如文本框的可见性或单选按钮组中的选项, 会发生什么? 您可以通过返回组件类的 `update()` 方法而不是函数的常规返回值来做到这一点。 diff --git a/chapters/zh-CN/chapter9/8.mdx b/chapters/zh-CN/chapter9/8.mdx index 846b00250..0382eb43b 100644 --- a/chapters/zh-CN/chapter9/8.mdx +++ b/chapters/zh-CN/chapter9/8.mdx @@ -1,4 +1,4 @@ -# Gradio,回顾! +# Gradio,回顾! [[Gradio,回顾!]] 关于使用 Gradio 构建酷炫的 ML 演示的章节到此结束 - 我们希望您喜欢它!回顾一下,在本章中,我们学习了: @@ -10,7 +10,7 @@ 如果您想测试您对本章所涵盖概念的理解,请查看下一节中的测验! -## 下一步去哪里? +## 下一步去哪里? [[下一步去哪里?]] 如果您想了解有关 Gradio 的更多信息,您可以 diff --git a/chapters/zh-CN/chapter9/9.mdx b/chapters/zh-CN/chapter9/9.mdx index b3cbe287c..9027af836 100644 --- a/chapters/zh-CN/chapter9/9.mdx +++ b/chapters/zh-CN/chapter9/9.mdx @@ -2,7 +2,7 @@ -# 章末测验 +# 章末小测验 [[章末小测验]] An empty colab notebook
-接下來就是安裝我們將會用到的函式庫。我們會使用 `pip` 這個Python的資源管理工具來安裝。在Colab notebook裡,你可以用 `!` 來執行系統指令,所以你可以用以下的指令來安裝 🤗 Transformers函式庫: +接下來就是安裝我們將會用到的函式庫。我們會使用 `pip` 這個Python的資源管理工具來安裝。在Colab notebook裡,你可以用 `!` 來執行系統指令,所以你可以用以下的指令來安裝 🤗 Transformers 函式庫: ``` !pip install transformers ``` -把函式庫導入到Python runtime可以確認你的資源包有被正確地安裝: +把函式庫導入到 Python runtime 可以確認你的資源包有被正確地安裝: ``` import transformers @@ -38,7 +38,7 @@ import transformers A gif showing the result of the two commands above: installation and import -這會安裝一個非常輕量的🤗 Transformers。裡面沒有安裝任何像是PyTorch或TensorFlow等的機器學習框架。因為我們會用到很多函式庫裡的不同功能,所以我們建議安裝包含了大部分使用情境所需資源的開發用版本: +這會安裝一個非常輕量的 🤗 Transformers。裡面沒有安裝任何像是 PyTorch 或 TensorFlow 等的機器學習框架。因為我們會用到很多函式庫裡的不同功能,所以我們建議安裝包含了大部分使用情境所需資源的開發用版本: ``` @@ -50,16 +50,16 @@ import transformers ## 使用Python虛擬環境 -如果你比較想用Python虛擬環境的話,第一步就是安裝Python。我們建議跟著[這篇教學](https://realpython.com/installing-python/)做為起手式。 +如果你比較想用 Python 虛擬環境的話,第一步就是安裝 Python。我們建議跟著[這篇教學](https://realpython.com/installing-python/)做為起手式。 -當你安裝好Python後,你應該就能從終端機執行Python指令了。在進行下一步之前你可以先執行以下指令來確認Python有沒有安裝好:`python --version` 這條指令會讓終端機顯示你所安裝的Python版本。 +當你安裝好 Python 後,你應該就能從終端機執行 Python 指令了。在進行下一步之前你可以先執行以下指令來確認 Python 有沒有安裝好:`python --version` 這條指令會讓終端機顯示你所安裝的 Python 版本。 -在終端機執行像是`python --version`的Python指令時,你應該把你的指令想成是用你系統上主要的Python版本來執行。我們建議不要在這個版本上安裝任何資源包,讓每個專案在各自獨立的環境裡運行就可以了。這樣每個專案都可以有各自的相依性跟資源包,你也不用擔心不同專案之間使用同一個環境時潛在的相容性問題。 +在終端機執行像是`python --version`的 Python 指令時,你應該把你的指令想成是用你系統上主要的 Python 版本來執行。我們建議不要在這個版本上安裝任何資源包,讓每個專案在各自獨立的環境裡運行就可以了。這樣每個專案都可以有各自的相依性跟資源包,你也不用擔心不同專案之間使用同一個環境時潛在的相容性問題。 -在Python我們可以用[*虛擬環境*](https://docs.python.org/3/tutorial/venv.html)來做這件事。虛擬環境是一個獨立包裝的樹狀目錄,每一個目錄下都有安裝特定版本的Python跟它需要的所有資源包。創建這樣的虛擬環境可以用很多不同的工具,不過我們會用一個叫做[`venv`](https://docs.python.org/3/library/venv.html#module-venv)的Python官方資源包。 +在 Python 我們可以用[*虛擬環境*](https://docs.python.org/3/tutorial/venv.html)來做這件事。虛擬環境是一個獨立包裝的樹狀目錄,每一個目錄下都有安裝特定版本的Python跟它需要的所有資源包。創建這樣的虛擬環境可以用很多不同的工具,不過我們會用一個叫做[`venv`](https://docs.python.org/3/library/venv.html#module-venv)的Python官方資源包。 首先,創建你希望你的程式執行時所在的目錄 - 舉例來說,你可能想要在你的家目錄下新增一個叫*transformers-course*的目錄: @@ -103,7 +103,7 @@ which python ### 安裝相依性資源包 -在之前的段落中提到的使用Google Colab的情況裡,你會需要安裝相依性資源包才能繼續。你可以用 `pip` 這個資源管理工具來安裝開發版的🤗 Transformers: +在之前的段落中提到的使用 Google Colab 的情況裡,你會需要安裝相依性資源包才能繼續。你可以用 `pip` 這個資源管理工具來安裝開發版的🤗 Transformers: ``` pip install "transformers[sentencepiece]" diff --git a/chapters/zh-TW/chapter1/1.mdx b/chapters/zh-TW/chapter1/1.mdx new file mode 100644 index 000000000..f779a48d0 --- /dev/null +++ b/chapters/zh-TW/chapter1/1.mdx @@ -0,0 +1,57 @@ +# 本章簡介 + + + +## 歡迎來到🤗教學 + + + +本教學將使用 Hugging Face 生態系統中的庫——🤗 Transformers、🤗 Datasets、🤗 Tokenizers 和 🤗 Accelerate——以及 Hugging Face Hub 教你自然語言處理 (NLP)。它是完全免費的,並且沒有廣告。 + + +## 有什麼是值得期待的? + +以下是課程的簡要概述: + +
+Brief overview of the chapters of the course. + +
+ +- 第 1 章到第 4 章介紹了 🤗 Transformers 庫的主要概念。在本課程的這一部分結束時,您將熟悉 Transformer 模型的工作原理,並將瞭解如何使用 [Hugging Face Hub](https://huggingface.co/models) 中的模型,在數據集上對其進行微調,並在 Hub 上分享您的結果。 +- 第 5 章到第 8 章在深入研究經典 NLP 任務之前,教授 🤗 Datasets和 🤗 Tokenizers的基礎知識。在本部分結束時,您將能夠自己解決最常見的 NLP 問題。 +- 第 9 章到第 12 章更加深入,探討瞭如何使用 Transformer 模型處理語音處理和計算機視覺中的任務。在此過程中,您將學習如何構建和分享模型,並針對生產環境對其進行優化。在這部分結束時,您將準備好將🤗 Transformers 應用於(幾乎)任何機器學習問題! + +這個課程: + +* 需要良好的 Python 知識 +* 最好先學習深度學習入門課程,例如[DeepLearning.AI](https://www.deeplearning.ai/) 提供的 [fast.ai實用深度學習教程](https://course.fast.ai/) +* 不需要事先具備 [PyTorch](https://pytorch.org/) 或 [TensorFlow](https://www.tensorflow.org/) 知識,雖然熟悉其中任何一個都會對huggingface的學習有所幫助 + +完成本課程後,我們建議您查看 [DeepLearning.AI的自然語言處理系列課程](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh),其中涵蓋了廣泛的傳統 NLP 模型,如樸素貝葉斯和 LSTM,這些模型非常值得瞭解! + +## 我們是誰? + +關於作者: + +**Matthew Carrigan** 是 Hugging Face 的機器學習工程師。他住在愛爾蘭都柏林,之前在 Parse.ly 擔任機器學習工程師,在此之前,他在Trinity College Dublin擔任博士後研究員。他不相信我們會通過擴展現有架構來實現 AGI,但無論如何都對機器人充滿希望。 + +**Lysandre Debut** 是 Hugging Face 的機器學習工程師,從早期的開發階段就一直致力於 🤗 Transformers 庫。他的目標是通過使用非常簡單的 API 開發工具,讓每個人都可以使用 NLP。 + +**Sylvain Gugger** 是 Hugging Face 的一名研究工程師,也是 🤗Transformers庫的核心維護者之一。此前,他是 fast.ai 的一名研究科學家,他與Jeremy Howard 共同編寫了[Deep Learning for Coders with fastai and Py Torch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)。他的主要研究重點是通過設計和改進允許模型在有限資源上快速訓練的技術,使深度學習更容易普及。 + +**Merve Noyan** 是 Hugging Face 的開發者倡導者,致力於開發工具並圍繞它們構建內容,以使每個人的機器學習平民化。 + +**Lucile Saulnier** 是 Hugging Face 的機器學習工程師,負責開發和支持開源工具的使用。她還積極參與了自然語言處理領域的許多研究項目,例如協作訓練和 BigScience。 + +**Lewis Tunstall** 是 Hugging Face 的機器學習工程師,專注於開發開源工具並使更廣泛的社區可以使用它們。他也是即將出版的一本書[O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/)的作者之一。 + +**Leandro von Werra** 是 Hugging Face 開源團隊的機器學習工程師,也是即將出版的一本書[O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/)的作者之一。他擁有多年的行業經驗,通過在整個機器學習堆棧中工作,將 NLP 項目投入生產。 + +你準備好了嗎?在本章中,您將學習: +* 如何使用 `pipeline()` 函數解決文本生成、分類等NLP任務 +* 關於 Transformer 架構 +* 如何區分編碼器、解碼器和編碼器-解碼器架構和用例 diff --git a/chapters/zh-TW/chapter1/10.mdx b/chapters/zh-TW/chapter1/10.mdx new file mode 100644 index 000000000..cea2082cd --- /dev/null +++ b/chapters/zh-TW/chapter1/10.mdx @@ -0,0 +1,258 @@ + + +# 章末小測試 + + + +這一章涵蓋了很多內容! 如果有一些不太明白的地方,請不要擔心; 下一章將幫助你瞭解這些模塊在底層是如何運作的。 + +讓我們來測試一下你在這一章學到了什麼! + +### 1. 探索 Hub 並尋找 `roberta-large-mnli` checkpoint。 它可以完成什麼類型的任務? + + +roberta-large-mnli 頁面回顧一下." + }, + { + text: "文本分類", + explain: "更準確地說,它對兩個句子在三個標籤(矛盾、無關、相近)之間的邏輯鏈接進行分類——這項任務也稱爲自然語言推理.", + correct: true + }, + { + text: "文本生成", + explain: "點擊前往roberta-large-mnli 頁面回顧一下." + } + ]} +/> + +### 2. 下面的代碼將會返回什麼結果? + +```py +from transformers import pipeline + +ner = pipeline("ner", grouped_entities=True) +ner("My name is Sylvain and I work at Hugging Face in Brooklyn.") +``` + +sentiment-analysis pipeline將會返回這些." + }, + { + text: "它將返回一個生成的文本來完成這句話。", + explain: "這個選項是不對的 — text-generation pipeline將會返回這些.", + }, + { + text: "它將返回代表人員、組織或位置的單詞。", + explain: "此外,使用 grouped_entities=True,它會將屬於同一實體的單詞組合在一起,例如“Hugging Face”。", + correct: true + } + ]} +/> + +### 3. 在此代碼示例中...的地方應該填寫什麼? + +```py +from transformers import pipeline + +filler = pipeline("fill-mask", model="bert-base-cased") +result = filler("...") +``` + + has been waiting for you.", + explain: "這個選項是不對的。 請查看 bert-base-cased 模型卡片,然後再嘗試找找錯在哪裏。" + }, + { + text: "This [MASK] has been waiting for you.", + explain: "正解! 這個模型的mask的掩碼是[MASK].", + correct: true + }, + { + text: "This man has been waiting for you.", + explain: "這個選項是不對的。 這個pipeline的作用是填充經過mask的文字,因此它需要在輸入的文本中存在mask的token。" + } + ]} +/> + +### 4. 爲什麼這段代碼會無法運行? + +```py +from transformers import pipeline + +classifier = pipeline("zero-shot-classification") +result = classifier("This is a course about the Transformers library") +``` + +candidate_labels=[...].", + correct: true + }, + { + text: "這個pipeline需要多個句子,而不僅僅是一個。", + explain: "這個選項是不對的。儘管正確使用時,此pipeline可以同時處理多個句子(與所有其他pipeline一樣)。" + }, + { + text: "像往常一樣,🤗 Transformers 庫出故障了。", + explain: "對此,我們不予置評!" + }, + { + text: "該 pipeline 需要更長的輸入; 這個句子太短了。", + explain: "這個選項是不對的。 不過請注意,在這個 pipeline 處理時,太長的文本將被截斷。" + } + ]} +/> + +### 5. “遷移學習”是什麼意思? + + + +### 6. 語言模型在預訓練時通常不需要標籤,這樣的說法是否正確。 + + +自監督,這意味着標籤是根據輸入自動創建的(例如:預測下一個單詞或填充一些[MARSK]單詞)。", + correct: true + }, + { + text: "錯誤", + explain: "這不是一個正確的答案。" + } + ]} +/> + + +### 7. 選擇最能描述「模型(model)」、「架構(architecture)」和「權重(weights)」的句子。 + + + +### 8. 你將使用以下哪種類型的模型來根據輸入的提示生成文本? + + + +### 9. 你會使用哪些類型的模型來生成文本的摘要? + + + +### 10. 你會使用哪一種類型的模型來根據特定的標籤對文本輸入進行分類? + + + +### 11. 模型中觀察到的偏見有哪些可能的來源? + + diff --git a/chapters/zh-TW/chapter1/2.mdx b/chapters/zh-TW/chapter1/2.mdx new file mode 100644 index 000000000..e9a1f74f6 --- /dev/null +++ b/chapters/zh-TW/chapter1/2.mdx @@ -0,0 +1,25 @@ +# 自然語言處理 + + + +在進入 Transformer 模型之前,讓我們快速概述一下自然語言處理是什麼以及我們為什麼這麼重視它。 + +## 什麼是自然語言處理? + +NLP 是語言學和機器學習交叉領域,專注於理解與人類語言相關的一切。 NLP 任務的目標不僅是單獨理解單個單詞,而且是能夠理解這些單詞的上下文。 + +以下是常見 NLP 任務的列表,每個任務都有一些示例: + +- **對整個句子進行分類**: 獲取評論的情緒,檢測電子郵件是否為垃圾郵件,確定句子在語法上是否正確或兩個句子在邏輯上是否相關 +- **對句子中的每個詞進行分類**: 識別句子的語法成分(名詞、動詞、形容詞)或命名實體(人、地點、組織) +- **生成文本內容**: 用自動生成的文本完成提示,用屏蔽詞填充文本中的空白 +- **從文本中提取答案**: 給定問題和上下文,根據上下文中提供的信息提取問題的答案 +- **從輸入文本生成新句子**: 將文本翻譯成另一種語言,總結文本 + +NLP 不僅限於書面文本。它還解決了語音識別和計算機視覺中的複雜挑戰,例如生成音頻樣本的轉錄或圖像描述。 +## 為什麼具有挑戰性? + +計算機處理信息的方式與人類不同。例如,當我們讀到「我餓了」這句話時,我們很容易理解它的意思。同樣,給定兩個句子,例如「我很餓」和「我很傷心」,我們可以輕鬆確定它們的相似程度。對於機器學習 (ML) 模型,此類任務更加困難。文本需要以一種使模型能夠從中學習的方式進行處理。而且由於語言很複雜,我們需要仔細考慮必須如何進行這種處理。關於如何表示文本已經做了很多研究,我們將在下一章中介紹一些方法。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter1/3.mdx b/chapters/zh-TW/chapter1/3.mdx new file mode 100644 index 000000000..31e4c8296 --- /dev/null +++ b/chapters/zh-TW/chapter1/3.mdx @@ -0,0 +1,287 @@ +# Transformers能做什麼? + + + +在本節中,我們將看看 Transformer 模型可以做什麼,並使用 🤗 Transformers 庫中的第一個工具:pipeline() 函數。 + +👀 看到那個右上角的 在Colab中打開的按鈕了嗎? 單擊它就可以打開一個包含本節所有代碼示例的 Google Colab 筆記本。 每一個有實例代碼的小節都會有它。 + +如果您想在本地運行示例,我們建議您查看準備. + + +## Transformer被應用於各個方面! +Transformer 模型用於解決各種 NLP 任務,就像上一節中提到的那樣。以下是一些使用 Hugging Face 和 Transformer 模型的公司和組織,他們也通過分享他們的模型回饋社區: + +![使用 Hugging Face 的公司](https://huggingface.co/course/static/chapter1/companies.PNG) +[🤗 Transformers 庫](https://github.com/huggingface/transformers)提供了創建和使用這些共享模型的功能。[模型中心(hub)](https://huggingface.co/models)包含數千個任何人都可以下載和使用的預訓練模型。您還可以將自己的模型上傳到 Hub! + + +⚠️ Hugging Face Hub 不限於 Transformer 模型。任何人都可以分享他們想要的任何類型的模型或數據集!創建一個 Huggingface.co 帳戶(https://huggingface.co/join)以使用所有可用功能! + + +在深入研究 Transformer 模型的底層工作原理之前,讓我們先看幾個示例,看看它們如何用於解決一些有趣的 NLP 問題。 + +## 使用pipelines + + +(這裡有一個視頻,但是國內可能打不開,譯者注) + + +🤗 Transformers 庫中最基本的對象是 **pipeline()** 函數。它將模型與其必要的預處理和後處理步驟連接起來,使我們能夠通過直接輸入任何文本並獲得最終的答案: + +```python +from transformers import pipeline + +classifier = pipeline("sentiment-analysis") +classifier("I've been waiting for a HuggingFace course my whole life.") +``` +```python out +[{'label': 'POSITIVE', 'score': 0.9598047137260437}] +``` + + +我們也可以多傳幾句! +```python +classifier( + ["I've been waiting for a HuggingFace course my whole life.", "I hate this so much!"] +) +``` +```python out +[{'label': 'POSITIVE', 'score': 0.9598047137260437}, + {'label': 'NEGATIVE', 'score': 0.9994558095932007}] +``` +默認情況下,此pipeline選擇一個特定的預訓練模型,該模型已針對英語情感分析進行了微調。創建**分類器**對象時,將下載並緩存模型。如果您重新運行該命令,則將使用緩存的模型,無需再次下載模型。 + +將一些文本傳遞到pipeline時涉及三個主要步驟: + +1. 文本被預處理為模型可以理解的格式。 +2. 預處理的輸入被傳遞給模型。 +3. 模型處理後輸出最終人類可以理解的結果。 + +目前[可用的一些pipeline](https://huggingface.co/transformers/main_classes/pipelines.html)是: + +* **特徵提取**(獲取文本的向量表示) +* **填充空缺** +* **ner**(命名實體識別) +* **問答** +* **情感分析** +* **文本摘要** +* **文本生成** +* **翻譯** +* **零樣本分類** + +讓我們來看看其中的一些吧! + +## 零樣本分類 +我們將首先處理一項非常具挑戰性的任務,我們需要對尚未標記的文本進行分類。這是實際項目中的常見場景,因為注釋文本通常很耗時並且需要領域專業知識。對於這項任務**zero-shot-classification**pipeline非常強大:它允許您直接指定用於分類的標籤,因此您不必依賴預訓練模型的標籤。下面的模型展示瞭如何使用這兩個標籤將句子分類為正面或負面——但也可以使用您喜歡的任何其他標籤集對文本進行分類。 + +```python +from transformers import pipeline + +classifier = pipeline("zero-shot-classification") +classifier( + "This is a course about the Transformers library", + candidate_labels=["education", "politics", "business"], +) +``` +```python out +{'sequence': 'This is a course about the Transformers library', + 'labels': ['education', 'business', 'politics'], + 'scores': [0.8445963859558105, 0.111976258456707, 0.043427448719739914]} +``` + +此pipeline稱為zero-shot,因為您不需要對數據上的模型進行微調即可使用它。它可以直接返回您想要的任何標籤列表的概率分數! + +✏️**快來試試吧!**使用您自己的序列和標籤,看看模型的行為。 + + +## 文本生成 +現在讓我們看看如何使用pipeline來生成一些文本。這裡的主要使用方法是您提供一個提示,模型將通過生成剩餘的文本來自動完成整段話。這類似於許多手機上的預測文本功能。文本生成涉及隨機性,因此如果您沒有得到相同的如下所示的結果,這是正常的。 + +```python +from transformers import pipeline + +generator = pipeline("text-generation") +generator("In this course, we will teach you how to") +``` +```python out +[{'generated_text': 'In this course, we will teach you how to understand and use ' + 'data flow and data interchange when handling user data. We ' + 'will be working with one or more of the most commonly used ' + 'data flows — data flows of various types, as seen by the ' + 'HTTP'}] +``` +您可以使用參數 **num_return_sequences** 控制生成多少個不同的序列,並使用參數 **max_length** 控制輸出文本的總長度。 + + +✏️**快來試試吧!**使用 num_return_sequences 和 max_length 參數生成兩個句子,每個句子 15 個單詞。 + + +## 在pipeline中使用 Hub 中的其他模型 +前面的示例使用了默認模型,但您也可以從 Hub 中選擇特定模型以在特定任務的pipeline中使用 - 例如,文本生成。轉到[模型中心(hub)](https://huggingface.co/models)並單擊左側的相應標籤將會只顯示該任務支持的模型。[例如這樣](https://huggingface.co/models?pipeline_tag=text-generation)。 + +讓我們試試 [**distilgpt2**](https://huggingface.co/distilgpt2) 模型吧!以下是如何在與以前相同的pipeline中加載它: + +```python +from transformers import pipeline + +generator = pipeline("text-generation", model="distilgpt2") +generator( + "In this course, we will teach you how to", + max_length=30, + num_return_sequences=2, +) +``` +```python out +[{'generated_text': 'In this course, we will teach you how to manipulate the world and ' + 'move your mental and physical capabilities to your advantage.'}, + {'generated_text': 'In this course, we will teach you how to become an expert and ' + 'practice realtime, and with a hands on experience on both real ' + 'time and real'}] +``` +您可以通過單擊語言標籤來篩選搜索結果,然後選擇另一種文本生成模型的模型。模型中心(hub)甚至包含支持多種語言的多語言模型。 + +通過單擊選擇模型後,您會看到有一個小組件,可讓您直接在線試用。通過這種方式,您可以在下載之前快速測試模型的功能。 + +✏️**快來試試吧!**使用標籤篩選查找另一種語言的文本生成模型。使用小組件測試並在pipeline中使用它! + + +## 推理 API +所有模型都可以使用 Inference API 直接通過瀏覽器進行測試,該 API 可在 [Hugging Face 網站](https://huggingface.co/)上找到。通過輸入自定義文本並觀察模型的輸出,您可以直接在此頁面上使用模型。 + +小組件形式的推理 API 也可作為付費產品使用,如果您的工作流程需要它,它會派上用場。有關更多詳細信息,請參閱[定價頁面](https://huggingface.co/pricing)。 + +## Mask filling +您將嘗試的下一個pipeline是 **fill-mask**。此任務的想法是填充給定文本中的空白: +```python +from transformers import pipeline + +unmasker = pipeline("fill-mask") +unmasker("This course will teach you all about models.", top_k=2) +``` +```python out +[{'sequence': 'This course will teach you all about mathematical models.', + 'score': 0.19619831442832947, + 'token': 30412, + 'token_str': ' mathematical'}, + {'sequence': 'This course will teach you all about computational models.', + 'score': 0.04052725434303284, + 'token': 38163, + 'token_str': ' computational'}] +``` +**top_k** 參數控制要顯示的結果有多少種。請注意,這裡模型填充了特殊的< **mask** >詞,它通常被稱為掩碼標記。其他掩碼填充模型可能有不同的掩碼標記,因此在探索其他模型時要驗證正確的掩碼字是什麼。檢查它的一種方法是查看小組件中使用的掩碼。 + + +✏️**快來試試吧!**在 Hub 上搜索基於 bert 的模型並在推理 API 小組件中找到它的掩碼。這個模型對上面pipeline示例中的句子預測了什麼? + + +## 命名實體識別 +命名實體識別 (NER) 是一項任務,其中模型必須找到輸入文本的哪些部分對應於諸如人員、位置或組織之類的實體。讓我們看一個例子: +```python +from transformers import pipeline + +ner = pipeline("ner", grouped_entities=True) +ner("My name is Sylvain and I work at Hugging Face in Brooklyn.") +``` +```python out +[{'entity_group': 'PER', 'score': 0.99816, 'word': 'Sylvain', 'start': 11, 'end': 18}, + {'entity_group': 'ORG', 'score': 0.97960, 'word': 'Hugging Face', 'start': 33, 'end': 45}, + {'entity_group': 'LOC', 'score': 0.99321, 'word': 'Brooklyn', 'start': 49, 'end': 57} +] +``` +在這裡,模型正確地識別出 Sylvain 是一個人 (PER),Hugging Face 是一個組織 (ORG),而布魯克林是一個位置 (LOC)。 + +我們在pipeline創建函數中傳遞選項 **grouped_entities=True** 以告訴pipeline將對應於同一實體的句子部分重新組合在一起:這裡模型正確地將「Hugging」和「Face」分組為一個組織,即使名稱由多個詞組成。事實上,正如我們即將在下一章看到的,預處理甚至會將一些單詞分成更小的部分。例如,**Sylvain** 分割為了四部分:**S、##yl、##va** 和 **##in**。在後處理步驟中,pipeline成功地重新組合了這些部分。 + + +✏️**快來試試吧!**在模型中心(hub)搜索能夠用英語進行詞性標注(通常縮寫為 POS)的模型。這個模型對上面例子中的句子預測了什麼? + + +## 問答系統 +問答pipeline使用來自給定上下文的信息回答問題: +```python +from transformers import pipeline + +question_answerer = pipeline("question-answering") +question_answerer( + question="Where do I work?", + context="My name is Sylvain and I work at Hugging Face in Brooklyn", +) +``` +```python out +{'score': 0.6385916471481323, 'start': 33, 'end': 45, 'answer': 'Hugging Face'} +klyn", +) + +``` +請注意,此pipeline通過從提供的上下文中提取信息來工作;它不會憑空生成答案。 + +## 文本摘要 +文本摘要是將文本縮減為較短文本的任務,同時保留文本中的主要(重要)信息。下面是一個例子: + +```python +from transformers import pipeline + +summarizer = pipeline("summarization") +summarizer( + """ + America has changed dramatically during recent years. Not only has the number of + graduates in traditional engineering disciplines such as mechanical, civil, + electrical, chemical, and aeronautical engineering declined, but in most of + the premier American universities engineering curricula now concentrate on + and encourage largely the study of engineering science. As a result, there + are declining offerings in engineering subjects dealing with infrastructure, + the environment, and related issues, and greater concentration on high + technology subjects, largely supporting increasingly complex scientific + developments. While the latter is important, it should not be at the expense + of more traditional engineering. + + Rapidly developing economies such as China and India, as well as other + industrial countries in Europe and Asia, continue to encourage and advance + the teaching of engineering. Both China and India, respectively, graduate + six and eight times as many traditional engineers as does the United States. + Other industrial countries at minimum maintain their output, while America + suffers an increasingly serious decline in the number of engineering graduates + and a lack of well-educated engineers. +""" +) +``` +```python out +[{'summary_text': ' America has changed dramatically during recent years . The ' + 'number of engineering graduates in the U.S. has declined in ' + 'traditional engineering disciplines such as mechanical, civil ' + ', electrical, chemical, and aeronautical engineering . Rapidly ' + 'developing economies such as China and India, as well as other ' + 'industrial countries in Europe and Asia, continue to encourage ' + 'and advance engineering .'}] +``` +與文本生成一樣,您指定結果的 **max_length** 或 **min_length**。 + +## 翻譯 +對於翻譯,如果您在任務名稱中提供語言對(例如「**translation_en_to_fr**」),則可以使用默認模型,但最簡單的方法是在[模型中心(hub)](https://huggingface.co/models)選擇要使用的模型。在這裡,我們將嘗試從法語翻譯成英語: + +```python +from transformers import pipeline + +translator = pipeline("translation", model="Helsinki-NLP/opus-mt-fr-en") +translator("Ce cours est produit par Hugging Face.") +``` +```python out +[{'translation_text': 'This course is produced by Hugging Face.'}] + +``` + +與文本生成和摘要一樣,您可以指定結果的 **max_length** 或 **min_length**。 + + + +✏️**快來試試吧!**搜索其他語言的翻譯模型,嘗試將前一句翻譯成幾種不同的語言。 + + + +到目前為止顯示的pipeline主要用於演示目的。它們是為特定任務而編程的,不能對他們進行自定義的修改。在下一章中,您將瞭解 **pipeline()** 函數內部的內容以及如何進行自定義的修改。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter1/4.mdx b/chapters/zh-TW/chapter1/4.mdx new file mode 100644 index 000000000..cb531b1b5 --- /dev/null +++ b/chapters/zh-TW/chapter1/4.mdx @@ -0,0 +1,177 @@ +# Transformers 是如何工作的? + + + +在本節中,我們將深入瞭解 Transformer 模型的架構。 + +## 一點Transformers的發展歷史 + +以下是 Transformer 模型(簡短)歷史中的一些關鍵節點: + +
+A brief chronology of Transformers models. + +
+ +[Transformer 架構](https://arxiv.org/abs/1706.03762) 於 2017 年 6 月推出。原本研究的重點是翻譯任務。隨後推出了幾個有影響力的模型,包括 + +- **2018 年 6 月**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), 第一個預訓練的 Transformer 模型,用於各種 NLP 任務並獲得極好的結果 + +- **2018 年 10 月**: [BERT](https://arxiv.org/abs/1810.04805), 另一個大型預訓練模型,該模型旨在生成更好的句子摘要(下一章將詳細介紹!) + +- **2019 年 2 月**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), GPT 的改進(並且更大)版本,由於道德問題沒有立即公開發布 + +- **2019 年 10 月**: [DistilBERT](https://arxiv.org/abs/1910.01108), BERT 的提煉版本,速度提高 60%,內存減輕 40%,但仍保留 BERT 97% 的性能 + +- **2019 年 10 月**: [BART](https://arxiv.org/abs/1910.13461) 和 [T5](https://arxiv.org/abs/1910.10683), 兩個使用與原始 Transformer 模型相同架構的大型預訓練模型(第一個這樣做) + +- **2020 年 5 月**, [GPT-3](https://arxiv.org/abs/2005.14165), GPT-2 的更大版本,無需微調即可在各種任務上表現良好(稱爲零樣本學習) + +這個列表並不全面,只是爲了突出一些不同類型的 Transformer 模型。大體上,它們可以分爲三類: + +- GPT-like (也被稱作自迴歸Transformer模型) +- BERT-like (也被稱作自動編碼Transformer模型) +- BART/T5-like (也被稱作序列到序列的 Transformer模型) + +稍後我們將更深入地探討這些分類。 + +## Transformers是語言模型 + +上面提到的所有 Transformer 模型(GPT、BERT、BART、T5 等)都被訓練爲語言模型。這意味着他們已經以無監督學習的方式接受了大量原始文本的訓練。無監督學習是一種訓練類型,其中目標是根據模型的輸入自動計算的。這意味着不需要人工來標記數據! + +這種類型的模型可以對其訓練過的語言進行統計理解,但對於特定的實際任務並不是很有用。因此,一般的預訓練模型會經歷一個稱爲*遷移學習*的過程。在此過程中,模型在給定任務上以監督方式(即使用人工註釋標籤)進行微調。 + +任務的一個例子是閱讀 *n* 個單詞的句子,預測下一個單詞。這被稱爲因果語言建模,因爲輸出取決於過去和現在的輸入。 + +
+Example of causal language modeling in which the next word from a sentence is predicted. + +
+ +另一個例子是*遮罩語言建模*,該模型預測句子中的遮住的詞。 + +
+Example of masked language modeling in which a masked word from a sentence is predicted. + +
+ +## Transformer是大模型 + +除了一些特例(如 DistilBERT)外,實現更好性能的一般策略是增加模型的大小以及預訓練的數據量。 + +
+Number of parameters of recent Transformers models +
+ +不幸的是,訓練模型,尤其是大型模型,需要大量的數據,時間和計算資源。它甚至會對環境產生影響,如下圖所示。 + +
+The carbon footprint of a large language model. + +
+ + + +Transformers是由一個團隊領導的(非常大的)模型項目,該團隊試圖減少預訓練對環境的影響,通過運行大量試驗以獲得最佳超參數。 + +想象一下,如果每次一個研究團隊、一個學生組織或一家公司想要訓練一個模型,都從頭開始訓練的。這將導致巨大的、不必要的浪費! + +這就是爲什麼共享語言模型至關重要:共享經過訓練的權重,當遇見新的需求時在預訓練的權重之上進行微調,可以降低訓練模型訓練的算力和時間消耗,降低全球的總體計算成本和碳排放。 + + +## 遷移學習 + + + +*預訓練是*訓練模型前的一個操作:隨機初始化權重,在沒有任何先驗知識的情況下開始訓練。 + +
+The pretraining of a language model is costly in both time and money. + +
+ +這種預訓練通常是在非常大量的數據上進行的。因此,它需要大量的數據,而且訓練可能需要幾周的時間。 + +另一方面,*微調*是在模型經過預訓練後完成的訓練。要執行微調,首先需要獲取一個經過預訓練的語言模型,然後使用特定於任務的數據集執行額外的訓練。等等,爲什麼不直接爲最後的任務而訓練呢?有幾個原因: + +* 預訓練模型已經在與微調數據集有一些相似之處的數據集上進行了訓練。因此,微調過程能夠利用模型在預訓練期間獲得的知識(例如,對於NLP問題,預訓練模型將對您在任務中使用的語言有某種統計規律上的理解)。 +* 由於預訓練模型已經在大量數據上進行了訓練,因此微調需要更少的數據來獲得不錯的結果。 +* 出於同樣的原因,獲得好結果所需的時間和資源要少得多 + +例如,可以利用英語的預訓練過的模型,然後在arXiv語料庫上對其進行微調,從而形成一個基於科學/研究的模型。微調只需要有限的數據量:預訓練模型獲得的知識可以“遷移”到目標任務上,因此被稱爲*遷移學習*。 + +
+The fine-tuning of a language model is cheaper than pretraining in both time and money. + +
+ +因此,微調模型具有較低的時間、數據、財務和環境成本。迭代不同的微調方案也更快、更容易,因爲與完整的預訓練相比,訓練的約束更少。 + +這個過程也會比從頭開始的訓練(除非你有很多數據)取得更好的效果,這就是爲什麼你應該總是嘗試利用一個預訓練的模型--一個儘可能接近你手頭的任務的模型--並對其進行微調。 + +## 一般的體系結構 +在這一部分,我們將介紹Transformer模型的一般架構。如果你不理解其中的一些概念,不要擔心;下文將詳細介紹每個組件。 + + + +## 介紹 + +該模型主要由兩個塊組成: + +* **Encoder (左側)**: 編碼器接收輸入並構建其表示(其特徵)。這意味着對模型進行了優化,以從輸入中獲得理解。 +* **Decoder (右側)**: 解碼器使用編碼器的表示(特徵)以及其他輸入來生成目標序列。這意味着該模型已針對生成輸出進行了優化。 + +
+Architecture of a Transformers models + +
+ +這些部件中的每一個都可以獨立使用,具體取決於任務: + +* **Encoder-only models**: 適用於需要理解輸入的任務,如句子分類和命名實體識別。 +* **Decoder-only models**: 適用於生成任務,如文本生成。 +* **Encoder-decoder models** 或者 **sequence-to-sequence models**: 適用於需要根據輸入進行生成的任務,如翻譯或摘要。 + +在後面的部分中,我們將單獨地深入研究這些體系結構。 + +## 注意力層 + +Transformer模型的一個關鍵特性是*注意力層*。事實上,介紹Transformer架構的文章的標題是[“注意力就是你所需要的”](https://arxiv.org/abs/1706.03762)! 我們將在課程的後面更加深入地探索注意力層;現在,您需要知道的是,這一層將告訴模型在處理每個單詞的表示時,要特別重視您傳遞給它的句子中的某些單詞(並且或多或少地忽略其他單詞)。 + +把它放在語境中,考慮將文本從英語翻譯成法語的任務。在輸入“You like this course”的情況下,翻譯模型還需要注意相鄰的單詞“You”,以獲得單詞“like”的正確翻譯,因爲在法語中,動詞“like”的變化取決於主題。然而,句子的其餘部分對於該詞的翻譯沒有用處。同樣,在翻譯“this”時,模型也需要注意“course”一詞,因爲“this”的翻譯不同,取決於相關名詞是單數還是複數。同樣,句子中的其他單詞對於“this”的翻譯也不重要。對於更復雜的句子(以及更復雜的語法規則),模型需要特別注意可能出現在句子中更遠位置的單詞,以便正確地翻譯每個單詞。 + +同樣的概念也適用於與自然語言相關的任何任務:一個詞本身有一個含義,但這個含義受語境的影響很大,語境可以是研究該詞之前或之後的任何其他詞(或多個詞)。 + +現在您已經瞭解了注意力層的含義,讓我們更仔細地瞭解Transformer架構。 + +## 原始的結構 + +Transformer架構最初是爲翻譯而設計的。在訓練期間,編碼器接收特定語言的輸入(句子),而解碼器需要輸出對應語言的翻譯。在編碼器中,注意力層可以使用一個句子中的所有單詞(正如我們剛纔看到的,給定單詞的翻譯可以取決於它在句子中的其他單詞)。然而,解碼器是按順序工作的,並且只能注意它已經翻譯過的句子中的單詞。例如,當我們預測了翻譯目標的前三個單詞時,我們將它們提供給解碼器,然後解碼器使用編碼器的所有輸入來嘗試預測第四個單詞。 + +爲了在訓練過程中加快速度(當模型可以訪問目標句子時),解碼器會被輸入整個目標,但不允許獲取到要翻譯的單詞(如果它在嘗試預測位置2的單詞時可以訪問位置2的單詞,解碼器就會偷懶,直接輸出那個單詞,從而無法學習到正確的語言關係!)。例如,當試圖預測第4個單詞時,注意力層只能獲取位置1到3的單詞。 + +最初的Transformer架構如下所示,編碼器位於左側,解碼器位於右側: + +
+Architecture of a Transformers models + +
+ +注意,解碼器塊中的第一個注意力層關聯到解碼器的所有(過去的)輸入,但是第二注意力層使用編碼器的輸出。因此,它可以訪問整個輸入句子,以最好地預測當前單詞。這是非常有用的,因爲不同的語言可以有語法規則將單詞按不同的順序排列,或者句子後面提供的一些上下文可能有助於確定給定單詞的最佳翻譯。 + +也可以在編碼器/解碼器中使用*注意力遮罩層*,以防止模型注意某些特殊單詞。例如,在批處理句子時,填充特殊詞使所有句子的長度一致。 + +## 架構與參數 + +在本課程中,當我們深入探討Transformers模型時,您將看到 +架構、參數和模型 +。 這些術語的含義略有不同: + +* **架構**: 這是模型的骨架 -- 每個層的定義以及模型中發生的每個操作。 +* **Checkpoints**: 這些是將在給架構中結構中加載的權重。 +* **模型**: 這是一個籠統的術語,沒有“架構”或“參數”那麼精確:它可以指兩者。爲了避免歧義,本課程使用將使用架構和參數。 + +例如,BERT是一個架構,而 `bert-base-cased`, 這是谷歌團隊爲BERT的第一個版本訓練的一組權重參數,是一個參數。我們可以說“BERT模型”和"`bert-base-cased`模型." diff --git a/chapters/zh-TW/chapter1/5.mdx b/chapters/zh-TW/chapter1/5.mdx new file mode 100644 index 000000000..acd28f78c --- /dev/null +++ b/chapters/zh-TW/chapter1/5.mdx @@ -0,0 +1,22 @@ +# “編碼器”模型 + + + + + +“編碼器”模型指僅使用編碼器的Transformer模型。在每個階段,注意力層都可以獲取初始句子中的所有單詞。這些模型通常具有“雙向”注意力,被稱爲自編碼模型。 + +這些模型的預訓練通常圍繞着以某種方式破壞給定的句子(例如:通過隨機遮蓋其中的單詞),並讓模型尋找或重建給定的句子。 + +“編碼器”模型最適合於需要理解完整句子的任務,例如:句子分類、命名實體識別(以及更普遍的單詞分類)和閱讀理解後回答問題。 + +該系列模型的典型代表有: + +- [ALBERT](https://huggingface.co/transformers/model_doc/albert.html) +- [BERT](https://huggingface.co/transformers/model_doc/bert.html) +- [DistilBERT](https://huggingface.co/transformers/model_doc/distilbert.html) +- [ELECTRA](https://huggingface.co/transformers/model_doc/electra.html) +- [RoBERTa](https://huggingface.co/transformers/model_doc/roberta.html) diff --git a/chapters/zh-TW/chapter1/6.mdx b/chapters/zh-TW/chapter1/6.mdx new file mode 100644 index 000000000..997f206bd --- /dev/null +++ b/chapters/zh-TW/chapter1/6.mdx @@ -0,0 +1,22 @@ +# 「解碼器」模型 + + + + + +「解碼器」模型通常指僅使用解碼器的 Transformer 模型。在每個階段,對於給定的單詞,注意力層只能獲取到句子中位於將要預測單詞前面的單詞。這些模型通常被稱爲自迴歸模型。 + +「解碼器」模型的預訓練通常圍繞預測句子中的下一個單詞進行。 + +這些模型最適合於涉及文本生成的任務。 + +該系列模型的典型代表有: + + +- [CTRL](https://huggingface.co/transformers/model_doc/ctrl.html) +- [GPT](https://huggingface.co/docs/transformers/model_doc/openai-gpt) +- [GPT-2](https://huggingface.co/transformers/model_doc/gpt2.html) +- [Transformer XL](https://huggingface.co/transformers/model_doc/transformerxl.html) diff --git a/chapters/zh-TW/chapter1/7.mdx b/chapters/zh-TW/chapter1/7.mdx new file mode 100644 index 000000000..1566714fa --- /dev/null +++ b/chapters/zh-TW/chapter1/7.mdx @@ -0,0 +1,21 @@ +# 序列到序列模型 + + + + + +編碼器-解碼器模型(也稱爲序列到序列模型)同時使用 Transformer 架構的編碼器和解碼器兩個部分。在每個階段,編碼器的注意力層可以訪問初始句子中的所有單詞,而解碼器的注意力層只能訪問位於輸入中將要預測單詞前面的單詞。 + +這些模型的預訓練可以使用訓練編碼器或解碼器模型的方式來完成,但通常涉及更復雜的內容。例如,[T5](https://huggingface.co/t5-base)通過將文本的隨機跨度(可以包含多個單詞)替換爲單個特殊單詞來進行預訓練,然後目標是預測該掩碼單詞替換的文本。 + +序列到序列模型最適合於圍繞根據給定輸入生成新句子的任務,如摘要、翻譯或生成性問答。 + +該系列模型的典型代表有: + +- [BART](https://huggingface.co/transformers/model_doc/bart.html) +- [mBART](https://huggingface.co/transformers/model_doc/mbart.html) +- [Marian](https://huggingface.co/transformers/model_doc/marian.html) +- [T5](https://huggingface.co/transformers/model_doc/t5.html) diff --git a/chapters/zh-TW/chapter1/8.mdx b/chapters/zh-TW/chapter1/8.mdx new file mode 100644 index 000000000..e6c3eeca5 --- /dev/null +++ b/chapters/zh-TW/chapter1/8.mdx @@ -0,0 +1,31 @@ +# Bias and limitations + + + +如果您打算在正式的項目中使用經過預訓練或經過微調的模型。請注意:雖然這些模型是很強大,但它們也有侷限性。其中最大的一個問題是,爲了對大量數據進行預訓練,研究人員通常會蒐集所有他們能找到的內容,中間可能夾帶一些意識形態或者價值觀的刻板印象。 + +爲了快速解釋清楚這個問題,讓我們回到一個使用 BERT 模型的 pipeline 的例子: + +```python +from transformers import pipeline + +unmasker = pipeline("fill-mask", model="bert-base-uncased") +result = unmasker("This man works as a [MASK].") +print([r["token_str"] for r in result]) + +result = unmasker("This woman works as a [MASK].") +print([r["token_str"] for r in result]) +``` + +```python out +['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic'] +['nurse', 'waitress', 'teacher', 'maid', 'prostitute'] +``` +當要求模型填寫這兩句話中缺少的單詞時,模型給出的答案中,只有一個與性別無關(服務生/女服務生)。其他職業通常與某一特定性別相關,妓女最終進入了模型中與「女人」和「工作」相關的前五位。儘管 BERT 是使用經過篩選和清洗後,明顯中立的數據集上建立的的 Transformer 模型,而不是通過從互聯網上搜集數據(它是在[Wikipedia 英文](https://huggingface.co/datasets/wikipedia)和[BookCorpus](https://huggingface.co/datasets/bookcorpus)數據集)。 + +因此,當您使用這些工具時,您需要記住,使用的原始模型的時候,很容易生成性別歧視、種族主義或恐同內容。這種固有偏見不會隨着微調模型而使消失。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter1/9.mdx b/chapters/zh-TW/chapter1/9.mdx new file mode 100644 index 000000000..d24ba6139 --- /dev/null +++ b/chapters/zh-TW/chapter1/9.mdx @@ -0,0 +1,16 @@ +# 總結 + + + +在本章中,您瞭解瞭如何使用來自🤗Transformers 的函數 pipeline() 處理不同的 NLP 任務。您還了解了如何在模型中心(hub)中搜索和使用模型,以及如何使用推理API直接在瀏覽器中測試模型。 + +我們討論了Transformer模型如何在應用層上工作,並討論了遷移學習和微調的重要性。您可以使用完整的體系結構,也可以僅使用編碼器或解碼器,具體取決於您要解決的任務類型。下表總結了這一點: + +| 模型 | 示例 | 任務| +| ---- | ---- |----| +| 編碼器 | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa |句子分類、命名實體識別、從文本中提取答案| +| 解碼器 | CTRL, GPT, GPT-2, Transformer XL |文本生成| +| 編碼器-解碼器 | BART, T5, Marian, mBART |文本摘要、翻譯、生成問題的回答| \ No newline at end of file diff --git a/chapters/zh-TW/chapter2/1.mdx b/chapters/zh-TW/chapter2/1.mdx new file mode 100644 index 000000000..70ce241f2 --- /dev/null +++ b/chapters/zh-TW/chapter2/1.mdx @@ -0,0 +1,23 @@ +# 本章簡介 + + + +正如你在 [Chapter 1](/course/chapter1),中看到的那樣,Transformers 模型通常非常大。對於數以百萬計到數千萬計數十億的參數,訓練和部署這些模型是一項複雜的任務。此外,由於幾乎每天都在發佈新模型,而且每種模型都有自己的實現,因此嘗試它們絕非易事。 + +創建 🤗Transformers 庫就是為了解決這個問題。它的目標是提供一個API,通過它可以加載、訓練和保存任何Transformer模型。這個庫的主要特點是: +- **易於使用**:下載、加載和使用最先進的NLP模型進行推理只需兩行代碼即可完成。 +- **靈活**:所有型號的核心都是簡單的 PyTorch **nn.Module** 或者 TensorFlow **tf.kears.Model**,可以像它們各自的機器學習(ML)框架中的任何其他模型一樣進行處理。 +- **簡單**:當前位置整個庫幾乎沒有任何摘要。“都在一個文件中”是一個核心概念:模型的正向傳遞完全定義在一個文件中,因此代碼本身是可以理解的,並且是可以破解的。 + +最後一個特性使🤗 Transformers與其他ML庫截然不同。這些模型不是基於通過文件共享的模塊構建的;相反,每一個模型都有自己的菜單。除了使模型更加容易接受和更容易理解,這還允許你輕鬆地在一個模型上實驗,而且不影響其他模型。 + +本章將從一個端到端的示例開始,在該示例中,我們一起使用模型和tokenizer分詞器來複制[Chapter 1](/course/chapter1)中引入的函數 pipeline(). 接下來,我們將討論模型API:我們將深入研究模型和配置類,並向您展示如何加載模型以及如何將數值輸入處理為輸出預測。 + +然後我們來看看標記器API,它是 pipeline() 函數的另一個主要組件。它是作用分詞器負責第一個和最後一個處理步驟,處理從文本到神經網絡數字輸入的轉換,以及在需要時轉換回文本。最後,我們將向您展示如何處理在一個準備好的批處理中通過一個模型發送多個句子的問題,然後詳細介紹 pipeline() 函數。 + + +⚠️ 為了從模型集線器和 🤗Transformers 的所有可用功能中獲益,我們建議creating an account. + \ No newline at end of file diff --git a/chapters/zh-TW/chapter2/2.mdx b/chapters/zh-TW/chapter2/2.mdx new file mode 100644 index 000000000..fcacec271 --- /dev/null +++ b/chapters/zh-TW/chapter2/2.mdx @@ -0,0 +1,359 @@ + + +# 管道的內部 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + + +這是第一部分,根據您使用 PyTorch 或者 TensorFlow,內容略有不同。點擊標題上方的平臺,選一個您喜歡的吧! + + +{#if fw === 'pt'} + +{:else} + +{/if} + +讓我們從一個完整的示例開始,看看在[Chapter 1](/course/chapter1)中執行以下代碼時在幕後發生了什麼 + +```python +from transformers import pipeline + +classifier = pipeline("sentiment-analysis") +classifier( + [ + "I've been waiting for a HuggingFace course my whole life.", + "I hate this so much!", + ] +) +``` + +獲得: + +```python out +[{'label': 'POSITIVE', 'score': 0.9598047137260437}, + {'label': 'NEGATIVE', 'score': 0.9994558095932007}] +``` + +正如我們在[Chapter 1](/course/chapter1)中看到的,此管道將三個步驟組合在一起:預處理、通過模型傳遞輸入和後處理: + +
+The full NLP pipeline: tokenization of text, conversion to IDs, and inference through the Transformer model and the model head. + +
+ +讓我們快速瀏覽一下這些內容。 + +## 使用分詞器進行預處理 + +與其他神經網絡一樣,Transformer 模型無法直接處理原始文本, 因此我們管道的第一步是將文本輸入轉換為模型能夠理解的數字。 為此,我們使用*tokenizer*(標記器),負責: + +- 將輸入拆分為單詞、子單詞或符號(如標點符號),稱為標記(*token*) +- 將每個標記(token)映射到一個整數 +- 添加可能對模型有用的其他輸入 + +所有這些預處理都需要以與模型預訓練時完全相同的方式完成,因此我們首先需要從[Model Hub](https://huggingface.co/models)中下載這些信息。為此,我們使用`AutoTokenizer`類及其`from_pretrained()`方法。使用我們模型的檢查點名稱,它將自動獲取與模型的標記器相關聯的數據,並對其進行緩存(因此只有在您第一次運行下面的代碼時才會下載)。 + +因為`sentiment-analysis`(情緒分析)管道的默認檢查點是`distilbert-base-uncased-finetuned-sst-2-english`(你可以看到它的模型卡[here](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)),我們運行以下程序: + +```python +from transformers import AutoTokenizer + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +``` + +一旦我們有了標記器,我們就可以直接將我們的句子傳遞給它,然後我們就會得到一本字典,它可以提供給我們的模型!剩下要做的唯一一件事就是將輸入ID列表轉換為張量。 + +您可以使用🤗 Transformers,而不必擔心哪個 ML 框架被用作後端;它可能是 PyTorch 或 TensorFlow,或 Flax。但是,Transformers型號只接受*張量*作為輸入。如果這是你第一次聽說張量,你可以把它們想象成NumPy數組。NumPy數組可以是標量(0D)、向量(1D)、矩陣(2D)或具有更多維度。它實際上是張量;其他 ML 框架的張量行為類似,通常與 NumPy 數組一樣易於實例化。 + +要指定要返回的張量類型(PyTorch、TensorFlow 或 plain NumPy),我們使用`return_tensors`參數: + +{#if fw === 'pt'} +```python +raw_inputs = [ + "I've been waiting for a HuggingFace course my whole life.", + "I hate this so much!", +] +inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt") +print(inputs) +``` +{:else} +```python +raw_inputs = [ + "I've been waiting for a HuggingFace course my whole life.", + "I hate this so much!", +] +inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="tf") +print(inputs) +``` +{/if} + +現在不要擔心填充和截斷;我們稍後會解釋這些。這裡要記住的主要事情是,您可以傳遞一個句子或一組句子,還可以指定要返回的張量類型(如果沒有傳遞類型,您將得到一組列表)。 + +{#if fw === 'pt'} + +以下是PyTorch張量的結果: + +```python out +{ + 'input_ids': tensor([ + [ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102], + [ 101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0] + ]), + 'attention_mask': tensor([ + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ]) +} +``` +{:else} + +以下是 TensorFlow 張量的結果: + +```python out +{ + 'input_ids': , + 'attention_mask': +} +``` +{/if} + +輸出本身是一個包含兩個鍵的字典,`input_ids`和`attention_mask`。`input_ids`包含兩行整數(每個句子一行),它們是每個句子中標記的唯一標記(token)。我們將在本章後面解釋什麼是`attention_mask`。 + +## 瀏覽模型 + +{#if fw === 'pt'} +我們可以像使用標記器一樣下載預訓練模型。🤗 Transformers提供了一個`AutoModel`類,該類還具有`from_pretrained()`方法: + +```python +from transformers import AutoModel + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +model = AutoModel.from_pretrained(checkpoint) +``` +{:else} +我們可以像使用標記器一樣下載預訓練模型。🤗 Transformers提供了一個`TFAutoModel`類,該類還具有`from_pretrained()`方法: + +```python +from transformers import TFAutoModel + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +model = TFAutoModel.from_pretrained(checkpoint) +``` +{/if} + +在這個代碼片段中,我們下載了之前在管道中使用的相同檢查點(它實際上應該已經被緩存),並用它實例化了一個模型。 + +這個架構只包含基本轉換器模塊:給定一些輸入,它輸出我們將調用的內容*隱藏狀態(hidden states)*,亦稱*特徵(features)*。對於每個模型輸入,我們將檢索一個高維向量,表示**Transformer模型對該輸入的上下文理解**。 + +如果這不合理,不要擔心。我們以後再解釋。 + +雖然這些隱藏狀態本身可能很有用,但它們通常是模型另一部分(稱為*頭部(head)*)的輸入。 在[Chapter 1](/course/chapter1)中,可以使用相同的體系結構執行不同的任務,但這些任務中的每個任務都有一個與之關聯的不同頭。 + +### 高維向量? + +Transformers 模塊的向量輸出通常較大。它通常有三個維度: + +- **Batch size**: 一次處理的序列數(在我們的示例中為2)。 +- **Sequence length**: 序列的數值表示的長度(在我們的示例中為16)。 +- **Hidden size**: 每個模型輸入的向量維度。 + +由於最後一個值,它被稱為「高維」。隱藏的大小可能非常大(768通常用於較小的型號,而在較大的型號中,這可能達到3072或更大)。 + +如果我們將預處理的輸入輸入到模型中,我們可以看到這一點: + +{#if fw === 'pt'} +```python +outputs = model(**inputs) +print(outputs.last_hidden_state.shape) +``` + +```python out +torch.Size([2, 16, 768]) +``` +{:else} +```py +outputs = model(inputs) +print(outputs.last_hidden_state.shape) +``` + +```python out +(2, 16, 768) +``` +{/if} + +注意🤗 Transformers 模型的輸出與`namedtuple`或詞典相似。您可以通過屬性(就像我們所做的那樣)或鍵(`輸出["last_hidden_state"]`)訪問元素,甚至可以通過索引訪問元素,前提是您確切知道要查找的內容在哪裡(`outputs[0]`)。 + +### 模型頭:數字的意義 + +模型頭將隱藏狀態的高維向量作為輸入,並將其投影到不同的維度。它們通常由一個或幾個線性層組成: + + +
+A Transformer network alongside its head. + +
+ +Transformers 模型的輸出直接發送到模型頭進行處理。 + +在此圖中,模型由其嵌入層和後續層表示。嵌入層將標記化輸入中的每個輸入ID轉換為表示關聯標記(token)的向量。後續層使用注意機制操縱這些向量,以生成句子的最終表示。 + + +🤗 Transformers中有許多不同的體系結構,每種體系結構都是圍繞處理特定任務而設計的。以下是一個非詳盡的列表: + +- `*Model` (retrieve the hidden states) +- `*ForCausalLM` +- `*ForMaskedLM` +- `*ForMultipleChoice` +- `*ForQuestionAnswering` +- `*ForSequenceClassification` +- `*ForTokenClassification` +- 以及其他 🤗 + +{#if fw === 'pt'} +對於我們的示例,我們需要一個帶有序列分類頭的模型(能夠將句子分類為肯定或否定)。因此,我們實際上不會使用`AutoModel`類,而是使用`AutoModelForSequenceClassification`: + +```python +from transformers import AutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) +outputs = model(**inputs) +``` +{:else} +For our example, we will need a model with a sequence classification head (to be able to classify the sentences as positive or negative). So, we won't actually use the `TFAutoModel` class, but `TFAutoModelForSequenceClassification`: + +```python +from transformers import TFAutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) +outputs = model(inputs) +``` +{/if} + +現在,如果我們觀察輸入的形狀,維度將低得多:模型頭將我們之前看到的高維向量作為輸入,並輸出包含兩個值的向量(每個標籤一個): + +```python +print(outputs.logits.shape) +``` + +{#if fw === 'pt'} + +```python out +torch.Size([2, 2]) +``` + +{:else} + +```python out +(2, 2) +``` + +{/if} + +因為我們只有兩個句子和兩個標籤,所以我們從模型中得到的結果是2 x 2的形狀。 + +## 對輸出進行後處理 + +我們從模型中得到的輸出值本身並不一定有意義。我們來看看, + +```python +print(outputs.logits) +``` + +{#if fw === 'pt'} +```python out +tensor([[-1.5607, 1.6123], + [ 4.1692, -3.3464]], grad_fn=) +``` +{:else} +```python out + +``` +{/if} + +我們的模型預測第一句為`[-1.5607, 1.6123]`,第二句為`[ 4.1692, -3.3464]`。這些不是概率,而是*logits*,即模型最後一層輸出的原始非標準化分數。要轉換為概率,它們需要經過[SoftMax](https://en.wikipedia.org/wiki/Softmax_function)層(所有🤗Transformers模型輸出logits,因為用於訓練的損耗函數通常會將最後的激活函數(如SoftMax)與實際損耗函數(如交叉熵)融合): + +{#if fw === 'pt'} +```py +import torch + +predictions = torch.nn.functional.softmax(outputs.logits, dim=-1) +print(predictions) +``` +{:else} +```py +import tensorflow as tf + +predictions = tf.math.softmax(outputs.logits, axis=-1) +print(predictions) +``` +{/if} + +{#if fw === 'pt'} +```python out +tensor([[4.0195e-02, 9.5980e-01], + [9.9946e-01, 5.4418e-04]], grad_fn=) +``` +{:else} +```python out +tf.Tensor( +[[4.01951671e-02 9.59804833e-01] + [9.9945587e-01 5.4418424e-04]], shape=(2, 2), dtype=float32) +``` +{/if} + +現在我們可以看到,模型預測第一句為`[0.0402, 0.9598]`,第二句為`[0.9995, 0.0005]`。這些是可識別的概率分數。 + +為了獲得每個位置對應的標籤,我們可以檢查模型配置的`id2label`屬性(下一節將對此進行詳細介紹): + +```python +model.config.id2label +``` + +```python out +{0: 'NEGATIVE', 1: 'POSITIVE'} +``` + +現在我們可以得出結論,該模型預測了以下幾點: + +- 第一句:否定:0.0402,肯定:0.9598 +- 第二句:否定:0.9995,肯定:0.0005 + +我們已經成功地複製了管道的三個步驟:使用標記化器進行預處理、通過模型傳遞輸入以及後處理!現在,讓我們花一些時間深入瞭解這些步驟中的每一步。 + + + +✏️ **試試看!** 選擇兩個(或更多)你自己的文本並在管道中運行它們。然後自己複製在這裡看到的步驟,並檢查是否獲得相同的結果! + + diff --git a/chapters/zh-TW/chapter2/3.mdx b/chapters/zh-TW/chapter2/3.mdx new file mode 100644 index 000000000..b93ad440e --- /dev/null +++ b/chapters/zh-TW/chapter2/3.mdx @@ -0,0 +1,264 @@ + + +# 模型 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +{#if fw === 'pt'} + +{:else} + +{/if} + +{#if fw === 'pt'} +在本節中,我們將更詳細地瞭解如何創建和使用模型。我們將使用 +AutoModel類,當您希望從檢查點實例化任何模型時,這非常方便。 + +這個AutoModel類及其所有相關項實際上是對庫中各種可用模型的簡單包裝。它是一個聰明的包裝器,因為它可以自動猜測檢查點的適當模型體系結構,然後用該體系結構實例化模型。 + +{:else} +在本節中,我們將更詳細地瞭解如何創建和使用模型。我們將使用 +AutoModel類,當您希望從檢查點實例化任何模型時,這非常方便。 + +這個AutoModel類及其所有相關項實際上是對庫中各種可用模型的簡單包裝。它是一個聰明的包裝器,因為它可以自動猜測檢查點的適當模型體系結構,然後用該體系結構實例化模型。 + +{/if} + +但是,如果您知道要使用的模型類型,則可以使用直接定義其體系結構的類。讓我們看看這是如何與BERT模型一起工作的。 + +## 創建轉換器 + +初始化BERT模型需要做的第一件事是加載配置對象: + +{#if fw === 'pt'} +```py +from transformers import BertConfig, BertModel + +# Building the config +config = BertConfig() + +# Building the model from the config +model = BertModel(config) +``` +{:else} +```py +from transformers import BertConfig, TFBertModel + +# Building the config +config = BertConfig() + +# Building the model from the config +model = TFBertModel(config) +``` +{/if} + +配置包含許多用於構建模型的屬性: + +```py +print(config) +``` + +```python out +BertConfig { + [...] + "hidden_size": 768, + "intermediate_size": 3072, + "max_position_embeddings": 512, + "num_attention_heads": 12, + "num_hidden_layers": 12, + [...] +} +``` + +雖然您還沒有看到所有這些屬性都做了什麼,但您應該認識到其中的一些屬性:hidden_size屬性定義了hidden_狀態向量的大小,num_hidden_layers定義了Transformer模型的層數。 + +### 不同的加載方式 + +從默認配置創建模型會使用隨機值對其進行初始化: + + +{#if fw === 'pt'} +```py +from transformers import BertConfig, BertModel + +config = BertConfig() +model = BertModel(config) + +# Model is randomly initialized! +``` +{:else} +```py +from transformers import BertConfig, TFBertModel + +config = BertConfig() +model = TFBertModel(config) + +# Model is randomly initialized! +``` +{/if} + + +該模型可以在這種狀態下使用,但會輸出胡言亂語;首先需要對其進行訓練。我們可以根據手頭的任務從頭開始訓練模型,但正如您在 +[Chapter 1](/course/chapter1) +,這將需要很長的時間和大量的數據,並將產生不可忽視的環境影響。為了避免不必要的重複工作,必須能夠共享和重用已經訓練過的模型。 + + +加載已經訓練過的Transformers模型很簡單-我們可以使用from_pretrained() +方法: + +{#if fw === 'pt'} +```py +from transformers import BertModel + +model = BertModel.from_pretrained("bert-base-cased") +``` + +正如您之前看到的,我們可以用等效的AutoModel類替換Bert模型。從現在開始,我們將這樣做,因為這會產生檢查點不可知的代碼;如果您的代碼適用於一個檢查點,那麼它應該與另一個檢查點無縫地工作。即使體系結構不同,這也適用,只要檢查點是針對類似任務(例如,情緒分析任務)訓練的。 + +{:else} +```py +from transformers import TFBertModel + +model = TFBertModel.from_pretrained("bert-base-cased") +``` + +正如您之前看到的,我們可以用等效的AutoModel類替換Bert模型。從現在開始,我們將這樣做,因為這會產生檢查點不可知的代碼;如果您的代碼適用於一個檢查點,那麼它應該與另一個檢查點無縫地工作。即使體系結構不同,這也適用,只要檢查點是針對類似任務(例如,情緒分析任務)訓練的。 + +{/if} + +在上面的代碼示例中,我們沒有使用BertConfig + +,而是通過Bert base cased標識符加載了一個預訓練模型。這是一個模型檢查點,由BERT的作者自己訓練;您可以在 +[model card](https://huggingface.co/bert-base-cased)中找到更多細節. + + + +該模型現在使用檢查點的所有權重進行初始化。它可以直接用於對訓練過的任務進行推理,也可以對新任務進行微調。通過預先訓練重量而不是從頭開始的訓練,我們可以很快取得好的效果。 + + + +權重已下載並緩存在緩存文件夾中(因此將來對from_pretrained()方法的調用將不會重新下載它們)默認為 +~/.cache/huggingface/transformers +. 您可以通過設置 +HF_HOME +環境變量來自定義緩存文件夾。 + + + +用於加載模型的標識符可以是模型中心Hub上任何模型的標識符,只要它與BERT體系結構兼容。可以找到可用的BERT檢查點的完整列表 +[here](https://huggingface.co/models?filter=bert) +. +### 保存模型 + +保存模型和加載模型一樣簡單--我們使用 +save_pretrained() +方法,類似於 +from_pretrained() +方法: + +```py +model.save_pretrained("directory_on_my_computer") +``` + +這會將兩個文件保存到磁盤: + +{#if fw === 'pt'} +``` +ls directory_on_my_computer + +config.json pytorch_model.bin +``` +{:else} +``` +ls directory_on_my_computer + +config.json tf_model.h5 +``` +{/if} + +如果你看一下 +config.json +文件,您將識別構建模型體系結構所需的屬性。該文件還包含一些元數據,例如檢查點的來源以及上次保存檢查點時使用的🤗 Transformers版本。 + +{#if fw === 'pt'} +這個 *pytorch_model.bin* 文件就是眾所周知的*state dictionary*; 它包含模型的所有權重。這兩個文件齊頭並進;配置是瞭解模型體系結構所必需的,而模型權重是模型的參數。 + +{:else} +這個 *pytorch_model.bin* 文件就是眾所周知的*state dictionary*; 它包含模型的所有權重。這兩個文件齊頭並進;配置是瞭解模型體系結構所必需的,而模型權重是模型的參數。 + +{/if} + +### 使用Transformers模型進行推理 + +既然您知道了如何加載和保存模型,那麼讓我們嘗試使用它進行一些預測。Transformer模型只能處理數字——分詞器生成的數字。但在我們討論標記化器之前,讓我們先探討模型接受哪些輸入。 + +標記化器可以將輸入轉換為適當的框架張量,但為了幫助您瞭解發生了什麼,我們將快速瞭解在將輸入發送到模型之前必須做什麼。 + +假設我們有幾個序列: + +```py +sequences = ["Hello!", "Cool.", "Nice!"] +``` + +分詞器將這些轉換為詞彙表索引,通常稱為 +input IDs +. 每個序列現在都是一個數字列表!結果是: + +```py no-format +encoded_sequences = [ + [101, 7592, 999, 102], + [101, 4658, 1012, 102], + [101, 3835, 999, 102], +] +``` + +這是一個編碼序列列表:一個列表列表。張量只接受矩形(想想矩陣)。此“數組”已為矩形,因此將其轉換為張量很容易: + +{#if fw === 'pt'} +```py +import torch + +model_inputs = torch.tensor(encoded_sequences) +``` +{:else} +```py +import tensorflow as tf + +model_inputs = tf.constant(encoded_sequences) +``` +{/if} + +### 使用張量作為模型的輸入 + + + +在模型中使用張量非常簡單-我們只需將輸入稱為模型: + + +```python +output = model(model_inputs) +``` + + + +雖然模型接受許多不同的參數,但只需要 +input IDs。我們稍後將解釋其他參數的作用以及何時需要它們,但首先我們需要更仔細地瞭解 +Transformer模型可以理解的輸入的標記 diff --git a/chapters/zh-TW/chapter2/4.mdx b/chapters/zh-TW/chapter2/4.mdx new file mode 100644 index 000000000..93bb26e65 --- /dev/null +++ b/chapters/zh-TW/chapter2/4.mdx @@ -0,0 +1,239 @@ + + +# 標記器(Tokenizer) + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + + + +標記器(Tokenizer)是 NLP 管道的核心組件之一。它們有一個目的:將文本轉換為模型可以處理的數據。模型只能處理數字,因此標記器(Tokenizer)需要將我們的文本輸入轉換為數字數據。在本節中,我們將確切地探討標記化管道中發生的事情。 + +在 NLP 任務中,通常處理的數據是原始文本。這是此類文本的示例 + +``` +Jim Henson was a puppeteer +``` + +但是,模型只能處理數字,因此我們需要找到一種將原始文本轉換為數字的方法。這就是標記器(tokenizer)所做的,並且有很多方法可以解決這個問題。目標是找到最有意義的表示——即對模型最有意義的表示——並且如果可能的話,找到最小的表示。 + +讓我們看一下標記化算法的一些示例,並嘗試回答您可能對標記化提出的一些問題。 + +## 基於詞的(Word-based) + + + +想到的第一種標記器是基於詞的(_word-based_).它通常很容易設置和使用,只需幾條規則,並且通常會產生不錯的結果。例如,在下圖中,目標是將原始文本拆分為單詞併為每個單詞找到一個數字表示: + +
+ An example of word-based tokenization. + +
+ +有多種方法可以拆分文本。例如,我們可以通過應用Python的`split()`函數,使用空格將文本標記為單詞: + +```py +tokenized_text = "Jim Henson was a puppeteer".split() +print(tokenized_text) +``` + +```python out +['Jim', 'Henson', 'was', 'a', 'puppeteer'] +``` + +還有一些單詞標記器的變體,它們具有額外的標點符號規則。使用這種標記器,我們最終可以得到一些非常大的“詞彙表”,其中詞彙表由我們在語料庫中擁有的獨立標記的總數定義。 + +每個單詞都分配了一個 ID,從 0 開始一直到詞彙表的大小。該模型使用這些 ID 來識別每個單詞。 + +如果我們想用基於單詞的標記器(tokenizer)完全覆蓋一種語言,我們需要為語言中的每個單詞都有一個標識符,這將生成大量的標記。例如,英語中有超過 500,000 個單詞,因此要構建從每個單詞到輸入 ID 的映射,我們需要跟蹤這麼多 ID。此外,像“dog”這樣的詞與“dogs”這樣的詞的表示方式不同,模型最初無法知道“dog”和“dogs”是相似的:它會將這兩個詞識別為不相關。這同樣適用於其他相似的詞,例如“run”和“running”,模型最初不會認為它們是相似的。 + +最後,我們需要一個自定義標記(token)來表示不在我們詞彙表中的單詞。這被稱為“未知”標記(token),通常表示為“[UNK]”或"<unk>"。如果你看到標記器產生了很多這樣的標記,這通常是一個不好的跡象,因為它無法檢索到一個詞的合理表示,並且你會在這個過程中丟失信息。製作詞彙表時的目標是以這樣一種方式進行,即標記器將盡可能少的單詞標記為未知標記。 + +減少未知標記數量的一種方法是使用更深一層的標記器(tokenizer),即基於字符的(_character-based_)標記器(tokenizer)。 + +## 基於字符(Character-based) + + + +基於字符的標記器(tokenizer)將文本拆分為字符,而不是單詞。這有兩個主要好處: + +- 詞彙量要小得多。 +- 詞彙外(未知)標記(token)要少得多,因為每個單詞都可以從字符構建。 + +但是這裡也出現了一些關於空格和標點符號的問題: + +
+ An example of character-based tokenization. + +
+ +這種方法也不是完美的。由於現在表示是基於字符而不是單詞,因此人們可能會爭辯說,從直覺上講,它的意義不大:每個字符本身並沒有多大意義,而單詞就是這種情況。然而,這又因語言而異;例如,在中文中,每個字符比拉丁語言中的字符包含更多的信息。 + +另一件要考慮的事情是,我們的模型最終會處理大量的詞符(token):雖然使用基於單詞的標記器(tokenizer),單詞只會是單個標記,但當轉換為字符時,它很容易變成 10 個或更多的詞符(token)。 + +為了兩全其美,我們可以使用結合這兩種方法的第三種技術:*子詞標記化(subword tokenization)*。 + +## 子詞標記化 + + + +子詞分詞算法依賴於這樣一個原則,即不應將常用詞拆分為更小的子詞,而應將稀有詞分解為有意義的子詞。 + +例如,“annoyingly”可能被認為是一個罕見的詞,可以分解為“annoying”和“ly”。這兩者都可能作為獨立的子詞出現得更頻繁,同時“annoyingly”的含義由“annoying”和“ly”的複合含義保持。 + +這是一個示例,展示了子詞標記化算法如何標記序列“Let's do tokenization!”: + +
+ A subword tokenization algorithm. + +
+ +這些子詞最終提供了很多語義含義:例如,在上面的示例中,“tokenization”被拆分為“token”和“ization”,這兩個具有語義意義同時節省空間的詞符(token)(只需要兩個標記(token)代表一個長詞)。這使我們能夠對較小的詞彙表進行相對較好的覆蓋,並且幾乎沒有未知的標記 + +這種方法在土耳其語等粘著型語言(agglutinative languages)中特別有用,您可以通過將子詞串在一起來形成(幾乎)任意長的複雜詞。 + +### 還有更多! + +不出所料,還有更多的技術。僅舉幾例: + +- Byte-level BPE, 用於 GPT-2 +- WordPiece, 用於 BERT +- SentencePiece or Unigram, 用於多個多語言模型 + +您現在應該對標記器(tokenizers)的工作原理有足夠的瞭解,以便開始使用 API。 + +## 加載和保存 + +加載和保存標記器(tokenizer)就像使用模型一樣簡單。實際上,它基於相同的兩種方法: `from_pretrained()` 和 `save_pretrained()` 。這些方法將加載或保存標記器(tokenizer)使用的算法(有點像*建築學(architecture)*的模型)以及它的詞彙(有點像*權重(weights)*模型)。 + +加載使用與 BERT 相同的檢查點訓練的 BERT 標記器(tokenizer)與加載模型的方式相同,除了我們使用 `BertTokenizer` 類: + +```py +from transformers import BertTokenizer + +tokenizer = BertTokenizer.from_pretrained("bert-base-cased") +``` + +{#if fw === 'pt'} +如同 `AutoModel`,`AutoTokenizer` 類將根據檢查點名稱在庫中獲取正確的標記器(tokenizer)類,並且可以直接與任何檢查點一起使用: + +{:else} +如同 `TFAutoModel`, `AutoTokenizer` 類將根據檢查點名稱在庫中獲取正確的標記器(tokenizer)類,並且可以直接與任何檢查點一起使用: + +{/if} + +```py +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") +``` + +我們現在可以使用標記器(tokenizer),如上一節所示: + +```python +tokenizer("Using a Transformer network is simple") +``` + +```python out +{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102], + 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0], + 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]} +``` + +保存標記器(tokenizer)與保存模型相同: + +```py +tokenizer.save_pretrained("directory_on_my_computer") +``` + +我們在[Chapter 3](/Couse/chapter3)中將更多地談論`token_type_ids`,稍後我們將解釋 `attention_mask` 鍵。首先,讓我們看看 `input_ids` 如何生成。為此,我們需要查看標記器(tokenizer)的中間方法。 + +## 編碼 + + + +將文本翻譯成數字被稱為編碼(_encoding_).編碼分兩步完成:標記化,然後轉換為輸入 ID。 + +正如我們所見,第一步是將文本拆分為單詞(或單詞的一部分、標點符號等),通常稱為*標記(token)*。有多個規則可以管理該過程,這就是為什麼我們需要使用模型名稱來實例化標記器(tokenizer),以確保我們使用模型預訓練時使用的相同規則。 + +第二步是將這些標記轉換為數字,這樣我們就可以用它們構建一個張量並將它們提供給模型。為此,標記器(tokenizer)有一個*詞彙(vocabulary)*,這是我們在實例化它時下載的部分 `from_pretrained()` 方法。同樣,我們需要使用模型預訓練時使用的相同詞彙。 + +為了更好地理解這兩個步驟,我們將分別探討它們。請注意,我們將使用一些單獨執行部分標記化管道的方法來向您展示這些步驟的中間結果,但實際上,您應該直接在您的輸入上調用標記器(tokenizer)(如第 2 部分所示)。 + +### 標記化 + +標記化過程由標記器(tokenizer)的`tokenize()` 方法實現: + +```py +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") + +sequence = "Using a Transformer network is simple" +tokens = tokenizer.tokenize(sequence) + +print(tokens) +``` + +此方法的輸出是一個字符串列表或標記(token): + +```python out +['Using', 'a', 'transform', '##er', 'network', 'is', 'simple'] +``` + +這個標記器(tokenizer)是一個子詞標記器(tokenizer):它對詞進行拆分,直到獲得可以用其詞彙表表示的標記(token)。`transformer` 就是這種情況,它分為兩個標記:`transform` 和 `##er`。 + +### 從詞符(token)到輸入 ID +輸入 ID 的轉換由標記器(tokenizer)的`convert_tokens_to_ids()`方法實現: + +```py +ids = tokenizer.convert_tokens_to_ids(tokens) + +print(ids) +``` + +```python out +[7993, 170, 11303, 1200, 2443, 1110, 3014] +``` + +這些輸出一旦轉換為適當的框架張量,就可以用作模型的輸入,如本章前面所見。 + + + +✏️ **試試看!** 在我們在第 2 節中使用的輸入句子(“I've been waiting for a HuggingFace course my whole life.”和“I hate this so much!”)複製最後兩個步驟(標記化和轉換為輸入 ID)。檢查您獲得的輸入 ID 是否與我們之前獲得的相同! + + + +## 解碼 + +*解碼(Decoding)* 正好相反:從詞彙索引中,我們想要得到一個字符串。這可以通過 `decode()` 方法實現,如下: + +```py +decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014]) +print(decoded_string) +``` + +```python out +'Using a Transformer network is simple' +``` + +請注意, `decode` 方法不僅將索引轉換回標記(token),還將屬於相同單詞的標記(token)組合在一起以生成可讀的句子。當我們使用預測新文本的模型(根據提示生成的文本,或序列到序列問題(如翻譯或摘要))時,這種行為將非常有用。 + +到現在為止,您應該瞭解標記器(tokenizer)可以處理的原子操作:標記化、轉換為 ID 以及將 ID 轉換回字符串。然而,我們只是刮到了冰山一角。在下一節中,我們將採用我們的方法來克服它的限制,並看看如何克服它們。 diff --git a/chapters/zh-TW/chapter2/5.mdx b/chapters/zh-TW/chapter2/5.mdx new file mode 100644 index 000000000..dd09f943d --- /dev/null +++ b/chapters/zh-TW/chapter2/5.mdx @@ -0,0 +1,355 @@ + + +# 處理多個序列 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +{#if fw === 'pt'} + +{:else} + +{/if} + +在上一節中,我們探討了最簡單的用例:對一個小長度的序列進行推理。然而,一些問題已經出現: + +* 我們如何處理多個序列? + + +* 我們如何處理多個序列不同長度? + + +* 詞彙索引是讓模型正常工作的唯一輸入嗎? + + +* 是否存在序列太長的問題? + +讓我們看看這些問題會帶來什麼樣的問題,以及如何使用🤗 Transformers API解決它們 + +## 模型需要一批輸入 + +在上一個練習中,您看到了序列如何轉換為數字列表。讓我們將此數字列表轉換為張量,並將其發送到模型: + +{#if fw === 'pt'} +```py +import torch +from transformers import AutoTokenizer, AutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) +input_ids = torch.tensor(ids) +# This line will fail. +model(input_ids) +``` + +```python out +IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1) +``` +{:else} +```py +import tensorflow as tf +from transformers import AutoTokenizer, TFAutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) +input_ids = tf.constant(ids) +# This line will fail. +model(input_ids) +``` + +```py out +InvalidArgumentError: Input to reshape is a tensor with 14 values, but the requested shape has 196 [Op:Reshape] +``` +{/if} + +哦不!為什麼失敗了?我們遵循了第2節中管道的步驟。 + +問題是我們向模型發送了一個序列,而 🤗Transformers 模型默認情況下需要多個句子。在這裡,當我們將分詞器應用於一個應用程序時,我們嘗試在幕後完成分詞器所做的一切,但如果仔細觀察,您會發現它不僅將輸入ID列表轉換為張量,還在其頂部添加了一個維度: + +{#if fw === 'pt'} +```py +tokenized_inputs = tokenizer(sequence, return_tensors="pt") +print(tokenized_inputs["input_ids"]) +``` + +```python out +tensor([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, + 2607, 2026, 2878, 2166, 1012, 102]]) +``` +{:else} +```py +tokenized_inputs = tokenizer(sequence, return_tensors="tf") +print(tokenized_inputs["input_ids"]) +``` + +```py out +tf.Tensor: shape=(1, 16), dtype=int32, numpy= +array([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, + 12172, 2607, 2026, 2878, 2166, 1012, 102]], dtype=int32)> +``` +{/if} + +讓我們重試並添加一個新維度: + +{#if fw === 'pt'} +```py +import torch +from transformers import AutoTokenizer, AutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) + +input_ids = torch.tensor([ids]) +print("Input IDs:", input_ids) + +output = model(input_ids) +print("Logits:", output.logits) +``` +{:else} +```py +import tensorflow as tf +from transformers import AutoTokenizer, TFAutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) + +input_ids = tf.constant([ids]) +print("Input IDs:", input_ids) + +output = model(input_ids) +print("Logits:", output.logits) +``` +{/if} + +我們打印輸入ID以及生成的logits-以下是輸出: + +{#if fw === 'pt'} +```python out +Input IDs: [[ 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]] +Logits: [[-2.7276, 2.8789]] +``` +{:else} +```py out +Input IDs: tf.Tensor( +[[ 1045 1005 2310 2042 3403 2005 1037 17662 12172 2607 2026 2878 + 2166 1012]], shape=(1, 14), dtype=int32) +Logits: tf.Tensor([[-2.7276208 2.8789377]], shape=(1, 2), dtype=float32) +``` +{/if} + +*Batching* 是一次通過模型發送多個句子的行為。如果你只有一句話,你可以用一個序列構建一個批次: + + +``` +batched_ids = [ids, ids] +``` + +這是一批兩個相同的序列! + + + +✏️ **Try it out!** 試試看!將此列表轉換為張量並通過模型傳遞。檢查您是否獲得與之前相同的登錄(但是隻有兩次) + + +批處理允許模型在輸入多個句子時工作。使用多個序列就像使用單個序列構建批一樣簡單。不過,還有第二個問題。當你試圖將兩個(或更多)句子組合在一起時,它們的長度可能不同。如果您以前使用過張量,那麼您知道它們必須是矩形,因此無法將輸入ID列表直接轉換為張量。為了解決這個問題,我們通常填充輸入。 + +## 填充輸入 + +以下列表不能轉換為張量: + +```py no-format +batched_ids = [ + [200, 200, 200], + [200, 200] +] +``` + +為了解決這個問題,我們將使用填充使張量具有矩形。Padding通過在值較少的句子中添加一個名為Padding token的特殊單詞來確保我們所有的句子長度相同。例如,如果你有10個包含10個單詞的句子和1個包含20個單詞的句子,填充將確保所有句子都包含20個單詞。在我們的示例中,生成的張量如下所示: + +```py no-format +padding_id = 100 + +batched_ids = [ + [200, 200, 200], + [200, 200, padding_id], +] +``` + +可以在tokenizer.pad_token_id中找到填充令牌ID. 讓我們使用它,將我們的兩句話分別發送到模型中,並分批發送到一起: + + +{#if fw === 'pt'} +```py no-format +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence1_ids = [[200, 200, 200]] +sequence2_ids = [[200, 200]] +batched_ids = [ + [200, 200, 200], + [200, 200, tokenizer.pad_token_id], +] + +print(model(torch.tensor(sequence1_ids)).logits) +print(model(torch.tensor(sequence2_ids)).logits) +print(model(torch.tensor(batched_ids)).logits) +``` + +```python out +tensor([[ 1.5694, -1.3895]], grad_fn=) +tensor([[ 0.5803, -0.4125]], grad_fn=) +tensor([[ 1.5694, -1.3895], + [ 1.3373, -1.2163]], grad_fn=) +``` +{:else} +```py no-format +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) + +sequence1_ids = [[200, 200, 200]] +sequence2_ids = [[200, 200]] +batched_ids = [ + [200, 200, 200], + [200, 200, tokenizer.pad_token_id], +] + +print(model(tf.constant(sequence1_ids)).logits) +print(model(tf.constant(sequence2_ids)).logits) +print(model(tf.constant(batched_ids)).logits) +``` + +```py out +tf.Tensor([[ 1.5693678 -1.3894581]], shape=(1, 2), dtype=float32) +tf.Tensor([[ 0.5803005 -0.41252428]], shape=(1, 2), dtype=float32) +tf.Tensor( +[[ 1.5693681 -1.3894582] + [ 1.3373486 -1.2163193]], shape=(2, 2), dtype=float32) +``` +{/if} + +我們批處理預測中的logits有點問題:第二行應該與第二句的logits相同,但我們得到了完全不同的值! + + +這是因為Transformer模型的關鍵特性是關注層,它將每個標記上下文化。這些將考慮填充標記,因為它們涉及序列中的所有標記。為了在通過模型傳遞不同長度的單個句子時,或者在傳遞一批應用了相同句子和填充的句子時獲得相同的結果,我們需要告訴這些注意層忽略填充標記。這是通過使用 attention mask來實現的。 + +## 注意力遮罩(Attention masks) + +*Attention masks* 是與輸入 ID 張量形狀完全相同的張量,用0和1填充:1s表示應注意相應的標記,0s表示不應注意相應的標記(即,模型的注意力層應忽略它們)。 + +讓我們用 attention mask 完成上一個示例: + +{#if fw === 'pt'} +```py no-format +batched_ids = [ + [200, 200, 200], + [200, 200, tokenizer.pad_token_id], +] + +attention_mask = [ + [1, 1, 1], + [1, 1, 0], +] + +outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask)) +print(outputs.logits) +``` + +```python out +tensor([[ 1.5694, -1.3895], + [ 0.5803, -0.4125]], grad_fn=) +``` +{:else} +```py no-format +batched_ids = [ + [200, 200, 200], + [200, 200, tokenizer.pad_token_id], +] + +attention_mask = [ + [1, 1, 1], + [1, 1, 0], +] + +outputs = model(tf.constant(batched_ids), attention_mask=tf.constant(attention_mask)) +print(outputs.logits) +``` + +```py out +tf.Tensor( +[[ 1.5693681 -1.3894582 ] + [ 0.5803021 -0.41252586]], shape=(2, 2), dtype=float32) +``` +{/if} + +現在我們得到了該批中第二個句子的相同登錄。 + +請注意,第二個序列的最後一個值是一個填充ID,它在attention mask中是一個0值。 + + + +✏️ 試試看!在第2節中使用的兩個句子上手動應用標記化(“我一生都在等待擁抱課程。”和“我非常討厭這個!”)。通過模型傳遞它們,並檢查您是否獲得與第2節中相同的登錄。現在使用填充標記將它們批處理在一起,然後創建適當的注意掩碼。檢查通過模型時是否獲得相同的結果! + + + +## 長序列 + +對於Transformers模型,我們可以通過模型的序列長度是有限的。大多數模型處理多達512或1024個令牌的序列,當要求處理更長的序列時,會崩潰。此問題有兩種解決方案: + + + +* 使用支持的序列長度較長的模型。 + + +* 截斷序列。 + + +模型有不同的支持序列長度,有些模型專門處理很長的序列。 +[Longformer](https://huggingface.co/transformers/model_doc/longformer.html) +這是一個例子,另一個是 +[LED](https://huggingface.co/transformers/model_doc/led.html) +. 如果您正在處理一項需要很長序列的任務,我們建議您查看這些模型。 + +否則,我們建議您通過指定max_sequence_length參數: + +```py +sequence = sequence[:max_sequence_length] +``` diff --git a/chapters/zh-TW/chapter2/6.mdx b/chapters/zh-TW/chapter2/6.mdx new file mode 100644 index 000000000..7bde73d6c --- /dev/null +++ b/chapters/zh-TW/chapter2/6.mdx @@ -0,0 +1,165 @@ + + +# 把它們放在一起 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +在最後幾節中,我們一直在盡最大努力手工完成大部分工作。我們探討了標記化器的工作原理,並研究了標記化、到輸入ID的轉換、填充、截斷和注意掩碼。 + +然而,正如我們在第2節中所看到的,🤗 Transformers API可以通過一個高級函數為我們處理所有這些,我們將在這裡深入討論。當你直接在句子上調用標記器時,你會得到準備通過模型傳遞的輸入 + +```py +from transformers import AutoTokenizer + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) + +sequence = "I've been waiting for a HuggingFace course my whole life." + +model_inputs = tokenizer(sequence) +``` + +這裡,`model_inputs` +變量包含模型良好運行所需的一切。對於DistilBERT,它包括輸入 ID和注意力掩碼(attention mask)。其他接受額外輸入的模型也會有標記器對象的輸出。 + +正如我們將在下面的一些示例中看到的,這種方法非常強大。首先,它可以標記單個序列: + +```py +sequence = "I've been waiting for a HuggingFace course my whole life." + +model_inputs = tokenizer(sequence) +``` + +它還一次處理多個序列,並且API沒有任何變化: + +```py +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +model_inputs = tokenizer(sequences) +``` + +它可以根據幾個目標進行填充: + +```py +# Will pad the sequences up to the maximum sequence length +model_inputs = tokenizer(sequences, padding="longest") + +# Will pad the sequences up to the model max length +# (512 for BERT or DistilBERT) +model_inputs = tokenizer(sequences, padding="max_length") + +# Will pad the sequences up to the specified max length +model_inputs = tokenizer(sequences, padding="max_length", max_length=8) +``` + +它還可以截斷序列: + +```py +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +# Will truncate the sequences that are longer than the model max length +# (512 for BERT or DistilBERT) +model_inputs = tokenizer(sequences, truncation=True) + +# Will truncate the sequences that are longer than the specified max length +model_inputs = tokenizer(sequences, max_length=8, truncation=True) +``` + +標記器對象可以處理到特定框架張量的轉換,然後可以直接發送到模型。例如,在下面的代碼示例中,我們提示標記器從不同的框架返回張量——`"pt"`返回Py Torch張量,`"tf"`返回TensorFlow張量,`"np"`返回NumPy數組: + +```py +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +# Returns PyTorch tensors +model_inputs = tokenizer(sequences, padding=True, return_tensors="pt") + +# Returns TensorFlow tensors +model_inputs = tokenizer(sequences, padding=True, return_tensors="tf") + +# Returns NumPy arrays +model_inputs = tokenizer(sequences, padding=True, return_tensors="np") +``` + +## 特殊詞符(token) + +如果我們看一下標記器返回的輸入 ID,我們會發現它們與之前的略有不同: + +```py +sequence = "I've been waiting for a HuggingFace course my whole life." + +model_inputs = tokenizer(sequence) +print(model_inputs["input_ids"]) + +tokens = tokenizer.tokenize(sequence) +ids = tokenizer.convert_tokens_to_ids(tokens) +print(ids) +``` + +```python out +[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102] +[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012] +``` + +一個在開始時添加了一個標記(token) ID,一個在結束時添加了一個標記(token) ID。讓我們解碼上面的兩個ID序列,看看這是怎麼回事: + +```py +print(tokenizer.decode(model_inputs["input_ids"])) +print(tokenizer.decode(ids)) +``` + +```python out +"[CLS] i've been waiting for a huggingface course my whole life. [SEP]" +"i've been waiting for a huggingface course my whole life." +``` + +標記器在開頭添加了特殊單詞`[CLS]`,在結尾添加了特殊單詞`[SEP]`。這是因為模型是用這些數據預訓練的,所以為了得到相同的推理結果,我們還需要添加它們。請注意,有些模型不添加特殊單詞,或者添加不同的單詞;模型也可能只在開頭或結尾添加這些特殊單詞。在任何情況下,標記器都知道需要哪些詞符,並將為您處理這些詞符。 + +## 結束:從標記器到模型 + +現在我們已經看到了標記器對象在應用於文本時使用的所有單獨步驟,讓我們最後一次看看它如何處理多個序列(填充!),非常長的序列(截斷!),以及多種類型的張量及其主要API: + +{#if fw === 'pt'} +```py +import torch +from transformers import AutoTokenizer, AutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt") +output = model(**tokens) +``` +{:else} +```py +import tensorflow as tf +from transformers import AutoTokenizer, TFAutoModelForSequenceClassification + +checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) +sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"] + +tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="tf") +output = model(**tokens) +``` +{/if} diff --git a/chapters/zh-TW/chapter2/7.mdx b/chapters/zh-TW/chapter2/7.mdx new file mode 100644 index 000000000..2cbff4f37 --- /dev/null +++ b/chapters/zh-TW/chapter2/7.mdx @@ -0,0 +1,32 @@ +# 基本用法完成! + + + +很好地完成了到這裡的課程!總而言之,在本章中,您可以: + +- 學習了Transformers模型的基本構造塊。 + + +- 瞭解了標記化管道的組成。 + + +- 瞭解瞭如何在實踐中使用Transformers模型。 + + +- 學習瞭如何利用分詞器將文本轉換為模型可以理解的張量。 + + +- 將分詞器和模型一起設置,以從文本到預測。 + + +- 瞭解了inputs IDs的侷限性,並瞭解了attention mask。 + + +- 使用多功能和可配置的分詞器方法。 + + + +從現在起,您應該能夠自由瀏覽🤗 Transformers文檔:詞彙聽起來很熟悉,並且您已經看到了大部分時間將使用的方法。 diff --git a/chapters/zh-TW/chapter2/8.mdx b/chapters/zh-TW/chapter2/8.mdx new file mode 100644 index 000000000..8f0ca8d7b --- /dev/null +++ b/chapters/zh-TW/chapter2/8.mdx @@ -0,0 +1,298 @@ + + + + +# 章末小測試 + + + +### 1. 語言建模 Pipeline 的順序是什麼? + + +### 2. Transformer模型的輸出有多少個維度,每個維度分別是什麼? + + +### 3.下列哪一個是Subword標記(Tokenization)的例子(從分詞的顆粒度來劃分)? + + +### 4.什麼是模型的 Head 層? + + +{#if fw === 'pt'} +### 5.什麼是AutoModel? +AutoNLP 產品相混淆了?" + }, + { + text: "一個根據Checkpoint(檢查點)返回模型體系結構的對象", + explain: "確切地說: AutoModel只需要知道初始化的Checkpoint(檢查點)就可以返回正確的體系結構。", + correct: true + }, + { + text: "一種可以自動檢測輸入語言來加載正確權重的模型", + explain: "不正確; 雖然有些Checkpoint(檢查點)和模型能夠處理多種語言,但是沒有內置的工具可以根據語言自動選擇Checkpoint(檢查點)。您應該前往 Model Hub 尋找完成所需任務的最佳Checkpoint(檢查點)!" + } + ]} +/> + +{:else} +### 5.什麼是 TFAutoModel? +AutoNLP 產品相混淆了?" + }, + { + text: "一個根據Checkpoint(檢查點)返回模型體系結構的對象", + explain: "確切地說: TFAutoModel只需要知道初始化的Checkpoint(檢查點)就可以返回正確的體系結構。", + correct: true + }, + { + text: "一種可以自動檢測輸入語言來加載正確權重的模型", + explain: "不正確; 雖然有些Checkpoint(檢查點)和模型能夠處理多種語言,但是沒有內置的工具可以根據語言自動選擇Checkpoint(檢查點)。您應該前往 Model Hub 尋找完成所需任務的最佳Checkpoint(檢查點)!" + } + ]} +/> + +{/if} + +### 6.當將不同長度的序列批處理在一起時,需要進行哪些處理? + + +### 7.將 SoftMax激活函數應用於序列分類(Sequence Classification)模型的 logits 輸出有什麼意義? + + +### 8.大多數標記器(Tokenizer)的API以什麼方法為核心? +編碼 ,因為它可以將文本編碼為id,將預測的id解碼為文本", + explain: "錯! 雖然 編碼 方法確實存在於標記器中,但是它不存在於模型中。" + }, + { + text: "直接調用標記器(Tokenizer)對象。", + explain: "完全正確!標記化器(Tokenizer) 的 __call__方法是一個非常強大的方法,可以處理幾乎任何事情。它也是從模型中獲取預測的方法。", + correct: true + }, + { + text: "pad(填充)", + explain: "錯! pad(填充)非常有用,但它只是標記器(Tokenizer) API的一部分。" + }, + { + text: "tokenize(標記)", + explain: "可以說,tokenize(標記)方法是最有用的方法之一,但它不是標記器(Tokenizer) API的核心方法。" + } + ]} +/> + +### 9.這個代碼示例中的`result`變量包含什麼? +```py +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") +result = tokenizer.tokenize("Hello!") +``` + +__call__ 或 convert_tokens_to_ids方法的作用!" + }, + { + text: "包含所有標記(Token)的字符串", + explain: "這將是次優的,因為Tokenizer會將字符串拆分為多個標記的列表。" + } + ]} +/> + +{#if fw === 'pt'} +### 10.下面的代碼有什麼錯誤嗎? +```py +from transformers import AutoTokenizer, AutoModel + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") +model = AutoModel.from_pretrained("gpt2") + +encoded = tokenizer("Hey!", return_tensors="pt") +result = model(**encoded) +``` + + + +{:else} +### 10.下面的代碼有什麼錯誤嗎? +```py +from transformers import AutoTokenizer, TFAutoModel + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") +model = TFAutoModel.from_pretrained("gpt2") + +encoded = tokenizer("Hey!", return_tensors="pt") +result = model(**encoded) +``` + + + +{/if} diff --git a/chapters/zh-TW/chapter3/1.mdx b/chapters/zh-TW/chapter3/1.mdx new file mode 100644 index 000000000..aacaf7dc6 --- /dev/null +++ b/chapters/zh-TW/chapter3/1.mdx @@ -0,0 +1,26 @@ + + +# 本章簡介 + + + +在 [第二章](/course/chapter2) 我們探索瞭如何使用標記器(Tokenizer)和預訓練模型進行預測。但是,如果您想為自己的數據集微調預訓練模型,該怎麼做呢?這就是本章的主題!你將學到: + +{#if fw === 'pt'} +* 如何從模型中心(hub)準備大型數據集 +* 如何使用高級`訓練`API微調一個模型 +* 如何使用自定義訓練過程 +* 如何利用🤗 Accelerate庫在任何分佈式設備上輕鬆運行自定義訓練過程 + +{:else} +* 如何從模型中心(hub)準備大型數據集 +* 如何使用 Keras 微調模型 +* 如何使用 Keras 進行預測 +* 如何使用自定義指標 + +{/if} + +為了將經過訓練的參數上傳到 Hugging Face Hub,您需要一個 huggingface.co 帳戶: [創建一個賬戶](https://huggingface.co/join) \ No newline at end of file diff --git a/chapters/zh-TW/chapter3/2.mdx b/chapters/zh-TW/chapter3/2.mdx new file mode 100644 index 000000000..1b9045aab --- /dev/null +++ b/chapters/zh-TW/chapter3/2.mdx @@ -0,0 +1,383 @@ + + +# 處理數據 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +{#if fw === 'pt'} +這一小節學習[第一小節](/course/chapter2)中提到的「如何使用模型中心(hub)大型數據集」,下面是我們用模型中心的數據在 PyTorch 上訓練句子分類器的一個例子: + +```python +import torch +from transformers import AdamW, AutoTokenizer, AutoModelForSequenceClassification + +# Same as before +checkpoint = "bert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = AutoModelForSequenceClassification.from_pretrained(checkpoint) +sequences = [ + "I've been waiting for a HuggingFace course my whole life.", + "This course is amazing!", +] +batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt") + +# This is new +batch["labels"] = torch.tensor([1, 1]) + +optimizer = AdamW(model.parameters()) +loss = model(**batch).loss +loss.backward() +optimizer.step() +``` +{:else} +這一小節學習[第一小節](/course/chapter2)中提到的「如何使用模型中心(hub)大型數據集」,下面是我們用模型中心的數據在 TensorFlow 上訓練句子分類器的一個例子: + +```python +import tensorflow as tf +import numpy as np +from transformers import AutoTokenizer, TFAutoModelForSequenceClassification + +# Same as before +checkpoint = "bert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) +sequences = [ + "I've been waiting for a HuggingFace course my whole life.", + "This course is amazing!", +] +batch = dict(tokenizer(sequences, padding=True, truncation=True, return_tensors="tf")) + +# This is new +model.compile(optimizer="adam", loss="sparse_categorical_crossentropy") +labels = tf.convert_to_tensor([1, 1]) +model.train_on_batch(batch, labels) +``` +{/if} + +當然,僅僅用兩句話訓練模型不會產生很好的效果。為了獲得更好的結果,您需要準備一個更大的數據集。 + +在本節中,我們將使用MRPC(微軟研究釋義語料庫)數據集作為示例,該數據集由威廉·多蘭和克里斯·布羅克特在[這篇文章](https://www.aclweb.org/anthology/I05-5002.pdf)發佈。該數據集由5801對句子組成,每個句子對帶有一個標籤,指示它們是否為同義(即,如果兩個句子的意思相同)。我們在本章中選擇了它,因為它是一個小數據集,所以很容易對它進行訓練。 + +### 從模型中心(Hub)加載數據集 + +{#if fw === 'pt'} + +{:else} + +{/if} + +模型中心(hub)不只是包含模型;它也有許多不同語言的多個數據集。點擊[數據集](https://huggingface.co/datasets)的鏈接即可進行瀏覽。我們建議您在閱讀本節後閱讀一下[加載和處理新的數據集](https://huggingface.co/docs/datasets/loading_datasets.html#from-the-huggingface-hub)這篇文章,這會讓您對huggingface的darasets更加清晰。但現在,讓我們使用MRPC數據集中的[GLUE 基準測試數據集](https://gluebenchmark.com/),它是構成MRPC數據集的10個數據集之一,這是一個學術基準,用於衡量機器學習模型在10個不同文本分類任務中的性能。 + +🤗 Datasets庫提供了一個非常便捷的命令,可以在模型中心(hub)上下載和緩存數據集。我們可以通過以下的代碼下載MRPC數據集: + +```py +from datasets import load_dataset + +raw_datasets = load_dataset("glue", "mrpc") +raw_datasets +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['sentence1', 'sentence2', 'label', 'idx'], + num_rows: 3668 + }) + validation: Dataset({ + features: ['sentence1', 'sentence2', 'label', 'idx'], + num_rows: 408 + }) + test: Dataset({ + features: ['sentence1', 'sentence2', 'label', 'idx'], + num_rows: 1725 + }) +}) +``` + +正如你所看到的,我們獲得了一個**DatasetDict**對象,其中包含訓練集、驗證集和測試集。每一個集合都包含幾個列(**sentence1**, **sentence2**, **label**, and **idx**)以及一個代表行數的變量,即每個集合中的行的個數(因此,訓練集中有3668對句子,驗證集中有408對,測試集中有1725對)。 + +默認情況下,此命令在下載數據集並緩存到 **~/.cache/huggingface/dataset**. 回想一下第2章,您可以通過設置**HF_HOME**環境變量來自定義緩存的文件夾。 + +我們可以訪問我們數據集中的每一個**raw_train_dataset**對象,如使用字典: + +```py +raw_train_dataset = raw_datasets["train"] +raw_train_dataset[0] +``` + +```python out +{'idx': 0, + 'label': 1, + 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .', + 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'} +``` + +我們可以看到標籤已經是整數了,所以我們不需要對標籤做任何預處理。要知道哪個數字對應於哪個標籤,我們可以查看**raw_train_dataset**的**features**. 這將告訴我們每列的類型: + +```py +raw_train_dataset.features +``` + +```python out +{'sentence1': Value(dtype='string', id=None), + 'sentence2': Value(dtype='string', id=None), + 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None), + 'idx': Value(dtype='int32', id=None)} +``` + +在上面的例子之中,**Label(標籤)** 是一種**ClassLabel(分類標籤)**,使用整數建立起到類別標籤的映射關係。**0**對應於**not_equivalent**,**1**對應於**equivalent**。 + + + +✏️ **試試看!** 查看訓練集的第15行元素和驗證集的87行元素。他們的標籤是什麼? + + + +### 預處理數據集 + +{#if fw === 'pt'} + +{:else} + +{/if} + +為了預處理數據集,我們需要將文本轉換為模型能夠理解的數字。正如你在[第二章](/course/chapter2)上看到的那樣 + +```py +from transformers import AutoTokenizer + +checkpoint = "bert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"]) +tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"]) +``` + +然而,在兩句話傳遞給模型,預測這兩句話是否是同義之前。我們需要這兩句話依次進行適當的預處理。幸運的是,標記器不僅僅可以輸入單個句子還可以輸入一組句子,並按照我們的BERT模型所期望的輸入進行處理: + +```py +inputs = tokenizer("This is the first sentence.", "This is the second one.") +inputs +``` + +```python out +{ + 'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102], + 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], + 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +} +``` + +我們在[第二章](/course/chapter2) 討論了**輸入詞id(input_ids)** 和 **注意力遮罩(attention_mask)** ,但我們在那個時候沒有討論**類型標記ID(token_type_ids)**。在這個例子中,**類型標記ID(token_type_ids)**的作用就是告訴模型輸入的哪一部分是第一句,哪一部分是第二句。 + + + +✏️ ** 試試看!** 選取訓練集中的第15個元素,將兩句話分別標記為一對。結果和上方的例子有什麼不同? + + + +如果我們將**input_ids**中的id轉換回文字: + +```py +tokenizer.convert_ids_to_tokens(inputs["input_ids"]) +``` + +我們將得到: + +```python out +['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]'] +``` + +所以我們看到模型需要輸入的形式是 **[CLS] sentence1 [SEP] sentence2 [SEP]**。因此,當有兩句話的時候。**類型標記ID(token_type_ids)** 的值是: + +```python out +['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]'] +[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1] +``` + +如您所見,輸入中 **[CLS] sentence1 [SEP]** 它們的類型標記ID均為**0**,而其他部分,對應於**sentence2 [SEP]**,所有的類型標記ID均為**1**. + +請注意,如果選擇其他的檢查點,則不一定具有**類型標記ID(token_type_ids)**(例如,如果使用DistilBERT模型,就不會返回它們)。只有當它在預訓練期間使用過這一層,模型在構建時依賴它們,才會返回它們。 + +用類型標記ID對BERT進行預訓練,並且使用[第一章](/course/chapter1)的遮罩語言模型,還有一個額外的應用類型,叫做下一句預測. 這項任務的目標是建立成對句子之間關係的模型。 + +在下一個句子預測任務中,會給模型輸入成對的句子(帶有隨機遮罩的標記),並被要求預測第二個句子是否緊跟第一個句子。為了提高模型的泛化能力,數據集中一半的兩個句子在原始文檔中挨在一起,另一半的兩個句子來自兩個不同的文檔。 + +一般來說,你不需要擔心是否有**類型標記ID(token_type_ids)**。在您的標輸入中:只要您對標記器和模型使用相同的檢查點,一切都會很好,因為標記器知道向其模型提供什麼。 + +現在我們已經瞭解了標記器如何處理一對句子,我們可以使用它對整個數據集進行處理:如[之前的章節](/course/chapter2),我們可以給標記器提供一組句子,第一個參數是它第一個句子的列表,第二個參數是第二個句子的列表。這也與我們在[第二章](/course/chapter2)中看到的填充和截斷選項兼容. 因此,預處理訓練數據集的一種方法是: + +```py +tokenized_dataset = tokenizer( + raw_datasets["train"]["sentence1"], + raw_datasets["train"]["sentence2"], + padding=True, + truncation=True, +) +``` + +這很有效,但它的缺點是返回字典(字典的鍵是**輸入詞id(input_ids)** , **注意力遮罩(attention_mask)** 和 **類型標記ID(token_type_ids)**,字典的值是鍵所對應值的列表)。而且只有當您在轉換過程中有足夠的內存來存儲整個數據集時才不會出錯(而🤗數據集庫中的數據集是以[Apache Arrow](https://arrow.apache.org/)文件存儲在磁盤上,因此您只需將接下來要用的數據加載在內存中,因此會對內存容量的需求要低一些)。 + +為了將數據保存為數據集,我們將使用[Dataset.map()](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasets.Dataset.map)方法,如果我們需要做更多的預處理而不僅僅是標記化,那麼這也給了我們一些額外的自定義的方法。這個方法的工作原理是在數據集的每個元素上應用一個函數,因此讓我們定義一個標記輸入的函數: + +```py +def tokenize_function(example): + return tokenizer(example["sentence1"], example["sentence2"], truncation=True) +``` + +此函數的輸入是一個字典(與數據集的項類似),並返回一個包含**輸入詞id(input_ids)** , **注意力遮罩(attention_mask)** 和 **類型標記ID(token_type_ids)** 鍵的新字典。請注意,如果像上面的**示例**一樣,如果鍵所對應的值包含多個句子(每個鍵作為一個句子列表),那麼它依然可以工作,就像前面的例子一樣標記器可以處理成對的句子列表。這樣的話我們可以在調用**map()**使用該選項 **batched=True** ,這將顯著加快標記與標記的速度。這個**標記器**來自[🤗 Tokenizers](https://github.com/huggingface/tokenizers)庫由Rust編寫而成。當我們一次給它大量的輸入時,這個標記器可以非常快。 + +請注意,我們現在在標記函數中省略了**padding**參數。這是因為在標記的時候將所有樣本填充到最大長度的效率不高。一個更好的做法:在構建批處理時填充樣本更好,因為這樣我們只需要填充到該批處理中的最大長度,而不是整個數據集的最大長度。當輸入長度變化很大時,這可以節省大量時間和處理能力! + +下面是我們如何在所有數據集上同時應用標記函數。我們在調用**map**時使用了**batch =True**,這樣函數就可以同時應用到數據集的多個元素上,而不是分別應用到每個元素上。這將使我們的預處理快許多 + +```py +tokenized_datasets = raw_datasets.map(tokenize_function, batched=True) +tokenized_datasets +``` + +🤗Datasets 庫應用這種處理的方式是向數據集添加新的字段,每個字段對應預處理函數返回的字典中的每個鍵: + +```python out +DatasetDict({ + train: Dataset({ + features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'], + num_rows: 3668 + }) + validation: Dataset({ + features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'], + num_rows: 408 + }) + test: Dataset({ + features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'], + num_rows: 1725 + }) +}) +``` + +在使用預處理函數**map()**時,甚至可以通過傳遞**num_proc**參數使用並行處理。我們在這裡沒有這樣做,因為🤗標記器庫已經使用多個線程來更快地標記我們的樣本,但是如果您沒有使用該庫支持的快速標記器,使用**num_proc**可能會加快預處理。 + +我們的**標記函數(tokenize_function)**返回包含**輸入詞id(input_ids)** , **注意力遮罩(attention_mask)** 和 **類型標記ID(token_type_ids)** 鍵的字典,所以這三個字段被添加到數據集的標記的結果中。注意,如果預處理函數**map()**為現有鍵返回一個新值,那將會修改原有鍵的值。 + +最後一件我們需要做的事情是,當我們一起批處理元素時,將所有示例填充到最長元素的長度——我們稱之為動態填充。 + +### 動態填充 + + + +{#if fw === 'pt'} +負責在批處理中將數據整理為一個batch的函數稱為*collate函數*。它是你可以在構建**DataLoader**時傳遞的一個參數,默認是一個函數,它將把你的數據集轉換為PyTorch張量,並將它們拼接起來(如果你的元素是列表、元組或字典,則會使用遞歸)。這在我們的這個例子中下是不可行的,因為我們的輸入不是都是相同大小的。我們故意在之後每個batch上進行填充,避免有太多填充的過長的輸入。這將大大加快訓練速度,但請注意,如果你在TPU上訓練,這可能會導致問題——TPU喜歡固定的形狀,即使這需要額外的填充。 + +{:else} + +負責在批處理中將數據整理為一個batch的函數稱為*collate函數*。它只會將您的樣本轉換為 tf.Tensor並將它們拼接起來(如果你的元素是列表、元組或字典,則會使用遞歸)。這在我們的這個例子中下是不可行的,因為我們的輸入不是都是相同大小的。我們故意在之後每個batch上進行填充,避免有太多填充的過長的輸入。這將大大加快訓練速度,但請注意,如果你在TPU上訓練,這可能會導致問題——TPU喜歡固定的形狀,即使這需要額外的填充。 + +{/if} + +為了解決句子長度統一的問題,我們必須定義一個collate函數,該函數會將每個batch句子填充到正確的長度。幸運的是,🤗transformer庫通過**DataCollatorWithPadding**為我們提供了這樣一個函數。當你實例化它時,需要一個標記器(用來知道使用哪個詞來填充,以及模型期望填充在左邊還是右邊),並將做你需要的一切: + +{#if fw === 'pt'} +```py +from transformers import DataCollatorWithPadding + +data_collator = DataCollatorWithPadding(tokenizer=tokenizer) +``` +{:else} +```py +from transformers import DataCollatorWithPadding + +data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf") +``` +{/if} + +為了測試這個新玩具,讓我們從我們的訓練集中抽取幾個樣本。這裡,我們刪除列**idx**, **sentence1**和**sentence2**,因為不需要它們,並查看一個batch中每個條目的長度: + +```py +samples = tokenized_datasets["train"][:8] +samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]} +[len(x) for x in samples["input_ids"]] +``` + +```python out +[50, 59, 47, 67, 59, 50, 62, 32] +``` + +毫無疑問,我們得到了不同長度的樣本,從32到67。動態填充意味著該批中的所有樣本都應該填充到長度為67,這是該批中的最大長度。如果沒有動態填充,所有的樣本都必須填充到整個數據集中的最大長度,或者模型可以接受的最大長度。讓我們再次檢查**data_collator**是否正確地動態填充了這批樣本: + +```py: + +```py +batch = data_collator(samples) +{k: v.shape for k, v in batch.items()} +``` + +{#if fw === 'tf'} + +```python out +{'attention_mask': TensorShape([8, 67]), + 'input_ids': TensorShape([8, 67]), + 'token_type_ids': TensorShape([8, 67]), + 'labels': TensorShape([8])} +``` + +{:else} + +```python out +{'attention_mask': torch.Size([8, 67]), + 'input_ids': torch.Size([8, 67]), + 'token_type_ids': torch.Size([8, 67]), + 'labels': torch.Size([8])} +``` + +看起來不錯!現在,我們已經將原始文本轉化為了模型可以處理的數據,我們已準備好對其進行微調! + +{/if} + + + +✏️ ** 試試看!** 在GLUE SST-2數據集上應用預處理。它有點不同,因為它是由單個句子而不是成對的句子組成的,但是我們所做的其他事情看起來應該是一樣的。另一個更難的挑戰,請嘗試編寫一個可用於任何GLUE任務的預處理函數。 + + + +{#if fw === 'tf'} + +現在我們有了 dataset 和 data collator,我們需要將 dataset 批次地應用 data collator。 我們可以手動載入批次並整理它們,但這需要大量工作,性能可能也不是很好。 相反,有一個簡單的方法可以為這個問題提供高效的解決方案:`to_tf_dataset()`。 這將在您的數據集上調用一個 `tf.data.Dataset`的方法,這個方法帶有一個可選的 data collator 功能。 `tf.data.Dataset` 是 Keras 可用於 `model.fit()` 的原生 TensorFlow 格式,因此這種方法會立即將🤗 Dataset 轉換為可用於訓練的格式。 讓我們看看它在我們的數據集上是如何使用的! + +```py +tf_train_dataset = tokenized_datasets["train"].to_tf_dataset( + columns=["attention_mask", "input_ids", "token_type_ids"], + label_cols=["labels"], + shuffle=True, + collate_fn=data_collator, + batch_size=8, +) + +tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset( + columns=["attention_mask", "input_ids", "token_type_ids"], + label_cols=["labels"], + shuffle=False, + collate_fn=data_collator, + batch_size=8, +) +``` + +就是這樣! 我們可以將這些數據集帶入下一節,在經過所有艱苦的數據預處理工作之後,訓練將變得非常簡單。 + +{/if} diff --git a/chapters/zh-TW/chapter3/3.mdx b/chapters/zh-TW/chapter3/3.mdx new file mode 100644 index 000000000..93555c2aa --- /dev/null +++ b/chapters/zh-TW/chapter3/3.mdx @@ -0,0 +1,172 @@ + + +# 使用 Trainer API 微調模型 + + + + + +🤗 Transformers提供了一個 **Trainer** 類來幫助您在自己的數據集上微調任何預訓練模型。完成上一節中的所有數據預處理工作後,您只需要執行幾個步驟來創建 **Trainer** .最難的部分可能是為 **Trainer.train()**配置運行環境,因為它在 CPU 上運行速度會非常慢。如果您沒有設置 GPU,您可以訪問免費的 GPU 或 TPU[Google Colab](https://colab.research.google.com/). + +下面的示例假設您已經執行了上一節中的示例。下面這段代碼,概括了您需要提前運行的代碼: + +```py +from datasets import load_dataset +from transformers import AutoTokenizer, DataCollatorWithPadding + +raw_datasets = load_dataset("glue", "mrpc") +checkpoint = "bert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) + + +def tokenize_function(example): + return tokenizer(example["sentence1"], example["sentence2"], truncation=True) + + +tokenized_datasets = raw_datasets.map(tokenize_function, batched=True) +data_collator = DataCollatorWithPadding(tokenizer=tokenizer) +``` + +### Training + +在我們定義我們的 **Trainer** 之前首先要定義一個 **TrainingArguments** 類,它將包含 **Trainer**用於訓練和評估的所有超參數。您唯一必須提供的參數是保存訓練模型的目錄,以及訓練過程中的檢查點。對於其餘的參數,您可以保留默認值,這對於基本微調應該非常有效。 + +```py +from transformers import TrainingArguments + +training_args = TrainingArguments("test-trainer") +``` + + + +💡 如果您想在訓練期間自動將模型上傳到 Hub,請將push_to_hub=True添加到TrainingArguments之中. 我們將在[第四章](/course/chapter4/3)中詳細介紹這部分。 + + + +第二步是定義我們的模型。正如在[之前的章節](/2_Using Transformers/Introduction)一樣,我們將使用 **AutoModelForSequenceClassification** 類,它有兩個參數: + +```py +from transformers import AutoModelForSequenceClassification + +model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) +``` + +你會注意到,和[第二章](/course/chapter2)不一樣的是,在實例化此預訓練模型後會收到警告。這是因為 BERT 沒有在句子對分類方面進行過預訓練,所以預訓練模型的頭部已經被丟棄,而是添加了一個適合句子序列分類的新頭部。警告表明一些權重沒有使用(對應於丟棄的預訓練頭的那些),而其他一些權重被隨機初始化(新頭的那些)。最後鼓勵您訓練模型,這正是我們現在要做的。 + +一旦我們有了我們的模型,我們就可以定義一個 **Trainer** 通過將之前構造的所有對象傳遞給它——我們的**model** 、**training_args** ,訓練和驗證數據集,**data_collator** ,和 **tokenizer** : + +```py +from transformers import Trainer + +trainer = Trainer( + model, + training_args, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["validation"], + data_collator=data_collator, + tokenizer=tokenizer, +) +``` + +請注意,當您在這裡完成**tokenizer**後,默認 **Trainer**使用 的**data_collator**會使用之前預定義的 **DataCollatorWithPadding** ,因此您可以在這個例子中跳過 **data_collator=data_collator**。在第 2 節中向您展示這部分處理仍然很重要! + +為了讓預訓練模型在在我們的數據集上微調,我們只需要調用**Trainer**的**train()** 方法 : + +```py +trainer.train() +``` + +這將開始微調(在GPU上應該需要幾分鐘),並每500步報告一次訓練損失。但是,它不會告訴您模型的性能如何(或質量如何)。這是因為: + +1. 我們沒有通過將**evaluation_strategy**設置為“**steps**”(在每次更新參數的時候評估)或“**epoch**”(在每個epoch結束時評估)來告訴**Trainer**在訓練期間進行評估。 +2. 我們沒有為**Trainer**提供一個**compute_metrics()**函數來直接計算模型的好壞(否則評估將只輸出loss,這不是一個非常直觀的數字)。 + + +### 評估 + +讓我們看看如何構建一個有用的 **compute_metrics()** 函數並在我們下次訓練時使用它。該函數必須採用 **EvalPrediction** 對象(帶有 **predictions** 和 **label_ids** 字段的參數元組)並將返回一個字符串到浮點數的字典(字符串是返回的指標的名稱,而浮點數是它們的值)。我們可以使用 **Trainer.predict()** 命令來使用我們的模型進行預測: + +```py +predictions = trainer.predict(tokenized_datasets["validation"]) +print(predictions.predictions.shape, predictions.label_ids.shape) +``` + +```python out +(408, 2) (408,) +``` + + **predict()** 的輸出結果是具有三個字段的命名元組: **predictions** , **label_ids** , 和 **metrics** .這 **metrics** 字段將只包含傳遞的數據集的loss,以及一些運行時間(預測所需的總時間和平均時間)。如果我們定義了自己的 **compute_metrics()** 函數並將其傳遞給 **Trainer** ,該字段還將包含**compute_metrics()**的結果。 + +**predict()** 方法是具有三個字段的命名元組: **predictions** , **label_ids** , 和 **metrics** .這 **metrics** 字段將只包含傳遞的數據集的loss,以及一些運行時間(預測所需的總時間和平均時間)。如果我們定義了自己的 **compute_metrics()** 函數並將其傳遞給 **Trainer** ,該字段還將包含**compute_metrics()** 的結果。如你看到的, **predictions** 是一個形狀為 408 x 2 的二維數組(408 是我們使用的數據集中元素的數量)。這些是我們傳遞給**predict()**的數據集的每個元素的結果(logits)(正如你在[之前的章節](/course/chapter2)看到的情況)。要將我們的預測的可以與真正的標籤進行比較,我們需要在第二個軸上取最大值的索引: + +```py +import numpy as np + +preds = np.argmax(predictions.predictions, axis=-1) +``` + +現在建立我們的 **compute_metric()** 函數來較為直觀地評估模型的好壞,我們將使用 🤗 [Evaluate](https://github.com/huggingface/evaluate/) 庫中的指標。我們可以像加載數據集一樣輕鬆加載與 MRPC 數據集關聯的指標,這次使用 **evaluate.load()** 函數。返回的對象有一個 **compute()**方法我們可以用來進行度量計算的方法: + +```py +import evaluate + +metric = evaluate.load("glue", "mrpc") +metric.compute(predictions=preds, references=predictions.label_ids) +``` + +```python out +{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542} +``` + +您獲得的確切結果可能會有所不同,因為模型頭的隨機初始化可能會影響最終建立的模型。在這裡,我們可以看到我們的模型在驗證集上的準確率為 85.78%,F1 分數為 89.97。這是用於評估 GLUE 基準的 MRPC 數據集結果的兩個指標。而在[BERT 論文](https://arxiv.org/pdf/1810.04805.pdf)中展示的基礎模型的 F1 分數為 88.9。那是 **uncased** 模型,而我們目前正在使用 **cased** 模型,通過改進得到了更好的結果。 + +最後將所有東西打包在一起,我們得到了我們的 **compute_metrics()** 函數: + +```py +def compute_metrics(eval_preds): + metric = evaluate.load("glue", "mrpc") + logits, labels = eval_preds + predictions = np.argmax(logits, axis=-1) + return metric.compute(predictions=predictions, references=labels) +``` + +為了查看模型在每個訓練週期結束的好壞,下面是我們如何使用**compute_metrics()**函數定義一個新的 **Trainer** : + +```py +training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch") +model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) + +trainer = Trainer( + model, + training_args, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["validation"], + data_collator=data_collator, + tokenizer=tokenizer, + compute_metrics=compute_metrics, +) +``` + +請注意,我們設置了了一個新的 **TrainingArguments** 它的**evaluation_strategy** 設置為 **epoch** 並創建了一個新模型。如果不創建新的模型就直接訓練,就只會繼續訓練之前我們已經訓練過的模型。要啟動新的訓練運行,我們執行: + +``` +trainer.train() +``` + +這一次,它將在訓練loss之外,還會輸出每個 epoch 結束時的驗證loss和指標。同樣,由於模型的隨機頭部初始化,您達到的準確率/F1 分數可能與我們發現的略有不同,但它應該在同一範圍內。 + +這 **Trainer** 將在多個 GPU 或 TPU 上開箱即用,並提供許多選項,例如混合精度訓練(在訓練的參數中使用 **fp16 = True** )。我們將在第 10 章討論它支持的所有內容。 + +使用**Trainer** API微調的介紹到此結束。對最常見的 NLP 任務執行此操作的示例將在第 7 章中給出,但現在讓我們看看如何在純 PyTorch 中執行相同的操作。 + + + +✏️ **試試看!** 使用您在第 2 節中進行的數據處理,在 GLUE SST-2 數據集上微調模型。 + + + diff --git a/chapters/zh-TW/chapter3/3_tf.mdx b/chapters/zh-TW/chapter3/3_tf.mdx new file mode 100644 index 000000000..21d095289 --- /dev/null +++ b/chapters/zh-TW/chapter3/3_tf.mdx @@ -0,0 +1,190 @@ + + +# 使用 Keras 微調一個模型 + + + +完成上一節中的所有數據預處理工作後,您只剩下最後的幾個步驟來訓練模型。 但是請注意,`model.fit()` 命令在 CPU 上運行會非常緩慢。 如果您沒有GPU,則可以在 [Google Colab](https://colab.research.google.com/) 上使用免費的 GPU 或 TPU(需要梯子)。 + +這一節的代碼示例假設您已經執行了上一節中的代碼示例。 下面一個簡短的摘要,包含了在開始學習這一節之前您需要的執行的代碼: + +```py +from datasets import load_dataset +from transformers import AutoTokenizer, DataCollatorWithPadding +import numpy as np + +raw_datasets = load_dataset("glue", "mrpc") +checkpoint = "bert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) + + +def tokenize_function(example): + return tokenizer(example["sentence1"], example["sentence2"], truncation=True) + + +tokenized_datasets = raw_datasets.map(tokenize_function, batched=True) + +data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf") + +tf_train_dataset = tokenized_datasets["train"].to_tf_dataset( + columns=["attention_mask", "input_ids", "token_type_ids"], + label_cols=["labels"], + shuffle=True, + collate_fn=data_collator, + batch_size=8, +) + +tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset( + columns=["attention_mask", "input_ids", "token_type_ids"], + label_cols=["labels"], + shuffle=False, + collate_fn=data_collator, + batch_size=8, +) +``` + +### 訓練模型 + +從🤗 Transformers 導入的 TensorFlow 模型已經是 Keras 模型。 下面的視頻是對 Keras 的簡短介紹。 + + + +這意味著,一旦我們有了數據,就需要很少的工作就可以開始對其進行訓練。 + + + +和[第二章](/course/chapter2)使用的方法一樣, 我們將使用二分類的 `TFAutoModelForSequenceClassification`類: + +```py +from transformers import TFAutoModelForSequenceClassification + +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) +``` + +您會注意到,與 [第二章](/course/chapter2) 不同的是,您在實例化此預訓練模型後會收到警告。 這是因為 BERT 沒有對句子對進行分類進行預訓練,所以預訓練模型的 head 已經被丟棄,而是插入了一個適合序列分類的新 head。 警告表明一些權重沒有使用(對應於丟棄的預訓練頭),而其他一些權重是隨機初始化的(新頭的權重)。 最後鼓勵您訓練模型,這正是我們現在要做的。 + +要在我們的數據集上微調模型,我們只需要在我們的模型上調用 `compile()` 方法,然後將我們的數據傳遞給 `fit()` 方法。 這將啟動微調過程(在 GPU 上應該需要幾分鐘)並輸出訓練loss,以及每個 epoch 結束時的驗證loss。 + + + +請注意🤗 Transformers 模型具有大多數 Keras 模型所沒有的特殊能力——它們可以自動使用內部計算的loss。 如果您沒有在 `compile()` 中設置損失函數,他們將默認使用內部計算的損失。 請注意,要使用內部損失,您需要將標籤作為輸入的一部分傳遞,而不是作為單獨的標籤(這是在 Keras 模型中使用標籤的正常方式)。 您將在課程的第 2 部分中看到這方面的示例,其中定義正確的損失函數可能很棘手。 然而,對於序列分類,標準的 Keras 損失函數可以正常工作,所以我們將在這裡使用它。 + + + +```py +from tensorflow.keras.losses import SparseCategoricalCrossentropy + +model.compile( + optimizer="adam", + loss=SparseCategoricalCrossentropy(from_logits=True), + metrics=["accuracy"], +) +model.fit( + tf_train_dataset, + validation_data=tf_validation_dataset, +) +``` + + + +請注意這裡有一個非常常見的陷阱——你只是*可以*將損失的名稱作為字符串傳遞給 Keras,但默認情況下,Keras 會假設你已經對輸出應用了 softmax。 然而,許多模型在應用 softmax 之前就輸出,也稱為 *logits*。 我們需要告訴損失函數我們的模型是否經過了softmax,唯一的方法是直接調用它,而不是用字符串的名稱。 + + + + +### 提升訓練的效果 + + + +如果您嘗試上面的代碼,它肯定會運行,但您會發現loss只是緩慢或零星地下降。 主要原因是*學習率*。 與loss一樣,當我們將優化器的名稱作為字符串傳遞給 Keras 時,Keras 會初始化該優化器具有所有參數的默認值,包括學習率。 但是,根據長期經驗,我們知道Transformer 模型更適合使用比 Adam 的默認值(1e-3)也寫成為 10 的 -3 次方,或 0.001,低得多的學習率。 5e-5 (0.00005) 比默認值大約低 20 倍,是一個更好的起點。 + +除了降低學習率,我們還有第二個技巧:我們可以慢慢降低學習率。在訓練過程中。 在文獻中,您有時會看到這被稱為 *decaying* 或 *annealing*學習率。 在 Keras 中,最好的方法是使用 *learning rate scheduler*。 一個好用的是`PolynomialDecay`——儘管有這個名字,但在默認設置下,它只是簡單地從初始值線性衰減學習率值在訓練過程中的最終值,這正是我們想要的。但是, 為了正確使用調度程序,我們需要告訴它訓練的次數。 我們將在下面為其計算“num_train_steps”。 + +```py +from tensorflow.keras.optimizers.schedules import PolynomialDecay + +batch_size = 8 +num_epochs = 3 +# 訓練步數是數據集中的樣本數除以batch size再乘以 epoch。 +# 注意這裡的tf_train_dataset是一個轉化為batch後的 tf.data.Dataset, +# 不是原來的 Hugging Face Dataset,所以它的 len() 已經是 num_samples // batch_size。 +num_train_steps = len(tf_train_dataset) * num_epochs +lr_scheduler = PolynomialDecay( + initial_learning_rate=5e-5, end_learning_rate=0.0, decay_steps=num_train_steps +) +from tensorflow.keras.optimizers import Adam + +opt = Adam(learning_rate=lr_scheduler) +``` + + + +🤗 Transformers 庫還有一個 `create_optimizer()` 函數,它將創建一個具有學習率衰減的 `AdamW` 優化器。 這是一個便捷的方式,您將在本課程的後續部分中詳細瞭解。 + + + +現在我們有了全新的優化器,我們可以嘗試使用它進行訓練。 首先,讓我們重新加載模型,以重置我們剛剛進行的訓練運行對權重的更改,然後我們可以使用新的優化器對其進行編譯: + +```py +import tensorflow as tf + +model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) +loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) +model.compile(optimizer=opt, loss=loss, metrics=["accuracy"]) +``` + +現在,我們再次進行fit: + +```py +model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3) +``` + + + +💡 如果您想在訓練期間自動將模型上傳到 Hub,您可以在 `model.fit()` 方法中傳遞 `PushToHubCallback`。 我們將在 [第四章](/course/chapter4/3) 中進行介紹 + + + +### 模型預測 + + + + +訓練和觀察的loss下降都非常好,但是如果我們想從訓練後的模型中獲得輸出,或者計算一些指標,或者在生產中使用模型呢? 為此,我們可以使用`predict()` 方法。 這將返回模型的輸出頭的*logits*數值,每個類一個。 + +```py +preds = model.predict(tf_validation_dataset)["logits"] +``` + +我們可以將這些 logit 轉換為模型的類別預測,方法是使用 argmax 找到最高的 logit,它對應於最有可能的類別: + +```py +class_preds = np.argmax(preds, axis=1) +print(preds.shape, class_preds.shape) +``` + +```python out +(408, 2) (408,) +``` + +現在,讓我們使用這些 `preds` 來計算一些指標! 我們可以像加載數據集一樣輕鬆地加載與 MRPC 數據集相關的指標,這次使用的是 `evaluate.load()` 函數。 返回的對象有一個 `compute()` 方法,我們可以使用它來進行度量計算: + +```py +import evaluate + +metric = evaluate.load("glue", "mrpc") +metric.compute(predictions=class_preds, references=raw_datasets["validation"]["label"]) +``` + +```python out +{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542} +``` + +您獲得的確切結果可能會有所不同,因為模型頭的隨機初始化可能會改變它獲得的指標。 在這裡,我們可以看到我們的模型在驗證集上的準確率為 85.78%,F1 得分為 89.97。 這些是用於評估 GLUE 基準的 MRPC 數據集結果的兩個指標。 [BERT 論文](https://arxiv.org/pdf/1810.04805.pdf) 中的表格報告了基本模型的 F1 分數為 88.9。 那是 `uncased` 模型,而我們目前使用的是 `cased` 模型,這解釋了為什麼我們會獲得更好的結果。 + +使用 Keras API 進行微調的介紹到此結束。 第 7 章將給出對大多數常見 NLP 任務執行此操作的示例。如果您想在 Keras API 上磨練自己的技能,請嘗試使第二節所使用的的數據處理在 GLUE SST-2 數據集上微調模型。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter3/4.mdx b/chapters/zh-TW/chapter3/4.mdx new file mode 100644 index 000000000..2cd3f1dea --- /dev/null +++ b/chapters/zh-TW/chapter3/4.mdx @@ -0,0 +1,358 @@ +# 一個完整的訓練 + + + + + +現在,我們將瞭解如何在不使用`Trainer`類的情況下獲得與上一節相同的結果。同樣,我們假設您已經學習了第 2 節中的數據處理。下面是一個簡短的總結,涵蓋了您需要的所有內容: + +```py +from datasets import load_dataset +from transformers import AutoTokenizer, DataCollatorWithPadding + +raw_datasets = load_dataset("glue", "mrpc") +checkpoint = "bert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(checkpoint) + + +def tokenize_function(example): + return tokenizer(example["sentence1"], example["sentence2"], truncation=True) + + +tokenized_datasets = raw_datasets.map(tokenize_function, batched=True) +data_collator = DataCollatorWithPadding(tokenizer=tokenizer) +``` + +### 訓練前的準備 + +在實際編寫我們的訓練循環之前,我們需要定義一些對象。第一個是我們將用於迭代批次的數據加載器。我們需要對我們的`tokenized_datasets`做一些處理,來處理`Trainer`自動為我們做的一些事情。具體來說,我們需要: + +- 刪除與模型不期望的值相對應的列(如`sentence1`和`sentence2`列)。 +- 將列名`label`重命名為`labels`(因為模型期望參數是`labels`)。 +- 設置數據集的格式,使其返回 PyTorch 張量而不是列表。 + +針對上面的每個步驟,我們的 `tokenized_datasets` 都有一個方法: + +```py +tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"]) +tokenized_datasets = tokenized_datasets.rename_column("label", "labels") +tokenized_datasets.set_format("torch") +tokenized_datasets["train"].column_names +``` + +然後,我們可以檢查結果中是否只有模型能夠接受的列: + +```python +["attention_mask", "input_ids", "labels", "token_type_ids"] +``` + +至此,我們可以輕鬆定義數據加載器: + +```py +from torch.utils.data import DataLoader + +train_dataloader = DataLoader( + tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator +) +eval_dataloader = DataLoader( + tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator +) +``` + +為了快速檢驗數據處理中沒有錯誤,我們可以這樣檢驗其中的一個批次: + +```py +for batch in train_dataloader: + break +{k: v.shape for k, v in batch.items()} +``` + +```python out +{'attention_mask': torch.Size([8, 65]), + 'input_ids': torch.Size([8, 65]), + 'labels': torch.Size([8]), + 'token_type_ids': torch.Size([8, 65])} +``` + +請注意,實際的形狀可能與您略有不同,因為我們為訓練數據加載器設置了`shuffle=True`,並且模型會將句子填充到`batch`中的最大長度。 + +現在我們已經完全完成了數據預處理(對於任何 ML 從業者來說都是一個令人滿意但難以實現的目標),讓我們將注意力轉向模型。我們完全像在上一節中所做的那樣實例化它: + +```py +from transformers import AutoModelForSequenceClassification + +model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) +``` +為了確保訓練過程中一切順利,我們將`batch`傳遞給這個模型: + +```py +outputs = model(**batch) +print(outputs.loss, outputs.logits.shape) +``` + +```python out +tensor(0.5441, grad_fn=) torch.Size([8, 2]) +``` + +當我們提供 `labels` 時, 🤗 Transformers 模型都將返回這個`batch`的`loss`,我們還得到了 `logits`(`batch`中的每個輸入有兩個,所以張量大小為 8 x 2)。 + +我們幾乎準備好編寫我們的訓練循環了!我們只是缺少兩件事:優化器和學習率調度器。由於我們試圖自行實現 `Trainer`的功能,我們將使用相同的優化器和學習率調度器。`Trainer` 使用的優化器是 `AdamW` , 與 `Adam` 相同,但在權重衰減正則化方面有所不同(參見[“Decoupled Weight Decay Regularization”](https://arxiv.org/abs/1711.05101)作者:Ilya Loshchilov 和 Frank Hutter): + +```py +from transformers import AdamW + +optimizer = AdamW(model.parameters(), lr=5e-5) +``` + +最後,默認使用的學習率調度器只是從最大值 (5e-5) 到 0 的線性衰減。 為了定義它,我們需要知道我們訓練的次數,即所有數據訓練的次數(epochs)乘以的數據量(這是我們所有訓練數據的數量)。`Trainer`默認情況下使用三個`epochs`,因此我們定義訓練過程如下: + +```py +from transformers import get_scheduler + +num_epochs = 3 +num_training_steps = num_epochs * len(train_dataloader) +lr_scheduler = get_scheduler( + "linear", + optimizer=optimizer, + num_warmup_steps=0, + num_training_steps=num_training_steps, +) +print(num_training_steps) +``` + +```python out +1377 +``` + +### 訓練循環 + +最後一件事:如果我們可以訪問 GPU,我們將希望使用 GPU(在 CPU 上,訓練可能需要幾個小時而不是幾分鐘)。為此,我們定義了一個 `device`,它在GPU可用的情況下指向GPU 我們將把我們的模型和`batche`放在`device`上: + +```py +import torch + +device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") +model.to(device) +device +``` + +```python out +device(type='cuda') +``` + +我們現在準備好訓練了!為了瞭解訓練何時結束,我們使用 `tqdm` 庫,在訓練步驟數上添加了一個進度條: + +```py +from tqdm.auto import tqdm + +progress_bar = tqdm(range(num_training_steps)) + +model.train() +for epoch in range(num_epochs): + for batch in train_dataloader: + batch = {k: v.to(device) for k, v in batch.items()} + outputs = model(**batch) + loss = outputs.loss + loss.backward() + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) +``` + +您可以看到訓練循環的核心與介紹中的非常相似。我們沒有要求任何檢驗,所以這個訓練循環不會告訴我們任何關於模型目前的狀態。我們需要為此添加一個評估循環。 + + +### 評估循環 + +正如我們之前所做的那樣,我們將使用 🤗 Evaluate 庫提供的指標。我們已經瞭解了 `metric.compute()` 方法,當我們使用 `add_batch()`方法進行預測循環時,實際上該指標可以為我們累積所有 `batch` 的結果。一旦我們累積了所有 `batch` ,我們就可以使用 `metric.compute()` 得到最終結果 .以下是在評估循環中實現所有這些的方法: + +```py +import evaluate + +metric = evaluate.load("glue", "mrpc") +model.eval() +for batch in eval_dataloader: + batch = {k: v.to(device) for k, v in batch.items()} + with torch.no_grad(): + outputs = model(**batch) + + logits = outputs.logits + predictions = torch.argmax(logits, dim=-1) + metric.add_batch(predictions=predictions, references=batch["labels"]) + +metric.compute() +``` + +```python out +{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535} +``` + +同樣,由於模型頭部初始化和數據改組的隨機性,您的結果會略有不同,但它們應該在同一個範圍內。 + + + +✏️ **試試看!** 修改之前的訓練循環以在 SST-2 數據集上微調您的模型。 + + + +### S使用🤗 Accelerate加速您的訓練循環 + + + +我們之前定義的訓練循環在單個 CPU 或 GPU 上運行良好。但是使用[🤗 Accelerate](https://github.com/huggingface/accelerate)庫,只需進行一些調整,我們就可以在多個 GPU 或 TPU 上啟用分佈式訓練。從創建訓練和驗證數據加載器開始,我們的手動訓練循環如下所示: + +```py +from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler + +model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) +optimizer = AdamW(model.parameters(), lr=3e-5) + +device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") +model.to(device) + +num_epochs = 3 +num_training_steps = num_epochs * len(train_dataloader) +lr_scheduler = get_scheduler( + "linear", + optimizer=optimizer, + num_warmup_steps=0, + num_training_steps=num_training_steps, +) + +progress_bar = tqdm(range(num_training_steps)) + +model.train() +for epoch in range(num_epochs): + for batch in train_dataloader: + batch = {k: v.to(device) for k, v in batch.items()} + outputs = model(**batch) + loss = outputs.loss + loss.backward() + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) +``` + +以下是變化: + +```diff ++ from accelerate import Accelerator + from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler + ++ accelerator = Accelerator() + + model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) + optimizer = AdamW(model.parameters(), lr=3e-5) + +- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") +- model.to(device) + ++ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare( ++ train_dataloader, eval_dataloader, model, optimizer ++ ) + + num_epochs = 3 + num_training_steps = num_epochs * len(train_dataloader) + lr_scheduler = get_scheduler( + "linear", + optimizer=optimizer, + num_warmup_steps=0, + num_training_steps=num_training_steps + ) + + progress_bar = tqdm(range(num_training_steps)) + + model.train() + for epoch in range(num_epochs): + for batch in train_dataloader: +- batch = {k: v.to(device) for k, v in batch.items()} + outputs = model(**batch) + loss = outputs.loss +- loss.backward() ++ accelerator.backward(loss) + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) +``` + +要添加的第一行是導入`Accelerator`。第二行實例化一個 `Accelerator`對象 ,它將查看環境並初始化適當的分佈式設置。 🤗 Accelerate 為您處理數據在設備間的傳遞,因此您可以刪除將模型放在設備上的那行代碼(或者,如果您願意,可使用 `accelerator.device` 代替 `device` )。 + +然後大部分工作會在將數據加載器、模型和優化器發送到的`accelerator.prepare()`中完成。這將會把這些對象包裝在適當的容器中,以確保您的分佈式訓練按預期工作。要進行的其餘更改是刪除將`batch`放在 `device` 的那行代碼(同樣,如果您想保留它,您可以將其更改為使用 `accelerator.device` ) 並將 `loss.backward()` 替換為`accelerator.backward(loss)`。 + + +⚠️ 為了使雲端 TPU 提供的加速發揮最大的效益,我們建議使用標記器(tokenizer)的 `padding=max_length` 和 `max_length` 參數將您的樣本填充到固定長度。 + + +如果您想複製並粘貼來直接運行,以下是 🤗 Accelerate 的完整訓練循環: + +```py +from accelerate import Accelerator +from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler + +accelerator = Accelerator() + +model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2) +optimizer = AdamW(model.parameters(), lr=3e-5) + +train_dl, eval_dl, model, optimizer = accelerator.prepare( + train_dataloader, eval_dataloader, model, optimizer +) + +num_epochs = 3 +num_training_steps = num_epochs * len(train_dl) +lr_scheduler = get_scheduler( + "linear", + optimizer=optimizer, + num_warmup_steps=0, + num_training_steps=num_training_steps, +) + +progress_bar = tqdm(range(num_training_steps)) + +model.train() +for epoch in range(num_epochs): + for batch in train_dl: + outputs = model(**batch) + loss = outputs.loss + accelerator.backward(loss) + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) +``` + +把這個放在 `train.py` 文件中,可以讓它在任何類型的分佈式設置上運行。要在分佈式設置中試用它,請運行以下命令: + +```bash +accelerate config +``` + +這將詢問您幾個配置的問題並將您的回答轉儲到此命令使用的配置文件中: + +``` +accelerate launch train.py +``` + +這將啟動分佈式訓練 + +這將啟動分佈式訓練。如果您想在 Notebook 中嘗試此操作(例如,在 Colab 上使用 TPU 進行測試),只需將代碼粘貼到 `training_function()` 並使用以下命令運行最後一個單元格: + +```python +from accelerate import notebook_launcher + +notebook_launcher(training_function) +``` + +您可以在[🤗 Accelerate repo](https://github.com/huggingface/accelerate/tree/main/examples)找到更多的示例。 diff --git a/chapters/zh-TW/chapter3/5.mdx b/chapters/zh-TW/chapter3/5.mdx new file mode 100644 index 000000000..a75f39af7 --- /dev/null +++ b/chapters/zh-TW/chapter3/5.mdx @@ -0,0 +1,25 @@ + + +# 微調,檢查! + + + +這是非常令人高興的! 在前兩章中,您瞭解了模型和標記器(tokenizer),現在您知道如何針對您自己的數據對它們進行微調。回顧一下,在本章中,您: + +{#if fw === 'pt'} +* 瞭解了[Hub](https://huggingface.co/datasets)中的數據集 +* 學習瞭如何加載和預處理數據集,包括使用動態填充和整理器 +* 實現您自己的模型微調和評估 +* 實施了一個較為底層的訓練循環 +* 使用 🤗 Accelerate 輕鬆調整您的訓練循環,使其適用於多個 GPU 或 TPU + +{:else} +* 瞭解了[Hub](https://huggingface.co/datasets)中的數據集 +* 學習瞭如何加載和預處理數據集 +* 學習瞭如何使用 Keras 微調和評估模型 +* 實現了自定義指標 + +{/if} diff --git a/chapters/zh-TW/chapter3/6.mdx b/chapters/zh-TW/chapter3/6.mdx new file mode 100644 index 000000000..9f90c3fc3 --- /dev/null +++ b/chapters/zh-TW/chapter3/6.mdx @@ -0,0 +1,289 @@ + + + + +# End-of-chapter quiz + + + +Test what you learned in this chapter! + +### 1.「情緒」數據集包含標記有情緒的 Twitter 消息。在[ Hub ]( https://huggingface.co/datasets 集線器)中搜索它,然後讀取數據集卡。哪一個不是它的基本情感? + + +### 2.在[ Hub ]( https://huggingface.co/datasets 集線器)中搜索‘ ar _ sarcasm’數據集,它支持哪個任務? + dataset card !" + }, + { + text: "命名實體識別", + explain: "不是這樣的ーー再看看 < a href =’https://huggingface.co/datasets/ar _ sarcasm’> dataset card !" + }, + { + text: "回答問題", + explain: "Alas, this question was not answered correctly. 再試一次!" + } + ]} +/> + +### 3.BERT 模型期望如何處理一對句子? + [ CLS ] 特殊令牌在開始時是必需的,但是這不是唯一的事情!" + }, + { + text: "表示句子1[ SEP ]的符號表示句子2[ SEP ]", + explain: "沒錯!", + correct: true + }, + { + text: "表示句子1[ SEP ]的符號表示句子2", + explain: "開頭需要一個 < code > [ CLS ] 特殊標記,還需要一個 < code > [ SEP ] 特殊標記來分隔兩個句子,但這還不是全部!" + } + ]} +/> + +{#if fw === 'pt'} +### 4.‘ Dataset.map ()’方法的好處是什麼? + + +### 5.什麼是動態填充? + + +### 6.校對函數的用途是什麼? + > DataCollatorWithPadding 。" + }, + { + text: "它把所有的樣品一批一批地放在一起。", + explain: "正確! You can pass the collate function as an argument of a DataLoader. We used the DataCollatorWithPadding function, which pads all items in a batch so they have the same length.", + correct: true + }, + { + text: "它預處理整個數據集。", + explain: "這將是一個預處理函數,而不是校對函數。" + }, + { + text: "它截斷數據集中的序列。", + explain: "校對函數用於處理單個批處理,而不是整個數據集。如果您對截斷感興趣,可以使用 < code > tokenizer 的 < truncate 參數。" + } + ]} +/> + +### 7.當你用一個預先訓練過的語言模型(例如‘ bert-base-uncased’)實例化一個‘ AutoModelForXxx’類,這個類對應於一個不同於它所被訓練的任務時會發生什麼? + Trainer 所做的,而不是 Accelerate 庫。再試一次!", + correct: true + }, + { + text: "丟棄預先訓練好的模型頭部。", + explain: "Something else needs to happen. 再試一次!" + }, + { + text: "沒有,因為模型仍然可以針對不同的任務進行微調。", + explain: "這個經過訓練的模特的頭沒有經過訓練來解決這個問題,所以我們應該丟掉這個頭!" + } + ]} +/> + +### 8.訓練爭論的目的是什麼? + TrainingArguments 。" + }, + { + text: "它只包含用於評估的超參數。", + explain: "In the example, we specified where the model and its checkpoints will be saved. 再試一次!" + }, + { + text: "您可以輕鬆地計算與數據集相關的指標。", + explain: "In the example, we used an evaluation_strategy as well, so this impacts evaluation. 再試一次!" + } + ]} +/> + +### 9.為什麼要使用 Accelerate 庫? +Trainer, not the 🤗 Accelerate library. 再試一次!" + }, + { + text: "它使我們的訓練循環工作在分佈式策略上", + explain: "正確! 隨著加速,你的訓練循環將為多個 gpu 和 TPUs 工作。", + correct: true + }, + { + text: "它提供了更多的優化功能。", + explain: "不,Accelerate 庫不提供任何優化功能。" + } + ]} +/> + +{:else} +### 4.當你用一個預先訓練過的語言模型(例如‘ bert-base-uncased’)實例化一個‘ tfautoodelforxxx’類時,會發生什麼? + + +### 5.來自“變壓器”的 TensorFlow 模型已經是 Keras 模型,這有什麼好處? + TPUStrategy scope 中的所有內容,包括模型的初始化。" + }, + { + text: "您可以利用現有的方法,如 < code > compile () 、 < code > fit () < c/ode > 和 < code > predict () 。", + explain: "正確! 一旦你有了這些數據,在這些數據上進行培訓只需要很少的工作。", + correct: true + }, + { + text: "你可以學習 Keras 和變形金剛。", + explain: "沒錯,但我們要找的是別的東西:)", + correct: true + }, + { + text: "困惑", + explain: "Keras 幫助我們訓練和評估模型,而不是計算與數據集相關的度量。" + } + ]} +/> + +### 6.如何定義自己的定製度量? + tfkeras.metrics. Metric 。", + explain: "太好了!", + correct: true + }, + { + text: "使用 Keras 函數 API。", + explain: "再試一次!" + }, + { + text: "通過使用帶簽名的可調用 < code > metric _ fn (y _ true,y _ pred) 。", + explain: "正確!", + correct: true + }, + { + text: "通過谷歌搜索。", + explain: "這不是我們要找的答案,但它應該能幫助你找到答案。", + correct: true + } + ]} +/> + +{/if} \ No newline at end of file diff --git a/chapters/zh-TW/chapter4/1.mdx b/chapters/zh-TW/chapter4/1.mdx new file mode 100644 index 000000000..97500a0a3 --- /dev/null +++ b/chapters/zh-TW/chapter4/1.mdx @@ -0,0 +1,20 @@ +# The Hugging Face Hub + + + +[Hugging Face Hub](https://huggingface.co/) -- 我們的主網站,是一個中央平臺,在這個網站上任何人都可以查找、使用和貢獻新的最先進的模型和數據集。它擁有各種各樣的模型,公開可用的模型超過 10,000個。我們在本章去探索Hub中的模型,並在第 5 章中探索Hub中的數據集。 + +Hub 中的模型不僅限於 🤗 Transformers 甚至 NLP。有用於自然語言處理的[Flair](https://github.com/flairNLP/flair),[AllenNLP](https://github.com/allenai/allennlp),[Asteroid](https://github.com/asteroid-team/asteroid)和用於音頻檢測的[pyannote](https://github.com/pyannote/pyannote-audio),以及對於視覺的[timm](https://github.com/rwightman/pytorch-image-models),這些例子只是Hub中冰山一角,更多的模型。可以由你去探索。 + +這些模型中的每一個都作為 Git 存儲庫託管,這允許進行版本控制和重現。在 Hub 上共享模型意味著將其向社區開放,讓任何希望使用它的人都可以輕鬆訪問它,從而使其他人不用為訓練模型而苦惱就可以直接使用模型。 + +此外,在 Hub 上共享模型會自動為該模型部署託管的推理 API。社區中的任何人都可以直接在模型頁面上自由地測試它,使用自定義輸入和適當的小部件。 + +最棒的是是在 Hub 上共享和使用任何公共模型是完全免費的!如果您不想公開模型,也存在[付費計劃](https://huggingface.co/pricing)。下面的視頻顯示瞭如何使用 Hub。 + + + +這部分需要有一個 Huggingface.co 帳戶,因為我們將在 Hugging Face Hub 上創建和管理存儲庫:[創建一個賬戶](https://huggingface.co/join) \ No newline at end of file diff --git a/chapters/zh-TW/chapter4/2.mdx b/chapters/zh-TW/chapter4/2.mdx new file mode 100644 index 000000000..66268aeef --- /dev/null +++ b/chapters/zh-TW/chapter4/2.mdx @@ -0,0 +1,97 @@ + + +# 使用預訓練的模型 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +模型中心使選擇合適的模型變得簡單,因此只需幾行代碼即可在任何下游庫中使用它。讓我們來看看如何實際使用這些模型之一,以及如何回饋社區。 + +假設我們正在尋找一種可以執行**mask**填充的French-based模型。 + +
+Selecting the Camembert model. +
+ +我們選擇 **camembert-base** 檢查點來嘗試一下。我們需要做的僅僅是輸入 `camembert-base`標識符!正如您在前幾章中看到的,我們可以使用 **pipeline()** 功能: + +```py +from transformers import pipeline + +camembert_fill_mask = pipeline("fill-mask", model="camembert-base") +results = camembert_fill_mask("Le camembert est :)") +``` + +```python out +[ + {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'}, + {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'}, + {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'}, + {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'}, + {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'} +] +``` + +如您所見,在管道中加載模型非常簡單。您唯一需要注意的是所選檢查點是否適合它將用於的任務。例如,這裡我們正在加載 **camembert-base** 檢查點在 **fill-mask** 管道,這完全沒問題。但是如果我們要在 **text-classification** 管道,結果沒有任何意義,因為 **camembert-base** 不適合這個任務!我們建議使用 Hugging Face Hub 界面中的任務選擇器來選擇合適的檢查點: + +
+The task selector on the web interface. +
+ +您還可以直接使用模型架構實例化檢查點: + +{#if fw === 'pt'} +```py +from transformers import CamembertTokenizer, CamembertForMaskedLM + +tokenizer = CamembertTokenizer.from_pretrained("camembert-base") +model = CamembertForMaskedLM.from_pretrained("camembert-base") +``` + +然而,我們建議使用[Auto* 類](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes),因為Auto* 類設計與架構無關。前面的代碼示例將只能在 CamemBERT 架構中加載可用的檢查點,但使用 **Auto*** 類使切換檢查點變得簡單: + +```py +from transformers import AutoTokenizer, AutoModelForMaskedLM + +tokenizer = AutoTokenizer.from_pretrained("camembert-base") +model = AutoModelForMaskedLM.from_pretrained("camembert-base") +``` +{:else} +```py +from transformers import CamembertTokenizer, TFCamembertForMaskedLM + +tokenizer = CamembertTokenizer.from_pretrained("camembert-base") +model = TFCamembertForMaskedLM.from_pretrained("camembert-base") +``` + +However, we recommend using the [`TFAuto*` classes](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes) instead, as these are by design architecture-agnostic. While the previous code sample limits users to checkpoints loadable in the CamemBERT architecture, using the `TFAuto*` classes makes switching checkpoints simple: +然而,我們建議使用[`TFAuto*` 類](https://huggingface.co/transformers/model_doc/auto.html?highlight=auto#auto-classes),因為`TFAuto*`類設計與架構無關。前面的代碼示例將只能在 CamemBERT 架構中加載可用的檢查點,但使用 `TFAuto*` 類使切換檢查點變得簡單: + +```py +from transformers import AutoTokenizer, TFAutoModelForMaskedLM + +tokenizer = AutoTokenizer.from_pretrained("camembert-base") +model = TFAutoModelForMaskedLM.from_pretrained("camembert-base") +``` +{/if} + + +使用預訓練模型時,一定要檢查它是如何訓練的,在哪些數據集上,它的限制和它的偏差。所有這些信息都應在其模型卡片上註明。 + diff --git a/chapters/zh-TW/chapter4/3.mdx b/chapters/zh-TW/chapter4/3.mdx new file mode 100644 index 000000000..9c324b118 --- /dev/null +++ b/chapters/zh-TW/chapter4/3.mdx @@ -0,0 +1,648 @@ + + +# 共享預訓練模型 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +在下面的步驟中,我們將看看將預訓練模型分享到 🤗 Hub 的最簡單方法。有可用的工具和實用程序可以讓直接在 Hub 上共享和更新模型變得簡單,我們將在下面進行探討。 + + + +我們鼓勵所有訓練模型的用戶通過與社區共享來做出貢獻——共享模型,即使是在非常特定的數據集上進行訓練,也將幫助他人,節省他們的時間和計算資源,並提供對有用的訓練工件的訪問。反過來,您可以從其他人所做的工作中受益! + +創建新模型存儲庫的方法有以下三種: + +- 使用 push_to_hub API 接口 +- 使用 huggingface_hub Python 庫 +- 使用 web 界面 + +創建存儲庫後,您可以通過 git 和 git-lfs 將文件上傳到其中。我們將在以下部分引導您創建模型存儲庫並將文件上傳到它們 + + +## 使用 push_to_hub API + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +將文件上傳到集線器的最簡單方法是利用 **push_to_hub** API 接口。 + +在繼續之前,您需要生成一個身份驗證令牌,以便 **huggingface_hub** API 知道您是誰以及您對哪些名稱空間具有寫入權限。確保你在一個環境中 **transformers** 已安裝(見[Setup](/course/chapter0))。如果您在筆記本中,可以使用以下功能登錄: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +在終端中,您可以運行: + +```bash +huggingface-cli login +``` + +在這兩種情況下,系統都會提示您輸入用戶名和密碼,這與您用於登錄 Hub 的用戶名和密碼相同。如果您還沒有 Hub 配置文件,則應該創建一個[here](https://huggingface.co/join)。 + +好的!您現在已將身份驗證令牌存儲在緩存文件夾中。讓我們創建一些存儲庫! + +{#if fw === 'pt'} + +如果你玩過 **Trainer** 用於訓練模型的 API,將其上傳到 Hub 的最簡單方法是設置 **push_to_hub=True** 當你定義你的 **TrainingArguments** : + +```py +from transformers import TrainingArguments + +training_args = TrainingArguments( + "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True +) +``` + +你聲明 **trainer.train()** 的時候, 這 **Trainer** 然後每次將您的模型保存到您的命名空間中的存儲庫中時(這裡是每個時代),它將上傳到集線器。該存儲庫將命名為您選擇的輸出目錄(此處 **bert-finetuned-mrpc** ) 但您可以選擇不同的名稱 **hub_model_id = a_different_name** 。 + +要將您的模型上傳到您所屬的組織,只需將其傳遞給 **hub_model_id = my_organization/my_repo_name** 。 + +訓練結束後,你應該做最後的 **trainer.push_to_hub()** 上傳模型的最新版本。它還將生成包含所有相關元數據的模型卡,報告使用的超參數和評估結果!以下是您可能會在此類模型卡中找到的內容示例: + +
+ An example of an auto-generated model card. +
+ + +{:else} + + +如果您使用Keras來訓練您的模型,則將其上傳到Hub的最簡單方法是在調用 **model.fit()** 時傳遞**PushToHubCallback**: + +```py +from transformers import PushToHubCallback + +callback = PushToHubCallback( + "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer +) +``` + +然後,您應該在對**model.fit()**的調用中添加**callbacks=[callback]**。然後,每次將模型保存在命名空間的存儲庫中(此處為每個 epoch)時,回調都會將模型上傳到 Hub。該存儲庫的名稱將類似於您選擇的輸出目錄(此處為**bert-finetuned-mrpc**),但您可以選擇另一個名稱,名稱為**hub_model_id = a_different_name**。 + +要將您的模型上傳到您所屬的組織,只需將其傳遞給 **hub_model_id = my_organization/my_repo_name** 。 + +{/if} + +在較低級別,可以通過模型、標記器和配置對象直接訪問模型中心 **push_to_hub()** 方法。此方法負責創建存儲庫並將模型和標記器文件直接推送到存儲庫。與我們將在下面看到的 API 不同,不需要手動處理。 + +為了瞭解它是如何工作的,讓我們首先初始化一個模型和一個標記器: + +{#if fw === 'pt'} + +```py +from transformers import AutoModelForMaskedLM, AutoTokenizer + +checkpoint = "camembert-base" + +model = AutoModelForMaskedLM.from_pretrained(checkpoint) +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +``` + +{:else} + +```py +from transformers import TFAutoModelForMaskedLM, AutoTokenizer + +checkpoint = "camembert-base" + +model = TFAutoModelForMaskedLM.from_pretrained(checkpoint) +tokenizer = AutoTokenizer.from_pretrained(checkpoint) +``` + +{/if} + +你可以自由地用這些做任何你想做的事情——向標記器添加標記,訓練模型,微調它。一旦您對生成的模型、權重和標記器感到滿意,您就可以利用 **push_to_hub()** 方法直接在 **model** 中: + +```py +model.push_to_hub("dummy-model") +``` + +這將創建新的存儲庫 **dummy-model** 在您的個人資料中,並用您的模型文件填充它。 +對標記器執行相同的操作,以便所有文件現在都可以在此存儲庫中使用: + +```py +tokenizer.push_to_hub("dummy-model") +``` + +如果您屬於一個組織,只需指定 **organization** 上傳到該組織的命名空間的參數: + +```py +tokenizer.push_to_hub("dummy-model", organization="huggingface") +``` + +如果您希望使用特定的 Hugging Face 令牌,您可以自由地將其指定給 **push_to_hub()** 方法也是: + +```py +tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="") +``` + +現在前往模型中心找到您新上傳的模型:*https://huggingface.co/user-or-organization/dummy-model*。 + +單擊“文件和版本”選項卡,您應該會在以下屏幕截圖中看到可見的文件: + +{#if fw === 'pt'} +
+Dummy model containing both the tokenizer and model files. +
+{:else} +
+Dummy model containing both the tokenizer and model files. +
+{/if} + + + +✏️ **試試看**!獲取與檢查點關聯的模型和標記器,並使用該方法將它們上傳到您的命名空間中的存儲庫。在刪除之前,請仔細檢查該存儲庫是否正確顯示在您的頁面上。 + + + +如您所見, **push_to_hub()** 方法接受多個參數,從而可以上傳到特定的存儲庫或組織命名空間,或使用不同的 API 令牌。我們建議您查看直接在[🤗 Transformers documentation](https://huggingface.co/transformers/model_sharing.html)瞭解什麼是可能的 + +這 **push_to_hub()** 方法由[huggingface_hub](https://github.com/huggingface/huggingface_hub)Python 包,為 Hugging Face Hub 提供直接 API。它集成在 🤗 Transformers 和其他幾個機器學習庫中,例如[allenlp](https://github.com/allenai/allennlp).雖然我們在本章中專注於 🤗 Transformers 集成,但將其集成到您自己的代碼或庫中很簡單。 + +跳到最後一部分,瞭解如何將文件上傳到新創建的存儲庫! + +## 使用 huggingface_hub python庫 + +這 **huggingface_hub** Python 庫是一個包,它為模型和數據集中心提供了一組工具。它為常見任務提供了簡單的方法和類,例如 +獲取有關集線器上存儲庫的信息並對其進行管理。它提供了在 git 之上工作的簡單 API 來管理這些存儲庫的內容並集成 Hub +在您的項目和庫中。 + +類似於使用 **push_to_hub** API,這將要求您將 API 令牌保存在緩存中。為此,您需要使用 **login** 來自 CLI 的命令,如上一節所述(同樣,確保在這些命令前面加上 **!** 字符(如果在 Google Colab 中運行): + +```bash +huggingface-cli login +``` + +這 **huggingface_hub** 包提供了幾種對我們有用的方法和類。首先,有幾種方法可以管理存儲庫的創建、刪除等: + +```python no-format +from huggingface_hub import ( + # User management + login, + logout, + whoami, + + # Repository creation and management + create_repo, + delete_repo, + update_repo_visibility, + + # And some methods to retrieve/change information about the content + list_models, + list_datasets, + list_metrics, + list_repo_files, + upload_file, + delete_file, +) +``` + + +此外,它還提供了非常強大的 **Repository** 用於管理本地存儲庫的類。我們將在接下來的幾節中探討這些方法和該類,以瞭解如何利用它們。 + +這 **create_repo** 方法可用於在集線器上創建新存儲庫: + + +```py +from huggingface_hub import create_repo + +create_repo("dummy-model") +``` + +這將創建存儲庫 **dummy-model** 在您的命名空間中。如果願意,您可以使用 **organization** 爭論: + +```py +from huggingface_hub import create_repo + +create_repo("dummy-model", organization="huggingface") +``` + +這將創建 **dummy-model** 存儲庫中的 **huggingface** 命名空間,假設您屬於該組織。 +其他可能有用的參數是: + +- private 以指定存儲庫是否應對其他人可見。 +- token 如果您想用給定的令牌覆蓋存儲在緩存中的令牌。 +- repo_type 如果你想創建一個或一個替代一個的而不是模型。接受的值和 datasetspace "dataset""space"。 + +創建存儲庫後,我們應該向其中添加文件!跳到下一部分以查看可以處理此問題的三種方法。 + + +## 使用網絡界面 + +Web 界面提供了直接在 Hub 中管理存儲庫的工具。使用該界面,您可以輕鬆創建存儲庫、添加文件(甚至是大文件!)、探索模型、可視化差異等等。 + +要創建新的存儲庫,請訪問[huggingface.co/new](https://huggingface.co/new): + +
+Page showcasing the model used for the creation of a new model repository. +
+ +首先,指定存儲庫的所有者:這可以是您或您所屬的任何組織。如果您選擇一個組織,該模型將出現在該組織的頁面上,並且該組織的每個成員都可以為存儲庫做出貢獻。 + +接下來,輸入您的模型名稱。這也將是存儲庫的名稱。最後,您可以指定您的模型是公開的還是私有的。私人模特要求您擁有付費 Hugging Face 帳戶,並允許您將模特隱藏在公眾視野之外。 + +創建模型存儲庫後,您應該看到如下頁面: + +
+An empty model page after creating a new repository. +
+ +這是您的模型將被託管的地方。要開始填充它,您可以直接從 Web 界面添加 README 文件。 + +
+The README file showing the Markdown capabilities. +
+ +README 文件在 Markdown 中 - 隨意使用它!本章的第三部分致力於構建模型卡。這些對於為您的模型帶來價值至關重要,因為它們是您告訴其他人它可以做什麼的地方。 + +如果您查看“文件和版本”選項卡,您會發現那裡還沒有很多文件——只有自述文件你剛剛創建和.git 屬性跟蹤大文件的文件。 + +
+The 'Files and versions' tab only shows the .gitattributes and README.md files. +
+ +接下來我們將看看如何添加一些新文件。 + +## 上傳模型文件 + +Hugging Face Hub 上的文件管理系統基於用於常規文件的 git 和 git-lfs(代表[Git Large File Storage](https://git-lfs.github.com/)) 對於較大的文件。 + +在下一節中,我們將介紹將文件上傳到 Hub 的三種不同方式:通過 **huggingface_hub** 並通過 git 命令。 + +### The `upload_file` approach + +使用 **upload_file** 不需要在您的系統上安裝 git 和 git-lfs。它使用 HTTP POST 請求將文件直接推送到 🤗 Hub。這種方法的一個限制是它不能處理大於 5GB 的文件。 +如果您的文件大於 5GB,請按照下面詳述的另外兩種方法進行操作。API 可以按如下方式使用: + +```py +from huggingface_hub import upload_file + +upload_file( + "/config.json", + path_in_repo="config.json", + repo_id="/dummy-model", +) +``` + +這將上傳文件 **config.json** 可在 **path_to_file** 到存儲庫的根目錄 **config.json** , 到 **dummy-model** 存儲庫。 +其他可能有用的參數是: + +- token,如果要通過給定的令牌覆蓋緩存中存儲的令牌。 +- repo_type, 如果你想要上傳一個 `dataset` 或一個 `space` 而不是模型。 接受的值為 `"dataset"` 和 `"space"`. + + +### The `Repository` class + +以類似 git 的方式管理本地存儲庫。它抽象了 git 可能遇到的大部分痛點,以提供我們需要的所有功能。 + +使用這個類需要安裝 git 和 git-lfs,所以確保你已經安裝了 git-lfs(參見[here](https://git-lfs.github.com/)安裝說明)並在開始之前進行設置。 + +為了開始使用我們剛剛創建的存儲庫,我們可以通過克隆遠程存儲庫將其初始化到本地文件夾開始: + +```py +from huggingface_hub import Repository + +repo = Repository("", clone_from="/dummy-model") +``` + +這創建了文件夾 **path_to_dummy_folder** 在我們的工作目錄中。該文件夾僅包含 **.gitattributes** 文件,因為這是通過實例化存儲庫時創建的唯一文件 **create_repo**。 + +從現在開始,我們可以利用幾種傳統的 git 方法: + +```py +repo.git_pull() +repo.git_add() +repo.git_commit() +repo.git_push() +repo.git_tag() +``` + +另外!我們建議您查看 **Repository** 可用文件[here](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management)有關所有可用方法的概述。 + +目前,我們有一個模型和一個標記器,我們希望將其推送到集線器。我們已經成功克隆了存儲庫,因此我們可以將文件保存在該存儲庫中。 + +我們首先通過拉取最新更改來確保我們的本地克隆是最新的: + +```py +repo.git_pull() +``` + +完成後,我們保存模型和標記器文件: + +```py +model.save_pretrained("") +tokenizer.save_pretrained("") +``` + +這 **path_to_dummy_folder** 現在包含所有模型和標記器文件。我們遵循通常的 git 工作流程,將文件添加到暫存區,提交它們並將它們推送到集線器: + +```py +repo.git_add() +repo.git_commit("Add model and tokenizer files") +repo.git_push() +``` + +恭喜!您剛剛將第一個文件推送到hub上。 + +### The git-based approach + +這是上傳文件的非常簡單的方法:我們將直接使用 git 和 git-lfs 來完成。大多數困難都被以前的方法抽象掉了,但是下面的方法有一些警告,所以我們將遵循一個更復雜的用例。 + +使用這個類需要安裝 git 和 git-lfs,所以請確保你有[git-lfs](https://git-lfs.github.com/)安裝(請參閱此處瞭解安裝說明)並在開始之前進行設置。 + +首先從初始化 git-lfs 開始: + +```bash +git lfs install +``` + +```bash +Updated git hooks. +Git LFS initialized. +``` + +完成後,第一步是克隆您的模型存儲庫: + +```bash +git clone https://huggingface.co// +``` + +我的用戶名是 **lysandre** 我使用了模型名稱 **dummy** ,所以對我來說,命令最終如下所示: + +``` +git clone https://huggingface.co/lysandre/dummy +``` + +我現在有一個名為的文件夾假在我的工作目錄中。我能 **cd** 進入文件夾並查看內容: + +```bash +cd dummy && ls +``` + +```bash +README.md +``` + +如果您剛剛使用 Hugging Face Hub 創建了您的存儲庫 **create_repo** 方法,這個文件夾應該只包含一個隱藏的 **.gitattributes** 文件。如果您按照上一節中的說明使用 Web 界面創建存儲庫,則該文件夾應包含一個自述文件文件旁邊的隱藏 **.gitattributes** 文件,如圖所示。 + +添加一個常規大小的文件,例如配置文件、詞彙文件,或者基本上任何幾兆字節以下的文件,就像在任何基於 git 的系統中所做的一樣。但是,更大的文件必須通過 git-lfs 註冊才能將它們推送到擁抱臉。 + +讓我們回到 Python 來生成我們想要提交到我們的虛擬存儲庫的模型和標記器: + +{#if fw === 'pt'} +```py +from transformers import AutoModelForMaskedLM, AutoTokenizer + +checkpoint = "camembert-base" + +model = AutoModelForMaskedLM.from_pretrained(checkpoint) +tokenizer = AutoTokenizer.from_pretrained(checkpoint) + +# Do whatever with the model, train it, fine-tune it... + +model.save_pretrained("") +tokenizer.save_pretrained("") +``` +{:else} +```py +from transformers import TFAutoModelForMaskedLM, AutoTokenizer + +checkpoint = "camembert-base" + +model = TFAutoModelForMaskedLM.from_pretrained(checkpoint) +tokenizer = AutoTokenizer.from_pretrained(checkpoint) + +# Do whatever with the model, train it, fine-tune it... + +model.save_pretrained("") +tokenizer.save_pretrained("") +``` +{/if} + +現在我們已經保存了一些模型和標記器工件,讓我們再看看假文件夾: + +```bash +ls +``` + +{#if fw === 'pt'} +```bash +config.json pytorch_model.bin README.md sentencepiece.bpe.model special_tokens_map.json tokenizer_config.json tokenizer.json +``` + +If you look at the file sizes (for example, with `ls -lh`), you should see that the model state dict file (*pytorch_model.bin*) is the only outlier, at more than 400 MB. + +{:else} +```bash +config.json README.md sentencepiece.bpe.model special_tokens_map.json tf_model.h5 tokenizer_config.json tokenizer.json +``` + +如果您查看文件大小(例如, **ls -lh** ),您應該會看到模型狀態 dict 文件 (pytorch_model.bin) 是唯一的異常值,超過 400 MB。 + +{/if} + + +✏️ 從 web 界面創建存儲庫時,*.gitattributes* 文件會自動設置為將具有某些擴展名的文件,例如 *.bin* 和 *.h5* 視為大文件,git-lfs 會對其進行跟蹤您無需進行必要的設置。 + + +我們現在可以繼續進行,就像我們通常使用傳統 Git 存儲庫一樣。我們可以使用以下命令將所有文件添加到 Git 的暫存環境中 **git add** 命令: + +```bash +git add . +``` + +然後我們可以查看當前暫存的文件: + +```bash +git status +``` + +{#if fw === 'pt'} +```bash +On branch main +Your branch is up to date with 'origin/main'. + +Changes to be committed: + (use "git restore --staged ..." to unstage) + modified: .gitattributes + new file: config.json + new file: pytorch_model.bin + new file: sentencepiece.bpe.model + new file: special_tokens_map.json + new file: tokenizer.json + new file: tokenizer_config.json +``` +{:else} +```bash +On branch main +Your branch is up to date with 'origin/main'. + +Changes to be committed: + (use "git restore --staged ..." to unstage) + modified: .gitattributes + new file: config.json + new file: sentencepiece.bpe.model + new file: special_tokens_map.json + new file: tf_model.h5 + new file: tokenizer.json + new file: tokenizer_config.json +``` +{/if} + +同樣,我們可以確保 git-lfs 使用其跟蹤正確的文件 **status** 命令: + +```bash +git lfs status +``` + +{#if fw === 'pt'} +```bash +On branch main +Objects to be pushed to origin/main: + + +Objects to be committed: + + config.json (Git: bc20ff2) + pytorch_model.bin (LFS: 35686c2) + sentencepiece.bpe.model (LFS: 988bc5a) + special_tokens_map.json (Git: cb23931) + tokenizer.json (Git: 851ff3e) + tokenizer_config.json (Git: f0f7783) + +Objects not staged for commit: + + +``` + +我們可以看到所有文件都有 **Git** 作為處理程序,除了其中有 **LFS**的*pytorch_model.bin* 和 *sentencepiece.bpe.model*。 + +{:else} +```bash +On branch main +Objects to be pushed to origin/main: + + +Objects to be committed: + + config.json (Git: bc20ff2) + sentencepiece.bpe.model (LFS: 988bc5a) + special_tokens_map.json (Git: cb23931) + tf_model.h5 (LFS: 86fce29) + tokenizer.json (Git: 851ff3e) + tokenizer_config.json (Git: f0f7783) + +Objects not staged for commit: + + +``` + +我們可以看到所有文件都有 **Git** 作為處理程序,除了其中有 **LFS**的*t5_model.h5*。 + +{/if} + +Let's proceed to the final steps, committing and pushing to 讓我們繼續最後的步驟,提交併推動擁抱臉遠程倉庫: + +```bash +git commit -m "First model version" +``` + +{#if fw === 'pt'} +```bash +[main b08aab1] First model version + 7 files changed, 29027 insertions(+) + 6 files changed, 36 insertions(+) + create mode 100644 config.json + create mode 100644 pytorch_model.bin + create mode 100644 sentencepiece.bpe.model + create mode 100644 special_tokens_map.json + create mode 100644 tokenizer.json + create mode 100644 tokenizer_config.json +``` +{:else} +```bash +[main b08aab1] First model version + 6 files changed, 36 insertions(+) + create mode 100644 config.json + create mode 100644 sentencepiece.bpe.model + create mode 100644 special_tokens_map.json + create mode 100644 tf_model.h5 + create mode 100644 tokenizer.json + create mode 100644 tokenizer_config.json +``` +{/if} + +推送可能需要一些時間,具體取決於您的互聯網連接速度和文件大小: + +```bash +git push +``` + +```bash +Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done. +Enumerating objects: 11, done. +Counting objects: 100% (11/11), done. +Delta compression using up to 12 threads +Compressing objects: 100% (9/9), done. +Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done. +Total 9 (delta 1), reused 0 (delta 0), pack-reused 0 +To https://huggingface.co/lysandre/dummy + 891b41d..b08aab1 main -> main +``` + +{#if fw === 'pt'} +If we take a look at the model repository when this is finished, we can see all the recently added files: + +
+The 'Files and versions' tab now contains all the recently uploaded files. +
+ +UI 允許您瀏覽模型文件和提交,並查看每個提交引入的差異: + +
+The diff introduced by the recent commit. +
+ +{:else} + +如果我們在完成後查看模型存儲庫,我們可以看到所有最近添加的文件: + +
+The 'Files and versions' tab now contains all the recently uploaded files. +
+ +UI 允許您瀏覽模型文件和提交,並查看每個提交引入的差異: + +
+The diff introduced by the recent commit. +
+{/if} diff --git a/chapters/zh-TW/chapter4/4.mdx b/chapters/zh-TW/chapter4/4.mdx new file mode 100644 index 000000000..b5eecf1a8 --- /dev/null +++ b/chapters/zh-TW/chapter4/4.mdx @@ -0,0 +1,87 @@ +# 構建模型卡片 + + + +模型卡片是一個配置文件,可以說與模型存儲庫中的模型和 tokenizer 文件一樣重要。它包含了模型的核心定義,確保了社區成員可以復現模型的結果,並提供一個其他成員可以在這個模型基礎上構建他們的組件的平臺。 + +記錄訓練和評估過程並提供有關使用的數據以及已完成的預處理和後續處理的足夠信息,有助於其他人瞭解對模型的能力——確保模型存在和目前的限制、偏差可以識別和理解。 + +因此,創建清晰定義模型的模型卡片是非常重要的一步。在這裡,我們提供了一些可以幫助您解決此問題的方法。創建模型卡片是通過您之前看到的 Markdown 文件:README.md 。 + +「模型卡片」的概念源於谷歌的一個研究方向, Margaret Mitchell 等人在論文[“Model Cards for Model Reporting”](https://arxiv.org/abs/1810.03993)中首次提出,此處包含的許多信息均基於該論文,我們建議您查看這篇論文以瞭解為什麼模型卡片在重視可重複性、可重用性和公平性的時候中如此重要。 + +模型卡通常以非常簡短的概述開始,說明模型的用途,然後是模型卡片需要的其他信息: + +- 模型描述 +- 預期用途和限制 +- 如何使用 +- 侷限性和偏見 +- 訓練數據 +- 訓練程序 +- 評價結果 + +讓我們來看看每個部分應該包含什麼。 + +### 模型描述: + +提供了有關模型的基本詳細信息。這包括架構、版本、如果它是在論文中介紹的,是否有原始的實現可用?作者以及有關模型的一般信息、任何版權都應歸於此處。這一部分還可以提及有關訓練程序、參數和重要免責聲明的一般信息。 + +### 預期用途和限制: + +在此描述模型可以適用的例子,包括可以應用它的語言、領域。模型卡的這一部分還可以記錄已知超出模型範圍的區域,或者可能表現不佳的區域。 + +### 使用方法: + +此部分應包括一些有關如何使用模型的示例。這可以展示使用 **pipeline()** 函數、模型和標記器類的使用以及其他任何您認為可能有幫助的代碼。 + +### 訓練數據: + +這部分應該指出模型是在哪個數據集上訓練的。也歡迎對數據集進行簡要描述。 + +### 訓練過程: + +此部分中,您應該描述從再現性角度來看有用的訓練的所有相關方面。這包括對數據進行的任何預處理和後處理,以及模型訓練的批量數、批量大小、學習率等細節。 + +### 變量和指標: + +在這裡,您應該描述您用於評估的指標,以及您測量的不同因素。提及使用了哪些指標、在哪個數據集上以及哪個數據集部分,可以輕鬆地將您的模型的性能與其他模型的性能進行比較。 + +### 評價結果: + +這些應該提前在前面的部分告知,例如預期的使用效果和示例。最後,提供模型在評估數據集上的表現的指示。如果模型使用決策閾值,要麼提供評估中使用的決策閾值,要麼提供在不同閾值下針對預期用途進行評估的詳細信息。 + +## 例子 + +查看以下幾個精心製作的模型卡的例子: + +* [bert-base-cased](https://huggingface.co/bert-base-cased) +* [gpt2](https://huggingface.co/gpt2) +* [distilbert](https://huggingface.co/distilbert-base-uncased) + +更多來自於不同組織和公司的示例可以在[這裡](https://github.com/huggingface/model_card/blob/master/examples.md)查閱. + +## 提示 + +發佈模型時不需要模型卡,製作一個模型時不需要包含上述所有部分。但是,模型的文檔會使未來的用戶受益,因此我們建議您儘自己的知識和能力填寫儘可能多的部分。 + +## 模型卡片元數據 + +如果您對 Hugging Face Hub 進行了一些探索,您應該已經看到某些模型屬於某些類別:您可以按任務、語言、庫等對其進行過濾。模型所屬的類別來自於您在模型卡片標題中添加的元數據。 + +例如,如果你看一下[`camembert-base` 模型卡片](https://huggingface.co/camembert-base/blob/main/README.md),您應該在模型卡標題中看到以下幾行: + +``` +--- +language: fr +license: mit +datasets: +- oscar +--- +``` + +該元數據由 Hugging Face Hub 解析,然後將這個模型識別為法語模型,擁有 MIT 許可證,在 Oscar 數據集上訓練。 + +允許的指定語言、許可證、標籤、數據集、指標以及模型在訓練時獲得的評估結果在[全部模型卡片的規格](https://raw.githubusercontent.com/huggingface/huggingface_hub/main/modelcard.md)可以查閱。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter4/5.mdx b/chapters/zh-TW/chapter4/5.mdx new file mode 100644 index 000000000..c783f71f2 --- /dev/null +++ b/chapters/zh-TW/chapter4/5.mdx @@ -0,0 +1,12 @@ +# Part 1 完結! + + + +這是課程第一部分的結尾!第 2 部分將在 11 月 15 日與大型社區活動一起發佈,[點擊這裡](https://huggingface.co/blog/course-launch-event)查看更多信息. + +您現在應該能夠針對文本分類問題(單個或成對句子)對預訓練模型進行微調,並將結果上傳到模型中心。為確保您掌握了第一部分的內容,您應該針對您感興趣的想法進行嘗試(不一定是英語)!一旦你完成,您可以在[Hugging Face 社區](https://discuss.huggingface.co/)的[這個話題](https://discuss.huggingface.co/t/share-your-projects/6803)分享您的項目。 + +我們迫不及待地想看看您將用它構建什麼! \ No newline at end of file diff --git a/chapters/zh-TW/chapter4/6.mdx b/chapters/zh-TW/chapter4/6.mdx new file mode 100644 index 000000000..22486196c --- /dev/null +++ b/chapters/zh-TW/chapter4/6.mdx @@ -0,0 +1,220 @@ + + + + +# 章末小測試 + + + +讓我們測試一下你在本章所學的知識! + +### 1. Hub 上的模型有什麼限制? + + +### 2.如何管理Hub上的模型? + + +### 3.你能使用Hugging Face Hub網頁接口做什麼? + + +### 4.模型卡是什麼? + + +### 5.哪些🤗 Transformers 庫的對象可以直接在 Hub 上通過push _ to _ Hub ()共享? +{#if fw === 'pt'} + +{:else} + +{/if} + +### 6.當使用push _ to _ hub ()方法或 CLI 工具時,第一步是什麼? + + +### 7.您正在使用一個模型和一個標記器————如何將它們上傳到 Hub? + huggingface _ hub 實用程序: 不需要額外的包裝!" + }, + { + text: "將它們保存到磁盤並調用 < code > transformers-cli upload-model ", + explain: "命令 < code > upload-model 不存在。" + } + ]} +/> + +### 8.您可以使用'Repository'類執行哪些 git 操作? +git _ commit () 方法就是為此而存在的。", + correct: true + }, + { + text: "拉一下", + explain: "這就是 < code > git _ pull () 方法的目的。", + correct: true + }, + { + text: "推一下", + explain: "方法 < code > git _ push () 可以做到這一點。", + correct: true + }, + { + text: "合併", + explain: "不,這個操作在這個 API 中是不可能的。" + } + ]} +/> diff --git a/chapters/zh-TW/chapter5/1.mdx b/chapters/zh-TW/chapter5/1.mdx new file mode 100644 index 000000000..fefee8e16 --- /dev/null +++ b/chapters/zh-TW/chapter5/1.mdx @@ -0,0 +1,22 @@ +# 本章簡介 + + + +在[第三章](/course/chapter3)第一次體驗了 🤗Datasets 庫,並發現在微調模型時有三個主要步驟: + +1. 從 Hugging Face Hub 加載一個數據集。 +2. 使用 Dataset.map() 對數據進行預處理。 +3. 載入和計算指標(特徵)。 + +但這只是🤗 Datasets的表面功能而已!在本章中,我們將深入瞭解這個庫。在此過程中,我們將找到以下問題的答案: + +* 當數據集不在 hub 上時,您該怎麼做? +* 如何對數據集進行切片?(如果你真正的特別需要使用pandas的時候該怎麼辦?) +* 當你的數據集很大,會撐爆你筆記本電腦的RAM時,你會怎麼做? +* 「內存映射」和 Apache Arrow 到底是什麼? +* 如何創建自己的數據集並將其推送到中心? + +您在這裡學到的技術將為您在[第6章](/course/chapter6)和[第7章](/course/chapter7)中的高級標記化和微調任務做好準備——所以,喝杯咖啡,讓我們開始吧! \ No newline at end of file diff --git a/chapters/zh-TW/chapter5/2.mdx b/chapters/zh-TW/chapter5/2.mdx new file mode 100644 index 000000000..f47239575 --- /dev/null +++ b/chapters/zh-TW/chapter5/2.mdx @@ -0,0 +1,167 @@ +# 如果我的數據集不在 Hub 上怎麼辦? + + + +你知道如何使用[Hugging Face Hub](https://huggingface.co/datasets)下載數據集, 但你經常會發現自己正在處理存儲在筆記本電腦或遠程服務器上的數據。在本節中,我們將向您展示如何使用 🤗 Datasets來加載 Hugging Face Hub 上不可用的數據集。 + + + +## 使用本地和遠程數據集 + +🤗 Datasets 提供了加載腳本來加載本地和遠程數據集。它支持幾種常見的數據格式,例如: + +| Data format | Loading script | Example | +| :----------------: | :------------: | :-----------------------------------------------------: | +| CSV & TSV | `csv` | `load_dataset("csv", data_files="my_file.csv")` | +| Text files | `text` | `load_dataset("text", data_files="my_file.txt")` | +| JSON & JSON Lines | `json` | `load_dataset("json", data_files="my_file.jsonl")` | +| Pickled DataFrames | `pandas` | `load_dataset("pandas", data_files="my_dataframe.pkl")` | + +如表所示, 對於每種數據格式, 我們只需要使用 `load_dataset()` 函數, 使用 `data_files` 指定一個或多個文件的路徑的參數。 讓我們從本地文件加載數據集開始;稍後我們將看到如何對遠程文件執行相同的操作。 + +## 加載本地數據集 + +對於這個例子,我們將使用 [SQuAD-it dataset](https://github.com/crux82/squad-it/), 這是一個大規模的意大利語問答數據集。 + +訓練和測試都託管在 GitHub 上, 因此我們可以通過`wget`命令非常簡單地下載它們: + +```python +!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz +!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz +``` + +這將下載兩個名為*SQuAD_it-train.json.gz* 和 *SQuAD_it-test.json.gz*的壓縮文件, 我們可以用Linux的解壓命令 `gzip`: + +```python +!gzip -dkv SQuAD_it-*.json.gz +``` + +```bash +SQuAD_it-test.json.gz: 87.4% -- replaced with SQuAD_it-test.json +SQuAD_it-train.json.gz: 82.2% -- replaced with SQuAD_it-train.json +``` + +我們可以看到壓縮文件已經被替換為SQuAD_it-train.json和SQuAD_it-text.json,並且數據以 JSON 格式存儲。 + + + +✎ 如果你想知道為什麼上面的shell命令中喲與一個字符`!`,那是因為我們是在 Jupyter notebook 中運行它們。如果您想在終端中下載和解壓縮數據集,只需刪除前綴!即可。 + + + +使用`load_dataset()`函數來加載JSON文件, 我們只需要知道我們是在處理普通的 JSON(類似於嵌套字典)還是 JSON 行(行分隔的 JSON)。像許多問答數據集一樣, SQuAD-it 使用嵌套格式,所有文本都存儲在 `data`文件中。這意味著我們可以通過指定參數`field`來加載數據集,如下所示: + +```py +from datasets import load_dataset + +squad_it_dataset = load_dataset("json", data_files="SQuAD_it-train.json", field="data") +``` + +默認情況下, 加載本地文件會創建一個帶有`train`的`DatasetDict` 對象。 我們可以通過 `squad_it_dataset`查看: + +```py +squad_it_dataset +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['title', 'paragraphs'], + num_rows: 442 + }) +}) +``` + +這向我們顯示了與訓練集相關聯的行數和列名。我們可以通過索引到 `train` 查看示例,如下所示: + +```py +squad_it_dataset["train"][0] +``` + +```python out +{ + "title": "Terremoto del Sichuan del 2008", + "paragraphs": [ + { + "context": "Il terremoto del Sichuan del 2008 o il terremoto...", + "qas": [ + { + "answers": [{"answer_start": 29, "text": "2008"}], + "id": "56cdca7862d2951400fa6826", + "question": "In quale anno si è verificato il terremoto nel Sichuan?", + }, + ... + ], + }, + ... + ], +} +``` + +很好, 我們已經加載了我們的第一個本地數據集! 但是, 雖然這對訓練集有效, 但是我們真正想要的是包括 `train` 和 `test` 的 `DatasetDict` 對象。這樣的話就可以使用 `Dataset.map()` 函數同時處理訓練集和測試集。 為此, 我們提供參數`data_files`的字典,將每個分割名稱映射到與該分割相關聯的文件: + +```py +data_files = {"train": "SQuAD_it-train.json", "test": "SQuAD_it-test.json"} +squad_it_dataset = load_dataset("json", data_files=data_files, field="data") +squad_it_dataset +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['title', 'paragraphs'], + num_rows: 442 + }) + test: Dataset({ + features: ['title', 'paragraphs'], + num_rows: 48 + }) +}) +``` + +這正是我們想要的。現在, 現在,我們可以應用各種預處理技術來清理數據、標記評論等。 + + + +`load_dataset()`函數的`data_files`參數非常靈活並且可以是單個文件路徑、文件路徑列表或將分割後的名稱映射到文件路徑的字典。您還可以根據Unix shell使用的規則對與指定模式匹配的文件進行全局定位(例如,您可以通過設置'data_files=“*.JSON”'將目錄中的所有JSON文件作為單個拆分進行全局定位)。有關更多詳細信息,請參閱🤗Datasets 文檔。 + + + +🤗 Datasets實際上支持輸入文件的自動解壓,所以我們可以跳過使用`gzip`,直接設置 `data_files`參數傳遞壓縮文件: + +```py +data_files = {"train": "SQuAD_it-train.json.gz", "test": "SQuAD_it-test.json.gz"} +squad_it_dataset = load_dataset("json", data_files=data_files, field="data") +``` + +如果您不想手動解壓縮許多 GZIP 文件,這會很有用。自動解壓也適用於其他常見格式,如 ZIP 和 TAR,因此您只需將 `data_files` 設置為壓縮文件所在的路徑,你就可以開始了! + +現在你知道如何在筆記本電腦或臺式機上加載本地文件,讓我們來看看加載遠程文件。 + +## 加載遠程數據集 + +如果你在公司擔任數據研究員或編碼員,那麼你要分析的數據集很有可能存儲在某個遠程服務器上。幸運的是,加載遠程文件就像加載本地文件一樣簡單!我們沒有提供本地文件的路徑, 而是將`load_dataset()`的`data_files`參數指向存儲遠程文件的一個或多個URL。例如, 對於託管在 GitHub 上的 SQuAD-it 數據集, 我們可以將 `data_files` 指向 _SQuAD_it-*.json.gz_ 的網址,如下所示: + +```py +url = "https://github.com/crux82/squad-it/raw/master/" +data_files = { + "train": url + "SQuAD_it-train.json.gz", + "test": url + "SQuAD_it-test.json.gz", +} +squad_it_dataset = load_dataset("json", data_files=data_files, field="data") +``` + +這將返回和上面的本地例子相同的 `DatasetDict` 對象, 但省去了我們手動下載和解壓 _SQuAD_it-*.json.gz_ 文件的步驟。這是我們對加載未託管在Hugging Face Hub的數據集的各種方法的總結。既然我們已經有了一個可以使用的數據集,讓我們開始大展身手吧! + + + +✏️ **試試看!** 選擇託管在GitHub或[UCI Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php)上的另一個數據集並嘗試使用上述技術在本地和遠程加載它。另外,可以嘗試加載CSV或者文本格式存儲的數據集(有關這些格式的更多信息,請參閱[文檔](https://huggingface.co/docs/datasets/loading.html#local-and-remote-files))。 + + + + diff --git a/chapters/zh-TW/chapter5/3.mdx b/chapters/zh-TW/chapter5/3.mdx new file mode 100644 index 000000000..2acf48b01 --- /dev/null +++ b/chapters/zh-TW/chapter5/3.mdx @@ -0,0 +1,743 @@ +# 是時候來學一下切片了 + + + +大多數情況下,您使用的數據都需根據模型所要求的輸入進行清洗。在本節中,我們將探索 🤗 Datasets 提供的用於數據集清洗的各種功能。 + + + +## 切片與切分我們的數據 + +與 Pandas 類似,🤗 Datasets 提供了幾個函數來操作 **Dataset** 和 **DatasetDict** 對象。我們在[第三章](/course/chapter3)已經遇到了 **Dataset.map()** 方法,在本節中,我們將探索我們可以使用的其他功能。 + +對於這個例子,我們將使用託管在[加州大學歐文分校機器學習存儲庫](https://archive.ics.uci.edu/ml/index.php)的[藥物審查數據集](https://archive.ics.uci.edu/ml/datasets/Drug+Review+Dataset+%28Drugs.com%29),其中包含患者對各種藥物的評論,以及正在治療的病情和患者滿意度的 10 星評級。 + +首先我們需要下載並提取數據,這可以通過 **wget** 和 **unzip** 命令: + +```py +!wget "https://archive.ics.uci.edu/ml/machine-learning-databases/00462/drugsCom_raw.zip" +!unzip drugsCom_raw.zip +``` + +由於 TSV 只是使用製表符而不是逗號作為分隔符的 CSV 變體,我們可以使用加載**csv**文件的**load_dataset()**函數並指定分隔符 示例如下: + +```py +from datasets import load_dataset + +data_files = {"train": "drugsComTrain_raw.tsv", "test": "drugsComTest_raw.tsv"} +# \t is the tab character in Python +drug_dataset = load_dataset("csv", data_files=data_files, delimiter="\t") +``` + +在進行任何類型的數據分析時,一個好的做法是抽取一個小的隨機樣本,以快速瞭解您正在處理的數據類型。在🤗數據集中,我們可以通過鏈接 **Dataset.shuffle()** 和 **Dataset.select()** 共同來完成抽取: + +```py +drug_sample = drug_dataset["train"].shuffle(seed=42).select(range(1000)) +# Peek at the first few examples +drug_sample[:3] +``` + +```python out +{'Unnamed: 0': [87571, 178045, 80482], + 'drugName': ['Naproxen', 'Duloxetine', 'Mobic'], + 'condition': ['Gout, Acute', 'ibromyalgia', 'Inflammatory Conditions'], + 'review': ['"like the previous person mention, I'm a strong believer of aleve, it works faster for my gout than the prescription meds I take. No more going to the doctor for refills.....Aleve works!"', + '"I have taken Cymbalta for about a year and a half for fibromyalgia pain. It is great\r\nas a pain reducer and an anti-depressant, however, the side effects outweighed \r\nany benefit I got from it. I had trouble with restlessness, being tired constantly,\r\ndizziness, dry mouth, numbness and tingling in my feet, and horrible sweating. I am\r\nbeing weaned off of it now. Went from 60 mg to 30mg and now to 15 mg. I will be\r\noff completely in about a week. The fibro pain is coming back, but I would rather deal with it than the side effects."', + '"I have been taking Mobic for over a year with no side effects other than an elevated blood pressure. I had severe knee and ankle pain which completely went away after taking Mobic. I attempted to stop the medication however pain returned after a few days."'], + 'rating': [9.0, 3.0, 10.0], + 'date': ['September 2, 2015', 'November 7, 2011', 'June 5, 2013'], + 'usefulCount': [36, 13, 128]} +``` + +請注意,出於可以復現的目的,我們已將在**Dataset.shuffle()**選取了固定的隨機數種子。 **Dataset.select()** 需要一個可迭代的索引,所以我們已經通過了 **range(1000)** 從隨機打亂的數據集中選取前 1,000 個示例。從抽取的數據中,我們已經可以看到我們數據集的一些特點: + +* **Unnamed: 0**這列看起來很像每個患者的匿名 ID。 +* **condition** 這列包含有描述健康狀況的標籤。 +* 評論長短不一,混合有 Python 行分隔符 (**\r\n**) 以及 HTML 字符代碼,如** &\#039;**。 + +讓我們看看我們如何使用 🤗 Datasets 來處理這些問題。為了驗證**Unnamed: 0** 列存儲的是患者 ID的猜想,我們可以使用 **Dataset.unique()** 函數來驗證匿名ID 的數量是否與拆分後每部分中的行數匹配: + +```py +for split in drug_dataset.keys(): + assert len(drug_dataset[split]) == len(drug_dataset[split].unique("Unnamed: 0")) +``` + +這似乎證實了我們的假設,所以讓我們把 **Unnamed: 0** 列重命名為患者的id。我們可以使用 **DatasetDict.rename_column()**函數一次性重命名DatasetDict中共有的列: + +```py +drug_dataset = drug_dataset.rename_column( + original_column_name="Unnamed: 0", new_column_name="patient_id" +) +drug_dataset +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'], + num_rows: 161297 + }) + test: Dataset({ + features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'], + num_rows: 53766 + }) +}) +``` + + + +✏️ **試試看!** 使用 `Dataset.unique()` 函數查找訓練和測試集中滿足某個條件的藥物經過去重之後的數量。 + + + +接下來,讓我們使用 **Dataset.map()**標準化所有 **condition** 標籤 .正如我們在[第三章](/course/chapter3)中所做的那樣,我們可以定義一個簡單的函數,可以將該函數應用於**drug_dataset** 拆分後每部分的所有行: + +```py +def lowercase_condition(example): + return {"condition": example["condition"].lower()} + + +drug_dataset.map(lowercase_condition) +``` + +```python out +AttributeError: 'NoneType' object has no attribute 'lower' +``` + +哦不,我們的map功能遇到了問題!從錯誤中我們可以推斷出 **condition** 列存在 **None** , 不能轉換為小寫,因為它們不是字符串。讓我們使用 **Dataset.filter()** 刪除這些行 ,其工作方式類似於 **Dataset.map()** 。例如: + +```py +def filter_nones(x): + return x["condition"] is not None +``` + +然後運行 **drug_dataset.filter(filter_nones)** ,我們可以在一行中使用lambda 函數.在 Python 中,lambda 函數是您無需明確命名即可使用的微函數(匿名函數)。它們一般採用如下形式: + +``` +lambda : +``` + +其中**lambda** 是 Python 的特殊[關鍵字](https://docs.python.org/3/reference/lexical_analysis.html#keywords), **arguments** 是以逗號進行分隔的函數輸入的列表/集合, **expression** 代表您希望執行的操作。例如,我們可以定義一個簡單的 lambda 函數來對一個數字進行平方,如下所示: + +``` +lambda x : x * x +``` + +我們需要將要輸入給這個函數值括在括號中: + +```py +(lambda x: x * x)(3) +``` + +```python out +9 +``` + +類似地,我們可以通過用逗號分隔多個參數來定義 lambda 函數。例如,我們可以按如下方式計算三角形的面積: + +```py +(lambda base, height: 0.5 * base * height)(4, 8) +``` + +```python out +16.0 +``` + +當您想定義小型、一次性使用的函數時,Lambda 函數非常方便(有關它們的更多信息,我們建議閱讀安德烈·布爾高寫的[真正的Python教程](https://realpython.com/python-lambda/))。在🤗 Datasets 中,我們可以使用 lambda 函數來定義簡單的映射和過濾操作,所以讓我們使用這個技巧來消除我們數據集中的 **None** 條目: + +```py +drug_dataset = drug_dataset.filter(lambda x: x["condition"] is not None) +``` + +當 **None** 條目已刪除,我們可以標準化我們的 **condition** 列: + +```py +drug_dataset = drug_dataset.map(lowercase_condition) +# Check that lowercasing worked +drug_dataset["train"]["condition"][:3] +``` + +```python out +['left ventricular dysfunction', 'adhd', 'birth control'] +``` + +有用!現在我們已經清理了標籤,讓我們來看看清洗後的評論文本。 + +## Creating new columns + +每當您處理客戶評論時,一個好的做法是檢查每個評論中的字數。評論可能只是一個詞,比如“太棒了!”或包含數千字的完整文章,根據實際的情況,您需要以不同的方式處理這些極端情況。為了計算每條評論中的單詞數,我們將使用基於空格分割每個文本的粗略方法。 + +讓我們定義一個簡單的函數來計算每條評論中的單詞數: + +```py +def compute_review_length(example): + return {"review_length": len(example["review"].split())} +``` + +與我們的 `lowercase_condition()` 函數不同,`compute_review_length()` 返回一個字典,其鍵與數據集中的列名之一不對應。 在這種情況下,當 `compute_review_length()` 傳遞給 `Dataset.map()` 時,它將應用於數據集中的所有行以創建新的 `review_length` 列: + +```py +drug_dataset = drug_dataset.map(compute_review_length) +# Inspect the first training example +drug_dataset["train"][0] +``` + +```python out +{'patient_id': 206461, + 'drugName': 'Valsartan', + 'condition': 'left ventricular dysfunction', + 'review': '"It has no side effect, I take it in combination of Bystolic 5 Mg and Fish Oil"', + 'rating': 9.0, + 'date': 'May 20, 2012', + 'usefulCount': 27, + 'review_length': 17} +``` + +正如預期的那樣,我們可以看到一個 **review_length** 列已添加到我們的訓練集中。我們可以使用 **Dataset.sort()**對這個新列進行排序,然後查看極端長度的評論的樣子: + +```py +drug_dataset["train"].sort("review_length")[:3] +``` + +```python out +{'patient_id': [103488, 23627, 20558], + 'drugName': ['Loestrin 21 1 / 20', 'Chlorzoxazone', 'Nucynta'], + 'condition': ['birth control', 'muscle spasm', 'pain'], + 'review': ['"Excellent."', '"useless"', '"ok"'], + 'rating': [10.0, 1.0, 6.0], + 'date': ['November 4, 2008', 'March 24, 2017', 'August 20, 2016'], + 'usefulCount': [5, 2, 10], + 'review_length': [1, 1, 1]} +``` + +正如我們所猜想的那樣,一些評論只包含一個詞,雖然這對於情感分析來說可能沒問題,但如果我們想要預測病情,這些評論可能並不適合。 + + + +🙋向數據集添加新列的另一種方法是使用函數Dataset.add_column() 。這允許您輸入Python 列表或 NumPy,在不適合使用Dataset.map()情況下可以很方便。 + + + +讓我們使用 **Dataset.filter()** 功能來刪除包含少於 30 個單詞的評論。與我們對 **condition** 列的處理相似,我們可以通過選取評論的長度高於此閾值來過濾掉非常短的評論: + +```py +drug_dataset = drug_dataset.filter(lambda x: x["review_length"] > 30) +print(drug_dataset.num_rows) +``` + +```python out +{'train': 138514, 'test': 46108} +``` + +如您所見,這已經從我們的原始訓練和測試集中刪除了大約 15% 的評論。 + + + +✏️ 試試看!使用 Dataset.sort() 函數查看單詞數最多的評論。請參閱文檔以瞭解您需要使用哪個參數按長度降序對評論進行排序。 + + + +我們需要處理的最後一件事是評論中是否存在 HTML 字符代碼。我們可以使用 Python 的**html**模塊取消這些字符的轉義,如下所示: + +```py +import html + +text = "I'm a transformer called BERT" +html.unescape(text) +``` + +```python out +"I'm a transformer called BERT" +``` + +我們將使用 **Dataset.map()** 對我們語料庫中的所有 HTML 字符進行轉義: + +```python +drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])}) +``` + +如您所見, **Dataset.map()** 方法對於處理數據非常有用——在示例中僅僅是淺嘗輒止就有很大的收穫! + +## map() 方法的超級加速 + +**Dataset.map()** 方法有一個 **batched** 參數,如果設置為 **True** , map 函數將會分批執行所需要進行的操作(批量大小是可配置的,但默認為 1,000)。例如,之前對所有 HTML 進行轉義的 map 函數運行需要一些時間(您可以從進度條中讀取所用時間)。我們可以通過使用列表推導同時處理多個元素來加快速度。 + +當您在使用 **Dataset.map()**函數時指定 **batched=True**。該函數會接收一個包含數據集字段的字典,每個值都是一個列表,而不僅僅是單個值。**Dataset.map()** 的返回值應該是相同的:一個包含我們想要更新或添加到數據集中的字段的字典,字典的鍵是要添加的字段,字典的值是結果的列表。例如,這是使用 **batched=True**對所有 HTML 字符進行轉義的方法 : + +```python +new_drug_dataset = drug_dataset.map( + lambda x: {"review": [html.unescape(o) for o in x["review"]]}, batched=True +) +``` + +如果您在筆記本中運行此代碼,您會看到此命令的執行速度比前一個命令快得多。這不是因為我們的評論已經是處理過的——如果你重新執行上一節的指令(沒有 **batched=True** ),它將花費與以前相同的時間。這是因為列表推導式通常比在同一代碼中用 **for** 循環執行相同的代碼更快,並且我們還通過同時訪問多個元素而不是一個一個來處理來提高處理的速度。 + +在[第六章](/course/chapter6)我們將遇到的“快速”標記器,它可以快速標記大文本列表。使用 **Dataset.map()** 和 **batched=True** 是加速的關鍵。例如,要使用快速標記器標記所有藥物評論,我們可以使用這樣的函數: + +```python +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") + + +def tokenize_function(examples): + return tokenizer(examples["review"], truncation=True) +``` + +正如你在[第三章](/course/chapter3)所看到的,我們原本就可以將一個或多個示例傳遞給分詞器,因此在**batched=True**是一個非必須的選項.讓我們藉此機會比較不同選項的性能。在筆記本中,您可以在您要測量的代碼行之前添加 **%time**來測試改行運行所消耗的時間: + +```python no-format +%time tokenized_dataset = drug_dataset.map(tokenize_function, batched=True) +``` + +您還可以通過將整個單元格計時 **%%time** 在單元格的開頭。在我們執行此操作的硬件上,該指令顯示 10.8 秒(這是寫在“Wall time”之後的數字)。 + + + +✏️ **試試看!** 使用和不使用 `batched=True` 執行相同的指令,然後使用慢速標記器嘗試(在 `AutoTokenizer.from_pretrained()` 方法中添加 `use_fast=False`),這樣你就可以看看在你的電腦上它需要多長的時間。 + + + +以下是我們在使用和不使用批處理時使用快速和慢速分詞器獲得的結果: + +Options | Fast tokenizer | Slow tokenizer +:--------------:|:--------------:|:-------------: +`batched=True` | 10.8s | 4min41s +`batched=False` | 59.2s | 5min3s + +這意味著使用帶有 **batched=True** 選項比沒有批處理的慢選項快 30 倍——這真是太棒了!這就是為什麼**AutoTokenizer** 的默認設置是**use_fast=True**的主要原因 (以及為什麼它們被稱為“快速”)。他們能夠實現這樣的加速,因為在底層的標記化代碼是在 Rust 中執行的,Rust 是一種可以輕鬆並行化執行的語言。 + +並行化也是快速標記器通過批處理實現近 6 倍加速的原因:單個標記化操作是不能並行的,但是當您想同時標記大量文本時,您可以將執行拆分為多個進程,每個進程都對自己的文本負責。 + +**Dataset.map()** 也有一些自己的並行化能力。由於它們不受 Rust 的支持,因此慢速分詞器的速度趕不上快速分詞器,但它們仍然會更快一些(尤其是當您使用沒有快速版本的分詞器時)。要啟用多處理,請在**Dataset.map()**時使用 **num_proc** 參數並指定要在調用中使用的進程數 : + +```py +slow_tokenizer = AutoTokenizer.from_pretrained("bert-base-cased", use_fast=False) + + +def slow_tokenize_function(examples): + return slow_tokenizer(examples["review"], truncation=True) + + +tokenized_dataset = drug_dataset.map(slow_tokenize_function, batched=True, num_proc=8) +``` + +您可以對處理的時間進行一些試驗,以確定要使用的最佳進程數;在我們的例子中,8 似乎產生了最好的速度增益。以下是我們在使用和不使用多處理時所需要的時間: + +Options | Fast tokenizer | Slow tokenizer +:--------------:|:--------------:|:-------------: +`batched=True` | 10.8s | 4min41s +`batched=False` | 59.2s | 5min3s +`batched=True`, `num_proc=8` | 6.52s | 41.3s +`batched=False`, `num_proc=8` | 9.49s | 45.2s + +對於慢速分詞器來說,這些結果要合理得多,但快速分詞器的性能也得到了顯著提高。但是請注意,情況並非總是如此——除了 **num_proc=8**,我們的測試表明,使用**batched=True**而不帶有**num_proc**參數的選項處理起來更快。通常,我們不建議將 Python 多線程處理用於具有**batched=True**功能的快速標記器 . + + + +使用num_proc以加快處理速度通常是一個好主意,只要您使用的函數還沒有自己帶有的進行某種多進程處理的方法。 + + + +將所有這些功能濃縮到一個方法中已經非常了不起,但還有更多!使用 **Dataset.map()** 和 **batched=True** 您可以更改數據集中的元素數量。當你想從一個例子中創建幾個訓練特徵時,這是非常有用的。我們將在[第七章](/course/chapter7).中進行的幾個NLP任務的預處理中使用到這個功能,它非常便利。 + + + +💡在機器學習中,一個例子通常可以為我們的模型提供一組特徵。在某些情況下,這些特徵會儲存在數據集的幾個列,但在其他情況下(例如此處的例子和用於問答的數據),可以從單個示例的一列中提取多個特徵 + + + +讓我們來看看它是如何工作的!在這裡,我們將標記化我們的示例並將最大截斷長度設置128,但我們將要求標記器返回全部文本塊,而不僅僅是第一個。這可以用 **return_overflowing_tokens=True** : + +```py +def tokenize_and_split(examples): + return tokenizer( + examples["review"], + truncation=True, + max_length=128, + return_overflowing_tokens=True, + ) +``` + +在使用**Dataset.map()** 正式在整個數據集上開始處理之前讓我們先在一個例子上測試一下: + +```py +result = tokenize_and_split(drug_dataset["train"][0]) +[len(inp) for inp in result["input_ids"]] +``` + +```python out +[128, 49] +``` + +瞧!我們在訓練集中的第一個示例變成了兩個特徵,因為它被標記為超過我們指定的最大截斷長度,因此結果被截成了兩段:第一段長度為 128 ,第二段長度為 49 。現在讓我們對所有元素執行此操作數據集! + +```py +tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True) +``` + +```python out +ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000 +``` + +不好了!它沒有起作用!為什麼呢?查看錯誤消息會給我們一個線索:列的長度不匹配,一列長度為 1,463,另一列長度為 1,000。1,000行的"review"給出了 1,463 行的新特徵,導致和原本的1000行數據不匹配。 + +問題出在我們試圖混合兩個不同大小的不同數據集: **drug_dataset** 列將有一定數量的元素(我們錯誤中的 1,000),但是我們正在構建**tokenized_dataset** 將有更多的元素(錯誤消息中的 1,463)。這不適用於 **Dataset** ,因此我們需要從舊數據集中刪除列或使它們的大小與新數據集中的大小相同。我們可以用 **remove_columns** 參數: + +```py +tokenized_dataset = drug_dataset.map( + tokenize_and_split, batched=True, remove_columns=drug_dataset["train"].column_names +) +``` + +現在這個過程沒有錯誤。我們可以通過比較長度來檢查新數據集的元素是否比原始數據集多得多: + +```py +len(tokenized_dataset["train"]), len(drug_dataset["train"]) +``` + +```python out +(206772, 138514) +``` + +我們提到我們還可以通過使舊列與新列的大小相同來處理長度不匹配的問題。為此,我們可以使用 **overflow_to_sample_mapping** 字段,當我們設置**return_overflowing_tokens=True** .它為我們提供了特徵到它所產生的樣本的映射。使用這個,我們可以將原始數據集中的每個鍵關聯到一個合適大小的值列表中,通過遍歷所有的數據來生成新特性: + +```py +def tokenize_and_split(examples): + result = tokenizer( + examples["review"], + truncation=True, + max_length=128, + return_overflowing_tokens=True, + ) + # Extract mapping between new and old indices + sample_map = result.pop("overflow_to_sample_mapping") + for key, values in examples.items(): + result[key] = [values[i] for i in sample_map] + return result +``` + +我們可以使用**Dataset.map()**來進行批處理,這樣無需我們刪除舊列: + +```py +tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True) +tokenized_dataset +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'], + num_rows: 206772 + }) + test: Dataset({ + features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'], + num_rows: 68876 + }) +}) +``` + +我們獲得了與以前相同數量的訓練特徵,但在這裡我們保留了所有舊字段。如果您在使用模型計算之後需要它們進行一些後處理,您可能需要使用這種方法。 + +您現在已經瞭解了 🤗 Datasets如何以各種方式用於預處理數據集。雖然🤗 Datasets 的處理功能會覆蓋你大部分的模型訓練需求,有時您可能需要切換到 Pandas 以使用更強大的功能,例如 **DataFrame.groupby()** 或用於可視化的高級 API。幸運的是,🤗 Datasets旨在與 Pandas、NumPy、PyTorch、TensorFlow 和 JAX 等庫可以相互轉換。讓我們來看看這是如何工作的。 + +## `🤗 Datasets 和 DataFrames 的相互轉換 + + + +為了實現各種第三方庫之間的轉換,🤗 Datasets 提供了一個 **Dataset.set_format()** 功能。此功能可以通過僅更改輸出格式的,輕鬆切換到另一種格式,而不會影響底層數據格式,即 Apache Arrow。格式化會在數據本身上進行。為了演示,讓我們將數據集轉換為 Pandas: + +```py +drug_dataset.set_format("pandas") +``` + +現在,當我們訪問數據集的元素時,我們會得到一個 **pandas.DataFrame** 而不是字典: + +```py +drug_dataset["train"][:3] +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
patient_iddrugNameconditionreviewratingdateusefulCountreview_length
095260Guanfacineadhd"My son is halfway through his fourth week of Intuniv..."8.0April 27, 2010192141
192703Lybrelbirth control"I used to take another oral contraceptive, which had 21 pill cycle, and was very happy- very light periods, max 5 days, no other side effects..."5.0December 14, 200917134
2138000Ortho Evrabirth control"This is my first time using any form of birth control..."8.0November 3, 20151089
+ +讓我們創建一個 **pandas.DataFrame** 來選擇 **drug_dataset[train]** 的所有元素: + +```py +train_df = drug_dataset["train"][:] +``` + + + +🚨 在底層,`Dataset.set_format()` 改變了數據集的 `__getitem__()` dunder 方法的返回格式。 這意味著當我們想從 `"pandas"` 格式的 `Dataset` 中創建像 `train_df` 這樣的新對象時,我們需要對整個數據集進行切片以獲得 `pandas.DataFrame`。 無論輸出格式如何,您都可以自己驗證 `drug_dataset["train"]` 的類型依然還是 `Dataset`。 + + + + +從這裡我們可以使用我們想要的所有 Pandas 功能。例如,我們可以通過花式鏈接來計算 **condition**類之間的分佈 : + +```py +frequencies = ( + train_df["condition"] + .value_counts() + .to_frame() + .reset_index() + .rename(columns={"index": "condition", "condition": "frequency"}) +) +frequencies.head() +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
conditionfrequency
0birth control27655
1depression8023
2acne5209
3anxiety4991
4pain4744
+ + +一旦我們完成了 Pandas 分析,我們總是通過使用對象 **Dataset.from_pandas()**方法可以創建一個新的 **Dataset** 如下: + + +```py +from datasets import Dataset + +freq_dataset = Dataset.from_pandas(frequencies) +freq_dataset +``` + +```python out +Dataset({ + features: ['condition', 'frequency'], + num_rows: 819 +}) +``` + + + +✏️ **試試看!** 計算每種藥物的平均評級並將結果存儲在一個新的Dataset. + + + +我們對 🤗 Datasets中可用的各種預處理技術的介紹到此結束。在最後一部分,讓我們創建一個驗證集來準備用於訓練分類器的數據集。在此之前,我們將輸出格式 **drug_dataset** 從 **pandas**重置到 **arrow** : + +```python +drug_dataset.reset_format() +``` + +## 創建驗證集 + +儘管我們有一個可以用於評估的測試集,但在開發過程中保持測試集不變並創建一個單獨的驗證集是一個很好的做法。一旦您對模型在測試集上的表現感到滿意,您就可以對驗證集進行最終的檢查。此過程有助於降低您過擬合測試集並部署在現實世界數據上失敗的模型的風險。 + +🤗 Datasets提供了一個基於**scikit-learn**的經典方法**Dataset.train_test_split()** .讓我們用它把我們的訓練集分成 **train** 和 **validation** (為了可以復現,我們將設置**seed**的值為一個常量): + +```py +drug_dataset_clean = drug_dataset["train"].train_test_split(train_size=0.8, seed=42) +# Rename the default "test" split to "validation" +drug_dataset_clean["validation"] = drug_dataset_clean.pop("test") +# Add the "test" set to our `DatasetDict` +drug_dataset_clean["test"] = drug_dataset["test"] +drug_dataset_clean +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'], + num_rows: 110811 + }) + validation: Dataset({ + features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'], + num_rows: 27703 + }) + test: Dataset({ + features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'], + num_rows: 46108 + }) +}) +``` + +太好了,我們現在已經準備好了一個數據集,可以用來訓練一些模型了!在[第五節]](/course/chapter5/5)我們將向您展示如何將數據集上傳到 Hugging Face Hub,但現在讓我們查看在本地計算機上保存數據集的幾種方法。 + +## 保存數據集 + + + +雖然 🤗 Datasets 會緩存每個下載的數據集和對它執行的操作,但有時你會想要將數據集保存到磁盤(例如,以防緩存被刪除)。如下表所示,🤗 Datasets 提供了三個主要功能來以不同的格式保存您的數據集: + +| 數據格式 | 對應的方法 | +| :---------: | :--------------------: | +| Arrow | `Dataset.save_to_disk()` | +| CSV | `Dataset.to_csv()` | +| JSON | `Dataset.to_json()` | + +例如,讓我們以 Arrow 格式保存我們清洗過的數據集: + +```py +drug_dataset_clean.save_to_disk("drug-reviews") +``` + +這將創建一個具有以下結構的目錄: + +``` +drug-reviews/ +├── dataset_dict.json +├── test +│ ├── dataset.arrow +│ ├── dataset_info.json +│ └── state.json +├── train +│ ├── dataset.arrow +│ ├── dataset_info.json +│ ├── indices.arrow +│ └── state.json +└── validation + ├── dataset.arrow + ├── dataset_info.json + ├── indices.arrow + └── state.json +``` + +在那裡我們可以看到每個部分.arrow表,以及一些元數據數據集信息.json和狀態文件保存在一起.您可以將 Arrow 格式視為一個精美的列和行的表格,它針對構建處理和傳輸大型數據集的高性能應用程序進行了優化。 + +保存數據集後,我們可以使用 **load_from_disk()** 功能從磁盤讀取數據如下: + +```py +from datasets import load_from_disk + +drug_dataset_reloaded = load_from_disk("drug-reviews") +drug_dataset_reloaded +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'], + num_rows: 110811 + }) + validation: Dataset({ + features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'], + num_rows: 27703 + }) + test: Dataset({ + features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'], + num_rows: 46108 + }) +}) +``` + +對於 CSV 和 JSON 格式,我們必須將每個部分存儲為單獨的文件。一種方法是迭代**DatasetDict**中的鍵和值 : + +```py +for split, dataset in drug_dataset_clean.items(): + dataset.to_json(f"drug-reviews-{split}.jsonl") +``` + +這將保存每個拆分都是[JSON的標準格式](https://jsonlines.org),其中數據集中的每一行都存儲為一行 JSON。這是第一個示例: + +```py +!head -n 1 drug-reviews-train.jsonl +``` + +```python out +{"patient_id":141780,"drugName":"Escitalopram","condition":"depression","review":"\"I seemed to experience the regular side effects of LEXAPRO, insomnia, low sex drive, sleepiness during the day. I am taking it at night because my doctor said if it made me tired to take it at night. I assumed it would and started out taking it at night. Strange dreams, some pleasant. I was diagnosed with fibromyalgia. Seems to be helping with the pain. Have had anxiety and depression in my family, and have tried quite a few other medications that haven't worked. Only have been on it for two weeks but feel more positive in my mind, want to accomplish more in my life. Hopefully the side effects will dwindle away, worth it to stick with it from hearing others responses. Great medication.\"","rating":9.0,"date":"May 29, 2011","usefulCount":10,"review_length":125} +``` + +然後我們可以使用[第二節](/course/chapter5/2)學過的技術加載 JSON 文件如下: + +```py +data_files = { + "train": "drug-reviews-train.jsonl", + "validation": "drug-reviews-validation.jsonl", + "test": "drug-reviews-test.jsonl", +} +drug_dataset_reloaded = load_dataset("json", data_files=data_files) +``` + +這就是我們探索 🤗 Datasets 的旅程!現在我們有了一個清洗過的數據集,以下是您可以嘗試的一些想法: + +1. 使用[第3章](/course/chapter3)的技術來訓練一個分類器,它可以根據藥物評論預測病人的情況。 +2. 使用 [Chapter 1](/course/chapter1) 中的“summarization”管道生成評論摘要。 + +接下來,我們將看看 🤗 Datasets如何使您能夠在不撐爆筆記本電腦內存的情況下處理龐大的數據集! \ No newline at end of file diff --git a/chapters/zh-TW/chapter5/4.mdx b/chapters/zh-TW/chapter5/4.mdx new file mode 100644 index 000000000..4cd7ae730 --- /dev/null +++ b/chapters/zh-TW/chapter5/4.mdx @@ -0,0 +1,287 @@ +# 大數據? 🤗 Datasets 來救援! + + + + +如今,不難發現我們經常使用數GB的數據集, 特別是如果你打算從頭開始預訓練像 BERT 或者 GPT-2 這樣的轉換器。 在這種情況下, _加載_ 數據集就是一個挑戰。例如, 用於預訓練 GPT-2 的 WebText 語料庫包含超過 800 萬個文檔和 40 GB 的文本 -- 將其加載到筆記本電腦的 RAM 中可能會讓它抓狂! + +幸運的是, 🤗 Datasets 旨在克服這些限制。它通過將數據集作為內存映射文件來處理,並通過在語料庫中流化條目來擺脫硬盤限制, 從而使你避免內存管理問題。 + + + +在本節中, 我們將探索🤗 Datasets 的特性。它有一個稱為 [the Pile](https://pile.eleuther.ai)的825 GB的語料庫。 讓我們開始吧! + +## 什麼是Pile? + +The Pile 是由[EleutherAI](https://www.eleuther.ai)創建的一個英語文本語料庫, 用於訓練大規模語言模型。它包含各種各樣的數據集, 涵蓋科學文章, GitHub 代碼庫以及過濾的Web文本。訓練語料庫在[14 GB chunks](https://the-eye.eu/public/AI/pile/), 並且你也可以下載幾個[單獨的組件](https://the-eye.eu/public/AI/pile_preliminary_components/)。 讓我們先來看看 PubMed Abstracts 數據集, 它是[PubMed](https://pubmed.ncbi.nlm.nih.gov/)上的1500萬篇生物醫學出版物的摘要的語料庫。 數據集採用[JSON行格式](https://jsonlines.org) 並使用`zstandard`庫進行壓縮, 所以我們首先需要先安裝`zstandard`庫: + +```py +!pip install zstandard +``` + +接下來, 我們可以使用[第二節](/course/chapter5/2)中所學的加載遠程數據集的方法加載數據集: + +```py +from datasets import load_dataset + +# This takes a few minutes to run, so go grab a tea or coffee while you wait :) +data_files = "https://the-eye.eu/public/AI/pile_preliminary_components/PUBMED_title_abstracts_2019_baseline.jsonl.zst" +pubmed_dataset = load_dataset("json", data_files=data_files, split="train") +pubmed_dataset +``` + +```python out +Dataset({ + features: ['meta', 'text'], + num_rows: 15518009 +}) +``` + +我們可以看到我們的數據集中有 15,518,009 行和 2 列 -- 這是非常多的! + + + +✎ 默認情況下, 🤗 Datasets 會自動解壓加載數據集所需的文件。 如果你想保留硬盤空間, 你可以傳遞 `DownloadConfig(delete_extracted=True)` 到 `download_config` 的 `load_dataset()`參數. 有關更多詳細信息, 請參閱文檔](https://huggingface.co/docs/datasets/package_reference/builder_classes.html?#datasets.utils.DownloadConfig)。 + + + +讓我們看看數據集的第一個元素的內容: + +```py +pubmed_dataset[0] +``` + +```python out +{'meta': {'pmid': 11409574, 'language': 'eng'}, + 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'} +``` + +可以看到, 這看起來像是醫學文章的摘要。 現在讓我們看看我們使用了RAM的多少存儲空間來加載數據集! + +## 內存映射的魔力 + +在 Python 中測量內存使用情況的一個簡單的方法是使用[`psutil`](https://psutil.readthedocs.io/en/latest/)庫,它可以使用 `pip`安裝, 如下所示: + +```python +!pip install psutil +``` + +它提供了一個 `Process` 類,這個類允許我們檢查當前進程的內存使用情況, 如下所示: + +```py +import psutil + +# Process.memory_info is expressed in bytes, so convert to megabytes +print(f"RAM used: {psutil.Process().memory_info().rss / (1024 * 1024):.2f} MB") +``` + +```python out +RAM used: 5678.33 MB +``` + +這裡的`rss`屬性是指 _常駐集_ 的大小, 它是進程在RAM中佔用的內存比例。 這個測量結果也包括了 Python 編譯器和我們加載的庫所使用的內存, 所以實際上用於加載數據集的內存會更小一些。為了比較, 讓我們使用 `dataset_size` 屬性看看數據集在磁盤上有多大。 由於結果像之前一樣用字節表示, 我們需要手動將其轉換為GB: + +```py +print(f"Number of files in dataset : {pubmed_dataset.dataset_size}") +size_gb = pubmed_dataset.dataset_size / (1024**3) +print(f"Dataset size (cache file) : {size_gb:.2f} GB") +``` + +```python out +Number of files in dataset : 20979437051 +Dataset size (cache file) : 19.54 GB +``` + +非常棒 -- 儘管它將近20GB, 但我們能夠佔用很少的RAM空間加載和訪問數據集! + + + +✏️ **試試看!** 從[subsets](https://the-eye.eu/public/AI/pile_preliminary_components/)中選擇一個大於你的筆記本或者臺式機的RAM大小的子集, 用 🤗 Datasets加載這個數據集, 並且測量RAM的使用量。 請注意, 要獲得準確的測量結果, 你需要在另一個進程中執行這個操作。你可以在 [the Pile paper](https://arxiv.org/abs/2101.00027)的表一中找到每個子集解壓後的大小。 + + + +如果你熟悉 Pandas, 這個結果可能會讓人感到很意外。因為 Wes Kinney 的著名的[經驗法則](https://wesmckinney.com/blog/apache-arrow-pandas-internals/) 是你需要的RAM應該是數據集的大小的5倍到10倍。 那麼 🤗 Datasets 是如何解決這個內存管理問題的呢? 🤗 Datasets 將每一個數據集看作一個[內存映射文件](https://en.wikipedia.org/wiki/Memory-mapped_file), 它提供了RAM和文件系統存儲之間的映射, 該映射允許庫訪問和操作數據集的元素, 而且無需將其完全加載到內存中。 + +內存映射文件也一個在多個進程之間共享, 這使得像 `Dataset.map()`之類的方法可以並行化, 並且無需移動或者賦值數據集。在底層, 這些功能都是由[Apache Arrow](https://arrow.apache.org)內存格式和[`pyarrow`](https://arrow.apache.org/docs/python/index.html)庫提供的支持, 使得數據加載和處理速度快如閃電。 (更多有關Apache Arrow的詳細信息以及與Pandas的比較, 請查看[Dejan Simic's blog post](https://towardsdatascience.com/apache-arrow-read-dataframe-with-zero-memory-69634092b1a).) 為了更清晰地看到這個過程, 讓我們通過迭代PubMed Abstracts數據集中的所有元素來運行一個速度測試小程序: + +```py +import timeit + +code_snippet = """batch_size = 1000 + +for idx in range(0, len(pubmed_dataset), batch_size): + _ = pubmed_dataset[idx:idx + batch_size] +""" + +time = timeit.timeit(stmt=code_snippet, number=1, globals=globals()) +print( + f"Iterated over {len(pubmed_dataset)} examples (about {size_gb:.1f} GB) in " + f"{time:.1f}s, i.e. {size_gb/time:.3f} GB/s" +) +``` + +```python out +'Iterated over 15518009 examples (about 19.5 GB) in 64.2s, i.e. 0.304 GB/s' +``` + +這裡我們使用了 Python的 `timeit` 模塊來測量執行 `code_snippet`所耗的時間。 你通常能以十分之幾GB/s到幾GB/s的速度迭代數據集。通過上述的方法就已經能夠解決大多數大數據集加載的限制, 但是有時候你不得不使用一個很大的數據集, 它甚至都不能存儲在筆記本電腦的硬盤上。例如, 如果我們嘗試下載整個 Pile, 我們需要825GB的可用磁盤空間! 為了處理這種情況, 🤗 Datasets 提供了一個流式功能, 這個功能允許我們動態下載和訪問元素, 並且不需要下載整個數據集。讓我們來看看這個功能是如何工作的。 + + + +💡在 Jupyter 筆記中你還可以使用[`%%timeit` magic function](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit)為單元格計時。 + + + +## 流式數據集 + +要使用數據集流, 你只需要將 `streaming=True` 參數傳遞給 `load_dataset()` 函數。接下來, 讓我們再次加載 PubMed Abstracts 數據集, 但是採用流模式: + +```py +pubmed_dataset_streamed = load_dataset( + "json", data_files=data_files, split="train", streaming=True +) +``` + +與我們在本章其他地方遇到的熟悉的 `Dataset` 不同, `streaming=True` 返回的對象是一個 `IterableDataset`。 顧名思義, 要訪問 `IterableDataset` , 我們需要迭代它。我們可以按照如下方式訪問流式數據集的第一個元素: + + +```py +next(iter(pubmed_dataset_streamed)) +``` + +```python out +{'meta': {'pmid': 11409574, 'language': 'eng'}, + 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'} +``` + +如果您需要在訓練期間標記流式數據集中的元素可以使用 `IterableDataset.map()`進行動態處理。該過程與我們在[第三章](/course/chapter3)中標記數據集的過程完全相同, 唯一的區別是輸出是逐個返回的: + +```py +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased") +tokenized_dataset = pubmed_dataset_streamed.map(lambda x: tokenizer(x["text"])) +next(iter(tokenized_dataset)) +``` + +```python out +{'input_ids': [101, 4958, 5178, 4328, 6779, ...], 'attention_mask': [1, 1, 1, 1, 1, ...]} +``` + + + +💡 你可以傳遞 `batched=True` 來通過流式加速標記化, 如同我們在上一節看到的那樣。它將逐批處理示例; 默認的批量大小為 1,000, 可以使用 `batch_size` 參數指定批量大小。 + + + +你還可以使用 `IterableDataset.shuffle()` 打亂流式數據集, 但與 `Dataset.shuffle()` 不同的是這隻會打亂預定義 `buffer_size` 中的元素: + +```py +shuffled_dataset = pubmed_dataset_streamed.shuffle(buffer_size=10_000, seed=42) +next(iter(shuffled_dataset)) +``` + +```python out +{'meta': {'pmid': 11410799, 'language': 'eng'}, + 'text': 'Randomized study of dose or schedule modification of granulocyte colony-stimulating factor in platinum-based chemotherapy for elderly patients with lung cancer ...'} +``` + +在這個示例中, 我們從緩衝區的前 10,000 個示例中隨機選擇了一個示例。一旦訪問了一個示例, 它在緩衝區中的位置就會被語料庫中的下一個示例填充 (即, 上述案例中的第 10,001個示例)。你還可以使用 `IterableDataset.take()` 和 `IterableDataset.skip()` 函數從流式數據集中選擇元素, 它的作用類似於 `Dataset.select()`。例如, 要選擇 PubMed Abstracts 數據集的前5個示例, 我們可以執行以下操作: + +```py +dataset_head = pubmed_dataset_streamed.take(5) +list(dataset_head) +``` + +```python out +[{'meta': {'pmid': 11409574, 'language': 'eng'}, + 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'}, + {'meta': {'pmid': 11409575, 'language': 'eng'}, + 'text': 'Clinical signs of hypoxaemia in children with acute lower respiratory infection: indicators of oxygen therapy ...'}, + {'meta': {'pmid': 11409576, 'language': 'eng'}, + 'text': "Hypoxaemia in children with severe pneumonia in Papua New Guinea ..."}, + {'meta': {'pmid': 11409577, 'language': 'eng'}, + 'text': 'Oxygen concentrators and cylinders ...'}, + {'meta': {'pmid': 11409578, 'language': 'eng'}, + 'text': 'Oxygen supply in rural africa: a personal experience ...'}] +``` + +同樣, 你可以使用 `IterableDataset.skip()` 函數將打亂的數據集拆分為訓練集和驗證集, 如下所示: + +```py +# Skip the first 1,000 examples and include the rest in the training set +train_dataset = shuffled_dataset.skip(1000) +# Take the first 1,000 examples for the validation set +validation_dataset = shuffled_dataset.take(1000) +``` + +讓我們用一個常見的任務來進行我們對數據集流的最後探索: 將多個數據集組合在一起創建一個心得語料庫。 🤗 Datasets 提供了一個 `interleave_datasets()` 函數, 它將一個 `IterableDataset` 對象列表組合為單個的 `IterableDataset`, 其中新數據集的元素是通過在列表中的對象交替獲得的。當你試圖組合大型數據集時, 這個函數特別有用, 讓我們通過下面這個例子來試著組合 Pile的自由法律數據集,它是來自美國法院的51 GB的法律意見數據集: + +```py +law_dataset_streamed = load_dataset( + "json", + data_files="https://the-eye.eu/public/AI/pile_preliminary_components/FreeLaw_Opinions.jsonl.zst", + split="train", + streaming=True, +) +next(iter(law_dataset_streamed)) +``` + +```python out +{'meta': {'case_ID': '110921.json', + 'case_jurisdiction': 'scotus.tar.gz', + 'date_created': '2010-04-28T17:12:49Z'}, + 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'} +``` + +這個數據集足夠大, 可以對大多數筆記本電腦的RAM有足夠的壓力, 但是我們已經能夠毫不費力地加載和訪問它! 現在我們使用 `interleave_datasets()` 函數加載來自 FreeLaw 和 PubMed Abstracts 的數據集: + +```py +from itertools import islice +from datasets import interleave_datasets + +combined_dataset = interleave_datasets([pubmed_dataset_streamed, law_dataset_streamed]) +list(islice(combined_dataset, 2)) +``` + +```python out +[{'meta': {'pmid': 11409574, 'language': 'eng'}, + 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'}, + {'meta': {'case_ID': '110921.json', + 'case_jurisdiction': 'scotus.tar.gz', + 'date_created': '2010-04-28T17:12:49Z'}, + 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}] +``` + +這裡我們使用了來自Python的 `itertools` 模塊的 `islice()` 函數從合併的數據集中選擇前兩個示例, 並且我們可以看到它們實際上就是兩個源數據集中的前兩個示例拼在一起形成的: + +最後, 如果你想流式傳輸整個825GB的 Pile, 你可以按照如下方式獲取所有準備好的文件: + +```py +base_url = "https://the-eye.eu/public/AI/pile/" +data_files = { + "train": [base_url + "train/" + f"{idx:02d}.jsonl.zst" for idx in range(30)], + "validation": base_url + "val.jsonl.zst", + "test": base_url + "test.jsonl.zst", +} +pile_dataset = load_dataset("json", data_files=data_files, streaming=True) +next(iter(pile_dataset["train"])) +``` + +```python out +{'meta': {'pile_set_name': 'Pile-CC'}, + 'text': 'It is done, and submitted. You can play “Survival of the Tastiest” on Android, and on the web...'} +``` + + + +✏️ **試試看!** 使用像[`mc4`](https://huggingface.co/datasets/mc4) 或者 [`oscar`](https://huggingface.co/datasets/oscar)這樣的大型 Common Crawl 語料庫來創建一個流式多語言數據集, 該數據集代表你選擇的國家/地區語言的口語比例。例如, 瑞士的四種民族語言分別是德語、法語、意大利語和羅曼什語, 因此你可以嘗試根據根據口語比例對Oscar子集進行採用來創建瑞士語料庫。 + + + +你現在擁有加載和處理各種類型和大小的數據集的所需的所有工具 -- 但是除非你非常幸運, 否則在你的NLP之旅中會有一個難題, 你將不得不創建一個數據集來解決手頭的問題。這就是下一節的主題! diff --git a/chapters/zh-TW/chapter5/5.mdx b/chapters/zh-TW/chapter5/5.mdx new file mode 100644 index 000000000..f65b1c0e2 --- /dev/null +++ b/chapters/zh-TW/chapter5/5.mdx @@ -0,0 +1,461 @@ +# 創建自己的數據集 + + + +有時,不存在合適的數據集適用於您構建 NLP 應用,因此您需要自己創建。在本節中,我們將向您展示如何創建一個[GitHub issues](https://github.com/features/issues/)的語料庫,GitHub issues通常用於跟蹤 GitHub 存儲庫中的錯誤或功能。該語料庫可用於各種目的,包括: +* 探索關閉未解決的issue或拉取請求需要多長時間 +* 訓練一個*多標籤分類器*可以根據issue的描述(例如,“錯誤”、“增強”或“issue”)用元數據標記issue +* 創建語義搜索引擎以查找與用戶查詢匹配的issue + +在這裡,我們將專注於創建語料庫,在下一節中,我們將探索語義搜索。我們將使用與流行的開源項目相關的 GitHub issue:🤗 Datasets!接下來讓我們看看如何獲取數據並探索這些issue中包含的信息。 + +## 獲取數據 + +您可以瀏覽 🤗 Datasets 中的所有issue[Issues tab](https://github.com/huggingface/datasets/issues).如以下屏幕截圖所示,在撰寫本文時,有 331 個未解決的issue和 668 個已關閉的issue。 + +
+The GitHub issues associated with 🤗 Datasets. +
+ +如果您單擊其中一個issue,您會發現它包含一個標題、一個描述和一組表徵該issue的標籤。下面的屏幕截圖顯示了一個示例. + +
+A typical GitHub issue in the 🤗 Datasets repository. +
+ +要下載所有存儲庫的issue,我們將使用[GitHub REST API](https://docs.github.com/en/rest)投票[Issues endpoint](https://docs.github.com/en/rest/reference/issues#list-repository-issues).此節點返回一個 JSON 對象列表,每個對象包含大量字段,其中包括標題和描述以及有關issue狀態的元數據等。 + +下載issue的一種便捷方式是通過 **requests** 庫,這是用 Python 中發出 HTTP 請求的標準方式。您可以通過運行以下的代碼來安裝庫: + +```python +!pip install requests +``` + +安裝庫後,您通過調用 **requests.get()** 功能來獲取**Issues**節點。例如,您可以運行以下命令來獲取第一頁上的第一個Issues: + +```py +import requests + +url = "https://api.github.com/repos/huggingface/datasets/issues?page=1&per_page=1" +response = requests.get(url) +``` + +這 **response** 對象包含很多關於請求的有用信息,包括 HTTP 狀態碼: + +```py +response.status_code +``` + +```python out +200 +``` + +其中一個狀態碼 **200** 表示請求成功(您可以[在這裡](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)找到可能的 HTTP 狀態代碼列表)。然而,我們真正感興趣的是有效的信息,由於我們知道我們的issues是 JSON 格式,讓我們按如下方式查看所有的信息: + +```py +response.json() +``` + +```python out +[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/2792', + 'repository_url': 'https://api.github.com/repos/huggingface/datasets', + 'labels_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/labels{/name}', + 'comments_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/comments', + 'events_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/events', + 'html_url': 'https://github.com/huggingface/datasets/pull/2792', + 'id': 968650274, + 'node_id': 'MDExOlB1bGxSZXF1ZXN0NzEwNzUyMjc0', + 'number': 2792, + 'title': 'Update GooAQ', + 'user': {'login': 'bhavitvyamalik', + 'id': 19718818, + 'node_id': 'MDQ6VXNlcjE5NzE4ODE4', + 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4', + 'gravatar_id': '', + 'url': 'https://api.github.com/users/bhavitvyamalik', + 'html_url': 'https://github.com/bhavitvyamalik', + 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers', + 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}', + 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}', + 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}', + 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions', + 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs', + 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos', + 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}', + 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events', + 'type': 'User', + 'site_admin': False}, + 'labels': [], + 'state': 'open', + 'locked': False, + 'assignee': None, + 'assignees': [], + 'milestone': None, + 'comments': 1, + 'created_at': '2021-08-12T11:40:18Z', + 'updated_at': '2021-08-12T12:31:17Z', + 'closed_at': None, + 'author_association': 'CONTRIBUTOR', + 'active_lock_reason': None, + 'pull_request': {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/2792', + 'html_url': 'https://github.com/huggingface/datasets/pull/2792', + 'diff_url': 'https://github.com/huggingface/datasets/pull/2792.diff', + 'patch_url': 'https://github.com/huggingface/datasets/pull/2792.patch'}, + 'body': '[GooAQ](https://github.com/allenai/gooaq) dataset was recently updated after splits were added for the same. This PR contains new updated GooAQ with train/val/test splits and updated README as well.', + 'performed_via_github_app': None}] +``` + +哇,這是很多信息!我們可以看到有用的字段,例如 **標題** , **內容** , **參與的成員**, **issue的描述信息**,以及打開issue的GitHub 用戶的信息。 + + + +✏️ 試試看!單擊上面 JSON 中的幾個 URL,以瞭解每個 GitHub issue中我url鏈接到的實際的地址。 + + +如 GitHub[文檔](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting) 中所述,未經身份驗證的請求限制為每小時 60 個請求。雖然你可以增加 **per_page** 查詢參數以減少您發出的請求數量,您仍然會遭到任何超過幾千個issue的存儲庫的速率限制。因此,您應該關注 GitHub 的[創建個人身份令牌](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token),創建一個個人訪問令牌這樣您就可以將速率限制提高到每小時 5,000 個請求。獲得令牌後,您可以將其包含在請求標頭中: + +```py +GITHUB_TOKEN = xxx # Copy your GitHub token here +headers = {"Authorization": f"token {GITHUB_TOKEN}"} +``` + + + +⚠️ 不要與陌生人共享存在GITHUB令牌的筆記本。我們建議您在使用完後將GITHUB令牌刪除,以避免意外洩漏此信息。一個更好的做法是,將令牌存儲在.env文件中,並使用 [`python-dotenv` library](https://github.com/theskumar/python-dotenv) 為您自動將其作為環境變量加載。 + + + +現在我們有了訪問令牌,讓我們創建一個可以從 GitHub 存儲庫下載所有issue的函數: + +```py +import time +import math +from pathlib import Path +import pandas as pd +from tqdm.notebook import tqdm + + +def fetch_issues( + owner="huggingface", + repo="datasets", + num_issues=10_000, + rate_limit=5_000, + issues_path=Path("."), +): + if not issues_path.is_dir(): + issues_path.mkdir(exist_ok=True) + + batch = [] + all_issues = [] + per_page = 100 # Number of issues to return per page + num_pages = math.ceil(num_issues / per_page) + base_url = "https://api.github.com/repos" + + for page in tqdm(range(num_pages)): + # Query with state=all to get both open and closed issues + query = f"issues?page={page}&per_page={per_page}&state=all" + issues = requests.get(f"{base_url}/{owner}/{repo}/{query}", headers=headers) + batch.extend(issues.json()) + + if len(batch) > rate_limit and len(all_issues) < num_issues: + all_issues.extend(batch) + batch = [] # Flush batch for next time period + print(f"Reached GitHub rate limit. Sleeping for one hour ...") + time.sleep(60 * 60 + 1) + + all_issues.extend(batch) + df = pd.DataFrame.from_records(all_issues) + df.to_json(f"{issues_path}/{repo}-issues.jsonl", orient="records", lines=True) + print( + f"Downloaded all the issues for {repo}! Dataset stored at {issues_path}/{repo}-issues.jsonl" + ) +``` + +現在我們可以調用 **fetch_issues()** 批量下載所有issue,避免超過GitHub每小時的請求數限制;結果將存儲在repository_name-issues.jsonl文件,其中每一行都是一個 JSON 對象,代表一個issue。讓我們使用這個函數從 🤗 Datasets中抓取所有issue: + +```py +# Depending on your internet connection, this can take several minutes to run... +fetch_issues() +``` + +下載issue後,我們可以使用我們 [section 2](/course/chapter5/2)新學會的方法在本地加載它們: + +```py +issues_dataset = load_dataset("json", data_files="datasets-issues.jsonl", split="train") +issues_dataset +``` + +```python out +Dataset({ + features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'timeline_url', 'performed_via_github_app'], + num_rows: 3019 +}) +``` + +太好了,我們已經從頭開始創建了我們的第一個數據集!但是為什麼會有幾千個issue,而🤗 Datasets存儲庫中的[Issues 選項卡](https://github.com/huggingface/datasets/issues)總共卻只顯示了大約 1,000 個issue🤔?如 GitHub [文檔](https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user)中所述,那是因為我們也下載了所有的拉取請求: + +>Git Hub的REST API v3認為每個pull請求都是一個issue,但並不是每個issue都是一個pull請求。因此,“Issues”節點可能在響應中同時返回issue和拉取請求。你可以通過pull_request 的 key來辨別pull請求。請注意,從“Issues”節點返回的pull請求的id將是一個issue id。 + +由於issue和pull request的內容有很大的不同,我們先做一些小的預處理,讓我們能夠區分它們。 + +## 清理數據 + +上面來自 GitHub 文檔的片段告訴我們, **pull_request** 列可用於區分issue和拉取請求。讓我們隨機挑選一些樣本,看看有什麼不同。我們將使用在[第三節](/course/chapter5/3), 學習的方法,使用 **Dataset.shuffle()** 和 **Dataset.select()** 抽取一個隨機樣本,然後將 **html_url** 和 **pull_request** 列使用zip函數打包,以便我們可以比較各種 URL: + +```py +sample = issues_dataset.shuffle(seed=666).select(range(3)) + +# Print out the URL and pull request entries +for url, pr in zip(sample["html_url"], sample["pull_request"]): + print(f">> URL: {url}") + print(f">> Pull request: {pr}\n") +``` + +```python out +>> URL: https://github.com/huggingface/datasets/pull/850 +>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/850', 'html_url': 'https://github.com/huggingface/datasets/pull/850', 'diff_url': 'https://github.com/huggingface/datasets/pull/850.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/850.patch'} + +>> URL: https://github.com/huggingface/datasets/issues/2773 +>> Pull request: None + +>> URL: https://github.com/huggingface/datasets/pull/783 +>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/783', 'html_url': 'https://github.com/huggingface/datasets/pull/783', 'diff_url': 'https://github.com/huggingface/datasets/pull/783.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/783.patch'} +``` + +這裡我們可以看到,每個pull請求都與各種url相關聯,而普通issue只有一個None條目。我們可以使用這一點不同來創建一個新的is_pull_request列通過檢查pull_request字段是否為None來區分它們: + +```py +issues_dataset = issues_dataset.map( + lambda x: {"is_pull_request": False if x["pull_request"] is None else True} +) +``` + + + +✏️ 試試看!計算在 🤗 Datasets中解決issue所需的平均時間。您可能會發現 Dataset.filter()函數對於過濾拉取請求和未解決的issue很有用,並且您可以使用Dataset.set_format()函數將數據集轉換為DataFrame,以便您可以輕鬆地按照需求修改創建和關閉的時間的格式(以時間戳格式)。 + + + +儘管我們可以通過刪除或重命名某些列來進一步清理數據集,但在此階段儘可能保持數據集“原始”狀態通常是一個很好的做法,以便它可以在多個應用程序中輕鬆使用。在我們將數據集推送到 Hugging Face Hub 之前,讓我們再添加一些缺少的數據:與每個issue和拉取請求相關的評論。我們接下來將添加它們——你猜對了——我們將依然使用GitHub REST API! + +## 擴充數據集 + +如以下屏幕截圖所示,與issue或拉取請求相關的評論提供了豐富的信息,特別是如果我們有興趣構建搜索引擎來回答用戶對這個項目的疑問。 + +
+Comments associated with an issue about 🤗 Datasets. +
+ +GitHub REST API 提供了一個 [評論節點](https://docs.github.com/en/rest/reference/issues#list-issue-comments) 返回與issue編號相關的所有評論。讓我們測試節點以查看它返回的內容: + +```py +issue_number = 2792 +url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments" +response = requests.get(url, headers=headers) +response.json() +``` + +```python out +[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/comments/897594128', + 'html_url': 'https://github.com/huggingface/datasets/pull/2792#issuecomment-897594128', + 'issue_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792', + 'id': 897594128, + 'node_id': 'IC_kwDODunzps41gDMQ', + 'user': {'login': 'bhavitvyamalik', + 'id': 19718818, + 'node_id': 'MDQ6VXNlcjE5NzE4ODE4', + 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4', + 'gravatar_id': '', + 'url': 'https://api.github.com/users/bhavitvyamalik', + 'html_url': 'https://github.com/bhavitvyamalik', + 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers', + 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}', + 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}', + 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}', + 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions', + 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs', + 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos', + 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}', + 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events', + 'type': 'User', + 'site_admin': False}, + 'created_at': '2021-08-12T12:21:52Z', + 'updated_at': '2021-08-12T12:31:17Z', + 'author_association': 'CONTRIBUTOR', + 'body': "@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?", + 'performed_via_github_app': None}] +``` + +我們可以看到註釋存儲在body字段中,所以讓我們編寫一個簡單的函數,通過在response.json()中為每個元素挑選body內容來返回與某個issue相關的所有評論: + +```py +def get_comments(issue_number): + url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments" + response = requests.get(url, headers=headers) + return [r["body"] for r in response.json()] + + +# Test our function works as expected +get_comments(2792) +``` + +```python out +["@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?"] +``` + +這看起來不錯,所以讓我們使用 **Dataset.map()** 方法在我們數據集中每個issue的添加一個**comments**列: + +```py +# Depending on your internet connection, this can take a few minutes... +issues_with_comments_dataset = issues_dataset.map( + lambda x: {"comments": get_comments(x["number"])} +) +``` + +最後一步是將增強數據集與原始數據保存在一起,以便我們可以將它們都推送到 Hub: + +```py +issues_with_comments_dataset.to_json("issues-datasets-with-comments.jsonl") +``` + +## 將數據集上傳到 Hugging Face Hub + + + +現在我們有了我們的增強數據集,是時候將它推送到 Hub 並且與社區共享它!要上傳數據集,我們將使用[🤗 Hub 庫](https://github.com/huggingface/huggingface_hub),它允許我們通過 Python API 與 Hugging Face Hub 進行交互。 🤗 Hub 預裝了🤗 Transformers,所以我們可以直接使用它。例如,我們可以使用 **list_datasets()** 獲取有關當前託管在 Hub 上的所有公共數據集的信息的函數: + +```py +from huggingface_hub import list_datasets + +all_datasets = list_datasets() +print(f"Number of datasets on Hub: {len(all_datasets)}") +print(all_datasets[0]) +``` + +```python out +Number of datasets on Hub: 1487 +Dataset Name: acronym_identification, Tags: ['annotations_creators:expert-generated', 'language_creators:found', 'languages:en', 'licenses:mit', 'multilinguality:monolingual', 'size_categories:10K + +✏️ 試試看!使用您的 Hugging Face Hub 用戶名和密碼獲取令牌並創建一個名為 github-issues.請記住永遠不要將您的憑據保存在 Colab 或任何其他存儲庫中,因為這些信息可能會被不法分子利用。 + +
+ +接下來,讓我們將存儲庫從 Hub 克隆到我們的本地機器,並將我們的數據集文件複製到其中。 🤗 Hub 提供了一個方便的 **Repository** 類,它包含許多常見 Git 命令的類,因此要克隆遠程存儲庫,我們只需要提供我們要克隆的 URL 和本地路徑: + +```py +from huggingface_hub import Repository + +repo = Repository(local_dir="github-issues", clone_from=repo_url) +!cp datasets-issues-with-comments.jsonl github-issues/ +``` + +默認情況下,使用Git LFS跟蹤各種文件擴展名(如.bin、.gz和.zip),以便在同一Git工作流中對大型文件進行版本控制。您可以在存儲庫的.gitattributes文件找到跟蹤文件擴展名的列表。要在列表中包含JSON行格式,我們可以運行以下命令: + +```py +repo.lfs_track("*.jsonl") +``` + +然後我們可以使用 **Repository.push_to_hub()** 將數據集推送到 Hub: + +```py +repo.push_to_hub() +``` + +如果我們導航到包含在 **repo_url** ,我們現在應該看到我們的數據集文件已經上傳。 + +
+Our dataset repository on the Hugging Face Hub. +
+ +從這裡,任何人都可以通過簡單地提供來下載數據集 **load_dataset()** 以存儲庫 ID 作為 **path** 爭論: + +```py +remote_dataset = load_dataset("lewtun/github-issues", split="train") +remote_dataset +``` + +```python out +Dataset({ + features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'], + num_rows: 2855 +}) +``` + +很酷,我們已經將我們的數據集推送到 Hub,其他人可以使用它!只剩下一件重要的事情要做:添加一個數據卡這解釋了語料庫是如何創建的,併為使用數據集的其他提供一些其他有用的信息。 + + + +💡 您還可以使用一些 Git 魔法直接從終端將數據集上傳到 Hugging Face Hub。有關如何執行此操作的詳細信息,請參閱 [🤗 Datasets guide](https://huggingface.co/docs/datasets/share.html#add-a-community-dataset) 指南。 + + + +## 創建數據集卡片 + +有據可查的數據集更有可能對其他人(包括你未來的自己!)有用,因為它們提供了上下文,使用戶能夠決定數據集是否與他們的任務相關,並評估任何潛在的偏見或與使用相關的風險。在 Hugging Face Hub 上,此信息存儲在每個數據集存儲庫的自述文件文件。在創建此文件之前,您應該執行兩個主要步驟: + +1. 使用[數據集標籤應用程序](https://huggingface.co/datasets/tagging/) 創建YAML格式的元數據標籤。這些標籤用於各種各樣的搜索功能,並確保您的數據集可以很容易地被社區成員找到。因為我們已經在這裡創建了一個自定義數據集,所以您需要克隆數據集標籤存儲庫並在本地運行應用程序。它的界面是這樣的: + +
+The `datasets-tagging` interface. +
+ +2.閱讀[🤗 Datasets guide](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) 關於創建信息性數據集卡片的指南,並將其作為模板使用。 + +您可以創建自述文件文件直接在Hub上,你可以在裡面找到模板數據集卡片 **lewtun/github-issues** 數據集存儲庫。填寫好的數據集卡片的屏幕截圖如下所示。! + +
+A dataset card. +
+ + + +✏️試試看!使用應用程序和 [🤗 Datasets guide](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) 指南來完成 GitHub issue數據集的 README.md 文件。 + + + +很好! 我們在本節中看到,創建一個好的數據集可能非常複雜,但幸運的是,將其上傳並與社區共享會很容易實現。在下一節中,我們將使用我們的新數據集創建一個帶有 🤗 Datasets的語義搜索引擎,該數據集可以將issue與最相關的issue和評論進行匹配。 + + + +✏️ 試試看!按照我們在本節中採取的步驟為您最喜歡的開源庫創建一個 GitHub issue數據集(當然,選擇 🤗 Datasets以外的其他東西!)。對於獎勵積分,微調多標籤分類器以預測該領域中存在的標籤。 + + + diff --git a/chapters/zh-TW/chapter5/6.mdx b/chapters/zh-TW/chapter5/6.mdx new file mode 100644 index 000000000..b5646f739 --- /dev/null +++ b/chapters/zh-TW/chapter5/6.mdx @@ -0,0 +1,526 @@ + + +# 使用 FAISS 進行語義搜索 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +在[第五小節](/course/chapter5/5), 我們從 🤗 Datasets 存儲庫創建了一個包含 GitHub 問題和評論的數據集。在本節中,我們將使用這些信息來構建一個搜索引擎,它可以幫助我們找到這個庫最緊迫問題的答案! + + + +## 使用嵌入進行語義搜索 + +正如我們在[第一章](/course/chapter1),學習的, 基於 Transformer 的語言模型會將文本中的每個標記轉換為嵌入向量.事實證明,可以“彙集”各個嵌入向量來創建整個句子、段落或文檔(在某些情況下)的向量表示。然後,通過計算每個嵌入之間的點積相似度(或其他一些相似度度量)並返回相似度最大的文檔,這些嵌入可用於在語料庫中找到相似的文檔。在本節中,我們將使用嵌入來開發語義搜索引擎。與基於將查詢中的關鍵字的傳統方法相比,這些搜索引擎具有多種優勢。 + +
+Semantic search. + +
+ +## ## 加載和準備數據集 + +我們需要做的第一件事是下載我們的 GitHub 問題數據集,所以讓我們使用 🤗 Hub 庫來解析我們的文件在 Hugging Face Hub 上存儲的數據: + +```py +from huggingface_hub import hf_hub_url + +data_files = hf_hub_url( + repo_id="lewtun/github-issues", + filename="datasets-issues-with-comments.jsonl", + repo_type="dataset", +) +``` + +將 URL 存儲在 **data_files** ,然後我們可以使用[第二小節](/course/chapter5/2)介紹的方法加載遠程數據集: + +```py +from datasets import load_dataset + +issues_dataset = load_dataset("json", data_files=data_files, split="train") +issues_dataset +``` + +```python out +Dataset({ + features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'], + num_rows: 2855 +}) +``` + +這裡我們在load_dataset()中使用了默認的訓練集分割,所以它返回一個數據集而不是數據集字典。第一項任務是過濾掉pull請求,因為這些請求很少用於回答用戶提出的問題,而且會給我們的搜索引擎帶來噪聲。現在應該很熟悉了,我們可以使用dataset.filter()函數來排除數據集中的這些行。同時,讓我們也過濾掉沒有註釋的行,因為這些行不會是用戶提問的答案: + +```py +issues_dataset = issues_dataset.filter( + lambda x: (x["is_pull_request"] == False and len(x["comments"]) > 0) +) +issues_dataset +``` + +```python out +Dataset({ + features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'], + num_rows: 771 +}) +``` + +我們可以看到我們的數據集中有很多列,其中大部分我們不需要構建我們的搜索引擎。從搜索的角度來看,信息量最大的列是 **title** , **body** , 和 **comments** ,而 **html_url** 為我們提供了一個回到源問題的鏈接。讓我們使用 **Dataset.remove_columns()** 刪除其餘部分的功能: + +```py +columns = issues_dataset.column_names +columns_to_keep = ["title", "body", "html_url", "comments"] +columns_to_remove = set(columns_to_keep).symmetric_difference(columns) +issues_dataset = issues_dataset.remove_columns(columns_to_remove) +issues_dataset +``` + +```python out +Dataset({ + features: ['html_url', 'title', 'comments', 'body'], + num_rows: 771 +}) +``` + +為了創建我們的嵌入,我們將用問題的標題和正文來擴充每條評論,因為這些字段通常包含有用的上下文信息。因為我們的 **comments** 列當前是每個問題的評論列表,我們需要“重新組合”列,以便每一條評論都包含一個 **(html_url, title, body, comment)** 元組。在 Pandas 中,我們可以使用 [DataFrame.explode() 函數](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html), 它為類似列表的列中的每個元素創建一個新行,同時複製所有其他列值。為了看到它的實際效果,讓我們首先切換到 Pandas的**DataFrame** 格式: + +```py +issues_dataset.set_format("pandas") +df = issues_dataset[:] +``` + +如果我們檢查這裡的第一行 **DataFrame** 我們可以看到有四個評論與這個問題相關: + +```py +df["comments"][0].tolist() +``` + +```python out +['the bug code locate in :\r\n if data_args.task_name is not None:\r\n # Downloading and loading a dataset from the hub.\r\n datasets = load_dataset("glue", data_args.task_name, cache_dir=model_args.cache_dir)', + 'Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com\r\n\r\nNormally, it should work if you wait a little and then retry.\r\n\r\nCould you please confirm if the problem persists?', + 'cannot connect,even by Web browser,please check that there is some problems。', + 'I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...'] +``` + +我們希望這些評論中的每一條都得到一行。讓我們檢查是否是這種情況: + +```py +comments_df = df.explode("comments", ignore_index=True) +comments_df.head(4) +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
html_urltitlecommentsbody
0https://github.com/huggingface/datasets/issues/2787ConnectionError: Couldn't reach https://raw.githubusercontent.comthe bug code locate in :\r\n if data_args.task_name is not None...Hello,\r\nI am trying to run run_glue.py and it gives me this error...
1https://github.com/huggingface/datasets/issues/2787ConnectionError: Couldn't reach https://raw.githubusercontent.comHi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com...Hello,\r\nI am trying to run run_glue.py and it gives me this error...
2https://github.com/huggingface/datasets/issues/2787ConnectionError: Couldn't reach https://raw.githubusercontent.comcannot connect,even by Web browser,please check that there is some problems。Hello,\r\nI am trying to run run_glue.py and it gives me this error...
3https://github.com/huggingface/datasets/issues/2787ConnectionError: Couldn't reach https://raw.githubusercontent.comI can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...Hello,\r\nI am trying to run run_glue.py and it gives me this error...
+ +太好了,我們可以看到評論成功被擴充, **comments** 是包含個人評論的列!現在我們已經完成了 Pandas要完成的部分功能,我們可以快速切換回 **Dataset** 通過加載 **DataFrame** 在內存中: + +```py +from datasets import Dataset + +comments_dataset = Dataset.from_pandas(comments_df) +comments_dataset +``` + +```python out +Dataset({ + features: ['html_url', 'title', 'comments', 'body'], + num_rows: 2842 +}) +``` + +太好了,我們獲取到了幾千條的評論! + + + + +✏️ **Try it out!** 看看能不能不用pandas就可以完成列的擴充; 這有點棘手; 你可能會發現 🤗 Datasets 文檔的 ["Batch mapping"](https://huggingface.co/docs/datasets/v1.12.1/about_map_batch.html?batch-mapping#batch-mapping) 對這個任務很有用。 + + + +現在我們每行有一個評論,讓我們創建一個新的 **comments_length** 列來存放每條評論的字數: + +```py +comments_dataset = comments_dataset.map( + lambda x: {"comment_length": len(x["comments"].split())} +) +``` + +我們可以使用這個新列來過濾掉簡短的評論,其中通常包括“cc @lewtun”或“謝謝!”之類與我們的搜索引擎無關的內容。雖然無法為過濾器選擇的精確數字,但大約大於15 個單詞似乎是一個不錯的選擇: + +```py +comments_dataset = comments_dataset.filter(lambda x: x["comment_length"] > 15) +comments_dataset +``` + +```python out +Dataset({ + features: ['html_url', 'title', 'comments', 'body', 'comment_length'], + num_rows: 2098 +}) +``` + +稍微清理了我們的數據集後,讓我們將問題標題、描述和評論連接到一個新的 **text** 列。像往常一樣,我們可以編寫一個簡單的函數,並將其傳遞給 **Dataset.map()**來做到這些 : + +```py +def concatenate_text(examples): + return { + "text": examples["title"] + + " \n " + + examples["body"] + + " \n " + + examples["comments"] + } + + +comments_dataset = comments_dataset.map(concatenate_text) +``` + +我們終於準備好創建一些嵌入了!讓我們來看看。 + +## 創建文本嵌入 + +我們在[第二章](/course/chapter2) 學過,我們可以通過使用 **AutoModel** 類來完成詞嵌入。我們需要做的就是選擇一個合適的檢查點來加載模型。幸運的是,有一個名為 **sentence-transformers** 專門用於創建詞嵌入。如庫中[文檔](https://www.sbert.net/examples/applications/semantic-search/README.html#symmetric-vs-asymmetric-semantic-search), 所述的,我們這次要實現的是非對稱語義搜索,因為我們有一個簡短的查詢,我們希望在比如問題評論等更長的文檔中找到其答案。通過查看[模型概述表](https://www.sbert.net/docs/pretrained_models.html#model-overview) 我們可以發現 **multi-qa-mpnet-base-dot-v1** 檢查點在語義搜索方面具有最佳性能,因此我們將在我們的應用程序中使用它。我們還將使用相同的檢查點加載標記器: + +{#if fw === 'pt'} + +```py +from transformers import AutoTokenizer, AutoModel + +model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1" +tokenizer = AutoTokenizer.from_pretrained(model_ckpt) +model = AutoModel.from_pretrained(model_ckpt) +``` + +為了加快嵌入過程,將模型和輸入放在 GPU 設備上,所以現在讓我們這樣做: + +```py +import torch + +device = torch.device("cuda") +model.to(device) +``` + +{:else} + +```py +from transformers import AutoTokenizer, TFAutoModel + +model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1" +tokenizer = AutoTokenizer.from_pretrained(model_ckpt) +model = TFAutoModel.from_pretrained(model_ckpt, from_pt=True) +``` + +請注意,我們已將 from_pt=True 設置為 from_pretrained() 方法的參數。這是因為 multi-qa-mpnet-base-dot-v1 檢查點只有PyTorch權重,因此設置 from_pt=True 會自動將它們轉換為TensorFlow格式。如您所見,在Transformers中的🤗框架之間切換非常簡單! + +{/if} + +正如我們之前提到的,我們希望將 GitHub 問題語料庫中的每個條目表示為單個向量,因此我們需要以某種方式“池化”或平均化我們的標記嵌入。一種流行的方法是在我們模型的輸出上執行CLS 池化,我們只獲取**[CLS]** 令牌的最後一個隱藏狀態。以下函數為我們提供了這樣的方法: + +```py +def cls_pooling(model_output): + return model_output.last_hidden_state[:, 0] +``` + +接下來,我們將創建一個輔助函數,該函數將標記文檔列表,將tensor放在 GPU 上,然後提供給模型,最後對輸出使用CLS 池化: + +{#if fw === 'pt'} + +```py +def get_embeddings(text_list): + encoded_input = tokenizer( + text_list, padding=True, truncation=True, return_tensors="pt" + ) + encoded_input = {k: v.to(device) for k, v in encoded_input.items()} + model_output = model(**encoded_input) + return cls_pooling(model_output) +``` + +我們可以通過在我們的語料庫中輸入第一個文本條目並檢查輸出維度來測試該函數是否有效: + +```py +embedding = get_embeddings(comments_dataset["text"][0]) +embedding.shape +``` + +```python out +torch.Size([1, 768]) +``` + +太好了,我們已經將語料庫中的第一個條目轉換為 768 維向量!我們可以用 **Dataset.map()** 應用我們的 **get_embeddings()** 函數到我們語料庫中的每一行,所以讓我們創建一個新的 **embeddings** 列如下: + +```py +embeddings_dataset = comments_dataset.map( + lambda x: {"embeddings": get_embeddings(x["text"]).detach().cpu().numpy()[0]} +) +``` + +{:else} + +```py +def get_embeddings(text_list): + encoded_input = tokenizer( + text_list, padding=True, truncation=True, return_tensors="tf" + ) + encoded_input = {k: v for k, v in encoded_input.items()} + model_output = model(**encoded_input) + return cls_pooling(model_output) +``` + +我們可以通過在我們的語料庫中輸入第一個文本條目並檢查輸出維度來測試該函數是否有效: + +```py +embedding = get_embeddings(comments_dataset["text"][0]) +embedding.shape +``` + +```python out +TensorShape([1, 768]) +``` + +太好了,我們已經將語料庫中的第一個條目轉換為 768 維向量!我們可以用 **Dataset.map()** 應用我們的 **get_embeddings()** 函數到我們語料庫中的每一行,所以讓我們創建一個新的 **embeddings** 列如下: + +```py +embeddings_dataset = comments_dataset.map( + lambda x: {"embeddings": get_embeddings(x["text"]).numpy()[0]} +) +``` + +{/if} + +請注意,我們已經將嵌入轉換為 NumPy 數組——這是因為當我們嘗試使用 FAISS 索引它們時,🤗 Datasets需要這種格式,我們接下來會這樣做。 + + +## 使用 FAISS 進行高效的相似性搜索 + +現在我們有了一個詞嵌入數據集,我們需要一些方法來搜索它們。為此,我們將在 🤗 Datasets中使用一種特殊的數據結構,稱為 FAISS指數.[FAISS](https://faiss.ai/) (short for Facebook AI Similarity Search) (Facebook AI Similarity Search 的縮寫)是一個提供高效算法來快速搜索和聚類嵌入向量的庫。FAISS 背後的基本思想是創建一個特殊的數據結構,稱為指數。這允許人們找到哪些嵌入詞與輸入的詞嵌入相似。在 🤗 Datasets中創建一個 FAISS 索引很簡單——我們使用 **Dataset.add_faiss_index()** 函數並指定我們要索引的數據集的哪一列: + +```py +embeddings_dataset.add_faiss_index(column="embeddings") +``` + +現在,我們可以使用**Dataset.get_nearest_examples()**函數進行最近鄰居查找。讓我們通過首先嵌入一個問題來測試這一點,如下所示: + +{#if fw === 'pt'} + +```py +question = "How can I load a dataset offline?" +question_embedding = get_embeddings([question]).cpu().detach().numpy() +question_embedding.shape +``` + +```python out +torch.Size([1, 768]) +``` + +{:else} + +```py +question = "How can I load a dataset offline?" +question_embedding = get_embeddings([question]).numpy() +question_embedding.shape +``` + +```python out +(1, 768) +``` + +{/if} + +就像文檔一樣,我們現在有一個 768 維向量表示查詢,我們可以將其與整個語料庫進行比較以找到最相似的嵌入: + +```py +scores, samples = embeddings_dataset.get_nearest_examples( + "embeddings", question_embedding, k=5 +) +``` + + **Dataset.get_nearest_examples()** 函數返回一個分數元組,對查詢和文檔之間的相似度進行排序,以及一組最佳匹配的結果(這裡是 5 個)。讓我們把這些收集到一個 **pandas.DataFrame** 以便我們可以輕鬆地對它們進行排序: + +```py +import pandas as pd + +samples_df = pd.DataFrame.from_dict(samples) +samples_df["scores"] = scores +samples_df.sort_values("scores", ascending=False, inplace=True) +``` + +現在我們可以遍歷前幾行來查看我們的查詢與評論的匹配程度: + +```py +for _, row in samples_df.iterrows(): + print(f"COMMENT: {row.comments}") + print(f"SCORE: {row.scores}") + print(f"TITLE: {row.title}") + print(f"URL: {row.html_url}") + print("=" * 50) + print() +``` + +```python out +""" +COMMENT: Requiring online connection is a deal breaker in some cases unfortunately so it'd be great if offline mode is added similar to how `transformers` loads models offline fine. + +@mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like? +SCORE: 25.505046844482422 +TITLE: Discussion using datasets in offline mode +URL: https://github.com/huggingface/datasets/issues/824 +================================================== + +COMMENT: The local dataset builders (csv, text , json and pandas) are now part of the `datasets` package since #1726 :) +You can now use them offline +\`\`\`python +datasets = load_dataset("text", data_files=data_files) +\`\`\` + +We'll do a new release soon +SCORE: 24.555509567260742 +TITLE: Discussion using datasets in offline mode +URL: https://github.com/huggingface/datasets/issues/824 +================================================== + +COMMENT: I opened a PR that allows to reload modules that have already been loaded once even if there's no internet. + +Let me know if you know other ways that can make the offline mode experience better. I'd be happy to add them :) + +I already note the "freeze" modules option, to prevent local modules updates. It would be a cool feature. + +---------- + +> @mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like? + +Indeed `load_dataset` allows to load remote dataset script (squad, glue, etc.) but also you own local ones. +For example if you have a dataset script at `./my_dataset/my_dataset.py` then you can do +\`\`\`python +load_dataset("./my_dataset") +\`\`\` +and the dataset script will generate your dataset once and for all. + +---------- + +About I'm looking into having `csv`, `json`, `text`, `pandas` dataset builders already included in the `datasets` package, so that they are available offline by default, as opposed to the other datasets that require the script to be downloaded. +cf #1724 +SCORE: 24.14896583557129 +TITLE: Discussion using datasets in offline mode +URL: https://github.com/huggingface/datasets/issues/824 +================================================== + +COMMENT: > here is my way to load a dataset offline, but it **requires** an online machine +> +> 1. (online machine) +> +> ``` +> +> import datasets +> +> data = datasets.load_dataset(...) +> +> data.save_to_disk(/YOUR/DATASET/DIR) +> +> ``` +> +> 2. copy the dir from online to the offline machine +> +> 3. (offline machine) +> +> ``` +> +> import datasets +> +> data = datasets.load_from_disk(/SAVED/DATA/DIR) +> +> ``` +> +> +> +> HTH. + + +SCORE: 22.893993377685547 +TITLE: Discussion using datasets in offline mode +URL: https://github.com/huggingface/datasets/issues/824 +================================================== + +COMMENT: here is my way to load a dataset offline, but it **requires** an online machine +1. (online machine) +\`\`\` +import datasets +data = datasets.load_dataset(...) +data.save_to_disk(/YOUR/DATASET/DIR) +\`\`\` +2. copy the dir from online to the offline machine +3. (offline machine) +\`\`\` +import datasets +data = datasets.load_from_disk(/SAVED/DATA/DIR) +\`\`\` + +HTH. +SCORE: 22.406635284423828 +TITLE: Discussion using datasets in offline mode +URL: https://github.com/huggingface/datasets/issues/824 +================================================== +""" +``` + +我們的第二個搜索結果似乎與查詢相符。 + + + +✏️ 試試看!創建您自己的查詢並查看您是否可以在檢索到的文檔中找到答案。您可能需要增加參數k以擴大搜索範圍。 + + \ No newline at end of file diff --git a/chapters/zh-TW/chapter5/7.mdx b/chapters/zh-TW/chapter5/7.mdx new file mode 100644 index 000000000..4ece9ccb8 --- /dev/null +++ b/chapters/zh-TW/chapter5/7.mdx @@ -0,0 +1,16 @@ +# 🤗 Datasets,回顧! + + + +這是對 🤗 Datasets 庫的一次完整遊覽——祝賀你走到這一步!憑藉從本章中獲得的知識,您應該能夠: + +- 從任何地方加載數據集,無論是 Hugging Face Hub、您的筆記本電腦還是您公司的遠程服務器。 +- 混合使用Dataset.map()和Dataset.filter()函數來整理數據。 +- 使用`Dataset.set_format()`在 Pandas 和 NumPy 等數據格式之間快速切換. +- 創建您自己的數據集並將其推送到 Hugging Face Hub。. +- 使用 Transformer 模型為您的文檔創建詞嵌入,並使用 FAISS 構建語義搜索引擎。. + +在[第七章](/course/chapter7),當我們深入研究 Transformer 模型非常適合的核心 NLP 任務時,我們將充分利用所有這些。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter5/8.mdx b/chapters/zh-TW/chapter5/8.mdx new file mode 100644 index 000000000..b6c62af5e --- /dev/null +++ b/chapters/zh-TW/chapter5/8.mdx @@ -0,0 +1,221 @@ + + +# 章末小測試 + + + +本章涵蓋了很多方面! 如果你沒有掌握所有細節, 不用擔心; 在下一章將幫助你瞭解內部的事情是如何工作的。 + +不過, 在繼續下一章之前, 讓我們測試一下你在本章學到的內容。 + +### 1.🤗 Datasets中的 `load_dataset ()` 函數允許你從下列哪個位置加載數據集? +load_dataset() 函數的 data_files 參數來加載本地數據集。", + correct: true + }, + { + text: "Hugging Face Hub", + explain: "正確! 你可以通過提供數據集 ID 在 Hub 上加載數據集, 例如 < code > load _ dataset ('em otion') 。", + correct: true + }, + { + text: "遠程服務器", + explain: "正確! 你可以將URL傳遞給 load_dataset() 函數的 data_files 參數來加載遠程文件。", + correct: true + }, + ]} +/> + +### 2.假設您加載了 GLUE 任務,如下所示: +```py +from datasets import load_dataset + +dataset = load_dataset("glue", "mrpc", split="train") +``` + +以下哪個命令將從 `dataset` 中生成50個元素的隨機樣本? + + dataset.sample (50) ", + explain: "這是不正確的——沒有 < code > Dataset.sample () 方法。" + }, + { + text: "dataset.shuffle().select(range(50))", + explain: "正確! 正如你在本章中看待的, 你首先打亂了數據集, 然後從中選擇樣本。", + correct: true + }, + { + text: "dataset.select(range(50)).shuffle()", + explain: "這是不正確的——儘管代碼會運行, 但它只會隨機處理數據集中的前50個元素。" + } + ]} +/> + +### 3.假設你有一個叫做寵物數據集的家庭寵物數據集,它有一個名字列表示每個寵物的名字。下列哪種方法可以讓你過濾所有名字以字母"L"開頭的寵物的數據? + pets _ dataset. filter (lambda x: x ['name'] . startswith ('L')) ", + explain: "正確! 為這些快速過濾使用 Python lambda 函數是一個好主意。你還能想到其他解決方案嗎?", + correct: true + }, + { + text: "< code > pets _ dataset. filter (lambda x ['name'] . startswith ('L') ", + explain: "這是不正確的—— lambda 函數採用通用格式 < code > lambda * arguments * : * expression * , 因此在這種情況下需要提供參數。" + }, + { + text: "創建一個類似於 < code > def filter _ names (x) : return x ['name'] . startswith ('L') 的函數並運行 < code > pets _ dataset. filter (filter _ names) 。", + explain: "正確!就像使用 < code > Dataset.map () 一樣,你可以將顯式函數傳遞給 < code > Dataset.filter () 。當你有一些不適合於簡短 lambda 函數的複雜邏輯時,這是非常有用的。其他解決方案中還有哪一個可行?", + correct: true + } + ]} +/> + +### 4.什麼是內存映射? + + +### 5.下列哪一項是內存映射的主要好處? + + +### 6.為什麼下面的代碼是錯誤的? +```py +from datasets import load_dataset + +dataset = load_dataset("allocine", streaming=True, split="train") +dataset[0] +``` + + IterableDataset 。", + explain: "正確! < code > IterableDataset 是一個生成器, 而不是一個容器, 因此你應該使用 < code > next (iter (dataset)) 來訪問它的元素。", + correct: true + }, + { + text: "數據集 < code > allocine 沒有分割< code >訓練集。", + explain: "這是不正確的---- 查看 Hub 上的[ < code > allocine dataset card ]( https://huggingface.co/datasets/allocine ), 看看它包含哪些拆分。" + } + ]} +/> + +### 7.創建數據集卡的主要好處是什麼? + + + +### 8.什麼是語義搜索? + + +### 9.對於非對稱語義搜索,通常有: + + +### 10.我可以使用數據集加載數據用於其他領域,如語音處理? + diff --git a/chapters/zh-TW/chapter6/1.mdx b/chapters/zh-TW/chapter6/1.mdx new file mode 100644 index 000000000..725a405f0 --- /dev/null +++ b/chapters/zh-TW/chapter6/1.mdx @@ -0,0 +1,19 @@ +# 本章簡介 + + + +在 [第三章] (/course/chapter3) 中,我們研究瞭如何在給定任務上微調模型。 當我們這樣做時,我們需要使用與模型預訓練相同的標記器——但是當我們想從頭開始訓練模型時該怎麼辦? 不過,使用在來自其他領域或語言的語料庫上預訓練的標記器通常不是最理想的。 例如,在英語語料庫上訓練的標記器在日語文本語料庫上表現不佳,因為兩種語言中空格和標點符號的使用非常不同。 + +在本章中,您將學習如何在文本語料庫上訓練一個全新的標記器,然後將其用於預訓練語言模型。 這一切都將在 [🤗 Tokenizers](https://github.com/huggingface/tokenizers) 庫的幫助下完成,該庫在 [🤗 Transformers](https://github.com /huggingface/transformers) 庫之內。 我們將仔細研究這個庫提供的功能,並探討快速標記器與“慢”版本的區別。 + +我們將涵蓋的主題包括: + +* 如何訓練一個新的標記器,類似於給定檢查點在新的文本語料庫上使用的標記器 +* 快速標記器的特殊功能 +* 目前 NLP 中使用的三種主要子詞標記化算法之間的差異 +* 如何使用🤗 Tokenizers 庫從頭開始構建標記器並在一些數據上對其進行訓練 + +本章介紹的技術將使您為 [第 7 章](/course/chapter7/6) 中的部分做好準備,在那部分中,我們著眼於為 Python 源代碼創建語言模型。 讓我們首先看一下什麼是“訓練”標記器? \ No newline at end of file diff --git a/chapters/zh-TW/chapter6/10.mdx b/chapters/zh-TW/chapter6/10.mdx new file mode 100644 index 000000000..5064e77bc --- /dev/null +++ b/chapters/zh-TW/chapter6/10.mdx @@ -0,0 +1,273 @@ + + +# 章末小測驗 + + + +讓我們測試一下您在本章中學到了什麼! + +### 1.你應該什麼時候訓練一個新的標記器? + + +### 2.當使用“ train_new_from_iterator()”時,使用文本列表生成器與文本列表相比有什麼優點? + train_new_from_iterator() 接受的唯一類型。", + explain: "文本列表是一種特殊的文本列表生成器,因此該方法也會接受這種方法。再試一次!" + }, + { + text: "您將避免立即將整個數據集載入內存中。", + explain: "沒錯!每一批文本都會在你迭代的時候從內存中釋放出來,如果你使用數據集存儲文本的話,增益將尤其明顯。", + correct: true + }, + { + text: "這將允許 Tokenizers 庫使用並行處理。", + explain: "不,無論如何它都將使用並行處理。" + }, + { + text: "你訓練的標記器將產生更好的文本。", + explain: "Tokenizer 不生成文本——您是否將其與語言模型混淆了?" + } + ]} +/> + +### 3.使用“快速”標記器的優點是什麼? + + +### 4.“token-classification”管道如何處理跨多個標記的實體? + + +### 5.“question-answering”流水線如何處理長上下文? + + +### 6.什麼是標準化? + + +### 7.什麼是子詞標記化的前標記化? + + +### 8.選擇描述標記化 BPE 模式最準確的句子。 + + +### 9.選擇適用於 WordPiece 標記模型的句子。 + + +### 10.選擇適用於 Unigram 標記模式的句子。 + diff --git a/chapters/zh-TW/chapter6/2.mdx b/chapters/zh-TW/chapter6/2.mdx new file mode 100644 index 000000000..57c320314 --- /dev/null +++ b/chapters/zh-TW/chapter6/2.mdx @@ -0,0 +1,256 @@ +# 根據已有的tokenizer訓練新的tokenizer + + + +如果您感興趣的語言中沒有可用的語言模型,或者如果您的語料庫與您的語言模型所訓練的語料庫有很大不同,您很可能希望從適合您的數據的標記器從頭開始重新訓練模型 . 這將需要在您的數據集上訓練一個新的標記器。 但這究竟是什麼意思? 當我們在 [第二章](/course/chapter2) 中第一次查看標記器時,我們看到大多數 Transformer 模型使用_子詞分詞算法_。 為了識別哪些子詞是感興趣的並且在手頭的語料庫中最常出現,標記器需要仔細查看語料庫中的所有文本——我們稱之為*training*的過程。 這種訓練的確切規則取決於所使用的標記器的類型,我們將在本章後面討論三種主要算法。 + + + + + +⚠️ 訓練標記器與訓練模型不同!模型訓練使用隨機梯度下降使每個batch的loss小一點。它本質上是隨機的(這意味著在進行兩次相同的訓練時,您必須設置一些隨機數種子才能獲得相同的結果)。訓練標記器是一個統計過程,它試圖確定哪些子詞最適合為給定的語料庫選擇,用於選擇它們的確切規則取決於分詞算法。它是確定性的,這意味著在相同的語料庫上使用相同的算法進行訓練時,您總是會得到相同的結果。 + + + +## 準備語料庫 + +🤗 Transformers 中有一個非常簡單的 API,你可以用它來訓練一個新的標記器,使它與現有標記器相同的特徵: **AutoTokenizer.train_new_from_iterator()** .為了復現這一點,假設我們想從頭開始訓練 GPT-2,但使用英語以外的語言。我們的首要任務是在訓練語料庫中收集該語言的大量數據。為了提供每個人都能理解的示例,我們在這裡不會使用俄語或中文之類的語言,而是使用在特定領域的英語語言:Python 代碼。 + +[🤗 Datasets](https://github.com/huggingface/datasets)庫可以幫助我們組裝一個 Python 源代碼語料庫。我們將使用**load_dataset()**功能下載和緩存[CodeSearchNet](https://huggingface.co/datasets/code_search_net)數據集。該數據集是為[CodeSearchNet 挑戰](https://wandb.ai/github/CodeSearchNet/benchmark)而創建的幷包含來自 GitHub 上開源庫的數百萬種編程語言的函數。在這裡,我們將加載此數據集的 Python 部分: + +```py +from datasets import load_dataset + +# This can take a few minutes to load, so grab a coffee or tea while you wait! +raw_datasets = load_dataset("code_search_net", "python") +``` + +我們可以查看訓練集的部分,以查看我們數據集中有哪些列: + +```py +raw_datasets["train"] +``` + +```python out +Dataset({ + features: ['repository_name', 'func_path_in_repository', 'func_name', 'whole_func_string', 'language', + 'func_code_string', 'func_code_tokens', 'func_documentation_string', 'func_documentation_tokens', 'split_name', + 'func_code_url' + ], + num_rows: 412178 +}) +``` + +我們可以看到數據集將文檔字符串與代碼分開,並且有他們各自的標記化後的結果。 這裡。 我們將只使用 `whole_func_string` 列來訓練我們的標記器。 我們可以通過指定到 `train` 中的一部分來查看這些函數的一個示例: + +```py +print(raw_datasets["train"][123456]["whole_func_string"]) +``` + +應該打印以下內容: + +```out +def handle_simple_responses( + self, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK): + """Accepts normal responses from the device. + + Args: + timeout_ms: Timeout in milliseconds to wait for each response. + info_cb: Optional callback for text sent from the bootloader. + + Returns: + OKAY packet's message. + """ + return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms) +``` + +我們需要做的第一件事是將數據集轉換為迭代器文本列表 - 例如,文本列表。使用文本列表將使我們的標記器運行得更快(訓練成批文本而不是一個接一個地處理單個文本),如果我們想避免一次將所有內容都放在內存中,它應該是一個迭代器。如果你的語料庫很大,你會想要利用這樣一個特性:🤗 Datasets 不會將所有內容都加載到 RAM 中,而是將數據集的元素存儲在磁盤上。 + +執行以下操作將創建一個包含 1,000 個文本的列表的列表,但會將所有內容加載到內存中: + +```py +# Don't uncomment the following line unless your dataset is small! +# training_corpus = [raw_datasets["train"][i: i + 1000]["whole_func_string"] for i in range(0, len(raw_datasets["train"]), 1000)] +``` + +使用 Python 生成器,我們可以避免 Python 將任何內容加載到內存中,直到真正需要為止。要創建這樣的生成器,您只需要將括號替換為圓括號: + +```py +training_corpus = ( + raw_datasets["train"][i : i + 1000]["whole_func_string"] + for i in range(0, len(raw_datasets["train"]), 1000) +) +``` + +這行代碼不會獲取數據集的任何元素;它只是創建了一個可以在 Python 中使用的對象 **for** 環形。文本只會在您需要時加載(即,當您處於 **for** 需要它們的循環),並且一次只會加載 1,000 個文本。這樣,即使您正在處理龐大的數據集,也不會耗盡所有內存。 + +生成器對象的問題在於它只能使用一次,每次訪問它將給出下一個值。 下面是一個例子: + +```py +gen = (i for i in range(10)) +print(list(gen)) +print(list(gen)) +``` + +我們第一次得到了這個列表,然後是一個空列表: + +```python out +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +[] +``` + +這就是我們定義一個返回生成器的函數的原因: + +```py +def get_training_corpus(): + return ( + raw_datasets["train"][i : i + 1000]["whole_func_string"] + for i in range(0, len(raw_datasets["train"]), 1000) + ) + + +training_corpus = get_training_corpus() +``` + +您還可以在一個 **for** 循環內部使用 **yield** 關鍵字定義您的生成器: + +```py +def get_training_corpus(): + dataset = raw_datasets["train"] + for start_idx in range(0, len(dataset), 1000): + samples = dataset[start_idx : start_idx + 1000] + yield samples["whole_func_string"] +``` + +這將產生與以前完全相同的生成器,但允許您使用比列表生成式中更復雜的邏輯。 + +## 訓練一個新的標記器 + +現在我們的語料庫是文本批量迭代器的形式,我們準備訓練一個新的標記器。為此,我們首先需要加載要與模型配對的標記器(此處為 GPT-2): + +```py +from transformers import AutoTokenizer + +old_tokenizer = AutoTokenizer.from_pretrained("gpt2") +``` + +即使我們要訓練一個新的標記器,最好還是這樣做以避免完全從頭開始。這樣,我們就不必指定任何關於標記化算法或我們想要使用的特殊標記;我們的新標記器將與 GPT-2 完全相同,唯一會改變的是輸入的數據,這將取決於我們訓練的語料。 + +首先讓我們看看這個標記器將如何處理示例的數據: + +```py +example = '''def add_numbers(a, b): + """Add the two numbers `a` and `b`.""" + return a + b''' + +tokens = old_tokenizer.tokenize(example) +tokens +``` + +```python out +['def', 'Ġadd', '_', 'n', 'umbers', '(', 'a', ',', 'Ġb', '):', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo', + 'Ġnumbers', 'Ġ`', 'a', '`', 'Ġand', 'Ġ`', 'b', '`', '."', '""', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb'] +``` + +這個標記器有一些特殊的符號,比如 **Ċ** 和 **Ġ** ,分別表示空格和換行符。正如我們所看到的,這不是太有效:標記器為每個空格返回單獨的標記,當它可以將縮進級別組合在一起時(因為在代碼中具有四個或八個空格的集合將非常普遍)。它也有點奇怪地拆分了函數名稱,而習慣使用**_**的函數命名的方法。 + +讓我們訓練一個新的標記器,看看它是否能解決這些問題。為此,我們將使用 **train_new_from_iterator()** 方法: + +```py +tokenizer = old_tokenizer.train_new_from_iterator(training_corpus, 52000) +``` +如果您的語料庫非常大,此命令可能需要一些時間,但對於這個 1.6 GB 文本數據集,它的速度非常快(在具有 12 個內核的 AMD Ryzen 9 3900X CPU 上為 1 分 16 秒)。 + +注意 **AutoTokenizer.train_new_from_iterator()** 僅當您使用的標記器是“快速(fast)”標記器時才有效。正如您將在下一節中看到的,🤗 Transformers 庫包含兩種類型的標記器:一些完全用 Python 編寫,而另一些(快速的)由 🤗 Tokenizers 庫支持,該庫用[Rust](https://www.rust-lang.org)編程語言編寫。 Python 是最常用於數據科學和深度學習應用程序的語言,但是當需要並行化以提高速度時,必須用另一種語言編寫。例如,模型計算核心的矩陣乘法是用 CUDA 編寫的,CUDA 是一個針對 GPU 的優化 C 庫。 + +用純 Python 訓練一個全新的標記器會非常緩慢,這就是我們開發 🤗 Tokenizers庫的原因。請注意,正如您無需學習 CUDA 語言即可在 GPU 上執行您的模型一樣,您也無需學習 Rust 即可使用快速標記器。 🤗 Tokenizers 庫為許多內部調用 Rust 代碼的方法提供 Python 綁定;例如,並行化新標記器的訓練,或者,正如我們在[第三章](/course/chapter3)中看到的,對一批輸入進行標記化。 + +大多數 Transformer 模型都有可用的快速標記器(您可以[在這裡](https://huggingface.co/transformers/#supported-frameworks)檢查一些例外情況),如果 **AutoTokenizer** 可用,API 總是為您選擇快速標記器。在下一節中,我們將看看快速標記器具有的其他一些特殊功能,這些功能對於標記分類和問答等任務非常有用。然而,在深入研究之前,讓我們在上一個示例中嘗試我們全新的標記器: + +```py +tokens = tokenizer.tokenize(example) +tokens +``` + +```python out +['def', 'Ġadd', '_', 'numbers', '(', 'a', ',', 'Ġb', '):', 'ĊĠĠĠ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo', 'Ġnumbers', 'Ġ`', + 'a', '`', 'Ġand', 'Ġ`', 'b', '`."""', 'ĊĠĠĠ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb'] +``` + +在這裡我們再次看到特殊符號 **Ċ** 和 **Ġ** 表示空格和換行符,但我們也可以看到我們的標記器學習了一些高度特定於 Python 函數語料庫的標記:例如,有一個 **ĊĠĠĠ** 表示縮進的標記,以及 **Ġ** 表示開始文檔字符串的三個引號的標記。標記器還正確使用**_**命名的規範將函數名稱拆分為 .這是一個非常緊湊的表示;相比之下,在同一個例子中使用簡單的英語標記器會給我們一個更長的句子: + +```py +print(len(tokens)) +print(len(old_tokenizer.tokenize(example))) +``` + +```python out +27 +36 +``` + +讓我們再看一個例子: + +```python +example = """class LinearLayer(): + def __init__(self, input_size, output_size): + self.weight = torch.randn(input_size, output_size) + self.bias = torch.zeros(output_size) + + def __call__(self, x): + return x @ self.weights + self.bias + """ +tokenizer.tokenize(example) +``` + +```python out +['class', 'ĠLinear', 'Layer', '():', 'ĊĠĠĠ', 'Ġdef', 'Ġ__', 'init', '__(', 'self', ',', 'Ġinput', '_', 'size', ',', + 'Ġoutput', '_', 'size', '):', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'weight', 'Ġ=', 'Ġtorch', '.', 'randn', '(', 'input', '_', + 'size', ',', 'Ġoutput', '_', 'size', ')', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'bias', 'Ġ=', 'Ġtorch', '.', 'zeros', '(', + 'output', '_', 'size', ')', 'ĊĊĠĠĠ', 'Ġdef', 'Ġ__', 'call', '__(', 'self', ',', 'Ġx', '):', 'ĊĠĠĠĠĠĠĠ', + 'Ġreturn', 'Ġx', 'Ġ@', 'Ġself', '.', 'weights', 'Ġ+', 'Ġself', '.', 'bias', 'ĊĠĠĠĠ'] +``` + +除了一個縮進對應的token,這裡我們還可以看到一個雙縮進的token: **ĊĠĠĠĠĠĠĠ** .特殊的 Python 詞如 **class** , **init** , **call** , **self** , 和 **return** 每個都被標記為一個標記,我們可以看到,以及分裂 **_** 和 **.** 標記器甚至可以正確拆分駝峰式名稱: **LinearLayer** 被標記為 **[ĠLinear, Layer]** . + +## 保存標記器 + +為了確保我們以後可以使用它,我們需要保存我們的新標記器。就像模型一樣,是通過 **save_pretrained()** 方法: + +```py +tokenizer.save_pretrained("code-search-net-tokenizer") +``` + +這將創建一個名為的*code-search-net-tokenizer*的新文件夾,它將包含重新加載標記器所需要的所有文件。如果您想與您的同事和朋友分享這個標記器,您可以通過登錄您的帳戶將其上傳到 Hub。如果您在notebook上工作,有一個方便的功能可以幫助您: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +這將顯示一個小部件,您可以在其中輸入您的 Hugging Face 登錄憑據。如果您不是在notebook上工作,只需在終端中輸入以下行: + +```bash +huggingface-cli login +``` + +登錄後,您可以通過執行以下命令來推送您的標記器: + +```py +tokenizer.push_to_hub("code-search-net-tokenizer") +``` + +這將在您的命名空間中創建一個名為**code-search-net-tokenizer**的新存儲庫 ,包含標記器文件。然後,您可以使用以下命令從任何地方加載標記器的 **from_pretrained()** 方法: + +```py +# Replace "huggingface-course" below with your actual namespace to use your own tokenizer +tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer") +``` + +您現在已準備好從頭開始訓練語言模型並根據您手頭的任務對其進行微調!我們將在[第七章](/course/chapter7)進行這部分。但首先,在本章的其餘部分,我們將仔細研究快速標記器,並詳細探討調用 **train_new_from_iterator()** 方法時實際發生的情況 . diff --git a/chapters/zh-TW/chapter6/3.mdx b/chapters/zh-TW/chapter6/3.mdx new file mode 100644 index 000000000..c35a3e3ca --- /dev/null +++ b/chapters/zh-TW/chapter6/3.mdx @@ -0,0 +1,473 @@ + + +# 快速標記器的特殊能力 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +在本節中,我們將仔細研究 🤗 Transformers 中標記器的功能。到目前為止,我們只使用它們來標記輸入或將 ID 解碼迴文本,但是標記器——尤其是那些由 🤗 Tokenizers 庫支持的——可以做更多的事情。為了說明這些附加功能,我們將探索如何重現結果 **token-classification** (我們稱之為 **ner** ) 和 **question-answering** 我們第一次在[Chapter 1](/course/chapter1)中遇到的管道. + + + +在接下來的討論中,我們會經常區分“慢”和“快”分詞器。慢速分詞器是在 🤗 Transformers 庫中用 Python 編寫的,而快速版本是由 🤗 分詞器提供的,它們是用 Rust 編寫的。如果你還記得在[Chapter 5](/course/chapter5/3)中報告了快速和慢速分詞器對藥物審查數據集進行分詞所需的時間的這張表,您應該知道為什麼我們稱它們為“快”和“慢”: + + | Fast tokenizer | Slow tokenizer +:--------------:|:--------------:|:-------------: +`batched=True` | 10.8s | 4min41s +`batched=False` | 59.2s | 5min3s + + + +⚠️ 對單個句子進行分詞時,您不會總是看到相同分詞器的慢速和快速版本之間的速度差異。事實上,快速版本實際上可能更慢!只有同時對大量文本進行標記時,您才能清楚地看到差異。 + + + +## 批量編碼 + + + +分詞器的輸出不是簡單的 Python 字典;我們得到的實際上是一個特殊的 **BatchEncoding** 目的。它是字典的子類(這就是為什麼我們之前能夠毫無問題地索引到該結果中的原因),但具有主要由快速標記器使用的附加方法。 + +除了它們的並行化能力之外,快速標記器的關鍵功能是它們始終跟蹤最終標記來自的原始文本範圍——我們稱之為偏移映射.這反過來又解鎖了諸如將每個單詞映射到它生成的標記或將原始文本的每個字符映射到它內部的標記等功能,反之亦然。讓我們看一個例子: + +```py +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") +example = "My name is Sylvain and I work at Hugging Face in Brooklyn." +encoding = tokenizer(example) +print(type(encoding)) +``` + +如前所述,我們得到一個 **BatchEncoding** 標記器輸出中的對象: + +```python out + +``` + +由於 **AutoTokenizer** 類默認選擇快速標記器,我們可以使用附加方法 this **BatchEncoding** 對象提供。我們有兩種方法來檢查我們的分詞器是快的還是慢的。我們可以檢查 **is_fast** 的屬性 **tokenizer** : + +```python +tokenizer.is_fast +``` + +```python out +True +``` + +或檢查我們的相同屬性 **encoding** : + +```python +encoding.is_fast +``` + +```python out +True +``` + +讓我們看看快速標記器使我們能夠做什麼。首先,我們可以訪問令牌而無需將 ID 轉換回令牌: + +```py +encoding.tokens() +``` + +```python out +['[CLS]', 'My', 'name', 'is', 'S', '##yl', '##va', '##in', 'and', 'I', 'work', 'at', 'Hu', '##gging', 'Face', 'in', + 'Brooklyn', '.', '[SEP]'] +``` + +在這種情況下,索引 5 處的令牌是 **##yl** ,它是原始句子中“Sylvain”一詞的一部分。我們也可以使用 **word_ids()** 獲取每個標記來自的單詞索引的方法: + +```py +encoding.word_ids() +``` + +```python out +[None, 0, 1, 2, 3, 3, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, None] +``` + +我們可以看到分詞器的特殊標記 **[CLS]** 和 **[SEP]** 被映射到 **None** ,然後每個標記都映射到它起源的單詞。這對於確定一個標記是否在單詞的開頭或兩個標記是否在同一個單詞中特別有用。我們可以依靠 **##** 前綴,但它僅適用於類似 BERT 的分詞器;這種方法適用於任何類型的標記器,只要它是快速的。在下一章中,我們將看到如何使用此功能將每個單詞的標籤正確應用於命名實體識別 (NER) 和詞性 (POS) 標記等任務中的標記。我們還可以使用它來屏蔽來自屏蔽語言建模中來自同一單詞的所有標記(一種稱為全詞掩碼)。 + + + +一個詞是什麼的概念很複雜。例如,“I'll”(“I will”的縮寫)算一兩個詞嗎?它實際上取決於分詞器和它應用的預分詞操作。一些標記器只是在空格上拆分,因此他們會將其視為一個詞。其他人在空格頂部使用標點符號,因此將其視為兩個詞。 + +✏️ 試試看!從bert base cased和roberta base檢查點創建一個標記器,並用它們標記“81s”。你觀察到了什麼?ID這個詞是什麼? + + + +同樣,有一個 **sentence_ids()** 我們可以用來將標記映射到它來自的句子的方法(儘管在這種情況下, **token_type_ids** 分詞器返回的信息可以為我們提供相同的信息)。 + +最後,我們可以將任何單詞或標記映射到原始文本中的字符,反之亦然,通過 **word_to_chars()** 或者 **token_to_chars()** 和 **char_to_word()** 或者 **char_to_token()** 方法。例如, **word_ids()** 方法告訴我們 **##yl** 是索引 3 處單詞的一部分,但它是句子中的哪個單詞?我們可以這樣發現: + +```py +start, end = encoding.word_to_chars(3) +example[start:end] +``` + +```python out +Sylvain +``` + +正如我們之前提到的,這一切都是由快速標記器跟蹤每個標記來自列表中的文本跨度這一事實提供支持的抵消.為了說明它們的用途,接下來我們將向您展示如何複製結果 **token-classification** 手動管道。 + + + +✏️ 試試看!創建您自己的示例文本,看看您是否能理解哪些標記與單詞 ID 相關聯,以及如何提取單個單詞的字符跨度。對於獎勵積分,請嘗試使用兩個句子作為輸入,看看句子 ID 是否對您有意義。 + + + +## 在令牌分類管道內 + +在[Chapter 1](/course/chapter1)我們第一次嘗試使用 NER——任務是識別文本的哪些部分對應於個人、地點或組織等實體——使用 🤗 Transformers **pipeline()** 功能。然後,在[Chapter 2](/course/chapter2),我們看到了管道如何將從原始文本中獲取預測所需的三個階段組合在一起:標記化、通過模型傳遞輸入和後處理。前兩步 **token-classification** 管道與任何其他管道相同,但後處理稍微複雜一些 - 讓我們看看如何! + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +### 通過管道獲得基本結果 + +首先,讓我們獲取一個標記分類管道,以便我們可以手動比較一些結果。默認使用的模型是[dbmdz/bert-large-cased-finetuned-conll03-english](https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english);它對句子執行 NER: + +```py +from transformers import pipeline + +token_classifier = pipeline("token-classification") +token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.") +``` + +```python out +[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12}, + {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14}, + {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16}, + {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18}, + {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35}, + {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40}, + {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45}, + {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}] +``` + +該模型正確地將“Sylvain”生成的每個標記識別為一個人,將“Hugging Face”生成的每個標記識別為一個組織,將“Brooklyn”生成的標記識別為一個位置。我們還可以要求管道將對應於同一實體的令牌組合在一起: + +```py +from transformers import pipeline + +token_classifier = pipeline("token-classification", aggregation_strategy="simple") +token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.") +``` + +```python out +[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18}, + {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45}, + {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}] +``` + +**aggregation_strategy** 選擇將更改為每個分組實體計算的分數。和 **simple** 分數只是給定實體中每個標記的分數的平均值:例如,“Sylvain”的分數是我們在前面的示例中看到的標記分數的平均值 **S** , **##yl** , **##va** , 和 **##in** .其他可用的策略是: + +- `"first"`, 其中每個實體的分數是該實體的第一個標記的分數(因此對於“Sylvain”,它將是 0.993828,標記的分數) + +- `"max"`,其中每個實體的分數是該實體中標記的最大分數(因此對於“Hugging Face”,它將是 0.98879766,即“Face”的分數) + +- `"average"`, 其中每個實體的分數是組成該實體的單詞分數的平均值(因此對於“Sylvain”,與“simple”策略,但“Hugging Face”的得分為 0.9819,“Hugging”得分的平均值為 0.975,“Face”得分為 0.98879) + +現在讓我們看看如何在不使用pipeline()函數的情況下獲得這些結果! + +### 從輸入到預測 + +{#if fw === 'pt'} + +首先,我們需要標記我們的輸入並將其傳遞給模型。這是完全按照[Chapter 2](/course/chapter2);我們使用 **AutoXxx** 類,然後在我們的示例中使用它們: + +```py +from transformers import AutoTokenizer, AutoModelForTokenClassification + +model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +model = AutoModelForTokenClassification.from_pretrained(model_checkpoint) + +example = "My name is Sylvain and I work at Hugging Face in Brooklyn." +inputs = tokenizer(example, return_tensors="pt") +outputs = model(**inputs) +``` + +由於我們正在使用 **AutoModelForTokenClassification** 在這裡,我們為輸入序列中的每個標記獲得一組 logits: + +```py +print(inputs["input_ids"].shape) +print(outputs.logits.shape) +``` + +```python out +torch.Size([1, 19]) +torch.Size([1, 19, 9]) +``` + +{:else} + +首先,我們需要標記我們的輸入並將其傳遞給模型。這是完全按照[Chapter 2](/course/chapter2);我們使用 **AutoXxx** 類,然後在我們的示例中使用它們: + +```py +from transformers import AutoTokenizer, TFAutoModelForTokenClassification + +model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +model = TFAutoModelForTokenClassification.from_pretrained(model_checkpoint) + +example = "My name is Sylvain and I work at Hugging Face in Brooklyn." +inputs = tokenizer(example, return_tensors="tf") +outputs = model(**inputs) +``` + +於我們正在使用 **AutoModelForTokenClassification** 在這裡,我們為輸入序列中的每個標記獲得一組 logits: + +```py +print(inputs["input_ids"].shape) +print(outputs.logits.shape) +``` + +```python out +(1, 19) +(1, 19, 9) +``` + +{/if} + +我們有一個包含 19 個標記的 1 個序列的批次,模型有 9 個不同的標籤,因此模型的輸出具有 1 x 19 x 9 的形狀。與文本分類管道一樣,我們使用 softmax 函數來轉換這些 logits到概率,我們採用 argmax 來獲得預測(請注意,我們可以在 logits 上採用 argmax,因為 softmax 不會改變順序): + +{#if fw === 'pt'} + +```py +import torch + +probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)[0].tolist() +predictions = outputs.logits.argmax(dim=-1)[0].tolist() +print(predictions) +``` + +{:else} + +```py +import tensorflow as tf + +probabilities = tf.math.softmax(outputs.logits, axis=-1)[0] +probabilities = probabilities.numpy().tolist() +predictions = tf.math.argmax(outputs.logits, axis=-1)[0] +predictions = predictions.numpy().tolist() +print(predictions) +``` + +{/if} + +```python out +[0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0] +``` + + **model.config.id2label** 屬性包含索引到標籤的映射,我們可以用它來理解預測: + +```py +model.config.id2label +``` + +```python out +{0: 'O', + 1: 'B-MISC', + 2: 'I-MISC', + 3: 'B-PER', + 4: 'I-PER', + 5: 'B-ORG', + 6: 'I-ORG', + 7: 'B-LOC', + 8: 'I-LOC'} +``` + +正如我們之前看到的,有 9 個標籤: **O** 是不在任何命名實體中的標記的標籤(它代表“外部”),然後我們為每種類型的實體(雜項、人員、組織和位置)提供兩個標籤。標籤 **B-XXX** 表示令牌在實體的開頭 **XXX** 和標籤 **I-XXX** 表示令牌在實體內 **XXX** .例如,在當前示例中,我們希望我們的模型對令牌進行分類 **S** 作為 **B-PER** (一個人實體的開始)和令牌 **##yl** , **##va** 和 **##in** 作為 **I-PER** (在個人實體內) + +在這種情況下,您可能認為模型是錯誤的,因為它給出了標籤 **I-PER** 對所有這四個令牌,但這並不完全正確。實際上有兩種格式 **B-** 和 **I-** 標籤:IOB1和IOB2. IOB2 格式(下面粉紅色)是我們介紹的格式,而在 IOB1 格式(藍色)中,標籤以 **B-** 僅用於分隔相同類型的兩個相鄰實體。我們使用的模型在使用該格式的數據集上進行了微調,這就是它分配標籤的原因 **I-PER** 到 **S** 令牌。 + +
+IOB1 vs IOB2 format + +
+ +了這張地圖,我們已經準備好(幾乎完全)重現第一個管道的結果——我們可以獲取每個未被歸類為的標記的分數和標籤 **O** : + +```py +results = [] +tokens = inputs.tokens() + +for idx, pred in enumerate(predictions): + label = model.config.id2label[pred] + if label != "O": + results.append( + {"entity": label, "score": probabilities[idx][pred], "word": tokens[idx]} + ) + +print(results) +``` + +```python out +[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S'}, + {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl'}, + {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va'}, + {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in'}, + {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu'}, + {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging'}, + {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face'}, + {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn'}] +``` + +這與我們之前的情況非常相似,只有一個例外:管道還為我們提供了有關 **start** 和 **end** 原始句子中的每個實體。這是我們的偏移映射將發揮作用的地方。要獲得偏移量,我們只需要設置 **return_offsets_mapping=True** 當我們將分詞器應用於我們的輸入時: + +```py +inputs_with_offsets = tokenizer(example, return_offsets_mapping=True) +inputs_with_offsets["offset_mapping"] +``` + +```python out +[(0, 0), (0, 2), (3, 7), (8, 10), (11, 12), (12, 14), (14, 16), (16, 18), (19, 22), (23, 24), (25, 29), (30, 32), + (33, 35), (35, 40), (41, 45), (46, 48), (49, 57), (57, 58), (0, 0)] +``` + +每個元組是對應於每個標記的文本跨度,其中 **(0, 0)** 保留用於特殊令牌。我們之前看到索引 5 處的令牌是 **##yl** , 其中有 **(12, 14)** 作為這裡的抵消。如果我們在示例中抓取相應的切片: + + +```py +example[12:14] +``` + +我們得到了正確的文本跨度,而沒有 **##** : + +```python out +yl +``` + +使用這個,我們現在可以完成之前的結果: + +```py +results = [] +inputs_with_offsets = tokenizer(example, return_offsets_mapping=True) +tokens = inputs_with_offsets.tokens() +offsets = inputs_with_offsets["offset_mapping"] + +for idx, pred in enumerate(predictions): + label = model.config.id2label[pred] + if label != "O": + start, end = offsets[idx] + results.append( + { + "entity": label, + "score": probabilities[idx][pred], + "word": tokens[idx], + "start": start, + "end": end, + } + ) + +print(results) +``` + +```python out +[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12}, + {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14}, + {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16}, + {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18}, + {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35}, + {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40}, + {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45}, + {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}] +``` + +這和我們從第一個管道中得到的一樣! + +### 分組實體 + +使用偏移量來確定每個實體的開始和結束鍵很方便,但該信息並不是絕對必要的。然而,當我們想要將實體組合在一起時,偏移量將為我們節省大量混亂的代碼。例如,如果我們想將令牌組合在一起 **Hu** , **##gging** , 和 **Face** ,我們可以制定特殊的規則,說前兩個應該附加,同時刪除 **##** ,以及 **Face** 應該添加一個空格,因為它不以 **##** — 但這僅適用於這種特定類型的標記器。我們必須為 SentencePiece 或 Byte-Pair-Encoding 分詞器(本章稍後討論)。 + +編寫另一組規則。使用偏移量,所有自定義代碼都消失了:我們可以在原始文本中獲取從第一個標記開始到最後一個標記結束的跨度。所以,在令牌的情況下 **Hu** , **##gging** , 和 **Face** ,我們應該從字符 33(開始 **Hu** ) 並在字符 45 之前結束(結束 **Face** ): + +```py +example[33:45] +``` + +```python out +Hugging Face +``` + +為了編寫在對實體進行分組的同時對預測進行後處理的代碼,我們將連續並標記為的實體分組在一起 **I-XXX** ,除了第一個,可以標記為 **B-XXX** 或者 **I-XXX** (因此,當我們得到一個實體時,我們停止對實體進行分組 **O** ,一種新型實體,或 **B-XXX** 這告訴我們一個相同類型的實體正在啟動): + +```py +import numpy as np + +results = [] +inputs_with_offsets = tokenizer(example, return_offsets_mapping=True) +tokens = inputs_with_offsets.tokens() +offsets = inputs_with_offsets["offset_mapping"] + +idx = 0 +while idx < len(predictions): + pred = predictions[idx] + label = model.config.id2label[pred] + if label != "O": + # Remove the B- or I- + label = label[2:] + start, _ = offsets[idx] + + # Grab all the tokens labeled with I-label + all_scores = [] + while ( + idx < len(predictions) + and model.config.id2label[predictions[idx]] == f"I-{label}" + ): + all_scores.append(probabilities[idx][pred]) + _, end = offsets[idx] + idx += 1 + + # The score is the mean of all the scores of the tokens in that grouped entity + score = np.mean(all_scores).item() + word = example[start:end] + results.append( + { + "entity_group": label, + "score": score, + "word": word, + "start": start, + "end": end, + } + ) + idx += 1 + +print(results) +``` + +我們得到了與第二條管道相同的結果! + +```python out +[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18}, + {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45}, + {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}] +``` + +這些偏移量非常有用的另一個任務示例是問答。深入研究這個管道,我們將在下一節中進行,也將使我們能夠了解 🤗 Transformers 庫中標記器的最後一個功能:當我們將輸入截斷為給定長度時處理溢出的標記。 diff --git a/chapters/zh-TW/chapter6/3b.mdx b/chapters/zh-TW/chapter6/3b.mdx new file mode 100644 index 000000000..4471c4cec --- /dev/null +++ b/chapters/zh-TW/chapter6/3b.mdx @@ -0,0 +1,639 @@ + + +# QA 管道中的快速標記器 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +我們現在將深入研究 **question-answering** 管道,看看如何利用偏移量從上下文中獲取手頭問題的答案,有點像我們在上一節中對分組實體所做的。然後我們將看到我們如何處理最終被截斷的非常長的上下文。如果您對問答任務不感興趣,可以跳過此部分。 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +## 使用 `question-answering` 管道 + +正如我們在[Chapter 1](/course/chapter1),我們可以使用 **question-answering** 像這樣的管道以獲得問題的答案: + +```py +from transformers import pipeline + +question_answerer = pipeline("question-answering") +context = """ +🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch, and TensorFlow — with a seamless integration +between them. It's straightforward to train your models with one before loading them for inference with the other. +""" +question = "Which deep learning libraries back 🤗 Transformers?" +question_answerer(question=question, context=context) +``` + +```python out +{'score': 0.97773, + 'start': 78, + 'end': 105, + 'answer': 'Jax, PyTorch and TensorFlow'} +``` + +與其他管道不同,它不能截斷和拆分長於模型接受的最大長度的文本(因此可能會丟失文檔末尾的信息),此管道可以處理非常長的上下文,並將返回回答這個問題,即使它在最後: + +```py +long_context = """ +🤗 Transformers: State of the Art NLP + +🤗 Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction, +question answering, summarization, translation, text generation and more in over 100 languages. +Its aim is to make cutting-edge NLP easier to use for everyone. + +🤗 Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and +then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and +can be modified to enable quick research experiments. + +Why should I use transformers? + +1. Easy-to-use state-of-the-art models: + - High performance on NLU and NLG tasks. + - Low barrier to entry for educators and practitioners. + - Few user-facing abstractions with just three classes to learn. + - A unified API for using all our pretrained models. + - Lower compute costs, smaller carbon footprint: + +2. Researchers can share trained models instead of always retraining. + - Practitioners can reduce compute time and production costs. + - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages. + +3. Choose the right framework for every part of a model's lifetime: + - Train state-of-the-art models in 3 lines of code. + - Move a single model between TF2.0/PyTorch frameworks at will. + - Seamlessly pick the right framework for training, evaluation and production. + +4. Easily customize a model or an example to your needs: + - We provide examples for each architecture to reproduce the results published by its original authors. + - Model internals are exposed as consistently as possible. + - Model files can be used independently of the library for quick experiments. + +🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration +between them. It's straightforward to train your models with one before loading them for inference with the other. +""" +question_answerer(question=question, context=long_context) +``` + +```python out +{'score': 0.97149, + 'start': 1892, + 'end': 1919, + 'answer': 'Jax, PyTorch and TensorFlow'} +``` + +讓我們看看它是如何做到這一切的! + +## 使用模型進行問答 + +與任何其他管道一樣,我們首先對輸入進行標記化,然後通過模型將其發送。默認情況下用於的檢查點 **question-answering** 管道是[distilbert-base-cased-distilled-squad](https://huggingface.co/distilbert-base-cased-distilled-squad)(名稱中的“squad”來自模型微調的數據集;我們將在[Chapter 7](/course/chapter7/7)): + +{#if fw === 'pt'} + +```py +from transformers import AutoTokenizer, AutoModelForQuestionAnswering + +model_checkpoint = "distilbert-base-cased-distilled-squad" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint) + +inputs = tokenizer(question, context, return_tensors="pt") +outputs = model(**inputs) +``` + +{:else} + +```py +from transformers import AutoTokenizer, TFAutoModelForQuestionAnswering + +model_checkpoint = "distilbert-base-cased-distilled-squad" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint) + +inputs = tokenizer(question, context, return_tensors="tf") +outputs = model(**inputs) +``` + +{/if} + +請注意,我們將問題和上下文標記為一對,首先是問題 + +
+An example of tokenization of question and context + +
+ +問答模型的工作方式與我們迄今為止看到的模型略有不同。以上圖為例,該模型已經過訓練,可以預測答案開始的標記的索引(此處為 21)和答案結束處的標記的索引(此處為 24)。這就是為什麼這些模型不返回一個 logits 的張量,而是返回兩個:一個用於對應於答案的開始標記的 logits,另一個用於對應於答案的結束標記的 logits。由於在這種情況下我們只有一個包含 66 個標記的輸入,我們得到: + +```py +start_logits = outputs.start_logits +end_logits = outputs.end_logits +print(start_logits.shape, end_logits.shape) +``` + +{#if fw === 'pt'} + +```python out +torch.Size([1, 66]) torch.Size([1, 66]) +``` + +{:else} + +```python out +(1, 66) (1, 66) +``` + +{/if} + +為了將這些 logits 轉換為概率,我們將應用一個 softmax 函數——但在此之前,我們需要確保我們屏蔽了不屬於上下文的索引。我們的輸入是 **[CLS] question [SEP] context [SEP]** ,所以我們需要屏蔽問題的標記以及 **[SEP]** 令牌。我們將保留 **[CLS]** 然而,因為某些模型使用它來表示答案不在上下文中。 + +由於我們將在之後應用 softmax,我們只需要用一個大的負數替換我們想要屏蔽的 logits。在這裡,我們使用 **-10000** : + +{#if fw === 'pt'} + +```py +import torch + +sequence_ids = inputs.sequence_ids() +# Mask everything apart from the tokens of the context +mask = [i != 1 for i in sequence_ids] +# Unmask the [CLS] token +mask[0] = False +mask = torch.tensor(mask)[None] + +start_logits[mask] = -10000 +end_logits[mask] = -10000 +``` + +{:else} + +```py +import tensorflow as tf + +sequence_ids = inputs.sequence_ids() +# Mask everything apart from the tokens of the context +mask = [i != 1 for i in sequence_ids] +# Unmask the [CLS] token +mask[0] = False +mask = tf.constant(mask)[None] + +start_logits = tf.where(mask, -10000, start_logits) +end_logits = tf.where(mask, -10000, end_logits) +``` + +{/if} + +現在我們已經正確屏蔽了與我們不想預測的位置相對應的 logits,我們可以應用 softmax: + +{#if fw === 'pt'} + +```py +start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)[0] +end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)[0] +``` + +{:else} + +```py +start_probabilities = tf.math.softmax(start_logits, axis=-1)[0].numpy() +end_probabilities = tf.math.softmax(end_logits, axis=-1)[0].numpy() +``` + +{/if} + +在這個階段,我們可以採用開始和結束概率的 argmax——但我們最終可能會得到一個大於結束索引的開始索引,所以我們需要採取更多的預防措施。我們將計算每個可能的概率 **start_index** 和 **end_index** 在哪裡 **start_index <= end_index** ,然後取元組 **(start_index, end_index)** 以最高的概率。 + +假設事件“答案開始於 **start_index** ”和“答案結束於 **end_index** ” 要獨立,答案開始於的概率 **start_index** 並結束於 **end_index** 是: + +$$\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]$$ + +所以,要計算所有的分數,我們只需要計算所有的產品 \\(\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]\\) where `start_index <= end_index`. + +首先讓我們計算所有可能的產品: +```py +scores = start_probabilities[:, None] * end_probabilities[None, :] +``` + +{#if fw === 'pt'} + +然後我們將屏蔽這些值 **start_index > end_index** 通過將它們設置為 **0** (其他概率都是正數)。這 **torch.triu()** 函數返回作為參數傳遞的 2D 張量的上三角部分,因此它會為我們做屏蔽: + +```py +scores = torch.triu(scores) +``` + +{:else} +然後我們將屏蔽這些值 **start_index > end_index** 通過將它們設置為 **0** (其他概率都是正數)。這 **torch.triu()** 函數返回作為參數傳遞的 2D 張量的上三角部分,因此它會為我們做屏蔽: + +```py +scores = np.triu(scores) +``` + +{/if} + +現在我們只需要得到最大值的索引。由於 PyTorch 將返回展平張量中的索引,因此我們需要使用地板除法 **//** 和模數 **%** 操作以獲得 **start_index** 和 **end_index** : + +```py +max_index = scores.argmax().item() +start_index = max_index // scores.shape[1] +end_index = max_index % scores.shape[1] +print(scores[start_index, end_index]) +``` + +我們還沒有完全完成,但至少我們已經有了正確的答案分數(您可以通過將其與上一節中的第一個結果進行比較來檢查這一點): + +```python out +0.97773 +``` + + + +✏️ **試試看!** 計算五個最可能的答案的開始和結束索引。 + + + +我們有 **start_index** 和 **end_index** 就標記而言的答案,所以現在我們只需要轉換為上下文中的字符索引。這是偏移量非常有用的地方。我們可以像在令牌分類任務中一樣抓住它們並使用它們: + +```py +inputs_with_offsets = tokenizer(question, context, return_offsets_mapping=True) +offsets = inputs_with_offsets["offset_mapping"] + +start_char, _ = offsets[start_index] +_, end_char = offsets[end_index] +answer = context[start_char:end_char] +``` + +現在我們只需要格式化所有內容以獲得我們的結果: + +```py +result = { + "answer": answer, + "start": start_char, + "end": end_char, + "score": scores[start_index, end_index], +} +print(result) +``` + +```python out +{'answer': 'Jax, PyTorch and TensorFlow', + 'start': 78, + 'end': 105, + 'score': 0.97773} +``` + +太棒了!這和我們的第一個例子一樣! + + + +✏️ **試試看!** 使用您之前計算的最佳分數來顯示五個最可能的答案。要檢查您的結果,請返回到第一個管道並在調用它時傳入。 + + + +## 處理長上下文 + +如果我們嘗試對我們之前作為示例使用的問題和長上下文進行標記化,我們將獲得比在 **question-answering** 管道(即 384): + +```py +inputs = tokenizer(question, long_context) +print(len(inputs["input_ids"])) +``` + +```python out +461 +``` + +因此,我們需要在最大長度處截斷我們的輸入。有幾種方法可以做到這一點,但我們不想截斷問題,只想截斷上下文。由於上下文是第二個句子,我們將使用 **"only_second"** 截斷策略。那麼出現的問題是問題的答案可能不在截斷上下文中。例如,在這裡,我們選擇了一個答案在上下文末尾的問題,當我們截斷它時,答案不存在 + +```py +inputs = tokenizer(question, long_context, max_length=384, truncation="only_second") +print(tokenizer.decode(inputs["input_ids"])) +``` + +```python out +""" +[CLS] Which deep learning libraries back [UNK] Transformers? [SEP] [UNK] Transformers : State of the Art NLP + +[UNK] Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction, +question answering, summarization, translation, text generation and more in over 100 languages. +Its aim is to make cutting-edge NLP easier to use for everyone. + +[UNK] Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and +then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and +can be modified to enable quick research experiments. + +Why should I use transformers? + +1. Easy-to-use state-of-the-art models: + - High performance on NLU and NLG tasks. + - Low barrier to entry for educators and practitioners. + - Few user-facing abstractions with just three classes to learn. + - A unified API for using all our pretrained models. + - Lower compute costs, smaller carbon footprint: + +2. Researchers can share trained models instead of always retraining. + - Practitioners can reduce compute time and production costs. + - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages. + +3. Choose the right framework for every part of a model's lifetime: + - Train state-of-the-art models in 3 lines of code. + - Move a single model between TF2.0/PyTorch frameworks at will. + - Seamlessly pick the right framework for training, evaluation and production. + +4. Easily customize a model or an example to your needs: + - We provide examples for each architecture to reproduce the results published by its original authors. + - Model internal [SEP] +""" +``` + +這意味著模型將很難選擇正確的答案。為了解決這個問題, **question-answering** 管道允許我們將上下文分成更小的塊,指定最大長度。為確保我們不會在完全錯誤的位置拆分上下文以找到答案,它還包括塊之間的一些重疊。 + +我們可以讓分詞器(快或慢)通過添加來為我們做這件事 **return_overflowing_tokens=True** ,我們可以指定我們想要的重疊 **stride** 爭論。這是一個使用較小句子的示例: + +```py +sentence = "This sentence is not too long but we are going to split it anyway." +inputs = tokenizer( + sentence, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2 +) + +for ids in inputs["input_ids"]: + print(tokenizer.decode(ids)) +``` + +```python out +'[CLS] This sentence is not [SEP]' +'[CLS] is not too long [SEP]' +'[CLS] too long but we [SEP]' +'[CLS] but we are going [SEP]' +'[CLS] are going to split [SEP]' +'[CLS] to split it anyway [SEP]' +'[CLS] it anyway. [SEP]' +``` + +正如我們所看到的,句子已被分成多個塊,使得每個條目 **inputs["input_ids"]** 最多有 6 個標記(我們需要添加填充以使最後一個條目與其他條目的大小相同)並且每個條目之間有 2 個標記的重疊。 + +讓我們仔細看看標記化的結果: + +```py +print(inputs.keys()) +``` + +```python out +dict_keys(['input_ids', 'attention_mask', 'overflow_to_sample_mapping']) +``` + +正如預期的那樣,我們得到了輸入 ID 和一個注意力掩碼。最後一個鍵, **overflow_to_sample_mapping** , 是一個映射,它告訴我們每個結果對應哪個句子——這裡我們有 7 個結果,它們都來自我們通過標記器的(唯一)句子: + +```py +print(inputs["overflow_to_sample_mapping"]) +``` + +```python out +[0, 0, 0, 0, 0, 0, 0] +``` + +當我們將幾個句子標記在一起時,這更有用。例如,這個: + +```py +sentences = [ + "This sentence is not too long but we are going to split it anyway.", + "This sentence is shorter but will still get split.", +] +inputs = tokenizer( + sentences, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2 +) + +print(inputs["overflow_to_sample_mapping"]) +``` + +讓我們: + +```python out +[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1] +``` + +這意味著第一個句子像以前一樣分成 7 個塊,接下來的 4 個塊來自第二個句子。 + + +現在讓我們回到我們的長期背景。默認情況下 **question-answering** 管道使用的最大長度為 384,正如我們之前提到的,步長為 128,這對應於模型微調的方式(您可以通過傳遞 **max_seq_len** 和 **stride** 調用管道時的參數)。因此,我們將在標記化時使用這些參數。我們還將添加填充(具有相同長度的樣本,因此我們可以構建張量)以及請求偏移量: + +```py +inputs = tokenizer( + question, + long_context, + stride=128, + max_length=384, + padding="longest", + truncation="only_second", + return_overflowing_tokens=True, + return_offsets_mapping=True, +) +``` + +那些 **inputs** 將包含模型期望的輸入 ID 和注意力掩碼,以及偏移量和 **overflow_to_sample_mapping** 我們剛剛談到。由於這兩個不是模型使用的參數,我們將把它們從 **inputs** (我們不會存儲地圖,因為它在這裡沒有用)在將其轉換為張量之前: + +{#if fw === 'pt'} + +```py +_ = inputs.pop("overflow_to_sample_mapping") +offsets = inputs.pop("offset_mapping") + +inputs = inputs.convert_to_tensors("pt") +print(inputs["input_ids"].shape) +``` + +```python out +torch.Size([2, 384]) +``` + +{:else} + +```py +_ = inputs.pop("overflow_to_sample_mapping") +offsets = inputs.pop("offset_mapping") + +inputs = inputs.convert_to_tensors("tf") +print(inputs["input_ids"].shape) +``` + +```python out +(2, 384) +``` + +{/if} + +我們的長上下文被分成兩部分,這意味著在它通過我們的模型後,我們將有兩組開始和結束 logits: + +```py +outputs = model(**inputs) + +start_logits = outputs.start_logits +end_logits = outputs.end_logits +print(start_logits.shape, end_logits.shape) +``` + +{#if fw === 'pt'} + +```python out +torch.Size([2, 384]) torch.Size([2, 384]) +``` + +{:else} + +```python out +(2, 384) (2, 384) +``` + +{/if} + +和以前一樣,我們在採用 softmax 之前首先屏蔽不屬於上下文的標記。我們還屏蔽了所有填充標記(由注意掩碼標記): + +{#if fw === 'pt'} + +```py +sequence_ids = inputs.sequence_ids() +# Mask everything apart from the tokens of the context +mask = [i != 1 for i in sequence_ids] +# Unmask the [CLS] token +mask[0] = False +# Mask all the [PAD] tokens +mask = torch.logical_or(torch.tensor(mask)[None], (inputs["attention_mask"] == 0)) + +start_logits[mask] = -10000 +end_logits[mask] = -10000 +``` + +{:else} + +```py +sequence_ids = inputs.sequence_ids() +# Mask everything apart from the tokens of the context +mask = [i != 1 for i in sequence_ids] +# Unmask the [CLS] token +mask[0] = False +# Mask all the [PAD] tokens +mask = tf.math.logical_or(tf.constant(mask)[None], inputs["attention_mask"] == 0) + +start_logits = tf.where(mask, -10000, start_logits) +end_logits = tf.where(mask, -10000, end_logits) +``` + +{/if} + +然後我們可以使用 softmax 將我們的 logits 轉換為概率: + +{#if fw === 'pt'} + +```py +start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1) +end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1) +``` + +{:else} + +```py +start_probabilities = tf.math.softmax(start_logits, axis=-1).numpy() +end_probabilities = tf.math.softmax(end_logits, axis=-1).numpy() +``` + +{/if} + +下一步與我們對小上下文所做的類似,但我們對兩個塊中的每一個都重複它。我們將分數歸因於所有可能的答案跨度,然後取得分最高的跨度: + +{#if fw === 'pt'} + +```py +candidates = [] +for start_probs, end_probs in zip(start_probabilities, end_probabilities): + scores = start_probs[:, None] * end_probs[None, :] + idx = torch.triu(scores).argmax().item() + + start_idx = idx // scores.shape[1] + end_idx = idx % scores.shape[1] + score = scores[start_idx, end_idx].item() + candidates.append((start_idx, end_idx, score)) + +print(candidates) +``` + +{:else} + +```py +candidates = [] +for start_probs, end_probs in zip(start_probabilities, end_probabilities): + scores = start_probs[:, None] * end_probs[None, :] + idx = np.triu(scores).argmax().item() + + start_idx = idx // scores.shape[1] + end_idx = idx % scores.shape[1] + score = scores[start_idx, end_idx].item() + candidates.append((start_idx, end_idx, score)) + +print(candidates) +``` + +{/if} + +```python out +[(0, 18, 0.33867), (173, 184, 0.97149)] +``` + +這兩個候選對應於模型能夠在每個塊中找到的最佳答案。該模型對正確答案在第二部分更有信心(這是一個好兆頭!)。現在我們只需要將這兩個標記跨度映射到上下文中的字符跨度(我們只需要映射第二個標記以獲得我們的答案,但看看模型在第一個塊中選擇了什麼很有趣)。 + + + +✏️ **試試看!** 修改上面的代碼以返回五個最可能的答案的分數和跨度(總計,而不是每個塊)。 + + + +這 **offsets** 我們之前抓取的實際上是一個偏移量列表,每個文本塊有一個列表: + +```py +for candidate, offset in zip(candidates, offsets): + start_token, end_token, score = candidate + start_char, _ = offset[start_token] + _, end_char = offset[end_token] + answer = long_context[start_char:end_char] + result = {"answer": answer, "start": start_char, "end": end_char, "score": score} + print(result) +``` + +```python out +{'answer': '\n🤗 Transformers: State of the Art NLP', 'start': 0, 'end': 37, 'score': 0.33867} +{'answer': 'Jax, PyTorch and TensorFlow', 'start': 1892, 'end': 1919, 'score': 0.97149} +``` + +如果我們忽略第一個結果,我們會得到與這個長上下文的管道相同的結果——是的! + + + +✏️ **試試看!** 使用您之前計算的最佳分數來顯示五個最可能的答案(對於整個上下文,而不是每個塊)。要檢查您的結果,請返回到第一個管道並在調用它時傳入。 + + + +我們對分詞器功能的深入研究到此結束。我們將在下一章再次將所有這些付諸實踐,屆時我們將向您展示如何在一系列常見的 NLP 任務上微調模型。 diff --git a/chapters/zh-TW/chapter6/4.mdx b/chapters/zh-TW/chapter6/4.mdx new file mode 100644 index 000000000..e866ea2bf --- /dev/null +++ b/chapters/zh-TW/chapter6/4.mdx @@ -0,0 +1,124 @@ +# 標準化和預標記化 + + + +在我們更深入地研究與 Transformer 模型(字節對編碼 [BPE]、WordPiece 和 Unigram)一起使用的三種最常見的子詞標記化算法之前,我們將首先看一下每個標記器應用於文本的預處理。以下是標記化管道中步驟的高級概述: + +
+The tokenization pipeline. + +
+ +在將文本拆分為子標記之前(根據其模型),分詞器執行兩個步驟: _normalization_ 和 _pre-tokenization_. + +## 正常化 + + + +標準化步驟涉及一些常規清理,例如刪除不必要的空格、小寫和/或刪除重音符號。如果你熟悉[Unicode normalization](http://www.unicode.org/reports/tr15/)(例如 NFC 或 NFKC),這也是 tokenizer 可能應用的東西。 + +🤗Transformers **tokenizer** 有一個屬性叫做 **backend_tokenizer** 它提供了對 🤗 Tokenizers 庫中底層標記器的訪問: + +```py +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased") +print(type(tokenizer.backend_tokenizer)) +``` + +```python out + +``` + +**normalizer** 的屬性 **tokenizer** 對象有一個 **normalize_str()** 我們可以用來查看標準化是如何執行的方法: + +```py +print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?")) +``` + +```python out +'hello how are u?' +``` + +在這個例子中,因為我們選擇了 **bert-base-uncased** 檢查點,標準化應用小寫並刪除重音。 + + + +✏️ **試試看!** 從檢查點加載標記器並將相同的示例傳遞給它。您可以看到分詞器的帶殼和無殼版本之間的主要區別是什麼? + + + + +## 預標記化 + + + +正如我們將在下一節中看到的,分詞器不能單獨在原始文本上進行訓練。相反,我們首先需要將文本拆分為小實體,例如單詞。這就是預標記化步驟的用武之地。 正如我們在[Chapter 2](/course/chapter2), 基於單詞的標記器可以簡單地將原始文本拆分為空白和標點符號的單詞。這些詞將是分詞器在訓練期間可以學習的子標記的邊界。 + +要查看快速分詞器如何執行預分詞,我們可以使用 **pre_tokenize_str()** 的方法 **pre_tokenizer** 的屬性 **tokenizer** 目的: + +```py +tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?") +``` + +```python out +[('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))] +``` + +請注意分詞器如何已經跟蹤偏移量,這就是它如何為我們提供上一節中使用的偏移量映射。這裡分詞器忽略了這兩個空格,只用一個替換它們,但偏移量在 **are** 和 **you** 考慮到這一點。 + +由於我們使用的是 BERT 分詞器,預分詞涉及對空格和標點符號進行拆分。對於這一步,其他標記器可以有不同的規則。例如,如果我們使用 GPT-2 標記器: + +```py +tokenizer = AutoTokenizer.from_pretrained("gpt2") +tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?") +``` + +它也會在空格和標點符號上拆分,但它會保留空格並將它們替換為 **Ġ** 符號,如果我們解碼令牌,則使其能夠恢復原始空格: + +```python out +[('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)), + ('?', (19, 20))] +``` + +另請注意,與 BERT 分詞器不同,此分詞器不會忽略雙空格 + +最後一個例子,讓我們看一下基於 SentencePiece 算法的 T5 分詞器: + +```py +tokenizer = AutoTokenizer.from_pretrained("t5-small") +tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?") +``` + +```python out +[('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))] +``` + +與 GPT-2 標記器一樣,這個標記器保留空格並用特定標記替換它們( **_** ),但 T5 分詞器只在空格上拆分,而不是標點符號。還要注意,它默認在句子的開頭添加了一個空格(之前 **Hello** ) 並忽略了之間的雙空格 **are** 和 **you** . + +現在我們已經瞭解了一些不同的標記器如何處理文本,我們可以開始探索底層算法本身。我們首先快速瀏覽一下廣泛適用的 SentencePiece;然後,在接下來的三個部分中,我們將研究用於子詞標記化的三種主要算法是如何工作的。 + +## 句子 + +[SentencePiece](https://github.com/google/sentencepiece) 是一種用於文本預處理的標記化算法,您可以將其與我們將在接下來的三個部分中看到的任何模型一起使用。它將文本視為 Unicode 字符序列,並用特殊字符替換空格, **▁** .與 Unigram 算法結合使用(參見[section 7](/course/chapter7/7)), 它甚至不需要預標記化步驟,這對於不使用空格字符的語言(如中文或日語)非常有用。 + +SentencePiece 的另一個主要特點是可逆標記化:由於沒有對空格進行特殊處理,因此只需通過將它們連接起來並替換 **_** s 帶空格——這會導致標準化的文本。正如我們之前看到的,BERT 分詞器刪除了重複的空格,因此它的分詞是不可逆的。 + +## 算法概述 + +在下面的部分中,我們將深入研究三種主要的子詞標記化算法:BPE(由 GPT-2 和其他人使用)、WordPiece(例如由 BERT 使用)和 Unigram(由 T5 和其他人使用)。在我們開始之前,這裡是它們各自工作原理的快速概述。如果您還沒有理解,請在閱讀下一節後立即回到此表。 + + +Model | BPE | WordPiece | Unigram +:----:|:---:|:---------:|:------: +Training | Starts from a small vocabulary and learns rules to merge tokens | Starts from a small vocabulary and learns rules to merge tokens | Starts from a large vocabulary and learns rules to remove tokens +Training step | Merges the tokens corresponding to the most common pair | Merges the tokens corresponding to the pair with the best score based on the frequency of the pair, privileging pairs where each individual token is less frequent | Removes all the tokens in the vocabulary that will minimize the loss computed on the whole corpus +Learns | Merge rules and a vocabulary | Just a vocabulary | A vocabulary with a score for each token +Encoding | Splits a word into characters and applies the merges learned during training | Finds the longest subword starting from the beginning that is in the vocabulary, then does the same for the rest of the word | Finds the most likely split into tokens, using the scores learned during training + +現在讓我們深入瞭解 BPE! \ No newline at end of file diff --git a/chapters/zh-TW/chapter6/5.mdx b/chapters/zh-TW/chapter6/5.mdx new file mode 100644 index 000000000..1471d292d --- /dev/null +++ b/chapters/zh-TW/chapter6/5.mdx @@ -0,0 +1,360 @@ +# 字節對編碼標記化 + + + +字節對編碼(BPE)最初被開發為一種壓縮文本的算法,然後在預訓練 GPT 模型時被 OpenAI 用於標記化。許多 Transformer 模型都使用它,包括 GPT、GPT-2、RoBERTa、BART 和 DeBERTa。 + + + + + +💡 本節深入介紹了BPE,甚至展示了一個完整的實現。如果你只想大致瞭解標記化算法,可以跳到最後。 + + + +## 訓練算法 + +BPE 訓練首先計算語料庫中使用的唯一單詞集(在完成標準化和預標記化步驟之後),然後通過獲取用於編寫這些單詞的所有符號來構建詞彙表。舉一個簡單的例子,假設我們的語料庫使用了這五個詞: + +``` +"hug", "pug", "pun", "bun", "hugs" +``` + +基礎詞彙將是 `["b", "g", "h", "n", "p", "s", "u"]`。對於實際情況,基本詞彙表將包含所有 ASCII 字符,至少,可能還包含一些 Unicode 字符。如果您正在標記的示例使用不在訓練語料庫中的字符,則該字符將轉換為未知標記。這就是為什麼許多 NLP 模型在分析帶有表情符號的內容方面非常糟糕的原因之一。 + + + +TGPT-2 和 RoBERTa 標記器(非常相似)有一個聰明的方法來處理這個問題: 他們不把單詞看成是用 Unicode 字符寫的,而是用字節寫的。這樣,基本詞彙表的大小很小(256),但你能想到的每個字符仍將被包含在內,而不會最終轉換為未知標記。這個技巧被稱為 *字節級 BPE*。 + + + +獲得這個基本詞彙後,我們添加新的標記,直到通過學習*合併*達到所需的詞彙量,這是將現有詞彙表的兩個元素合併為一個新元素的規則。因此在開始時,這些合併將創建具有兩個字符的標記,然後隨著訓練的進行,會創建更長的子詞。 + +在分詞器訓練期間的任何一步,BPE 算法都會搜索最常見的現有標記對 ("對",這裡我們指的是單詞中的兩個連續標記)。最頻繁的一對將被合併,我們沖洗並重複下一步。 + +回到我們之前的例子,讓我們假設單詞具有以下頻率: + +``` +("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5) +``` + +意味著 `"hug"` 在語料庫中出現了10次, `"pug"` 5次, `"pun"` 12次, `"bun"` 4次, 以及 `"hugs"` 5次。我們通過將每個單詞拆分為字符(形成我們初始詞彙表的字符)來開始訓練,這樣我們就可以將每個單詞視為一個標記列表: + +``` +("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5) +``` + +然後我們看成對。這對 `("h", "u")` 出現在單詞 `"hug"` 和 `"hugs"`中,所以語料庫中總共有15次。不過,這並不是最頻繁的一對:這個榮譽屬於 `("u", "g")`,它出現在 `"hug"`, `"pug"`, 以及 `"hugs"`中,在詞彙表中總共 20 次。 + +因此,標記器學習的第一個合併規則是 `("u", "g") -> "ug"`,意思就是 `"ug"` 將被添加到詞彙表中,並且這對應該合併到語料庫的所有單詞中。在這個階段結束時,詞彙表和語料庫看起來像這樣: + +``` +Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug"] +Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5) +``` + +現在我們有一些導致標記長於兩個字符的對: 例如 `("h", "ug")`, 在語料庫中出現15次。然而,這個階段最頻繁的對是 `("u", "n")`,在語料庫中出現16次,所以學到的第二個合併規則是 `("u", "n") -> "un"`。將其添加到詞彙表併合並所有現有的這個對,將出現: + +``` +Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un"] +Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("h" "ug" "s", 5) +``` + +現在最頻繁的一對是 `("h", "ug")`,所以我們學習了合併規則 `("h", "ug") -> "hug"`,這給了我們第一個三個字母的標記。合併後,語料庫如下所示: + +``` +Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"] +Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5) +``` + +我們繼續這樣合併,直到達到我們所需的詞彙量。 + + + +✏️ **現在輪到你了!**你認為下一個合併規則是什麼? + + + +## 標記化算法 + +標記化緊跟訓練過程,從某種意義上說,通過應用以下步驟對新輸入進行標記: + +1. 規範化 +2. 預標記化 +3. 將單詞拆分為單個字符 +4. 將學習的合併規則按順序應用於這些拆分 + +讓我們以我們在訓練期間使用的示例為例,學習三個合併規則: + +``` +("u", "g") -> "ug" +("u", "n") -> "un" +("h", "ug") -> "hug" +``` + +這個單詞 `"bug"` 將被標記為 `["b", "ug"]`。然而 `"mug"`,將被標記為 `["[UNK]", "ug"]`,因為字母 `"m"` 不再基本詞彙表中。同樣,單詞`"thug"` 會被標記為 `["[UNK]", "hug"]`: 字母 `"t"` 不在基本詞彙表中,應用合併規則首先導致 `"u"` 和 `"g"` 被合併,然後是 `"hu"` 和 `"g"` 被合併。 + + + +✏️ **現在輪到你了!** 你認為這個詞 `"unhug"` 將如何被標記? + + + +## 實現 BPE + +現在讓我們看一下 BPE 算法的實現。這不會是你可以在大型語料庫上實際使用的優化版本;我們只是想向你展示代碼,以便你可以更好地理解算法 + +首先我們需要一個語料庫,所以讓我們用幾句話創建一個簡單的語料庫: + +```python +corpus = [ + "This is the Hugging Face course.", + "This chapter is about tokenization.", + "This section shows several tokenizer algorithms.", + "Hopefully, you will be able to understand how they are trained and generate tokens.", +] +``` + +接下來,我們需要將該語料庫預先標記為單詞。由於我們正在複製 BPE 標記器(如 GPT-2),我們將使用 `gpt2` 標記器作為預標記化的標記器: + +```python +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("gpt2") +``` + +然後我們在進行預標記化時計算語料庫中每個單詞的頻率: + +```python +from collections import defaultdict + +word_freqs = defaultdict(int) + +for text in corpus: + words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text) + new_words = [word for word, offset in words_with_offsets] + for word in new_words: + word_freqs[word] += 1 + +print(word_freqs) +``` + +```python out +defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1, + 'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1, + 'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1, + 'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1}) +``` + +下一步是計算基本詞彙,由語料庫中使用的所有字符組成: + +```python +alphabet = [] + +for word in word_freqs.keys(): + for letter in word: + if letter not in alphabet: + alphabet.append(letter) +alphabet.sort() + +print(alphabet) +``` + +```python out +[ ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', + 't', 'u', 'v', 'w', 'y', 'z', 'Ġ'] +``` + +我們還在該詞彙表的開頭添加了模型使用的特殊標記。對於GPT-2,唯一的特殊標記是 `"<|endoftext|>"`: + +```python +vocab = ["<|endoftext|>"] + alphabet.copy() +``` + +我們現在需要將每個單詞拆分為單獨的字符,以便能夠開始訓練: + +```python +splits = {word: [c for c in word] for word in word_freqs.keys()} +``` + +現在我們已準備好進行訓練,讓我們編寫一個函數來計算每對的頻率。我們需要在訓練的每個步驟中使用它: + +```python +def compute_pair_freqs(splits): + pair_freqs = defaultdict(int) + for word, freq in word_freqs.items(): + split = splits[word] + if len(split) == 1: + continue + for i in range(len(split) - 1): + pair = (split[i], split[i + 1]) + pair_freqs[pair] += freq + return pair_freqs +``` + +讓我們來看看這個字典在初始拆分後的一部分: + +```python +pair_freqs = compute_pair_freqs(splits) + +for i, key in enumerate(pair_freqs.keys()): + print(f"{key}: {pair_freqs[key]}") + if i >= 5: + break +``` + +```python out +('T', 'h'): 3 +('h', 'i'): 3 +('i', 's'): 5 +('Ġ', 'i'): 2 +('Ġ', 't'): 7 +('t', 'h'): 3 +``` + +現在, 找到最頻繁的對只需要一個快速的循環: + +```python +best_pair = "" +max_freq = None + +for pair, freq in pair_freqs.items(): + if max_freq is None or max_freq < freq: + best_pair = pair + max_freq = freq + +print(best_pair, max_freq) +``` + +```python out +('Ġ', 't') 7 +``` + +所以第一個要學習的合併是 `('Ġ', 't') -> 'Ġt'`, 我們添加 `'Ġt'` 到詞彙表: + +```python +merges = {("Ġ", "t"): "Ġt"} +vocab.append("Ġt") +``` + +要繼續接下來的步驟,我們需要在我們的`分詞`字典中應用該合併。讓我們為此編寫另一個函數: + +```python +def merge_pair(a, b, splits): + for word in word_freqs: + split = splits[word] + if len(split) == 1: + continue + + i = 0 + while i < len(split) - 1: + if split[i] == a and split[i + 1] == b: + split = split[:i] + [a + b] + split[i + 2 :] + else: + i += 1 + splits[word] = split + return splits +``` + +我們可以看看第一次合併的結果: + +```py +splits = merge_pair("Ġ", "t", splits) +print(splits["Ġtrained"]) +``` + +```python out +['Ġt', 'r', 'a', 'i', 'n', 'e', 'd'] +``` + +現在我們有了循環所需的一切,直到我們學會了我們想要的所有合併。我們的目標是詞彙量達到50: + +```python +vocab_size = 50 + +while len(vocab) < vocab_size: + pair_freqs = compute_pair_freqs(splits) + best_pair = "" + max_freq = None + for pair, freq in pair_freqs.items(): + if max_freq is None or max_freq < freq: + best_pair = pair + max_freq = freq + splits = merge_pair(*best_pair, splits) + merges[best_pair] = best_pair[0] + best_pair[1] + vocab.append(best_pair[0] + best_pair[1]) +``` + +結果,我們學習了 19 條合併規則(初始詞彙表的大小 31 -- 30 字母字符,加上特殊標記): + +```py +print(merges) +``` + +```python out +{('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en', + ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok', + ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe', + ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'} +``` + +詞彙表由特殊標記、初始字母和所有合併結果組成: + +```py +print(vocab) +``` + +```python out +['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', + 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se', + 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni'] +``` + + + +💡 在同一語料庫上使用 `train_new_from_iterator()` 不會產生完全相同的詞彙表。這是因為當有最頻繁對的選擇時,我們選擇遇到的第一個, 而 🤗 Tokenizers 庫根據內部ID選擇第一個。 + + + +為了對新文本進行分詞,我們對其進行預分詞、拆分,然後應用學到的所有合併規則: + +```python +def tokenize(text): + pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text) + pre_tokenized_text = [word for word, offset in pre_tokenize_result] + splits = [[l for l in word] for word in pre_tokenized_text] + for pair, merge in merges.items(): + for idx, split in enumerate(splits): + i = 0 + while i < len(split) - 1: + if split[i] == pair[0] and split[i + 1] == pair[1]: + split = split[:i] + [merge] + split[i + 2 :] + else: + i += 1 + splits[idx] = split + + return sum(splits, []) +``` + +我們可以在任何由字母表中的字符組成的文本上嘗試這個: + +```py +tokenize("This is not a token.") +``` + +```python out +['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġa', 'Ġtoken', '.'] +``` + + + +⚠️ 如果存在未知字符,我們的實現將拋出錯誤,因為我們沒有做任何處理它們。GPT-2 實際上沒有未知標記(使用字節級 BPE 時不可能得到未知字符),但這可能發生在這裡,因為我們沒有在初始詞彙表中包含所有可能的字節。 BPE 的這方面超出了本節的範圍,因此我們忽略了細節。 + + + +這就是 BPE 算法!接下來,我們將看看 WordPiece。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter6/6.mdx b/chapters/zh-TW/chapter6/6.mdx new file mode 100644 index 000000000..69c08c682 --- /dev/null +++ b/chapters/zh-TW/chapter6/6.mdx @@ -0,0 +1,373 @@ +# WordPiece 標記化 + + + +WordPiece 是 Google 為預訓練 BERT 而開發的標記化算法。此後,它在不少基於 BERT 的 Transformer 模型中得到重用,例如 DistilBERT、MobileBERT、Funnel Transformers 和 MPNET。它在訓練方面與 BPE 非常相似,但實際標記化的方式不同。 + + + + + +💡 本節深入介紹 WordPiece,甚至展示完整的實現。如果您只想大致瞭解標記化算法,可以跳到最後。 + + + +## 訓練算法 + + + +⚠️ Google 從未開源 WordPiece 訓練算法的實現,因此以下是我們基於已發表文獻的最佳猜測。它可能不是 100% 準確的。 + + + +與 BPE 一樣,WordPiece 從一個小詞彙表開始,包括模型使用的特殊標記和初始字母表。因為它通過添加前綴來識別子詞 (如同 `##` 對於 BERT),每個單詞最初是通過將該前綴添加到單詞內的所有字符來拆分的。所以,例如 `"word"` ,像這樣拆分: + +``` +w ##o ##r ##d +``` + +因此,初始字母表包含出現在單詞開頭的所有字符以及出現在單詞內部的以 WordPiece 前綴開頭的字符。 + +然後,再次像 BPE 一樣,WordPiece 學習合併規則。主要區別在於選擇要合併的對的方式。WordPiece 不是選擇最頻繁的對,而是使用以下公式計算每對的分數: + +$$\mathrm{score} = (\mathrm{freq\_of\_pair}) / (\mathrm{freq\_of\_first\_element} \times \mathrm{freq\_of\_second\_element})$$ + +通過將配對的頻率除以其每個部分的頻率的乘積, 該算法優先合併單個部分在詞彙表中頻率較低的對。例如,它不一定會合並 `("un", "##able")` 即使這對在詞彙表中出現的頻率很高,因為 `"un"` 和 `"##able"` 很可能每個詞都出現在很多其他詞中並且出現頻率很高。相比之下,像 `("hu", "##gging")` 可能會更快地合併 (假設 "hugging" 經常出現在詞彙表中),因為 `"hu"` 和 `"##gging"` 這兩個詞單獨出現地頻率可能較低。 + +讓我們看看我們在 BPE 訓練示例中使用的相同詞彙: + +``` +("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5) +``` + +這裡的拆分將是: + +``` +("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5) +``` + +所以最初的詞彙將是 `["b", "h", "p", "##g", "##n", "##s", "##u"]` (如果我們暫時忘記特殊標記)。最頻繁的一對是 `("##u", "##g")` (目前20次),但 `"##u"` 單獨出現的頻率非常高,所以它的分數不是最高的(它是 1 / 36)。所有帶有 `"##u"` 的對實際上都有相同的分數(1 / 36),所以分數最高的對是 `("##g", "##s")` -- 唯一沒有 `"##u"` 的對-- 1 / 20,所以學習的第一個合併是 `("##g", "##s") -> ("##gs")`。 + +請注意,當我們合併時,我們刪除了兩個標記之間的 `##`,所以我們添加 `"##gs"` 到詞彙表中,並在語料庫的單詞中應用該合併: + +``` +Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"] +Corpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5) +``` + +在這一點中, `"##u"` 是在所有可能的對中,因此它們最終都具有相同的分數。假設在這種情況下,第一對被合併, `("h", "##u") -> "hu"`。這使得我們: + +``` +Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu"] +Corpus: ("hu" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5) +``` + +然後下一個最高的分數由 `("hu", "##g")` 和 `("hu", "##gs")` 共享(1/15,與其他所有對的 1/21 相比),因此合併得分最高的第一對: + +``` +Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"] +Corpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5) +``` + +我們繼續這樣處理,直到達到我們所需的詞彙量。 + + + +✏️ **現在輪到你了!** 下一個合併規則是什麼? + + +## 標記化算法 + +WordPiece 和 BPE 中的標記化的不同在於 WordPiece 只保存最終詞彙,而不是學習的合併規則。從要標記的單詞開始,WordPiece 找到詞彙表中最長的子詞,然後對其進行拆分。例如,如果我們使用上面例子中學到的詞彙,對於單詞 `"hugs"`,詞彙表中從頭開始的最長子詞是 `"hug"`,所以我們在那裡拆分並得到 `["hug", "##s"]`。 然後我們繼續使用詞彙表中的 `"##s"`,因此 `"hugs"` 的標記化是 `["hug", "##s"]`. + +使用 BPE, 我們將按順序應用學習到的合併並將其標記為 `["hu", "##gs"]`,所以編碼不同。 + +再舉一個例子,讓我們看看 `"bugs"` 將如何被標記化。 `"b"` 是從詞彙表中單詞開頭開始的最長子詞,所以我們在那裡拆分並得到 `["b", "##ugs"]`。然後 `"##u"` 是詞彙表中從 `"##ugs"` 開始的最長的子詞,所以我們在那裡拆分並得到 `["b", "##u, "##gs"]`。最後, `"##gs"` 在詞彙表中,所以最後一個列表是 `"bugs"` 的標記化。 + +當分詞達到無法在詞彙表中找到子詞的階段時, 整個詞被標記為未知 -- 例如, `"mug"` 將被標記為 `["[UNK]"]`,就像 `"bum"` (即使我們可以以 `"b"` 和 `"##u"` 開始, `"##m"` 不在詞彙表中,由此產生的標記將只是 `["[UNK]"]`, 不是 `["b", "##u", "[UNK]"]`)。這是與 BPE 的另一個區別,BPE 只會將不在詞彙表中的單個字符分類為未知。 + + + +✏️ **現在輪到你了!** `"pugs"` 將被如何標記? + + + +## 實現 WordPiece + +現在讓我們看一下 WordPiece 算法的實現。與 BPE 一樣,這只是教學,你將無法在大型語料庫中使用它。 + +我們將使用與 BPE 示例中相同的語料庫: + +```python +corpus = [ + "This is the Hugging Face course.", + "This chapter is about tokenization.", + "This section shows several tokenizer algorithms.", + "Hopefully, you will be able to understand how they are trained and generate tokens.", +] +``` + +首先,我們需要將語料庫預先標記為單詞。由於我們正在複製 WordPiece 標記器 (如 BERT),因此我們將使用 `bert-base-cased` 標記器用於預標記化: + +```python +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("bert-base-cased") +``` + +然後我們在進行預標記化時計算語料庫中每個單詞的頻率: + +```python +from collections import defaultdict + +word_freqs = defaultdict(int) +for text in corpus: + words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text) + new_words = [word for word, offset in words_with_offsets] + for word in new_words: + word_freqs[word] += 1 + +word_freqs +``` + +```python out +defaultdict( + int, {'This': 3, 'is': 2, 'the': 1, 'Hugging': 1, 'Face': 1, 'Course': 1, '.': 4, 'chapter': 1, 'about': 1, + 'tokenization': 1, 'section': 1, 'shows': 1, 'several': 1, 'tokenizer': 1, 'algorithms': 1, 'Hopefully': 1, + ',': 1, 'you': 1, 'will': 1, 'be': 1, 'able': 1, 'to': 1, 'understand': 1, 'how': 1, 'they': 1, 'are': 1, + 'trained': 1, 'and': 1, 'generate': 1, 'tokens': 1}) +``` + +正如我們之前看到的,字母表是由單詞的所有第一個字母組成的唯一集合,以及出現在前綴為 `##` 的其他字母: + +```python +alphabet = [] +for word in word_freqs.keys(): + if word[0] not in alphabet: + alphabet.append(word[0]) + for letter in word[1:]: + if f"##{letter}" not in alphabet: + alphabet.append(f"##{letter}") + +alphabet.sort() +alphabet + +print(alphabet) +``` + +```python out +['##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', '##l', '##m', '##n', '##o', '##p', '##r', '##s', + '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', + 'w', 'y'] +``` + +我們還在該詞彙表的開頭添加了模型使用的特殊標記。在使用 BERT 的情況下,它是列表 `["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]`: + +```python +vocab = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + alphabet.copy() +``` + +接下來我們需要拆分每個單詞, 所有不是第一個字母的字母都以 `##` 為前綴: + +```python +splits = { + word: [c if i == 0 else f"##{c}" for i, c in enumerate(word)] + for word in word_freqs.keys() +} +``` + +現在我們已經準備好訓練了,讓我們編寫一個函數來計算每對的分數。我們需要在訓練的每個步驟中使用它: + +```python +def compute_pair_scores(splits): + letter_freqs = defaultdict(int) + pair_freqs = defaultdict(int) + for word, freq in word_freqs.items(): + split = splits[word] + if len(split) == 1: + letter_freqs[split[0]] += freq + continue + for i in range(len(split) - 1): + pair = (split[i], split[i + 1]) + letter_freqs[split[i]] += freq + pair_freqs[pair] += freq + letter_freqs[split[-1]] += freq + + scores = { + pair: freq / (letter_freqs[pair[0]] * letter_freqs[pair[1]]) + for pair, freq in pair_freqs.items() + } + return scores +``` + +讓我們來看看這個字典在初始拆分後的一部分: + +```python +pair_scores = compute_pair_scores(splits) +for i, key in enumerate(pair_scores.keys()): + print(f"{key}: {pair_scores[key]}") + if i >= 5: + break +``` + +```python out +('T', '##h'): 0.125 +('##h', '##i'): 0.03409090909090909 +('##i', '##s'): 0.02727272727272727 +('i', '##s'): 0.1 +('t', '##h'): 0.03571428571428571 +('##h', '##e'): 0.011904761904761904 +``` + +現在,找到得分最高的對只需要一個快速循環: + +```python +best_pair = "" +max_score = None +for pair, score in pair_scores.items(): + if max_score is None or max_score < score: + best_pair = pair + max_score = score + +print(best_pair, max_score) +``` + +```python out +('a', '##b') 0.2 +``` + +所以第一個要學習的合併是 `('a', '##b') -> 'ab'`, 並且我們添加 `'ab'` 到詞彙表中: + +```python +vocab.append("ab") +``` + +要繼續接下來的步驟,我們需要在我們的 `拆分` 字典中應用該合併。讓我們為此編寫另一個函數: + +```python +def merge_pair(a, b, splits): + for word in word_freqs: + split = splits[word] + if len(split) == 1: + continue + i = 0 + while i < len(split) - 1: + if split[i] == a and split[i + 1] == b: + merge = a + b[2:] if b.startswith("##") else a + b + split = split[:i] + [merge] + split[i + 2 :] + else: + i += 1 + splits[word] = split + return splits +``` + +我們可以看看第一次合併的結果: + +```py +splits = merge_pair("a", "##b", splits) +splits["about"] +``` + +```python out +['ab', '##o', '##u', '##t'] +``` + +現在我們有了循環所需的一切,直到我們學會了我們想要的所有合併。我們的目標詞彙量為70: + +```python +vocab_size = 70 +while len(vocab) < vocab_size: + scores = compute_pair_scores(splits) + best_pair, max_score = "", None + for pair, score in scores.items(): + if max_score is None or max_score < score: + best_pair = pair + max_score = score + splits = merge_pair(*best_pair, splits) + new_token = ( + best_pair[0] + best_pair[1][2:] + if best_pair[1].startswith("##") + else best_pair[0] + best_pair[1] + ) + vocab.append(new_token) +``` + +然後我們可以查看生成的詞彙表: + +```py +print(vocab) +``` + +```python out +['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', '##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', + '##l', '##m', '##n', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', + 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', '##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully', + 'Th', 'ch', '##hm', 'cha', 'chap', 'chapt', '##thm', 'Hu', 'Hug', 'Hugg', 'sh', 'th', 'is', '##thms', '##za', '##zat', + '##ut'] +``` + +正如我們所看到的,與 BPE 相比,這個標記器將單詞的一部分作為標記學習得更快一些。 + + + +💡 在同一語料庫上使用 `train_new_from_iterator()` 不會產生完全相同的詞彙表。這是因為 🤗 Tokenizers 庫沒有為訓練實現 WordPiece(因為我們不完全確定它的內部結構),而是使用 BPE。 + + + +為了對新文本進行分詞,我們對其進行預分詞、拆分,然後對每個單詞應用分詞算法。也就是說,我們從第一個詞的開頭尋找最大的子詞並將其拆分,然後我們在第二部分重複這個過程,對於該詞的其餘部分和文本中的以下詞,依此類推: + +```python +def encode_word(word): + tokens = [] + while len(word) > 0: + i = len(word) + while i > 0 and word[:i] not in vocab: + i -= 1 + if i == 0: + return ["[UNK]"] + tokens.append(word[:i]) + word = word[i:] + if len(word) > 0: + word = f"##{word}" + return tokens +``` + +讓我們用詞彙表中的一個單詞和另一個不在詞彙表中的單詞進行測試: + +```python +print(encode_word("Hugging")) +print(encode_word("HOgging")) +``` + +```python out +['Hugg', '##i', '##n', '##g'] +['[UNK]'] +``` + +現在,讓我們編寫一個標記文本的函數: + +```python +def tokenize(text): + pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text) + pre_tokenized_text = [word for word, offset in pre_tokenize_result] + encoded_words = [encode_word(word) for word in pre_tokenized_text] + return sum(encoded_words, []) +``` + +我們可以在任何文本上嘗試: + +```python +tokenize("This is the Hugging Face course!") +``` + +```python out +['Th', '##i', '##s', 'is', 'th', '##e', 'Hugg', '##i', '##n', '##g', 'Fac', '##e', 'c', '##o', '##u', '##r', '##s', + '##e', '[UNK]'] +``` + +這就是 WordPiece 算法的全部內容!現在讓我們來看看 Unigram。 diff --git a/chapters/zh-TW/chapter6/7.mdx b/chapters/zh-TW/chapter6/7.mdx new file mode 100644 index 000000000..95e013cb8 --- /dev/null +++ b/chapters/zh-TW/chapter6/7.mdx @@ -0,0 +1,381 @@ +# Unigram標記化 + + + +在 SentencePiece 中經常使用 Unigram 算法,該算法是 AlBERT、T5、mBART、Big Bird 和 XLNet 等模型使用的標記化算法。 + + + + + +💡 本節深入介紹了 Unigram,甚至展示了一個完整的實現。如果你只想大致瞭解標記化算法,可以跳到最後。 + + + +## 訓練算法 + +與 BPE 和 WordPiece 相比,Unigram 在另一個方向上工作:它從一個較大的詞彙表開始,然後從中刪除標記,直到達到所需的詞彙表大小。有多種選項可用於構建基本詞彙表:例如,我們可以採用預標記化單詞中最常見的子串,或者在具有大詞彙量的初始語料庫上應用 BPE。 + +在訓練的每一步,Unigram 算法都會在給定當前詞彙的情況下計算語料庫的損失。然後,對於詞彙表中的每個符號,算法計算如果刪除該符號,整體損失會增加多少,並尋找增加最少的符號。這些符號對語料庫的整體損失影響較小,因此從某種意義上說,它們「不太需要」並且是移除的最佳候選者。 + +這是一個非常昂貴的操作,所以我們不只是刪除與最低損失增加相關的單個符號,而且\\(p\\) (\\(p\\)是一個可以控制的超參數,通常是 10 或 20)與最低損失增加相關的符號的百分比。然後重複這個過程,直到詞彙量達到所需的大小。 + +請注意:我們從不刪除基本字符,以確保可以標記任何單詞。 + +這或許仍然有點模糊:算法的主要部分是計算語料庫的損失,並查看當我們從詞彙表中刪除一些標記時它會如何變化,但我們還沒有解釋如何做到這一點。這一步依賴於 Unigram 模型的標記化算法,因此我們接下來將深入研究。 + +我們將重用前面示例中的語料庫: + +``` +("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5) +``` + +對於此示例,我們將採用初始詞彙表的所有嚴格子字符串: + +``` +["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"] +``` + +## 標記化算法 + +Unigram 模型是一種語言模型,它認為每個標記都獨立於它之前的標記。它是最簡單的語言模型,從某種意義上說, 給定先前上下文的標記 X 的概率就是標記 X 的概率。因此,如果我們使用 Unigram 語言模型生成文本,我們將始終預測最常見的標記。 + +給定標記的概率是它在原始語料庫中的頻率(我們找到它的次數),除以詞彙表中所有標記的所有頻率的總和(以確保概率總和為 1)。例如, `"ug"` 在 `"hug"` 、 `"pug"` 以及 `"hugs"` 中,所以它在我們的語料庫中的頻率為 20。 + +以下是詞彙表中所有可能的子詞的出現頻率: + +``` +("h", 15) ("u", 36) ("g", 20) ("hu", 15) ("ug", 20) ("p", 17) ("pu", 17) ("n", 16) +("un", 16) ("b", 4) ("bu", 4) ("s", 5) ("hug", 15) ("gs", 5) ("ugs", 5) +``` + +所以,所有頻率之和為210, 並且子詞 `"ug"` 出現的概率是 20/210。 + + + +✏️ **現在輪到你了!** 編寫代碼來計算上面的頻率,並仔細檢查顯示的結果以及總和是否正確。 + + + +現在,為了對給定的單詞進行標記,我們將所有可能的分割視為標記,並根據 Unigram 模型計算每個分割的概率。由於所有標記都被認為是獨立的,所以這個概率只是每個標記概率的乘積。例如 `"pug"` 的標記化 `["p", "u", "g"]` 的概率為: + +$$P([``p", ``u", ``g"]) = P(``p") \times P(``u") \times P(``g") = \frac{5}{210} \times \frac{36}{210} \times \frac{20}{210} = 0.000389$$ + +相比之下,標記化 `["pu", "g"]` 的概率為: + +$$P([``pu", ``g"]) = P(``pu") \times P(``g") = \frac{5}{210} \times \frac{20}{210} = 0.0022676$$ + +所以一個更有可能。一般來說,具有儘可能少的標記的標記化將具有最高的概率(因為每個標記重複除以 210),這對應於我們直觀想要的:將一個單詞分成儘可能少的標記。 + +使用 Unigram 模型對單詞進行分詞是概率最高的分詞。在示例 `"pug"` 中,這裡是我們為每個可能的分割獲得的概率: + +``` +["p", "u", "g"] : 0.000389 +["p", "ug"] : 0.0022676 +["pu", "g"] : 0.0022676 +``` + +所以 `"pug"` 將被標記為 `["p", "ug"]` 或者 `["pu", "g"]`,取決於首先遇到這些分割中的哪一個(請注意:在更大的語料庫中,這樣的相等的情況很少見)。 + +在這種情況下,很容易找到所有可能的分割並計算它們的概率,但一般來說會有點困難。有一種用於此的經典算法,稱為 *維特比(Viterbi)算法*。本質上,我們可以構建一個圖來檢測給定單詞的可能分割,如果從_a_到_b_的子詞在詞彙表中,則從字符_a_到字符_b_之間存在一個分支,並將子詞的概率歸因於該分支。 + +為了在該圖中找到將具有最佳分數的路徑,維特比算法為單詞中的每個位置確定在該位置結束的具有最佳分數的分段。由於我們從開始到結束,可以通過循環遍歷以當前位置結尾的所有子詞,然後使用該子詞開始位置的最佳標記化分數來找到最佳分數。然後,我們只需要展開到達終點所採取的路徑。 + +讓我們看一個使用我們的詞彙表和單詞 `"unhug"` 的例子。對於每個位置,以最好的分數結尾的子詞如下: + +``` +Character 0 (u): "u" (score 0.171429) +Character 1 (n): "un" (score 0.076191) +Character 2 (h): "un" "h" (score 0.005442) +Character 3 (u): "un" "hu" (score 0.005442) +Character 4 (g): "un" "hug" (score 0.005442) +``` + +因此 `"unhug"` 將被標記為 `["un", "hug"]`。 + + + +✏️ **現在輪到你了!** 確定單詞 `"huggun"` 的標記化及其分數。 + + + +## 回到訓練 + +現在我們已經瞭解了標記化的工作原理,我們可以更深入地研究訓練期間使用的損失。在任何給定的階段,這個損失是通過對語料庫中的每個單詞進行標記來計算的,使用當前詞彙表和由語料庫中每個標記的頻率確定的 Unigram 模型(如前所述)。 + +語料庫中的每個詞都有一個分數,損失是這些分數的負對數似然 -- 即所有詞的語料庫中所有詞的總和 `-log(P(word))`。 + +讓我們用以下語料庫回到我們的例子: + +``` +("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5) +``` + +每個單詞的標記化及其各自的分數是: + +``` +"hug": ["hug"] (score 0.071428) +"pug": ["pu", "g"] (score 0.007710) +"pun": ["pu", "n"] (score 0.006168) +"bun": ["bu", "n"] (score 0.001451) +"hugs": ["hug", "s"] (score 0.001701) +``` + +所以損失是: + +``` +10 * (-log(0.071428)) + 5 * (-log(0.007710)) + 12 * (-log(0.006168)) + 4 * (-log(0.001451)) + 5 * (-log(0.001701)) = 169.8 +``` + +現在我們需要計算刪除每個標記如何影響損失。這相當乏味,所以我們在這裡只對兩個標記進行操作,並保存整個過程以備有代碼來幫助我們。在這個(非常)特殊的情況下,我們對所有單詞有兩個等效的標記:正如我們之前看到的,例如, `"pug"` 可以以相同的分數被標記為 `["p", "ug"]`。因此,去除詞彙表中的 `"pu"` 標記將給出完全相同的損失。 + +另一方面,去除 `"hug"` 損失變得更糟, 因為 `"hug"` 和 `"hugs"` 的標記化會變成: + +``` +"hug": ["hu", "g"] (score 0.006802) +"hugs": ["hu", "gs"] (score 0.001701) +``` + +這些變化將導致損失增加: + +``` +- 10 * (-log(0.071428)) + 10 * (-log(0.006802)) = 23.5 +``` + +因此, 標記 `"pu"`可能會從詞彙表中刪除,但不會刪除 `"hug"`. + +## 實現 Unigram + +現在讓我們在代碼中實現我們迄今為止看到的所有內容。與 BPE 和 WordPiece 一樣,這不是 Unigram 算法的有效實現(恰恰相反),但它應該可以幫助你更好地理解它。 + +我們將使用與之前相同的語料庫作為示例: + +```python +corpus = [ + "This is the Hugging Face course.", + "This chapter is about tokenization.", + "This section shows several tokenizer algorithms.", + "Hopefully, you will be able to understand how they are trained and generate tokens.", +] +``` + +這一次,我們將使用 `xlnet-base-cased` 作為我們的模型: + +```python +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained("xlnet-base-cased") +``` + +與 BPE 和 WordPiece 一樣,我們首先計算語料庫中每個單詞的出現次數: + +```python +from collections import defaultdict + +word_freqs = defaultdict(int) +for text in corpus: + words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text) + new_words = [word for word, offset in words_with_offsets] + for word in new_words: + word_freqs[word] += 1 + +word_freqs +``` + +然後,我們需要將我們的詞彙表初始化為大於我們最終想要的詞彙量。我們必須包含所有基本字符(否則我們將無法標記每個單詞),但對於較大的子字符串,我們將只保留最常見的字符,因此我們按頻率對它們進行排序: + +```python +char_freqs = defaultdict(int) +subwords_freqs = defaultdict(int) +for word, freq in word_freqs.items(): + for i in range(len(word)): + char_freqs[word[i]] += freq + # Loop through the subwords of length at least 2 + for j in range(i + 2, len(word) + 1): + subwords_freqs[word[i:j]] += freq + +# Sort subwords by frequency +sorted_subwords = sorted(subwords_freqs.items(), key=lambda x: x[1], reverse=True) +sorted_subwords[:10] +``` + +```python out +[('▁t', 7), ('is', 5), ('er', 5), ('▁a', 5), ('▁to', 4), ('to', 4), ('en', 4), ('▁T', 3), ('▁Th', 3), ('▁Thi', 3)] +``` + +我們用最優的子詞對字符進行分組,以獲得大小為 300 的初始詞彙表: + +```python +token_freqs = list(char_freqs.items()) + sorted_subwords[: 300 - len(char_freqs)] +token_freqs = {token: freq for token, freq in token_freqs} +``` + + + +💡 SentencePiece 使用一種稱為增強後綴數組(ESA)的更高效算法來創建初始詞彙表。 + + + +接下來,我們計算所有頻率的總和,將頻率轉換為概率。對於我們的模型,我們將存儲概率的對數,因為添加對數比乘以小數在數值上更穩定,這將簡化模型損失的計算: + +```python +from math import log + +total_sum = sum([freq for token, freq in token_freqs.items()]) +model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()} +``` + +N現在主要功能是使用 Viterbi 算法標記單詞的功能。正如我們之前看到的,該算法計算單詞的每個子串的最佳分段,我們將其存儲在名為 `best_segmentations` 的變量中。我們將在單詞的每個位置(從 0 到其總長度)存儲一個字典,有兩個鍵:最佳分割中最後一個標記的開始索引,以及最佳分割的分數。使用最後一個標記的開始索引,一旦列表完全填充,我們將能夠檢索完整的分段。 + +填充列表只需兩個循環:主循環遍歷每個起始位置,第二個循環嘗試從該起始位置開始的所有子字符串。如果子串在詞彙表中,我們有一個新的詞分段,直到該結束位置,我們將其與 `best_segmentations` 相比較。 + +一旦主循環完成,我們就從結尾開始,從一個開始位置跳到下一個,記錄我們前進的標記,直到我們到達單詞的開頭: + +```python +def encode_word(word, model): + best_segmentations = [{"start": 0, "score": 1}] + [ + {"start": None, "score": None} for _ in range(len(word)) + ] + for start_idx in range(len(word)): + # This should be properly filled by the previous steps of the loop + best_score_at_start = best_segmentations[start_idx]["score"] + for end_idx in range(start_idx + 1, len(word) + 1): + token = word[start_idx:end_idx] + if token in model and best_score_at_start is not None: + score = model[token] + best_score_at_start + # If we have found a better segmentation ending at end_idx, we update + if ( + best_segmentations[end_idx]["score"] is None + or best_segmentations[end_idx]["score"] > score + ): + best_segmentations[end_idx] = {"start": start_idx, "score": score} + + segmentation = best_segmentations[-1] + if segmentation["score"] is None: + # We did not find a tokenization of the word -> unknown + return [""], None + + score = segmentation["score"] + start = segmentation["start"] + end = len(word) + tokens = [] + while start != 0: + tokens.insert(0, word[start:end]) + next_start = best_segmentations[start]["start"] + end = start + start = next_start + tokens.insert(0, word[start:end]) + return tokens, score +``` + +我們已經可以在一些詞上嘗試我們的初始模型: + +```python +print(encode_word("Hopefully", model)) +print(encode_word("This", model)) +``` + +```python out +(['H', 'o', 'p', 'e', 'f', 'u', 'll', 'y'], 41.5157494601402) +(['This'], 6.288267030694535) +``` + +現在很容易計算模型在語料庫上的損失! + +```python +def compute_loss(model): + loss = 0 + for word, freq in word_freqs.items(): + _, word_loss = encode_word(word, model) + loss += freq * word_loss + return loss +``` + +我們可以檢查它是否適用於我們擁有的模型: + +```python +compute_loss(model) +``` + +```python out +413.10377642940875 +``` + +計算每個標記的分數也不是很難;我們只需要計算通過刪除每個標記獲得的模型的損失: + +```python +import copy + + +def compute_scores(model): + scores = {} + model_loss = compute_loss(model) + for token, score in model.items(): + # We always keep tokens of length 1 + if len(token) == 1: + continue + model_without_token = copy.deepcopy(model) + _ = model_without_token.pop(token) + scores[token] = compute_loss(model_without_token) - model_loss + return scores +``` + +我們可以在給定的標記上嘗試: + +```python +scores = compute_scores(model) +print(scores["ll"]) +print(scores["his"]) +``` + +自從 `"ll"` 用於標記化 `"Hopefully"`, 刪除它可能會讓我們使用標記 `"l"` 兩次相反,我們預計它將產生正損失。 `"his"` 僅在單詞`"This"` 內使用,它被標記為自身,所以我們期望它的損失為零。結果如下: + +```python out +6.376412403623874 +0.0 +``` + + + +💡 這種方法非常低效,因此 SentencePiece 使用了沒有標記 X 的模型損失的近似值:它不是從頭開始,而是通過其在剩餘詞彙表中的分段替換標記 X。這樣,所有分數可以與模型損失同時計算。 + + + +完成所有這些後,我們需要做的最後一件事是將模型使用的特殊標記添加到詞彙表中,然後循環直到我們從詞彙表中修剪了足夠的標記以達到我們想要的大小: + +```python +percent_to_remove = 0.1 +while len(model) > 100: + scores = compute_scores(model) + sorted_scores = sorted(scores.items(), key=lambda x: x[1]) + # Remove percent_to_remove tokens with the lowest scores. + for i in range(int(len(model) * percent_to_remove)): + _ = token_freqs.pop(sorted_scores[i][0]) + + total_sum = sum([freq for token, freq in token_freqs.items()]) + model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()} +``` + +然後,為了標記一些文本,我們只需要應用預標記化,然後使用我們的 `encode_word()` 函數: + +```python +def tokenize(text, model): + words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text) + pre_tokenized_text = [word for word, offset in words_with_offsets] + encoded_words = [encode_word(word, model)[0] for word in pre_tokenized_text] + return sum(encoded_words, []) + + +tokenize("This is the Hugging Face course.", model) +``` + +```python out +['▁This', '▁is', '▁the', '▁Hugging', '▁Face', '▁', 'c', 'ou', 'r', 's', 'e', '.'] +``` + +Unigram 就是這樣!希望現在你感覺自己是標記器所有方面的專家。在下一節中,我們將深入研究 🤗 Tokenizers 庫的構建塊,並向您展示如何使用它們來構建您自己的標記器。 diff --git a/chapters/zh-TW/chapter6/8.mdx b/chapters/zh-TW/chapter6/8.mdx new file mode 100644 index 000000000..9a31a2bf1 --- /dev/null +++ b/chapters/zh-TW/chapter6/8.mdx @@ -0,0 +1,564 @@ +# 逐塊地構建標記器 + + + +正如我們在前幾節中看到的,標記化包括幾個步驟: + +- 規範化(任何認為必要的文本清理,例如刪除空格或重音符號、Unicode 規範化等) +- 預標記化(將輸入拆分為單詞) +- 通過模型處理輸入(使用預先拆分的詞來生成一系列標記) +- 後處理(添加標記器的特殊標記,生成注意力掩碼和標記類型 ID) + +提醒一下,這裡再看一下整個過程 + +
+The tokenization pipeline. + +
+ +🤗 Tokenizers 庫旨在為每個步驟提供多個選項,您可以將它們混合和匹配在一起。在本節中,我們將看到如何從頭開始構建標記器,而不是像我們[第二節 2](/course/chapter6/2)那樣從舊的標記器訓練新的標記器.然後,您將能夠構建您能想到的任何類型的標記器! + + + +更準確地說,該庫是圍繞一個中央「Tokenizer」類構建的,構建這個類的每一部分可以在子模塊的列表中重新組合: + +- `normalizers` 包含你可以使用的所有可能的Normalizer類型(完整列表[在這裡](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.normalizers))。 +- `pre_tokenizesr` 包含您可以使用的所有可能的PreTokenizer類型(完整列表[在這裡](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.pre_tokenizers))。 +- `models` 包含您可以使用的各種類型的Model,如BPE、WordPiece和Unigram(完整列表[在這裡](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.models))。 +- `trainers` 包含所有不同類型的 trainer,你可以使用一個語料庫訓練你的模型(每種模型一個;完整列表[在這裡](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.trainers))。 +- `post_processors` 包含你可以使用的各種類型的PostProcessor(完整列表[在這裡](https://huggingface.co/docs/tokenizers/python/latest/api/reference.html#module-tokenizers.processors))。 +- `decoders` 包含各種類型的Decoder,可以用來解碼標記化的輸出(完整列表[在這裡](https://huggingface.co/docs/tokenizers/python/latest/components.html#decoders))。 + +您可以[在這裡](https://huggingface.co/docs/tokenizers/python/latest/components.html)找到完整的模塊列表。 + +## 獲取語​​料庫 + +為了訓練我們的新標記器,我們將使用一個小的文本語料庫(因此示例運行得很快)。獲取語​​料庫的步驟與我們在[在這章的開始]((/course/chapter6/2)那一小節,但這次我們將使用[WikiText-2](https://huggingface.co/datasets/wikitext)數據集: + +```python +from datasets import load_dataset + +dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train") + + +def get_training_corpus(): + for i in range(0, len(dataset), 1000): + yield dataset[i : i + 1000]["text"] +``` + +**get_training_corpus()** 函數是一個生成器,每次調用的時候將產生 1,000 個文本,我們將用它來訓練標記器。 + +🤗 Tokenizers 也可以直接在文本文件上進行訓練。以下是我們如何生成一個文本文件,其中包含我們可以在本地使用的來自 WikiText-2 的所有文本/輸入: + +```python +with open("wikitext-2.txt", "w", encoding="utf-8") as f: + for i in range(len(dataset)): + f.write(dataset[i]["text"] + "\n") +``` + +接下來,我們將向您展示如何逐塊構建您自己的 BERT、GPT-2 和 XLNet 標記器。這將為我們提供三個主要標記化算法的示例:WordPiece、BPE 和 Unigram。讓我們從 BERT 開始吧! + +## 從頭開始構建 WordPiece 標記器 + +要使用 🤗 Tokenizers 庫構建標記器,我們首先使用 **model** 實例化一個 **Tokenizer** 對象,然後將 **normalizer** , **pre_tokenizer** , **post_processor** , 和 **decoder** 屬性設置成我們想要的值。 + +對於這個例子,我們將創建一個 **Tokenizer** 使用 WordPiece 模型: + +```python +from tokenizers import ( + decoders, + models, + normalizers, + pre_tokenizers, + processors, + trainers, + Tokenizer, +) + +tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]")) +``` + +我們必須指定 **unk_token** 這樣模型才知道當它遇到以前沒有見過的字符時要返回什麼。我們可以在此處設置的其他參數包括我們模型的**vocab(字典)**(我們將訓練模型,所以我們不需要設置它)和 **max_input_chars_per_word** 即每個單詞的最大長度(比傳遞的值長的單詞將被拆分) + +標記化的第一步是規範化,所以讓我們從它開始。 由於 BERT 被廣泛使用,所以有一個可以使用的 `BertNormalizer`,我們可以為 BERT 設置經典的選項:`lowercase(小寫)` 和 `strip_accents(去除音調)`,不言自明; `clean_text` 刪除所有控制字符並將重複的空格替換為一個; 和 `handle_chinese_chars`,在漢字周圍放置空格。 要實現 `bert-base-uncased` ,我們可以這樣設置這個規範器: + +```python +tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True) +``` + +然而,一般來說,在構建新的標記器時,您可以使用已經在 🤗 Tokenizers庫中實現的非常方便的normalizer——所以讓我們看看如何手動創建 BERT normalizer。 該庫提供了一個“Lowercase(小寫)”的normalizer和一個“StripAccents”的normalizer,您可以使用“序列”組合多個normalizer: + +```python +tokenizer.normalizer = normalizers.Sequence( + [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()] +) +``` + +我們也在使用 **NFD** Unicode normalizer,否則 **StripAccents** normalizer 無法正確識別帶重音的字符,因此沒辦法刪除它們。 + +正如我們之前看到的,我們可以使用 **normalize** 的 **normalize_str()** 方法查看它對給定文本的影響: + +```python +print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?")) +``` + +```python out +hello how are u? +``` + + + +**更進一步**如果您在包含 unicode 字符的字符串上測試先前normalizers的兩個版本,您肯定會注意到這兩個normalizers並不完全等效。 +為了不過度使用 `normalizers.Sequence` 使版本過於複雜,我們沒有包含當 `clean_text` 參數設置為 `True` 時 `BertNormalizer` 需要的正則表達式替換 - 這是默認行為。 但不要擔心:通過在normalizer序列中添加兩個 `normalizers.Replace` 可以在不使用方便的 `BertNormalizer` 的情況下獲得完全相同的規範化。 + + + +接下來是預標記步驟。 同樣,我們可以使用一個預構建的“BertPreTokenizer”: + +```python +tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer() +``` + +或者我們可以從頭開始構建它: + +```python +tokenizer.pre_tokenizer = pre_tokenizers.Whitespace() +``` + +請注意,`Whitespace` 預標記器會在空格和所有非字母、數字或下劃線字符的字符上進行拆分,因此在本次的例子中上會根據空格和標點符號進行拆分: + +```python +tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.") +``` + +```python out +[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)), + ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))] +``` + +如果您只想在空白處進行拆分,則應使用 **WhitespaceSplit** 代替預標記器: + +```python +pre_tokenizer = pre_tokenizers.WhitespaceSplit() +pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.") +``` + +```python out +[("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))] +``` + +像normalizers一樣,您可以使用 **Sequence** 組成幾個預標記器: + +```python +pre_tokenizer = pre_tokenizers.Sequence( + [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()] +) +pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.") +``` + +```python out +[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)), + ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))] +``` + +標記化管道的下一步是輸入給模型。我們已經在初始化中指定了我們的模型,但我們仍然需要訓練它,這將需要一個 **WordPieceTrainer** .在 🤗 Tokenizers 中實例化訓練器時要記住的主要事情是,您需要將您打算使用的所有特殊標記傳遞給它 - 否則它不會將它們添加到詞彙表中,因為它們不在訓練語料庫中: + +```python +special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"] +trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens) +``` + +以及指定 **vocab_size(詞典大小)** 和 **special_tokens(特殊的標記)** ,我們可以設置 **min_frequency** (記號必須出現在詞彙表中的次數)或更改 **continuing_subword_prefix** (如果我們想使用與 **##**指代存在與字詞相同的前綴 )。 + +要使用我們之前定義的迭代器訓練我們的模型,我們只需要執行以下命令: + +```python +tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer) +``` + +我們還可以使用文本文件來訓練我們的標記器,它看起來像這樣(我們需要先初始化一個空的 **WordPiece** ): + +```python +tokenizer.model = models.WordPiece(unk_token="[UNK]") +tokenizer.train(["wikitext-2.txt"], trainer=trainer) +``` + +在這兩種情況下,我們都可以通過調用文本來測試標記器 **encode()** 方法: + +```python +encoding = tokenizer.encode("Let's test this tokenizer.") +print(encoding.tokens) +``` + +```python out +['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.'] +``` + +這個 **encoding** 獲得的是一個 **Encoding**對象 ,它的屬性中包含標記器的所有必要輸出: **ids** , **type_ids** , **tokens** , **offsets** , **attention_mask** , **special_tokens_mask** , 和 **overflowing** . + +標記化管道的最後一步是後處理。我們需要添加 **[CLS]** 開頭的標記和 **[SEP]** 標記在末尾(或在每個句子之後,如果我們有一對句子)。我們將使用一個 **TemplateProcessor** 為此,但首先我們需要知道 **[CLS]** 和 **[SEP]** 在詞彙表中的ID: + +```python +cls_token_id = tokenizer.token_to_id("[CLS]") +sep_token_id = tokenizer.token_to_id("[SEP]") +print(cls_token_id, sep_token_id) +``` + +```python out +(2, 3) +``` + +為了給 **TemplateProcessor** 編寫模板,我們必須指定如何處理單個句子和一對句子。對於兩者,我們都編寫了我們想要使用的特殊標記;第一個(或單個)句子表示為 **$A** ,而第二個句子(如果對一對進行編碼)表示為 **$B** .對於這些特殊標記和句子,我們還需要使用在冒號後指定相應的標記類型 ID。 + +因此經典的 BERT 模板定義如下: + +```python +tokenizer.post_processor = processors.TemplateProcessing( + single=f"[CLS]:0 $A:0 [SEP]:0", + pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1", + special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)], +) +``` + +請注意,我們需要傳遞特殊標記的 ID,以便標記器可以正確地將特殊標記轉換為它們的 ID。 + +添加後,我們之前的示例將輸出出: + +```python +encoding = tokenizer.encode("Let's test this tokenizer.") +print(encoding.tokens) +``` + +```python out +['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]'] +``` + +在一對句子中,我們得到了正確的結果: +```python +encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.") +print(encoding.tokens) +print(encoding.type_ids) +``` + +```python out +['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]'] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1] +``` + +我們幾乎從頭開始構建了這個標記器——但是還有最後一步是指定一個解碼器: + +```python +tokenizer.decoder = decoders.WordPiece(prefix="##") +``` + +讓我們測試一下我們之前的 **encoding** : + +```python +tokenizer.decode(encoding.ids) +``` + +```python out +"let's test this tokenizer... on a pair of sentences." +``` + +很好!我們可以將標記器保存在一個 JSON 文件中,如下所示: + +```python +tokenizer.save("tokenizer.json") +``` + +然後我們可以使用**from_file()** 方法從該文件裡重新加載 **Tokenizer** 對象: + +```python +new_tokenizer = Tokenizer.from_file("tokenizer.json") +``` + +要在 🤗 Transformers 中使用這個標記器,我們必須將它包裹在一個 **PreTrainedTokenizerFast** 類中。我們可以使用泛型類,或者,如果我們的標記器對應於現有模型,則使用該類(例如這裡的 **BertTokenizerFast** )。如果您應用本課來構建全新的標記器,則必須使用第一個選項。 + +要將標記器包裝在 `PreTrainedTokenizerFast` 類中,我們可以將我們構建的標記器作為`tokenizer_object` 傳遞,或者將我們保存為`tokenizer_file` 的標記器文件傳遞。 要記住的關鍵是我們必須手動設置所有特殊標記,因為該類無法從 `tokenizer` 對象推斷出哪個標記是掩碼標記、`[CLS]` 標記等: + +```python +from transformers import PreTrainedTokenizerFast + +wrapped_tokenizer = PreTrainedTokenizerFast( + tokenizer_object=tokenizer, + # tokenizer_file="tokenizer.json", # You can load from the tokenizer file, alternatively + unk_token="[UNK]", + pad_token="[PAD]", + cls_token="[CLS]", + sep_token="[SEP]", + mask_token="[MASK]", +) +``` + +如果您使用特定的標記器類(例如 **BertTokenizerFast** ),您只需要指定與默認標記不同的特殊標記(此處沒有): + +```python +from transformers import BertTokenizerFast + +wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer) +``` + +然後,您可以像使用任何其他 🤗 Transformers 標記器一樣使用此標記器。你可以用 **save_pretrained()** 方法,或使用 **push_to_hub()** 方法。 + +現在我們已經瞭解瞭如何構建 WordPiece 標記器,讓我們對 BPE 標記器進行同樣的操作。因為您已經知道了所有步驟,所以我們會進行地更快一點,並且只突出展示兩者不一樣的地方。 + +## 從頭開始構建 BPE 標記器 + +現在讓我們構建一個 GPT-2 標記器。與 BERT 標記器一樣,我們首先使用 **Tokenizer** 初始化一個BPE 模型: + +```python +tokenizer = Tokenizer(models.BPE()) +``` + +和 BERT 一樣,如果我們有一個詞彙表,我們可以用一個詞彙表來初始化這個模型(在這種情況下,我們需要傳遞 `vocab` 和 `merges`),但是由於我們將從頭開始訓練,所以我們不需要這樣去做。 我們也不需要指定“unk_token”,因為 GPT-2 使用的字節級 BPE,不需要“unk_token”。 + +GPT-2 不使用歸一化器,因此我們跳過該步驟並直接進入預標記化: + +```python +tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False) +``` + +我們在此處添加到 `ByteLevel` 的選項是不在句子開頭添加空格(默認為ture)。 我們可以看一下使用這個標記器對之前示例文本的預標記: + +```python +tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!") +``` + +```python out +[('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)), + ('tokenization', (15, 27)), ('!', (27, 28))] +``` + +接下來是需要訓練的模型。對於 GPT-2,唯一的特殊標記是文本結束標記: + +```python +trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"]) +tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer) +``` + +與 `WordPieceTrainer` 以及 `vocab_size` 和 `special_tokens` 一樣,我們可以指定 `min_frequency` 如果我們願意,或者如果我們有一個詞尾後綴(如 `` ),我們可以使用 `end_of_word_suffix` 設置它。 + +這個標記器也可以在文本文件上訓練: + +```python +tokenizer.model = models.BPE() +tokenizer.train(["wikitext-2.txt"], trainer=trainer) +``` + +讓我們看一下示例文本的標記化後的結果: + +```python +encoding = tokenizer.encode("Let's test this tokenizer.") +print(encoding.tokens) +``` + +```python out +['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.'] +``` + +我們對 GPT-2 標記器添加字節級後處理,如下所示: + +```python +tokenizer.post_processor = processors.ByteLevel(trim_offsets=False) +``` + +`trim_offsets = False` 選項指示我們應該保留以 'Ġ' 開頭的標記的偏移量:這樣偏移量的開頭將指向單詞之前的空格,而不是第一個單詞的字符(因為空格在技術上是標記的一部分)。 讓我們看看我們剛剛編碼的文本的結果,其中 `'Ġtest'` 是索引第 4 處的標記: + +```python +sentence = "Let's test this tokenizer." +encoding = tokenizer.encode(sentence) +start, end = encoding.offsets[4] +sentence[start:end] +``` + +```python out +' test' +``` + +最後,我們添加一個字節級解碼器: + +```python +tokenizer.decoder = decoders.ByteLevel() +``` + +我們可以仔細檢查它是否正常工作: + +```python +tokenizer.decode(encoding.ids) +``` + +```python out +"Let's test this tokenizer." +``` + +很好!現在我們完成了,我們可以像以前一樣保存標記器,並將它包裝在一個 **PreTrainedTokenizerFast** 或者 **GPT2TokenizerFast** 如果我們想在 🤗 Transformers中使用它: + +```python +from transformers import PreTrainedTokenizerFast + +wrapped_tokenizer = PreTrainedTokenizerFast( + tokenizer_object=tokenizer, + bos_token="<|endoftext|>", + eos_token="<|endoftext|>", +) +``` + +或者: + +```python +from transformers import GPT2TokenizerFast + +wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer) +``` + +作為最後一個示例,我們將向您展示如何從頭開始構建 Unigram 標記器。 + +## 從頭開始構建 Unigram 標記器 + +現在讓我們構建一個 XLNet 標記器。與之前的標記器一樣,我們首先使用 Unigram 模型初始化一個 **Tokenizer** : + +```python +tokenizer = Tokenizer(models.Unigram()) +``` + +同樣,如果我們有詞彙表,我們可以用詞彙表初始化這個模型。 + +對於標準化,XLNet 使用了一些替換的方法(來自 SentencePiece): + +```python +from tokenizers import Regex + +tokenizer.normalizer = normalizers.Sequence( + [ + normalizers.Replace("``", '"'), + normalizers.Replace("''", '"'), + normalizers.NFKD(), + normalizers.StripAccents(), + normalizers.Replace(Regex(" {2,}"), " "), + ] +) +``` + +這會取代 **“** 和 **”** 和 **”** 以及任何兩個或多個空格與單個空格的序列,以及刪除文本中的重音以進行標記。 + +用於任何 SentencePiece 標記器的預標記器是 `Metaspace`: + +```python +tokenizer.pre_tokenizer = pre_tokenizers.Metaspace() +``` + +我們可以像以前一樣查看示例文本的預標記化: + +```python +tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!") +``` + +```python out +[("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))] +``` + +接下來是需要訓練的模型。 XLNet 有不少特殊的標記: + +```python +special_tokens = ["", "", "", "", "", "", ""] +trainer = trainers.UnigramTrainer( + vocab_size=25000, special_tokens=special_tokens, unk_token="" +) +tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer) +``` + +不要忘記`UnigramTrainer` 的一個非常重要的參數是`unk_token`。 我們還可以傳遞特定於 Unigram 算法的其他參數,例如刪除標記的每個步驟的“shrinking_factor(收縮因子)”(默認為 0.75)或指定給定標記的最大長度的“max_piece_length”(默認為 16) . + +這個標記器也可以在文本文件上訓練: + +```python +tokenizer.model = models.Unigram() +tokenizer.train(["wikitext-2.txt"], trainer=trainer) +``` + +讓我們看一下示例文本的標記化後的結果: + +```python +encoding = tokenizer.encode("Let's test this tokenizer.") +print(encoding.tokens) +``` + +```python out +['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.'] +``` + +A peculiarity of XLNet is that it puts the `` token at the end of the sentence, with a type ID of 2 (to distinguish it from the other tokens). It's padding on the left, as a result. We can deal with all the special tokens and token type IDs with a template, like for BERT, but first we have to get the IDs of the `` and `` tokens: +XLNet 的一個特點是它將`` 標記放在句子的末尾,類型ID 為2(以將其與其他標記區分開來)。它會將結果填充在左側。 我們可以使用模板處理所有特殊標記和標記類型 ID,例如 BERT,但首先我們必須獲取 `` 和 `` 標記的 ID: + +```python +cls_token_id = tokenizer.token_to_id("") +sep_token_id = tokenizer.token_to_id("") +print(cls_token_id, sep_token_id) +``` + +```python out +0 1 +``` + +模板如下所示: + +```python +tokenizer.post_processor = processors.TemplateProcessing( + single="$A:0 :0 :2", + pair="$A:0 :0 $B:1 :1 :2", + special_tokens=[("", sep_token_id), ("", cls_token_id)], +) +``` + +我們可以通過編碼一對句子來測試它的工作原理: + +```python +encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!") +print(encoding.tokens) +print(encoding.type_ids) +``` + +```python out +['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '', '▁', 'on', '▁', 'a', '▁pair', + '▁of', '▁sentence', 's', '!', '', ''] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2] +``` + +最後,我們添加一個 **Metaspace** 解碼器: + +```python +tokenizer.decoder = decoders.Metaspace() +``` + +我們完成了這個標記器! 我們可以像以前一樣保存標記器,如果我們想在 🤗 Transformers 中使用它,可以將它包裝在 `PreTrainedTokenizerFast` 或 `XLNetTokenizerFast` 中。 使用 `PreTrainedTokenizerFast` 時要注意的一件事是,我們需要告訴🤗 Transformers 庫應該在左側填充特殊標記: +```python +from transformers import PreTrainedTokenizerFast + +wrapped_tokenizer = PreTrainedTokenizerFast( + tokenizer_object=tokenizer, + bos_token="", + eos_token="", + unk_token="", + pad_token="", + cls_token="", + sep_token="", + mask_token="", + padding_side="left", +) +``` + +或者: + +```python +from transformers import XLNetTokenizerFast + +wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer) +``` + +現在您已經瞭解瞭如何使用各種構建塊來構建現有的標記器,您應該能夠使用 🤗 tokenizer庫編寫您想要的任何標記器,並能夠在 🤗 Transformers中使用它。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter6/9.mdx b/chapters/zh-TW/chapter6/9.mdx new file mode 100644 index 000000000..2a8064b81 --- /dev/null +++ b/chapters/zh-TW/chapter6/9.mdx @@ -0,0 +1,16 @@ +# 標記器,回顧! + + + +完成這一章,辛苦了! + +在深入研究標記器之後,您應該: + +- 能夠使用舊的標記器作為模板來訓練新的標記器 +- 瞭解如何使用偏移量將標記的位置映射到其原始文本範圍 +- 瞭解 BPE、WordPiece 和 Unigram 之間的區別 +- 能夠混合和匹配 🤗 Tokenizers 庫提供的塊來構建您自己的標記器 +- 能夠在 🤗 Transformers 庫中使用該標記器 \ No newline at end of file diff --git a/chapters/zh-TW/chapter7/1.mdx b/chapters/zh-TW/chapter7/1.mdx new file mode 100644 index 000000000..038fcc3bd --- /dev/null +++ b/chapters/zh-TW/chapter7/1.mdx @@ -0,0 +1,33 @@ + + +# 章節簡介 + +在[第三章](/course/chapter3),您瞭解瞭如何微調文本分類的模型。在本章中,我們將處理以下常見NLP任務: + +- 標記(token)分類 +- 遮罩語言建模(如BERT) +- 提取文本摘要 +- 翻譯 +- 因果語言建模預訓練(如GPT-2) +- 問答 + +{#if fw === 'pt'} + +為此,您需要利用[第三章](/course/chapter3)中學到的`Trainer` API 和🤗Accelerate 庫、[第五章](/course/chapter5)中的 🤗 Datasets 庫以及[第六章](/course/chapter6)中的 🤗 Tokenizers 庫的所有知識。我們還會將結果上傳到模型中心,就像我們在[第四章](/course/chapter4)中所做的那樣,所以這確實是將之前所有內容彙集在一起的章節! + +每個部分都可以獨立閱讀,並將向您展示如何使用API或按照您自己的訓練循環訓練模型,使用🤗 Accelerate 加速。你可以隨意跳過其中一部分,把注意力集中在你最感興趣的那一部分:API可以優化或訓練您的模型而無需擔心幕後發生了什麼,而訓練循環使用可以讓您更輕鬆地自定義所需的任何結構。 + +{:else} + +為此,您需要利用[第三章](/course/chapter3)中學到的有關Keras API、[第五章](/course/chapter5)中的 🤗 Datasets 庫以及[第六章](/course/chapter6)中的 🤗 Tokenizers 庫的所有知識。我們還會將結果上傳到模型中心,就像我們在[第四章](/course/chapter4)中所做的那樣,所以這確實是將之前所有內容彙集在一起的章節! + +每個部分都可以獨立閱讀。 + +{/if} + + + + +如果您按順序閱讀這些部分,您會注意到它們有很多共同的代碼和陳述。 重複是有意為之的,讓您可以深入(或稍後返回)任何您感興趣的任務並找到一個完整的工作示例。 + + diff --git a/chapters/zh-TW/chapter7/2.mdx b/chapters/zh-TW/chapter7/2.mdx new file mode 100644 index 000000000..3652439a7 --- /dev/null +++ b/chapters/zh-TW/chapter7/2.mdx @@ -0,0 +1,978 @@ + + +# Token 分類 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +我們將探索的第一個應用是Token分類。這個通用任務包括任何可以表述為“為句子中的詞或字分配標籤”的問題,例如: + +- **實體命名識別 (NER)**: 找出句子中的實體(如人物、地點或組織)。這可以通過為每個實體或“無實體”指定一個類別的標籤。 +- **詞性標註 (POS)**: 將句子中的每個單詞標記為對應於特定的詞性(如名詞、動詞、形容詞等)。 +- **分塊(chunking)**: 找到屬於同一實體的Token。這個任務(可結合POS或NER)可以任何將一塊Token作為制定一個標籤(通常是B -),另一個標籤(通常I -)表示Token是否是同一塊,和第三個標籤(通常是O)表示Token不屬於任何塊。也就是標出句子中的短語塊,例如名詞短語(NP),動詞短語(VP)等。 + + + +當然,還有很多其他類型的token分類問題;這些只是幾個有代表性的例子。在本節中,我們將在 NER 任務上微調模型 (BERT),然後該模型將能夠計算如下預測: + + + + + +One-hot encoded labels for question answering. + + + +您可以[在這裡](https://huggingface.co/huggingface-course/bert-finetuned-ner?text=My+name+is+Sylvain+and+I+work+at+Hugging+Face+in+Brooklyn).找到我們將訓練並上傳到 Hub的模型,可以嘗試輸入一些句子看看模型的預測結果。 + +## 準備數據 + +首先,我們需要一個適合標記分類的數據集。在本節中,我們將使用[CoNLL-2003 數據集](https://huggingface.co/datasets/conll2003), 其中包含來自路透社的新聞報道。 + + + +💡 只要您的數據集由帶有相應標籤的分割成單詞並的文本組成,您就能夠將這裡描述的數據處理過程應用到您自己的數據集。如果需要複習如何在.Dataset中加載自定義數據,請參閱[Chapter 5](/course/chapter5)。 + + + +### CoNLL-2003 數據集 + +要加載 CoNLL-2003 數據集,我們使用 來自 🤗 Datasets 庫的**load_dataset()** 方法: + +```py +from datasets import load_dataset + +raw_datasets = load_dataset("conll2003") +``` + +這將下載並緩存數據集,就像和我們在[第三章](/course/chapter3) 加載GLUE MRPC 數據集一樣。檢查這個對象可以讓我們看到存在哪些列,以及訓練集、驗證集和測試集之間是如何分割的: + +```py +raw_datasets +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'], + num_rows: 14041 + }) + validation: Dataset({ + features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'], + num_rows: 3250 + }) + test: Dataset({ + features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'], + num_rows: 3453 + }) +}) +``` + +特別是,我們可以看到數據集包含我們之前提到的三個任務的標籤:NER、POS 和chunking。與其他數據集的一個很大區別是輸入文本不是作為句子或文檔呈現的,而是單詞列表(最後一列稱為 **tokens** ,但它包含的是這些詞是預先標記化的輸入,仍然需要通過標記器進行子詞標記)。 + +我們來看看訓練集的第一個元素: + +```py +raw_datasets["train"][0]["tokens"] +``` + +```python out +['EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'lamb', '.'] +``` + +由於我們要執行命名實體識別,我們將查看 NER 標籤: + +```py +raw_datasets["train"][0]["ner_tags"] +``` + +```python out +[3, 0, 7, 0, 0, 0, 7, 0, 0] +``` + +這一列是類標籤的序列。元素的類型在ner_feature的feature屬性中,我們可以通過查看該特性的names屬性來訪問名稱列表: + +```py +ner_feature = raw_datasets["train"].features["ner_tags"] +ner_feature +``` + +```python out +Sequence(feature=ClassLabel(num_classes=9, names=['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC'], names_file=None, id=None), length=-1, id=None) +``` + +因此,這一列包含的元素是ClassLabels的序列。序列元素的類型在`ner_feature`的`feature`中,我們可以通過查看該`feature`的`names`屬性來訪問名稱列表: + +```py +label_names = ner_feature.feature.names +label_names +``` + +```python out +['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC'] +``` + +我們在[第六章](/course/chapter6/3), 深入研究**token-classification** 管道時已經看到了這些標籤 ,但為了快速複習: + +- `O` 表示這個詞不對應任何實體。 +- `B-PER`/`I-PER`意味著這個詞對應於人名實體的開頭/內部。 +- `B-ORG`/`I-ORG` 的意思是這個詞對應於組織名稱實體的開頭/內部。 +- `B-LOC`/`I-LOC` 指的是是這個詞對應於地名實體的開頭/內部。 +- `B-MISC`/`I-MISC` 表示該詞對應於一個雜項實體的開頭/內部。 + +現在解碼我們之前看到的標籤: + +```python +words = raw_datasets["train"][0]["tokens"] +labels = raw_datasets["train"][0]["ner_tags"] +line1 = "" +line2 = "" +for word, label in zip(words, labels): + full_label = label_names[label] + max_length = max(len(word), len(full_label)) + line1 += word + " " * (max_length - len(word) + 1) + line2 += full_label + " " * (max_length - len(full_label) + 1) + +print(line1) +print(line2) +``` + +```python out +'EU rejects German call to boycott British lamb .' +'B-ORG O B-MISC O O O B-MISC O O' +``` + +例如混合 **B-** 和 **I-** 標籤,這是相同的代碼在索引 4 的訓練集元素上的預測結果: + +```python out +'Germany \'s representative to the European Union \'s veterinary committee Werner Zwingmann said on Wednesday consumers should buy sheepmeat from countries other than Britain until the scientific advice was clearer .' +'B-LOC O O O O B-ORG I-ORG O O O B-PER I-PER O O O O O O O O O O O B-LOC O O O O O O O' +``` + +正如我們所看到的,跨越兩個單詞的實體,如“European Union”和“Werner Zwingmann”,模型為第一個單詞標註了一個B-標籤,為第二個單詞標註了一個I-標籤。 + + + +✏️ **輪到你了!** 使用 POS 或chunking標籤識別同一個句子。 + + + +### 處理數據 + + + +像往常一樣,我們的文本需要轉換為Token ID,然後模型才能理解它們。正如我們在[第六章](/course/chapter6/)所學的那樣。不過在標記任務中,一個很大的區別是我們有pre-tokenized的輸入。幸運的是,tokenizer API可以很容易地處理這個問題;我們只需要用一個特殊的tokenizer。 + +首先,讓我們創建`tokenizer`對象。如前所述,我們將使用 BERT 預訓練模型,因此我們將從下載並緩存關聯的分詞器開始: + +```python +from transformers import AutoTokenizer + +model_checkpoint = "bert-base-cased" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +``` + +你可以更換把 `model_checkpoint` 更換為 [Hub](https://huggingface.co/models),上您喜歡的任何其他型號,或使用您本地保存的預訓練模型和分詞器。唯一的限制是分詞器需要由 🤗 Tokenizers 庫支持,有一個“快速”版本可用。你可以在[這張大表](https://huggingface.co/transformers/#supported-frameworks), 上看到所有帶有快速版本的架構,或者檢查 您可以通過查看它`is_fast` 屬性來檢測正在使用的`tokenizer`對象是否由 🤗 Tokenizers 支持: + +```py +tokenizer.is_fast +``` + +```python out +True +``` + +要對預先標記的輸入進行標記,我們可以像往常一樣使用我們的`tokenizer` 只需添加 `is_split_into_words=True`: + +```py +inputs = tokenizer(raw_datasets["train"][0]["tokens"], is_split_into_words=True) +inputs.tokens() +``` + +```python out +['[CLS]', 'EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'la', '##mb', '.', '[SEP]'] +``` + +正如我們所見,分詞器添加了模型使用的特殊Token(`[CLS]` 在開始和`[SEP]` 最後) 而大多數單詞未被修改。然而,單詞 `lamb`,被分為兩個子單詞 `la` and `##mb`。這導致了輸入和標籤之間的不匹配:標籤列表只有9個元素,而我們的輸入現在有12個token 。計算特殊Token很容易(我們知道它們在開頭和結尾),但我們還需要確保所有標籤與適當的單詞對齊。 +幸運的是,由於我們使用的是快速分詞器,因此我們可以訪問🤗 Tokenizers超能力,這意味著我們可以輕鬆地將每個令牌映射到其相應的單詞(如[Chapter 6](/course/chapter6/3)): + +```py +inputs.word_ids() +``` + +```python out +[None, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, None] +``` + +通過一點點工作,我們可以擴展我們的標籤列表以匹配token 。我們將應用的第一條規則是,特殊token 的標籤為 `-100` 。這是因為默認情況下 `-100` 是一個在我們將使用的損失函數(交叉熵)中被忽略的索引。然後,每個token 都會獲得與其所在單詞的token 相同的標籤,因為它們是同一實體的一部分。對於單詞內部但不在開頭的Token,我們將`B-` 替換為 `I-` (因為token 不以實體開頭): + +```python +def align_labels_with_tokens(labels, word_ids): + new_labels = [] + current_word = None + for word_id in word_ids: + if word_id != current_word: + # Start of a new word! + current_word = word_id + label = -100 if word_id is None else labels[word_id] + new_labels.append(label) + elif word_id is None: + # Special token + new_labels.append(-100) + else: + # Same word as previous token + label = labels[word_id] + # If the label is B-XXX we change it to I-XXX + if label % 2 == 1: + label += 1 + new_labels.append(label) + + return new_labels +``` + +讓我們在我們的第一句話上試一試: + +```py +labels = raw_datasets["train"][0]["ner_tags"] +word_ids = inputs.word_ids() +print(labels) +print(align_labels_with_tokens(labels, word_ids)) +``` + +```python out +[3, 0, 7, 0, 0, 0, 7, 0, 0] +[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100] +``` + +正如我們所看到的,我們的函數為開頭和結尾的兩個特殊標記添加了 `-100` ,併為分成兩個標記的單詞添加了一個新的`0` 。 + + + +✏️ **輪到你了!** 一些研究人員更喜歡每個詞只歸屬一個標籤, 並分配 `-100` 給定詞中的其他子標記。這是為了避免分解成大量子標記的長詞對損失造成嚴重影響。按照此規則更改前一個函數使標籤與輸入id對齊。 + + + +為了預處理我們的整個數據集,我們需要標記所有輸入並在所有標籤上應用 `align_labels_with_tokens()` 。為了利用我們的快速分詞器的速度優勢,最好同時對大量文本進行分詞,因此我們將編寫一個處理示例列表的函數並使用帶 `batched=True` 有選項的 `Dataset.map()`方法 .與我們之前的示例唯一不同的是當分詞器的輸入是文本列表(或者像例子中的單詞列表)時 `word_ids()` 函數需要獲取我們想要單詞的索引的ID,所以我們也添加它: + +```py +def tokenize_and_align_labels(examples): + tokenized_inputs = tokenizer( + examples["tokens"], truncation=True, is_split_into_words=True + ) + all_labels = examples["ner_tags"] + new_labels = [] + for i, labels in enumerate(all_labels): + word_ids = tokenized_inputs.word_ids(i) + new_labels.append(align_labels_with_tokens(labels, word_ids)) + + tokenized_inputs["labels"] = new_labels + return tokenized_inputs +``` + +請注意,我們還沒有填充我們的輸入;我們稍後會在使用數據整理器創建batch時這樣做。 + +我們現在可以一次性將所有預處理應用於數據集的其他部分: + +```py +tokenized_datasets = raw_datasets.map( + tokenize_and_align_labels, + batched=True, + remove_columns=raw_datasets["train"].column_names, +) +``` + +我們已經完成了最難的部分!現在數據已經被預處理了,實際的訓練看起來很像我們[第三章](/course/chapter3)做的. + +{#if fw === 'pt'} + +## 使用 Trainer API 微調模型 + +使用 `Trainer` 的實際代碼會和以前一樣;唯一的變化是數據整理成時批處理的方式和度量計算函數。 + +{:else} + +## 使用 Keras 微調模型 + +使用Keras的實際代碼將與之前非常相似;唯一的變化是將數據整理成批處理的方式和指標計算函數。 + +{/if} + + +### 數據排序 + +我們不能像[第三章](/course/chapter3)那樣只使用一個 `DataCollatorWithPadding `因為這隻會填充輸入(輸入 ID、注意掩碼和標記類型 ID)。在這裡我們的標籤應該以與輸入完全相同的方式填充,以便它們保持長度相同,使用 `-100 ` ,這樣在損失計算中就可以忽略相應的預測。 + +這一切都是由一個 [`DataCollatorForTokenClassification`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorfortokenclassification)完成.它是一個帶有填充的數據整理器它需要 `tokenizer ` 用於預處理輸入: + +{#if fw === 'pt'} + +```py +from transformers import DataCollatorForTokenClassification + +data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer) +``` + +{:else} + +```py +from transformers import DataCollatorForTokenClassification + +data_collator = DataCollatorForTokenClassification( + tokenizer=tokenizer, return_tensors="tf" +) +``` + +{/if} + +為了在幾個樣本上測試這一點,我們可以在訓練集中的示例列表上調用它: + +```py +batch = data_collator([tokenized_datasets["train"][i] for i in range(2)]) +batch["labels"] +``` + +```python out +tensor([[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100], + [-100, 1, 2, -100, -100, -100, -100, -100, -100, -100, -100, -100]]) +``` + +讓我們將其與數據集中第一個和第二個元素的標籤進行比較: + +```py +for i in range(2): + print(tokenized_datasets["train"][i]["labels"]) +``` + +```python out +[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100] +[-100, 1, 2, -100] +``` + +{#if fw === 'pt'} + +正如我們所看到的,第二組標籤的長度已經使用 `-100` 填充到與第一組標籤相同。 + +{:else} + +我們的數據整理器已準備就緒!現在,讓我們用它來製作一個帶有`to_tf_dataset()`方法的`tf.data.Dataset`。 + +```py +tf_train_dataset = tokenized_datasets["train"].to_tf_dataset( + columns=["attention_mask", "input_ids", "labels", "token_type_ids"], + collate_fn=data_collator, + shuffle=True, + batch_size=16, +) + +tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset( + columns=["attention_mask", "input_ids", "labels", "token_type_ids"], + collate_fn=data_collator, + shuffle=False, + batch_size=16, +) +``` + + + Next stop: the model itself. + +{/if} + +{#if fw === 'tf'} + +### 定義模型 + +由於我們正在研究Token分類問題,因此我們將使用 `AutoModelForTokenClassification` 類。定義這個模型時要記住的主要事情是傳遞一些關於我們的標籤數量的信息。執行此操作的最簡單方法是將該數字傳遞給 `num_labels` 參數,但是如果我們想要一個很好的推理小部件,就像我們在本節開頭看到的那樣,最好設置正確的標籤對應關係。 + +它們應該由兩個字典設置, `id2label` 和 `label2id` ,其中包含從 ID 到標籤的映射,反之亦然: + +```py +id2label = {i: label for i, label in enumerate(label_names)} +label2id = {v: k for k, v in id2label.items()} +``` + +現在我們可以將它們傳遞給 `AutoModelForTokenClassification.from_pretrained()` 方法,它們將在模型的配置中設置,然後保存並上傳到Hub: + +```py +from transformers import TFAutoModelForTokenClassification + +model = TFAutoModelForTokenClassification.from_pretrained( + model_checkpoint, + id2label=id2label, + label2id=label2id, +) +``` + +就像我們在[第三章](/course/chapter3),定義我們的 `AutoModelForSequenceClassification` ,創建模型會發出警告,提示一些權重未被使用(來自預訓練頭的權重)和一些其他權重被隨機初始化(來自新Token分類頭的權重),我們將要訓練這個模型。我們將在一分鐘內完成,但首先讓我們仔細檢查我們的模型是否具有正確數量的標籤: + +```python +model.config.num_labels +``` + +```python out +9 +``` + + + +⚠️ 如果您的模型標籤數量錯誤,則在稍後調用 `model.fit()` 時將收到一個模糊的錯誤。調試起來可能很煩人,因此請確保執行此檢查以確認您具有預期的標籤數。 + + + +### 微調模型 + +現在,我們已準備好訓練模型了!不過,我們首先要做兩件事:應該登錄到Hugging Face並定義我們的訓練超參數。如果你在notebook上工作,有一個便利功能可以幫助你做到這一點: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +這將顯示一個小部件,您可以在其中輸入您的 Hugging Face 賬號和密碼。 + +如果您不是在notebook上工作,只需在終端中輸入以下行: + +```bash +huggingface-cli login +``` + +登錄後,我們可以準備編譯模型所需的一切。🤗 Transformers提供了一個方便的`create_optimizer()` 函數,該函數將為您提供一個`AdamW`優化器,其中包含適當的權重衰減和學習速率衰減設置,與內置的`Adam`優化器相似,這兩者都將提高模型的性能: + +```python +from transformers import create_optimizer +import tensorflow as tf + +# Train in mixed-precision float16 +# Comment this line out if you're using a GPU that will not benefit from this +tf.keras.mixed_precision.set_global_policy("mixed_float16") + +# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied +# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset, +# not the original Hugging Face Dataset, so its len() is already num_samples // batch_size. +num_epochs = 3 +num_train_steps = len(tf_train_dataset) * num_epochs + +optimizer, schedule = create_optimizer( + init_lr=2e-5, + num_warmup_steps=0, + num_train_steps=num_train_steps, + weight_decay_rate=0.01, +) +model.compile(optimizer=optimizer) +``` + +還要注意,我們不為`compile()`提供`loss`參數。這是因為模型實際上可以在內部計算損失 - 如果您編譯時沒有損失並在輸入字典中提供標籤(就像我們在數據集中所做的那樣),那麼模型將使用該內部損失進行訓練,這將適用於您選擇的任務和模型類型。 + +接下來,我們定義一個`PushToHubCallback`,以便在訓練期間將模型上傳到 Hub,並使用該回調來擬合模型: + +```python +from transformers.keras_callbacks import PushToHubCallback + +callback = PushToHubCallback(output_dir="bert-finetuned-ner", tokenizer=tokenizer) + +model.fit( + tf_train_dataset, + validation_data=tf_eval_dataset, + callbacks=[callback], + epochs=num_epochs, +) +``` + +您之前已經看過其中的大部分內容:我們設置了一些超參數(例如學習率、要訓練的 epoch 數和權重衰減),然後我們指定 `push_to_hub=True` 表明我們想要保存模型並在每個時期結束時對其進行評估,並且我們想要將我們的結果上傳到模型中心。請注意,可以使用hub_model_id參數指定要推送到的存儲庫的名稱(特別是,必須使用這個參數來推送到一個組織)。例如,當我們將模型推送到[`huggingface-course` organization](https://huggingface.co/huggingface-course), 我們添加了 `hub_model_id=huggingface-course/bert-finetuned-ner` 到 `TrainingArguments` .默認情況下,使用的存儲庫將在您的命名空間中並以您設置的輸出目錄命名,因此在我們的例子中它將是 `sgugger/bert-finetuned-ner` . + + + +💡 如果您正在使用的輸出目錄已經存在,那麼輸出目錄必須是從同一個存儲庫clone下來的。如果不是,您將在聲明 `model.fit()` 時遇到錯誤,並且需要設置一個新名稱。 + + + +請注意,當訓練發生時,每次保存模型時(這裡是每個epooch),它都會在後臺上傳到 Hub。這樣,如有必要,您將能夠在另一臺機器上繼續您的訓練。 + +在此階段,您可以使用模型中心上的推理小組件來測試模型並與朋友共享。您已經成功微調了令牌分類任務的模型 - 恭喜!但是,我們的模型到底有多好呢?我們應該評估一些指標來找出答案。 + +{/if} + + +### 評估指標 + +{#if fw === 'pt'} + +為了讓 `Trainer` 在每個epoch計算一個度量,我們需要定義一個 `compute_metrics()` 函數,該函數接受預測和標籤數組,並返回一個包含度量名稱和值的字典 + +用於評估Token分類預測的傳統框架是 [*seqeval*](https://github.com/chakki-works/seqeval). 要使用此指標,我們首先需要安裝seqeval庫: + +```py +!pip install seqeval +``` + +然後我們可以通過加載它 `load_metric()` 函數就像我們在[第三章](/course/chapter3)做的那樣: + +{:else} + +用於評估Token分類預測的傳統框架是 [*seqeval*](https://github.com/chakki-works/seqeval). 要使用此指標,我們首先需要安裝seqeval庫: + +```py +!pip install seqeval +``` + +然後我們可以通過加載它 `load_metric()` 函數就像我們在[第三章](/course/chapter3)做的那樣: + +{/if} + +```py +from datasets import load_metric + +metric = load_metric("seqeval") +``` + +這個評估方式與標準精度不同:它實際上將標籤列表作為字符串,而不是整數,因此在將預測和標籤傳遞給它之前,我們需要完全解碼它們。讓我們看看它是如何工作的。首先,我們將獲得第一個訓練示例的標籤: + +```py +labels = raw_datasets["train"][0]["ner_tags"] +labels = [label_names[i] for i in labels] +labels +``` + +```python out +['B-ORG', 'O', 'B-MISC', 'O', 'O', 'O', 'B-MISC', 'O', 'O'] +``` + +然後我們可以通過更改索引 2 處的值來為那些創建假的預測: + +```py +predictions = labels.copy() +predictions[2] = "O" +metric.compute(predictions=[predictions], references=[labels]) +``` + +請注意,該指標的輸入是預測列表(不僅僅是一個)和標籤列表。這是輸出: + +```python out +{'MISC': {'precision': 1.0, 'recall': 0.5, 'f1': 0.67, 'number': 2}, + 'ORG': {'precision': 1.0, 'recall': 1.0, 'f1': 1.0, 'number': 1}, + 'overall_precision': 1.0, + 'overall_recall': 0.67, + 'overall_f1': 0.8, + 'overall_accuracy': 0.89} +``` + +{#if fw === 'pt'} + +它返回很多信息!我們獲得每個單獨實體以及整體的準確率、召回率和 F1 分數。對於我們的度量計算,我們將只保留總分,但可以隨意調整 `compute_metrics()` 函數返回您想要查看的所有指標。 + +這`compute_metrics()` 函數首先採用 logits 的 argmax 將它們轉換為預測(像往常一樣,logits 和概率的順序相同,因此我們不需要應用 softmax)。然後我們必須將標籤和預測從整數轉換為字符串。我們刪除標籤為 `-100` 所有值 ,然後將結果傳遞給 `metric.compute()` 方法: + +```py +import numpy as np + + +def compute_metrics(eval_preds): + logits, labels = eval_preds + predictions = np.argmax(logits, axis=-1) + + # Remove ignored index (special tokens) and convert to labels + true_labels = [[label_names[l] for l in label if l != -100] for label in labels] + true_predictions = [ + [label_names[p] for (p, l) in zip(prediction, label) if l != -100] + for prediction, label in zip(predictions, labels) + ] + all_metrics = metric.compute(predictions=true_predictions, references=true_labels) + return { + "precision": all_metrics["overall_precision"], + "recall": all_metrics["overall_recall"], + "f1": all_metrics["overall_f1"], + "accuracy": all_metrics["overall_accuracy"], + } +``` + +現在已經完成了,我們幾乎準備好定義我們的 `Trainer` .我們只需要一個 `model` 微調! + +{:else} + +它返回很多信息!我們獲得每個單獨實體以及整體的準確率、召回率和 F1 分數。對於我們的度量計算,我們將只保留總分,但可以隨意調整 `compute_metrics()` 函數返回您想要查看的所有指標。 + +這`compute_metrics()` 函數首先採用 logits 的 argmax 將它們轉換為預測(像往常一樣,logits 和概率的順序相同,因此我們不需要應用 softmax)。然後我們必須將標籤和預測從整數轉換為字符串。我們刪除標籤為 `-100` 所有值 ,然後將結果傳遞給 `metric.compute()` 方法: + +```py +import numpy as np + +all_predictions = [] +all_labels = [] +for batch in tf_eval_dataset: + logits = model.predict(batch)["logits"] + labels = batch["labels"] + predictions = np.argmax(logits, axis=-1) + for prediction, label in zip(predictions, labels): + for predicted_idx, label_idx in zip(prediction, label): + if label_idx == -100: + continue + all_predictions.append(label_names[predicted_idx]) + all_labels.append(label_names[label_idx]) +metric.compute(predictions=[all_predictions], references=[all_labels]) +``` + + +```python out +{'LOC': {'precision': 0.91, 'recall': 0.92, 'f1': 0.91, 'number': 1668}, + 'MISC': {'precision': 0.70, 'recall': 0.79, 'f1': 0.74, 'number': 702}, + 'ORG': {'precision': 0.85, 'recall': 0.90, 'f1': 0.88, 'number': 1661}, + 'PER': {'precision': 0.95, 'recall': 0.95, 'f1': 0.95, 'number': 1617}, + 'overall_precision': 0.87, + 'overall_recall': 0.91, + 'overall_f1': 0.89, + 'overall_accuracy': 0.97} +``` + +與我們的模型相比,您的模型做得如何?如果您獲得類似的數字,那麼您的訓練就是成功的! + +{/if} + +{#if fw === 'pt'} + +### 定義模型 + +由於我們正在研究Token分類問題,因此我們將使用 `AutoModelForTokenClassification` 類。定義這個模型時要記住的主要事情是傳遞一些關於我們的標籤數量的信息。執行此操作的最簡單方法是將該數字傳遞給 `num_labels` 參數,但是如果我們想要一個很好的推理小部件,就像我們在本節開頭看到的那樣,最好設置正確的標籤對應關係。 + +它們應該由兩個字典設置, `id2label` 和 `label2id` ,其中包含從 ID 到標籤的映射,反之亦然: + +```py +id2label = {i: label for i, label in enumerate(label_names)} +label2id = {v: k for k, v in id2label.items()} +``` + +現在我們可以將它們傳遞給 `AutoModelForTokenClassification.from_pretrained()` 方法,它們將在模型的配置中設置,然後保存並上傳到Hub: + +```py +from transformers import AutoModelForTokenClassification + +model = AutoModelForTokenClassification.from_pretrained( + model_checkpoint, + id2label=id2label, + label2id=label2id, +) +``` + +就像我們在[第三章](/course/chapter3),定義我們的 `AutoModelForSequenceClassification` ,創建模型會發出警告,提示一些權重未被使用(來自預訓練頭的權重)和一些其他權重被隨機初始化(來自新Token分類頭的權重),我們將要訓練這個模型。我們將在一分鐘內完成,但首先讓我們仔細檢查我們的模型是否具有正確數量的標籤: + +```python +model.config.num_labels +``` + +```python out +9 +``` + + + +⚠️ 如果模型的標籤數量錯誤,稍後調用Trainer.train()方法時會出現一個模糊的錯誤(類似於“CUDA error: device-side assert triggered”)。這是用戶報告此類錯誤的第一個原因,因此請確保進行這樣的檢查以確認您擁有預期數量的標籤。 + + + +### 微調模型 + +我們現在準備好訓練我們的模型了!在定義我們的 `Trainer`之前,我們只需要做最後兩件事:登錄 Hugging Face 並定義我們的訓練參數。如果您在notebook上工作,有一個方便的功能可以幫助您: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` +這將顯示一個小部件,您可以在其中輸入您的 Hugging Face 賬號和密碼。如果您不是在notebook上工作,只需在終端中輸入以下行: + +```bash +huggingface-cli login +``` + +Once this is done, we can define our `TrainingArguments`: + +```python +from transformers import TrainingArguments + +args = TrainingArguments( + "bert-finetuned-ner", + evaluation_strategy="epoch", + save_strategy="epoch", + learning_rate=2e-5, + num_train_epochs=3, + weight_decay=0.01, + push_to_hub=True, +) +``` + +您之前已經看過其中的大部分內容:我們設置了一些超參數(例如學習率、要訓練的 epoch 數和權重衰減),然後我們指定 `push_to_hub=True` 表明我們想要保存模型並在每個時期結束時對其進行評估,並且我們想要將我們的結果上傳到模型中心。請注意,可以使用hub_model_id參數指定要推送到的存儲庫的名稱(特別是,必須使用這個參數來推送到一個組織)。例如,當我們將模型推送到[`huggingface-course` organization](https://huggingface.co/huggingface-course), 我們添加了 `hub_model_id=huggingface-course/bert-finetuned-ner` 到 `TrainingArguments` 。默認情況下,使用的存儲庫將在您的命名空間中並以您設置的輸出目錄命名,因此在我們的例子中它將是 `sgugger/bert-finetuned-ner`。 + + + +💡 如果您正在使用的輸出目錄已經存在,那麼輸出目錄必須是從同一個存儲庫clone下來的。如果不是,您將在聲明 `Trainer` 時遇到錯誤,並且需要設置一個新名稱。 + + + +最後,我們只是將所有內容傳遞給 `Trainer` 並啟動訓練: + +```python +from transformers import Trainer + +trainer = Trainer( + model=model, + args=args, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["validation"], + data_collator=data_collator, + compute_metrics=compute_metrics, + tokenizer=tokenizer, +) +trainer.train() +``` + +請注意,當訓練發生時,每次保存模型時(這裡是每個epooch),它都會在後臺上傳到 Hub。這樣,如有必要,您將能夠在另一臺機器上繼續您的訓練。 + +訓練完成後,我們使用 `push_to_hub()` 確保我們上傳模型的最新版本 + +```py +trainer.push_to_hub(commit_message="Training complete") +``` + +This command returns the URL of the commit it just did, if you want to inspect it: + +```python out +'https://huggingface.co/sgugger/bert-finetuned-ner/commit/26ab21e5b1568f9afeccdaed2d8715f571d786ed' +``` + +這 `Trainer` 還創建了一張包含所有評估結果的模型卡並上傳。在此階段,您可以使用模型中心上的推理小部件來測試您的模型並與您的朋友分享。您已成功在Token分類任務上微調模型 - 恭喜! + +如果您想更深入地瞭解訓練循環,我們現在將向您展示如何使用 🤗 Accelerate 做同樣的事情。 + +## 自定義訓練循環 + +現在讓我們看一下完整的訓練循環,這樣您可以輕鬆定義所需的部分。它看起來很像我們在[第三章](/course/chapter3/4), 所做的,對評估進行了一些更改。 + +### 做好訓練前的準備 +首先我們需要為我們的數據集構建 `DataLoader` 。我們將重用我們的 `data_collator` 作為一個 `collate_fn` 並打亂訓練集,但不打亂驗證集: + +```py +from torch.utils.data import DataLoader + +train_dataloader = DataLoader( + tokenized_datasets["train"], + shuffle=True, + collate_fn=data_collator, + batch_size=8, +) +eval_dataloader = DataLoader( + tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8 +) +``` + +接下來,我們重新實例化我們的模型,以確保我們不會從之前的訓練繼續訓練,而是再次從 BERT 預訓練模型開始: + +```py +model = AutoModelForTokenClassification.from_pretrained( + model_checkpoint, + id2label=id2label, + label2id=label2id, +) +``` + +然後我們將需要一個優化器。我們將使用經典 `AdamW` ,這就像 `Adam` ,但在應用權重衰減的方式上進行了改進: +```py +from torch.optim import AdamW + +optimizer = AdamW(model.parameters(), lr=2e-5) +``` + +Once we have all those objects, we can send them to the `accelerator.prepare()` method: + +```py +from accelerate import Accelerator + +accelerator = Accelerator() +model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare( + model, optimizer, train_dataloader, eval_dataloader +) +``` + + + +🚨 如果您在 TPU 上進行訓練,則需要將以上單元格中的所有代碼移動到專用的訓練函數中。有關詳細信息,請參閱 [第3章](/course/chapter3)。 + + + +現在我們已經發送了我們的 `train_dataloader` 到 `accelerator.prepare()` ,我們可以使用它的長度來計算訓練步驟的數量。請記住,我們應該始終在準備好dataloader後執行此操作,因為該方法會改變其長度。我們使用經典線性學習率調度: + +```py +from transformers import get_scheduler + +num_train_epochs = 3 +num_update_steps_per_epoch = len(train_dataloader) +num_training_steps = num_train_epochs * num_update_steps_per_epoch + +lr_scheduler = get_scheduler( + "linear", + optimizer=optimizer, + num_warmup_steps=0, + num_training_steps=num_training_steps, +) +``` + +最後,要將我們的模型推送到 Hub,我們需要創建一個 `Repository` 工作文件夾中的對象。如果您尚未登錄,請先登錄 Hugging Face。我們將從我們想要為模型提供的模型 ID 中確定存儲庫名稱(您可以自由地用自己的選擇替換 `repo_name` ;它只需要包含您的用戶名,可以使用`get_full_repo_name()`函數的查看目前的repo_name): + +```py +from huggingface_hub import Repository, get_full_repo_name + +model_name = "bert-finetuned-ner-accelerate" +repo_name = get_full_repo_name(model_name) +repo_name +``` + +```python out +'sgugger/bert-finetuned-ner-accelerate' +``` + +Then we can clone that repository in a local folder. If it already exists, this local folder should be an existing clone of the repository we are working with: + +```py +output_dir = "bert-finetuned-ner-accelerate" +repo = Repository(output_dir, clone_from=repo_name) +``` + +We can now upload anything we save in `output_dir` by calling the `repo.push_to_hub()` method. This will help us upload the intermediate models at the end of each epoch. + +### Training loop + +### 訓練循環 +我們現在準備編寫完整的訓練循環。為了簡化它的評估部分,我們定義了這個 `postprocess()` 接受預測和標籤並將它們轉換為字符串列表的函數,也就是 `metric`對象需要的輸入格式: + +```py +def postprocess(predictions, labels): + predictions = predictions.detach().cpu().clone().numpy() + labels = labels.detach().cpu().clone().numpy() + + # Remove ignored index (special tokens) and convert to labels + true_labels = [[label_names[l] for l in label if l != -100] for label in labels] + true_predictions = [ + [label_names[p] for (p, l) in zip(prediction, label) if l != -100] + for prediction, label in zip(predictions, labels) + ] + return true_labels, true_predictions +``` + +然後我們可以編寫訓練循環。在定義一個進度條來跟蹤訓練的進行後,循環分為三個部分: + +- 訓練本身,這是對`train_dataloader`的經典迭代,向前傳遞模型,然後反向傳遞和優化參數 +- 評估,在獲得我們模型的輸出後:因為兩個進程可能將輸入和標籤填充成不同的形狀,在調用`gather()`方法前我們需要使用`accelerator.pad_across_processes()`來讓預測和標籤形狀相同。如果我們不這樣做,評估要麼出錯,要麼永遠不會得到結果。然後,我們將結果發送給`metric.add_batch()`,並在計算循環結束後調用`metric.compute()`。 +- 保存和上傳,首先保存模型和標記器,然後調用`repo.push_to_hub()`。注意,我們使用參數`blocking=False`告訴🤗 hub 庫用在異步進程中推送。這樣,訓練將正常繼續,並且該(長)指令將在後臺執行。 + +這是訓練循環的完整代碼: + +```py +from tqdm.auto import tqdm +import torch + +progress_bar = tqdm(range(num_training_steps)) + +for epoch in range(num_train_epochs): + # Training + model.train() + for batch in train_dataloader: + outputs = model(**batch) + loss = outputs.loss + accelerator.backward(loss) + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) + + # Evaluation + model.eval() + for batch in eval_dataloader: + with torch.no_grad(): + outputs = model(**batch) + + predictions = outputs.logits.argmax(dim=-1) + labels = batch["labels"] + + # Necessary to pad predictions and labels for being gathered + predictions = accelerator.pad_across_processes(predictions, dim=1, pad_index=-100) + labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100) + + predictions_gathered = accelerator.gather(predictions) + labels_gathered = accelerator.gather(labels) + + true_predictions, true_labels = postprocess(predictions_gathered, labels_gathered) + metric.add_batch(predictions=true_predictions, references=true_labels) + + results = metric.compute() + print( + f"epoch {epoch}:", + { + key: results[f"overall_{key}"] + for key in ["precision", "recall", "f1", "accuracy"] + }, + ) + + # Save and upload + accelerator.wait_for_everyone() + unwrapped_model = accelerator.unwrap_model(model) + unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) + if accelerator.is_main_process: + tokenizer.save_pretrained(output_dir) + repo.push_to_hub( + commit_message=f"Training in progress epoch {epoch}", blocking=False + ) +``` + +果這是您第一次看到用 🤗 Accelerate 保存的模型,讓我們花點時間檢查一下它附帶的三行代碼: + +```py +accelerator.wait_for_everyone() +unwrapped_model = accelerator.unwrap_model(model) +unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) +``` + +第一行是不言自明的:它告訴所有進程等到都處於那個階段再繼續(阻塞)。這是為了確保在保存之前,我們在每個過程中都有相同的模型。然後獲取`unwrapped_model`,它是我們定義的基本模型。 +`accelerator.prepare()`方法將模型更改為在分佈式訓練中工作,所以它不再有`save_pretraining()`方法;`accelerator.unwrap_model()`方法將撤銷該步驟。最後,我們調用`save_pretraining()`,但告訴該方法使用`accelerator.save()`而不是`torch.save()`。 + +當完成之後,你應該有一個模型,它產生的結果與`Trainer`的結果非常相似。你可以在[hugs face-course/bert-fine - tuning -ner-accelerate](https://huggingface.co/huggingface-course/bert-finetuned-ner-accelerate)中查看我們使用這個代碼訓練的模型。如果你想測試訓練循環的任何調整,你可以直接通過編輯上面顯示的代碼來實現它們! + +{/if} + +## 使用微調模型 + +我們已經向您展示瞭如何使用我們在模型中心微調的模型和推理小部件。在本地使用它 `pipeline` ,您只需要指定正確的模型標識符: + +```py +from transformers import pipeline + +# Replace this with your own checkpoint +model_checkpoint = "huggingface-course/bert-finetuned-ner" +token_classifier = pipeline( + "token-classification", model=model_checkpoint, aggregation_strategy="simple" +) +token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.") +``` + +```python out +[{'entity_group': 'PER', 'score': 0.9988506, 'word': 'Sylvain', 'start': 11, 'end': 18}, + {'entity_group': 'ORG', 'score': 0.9647625, 'word': 'Hugging Face', 'start': 33, 'end': 45}, + {'entity_group': 'LOC', 'score': 0.9986118, 'word': 'Brooklyn', 'start': 49, 'end': 57}] +``` + +太棒了!我們的模型與此管道的默認模型一樣有效! diff --git a/chapters/zh-TW/chapter7/3.mdx b/chapters/zh-TW/chapter7/3.mdx new file mode 100644 index 000000000..7f6e95008 --- /dev/null +++ b/chapters/zh-TW/chapter7/3.mdx @@ -0,0 +1,1045 @@ + + +# 微調掩碼語言模型 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +對於許多涉及 Transformer 模型的 NLP 程序, 你可以簡單地從 Hugging Face Hub 中獲取一個預訓練的模型, 然後直接在你的數據上對其進行微調, 以完成手頭的任務。只要用於預訓練的語料庫與用於微調的語料庫沒有太大區別, 遷移學習通常會產生很好的結果。 + +但是, 在某些情況下, 你需要先微調數據上的語言模型, 然後再訓練特定於任務的head。例如, 如果您的數據集包含法律合同或科學文章, 像 BERT 這樣的普通 Transformer 模型通常會將您語料庫中的特定領域詞視為稀有標記, 結果性能可能不盡如人意。通過在域內數據上微調語言模型, 你可以提高許多下游任務的性能, 這意味著您通常只需執行一次此步驟! + +這種在域內數據上微調預訓練語言模型的過程通常稱為 _領域適應_。 它於 2018 年由 [ULMFiT](https://arxiv.org/abs/1801.06146)推廣, 這是使遷移學習真正適用於 NLP 的首批神經架構之一 (基於 LSTM)。 下圖顯示了使用 ULMFiT 進行域自適應的示例; 在本節中, 我們將做類似的事情, 但使用的是 Transformer 而不是 LSTM! + +
+ULMFiT. + +
+ +在本節結束時, 你將在Hub上擁有一個[掩碼語言模型(masked language model)](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.), 該模型可以自動完成句子, 如下所示: + + + + +讓我們開始吧! + + + + + +🙋 如果您對“掩碼語言建模”和“預訓練模型”這兩個術語感到陌生, 請查看[第一章](/course/chapter1), 我們在其中解釋了所有這些核心概念, 並附有視頻! + + + +## 選擇用於掩碼語言建模的預訓練模型 + +首先, 讓我們為掩碼語言建模選擇一個合適的預訓練模型。如以下屏幕截圖所示, 你可以通過在[Hugging Face Hub](https://huggingface.co/models?pipeline_tag=fill-mask&sort=downloads)上應用"Fill-Mask"過濾器找到: + +
+Hub models. +
+ +儘管 BERT 和 RoBERTa 系列模型的下載量最大, 但我們將使用名為 [DistilBERT](https://huggingface.co/distilbert-base-uncased)的模型。 +可以更快地訓練, 而下游性能幾乎沒有損失。這個模型使用一種稱為[_知識蒸餾_](https://en.wikipedia.org/wiki/Knowledge_distillation)的特殊技術進行訓練, 其中使用像 BERT 這樣的大型“教師模型”來指導參數少得多的“學生模型”的訓練。在本節中對知識蒸餾細節的解釋會使我們離題太遠, 但如果你有興趣, 可以閱讀所有相關內容 [_Natural Language Processing with Transformers_](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/) (俗稱Transformers教科書)。 + +{#if fw === 'pt'} + +讓我們繼續, 使用 `AutoModelForMaskedLM` 類下載 DistilBERT: + +```python +from transformers import AutoModelForMaskedLM + +model_checkpoint = "distilbert-base-uncased" +model = AutoModelForMaskedLM.from_pretrained(model_checkpoint) +``` + +我們可以通過調用 `num_parameters()` 方法查看模型有多少參數: + +```python +distilbert_num_parameters = model.num_parameters() / 1_000_000 +print(f"'>>> DistilBERT number of parameters: {round(distilbert_num_parameters)}M'") +print(f"'>>> BERT number of parameters: 110M'") +``` + +```python out +'>>> DistilBERT number of parameters: 67M' +'>>> BERT number of parameters: 110M' +``` + +{:else} + +讓我們繼續, 使用 `AutoModelForMaskedLM` 類下載 DistilBERT: + +```python +from transformers import TFAutoModelForMaskedLM + +model_checkpoint = "distilbert-base-uncased" +model = TFAutoModelForMaskedLM.from_pretrained(model_checkpoint) +``` + +我們可以通過調用 `summary()` 方法查看模型有多少參數: + +```python +model(model.dummy_inputs) # Build the model +model.summary() +``` + +```python out +Model: "tf_distil_bert_for_masked_lm" +_________________________________________________________________ +Layer (type) Output Shape Param # +================================================================= +distilbert (TFDistilBertMain multiple 66362880 +_________________________________________________________________ +vocab_transform (Dense) multiple 590592 +_________________________________________________________________ +vocab_layer_norm (LayerNorma multiple 1536 +_________________________________________________________________ +vocab_projector (TFDistilBer multiple 23866170 +================================================================= +Total params: 66,985,530 +Trainable params: 66,985,530 +Non-trainable params: 0 +_________________________________________________________________ +``` + +{/if} + +DistilBERT 大約有 6700 萬個參數, 大約比 BERT 基本模型小兩倍, 這大致意味著訓練的速度提高了兩倍 -- 非常棒! 現在讓我們看看這個模型預測什麼樣的標記最有可能完成一小部分文本: + +```python +text = "This is a great [MASK]." +``` + +作為人類, 我們可以想象 `[MASK]` 標記有很多可能性, 例如 "day"、 "ride" 或者 "painting"。對於預訓練模型, 預測取決於模型所訓練的語料庫, 因為它會學習獲取數據中存在的統計模式。與 BERT 一樣, DistilBERT 在[English Wikipedia](https://huggingface.co/datasets/wikipedia) 和 [BookCorpus](https://huggingface.co/datasets/bookcorpus) 數據集上進行預訓練, 所以我們期望對 `[MASK]` 的預測能夠反映這些領域。為了預測掩碼, 我們需要 DistilBERT 的標記器來生成模型的輸入, 所以讓我們也從 Hub 下載它: + +```python +from transformers import AutoTokenizer + +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +``` + +使用標記器和模型, 我們現在可以將我們的文本示例傳遞給模型, 提取 logits, 並打印出前 5 個候選: + +{#if fw === 'pt'} + +```python +import torch + +inputs = tokenizer(text, return_tensors="pt") +token_logits = model(**inputs).logits +# Find the location of [MASK] and extract its logits +mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1] +mask_token_logits = token_logits[0, mask_token_index, :] +# Pick the [MASK] candidates with the highest logits +top_5_tokens = torch.topk(mask_token_logits, 5, dim=1).indices[0].tolist() + +for token in top_5_tokens: + print(f"'>>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}'") +``` + +{:else} + +```python +import numpy as np +import tensorflow as tf + +inputs = tokenizer(text, return_tensors="np") +token_logits = model(**inputs).logits +# Find the location of [MASK] and extract its logits +mask_token_index = np.argwhere(inputs["input_ids"] == tokenizer.mask_token_id)[0, 1] +mask_token_logits = token_logits[0, mask_token_index, :] +# Pick the [MASK] candidates with the highest logits +# We negate the array before argsort to get the largest, not the smallest, logits +top_5_tokens = np.argsort(-mask_token_logits)[:5].tolist() + +for token in top_5_tokens: + print(f">>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}") +``` + +{/if} + +```python out +'>>> This is a great deal.' +'>>> This is a great success.' +'>>> This is a great adventure.' +'>>> This is a great idea.' +'>>> This is a great feat.' +``` + +我們可以從輸出中看到模型的預測是指日常用語, 鑑於英語維基百科的基礎, 這也許並不奇怪。讓我們看看我們如何將這個領域改變為更小眾的東西 -- 高度兩極分化的電影評論! + + +## 數據集 + +為了展示域適配, 我們將使用著名的[大型電影評論數據集(Large Movie Review Dataset)](https://huggingface.co/datasets/imdb) (或者簡稱為IMDb), 這是一個電影評論語料庫, 通常用於對情感分析模型進行基準測試。通過在這個語料庫上對 DistilBERT 進行微調, 我們預計語言模型將根據維基百科的事實數據調整其詞彙表, 這些數據已經預先訓練到電影評論中更主觀的元素。我們可以使用🤗 Datasets中的`load_dataset()`函數從Hugging Face 中獲取數據: + +```python +from datasets import load_dataset + +imdb_dataset = load_dataset("imdb") +imdb_dataset +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['text', 'label'], + num_rows: 25000 + }) + test: Dataset({ + features: ['text', 'label'], + num_rows: 25000 + }) + unsupervised: Dataset({ + features: ['text', 'label'], + num_rows: 50000 + }) +}) +``` + +我們可以看到 `train` 和 `test` 每個拆分包含 25,000 條評論, 而有一個未標記的拆分稱為 `unsupervised` 包含 50,000 條評論。讓我們看一些示例, 以瞭解我們正在處理的文本類型。正如我們在本課程的前幾章中所做的那樣, 我們將鏈接 `Dataset.shuffle()` 和 `Dataset.select()` 函數創建隨機樣本: + +```python +sample = imdb_dataset["train"].shuffle(seed=42).select(range(3)) + +for row in sample: + print(f"\n'>>> Review: {row['text']}'") + print(f"'>>> Label: {row['label']}'") +``` + +```python out + +'>>> Review: This is your typical Priyadarshan movie--a bunch of loony characters out on some silly mission. His signature climax has the entire cast of the film coming together and fighting each other in some crazy moshpit over hidden money. Whether it is a winning lottery ticket in Malamaal Weekly, black money in Hera Pheri, "kodokoo" in Phir Hera Pheri, etc., etc., the director is becoming ridiculously predictable. Don\'t get me wrong; as clichéd and preposterous his movies may be, I usually end up enjoying the comedy. However, in most his previous movies there has actually been some good humor, (Hungama and Hera Pheri being noteworthy ones). Now, the hilarity of his films is fading as he is using the same formula over and over again.

Songs are good. Tanushree Datta looks awesome. Rajpal Yadav is irritating, and Tusshar is not a whole lot better. Kunal Khemu is OK, and Sharman Joshi is the best.' +'>>> Label: 0' + +'>>> Review: Okay, the story makes no sense, the characters lack any dimensionally, the best dialogue is ad-libs about the low quality of movie, the cinematography is dismal, and only editing saves a bit of the muddle, but Sam" Peckinpah directed the film. Somehow, his direction is not enough. For those who appreciate Peckinpah and his great work, this movie is a disappointment. Even a great cast cannot redeem the time the viewer wastes with this minimal effort.

The proper response to the movie is the contempt that the director San Peckinpah, James Caan, Robert Duvall, Burt Young, Bo Hopkins, Arthur Hill, and even Gig Young bring to their work. Watch the great Peckinpah films. Skip this mess.' +'>>> Label: 0' + +'>>> Review: I saw this movie at the theaters when I was about 6 or 7 years old. I loved it then, and have recently come to own a VHS version.

My 4 and 6 year old children love this movie and have been asking again and again to watch it.

I have enjoyed watching it again too. Though I have to admit it is not as good on a little TV.

I do not have older children so I do not know what they would think of it.

The songs are very cute. My daughter keeps singing them over and over.

Hope this helps.' +'>>> Label: 1' +``` + +是的, 這些肯定是電影評論, 如果你年齡足夠,你甚至可能會理解上次評論中關於擁有 VHS 版本的評論😜! 雖然我們不需要語言建模的標籤, 但我們已經可以看到 `0` 表示負面評論, 而 `1` 對應正面。 + + + +✏️ **試試看!** 創建 `無監督` 拆分的隨機樣本, 並驗證標籤既不是 `0` 也不是 `1`。在此過程中, 你還可以檢查 `train` 和 `test` 拆分中的標籤是否確實為 `0` 或 `1` -- 這是每個 NLP 從業者在新項目開始時都應該執行的有用的健全性檢查! + + + +現在我們已經快速瀏覽了數據, 讓我們深入研究為掩碼語言建模做準備。正如我們將看到的, 與我們在[第三章](/course/chapter3)中看到的序列分類任務相比, 還需要採取一些額外的步驟。讓我們繼續! + +## 預處理數據 + + + +對於自迴歸和掩碼語言建模, 一個常見的預處理步驟是連接所有示例, 然後將整個語料庫拆分為相同大小的塊。 這與我們通常的方法完全不同, 我們只是簡單地標記單個示例。為什麼要將所有內容連接在一起? 原因是單個示例如果太長可能會被截斷, 這將導致丟失可能對語言建模任務有用的信息! + +因此, 我們將像往常一樣首先標記我們的語料庫, 但是 _沒有_ 在我們的標記器中設置 `truncation=True` 選項。 我們還將獲取可用的單詞 ID ((如果我們使用快速標記器, 它們是可用的, 如 [第六章](/course/chapter6/3)中所述), 因為我們稍後將需要它們來進行全字屏蔽。我們將把它包裝在一個簡單的函數中, 當我們在做的時候, 我們將刪除 `text` 和 `label` 列, 因為我們不再需要它們: + +```python +def tokenize_function(examples): + result = tokenizer(examples["text"]) + if tokenizer.is_fast: + result["word_ids"] = [result.word_ids(i) for i in range(len(result["input_ids"]))] + return result + + +# Use batched=True to activate fast multithreading! +tokenized_datasets = imdb_dataset.map( + tokenize_function, batched=True, remove_columns=["text", "label"] +) +tokenized_datasets +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['attention_mask', 'input_ids', 'word_ids'], + num_rows: 25000 + }) + test: Dataset({ + features: ['attention_mask', 'input_ids', 'word_ids'], + num_rows: 25000 + }) + unsupervised: Dataset({ + features: ['attention_mask', 'input_ids', 'word_ids'], + num_rows: 50000 + }) +}) +``` + +由於 DistilBERT 是一個類似 BERT 的模型, 我們可以看到編碼文本由我們在其他章節中看到的 `input_ids` 和 `attention_mask` 組成, 以及我們添加的 `word_ids`。 + +現在我們已經標記了我們的電影評論, 下一步是將它們組合在一起並將結果分成塊。 但是這些塊應該有多大? 這最終將取決於你可用的 GPU 內存量, 但一個好的起點是查看模型的最大上下文大小是多少。這可以通過檢查標記器的 `model_max_length` 屬性來判斷: + +```python +tokenizer.model_max_length +``` + +```python out +512 +``` + +該值來自於與檢查點相關聯的 *tokenizer_config.json* 文件; 在這種情況下, 我們可以看到上下文大小是 512 個標記, 就像 BERT 一樣。 + + + +✏️ **試試看!** 一些 Transformer 模型, 例如 [BigBird](https://huggingface.co/google/bigbird-roberta-base) 和 [Longformer](hf.co/allenai/longformer-base-4096), 它們具有比BERT和其他早期Transformer模型更長的上下文長度。為這些檢查點之一實例化標記器, 並驗證 `model_max_length` 是否與模型卡上引用的內容一致。 + + + +因此, 以便在像Google Colab 那樣的 GPU 上運行我們的實驗, 我們將選擇可以放入內存的更小一些的東西: + +```python +chunk_size = 128 +``` + + + +請注意, 在實際場景中使用較小的塊大小可能是有害的, 因此你應該使用與將應用模型的用例相對應的大小。 + + + +有趣的來了。為了展示串聯是如何工作的, 讓我們從我們的標記化訓練集中取一些評論並打印出每個評論的標記數量: + +```python +# Slicing produces a list of lists for each feature +tokenized_samples = tokenized_datasets["train"][:3] + +for idx, sample in enumerate(tokenized_samples["input_ids"]): + print(f"'>>> Review {idx} length: {len(sample)}'") +``` + +```python out +'>>> Review 0 length: 200' +'>>> Review 1 length: 559' +'>>> Review 2 length: 192' +``` + +然後我們可以用一個簡單的字典理解來連接所有例子, 如下所示: + +```python +concatenated_examples = { + k: sum(tokenized_samples[k], []) for k in tokenized_samples.keys() +} +total_length = len(concatenated_examples["input_ids"]) +print(f"'>>> Concatenated reviews length: {total_length}'") +``` + +```python out +'>>> Concatenated reviews length: 951' +``` + +很棒, 總長度檢查出來了 -- 現在, 讓我們將連接的評論拆分為大小為 `block_size` 的塊。為此, 我們迭代了 `concatenated_examples` 中的特徵, 並使用列表理解來創建每個特徵的切片。結果是每個特徵的塊字典: + +```python +chunks = { + k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)] + for k, t in concatenated_examples.items() +} + +for chunk in chunks["input_ids"]: + print(f"'>>> Chunk length: {len(chunk)}'") +``` + +```python out +'>>> Chunk length: 128' +'>>> Chunk length: 128' +'>>> Chunk length: 128' +'>>> Chunk length: 128' +'>>> Chunk length: 128' +'>>> Chunk length: 128' +'>>> Chunk length: 128' +'>>> Chunk length: 55' +``` + +正如你在這個例子中看到的, 最後一個塊通常會小於最大塊大小。有兩種主要的策略來處理這個問題: + +* 如果最後一個塊小於 `chunk_size`, 請刪除它。 +* 填充最後一個塊, 直到其長度等於 `chunk_size`。 + +我們將在這裡採用第一種方法, 因此讓我們將上述所有邏輯包裝在一個函數中, 我們可以將其應用於我們的標記化數據集: + +```python +def group_texts(examples): + # Concatenate all texts + concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()} + # Compute length of concatenated texts + total_length = len(concatenated_examples[list(examples.keys())[0]]) + # We drop the last chunk if it's smaller than chunk_size + total_length = (total_length // chunk_size) * chunk_size + # Split by chunks of max_len + result = { + k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)] + for k, t in concatenated_examples.items() + } + # Create a new labels column + result["labels"] = result["input_ids"].copy() + return result +``` + +注意, 在 `group_texts()` 的最後一步中, 我們創建了一個新的 `labels` 列, 它是 `input_ids` 列的副本。我們很快就會看到, 這是因為在掩碼語言建模中, 目標是預測輸入批次中隨機掩碼的標記, 並通過創建一個 `labels` 列, 們為我們的語言模型提供了基礎事實以供學習。 + +現在, 讓我們使用我們可信賴的 `Dataset.map()` 函數將 `group_texts()` 應用到我們的標記化數據集: + +```python +lm_datasets = tokenized_datasets.map(group_texts, batched=True) +lm_datasets +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['attention_mask', 'input_ids', 'labels', 'word_ids'], + num_rows: 61289 + }) + test: Dataset({ + features: ['attention_mask', 'input_ids', 'labels', 'word_ids'], + num_rows: 59905 + }) + unsupervised: Dataset({ + features: ['attention_mask', 'input_ids', 'labels', 'word_ids'], + num_rows: 122963 + }) +}) +``` + +你可以看到, 對文本進行分組, 然後對文本進行分塊, 產生的示例比我們最初的 25,000 用於 `train`和 `test` 拆分的示例多得多。那是因為我們現在有了涉及 _連續標記_ 的示例, 這些示例跨越了原始語料庫中的多個示例。你可以通過在其中一個塊中查找特殊的 `[SEP]` 和 `[CLS]` 標記來明確的看到這一點: + +```python +tokenizer.decode(lm_datasets["train"][1]["input_ids"]) +``` + +```python out +".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless" +``` + +在此示例中, 你可以看到兩篇重疊的電影評論, 一篇關於高中電影, 另一篇關於無家可歸。 讓我們也看看掩碼語言建模的標籤是什麼樣的: + +```python out +tokenizer.decode(lm_datasets["train"][1]["labels"]) +``` + +```python out +".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless" +``` + +正如前面的 `group_texts()` 函數所期望的那樣, 這看起來與解碼後的 `input_ids` 相同 -- 但是我們的模型怎麼可能學到任何東西呢? 我們錯過了一個關鍵步驟: 在輸入中的隨機位置插入 `[MASK]` 標記! 讓我們看看如何使用特殊的數據整理器在微調期間即時執行此操作。 + +## 使用 `Trainer` API 微調DistilBERT + +微調屏蔽語言模型幾乎與微調序列分類模型相同, 就像我們在 [第三章](/course/chapter3)所作的那樣。 唯一的區別是我們需要一個特殊的數據整理器, 它可以隨機屏蔽每批文本中的一些標記。幸運的是, 🤗 Transformers 為這項任務準備了專用的 `DataCollatorForLanguageModeling` 。我們只需要將它轉遞給標記器和一個 `mlm_probability` 參數, 該參數指定要屏蔽的標記的分數。我們將選擇 15%, 這是 BERT 使用的數量也是文獻中的常見選擇: + +```python +from transformers import DataCollatorForLanguageModeling + +data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=0.15) +``` + +要了解隨機掩碼的工作原理, 讓我們向數據整理器提供一些示例。由於它需要一個 `dict` 的列表, 其中每個 `dict` 表示單個連續文本塊, 我們首先迭代數據集, 然後再將批次提供給整理器。我們刪除了這個數據整理器的 `"word_ids"` 鍵, 因為它不需要它: + +```python +samples = [lm_datasets["train"][i] for i in range(2)] +for sample in samples: + _ = sample.pop("word_ids") + +for chunk in data_collator(samples)["input_ids"]: + print(f"\n'>>> {tokenizer.decode(chunk)}'") +``` + +```python output +'>>> [CLS] bromwell [MASK] is a cartoon comedy. it ran at the same [MASK] as some other [MASK] about school life, [MASK] as " teachers ". [MASK] [MASK] [MASK] in the teaching [MASK] lead [MASK] to believe that bromwell high\'[MASK] satire is much closer to reality than is " teachers ". the scramble [MASK] [MASK] financially, the [MASK]ful students whogn [MASK] right through [MASK] pathetic teachers\'pomp, the pettiness of the whole situation, distinction remind me of the schools i knew and their students. when i saw [MASK] episode in [MASK] a student repeatedly tried to burn down the school, [MASK] immediately recalled. [MASK]...' + +'>>> .... at.. [MASK]... [MASK]... high. a classic line plucked inspector : i\'[MASK] here to [MASK] one of your [MASK]. student : welcome to bromwell [MASK]. i expect that many adults of my age think that [MASK]mwell [MASK] is [MASK] fetched. what a pity that it isn\'t! [SEP] [CLS] [MASK]ness ( or [MASK]lessness as george 宇in stated )公 been an issue for years but never [MASK] plan to help those on the street that were once considered human [MASK] did everything from going to school, [MASK], [MASK] vote for the matter. most people think [MASK] the homeless' +``` + +很棒, 成功了! 我們可以看到, `[MASK]` 標記已隨機插入我們文本中的不同位置。 這些將是我們的模型在訓練期間必須預測的標記 -- 數據整理器的美妙之處在於, 它將隨機化每個批次的 `[MASK]` 插入! + + + +✏️ **試試看!** 多次運行上面的代碼片段, 看看隨機屏蔽發生在你眼前! 還要將 `tokenizer.decode()` 方法替換為 `tokenizer.convert_ids_to_tokens()` 以查看有時會屏蔽給定單詞中的單個標記, 而不是其他標記。 + + + +{#if fw === 'pt'} + +隨機掩碼的一個副作用是, 當使用 `Trainer` 時, 我們的評估指標將不是確定性的, 因為我們對訓練集和測試集使用相同的數據整理器。稍後我們會看到, 當我們使用 🤗 Accelerate 進行微調時, 我們將如何利用自定義評估循環的靈活性來凍結隨機性。 + +{/if} + +在為掩碼語言建模訓練模型時, 可以使用的一種技術是將整個單詞一起屏蔽, 而不僅僅是單個標記。這種方法稱為 _全詞屏蔽_。 如果我們想使用全詞屏蔽, 我們需要自己構建一個數據整理器。數據整理器只是一個函數, 它接受一個樣本列表並將它們轉換為一個批次, 所以現在讓我們這樣做吧! 我們將使用之前計算的單詞 ID 在單詞索引和相應標記之間進行映射, 然後隨機決定要屏蔽哪些單詞並將該屏蔽應用於輸入。請注意, 除了與掩碼對應的標籤外, 所有的標籤均為 `-100`。 + +{#if fw === 'pt'} + +```py +import collections +import numpy as np + +from transformers import default_data_collator + +wwm_probability = 0.2 + + +def whole_word_masking_data_collator(features): + for feature in features: + word_ids = feature.pop("word_ids") + + # Create a map between words and corresponding token indices + mapping = collections.defaultdict(list) + current_word_index = -1 + current_word = None + for idx, word_id in enumerate(word_ids): + if word_id is not None: + if word_id != current_word: + current_word = word_id + current_word_index += 1 + mapping[current_word_index].append(idx) + + # Randomly mask words + mask = np.random.binomial(1, wwm_probability, (len(mapping),)) + input_ids = feature["input_ids"] + labels = feature["labels"] + new_labels = [-100] * len(labels) + for word_id in np.where(mask)[0]: + word_id = word_id.item() + for idx in mapping[word_id]: + new_labels[idx] = labels[idx] + input_ids[idx] = tokenizer.mask_token_id + feature["labels"] = new_labels + + return default_data_collator(features) +``` + +{:else} + +```py +import collections +import numpy as np + +from transformers.data import tf_default_data_collator + +wwm_probability = 0.2 + + +def whole_word_masking_data_collator(features): + for feature in features: + word_ids = feature.pop("word_ids") + + # Create a map between words and corresponding token indices + mapping = collections.defaultdict(list) + current_word_index = -1 + current_word = None + for idx, word_id in enumerate(word_ids): + if word_id is not None: + if word_id != current_word: + current_word = word_id + current_word_index += 1 + mapping[current_word_index].append(idx) + + # Randomly mask words + mask = np.random.binomial(1, wwm_probability, (len(mapping),)) + input_ids = feature["input_ids"] + labels = feature["labels"] + new_labels = [-100] * len(labels) + for word_id in np.where(mask)[0]: + word_id = word_id.item() + for idx in mapping[word_id]: + new_labels[idx] = labels[idx] + input_ids[idx] = tokenizer.mask_token_id + feature["labels"] = new_labels + + return tf_default_data_collator(features) +``` + +{/if} + +Next, we can try it on the same samples as before: + +```py +samples = [lm_datasets["train"][i] for i in range(2)] +batch = whole_word_masking_data_collator(samples) + +for chunk in batch["input_ids"]: + print(f"\n'>>> {tokenizer.decode(chunk)}'") +``` + +```python out +'>>> [CLS] bromwell high is a cartoon comedy [MASK] it ran at the same time as some other programs about school life, such as " teachers ". my 35 years in the teaching profession lead me to believe that bromwell high\'s satire is much closer to reality than is " teachers ". the scramble to survive financially, the insightful students who can see right through their pathetic teachers\'pomp, the pettiness of the whole situation, all remind me of the schools i knew and their students. when i saw the episode in which a student repeatedly tried to burn down the school, i immediately recalled.....' + +'>>> .... [MASK] [MASK] [MASK] [MASK]....... high. a classic line : inspector : i\'m here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn\'t! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless' +``` + + + +✏️ **試試看!** 多次運行上面的代碼片段, 看看隨機屏蔽發生在你眼前! 還要將 `tokenizer.decode()` 方法替換為 `tokenizer.convert_ids_to_tokens()` 以查看來自給定單詞的標記始終被屏蔽在一起。 + + + +現在我們有兩個數據整理器, 其餘的微調步驟是標準的。如果您沒有足夠幸運地獲得神話般的 P100 GPU 😭, 在 Google Colab 上進行訓練可能需要一段時間, 因此我們將首先將訓練集的大小縮減為幾千個示例。別擔心, 我們仍然會得到一個相當不錯的語言模型! 在 🤗 Datasets 中快速下采樣數據集的方法是通過我們在 [第五章](/course/chapter5) 中看到的 `Dataset.train_test_split()` 函數: + +```python +train_size = 10_000 +test_size = int(0.1 * train_size) + +downsampled_dataset = lm_datasets["train"].train_test_split( + train_size=train_size, test_size=test_size, seed=42 +) +downsampled_dataset +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['attention_mask', 'input_ids', 'labels', 'word_ids'], + num_rows: 10000 + }) + test: Dataset({ + features: ['attention_mask', 'input_ids', 'labels', 'word_ids'], + num_rows: 1000 + }) +}) +``` + +這會自動創建新的 `train` 和 `test` 拆分, 訓練集大小設置為 10,000 個示例, 驗證設置為其中的 10% -- 如果你有一個強大的 GPU, 可以隨意增加它! 我們需要做的下一件事是登錄 Hugging Face Hub。如果你在筆記本中運行此代碼, 則可以使用以下實用程序函數執行此操作: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +這將顯示一個小部件, 你可以在其中輸入你的憑據。或者, 你可以運行: + +``` +huggingface-cli login +``` + +在你最喜歡的終端中登錄。 + +{#if fw === 'tf'} + +登陸後, 我們就可以創建我們的 `tf.data` 數據集。我們將在這裡只使用標準數據整理器, 但你也可以嘗試使用整個單詞掩碼整理器並將結果作為練習進行比較: + +```python +tf_train_dataset = downsampled_dataset["train"].to_tf_dataset( + columns=["input_ids", "attention_mask", "labels"], + collate_fn=data_collator, + shuffle=True, + batch_size=32, +) + +tf_eval_dataset = downsampled_dataset["test"].to_tf_dataset( + columns=["input_ids", "attention_mask", "labels"], + collate_fn=data_collator, + shuffle=False, + batch_size=32, +) +``` + +接下來, 我們設置訓練超參數並編譯模型。我們使用 🤗 Transformers 庫中的 `create_optimizer()` 函數, 它提供了一個具有線性學習率衰減的 `AdamW` 優化器。我們還使用了模型內置的損失, 當沒有損失被指定為 `compile()` 參數是, 這是默認值, 並且我們將訓練精度設置為 `"mixed_float16"`。請注意,如果你使用的是Colab GPU或其他不支持float16加速的GPU, 你可能應該註釋掉這一行。 + +此外, 我們還設置了一個 `PushToHubCallback`, 它將在每個epoch後將模型保存到Hub。你可以使用 `hub_model_id` 參數指定你想要推送到的存儲庫的名稱 (特別是你將必須使用該參數來推送到組織)。例如, 為了將模型推送到 [`huggingface-course` organization](https://huggingface.co/huggingface-course), 我們添加了 `hub_model_id="huggingface-course/distilbert-finetuned-imdb"`。默認情況下, 使用的存儲庫將位於你的命名空間中, 並以你設置的輸出目錄命名, 因此在我們的示例中, 它將是 `"lewtun/distilbert-finetuned-imdb"`。 + +```python +from transformers import create_optimizer +from transformers.keras_callbacks import PushToHubCallback +import tensorflow as tf + +num_train_steps = len(tf_train_dataset) +optimizer, schedule = create_optimizer( + init_lr=2e-5, + num_warmup_steps=1_000, + num_train_steps=num_train_steps, + weight_decay_rate=0.01, +) +model.compile(optimizer=optimizer) + +# Train in mixed-precision float16 +tf.keras.mixed_precision.set_global_policy("mixed_float16") + +callback = PushToHubCallback( + output_dir=f"{model_name}-finetuned-imdb", tokenizer=tokenizer +) +``` + +我們現在已經準備好運行 `model.fit()` 了 -- 但在此之前, 讓我們先簡單地看看 _perplexity_, 它是評估語言模型性能的常用指標。 + +{:else} + +登陸後, 我們可以指定 `Trainer` 參數: + +```python +from transformers import TrainingArguments + +batch_size = 64 +# Show the training loss with every epoch +logging_steps = len(downsampled_dataset["train"]) // batch_size +model_name = model_checkpoint.split("/")[-1] + +training_args = TrainingArguments( + output_dir=f"{model_name}-finetuned-imdb", + overwrite_output_dir=True, + evaluation_strategy="epoch", + learning_rate=2e-5, + weight_decay=0.01, + per_device_train_batch_size=batch_size, + per_device_eval_batch_size=batch_size, + push_to_hub=True, + fp16=True, + logging_steps=logging_steps, +) +``` + +在這裡, 我們調整了一些默認選項, 包括 `logging_steps` , 以確保我們跟蹤每個epoch的訓練損失。我們還使用了 `fp16=True` 來實現混合精度訓練, 這給我們帶來了另一個速度提升。默認情況下, `Trainer` 將刪除不屬於模型的 `forward()` 方法的列。這意味著, 如果你使用整個單詞屏蔽排序器, 你還需要設置 `remove_unused_columns=False`, 以確保我們不會在訓練期間丟失 `word_ids` 列。 + +請注意, 你可以使用 `hub_model_id` 參數指定要推送到的存儲庫的名稱((特別是你將必須使用該參數向組織推送)。例如, 當我們將模型推送到 [`huggingface-course` organization](https://huggingface.co/huggingface-course) 時, 我們添加了 `hub_model_id="huggingface-course/distilbert-finetuned-imdb"` 到 `TrainingArguments` 中。默認情況下, 使用的存儲庫將在你的命名空間中並以你設置的輸出目錄命名, 因此在我們的示例中, 它將是 `"lewtun/distilbert-finetuned-imdb"`。 + +我們現在擁有實例化 `Trainer` 的所有成分。這裡我們只使用標準的 `data_collator`, 但你可以嘗試使用整個單詞掩碼整理器並比較結果作為練習: + +```python +from transformers import Trainer + +trainer = Trainer( + model=model, + args=training_args, + train_dataset=downsampled_dataset["train"], + eval_dataset=downsampled_dataset["test"], + data_collator=data_collator, + tokenizer=tokenizer, +) +``` + +我們現在準備運行 `trainer.train()` -- 但在此之前讓我們簡要地看一下 _perplexity_, 這是評估語言模型性能的常用指標。 + +{/if} + +### 語言模型的perplexity + + + +與文本分類或問答等其他任務不同, 在這些任務中, 我們會得到一個帶標籤的語料庫進行訓練, 而語言建模則沒有任何明確的標籤。那麼我們如何確定什麼是好的語言模型呢? 就像手機中的自動更正功能一樣, 一個好的語言模型是為語法正確的句子分配高概率, 為無意義的句子分配低概率。為了讓你更好地瞭解這是什麼樣子, 您可以在網上找到一整套 "autocorrect fails", 其中一個人手機中的模型產生了一些相當有趣 (而且通常不合適) 的結果! + +{#if fw === 'pt'} + +假設我們的測試集主要由語法正確的句子組成, 那麼衡量我們的語言模型質量的一種方法是計算它分配給測試集中所有句子中的下一個單詞的概率。高概率表明模型對看不見的例子並不感到 "驚訝" 或 "疑惑", 並表明它已經學習了語言中的基本語法模式。 perplexity度有多種數學定義, 但我們將使用的定義是交叉熵損失的指數。因此, 我們可以通過 `Trainer.evaluate()` 函數計算測試集上的交叉熵損失, 然後取結果的指數來計算預訓練模型的perplexity度: + +```python +import math + +eval_results = trainer.evaluate() +print(f">>> Perplexity: {math.exp(eval_results['eval_loss']):.2f}") +``` + +{:else} + +假設我們的測試集主要由語法正確的句子組成, 那麼衡量我們的語言模型質量的一種方法是計算測試集所有句子中它分配給下一個單詞的概率。高概率表明, 該模型表明該模型對未見過的示例不感到 "驚訝" 或 "困惑", 並表明它已經學會了該語言的基本語法模式。關於perplexity度有各種不同的數學定義, 但我們要用的定義是交叉熵損失的指數。因此, 我們可以通過 `model.evaluate()` 方法計算測試集上的交叉熵損失, 然後取結果的指數來計算我們預訓練模型的perplexity度: + +```python +import math + +eval_loss = model.evaluate(tf_eval_dataset) +print(f"Perplexity: {math.exp(eval_loss):.2f}") +``` + +{/if} + +```python out +>>> Perplexity: 21.75 +``` + +較低的perplexity分數意味著更好的語言模型, 我們可以在這裡看到我們的起始模型有一個較大的值。看看我們能不能通過微調來降低它! 為此, 我們首先運行訓練循環: + +{#if fw === 'pt'} + +```python +trainer.train() +``` + +{:else} + +```python +model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback]) +``` + +{/if} + +然後像以前一樣計算測試集上的perplexity度: + +{#if fw === 'pt'} + +```python +eval_results = trainer.evaluate() +print(f">>> Perplexity: {math.exp(eval_results['eval_loss']):.2f}") +``` + +{:else} + +```python +eval_loss = model.evaluate(tf_eval_dataset) +print(f"Perplexity: {math.exp(eval_loss):.2f}") +``` + +{/if} + +```python out +>>> Perplexity: 11.32 +``` + +很好 -- 這大大減少了困惑, 這告訴我們模型已經瞭解了一些關於電影評論領域的知識! + +{#if fw === 'pt'} + +訓練完成後, 我們可以將帶有訓練信息的模型卡推送到 Hub (檢查點在訓練過程中自行保存): + +```python +trainer.push_to_hub() +``` + +{/if} + + + +✏️ **輪到你了!** 將數據整理器改為全字屏蔽整理器後運行上面的訓練。你有得到更好的結果嗎? + + + +{#if fw === 'pt'} + +在我們的用例中, 我們不需要對訓練循環做任何特別的事情, 但在某些情況下, 你可能需要實現一些自定義邏輯。對於這些應用, 你可以使用 🤗 Accelerate -- 讓我們來看看吧! + +## 使用 🤗 Accelerate 微調 DistilBERT + +正如我們在 `Trainer` 中看到的, 對掩碼語言模型的微調與 [第三章](/course/chapter3) 中的文本分類示例非常相似。事實上, 唯一的微妙之處是使用特殊的數據整理器, 我們已經在本節的前面介紹過了! + +然而, 我們看到 `DataCollatorForLanguageModeling` 還對每次評估應用隨機掩碼, 因此每次訓練運行時, 我們都會看到perplexity度分數的一些波動。消除這種隨機性來源的一種方法是應用掩碼 _一次_ 在整個測試集上, 然後使用🤗 Transformers 中的默認數據整理器在評估期間收集批次。為了看看它是如何工作的, 讓我們實現一個簡單的函數, 將掩碼應用於批處理, 類似於我們第一次遇到的 `DataCollatorForLanguageModeling`: + +```python +def insert_random_mask(batch): + features = [dict(zip(batch, t)) for t in zip(*batch.values())] + masked_inputs = data_collator(features) + # Create a new "masked" column for each column in the dataset + return {"masked_" + k: v.numpy() for k, v in masked_inputs.items()} +``` + +接下來, 我們將此函數應用於我們的測試集並刪除未掩碼的列, 以便我們可以用掩碼的列替換它們。你可以通過用適當的替換上面的 `data_collator` 來使整個單詞掩碼, 在這種情況下, 你應該刪除此處的第一行: + +```py +downsampled_dataset = downsampled_dataset.remove_columns(["word_ids"]) +eval_dataset = downsampled_dataset["test"].map( + insert_random_mask, + batched=True, + remove_columns=downsampled_dataset["test"].column_names, +) +eval_dataset = eval_dataset.rename_columns( + { + "masked_input_ids": "input_ids", + "masked_attention_mask": "attention_mask", + "masked_labels": "labels", + } +) +``` + +然後我們可以像往常一樣設置數據加載器, 但我們將使用🤗 Transformers 中的 `default_data_collator` 作為評估集: + +```python +from torch.utils.data import DataLoader +from transformers import default_data_collator + +batch_size = 64 +train_dataloader = DataLoader( + downsampled_dataset["train"], + shuffle=True, + batch_size=batch_size, + collate_fn=data_collator, +) +eval_dataloader = DataLoader( + eval_dataset, batch_size=batch_size, collate_fn=default_data_collator +) +``` + +從這裡開始, 我們遵循🤗 Accelerate 的標準步驟。第一個任務是加載預訓練模型的新版本: + +``` +model = AutoModelForMaskedLM.from_pretrained(model_checkpoint) +``` + +然後我們需要指定優化器; 我們將使用標準的 `AdamW`: + +```python +from torch.optim import AdamW + +optimizer = AdamW(model.parameters(), lr=5e-5) +``` + +有了這些對象, 我們現在可以準備使用 `Accelerator` 加速器進行訓練的一切: + +```python +from accelerate import Accelerator + +accelerator = Accelerator() +model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare( + model, optimizer, train_dataloader, eval_dataloader +) +``` + +現在我們的模型、優化器和數據加載器都配置好了, 我們可以指定學習率調度器如下: + +```python +from transformers import get_scheduler + +num_train_epochs = 3 +num_update_steps_per_epoch = len(train_dataloader) +num_training_steps = num_train_epochs * num_update_steps_per_epoch + +lr_scheduler = get_scheduler( + "linear", + optimizer=optimizer, + num_warmup_steps=0, + num_training_steps=num_training_steps, +) +``` + +在訓練之前要做的最後一件事是: 在 Hugging Face Hub 上創建一個模型庫! 我們可以使用 🤗 Hub 庫來首先生成我們的 repo 的全名: + +```python +from huggingface_hub import get_full_repo_name + +model_name = "distilbert-base-uncased-finetuned-imdb-accelerate" +repo_name = get_full_repo_name(model_name) +repo_name +``` + +```python out +'lewtun/distilbert-base-uncased-finetuned-imdb-accelerate' +``` + +然後使用來自🤗 Hub 的 `Repository` 類: + +```python +from huggingface_hub import Repository + +output_dir = model_name +repo = Repository(output_dir, clone_from=repo_name) +``` + +完成後, 只需寫出完整的訓練和評估循環即可: + +```python +from tqdm.auto import tqdm +import torch +import math + +progress_bar = tqdm(range(num_training_steps)) + +for epoch in range(num_train_epochs): + # Training + model.train() + for batch in train_dataloader: + outputs = model(**batch) + loss = outputs.loss + accelerator.backward(loss) + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) + + # Evaluation + model.eval() + losses = [] + for step, batch in enumerate(eval_dataloader): + with torch.no_grad(): + outputs = model(**batch) + + loss = outputs.loss + losses.append(accelerator.gather(loss.repeat(batch_size))) + + losses = torch.cat(losses) + losses = losses[: len(eval_dataset)] + try: + perplexity = math.exp(torch.mean(losses)) + except OverflowError: + perplexity = float("inf") + + print(f">>> Epoch {epoch}: Perplexity: {perplexity}") + + # Save and upload + accelerator.wait_for_everyone() + unwrapped_model = accelerator.unwrap_model(model) + unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) + if accelerator.is_main_process: + tokenizer.save_pretrained(output_dir) + repo.push_to_hub( + commit_message=f"Training in progress epoch {epoch}", blocking=False + ) +``` + +```python out +>>> Epoch 0: Perplexity: 11.397545307900472 +>>> Epoch 1: Perplexity: 10.904909330983092 +>>> Epoch 2: Perplexity: 10.729503505340409 +``` + +很棒, 我們已經能夠評估每個 epoch 的perplexity度, 並確保多次訓練運行是可重複的! + +{/if} + +## 使用我們微調的模型 + +你可以通過在Hub上使用其他小部件或在本地使用🤗 Transformers 的`管道`於微調模型進行交互。讓我們使用後者來下載我們的模型, 使用 `fill-mask` 管道: + +```python +from transformers import pipeline + +mask_filler = pipeline( + "fill-mask", model="huggingface-course/distilbert-base-uncased-finetuned-imdb" +) +``` + +然後, 我們可以向管道提供 "This is a great [MASK]" 示例文本, 並查看前 5 個預測是什麼: + +```python +preds = mask_filler(text) + +for pred in preds: + print(f">>> {pred['sequence']}") +``` + +```python out +'>>> this is a great movie.' +'>>> this is a great film.' +'>>> this is a great story.' +'>>> this is a great movies.' +'>>> this is a great character.' +``` + +好的 -- 我們的模型顯然已經調整了它的權重來預測與電影更密切相關的詞! + + + +這結束了我們訓練語言模型的第一個實驗。在 [第六節](/course/chapter7/section6)中你將學習如何從頭開始訓練像 GPT-2 這樣的自迴歸模型; 如果你想了解如何預訓練您自己的 Transformer 模型, 請前往那裡! + + + +✏️ **試試看!** 為了量化域適應的好處, 微調 IMDb 標籤上的分類器和預先訓練和微調的Distil BERT檢查點。如果你需要複習文本分類, 請查看 [第三章](/course/chapter3)。 + + diff --git a/chapters/zh-TW/chapter7/4.mdx b/chapters/zh-TW/chapter7/4.mdx new file mode 100644 index 000000000..fe3c33cd6 --- /dev/null +++ b/chapters/zh-TW/chapter7/4.mdx @@ -0,0 +1,996 @@ + + +# 翻譯 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +現在讓我們深入研究翻譯。這是另一個[sequence-to-sequence 任務](/course/chapter1/7),這意味著這是一個可以表述為從一個序列到另一個序列的問題。從這個意義上說,這個問題非常類似[文本摘要](/course/chapter7/6),並且您可以將我們將在此處學習到的一些內容遷移到其他的序列到序列問題,例如: + +- **風格遷移** : 創建一個模型將某種風格遷移到一段文本(例如,正式的風格遷移到休閒的風格或莎士比亞英語到現代英語) +- **生成問題的回答** :創建一個模型,在給定上下文的情況下生成問題的答案 + + + +如果您有足夠大的兩種(或更多)語言的文本語料庫,您可以從頭開始訓練一個新的翻譯模型,就像我們在[因果語言建模](/course/chapter7/6)部分中所做的那樣。然而,微調現有的翻譯模型會更快,無論是從像 mT5 或 mBART 這樣的多語言模型微調到特定的語言對,或者是專門用於從一種語言翻譯成另一種語言的模型。 + +在本節中,我們將對[KDE4 數據集](https://huggingface.co/datasets/kde4)上的Marian模型進行微調,該模型經過了從英語到法語的翻譯預訓練(因為很多“ Hugging Face”的員工會說這兩種語言),它是[KDE應用程序](https://apps.kde.org/)本地化文件的數據集。我們將使用的模型已經在從[Opus 數據集](https://opus.nlpl.eu/)(實際上包含KDE4數據集)中提取的法語和英語文本的大型語料庫上進行了預先訓練。但是,即使我們使用的預訓練模型在其預訓練期間使用了這部分數據集,我們也會看到,經過微調後,我們可以得到一個更好的版本。 + +完成後,我們將擁有一個模型,可以進行這樣的翻譯: + + + + + +One-hot encoded labels for question answering. + + + +與前面的部分一樣,您可以使用以下代碼找到我們將訓練並上傳到 Hub 的實際模型,並[在這裡](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr?text=This+plugin+allows+you+to+automatically+translate+web+pages+between+several+languages.)查看模型輸出的結果 + +## 準備數據 + +為了從頭開始微調或訓練翻譯模型,我們需要一個適合該任務的數據集。如前所述,我們將使用[KDE4 數據集](https://huggingface.co/datasets/kde4)在本節中,但您可以很容易地調整代碼以使用您自己的數據,只要您有要互譯的兩種語言的句子對。如果您需要複習如何將自定義數據加載到 **Dataset** 可以複習一下[第五章](/course/chapter5). + +### KDE4 數據集 + +像往常一樣,我們使用 **load_dataset()** 函數: + +```py +from datasets import load_dataset, load_metric + +raw_datasets = load_dataset("kde4", lang1="en", lang2="fr") +``` + +如果您想使用不同的語言對,您可以使用它們的代碼來指定它們。該數據集共有 92 種語言可用;您可以通過[數據集卡片](https://huggingface.co/datasets/kde4)展開其上的語言標籤來查看它們. + +Language available for the KDE4 dataset. + +我們來看看數據集: + +```py +raw_datasets +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['id', 'translation'], + num_rows: 210173 + }) +}) +``` + +我們有 210,173 對句子,但在一次訓練過程中,我們需要創建自己的驗證集。正如我們在[第五章](/course/chapter5)學的的那樣, **Dataset** 有一個 **train_test_split()** 方法,可以幫我們拆分數據集。我們將設定固定的隨機數種子: + +```py +split_datasets = raw_datasets["train"].train_test_split(train_size=0.9, seed=20) +split_datasets +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['id', 'translation'], + num_rows: 189155 + }) + test: Dataset({ + features: ['id', 'translation'], + num_rows: 21018 + }) +}) +``` + +我們可以將 **test** 的鍵重命名為 **validation** 像這樣: + +```py +split_datasets["validation"] = split_datasets.pop("test") +``` + +現在讓我們看一下數據集的一個元素: + +```py +split_datasets["train"][1]["translation"] +``` + +```python out +{'en': 'Default to expanded threads', + 'fr': 'Par défaut, développer les fils de discussion'} +``` + +我們得到一個字典,其中包含我們請求的兩種語言的兩個句子。這個充滿技術計算機科學術語的數據集的一個特殊之處在於它們都完全用法語翻譯。然而,法國工程師通常很懶惰,在交談時,大多數計算機科學專用詞彙都用英語表述。例如,“threads”這個詞很可能出現在法語句子中,尤其是在技術對話中;但在這個數據集中,它被翻譯成更正確的“fils de Discussion”。我們使用的預訓練模型已經在一個更大的法語和英語句子語料庫上進行了預訓練,採用了更簡單的選擇,即保留單詞的原樣: + +```py +from transformers import pipeline + +model_checkpoint = "Helsinki-NLP/opus-mt-en-fr" +translator = pipeline("translation", model=model_checkpoint) +translator("Default to expanded threads") +``` + +```python out +[{'translation_text': 'Par défaut pour les threads élargis'}] +``` + +這種情況的另一個例子是“plugin”這個詞,它不是正式的法語詞,但大多數母語人士都能理解,也不會費心去翻譯。 +在 KDE4 數據集中,這個詞在法語中被翻譯成更正式的“module d’extension”: +```py +split_datasets["train"][172]["translation"] +``` + +```python out +{'en': 'Unable to import %1 using the OFX importer plugin. This file is not the correct format.', + 'fr': "Impossible d'importer %1 en utilisant le module d'extension d'importation OFX. Ce fichier n'a pas un format correct."} +``` + +然而,我們的預訓練模型堅持使用簡練而熟悉的英文單詞: + +```py +translator( + "Unable to import %1 using the OFX importer plugin. This file is not the correct format." +) +``` + +```python out +[{'translation_text': "Impossible d'importer %1 en utilisant le plugin d'importateur OFX. Ce fichier n'est pas le bon format."}] +``` + +看看我們的微調模型是否能識別數據集的這些特殊性。(劇透警告:它會)。 + + + + + +✏️ **輪到你了!** 另一個在法語中經常使用的英語單詞是“email”。在訓練數據集中找到使用這個詞的第一個樣本。它是如何翻譯的?預訓練模型如何翻譯同一個英文句子? + + + +### 處理數據 + + + +您現在應該知道我們的下一步該做些什麼了:所有文本都需要轉換為token ID,以便模型能夠理解它們。對於這個任務,我們需要同時標記輸入和目標。我們的首要任務是創建我們的 **tokenizer** 對象。如前所述,我們將使用 Marian 英語到法語的預訓練模型。如果您使用另一對語言嘗試此代碼,請確保調整模型Checkpoint。[Helsinki-NLP](https://huggingface.co/Helsinki-NLP)組織提供了多種語言的一千多種模型。 + +```python +from transformers import AutoTokenizer + +model_checkpoint = "Helsinki-NLP/opus-mt-en-fr" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="tf") +``` + +您可以將 **model_checkpoint** 更換為[Hub](https://huggingface.co/models)上你喜歡的任何其他型號,或本地保存的預訓練模型和標記器。 + + + +💡 如果正在使用mart、mBART-50或M2 M100等多語言標記器,則需要在tokenizer中設置tokenizer.src_lang和tokenizer.tgt_lang為正確的輸入和目標的語言代碼。 + + + +我們的數據準備非常簡單。 只需要記住一件事:您照常處理輸入,但對於這次的輸出目標,您需要將標記器包裝在上下文管理器“as_target_tokenizer()”中。 + +Python 中的上下文管理器引入了 **with** 語句,當您有兩個相關的操作要成對執行時很有用。最常見的例子是當您寫入或讀取文件時,下面是一個例子: + +``` +with open(file_path) as f: + content = f.read() +``` + +這裡成對執行的兩個相關操作是打開和關閉文件的操作。打開的文件f對應的對象只在with下的縮進塊內有效;在該塊之前打開,在該塊的末尾關閉。 + +在本例中,上下文管理器 as_target_tokenizer() 將在執行縮進塊之前將標記器設置為輸出語言(此處為法語),然後將其設置回輸入語言(此處為英語)。 + +因此,預處理一個樣本如下所示: + +```python +en_sentence = split_datasets["train"][1]["translation"]["en"] +fr_sentence = split_datasets["train"][1]["translation"]["fr"] + +inputs = tokenizer(en_sentence) +with tokenizer.as_target_tokenizer(): + targets = tokenizer(fr_sentence) +``` + +如果我們忘記在上下文管理器中標記目標,它們將被輸入標記器標記,在Marian模型的情況下,會導致輸出的異常: + +```python +wrong_targets = tokenizer(fr_sentence) +print(tokenizer.convert_ids_to_tokens(wrong_targets["input_ids"])) +print(tokenizer.convert_ids_to_tokens(targets["input_ids"])) +``` + +```python out +['▁Par', '▁dé', 'f', 'aut', ',', '▁dé', 've', 'lop', 'per', '▁les', '▁fil', 's', '▁de', '▁discussion', ''] +['▁Par', '▁défaut', ',', '▁développer', '▁les', '▁fils', '▁de', '▁discussion', ''] +``` + +正如我們所看到的,使用英語標記器來預處理法語句子會產生更多的標記,因為標記器不知道任何法語單詞(除了那些也出現在英語語言中的單詞,比如“discussion”)。 + +`inputs` 和 `targets` 都是帶有我們常用鍵(輸入 ID、注意掩碼等)的字典,所以最後一步是在輸入中設置一個 `"labels"` 鍵。 我們在數據集的預處理函數中執行此操作: + +```python +max_input_length = 128 +max_target_length = 128 + + +def preprocess_function(examples): + inputs = [ex["en"] for ex in examples["translation"]] + targets = [ex["fr"] for ex in examples["translation"]] + model_inputs = tokenizer(inputs, max_length=max_input_length, truncation=True) + + # Set up the tokenizer for targets + with tokenizer.as_target_tokenizer(): + labels = tokenizer(targets, max_length=max_target_length, truncation=True) + + model_inputs["labels"] = labels["input_ids"] + return model_inputs +``` + +請注意,我們為輸入和輸出設置了相同的最大長度。由於我們處理的文本看起來很短,我們使用 128。 + + + +💡如果你使用的是T5模型(更具體地說,是T5 -xxx檢查點之一),模型將需要文本輸入有一個前綴來表示正在進行的任務,例如從英語到法語的翻譯 + + + + + +⚠️ 我們不關注目標的注意力掩碼,因為模型不會需要它。相反,對應於填充標記的標籤應設置為-100,以便在loss計算中忽略它們。這將在稍後由我們的數據整理器完成,因為我們正在應用動態填充,但是如果您在此處使用填充,您應該調整預處理函數以將與填充標記對應的所有標籤設置為 -100。 + + + +我們現在可以對數據集的所有數據一次性應用該預處理: + +```py +tokenized_datasets = split_datasets.map( + preprocess_function, + batched=True, + remove_columns=split_datasets["train"].column_names, +) +``` + +現在數據已經過預處理,我們準備好微調我們的預訓練模型! + +{#if fw === 'pt'} + +## 使用 Trainer API 微調模型 + +使用 `Trainer` 的實際代碼將與以前相同,只是稍作改動:我們在這裡使用 [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer), 它是 `Trainer` 的子類,它可以正確處理這種序列到序列的評估,並使用 `generate()` 方法來預測輸入的輸出。 當我們討論評估指標時,我們將更詳細地探討這一點。 + +首先,我們需要一個實際的模型來進行微調。 我們將使用通常的 `AutoModel` API: + +```py +from transformers import AutoModelForSeq2SeqLM + +model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint) +``` + +{:else} + +## 使用 Keras 微調模型 + +首先,我們需要一個實際的模型來進行微調。 我們將使用通常的 `AutoModel` API: + +```py +from transformers import TFAutoModelForSeq2SeqLM + +model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint, from_pt=True) +``` + + + +💡 `Helsinki-NLP/opus-mt-en-fr` checkpoint只有 PyTorch 的權重,所以在使用`from_pretrained()`方法加載模型時不會使用 `from_pt=True` 參數。 當您指定`from_pt=True`,庫會自動下載並轉換PyTorch 為您提供權重。 如您所見,使用🤗transormer 在兩者之間切換非常簡單 + + + +{/if} + +請注意,這次我們使用的是在翻譯任務上訓練過的模型,並且實際上已經可以使用,因此沒有關於丟失權重或新初始化權重的警告。 + +### 數據整理 + +我們需要一個數據整理器來處理動態批處理的填充。在本例中,我們不能像[第3章](/course/chapter3)那樣使用帶填充的**DataCollatorWithPadding**,因為它只填充輸入(輸入ID、注意掩碼和令牌類型ID)。我們的標籤也應該填充到標籤中遇到的最大長度。而且,如前所述,用於填充標籤的填充值應為-100,而不是標記器的填充標記,以確保在損失計算中忽略這些填充值。 + +這一切都可以由 [`DataCollatorForSeq2Seq`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorforseq2seq) 完成。 與 `DataCollatorWithPadding` 一樣,它採用用於預處理輸入的`tokenizer`,但它也採用`model`。 這是因為數據整理器還將負責準備解碼器輸入 ID,它們是標籤偏移之後形成的,開頭帶有特殊標記。 由於不同架構的這種轉變略有不同,因此“DataCollatorForSeq2Seq”需要知道“模型”對象: + +{#if fw === 'pt'} + +```py +from transformers import DataCollatorForSeq2Seq + +data_collator = DataCollatorForSeq2Seq(tokenizer, model=model) +``` + +{:else} + +```py +from transformers import DataCollatorForSeq2Seq + +data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf") +``` + +{/if} + +為了在幾個樣本上進行測試,我們只需在我們標記化訓練集中的部分數據上調用它: + +```py +batch = data_collator([tokenized_datasets["train"][i] for i in range(1, 3)]) +batch.keys() +``` + +```python out +dict_keys(['attention_mask', 'input_ids', 'labels', 'decoder_input_ids']) +``` + +我們可以檢查我們的標籤是否已使用 **-100** 填充到批次的最大長度: + +```py +batch["labels"] +``` + +```python out +tensor([[ 577, 5891, 2, 3184, 16, 2542, 5, 1710, 0, -100, + -100, -100, -100, -100, -100, -100], + [ 1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817, + 550, 7032, 5821, 7907, 12649, 0]]) +``` + +我們還可以查看解碼器輸入 ID,看看它們是標籤的偏移形成的版本: + +```py +batch["decoder_input_ids"] +``` + +```python out +tensor([[59513, 577, 5891, 2, 3184, 16, 2542, 5, 1710, 0, + 59513, 59513, 59513, 59513, 59513, 59513], + [59513, 1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, + 817, 550, 7032, 5821, 7907, 12649]]) +``` + +以下是我們數據集中第一個和第二個元素的標籤: + +```py +for i in range(1, 3): + print(tokenized_datasets["train"][i]["labels"]) +``` + +```python out +[577, 5891, 2, 3184, 16, 2542, 5, 1710, 0] +[1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817, 550, 7032, 5821, 7907, 12649, 0] +``` + +{#if fw === 'pt'} + +我們將把這個 `data_collator` 傳遞給 `Seq2SeqTrainer`。 接下來,讓我們看一下評估指標。 + +{:else} + +我們現在可以使用 `data_collator` 將我們的每個數據集轉換為 `tf.data.Dataset`,準備好進行訓練: + +```python +tf_train_dataset = tokenized_datasets["train"].to_tf_dataset( + columns=["input_ids", "attention_mask", "labels"], + collate_fn=data_collator, + shuffle=True, + batch_size=32, +) +tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset( + columns=["input_ids", "attention_mask", "labels"], + collate_fn=data_collator, + shuffle=False, + batch_size=16, +) +``` + +{/if} + + +### 評估指標 + + + +{#if fw === 'pt'} + +`Seq2SeqTrainer` 添加到其超類 `Trainer` 的功能是在評估或預測期間使用 `generate()` 方法的能力。 在訓練期間,模型將使用帶有注意掩碼的“decoder_input_ids”,以確保它不使用預測的標記之後的標記,以加快訓練速度。 在推理過程中,我們將無法使用預測的標記之後的標記,因為我們沒有標籤,因此使用相同的設置使用帶有注意掩碼的“decoder_input_ids”,評估我們的模型是個好主意。 + +正如我們在[第一章](/course/chapter1/6)看到的,解碼器通過一個一個地預測標記來執行推理——這是🤗 Transformers 在幕後通過 **generate()** 方法實現的。如果我們設置 predict_with_generate=True,Seq2 Seq Trainer 將允許我們使用該方法進行評估。 + + +{/if} + +用於翻譯的傳統指標是[BLEU 分數](https://en.wikipedia.org/wiki/BLEU), 由Kishore Papineni等人在[2002年的一篇文章](https://aclanthology.org/P02-1040.pdf)中引入。BLEU 分數評估翻譯與其標籤的接近程度。它不衡量模型生成輸出的可懂度或語法正確性,而是使用統計規則來確保生成輸出中的所有單詞也出現在目標中。此外,如果相同單詞在目標中沒有重複,則有規則懲罰相同單詞的重複(以避免模型輸出類似 **the the the the the**的句子 ) 並輸出比目標中短的句子(以避免模型輸出像 **the** 這樣的句子)。 + +BLEU 的一個缺點是它需要文本已經被分詞,這使得比較使用不同標記器的模型之間的分數變得困難。因此,當今用於基準翻譯模型的最常用指標是[SacreBLEU](https://github.com/mjpost/sacrebleu),它通過標準化標記化步驟解決了這個缺點(和其他的一些缺點)。要使用此指標,我們首先需要安裝 SacreBLEU 庫: + +```py +!pip install sacrebleu +``` + +然後我們可以就像我們在[第三章](/course/chapter3)那樣通過 **load_metric()** 加載它 : + +```py +from datasets import load_metric + +metric = load_metric("sacrebleu") +``` + +該指標將文本作為輸入和目標結果。它旨在接受多個可接受的目標,因為同一個句子通常有多個可接受的翻譯——我們使用的數據集只提供一個,但在 NLP 中找到將多個句子作為標籤的數據集不是一個難題。因此,預測結果應該是一個句子列表,而參考應該是一個句子列表的列表。 + +讓我們嘗試一個例子: + +```py +predictions = [ + "This plugin lets you translate web pages between several languages automatically." +] +references = [ + [ + "This plugin allows you to automatically translate web pages between several languages." + ] +] +metric.compute(predictions=predictions, references=references) +``` + +```python out +{'score': 46.750469682990165, + 'counts': [11, 6, 4, 3], + 'totals': [12, 11, 10, 9], + 'precisions': [91.67, 54.54, 40.0, 33.33], + 'bp': 0.9200444146293233, + 'sys_len': 12, + 'ref_len': 13} +``` + +這得到了 46.75 的 BLEU 分數,這是相當不錯的——作為參考,原始 Transformer 模型在[“Attention Is All You Need” 論文](https://arxiv.org/pdf/1706.03762.pdf)類似的英語和法語翻譯任務中獲得了 41.8 的 BLEU 分數! (有關各個指標的更多信息,例如 **counts** 和 **bp** ,見[SacreBLEU 倉庫](https://github.com/mjpost/sacrebleu/blob/078c440168c6adc89ba75fe6d63f0d922d42bcfe/sacrebleu/metrics/bleu.py#L74).) 另一方面,如果我們嘗試使用翻譯模型中經常出現的兩種糟糕的預測類型(大量重複或太短),我們將得到相當糟糕的 BLEU 分數: + +```py +predictions = ["This This This This"] +references = [ + [ + "This plugin allows you to automatically translate web pages between several languages." + ] +] +metric.compute(predictions=predictions, references=references) +``` + +```python out +{'score': 1.683602693167689, + 'counts': [1, 0, 0, 0], + 'totals': [4, 3, 2, 1], + 'precisions': [25.0, 16.67, 12.5, 12.5], + 'bp': 0.10539922456186433, + 'sys_len': 4, + 'ref_len': 13} +``` + +```py +predictions = ["This plugin"] +references = [ + [ + "This plugin allows you to automatically translate web pages between several languages." + ] +] +metric.compute(predictions=predictions, references=references) +``` + +```python out +{'score': 0.0, + 'counts': [2, 1, 0, 0], + 'totals': [2, 1, 0, 0], + 'precisions': [100.0, 100.0, 0.0, 0.0], + 'bp': 0.004086771438464067, + 'sys_len': 2, + 'ref_len': 13} +``` + +分數可以從 0 到 100,越高越好。 + +{#if fw === 'tf'} + +為了從模型輸出可以被評估基準可以使用的文本,我們將使用 **tokenizer.batch_decode()** 方法。我們只需要清理標籤中所有 **-100** (標記器會自動為填充標記做同樣的事情): + +```py +import numpy as np + + +def compute_metrics(): + all_preds = [] + all_labels = [] + sampled_dataset = tokenized_datasets["validation"].shuffle().select(range(200)) + tf_generate_dataset = sampled_dataset.to_tf_dataset( + columns=["input_ids", "attention_mask", "labels"], + collate_fn=data_collator, + shuffle=False, + batch_size=4, + ) + for batch in tf_generate_dataset: + predictions = model.generate( + input_ids=batch["input_ids"], attention_mask=batch["attention_mask"] + ) + decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) + labels = batch["labels"].numpy() + labels = np.where(labels != -100, labels, tokenizer.pad_token_id) + decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True) + decoded_preds = [pred.strip() for pred in decoded_preds] + decoded_labels = [[label.strip()] for label in decoded_labels] + all_preds.extend(decoded_preds) + all_labels.extend(decoded_labels) + + result = metric.compute(predictions=all_preds, references=all_labels) + return {"bleu": result["score"]} +``` + +{:else} + +為了從模型輸出到度量可以使用的文本,我們將使用 `tokenizer.batch_decode()` 方法。 我們只需要清理標籤中的所有 `-100`(標記器將自動對填充標記執行相同操作): + +```py +import numpy as np + + +def compute_metrics(eval_preds): + preds, labels = eval_preds + # In case the model returns more than the prediction logits + if isinstance(preds, tuple): + preds = preds[0] + + decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True) + + # Replace -100s in the labels as we can't decode them + labels = np.where(labels != -100, labels, tokenizer.pad_token_id) + decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True) + + # Some simple post-processing + decoded_preds = [pred.strip() for pred in decoded_preds] + decoded_labels = [[label.strip()] for label in decoded_labels] + + result = metric.compute(predictions=decoded_preds, references=decoded_labels) + return {"bleu": result["score"]} +``` + +{/if} + +現在這已經完成了,我們已經準備好微調我們的模型了! + +### 微調模型 + +第一步是登錄 Hugging Face,這樣您就可以將結果上傳到模型中心。有一個方便的功能可以幫助您在notebook中完成此操作: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +這將顯示一個小部件,您可以在其中輸入您的 Hugging Face 登錄憑據。 + +如果您不是在notebook上運行代碼,只需在終端中輸入以下行: + +```bash +huggingface-cli login +``` + +{#if fw === 'tf'} + +在我們開始之前,讓我們看看我們在沒有任何訓練的情況下從我們的模型中得到了什麼樣的結果: + +```py +print(compute_metrics()) +``` + +``` +{'bleu': 33.26983701454733} +``` + +一旦完成,我們就可以準備編譯和訓練模型所需的一切。 注意當使用 `tf.keras.mixed_precision.set_global_policy("mixed_float16")`時——這將告訴 Keras 使用 float16 進行訓練,這可以顯著提高支持它的 GPU(Nvidia 20xx/V100 或更高版本)的速度。 + +```python +from transformers import create_optimizer +from transformers.keras_callbacks import PushToHubCallback +import tensorflow as tf + +# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied +# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset, +# not the original Hugging Face Dataset, so its len() is already num_samples // batch_size. +num_epochs = 3 +num_train_steps = len(tf_train_dataset) * num_epochs + +optimizer, schedule = create_optimizer( + init_lr=5e-5, + num_warmup_steps=0, + num_train_steps=num_train_steps, + weight_decay_rate=0.01, +) +model.compile(optimizer=optimizer) + +# Train in mixed-precision float16 +tf.keras.mixed_precision.set_global_policy("mixed_float16") +``` + +接下來,我們定義一個 `PushToHubCallback` 以便在訓練期間將我們的模型上傳到 Hub,正如我們在 [第 2 節]((/course/chapter7/2)) 中看到的,然後我們只需擬合模型時添加該回調函數: + +```python +from transformers.keras_callbacks import PushToHubCallback + +callback = PushToHubCallback( + output_dir="marian-finetuned-kde4-en-to-fr", tokenizer=tokenizer +) + +model.fit( + tf_train_dataset, + validation_data=tf_eval_dataset, + callbacks=[callback], + epochs=num_epochs, +) +``` + +請注意,您可以使用 `hub_model_id` 參數指定要推送到的存儲庫的名稱(當您想把模型推送到指定的組織的時候,您也必須使用此參數)。 例如,當我們將模型推送到 [`huggingface-course` 組織](https://huggingface.co/huggingface-course) 時,我們添加了 `hub_model_id="huggingface-course/marian-finetuned-kde4-en- to-fr"` 到 `Seq2SeqTrainingArguments`。 默認情況下,使用的存儲庫將在您的命名空間中,並以您設置的輸出目錄命名,因此這裡將是 `"sgugger/marian-finetuned-kde4-en-to-fr"`。 + + + +💡如果您使用的輸出目錄已經存在,則它需要是您要推送到的存儲庫的本地克隆。如果不是,您將在定義您的名稱時會遇到錯誤,並且需要設置一個新名稱。 + + + +最後,讓我們看看訓練結束後我們的指標是什麼樣的: + +```py +print(compute_metrics()) +``` + +``` +{'bleu': 57.334066271545865} +``` + +在這個階段,您可以使用模型中心上的推理小部件來測試您的模型並與您的朋友分享。 您已經成功地微調了翻譯任務中的模型——恭喜! + +{:else} + +一旦完成,我們就可以定義我們的 `Seq2SeqTrainingArguments`。 與 `Trainer` 一樣,我們使用 `TrainingArguments` 的子類,其中包含更多可以設置的字段: + +```python +from transformers import Seq2SeqTrainingArguments + +args = Seq2SeqTrainingArguments( + f"marian-finetuned-kde4-en-to-fr", + evaluation_strategy="no", + save_strategy="epoch", + learning_rate=2e-5, + per_device_train_batch_size=32, + per_device_eval_batch_size=64, + weight_decay=0.01, + save_total_limit=3, + num_train_epochs=3, + predict_with_generate=True, + fp16=True, + push_to_hub=True, +) +``` + +除了通常的超參數(如學習率、訓練輪數、批次大小和一些權重衰減)之外,與我們在前幾節中看到的相比,這裡有一些變化: + +- 我們沒有設置任何定期評估,因為評估需要耗費一定的時間;我們只會在訓練開始之前和結束之後評估我們的模型一次。 +- 我們設置fp16=True,這可以加快支持fp16的 GPU 上的訓練速度。 +- 和上面我們討論的那樣,我們設置predict_with_generate=True +- 我們用push_to_hub=True在每個 epoch 結束時將模型上傳到 Hub。 + +請注意,您可以使用 `hub_model_id` 參數指定要推送到的存儲庫的名稱(當您想把模型推送到指定的組織的時候,您也必須使用此參數)。 例如,當我們將模型推送到 [`huggingface-course` 組織](https://huggingface.co/huggingface-course) 時,我們添加了 `hub_model_id="huggingface-course/marian-finetuned-kde4-en- to-fr"` 到 `Seq2SeqTrainingArguments`。 默認情況下,使用的存儲庫將在您的命名空間中,並以您設置的輸出目錄命名,因此這裡將是 `"sgugger/marian-finetuned-kde4-en-to-fr"`。 + + + +💡如果您使用的輸出目錄已經存在,則它需要是您要推送到的存儲庫的本地克隆。如果不是,您將在定義您的名稱時會遇到錯誤,並且需要設置一個新名稱。 + + + + +最後,我們需要將所有內容傳遞給 **Seq2SeqTrainer** : + +```python +from transformers import Seq2SeqTrainer + +trainer = Seq2SeqTrainer( + model, + args, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["validation"], + data_collator=data_collator, + tokenizer=tokenizer, + compute_metrics=compute_metrics, +) +``` + +在訓練之前,我們將首先查看我們的模型獲得的分數,以仔細檢查我們的微調沒有讓事情變得更糟。此命令需要一些時間,因此您可以在執行時喝杯咖啡: + +```python +trainer.evaluate(max_length=max_target_length) +``` + +```python out +{'eval_loss': 1.6964408159255981, + 'eval_bleu': 39.26865061007616, + 'eval_runtime': 965.8884, + 'eval_samples_per_second': 21.76, + 'eval_steps_per_second': 0.341} +``` + +BLEU的分數還不錯,這反映了我們的模型已經擅長將英語句子翻譯成法語句子。 + +接下來是訓練,這也需要一些時間: + +```python +trainer.train() +``` + +請注意,當訓練發生時,每次保存模型時(這裡是每個時期),它都會在後臺上傳到 Hub。這樣,如有必要,您將能夠在另一臺機器上繼續您的訓練。 + +訓練完成後,我們再次評估我們的模型——希望我們會看到 BLEU 分數有所改善! + +```py +trainer.evaluate(max_length=max_target_length) +``` + +```python out +{'eval_loss': 0.8558505773544312, + 'eval_bleu': 52.94161337775576, + 'eval_runtime': 714.2576, + 'eval_samples_per_second': 29.426, + 'eval_steps_per_second': 0.461, + 'epoch': 3.0} +``` + +這是近 14 點的改進,這很棒。 + +最後,我們使用 **push_to_hub()** 方法來確保我們上傳模型的最新版本。這 **Trainer** 還創建了一張包含所有評估結果的模型卡並上傳。此模型卡包含可幫助模型中心為推理演示選擇小部件的元數據。通常不需要做額外的更改,因為它可以從模型類中推斷出正確的小部件,但在這種情況下,相同的模型類可以用於所有類型的序列到序列問題,所以我們指定它是一個翻譯模型: + +```py +trainer.push_to_hub(tags="translation", commit_message="Training complete") +``` + +如果您想檢查命令執行的結果,此命令將返回它剛剛執行的提交的 URL,可以打開url進行檢查: + +```python out +'https://huggingface.co/sgugger/marian-finetuned-kde4-en-to-fr/commit/3601d621e3baae2bc63d3311452535f8f58f6ef3' +``` + +在此階段,您可以使用模型中心上的推理小部件來測試您的模型並與您的朋友分享。您已成功微調翻譯任務的模型 - 恭喜! + +如果您想更深入地瞭解訓練循環,我們現在將向您展示如何使用 🤗 Accelerate 做同樣的事情。 + +{/if} + +{#if fw === 'pt'} + +## 自定義訓練循環 + +現在讓我們看一下完整的訓練循環,以便您可以輕鬆自定義所需的部分。它看起來很像我們在[本章第二節](/course/chapter7/2)和[第三章第四小節](/course/chapter3/4)所做的。 + +### 準備訓練所需的一切 + +您已經多次看到所有這些,因此這一塊會簡略進行。首先我們將構建我們的數據集的**DataLoader** ,在將數據集設置為 **torch** 格式,我們就得到了 PyTorch 張量: + +```py +from torch.utils.data import DataLoader + +tokenized_datasets.set_format("torch") +train_dataloader = DataLoader( + tokenized_datasets["train"], + shuffle=True, + collate_fn=data_collator, + batch_size=8, +) +eval_dataloader = DataLoader( + tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8 +) +``` + +接下來我們重新實例化我們的模型,以確保我們不會繼續上一節的微調,而是再次從預訓練模型開始重新訓練: + +```py +model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint) +``` + +然後我們需要一個優化器: + +```py +from transformers import AdamW + +optimizer = AdamW(model.parameters(), lr=2e-5) +``` + +一旦我們擁有所有這些對象,我們就可以將它們發送到 `accelerator.prepare()` 方法。 請記住,如果您想在 Colab 筆記本訓練中使用TPU,則需要將所有這些代碼移動到訓練函數中,並且不應執行任何實例化“加速器”的對象。 + +```py +from accelerate import Accelerator + +accelerator = Accelerator() +model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare( + model, optimizer, train_dataloader, eval_dataloader +) +``` + +現在我們已經發送了我們的 **train_dataloader** 到 **accelerator.prepare()** ,我們可以使用它的長度來計算訓練步驟的數量。請記住,我們應該始終在準備好數據加載器後執行此操作,因為該方法會更改 **DataLoader** .我們使用從學習率衰減到 0 的經典線性學習率調度: + +```py +from transformers import get_scheduler + +num_train_epochs = 3 +num_update_steps_per_epoch = len(train_dataloader) +num_training_steps = num_train_epochs * num_update_steps_per_epoch + +lr_scheduler = get_scheduler( + "linear", + optimizer=optimizer, + num_warmup_steps=0, + num_training_steps=num_training_steps, +) +``` + +最後,要將我們的模型推送到 Hub,我們需要創建一個 **Repository** 工作文件夾中的對象。如果您尚未登錄,請先登錄 Hugging Face。我們將從我們想要為模型提供的模型 ID 中確定存儲庫名稱(您可以自由地用自己的選擇替換 **repo_name** ;它需要包含您的用戶名,可以使用**get_full_repo_name()**函數的查看目前的repo_name): + +```py +from huggingface_hub import Repository, get_full_repo_name + +model_name = "marian-finetuned-kde4-en-to-fr-accelerate" +repo_name = get_full_repo_name(model_name) +repo_name +``` + +```python out +'sgugger/marian-finetuned-kde4-en-to-fr-accelerate' +``` + +然後我們可以在本地文件夾中克隆該存儲庫。如果它已經存在,這個本地文件夾應該是我們正在使用的存儲庫的克隆: + +```py +output_dir = "marian-finetuned-kde4-en-to-fr-accelerate" +repo = Repository(output_dir, clone_from=repo_name) +``` + +我們現在可以通過調用 **repo.push_to_hub()** 方法上傳我們保存的任何內容 **output_dir** 。這將幫助我們在每個 epoch 結束時上傳過程中的模型。 + +### 訓練循環 + +我們現在準備編寫完整的訓練循環。為了簡化它的評估部分,我們定義了這個 **postprocess()** 函數接收預測結果和正確標籤並將它們轉換為我們 **metric** 對象所需要的字符串列表: + +```py +def postprocess(predictions, labels): + predictions = predictions.cpu().numpy() + labels = labels.cpu().numpy() + + decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) + + # Replace -100 in the labels as we can't decode them. + labels = np.where(labels != -100, labels, tokenizer.pad_token_id) + decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True) + + # Some simple post-processing + decoded_preds = [pred.strip() for pred in decoded_preds] + decoded_labels = [[label.strip()] for label in decoded_labels] + return decoded_preds, decoded_labels +``` + +訓練循環看起來和[本章第二節](/course/chapter7/2)與[第三章](/course/chapter3)很像,在評估部分有一些不同 - 所以讓我們專注於這一點! + +首先要注意的是我們使用 `generate()` 方法來計算預測,但這是我們基礎模型上的一個方法,而不是包裝模型🤗 Accelerate 在 `prepare()` 方法中創建。 這就是為什麼我們先解包模型,然後調用這個方法。 + +第二件事是,就像[token 分類](/course/chapter7/2),兩個進程可能將輸入和標籤填充為不同的形狀,因此我們在調用 **gather()** 方法之前使用 **accelerator.pad_across_processes()** 使預測和標籤具有相同的形狀。如果我們不這樣做,評估要麼出錯,要麼永遠在阻塞。 + +```py +from tqdm.auto import tqdm +import torch + +progress_bar = tqdm(range(num_training_steps)) + +for epoch in range(num_train_epochs): + # Training + model.train() + for batch in train_dataloader: + outputs = model(**batch) + loss = outputs.loss + accelerator.backward(loss) + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) + + # Evaluation + model.eval() + for batch in tqdm(eval_dataloader): + with torch.no_grad(): + generated_tokens = accelerator.unwrap_model(model).generate( + batch["input_ids"], + attention_mask=batch["attention_mask"], + max_length=128, + ) + labels = batch["labels"] + + # Necessary to pad predictions and labels for being gathered + generated_tokens = accelerator.pad_across_processes( + generated_tokens, dim=1, pad_index=tokenizer.pad_token_id + ) + labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100) + + predictions_gathered = accelerator.gather(generated_tokens) + labels_gathered = accelerator.gather(labels) + + decoded_preds, decoded_labels = postprocess(predictions_gathered, labels_gathered) + metric.add_batch(predictions=decoded_preds, references=decoded_labels) + + results = metric.compute() + print(f"epoch {epoch}, BLEU score: {results['score']:.2f}") + + # Save and upload + accelerator.wait_for_everyone() + unwrapped_model = accelerator.unwrap_model(model) + unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) + if accelerator.is_main_process: + tokenizer.save_pretrained(output_dir) + repo.push_to_hub( + commit_message=f"Training in progress epoch {epoch}", blocking=False + ) +``` + +```python out +epoch 0, BLEU score: 53.47 +epoch 1, BLEU score: 54.24 +epoch 2, BLEU score: 54.44 +``` + +完成此操作後,您應該有一個模型,其結果與使用 `Seq2SeqTrainer` 訓練的模型非常相似。 您可以在 [*huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate*](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate)查看訓練完的結果。 如果您想測試對訓練循環的任何調整,您可以通過編輯上面顯示的代碼直接實現它們! + +{/if} + +## 使用微調後的模型 + +我們已經向您展示瞭如何將我們在模型中心微調的模型與推理小部件一起使用。 要在“管道”中本地使用它,我們只需要指定正確的模型標識符: + +```py +from transformers import pipeline + +# Replace this with your own checkpoint +model_checkpoint = "huggingface-course/marian-finetuned-kde4-en-to-fr" +translator = pipeline("translation", model=model_checkpoint) +translator("Default to expanded threads") +``` + +```python out +[{'translation_text': 'Par défaut, développer les fils de discussion'}] +``` + +正如預期的那樣,我們的預訓練模型將其知識適應了我們對其進行微調的語料庫,而不是單獨留下英文單詞“threads”,而是將其翻譯成法語官方版本。 “”的翻譯也是一樣的: + +```py +translator( + "Unable to import %1 using the OFX importer plugin. This file is not the correct format." +) +``` + +```python out +[{'translation_text': "Impossible d'importer %1 en utilisant le module externe d'importation OFX. Ce fichier n'est pas le bon format."}] +``` + +風格適應的另一個很好的例子! + + + +✏️ **輪到你了!** “電子郵件”這個詞在模型返回了什麼? + + diff --git a/chapters/zh-TW/chapter7/5.mdx b/chapters/zh-TW/chapter7/5.mdx new file mode 100644 index 000000000..eeab998ed --- /dev/null +++ b/chapters/zh-TW/chapter7/5.mdx @@ -0,0 +1,1047 @@ + + +# 提取文本摘要 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + + +在本節中,我們將看看如何使用 Transformer 模型將長文檔壓縮為摘要,這項任務稱為文本摘要.這是最具挑戰性的 NLP 任務之一,因為它需要一系列能力,例如理解長篇文章和生成能夠捕捉文檔中主要主題的連貫文本。但是,如果做得好,文本摘要是一種強大的工具,可以減輕領域專家詳細閱讀長文檔的負擔,從而加快各種業務流程。 + + + +儘管在[Hugging Face Hub](https://huggingface.co/models?pipeline_tag=summarization=downloads)上已經存在各種微調模型用於文本摘要,幾乎所有這些都只適用於英文文檔。因此,為了在本節中添加一些變化,我們將為英語和西班牙語訓練一個雙語模型。在本節結束時,您將有一個可以總結客戶評論的[模型](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es)。 + + + + +如下所示:正如我們將看到的,這些摘要很簡潔,因為它們是從客戶在產品評論中提供的標題中學到的。讓我們首先為這項任務準備一個合適的雙語語料庫。 + +## 準備多語言語料庫 + +我們將使用[多語言亞馬遜評論語料庫](https://huggingface.co/datasets/amazon_reviews_multi)創建我們的雙語摘要器。該語料庫由六種語言的亞馬遜產品評論組成,通常用於對多語言分類器進行基準測試。然而,由於每條評論都附有一個簡短的標題,我們可以使用標題作為我們模型學習的目標摘要!首先,讓我們從 Hugging Face Hub 下載英語和西班牙語子集: + +```python +from datasets import load_dataset + +spanish_dataset = load_dataset("amazon_reviews_multi", "es") +english_dataset = load_dataset("amazon_reviews_multi", "en") +english_dataset +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'], + num_rows: 200000 + }) + validation: Dataset({ + features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'], + num_rows: 5000 + }) + test: Dataset({ + features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'], + num_rows: 5000 + }) +}) +``` + +如您所見,對於每種語言,都有 200,000 條評論 **train** 拆分,每個評論有 5,000 條評論 **validation** 和 **test** 分裂。我們感興趣的評論信息包含在 **review_body** 和 **review_title** 列。讓我們通過創建一個簡單的函數來查看一些示例,該函數使用我們在[第五章](/course/chapter5)學到過: + +```python +def show_samples(dataset, num_samples=3, seed=42): + sample = dataset["train"].shuffle(seed=seed).select(range(num_samples)) + for example in sample: + print(f"\n'>> Title: {example['review_title']}'") + print(f"'>> Review: {example['review_body']}'") + + +show_samples(english_dataset) +``` + +```python out +'>> Title: Worked in front position, not rear' +'>> Review: 3 stars because these are not rear brakes as stated in the item description. At least the mount adapter only worked on the front fork of the bike that I got it for.' + +'>> Title: meh' +'>> Review: Does it’s job and it’s gorgeous but mine is falling apart, I had to basically put it together again with hot glue' + +'>> Title: Can\'t beat these for the money' +'>> Review: Bought this for handling miscellaneous aircraft parts and hanger "stuff" that I needed to organize; it really fit the bill. The unit arrived quickly, was well packaged and arrived intact (always a good sign). There are five wall mounts-- three on the top and two on the bottom. I wanted to mount it on the wall, so all I had to do was to remove the top two layers of plastic drawers, as well as the bottom corner drawers, place it when I wanted and mark it; I then used some of the new plastic screw in wall anchors (the 50 pound variety) and it easily mounted to the wall. Some have remarked that they wanted dividers for the drawers, and that they made those. Good idea. My application was that I needed something that I can see the contents at about eye level, so I wanted the fuller-sized drawers. I also like that these are the new plastic that doesn\'t get brittle and split like my older plastic drawers did. I like the all-plastic construction. It\'s heavy duty enough to hold metal parts, but being made of plastic it\'s not as heavy as a metal frame, so you can easily mount it to the wall and still load it up with heavy stuff, or light stuff. No problem there. For the money, you can\'t beat it. Best one of these I\'ve bought to date-- and I\'ve been using some version of these for over forty years.' +``` + + + +✏️ **試試看!** 更改 `Dataset.shuffle()` 命令中的隨機種子以探索語料庫中的其他評論。 如果您是說西班牙語的人,請查看 `spanish_dataset` 中的一些評論,看看標題是否也像合理的摘要。 + + + +此示例顯示了人們通常在網上找到的評論的多樣性,從正面到負面(以及介於兩者之間的所有內容!)。儘管標題為“meh”的示例信息量不大,但其他標題看起來像是對評論本身的體面總結。在單個 GPU 上訓練所有 400,000 條評論的摘要模型將花費太長時間,因此我們將專注於為單個產品領域生成摘要。為了瞭解我們可以選擇哪些域,讓我們將 **english_dataset** 轉換到 **pandas.DataFrame** 並計算每個產品類別的評論數量: + +```python +english_dataset.set_format("pandas") +english_df = english_dataset["train"][:] +# Show counts for top 20 products +english_df["product_category"].value_counts()[:20] +``` + +```python out +home 17679 +apparel 15951 +wireless 15717 +other 13418 +beauty 12091 +drugstore 11730 +kitchen 10382 +toy 8745 +sports 8277 +automotive 7506 +lawn_and_garden 7327 +home_improvement 7136 +pet_products 7082 +digital_ebook_purchase 6749 +pc 6401 +electronics 6186 +office_product 5521 +shoes 5197 +grocery 4730 +book 3756 +Name: product_category, dtype: int64 +``` + +英語數據集中最受歡迎的產品是家居用品、服裝和無線電子產品。不過,為了堅持亞馬遜的主題,讓我們專注於總結書籍的評論——畢竟,這是亞馬遜這家公司成立的基礎!我們可以看到兩個符合要求的產品類別( **book** 和 **digital_ebook_purchase** ),所以讓我們為這些產品過濾兩種語言的數據集。正如我們在[第五章](/course/chapter5)學到的, 這 **Dataset.filter()** 函數允許我們非常有效地對數據集進行切片,因此我們可以定義一個簡單的函數來執行此操作: + +```python +def filter_books(example): + return ( + example["product_category"] == "book" + or example["product_category"] == "digital_ebook_purchase" + ) +``` + +現在,當我們將此函數應用於 **english_dataset** 和 **spanish_dataset** ,結果將只包含涉及書籍類別的那些行。在應用過濾器之前,讓我們將**english_dataset**的格式從 **pandas** 切換回到 **arrow** : + +```python +english_dataset.reset_format() +``` + +然後我們可以應用過濾器功能,作為健全性檢查,讓我們檢查評論樣本,看看它們是否確實與書籍有關: + +```python +spanish_books = spanish_dataset.filter(filter_books) +english_books = english_dataset.filter(filter_books) +show_samples(english_books) +``` + +```python out +'>> Title: I\'m dissapointed.' +'>> Review: I guess I had higher expectations for this book from the reviews. I really thought I\'d at least like it. The plot idea was great. I loved Ash but, it just didnt go anywhere. Most of the book was about their radio show and talking to callers. I wanted the author to dig deeper so we could really get to know the characters. All we know about Grace is that she is attractive looking, Latino and is kind of a brat. I\'m dissapointed.' + +'>> Title: Good art, good price, poor design' +'>> Review: I had gotten the DC Vintage calendar the past two years, but it was on backorder forever this year and I saw they had shrunk the dimensions for no good reason. This one has good art choices but the design has the fold going through the picture, so it\'s less aesthetically pleasing, especially if you want to keep a picture to hang. For the price, a good calendar' + +'>> Title: Helpful' +'>> Review: Nearly all the tips useful and. I consider myself an intermediate to advanced user of OneNote. I would highly recommend.' +``` + +好的,我們可以看到評論並不是嚴格意義上的書籍,可能是指日曆和 OneNote 等電子應用程序等內容。儘管如此,該領域似乎適合訓練摘要模型。在我們查看適合此任務的各種模型之前,我們還有最後一點數據準備要做:將英語和西班牙語評論合併為一個 **DatasetDict** 目的。 🤗 Datasets 提供了一個方便的 **concatenate_datasets()** 函數(顧名思義)合併 **Dataset** 對象。因此,為了創建我們的雙語數據集,我們將遍歷每個拆分,連接該拆分的數據集,並打亂結果以確保我們的模型不會過度擬合單一語言: + +```python +from datasets import concatenate_datasets, DatasetDict + +books_dataset = DatasetDict() + +for split in english_books.keys(): + books_dataset[split] = concatenate_datasets( + [english_books[split], spanish_books[split]] + ) + books_dataset[split] = books_dataset[split].shuffle(seed=42) + +# Peek at a few examples +show_samples(books_dataset) +``` + +```python out +'>> Title: Easy to follow!!!!' +'>> Review: I loved The dash diet weight loss Solution. Never hungry. I would recommend this diet. Also the menus are well rounded. Try it. Has lots of the information need thanks.' + +'>> Title: PARCIALMENTE DAÑADO' +'>> Review: Me llegó el día que tocaba, junto a otros libros que pedí, pero la caja llegó en mal estado lo cual dañó las esquinas de los libros porque venían sin protección (forro).' + +'>> Title: no lo he podido descargar' +'>> Review: igual que el anterior' +``` + +這當然看起來像是英語和西班牙語評論的混合!現在我們有了一個訓練語料庫,最後要檢查的一件事是評論中單詞的分佈及其標題。這對於摘要任務尤其重要,其中數據中的簡短參考摘要會使模型偏向於僅在生成的摘要中輸出一兩個單詞。下面的圖顯示了單詞分佈,我們可以看到有些標題嚴重偏向於 1-2 個單詞: + +
+Word count distributions for the review titles and texts. + +
+ +為了解決這個問題,我們將過濾掉標題非常短的示例,以便我們的模型可以生成更有趣的摘要。由於我們正在處理英文和西班牙文文本,因此我們可以使用粗略的啟發式方法在空白處拆分標題,然後使用我們可信賴的 **Dataset.filter()** 方法如下: + +```python +books_dataset = books_dataset.filter(lambda x: len(x["review_title"].split()) > 2) +``` + +現在我們已經準備好了我們的語料庫,讓我們來看看一些可以對其進行微調的可能的 Transformer 模型! + +## 文本摘要模型 + +如果你仔細想想,文本摘要是一種類似於機器翻譯的任務:我們有一個像評論這樣的文本正文,我們希望將其“翻譯”成一個較短的版本,以捕捉輸入的顯著特徵。因此,大多數用於文本摘要的 Transformer 模型採用了我們在[第一章](/course/chapter1)遇到的編碼器-解碼器架構。儘管有一些例外,例如 GPT 系列模型,它們在few-shot(少量微調)之後也可以提取摘要。下表列出了一些流行的預訓練模型,可以對其進行微調以進行彙總。 + +| Transformer 模型 | 描述 | 多種語言? | +| :---------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------: | +| [GPT-2](https://huggingface.co/gpt2-xl) | 雖然訓練為自迴歸語言模型,但您可以通過在輸入文本末尾附加“TL;DR”來使 GPT-2 生成摘要。 | ❌ | +| [PEGASUS](https://huggingface.co/google/pegasus-large) | 在預訓練是的目標是來預測多句子文本中的屏蔽句子。 這個預訓練目標比普通語言建模更接近文本摘要,並且在流行的基準測試中得分很高。 | ❌ | +| [T5](https://huggingface.co/t5-base) | 通用的 Transformer 架構,在文本到文本的框架中制定所有任務; 例如,模型文本摘要的輸入格式是`summarize: ARTICLE`。 | ❌ | +| [mT5](https://huggingface.co/google/mt5-base) | T5 的多語言版本,在多語言 Common Crawl 語料庫 (mC4) 上進行預訓練,涵蓋 101 種語言。 | ✅ | +| [BART](https://huggingface.co/facebook/bart-base) | 一種新穎的 Transformer 架構,其中包含經過訓練的編碼器和解碼器堆棧,以重建被破壞的輸入,結合了 BERT 和 GPT-2 的預訓練方案。 | ❌ | +| [mBART-50](https://huggingface.co/facebook/mbart-large-50) | BART 的多語言版本,預訓練了 50 種語言。 | ✅ | + +從此表中可以看出,大多數用於摘要的 Transformer 模型(以及大多數 NLP 任務)都是單語的。如果您的任務是使用“有大量語料庫”的語言(如英語或德語),這很好,但對於世界各地正在使用的數千種其他語言,則不然。幸運的是,有一類多語言 Transformer 模型,如 mT5 和 mBART,可以解決問題。這些模型是使用語言建模進行預訓練的,但有一點不同:它們不是在一種語言的語料庫上訓練,而是同時在 50 多種語言的文本上進行聯合訓練! + +我們將使用 mT5,這是一種基於 T5 的有趣架構,在文本到文本框架中進行了預訓練。在 T5 中,每個 NLP 任務都是根據提示前綴來制定的,例如 **summarize:** 這使模型使生成的文本適應提示。如下圖所示,這讓 T5 變得非常通用,因為你可以用一個模型解決很多任務! + + +
+Different tasks performed by the T5 architecture. + +
+ +mT5 不使用前綴,但具有 T5 的大部分功能,並且具有多語言的優勢。現在我們已經選擇了一個模型,讓我們來看看準備我們的訓練數據。 + + + +✏️ **試試看!** 完成本節後,通過使用相同的技術對 mBART 進行微調,看看 mT5 與 mBART 相比有多好。 對於獎勵積分,您還可以嘗試僅在英文評論上微調 T5。 由於 T5 需要一個特殊的前綴提示,因此您需要在下面的預處理步驟中將“summarize:”添加到輸入示例中。 + + + +## 預處理數據 + + + +我們的下一個任務是對我們的評論及其標題進行標記和編碼。像往常一樣,我們首先加載與預訓練模型檢查點相關的標記器。我們將使用 **mt5-small** 作為我們的檢查點,以便我們可以在合理的時間內微調模型: + +```python +from transformers import AutoTokenizer + +model_checkpoint = "google/mt5-small" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +``` + + + +💡在 NLP 項目的早期階段,一個好的做法是在小樣本數據上訓練一類“小”模型。這使您可以更快地調試和迭代端到端工作流。一旦您對結果充滿信心,您始終可以通過簡單地更改模型檢查點來在大規模數據上訓練模型! + + + +讓我們在一個小例子上測試 mT5 標記器: + +```python +inputs = tokenizer("I loved reading the Hunger Games!") +inputs +``` + +```python out +{'input_ids': [336, 259, 28387, 11807, 287, 62893, 295, 12507, 1], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]} +``` + +在這裡我們可以看到我們在[第三章](/course/chapter3)第一次微調實驗中遇到的熟悉的 **input_ids** 和 **attention_mask** .讓我們用分詞器解碼這些輸入 ID ,可以**convert_ids_to_tokens()** 函數來查看我們正在處理什麼樣的標記器: + +```python +tokenizer.convert_ids_to_tokens(inputs.input_ids) +``` + +```python out +['▁I', '▁', 'loved', '▁reading', '▁the', '▁Hung', 'er', '▁Games', ''] +``` + +特殊的 Unicode 字符 `▁` 和序列結束標記 `` 表明我們正在處理 SentencePiece 分詞器,它基於在[第六章](/course/chapter6)中討論的Unigram分詞算法. Unigram 對多語言語料庫特別有用,因為它允許 SentencePiece 不知道重音、標點符號以及許多語言(如日語)沒有空格字符。 + +為了標記我們的語料庫,我們必須處理與摘要相關的細節:因為我們的標籤也是文本,它們可能會超過模型的最大上下文大小。這意味著我們需要對評論及其標題進行截斷,以確保我們不會將過長的輸入傳遞給我們的模型。 🤗 Transformers 中的分詞器提供了一個漂亮的 **as_target_tokenizer()** 函數,它允許您並行分詞並標記標籤的函數。這通常是使用預處理函數內的上下文管理器完成的,該函數首先對輸入進行編碼,然後將標籤編碼為單獨的列。以下是 mT5 的此函數的示例: + +```python +max_input_length = 512 +max_target_length = 30 + + +def preprocess_function(examples): + model_inputs = tokenizer( + examples["review_body"], max_length=max_input_length, truncation=True + ) + # Set up the tokenizer for targets + with tokenizer.as_target_tokenizer(): + labels = tokenizer( + examples["review_title"], max_length=max_target_length, truncation=True + ) + + model_inputs["labels"] = labels["input_ids"] + return model_inputs +``` + +讓我們通過這段代碼來了解發生了什麼。我們做的第一件事是定義值 **max_input_length** 和 **max_target_length** ,它為我們的評論和標題的長度設置了上限。由於評論正文通常比標題大得多,我們相應地調整了這些值。然後,在 **preprocess_function()** 我們可以看到評論首先被標記化,然後是標題在 **as_target_tokenizer()** 函數里也做了相同的處理. + +有了 `preprocess_function()`,我們在整個課程中廣泛使用的方便的 `Dataset.map()` 函數來標記整個語料庫是一件簡單的事情: + +```python +tokenized_datasets = books_dataset.map(preprocess_function, batched=True) +``` + +既然語料庫已經預處理完畢,我們來看看一些常用的摘要指標。正如我們將看到的,在衡量機器生成的文本的質量方面沒有靈丹妙藥。 + + + +💡 你可能已經注意到我們在上面的 `Dataset.map()` 函數中使用了 `batched=True`。 這會以 1,000 個(默認)為單位對示例進行編碼,並允許您利用 🤗 Transformers 中快速標記器的多線程功能。 在可能的情況下,嘗試使用 `batched=True` 來加速您的預處理! + + + + +## 文本摘要的指標 + + + +與我們在本課程中涵蓋的大多數其他任務相比,衡量文本生成任務(如摘要或翻譯)的性能並不那麼簡單。例如,對於“我喜歡閱讀飢餓遊戲”這樣的評論,有多個有效摘要,例如“我喜歡飢餓遊戲”或“飢餓遊戲是一本好書”。顯然,在生成的摘要和標籤之間應用某種精確匹配並不是一個好的解決方案——即使是人類在這樣的指標下也會表現不佳,因為我們都有自己的寫作風格。 + +總而言之,最常用的指標之一是[ROUGE 分數](https://en.wikipedia.org/wiki/ROUGE_(metric))(Recall-Oriented Understudy for Gisting Evaluation 的縮寫)。該指標背後的基本思想是將生成的摘要與一組通常由人類創建的參考摘要進行比較。為了更精確,假設我們要比較以下兩個摘要: + +```python +generated_summary = "I absolutely loved reading the Hunger Games" +reference_summary = "I loved reading the Hunger Games" +``` +比較它們的一種方法是計算重疊單詞的數量,在這種情況下為 6。但是,這有點粗糙,因此 ROUGE 是基於計算計算重疊的 _precision_ 和 _recall_ 分數。。 + + + +🙋 如果這是您第一次聽說精確率和召回率,請不要擔心——我們將一起通過一些明確的示例來說明一切。 這些指標通常在分類任務中遇到,因此如果您想了解在該上下文中如何定義精確度和召回率,我們建議查看 scikit-learn [指南](https://scikit-learn.org/stable /auto_examples/model_selection/plot_precision_recall.html)。 + + + +對於 ROUGE,recall 衡量生成的參考摘要包含了多少參考摘要。如果我們只是比較單詞,recall可以根據以下公式計算: + +$$ \mathrm{Recall} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, reference\, summary}} $$ + +對於我們上面的簡單例子,這個公式給出了 6/6 = 1 的完美召回率;即,參考摘要中的所有單詞都已由模型生成。這聽起來可能很棒,但想象一下,如果我們生成的摘要是“我真的很喜歡整晚閱讀飢餓遊戲”。這也將有完美的recall,但可以說是一個更糟糕的總結,因為它很冗長。為了處理這些場景,我們還計算了pecision,它在 ROUGE 上下文中衡量生成的摘要中有多少是相關的: + +$$ \mathrm{Precision} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, generated\, summary}} $$ + +將此應用到我們的詳細摘要中會得到 6/10 = 0.6 的精度,這比我們較短的摘要獲得的 6/7 = 0.86 的精度要差得多。在實踐中,通常計算精度和召回率,然後報告 F1-score(精度和召回率的調和平均值)。我們可以在 🤗 Datasets 中通過安裝 **rouge_score** 包來計算他們: + +```py +!pip install rouge_score +``` + +然後按如下方式加載 ROUGE 指標: + +```python +from datasets import load_metric + +rouge_score = load_metric("rouge") +``` + +然後我們可以使用 **rouge_score.compute()** 一次性計算所有指標的函數: + +```python +scores = rouge_score.compute( + predictions=[generated_summary], references=[reference_summary] +) +scores +``` + +```python out +{'rouge1': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)), + 'rouge2': AggregateScore(low=Score(precision=0.67, recall=0.8, fmeasure=0.73), mid=Score(precision=0.67, recall=0.8, fmeasure=0.73), high=Score(precision=0.67, recall=0.8, fmeasure=0.73)), + 'rougeL': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)), + 'rougeLsum': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92))} +``` + +哇,那個輸出中有很多信息——這都是什麼意思?首先,🤗 Datasets實際上計算了精度、召回率和 F1 分數的置信區間;這些是你可以在這裡看到的 **low** , **mid** , 和 **high** 屬性。此外,🤗 Datasets在比較生成摘要和參考摘要時,會根據不同類型的文本粒度計算各種 ROUGE 分數。這 **rouge1** 變體是一元組的重疊——這只是表達單詞重疊的一種奇特方式,這正是我們上面討論的度量標準。為了驗證這一點,讓我們輸出 **mid** 的數值: + +```python +scores["rouge1"].mid +``` + +```python out +Score(precision=0.86, recall=1.0, fmeasure=0.92) +``` +太好了,準確率和召回率匹配了!那麼其他的 ROUGE 分數呢? **rouge2** 測量二元組之間的重疊(想想單詞對的重疊),而 **rougeL** 和 **rougeLsum** 通過在生成的和參考摘要中查找最長的公共子串來測量最長的單詞匹配序列。中的“總和” **rougeLsum** 指的是這個指標是在整個摘要上計算的,而 **rougeL** 計算為單個句子的平均值。 + + + + ✏️ **試試看!** 創建您自己的生成和參考摘要示例,並查看生成的 ROUGE 分數是否與基於精確度和召回率公式的手動計算一致。 對於附加分,將文本拆分為二元組並比較“rouge2”指標的精度和召回率。 + + + +我們將使用這些 ROUGE 分數來跟蹤我們模型的性能,但在此之前,讓我們做每個優秀的 NLP 從業者都應該做的事情:創建一個強大而簡單的baseline! + +### 創建強大的baseline + +文本摘要的一個常見基線是簡單地取一篇文章的前三個句子,通常稱為 _lead-3_ 基線。 我們可以使用句號(英文使用.)來跟蹤句子邊界,但這在"U.S." or "U.N."之類的首字母縮略詞上會失敗。所以我們將使用 `nltk` 庫,它包含一個更好的算法來處理這些情況。 您可以使用 `pip` 安裝軟件包,如下所示: + +```python +!pip install nltk +``` + +然後下載標點規則: + +```python +import nltk + +nltk.download("punkt") +``` +接下來,我們從 `nltk` 導入句子標記器並創建一個簡單的函數來提取評論中的前三個句子。 文本摘要的約定是用換行符分隔每個摘要,因此我們也將其包含在內並在訓練示例上對其進行測試: + +```python +from nltk.tokenize import sent_tokenize + + +def three_sentence_summary(text): + return "\n".join(sent_tokenize(text)[:3]) + + +print(three_sentence_summary(books_dataset["train"][1]["review_body"])) +``` + +```python out +'I grew up reading Koontz, and years ago, I stopped,convinced i had "outgrown" him.' +'Still,when a friend was looking for something suspenseful too read, I suggested Koontz.' +'She found Strangers.' +``` + +這似乎有效,所以讓我們現在實現一個函數,從數據集中提取這些“摘要”並計算baseline的 ROUGE 分數: + +```python +def evaluate_baseline(dataset, metric): + summaries = [three_sentence_summary(text) for text in dataset["review_body"]] + return metric.compute(predictions=summaries, references=dataset["review_title"]) +``` + +然後我們可以使用這個函數來計算驗證集上的 ROUGE 分數,並使用 Pandas 對它們進行一些美化: + +```python +import pandas as pd + +score = evaluate_baseline(books_dataset["validation"], rouge_score) +rouge_names = ["rouge1", "rouge2", "rougeL", "rougeLsum"] +rouge_dict = dict((rn, round(score[rn].mid.fmeasure * 100, 2)) for rn in rouge_names) +rouge_dict +``` + +```python out +{'rouge1': 16.74, 'rouge2': 8.83, 'rougeL': 15.6, 'rougeLsum': 15.96} +``` + +我們可以看到`rouge2`的分數明顯低於其他; 這可能反映了這樣一個事實,即評論標題通常很簡潔,因此lead-3 baseline過於冗長。 現在我們有了一個很好的基準,讓我們將注意力轉向微調 mT5! + +{#if fw === 'pt'} + +## 使用 `Trainer` API微調mT5 + +微調模型以進行提取摘要與我們在本章中介紹的其他任務非常相似。 我們需要做的第一件事是從`mt5-small`檢查點加載預訓練模型。 由於摘要提取是一個序列到序列的任務,我們可以使用 AutoModelForSeq2SeqLM 類加載模型,該類會自動下載並緩存權重: + +```python +from transformers import AutoModelForSeq2SeqLM + +model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint) +``` + +{:else} + +## 使用 `Keras` API微調mT5 + +微調模型以進行提取摘要與我們在本章中介紹的其他任務非常相似。 我們需要做的第一件事是從`mt5-small`檢查點加載預訓練模型。 由於摘要提取是一個序列到序列的任務,我們可以使用 AutoModelForSeq2SeqLM 類加載模型,該類會自動下載並緩存權重: + +```python +from transformers import TFAutoModelForSeq2SeqLM + +model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint) +``` + +{/if} + + + +💡 If you're wondering why you don't see any warnings about fine-tuning the model on a downstream task, that's because for sequence-to-sequence tasks we keep all the weights of the network. Compare this to our text classification model in [Chapter 3](/course/chapter3), where the head of the pretrained model was replaced with a randomly initialized network. +💡 如果您想知道為什麼在下游任務中沒有看到任何關於微調模型的警告,那是因為對於序列到序列的任務,我們保留了網絡的所有權重。與我們在[第三章] (/course/chapter3)中的文本分類模型進行比較,文本分類模型預訓練模型的頭部被隨機初始化的網絡替換。 + + + +我們需要做的下一件事是登錄 Hugging Face Hub。如果您在notebook中運行此代碼,則可以使用以下實用程序函數執行此操作: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +這將顯示一個小部件,您可以在其中輸入您的憑據。或者,您可以在終端中運行此命令並在那裡登錄: + +``` +huggingface-cli login +``` + +{#if fw === 'pt'} + +我們需要生成摘要以便在訓練期間計算 ROUGE 分數。幸運的是,🤗 Transformers 提供了專用的 `Seq2SeqTrainingArguments` 和 `Seq2SeqTrainer` 類,可以自動為我們完成這項工作! 為了瞭解它是如何工作的,讓我們首先為我們的實驗定義超參數和其他參數: + +```python +from transformers import Seq2SeqTrainingArguments + +batch_size = 8 +num_train_epochs = 8 +# Show the training loss with every epoch +logging_steps = len(tokenized_datasets["train"]) // batch_size +model_name = model_checkpoint.split("/")[-1] + +args = Seq2SeqTrainingArguments( + output_dir=f"{model_name}-finetuned-amazon-en-es", + evaluation_strategy="epoch", + learning_rate=5.6e-5, + per_device_train_batch_size=batch_size, + per_device_eval_batch_size=batch_size, + weight_decay=0.01, + save_total_limit=3, + num_train_epochs=num_train_epochs, + predict_with_generate=True, + logging_steps=logging_steps, + push_to_hub=True, +) +``` + +在這裡, **predict_with_generate** 參數已設置為True表明我們應該在評估期間生成摘要,以便我們可以計算每個時期的 ROUGE 分數。正如在[第一章](/course/chapter1)所討論的,解碼器通過逐個預測令牌來執行推理,這是由模型的 **generate()** 方法實現的。設置 **predict_with_generate=True** 告訴 **Seq2SeqTrainer** 使用該方法進行評估。我們還調整了一些默認的超參數,例如學習率、epoch數和權重衰減,並且我們設置了 **save_total_limit** 訓練期間最多隻保存 3 個檢查點的選項——這是因為即使是 mT5 的“small”版本也使用大約 1 GB 的硬盤空間,我們可以通過限制我們保存的副本數量來節省一點空間。 + +`push_to_hub=True` 參數將允許我們在訓練後將模型推送到 Hub; 您將在`output_dir`定義的位置中的用戶配置文件下找到存儲庫。 請注意,您可以使用 `hub_model_id` 參數指定要推送到的存儲庫的名稱(特別是當您想要推送到組織時,您必須使用此參數)。 例如,當我們將模型推送到 [`huggingface-course` 組織](https://huggingface.co/huggingface-course) 時,我們添加了`hub_model_id="huggingface-course/mt5-finetuned-amazon-en-es"` 到 `Seq2SeqTrainingArguments`。 + +我們需要做的下一件事是為訓練器提供一個“compute_metrics()”函數,以便我們可以在訓練期間評估我們的模型。 總結起來,這比簡單地在模型的預測上調用 `rouge_score.compute()` 更復雜一些,因為我們需要在計算 ROUGE 分數之前將輸出和標籤解碼為文本。 下面的函數正是這樣做的,並且還利用 `nltk` 中的 `sent_tokenize()` 函數來用換行符分隔摘要語句: + +```python +import numpy as np + + +def compute_metrics(eval_pred): + predictions, labels = eval_pred + # Decode generated summaries into text + decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) + # Replace -100 in the labels as we can't decode them + labels = np.where(labels != -100, labels, tokenizer.pad_token_id) + # Decode reference summaries into text + decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True) + # ROUGE expects a newline after each sentence + decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds] + decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels] + # Compute ROUGE scores + result = rouge_score.compute( + predictions=decoded_preds, references=decoded_labels, use_stemmer=True + ) + # Extract the median scores + result = {key: value.mid.fmeasure * 100 for key, value in result.items()} + return {k: round(v, 4) for k, v in result.items()} +``` + +{/if} + +接下來,我們需要為我們的序列到序列任務定義一個數據整理器。由於 mT5 是一個編碼器-解碼器 Transformer 模型,準備我們的批次的一個微妙之處是,在解碼過程中,我們需要將標籤向右移動一個。 這是為了確保解碼器只看到之前的真實的標籤,而不是當前或未來的標籤,這對於模型來說很容易記憶。 這類似於在 [因果語言建模](/course/chapter7/6) 等任務中如何將掩蔽的自我注意應用於輸入。 + +幸運的是,🤗 Transformers 提供了一個 `DataCollatorForSeq2Seq` 整理器,它將為我們動態填充輸入和標籤。 要實例化這個收集器,我們只需要提供 `tokenizer` 和 `model`: + +{#if fw === 'pt'} + +```python +from transformers import DataCollatorForSeq2Seq + +data_collator = DataCollatorForSeq2Seq(tokenizer, model=model) +``` + +{:else} + +```python +from transformers import DataCollatorForSeq2Seq + +data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf") +``` + +{/if} + +讓我們看看這個整理器在輸入一小批示例時會產生什麼。 首先,我們需要刪除帶有字符串的列,因為整理器不知道如何填充這些元素: + +```python +tokenized_datasets = tokenized_datasets.remove_columns( + books_dataset["train"].column_names +) +``` + +由於 collator 需要一個 `dict` 的列表,其中每個 `dict` 代表數據集中的一個示例,我們還需要在將數據傳遞給 data collator 之前將數據整理成預期的格式: + +```python +features = [tokenized_datasets["train"][i] for i in range(2)] +data_collator(features) +``` + +```python out +{'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]), 'input_ids': tensor([[ 1494, 259, 8622, 390, 259, 262, 2316, 3435, 955, + 772, 281, 772, 1617, 263, 305, 14701, 260, 1385, + 3031, 259, 24146, 332, 1037, 259, 43906, 305, 336, + 260, 1, 0, 0, 0, 0, 0, 0], + [ 259, 27531, 13483, 259, 7505, 260, 112240, 15192, 305, + 53198, 276, 259, 74060, 263, 260, 459, 25640, 776, + 2119, 336, 259, 2220, 259, 18896, 288, 4906, 288, + 1037, 3931, 260, 7083, 101476, 1143, 260, 1]]), 'labels': tensor([[ 7483, 259, 2364, 15695, 1, -100], + [ 259, 27531, 13483, 259, 7505, 1]]), 'decoder_input_ids': tensor([[ 0, 7483, 259, 2364, 15695, 1], + [ 0, 259, 27531, 13483, 259, 7505]])} +``` + +這裡要注意的主要是第一個例子比第二個例子要長,所以第二個例子的 `input_ids` 和 `attention_mask` 已經在右側填充了一個 `[PAD]` 標記(其 ID 是 ` 0`)。 類似地,我們可以看到 `labels` 已用 `-100` 填充,以確保填充標記被損失函數忽略。 最後,我們可以看到一個新的 `decoder_input_ids`,它通過在第一個條目中插入 `[PAD]` 標記將標籤向右移動。 + +{#if fw === 'pt'} + +我們終於擁有了訓練所需的所有的前期準備!我們現在只需要使用標準參數實例化訓練器: + +```python +from transformers import Seq2SeqTrainer + +trainer = Seq2SeqTrainer( + model, + args, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["validation"], + data_collator=data_collator, + tokenizer=tokenizer, + compute_metrics=compute_metrics, +) +``` + +並啟動我們的訓練: + +```python +trainer.train() +``` + +在訓練期間,您應該會看到訓練損失減少並且 ROUGE 分數隨著每個 epoch 增加。訓練完成後,您可以通過運行**Trainer.evaluate()** 查看最終的 ROUGE 分數 : + +```python +trainer.evaluate() +``` + +```python out +{'eval_loss': 3.028524398803711, + 'eval_rouge1': 16.9728, + 'eval_rouge2': 8.2969, + 'eval_rougeL': 16.8366, + 'eval_rougeLsum': 16.851, + 'eval_gen_len': 10.1597, + 'eval_runtime': 6.1054, + 'eval_samples_per_second': 38.982, + 'eval_steps_per_second': 4.914} +``` + +從分數中我們可以看到,我們的模型輕鬆超過了我們的lead-3 baseline——很好!最後要做的是將模型權重推送到 Hub,如下所示: + +``` +trainer.push_to_hub(commit_message="Training complete", tags="summarization") +``` + +```python out +'https://huggingface.co/huggingface-course/mt5-finetuned-amazon-en-es/commit/aa0536b829b28e73e1e4b94b8a5aacec420d40e0' +``` + +這會將檢查點和配置文件保存到 **output_dir** , 在將所有文件上傳到集線器之前。通過指定 **tags** 參數,我們還確保集線器上的小部件將是一個用於彙總管道的小部件,而不是與 mT5 架構關聯的默認文本生成小部件(有關模型標籤的更多信息,請參閱[🤗 Hub 文檔](https://huggingface.co/docs/hub/main#how-is-a-models-type-of-inference-api-and-widget-determined))。輸出來自 **trainer.push_to_hub()** 是 Git 提交哈希的 URL,因此您可以輕鬆查看對模型存儲庫所做的更改! + +在結束本節之前,讓我們看一下如何使用 🤗 Accelerate 提供的底層API對 mT5 進行微調。 + +{:else} + +我們幾乎準備好訓練了! 我們只需要使用我們上面定義的數據整理器將我們的數據集轉換為 tf.data.Dataset ,然後 `compile()` 和 `fit()` 模型。 首先,轉換數據集: +```python +tf_train_dataset = tokenized_datasets["train"].to_tf_dataset( + columns=["input_ids", "attention_mask", "labels"], + collate_fn=data_collator, + shuffle=True, + batch_size=8, +) +tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset( + columns=["input_ids", "attention_mask", "labels"], + collate_fn=data_collator, + shuffle=False, + batch_size=8, +) +``` + +現在,我們定義訓練超參數並編譯: + +```python +from transformers import create_optimizer +import tensorflow as tf + +# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied +# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset, +# not the original Hugging Face Dataset, so its len() is already num_samples // batch_size. +num_train_epochs = 8 +num_train_steps = len(tf_train_dataset) * num_train_epochs +model_name = model_checkpoint.split("/")[-1] + +optimizer, schedule = create_optimizer( + init_lr=5.6e-5, + num_warmup_steps=0, + num_train_steps=num_train_steps, + weight_decay_rate=0.01, +) + +model.compile(optimizer=optimizer) + +# Train in mixed-precision float16 +tf.keras.mixed_precision.set_global_policy("mixed_float16") +``` + +最後,我們擬合模型。 我們在每個 epoch 之後使用`PushToHubCallback`將模型保存到 Hub,這將允許我們稍後使用它進行推理: +```python +from transformers.keras_callbacks import PushToHubCallback + +callback = PushToHubCallback( + output_dir=f"{model_name}-finetuned-amazon-en-es", tokenizer=tokenizer +) + +model.fit( + tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback], epochs=8 +) +``` + +我們在訓練期間輸出了一些loss,但實際上我們希望看到我們之前計算的 ROUGE 指標。 要獲得這些指標,我們需要從模型生成輸出並將它們轉換為字符串。 讓我們為 ROUGE 指標構建一些標籤和預測列表以進行比較(請注意,如果您在本節中遇到import的錯誤,您可能需要`!pip install tqdm`): + +```python +from tqdm import tqdm +import numpy as np + +all_preds = [] +all_labels = [] +for batch in tqdm(tf_eval_dataset): + predictions = model.generate(**batch) + decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) + labels = batch["labels"].numpy() + labels = np.where(labels != -100, labels, tokenizer.pad_token_id) + decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True) + decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds] + decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels] + all_preds.extend(decoded_preds) + all_labels.extend(decoded_labels) +``` + +一旦我們有了標籤和預測字符串列表,計算 ROUGE 分數就很容易了: + +```python +result = rouge_score.compute( + predictions=decoded_preds, references=decoded_labels, use_stemmer=True +) +result = {key: value.mid.fmeasure * 100 for key, value in result.items()} +{k: round(v, 4) for k, v in result.items()} +``` + +``` +{'rouge1': 31.4815, 'rouge2': 25.4386, 'rougeL': 31.4815, 'rougeLsum': 31.4815} +``` + + +{/if} + +{#if fw === 'pt'} + +## 使用 🤗 Accelerate 微調 mT5 + +使用 🤗 Accelerate 微調我們的模型與我們在 [Chapter 3](/course/chapter3) 中遇到的文本分類示例非常相似。 主要區別在於需要在訓練期間顯式生成摘要並定義我們如何計算 ROUGE 分數(回想一下,`Seq2SeqTrainer` 為我們生成了摘要)。 讓我們看看我們如何在 🤗 Accelerate 中實現這兩個要求! + +### 為訓練做好一切準備 + +The first thing we need to do is create a `DataLoader` for each of our splits. Since the PyTorch dataloaders expect batches of tensors, we need to set the format to `"torch"` in our datasets: +我們需要做的第一件事是為每個數據集的每一個拆分創建一個`DataLoader`。 由於 PyTorch 數據加載器需要成批的張量,我們需要在數據集中將格式設置為`torch`: + +```python +tokenized_datasets.set_format("torch") +``` + +現在我們已經有了僅由張量組成的數據集,接下來要做的是再次實例化`DataCollatorForSeq2Seq`。 為此,我們需要提供模型微調前的版本,所以讓我們從緩存中再次加載它: + +```python +model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint) +``` + +然後我們可以實例化數據整理器並使用它來定義我們的數據加載器: + +```python +from torch.utils.data import DataLoader + +batch_size = 8 +train_dataloader = DataLoader( + tokenized_datasets["train"], + shuffle=True, + collate_fn=data_collator, + batch_size=batch_size, +) +eval_dataloader = DataLoader( + tokenized_datasets["validation"], collate_fn=data_collator, batch_size=batch_size +) +``` + +接下來要做的是定義我們想要使用的優化器。與我們的其他示例一樣,我們將使用 **AdamW** ,這適用於大多數問題: + +```python +from torch.optim import AdamW + +optimizer = AdamW(model.parameters(), lr=2e-5) +``` + +最後,我們將模型、優化器和數據加載器提供給 **accelerator.prepare()** 方法: + +```python +from accelerate import Accelerator + +accelerator = Accelerator() +model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare( + model, optimizer, train_dataloader, eval_dataloader +) +``` + + + +🚨如果您在 TPU 上進行訓練,則需要將上述所有代碼移動到專門的訓練函數中。有關詳細信息,請參閱[第三章](/course/chapter3)。 + + + +現在我們已經準備好了我們索要用的對象,還有三件事要做: + +* 定義學習率調度計劃。 +* 實現一個功能來對摘要進行後續處理以進行評估。 +* 在 Hub 上創建一個存儲庫,我們可以將模型推送到該存儲庫。 + +對於學習率調度,我們將使用前幾節中的標準線性衰減: + +```python +from transformers import get_scheduler + +num_train_epochs = 10 +num_update_steps_per_epoch = len(train_dataloader) +num_training_steps = num_train_epochs * num_update_steps_per_epoch + +lr_scheduler = get_scheduler( + "linear", + optimizer=optimizer, + num_warmup_steps=0, + num_training_steps=num_training_steps, +) +``` + +對於後續處理,我們需要一個函數,將生成的摘要拆分為由換行符分隔的句子。 這是 ROUGE 指標所期望的格式,我們可以使用以下代碼片段來實現: + +```python +def postprocess_text(preds, labels): + preds = [pred.strip() for pred in preds] + labels = [label.strip() for label in labels] + + # ROUGE expects a newline after each sentence + preds = ["\n".join(nltk.sent_tokenize(pred)) for pred in preds] + labels = ["\n".join(nltk.sent_tokenize(label)) for label in labels] + + return preds, labels +``` + +如果你還記得我們是如何定義 `Seq2SeqTrainer` 的 `compute_metrics()` 函數的,這對你來說應該很熟悉。 + +最後,我們需要在 Hugging Face Hub 上創建一個模型存儲庫。 為此,我們可以使用🤗 Hub 庫的get_full_repo_name。 我們只需要為我們的存儲庫定義一個名稱,該庫有一個非常好用的函數可以將存儲庫 ID 與用戶配置文件結合起來: +```python +from huggingface_hub import get_full_repo_name + +model_name = "test-bert-finetuned-squad-accelerate" +repo_name = get_full_repo_name(model_name) +repo_name +``` + +```python out +'lewtun/mt5-finetuned-amazon-en-es-accelerate' +``` + +現在我們可以使用這個存儲庫名稱將本地版本克隆到我們的結果目錄中,該目錄將存儲訓練的模型: + +```python +from huggingface_hub import Repository + +output_dir = "results-mt5-finetuned-squad-accelerate" +repo = Repository(output_dir, clone_from=repo_name) +``` +這將允許我們在訓練期間通過調用 `repo.push_to_hub()` 方法將模型推送到 Hub! 現在讓我們通過寫出完整的訓練循環來結束我們的分析。 + +### 訓練循環 + +文本摘要的訓練循環與我們遇到的其他 🤗 Accelerate 示例非常相似,大致分為四個主要步驟:這 + +1. 通過在每個epoch 迭代 `train_dataloader` 中的所有示例來訓練模型。 +2. 在每個 epoch 結束時生成模型摘要,首先生成標記,然後將它們(和參考摘要)解碼為文本。 +3. 使用我們之前看到的相同技術計算 ROUGE 分數。 +4. 保存檢查點並將所有內容推送到 Hub。 在這裡,我們依賴 `Repository` 對象的巧妙的 `blocking=False` 參數,以便我們可以在每個 epoch 異步地上傳檢查點。 這使我們能夠繼續訓練,而不必等待與 GB 大小的模型慢呼呼的上傳! + +這些步驟可以在以下代碼塊中看到: + +```python +from tqdm.auto import tqdm +import torch +import numpy as np + +progress_bar = tqdm(range(num_training_steps)) + +for epoch in range(num_train_epochs): + # Training + model.train() + for step, batch in enumerate(train_dataloader): + outputs = model(**batch) + loss = outputs.loss + accelerator.backward(loss) + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) + + # Evaluation + model.eval() + for step, batch in enumerate(eval_dataloader): + with torch.no_grad(): + generated_tokens = accelerator.unwrap_model(model).generate( + batch["input_ids"], + attention_mask=batch["attention_mask"], + ) + + generated_tokens = accelerator.pad_across_processes( + generated_tokens, dim=1, pad_index=tokenizer.pad_token_id + ) + labels = batch["labels"] + + # If we did not pad to max length, we need to pad the labels too + labels = accelerator.pad_across_processes( + batch["labels"], dim=1, pad_index=tokenizer.pad_token_id + ) + + generated_tokens = accelerator.gather(generated_tokens).cpu().numpy() + labels = accelerator.gather(labels).cpu().numpy() + + # Replace -100 in the labels as we can't decode them + labels = np.where(labels != -100, labels, tokenizer.pad_token_id) + if isinstance(generated_tokens, tuple): + generated_tokens = generated_tokens[0] + decoded_preds = tokenizer.batch_decode( + generated_tokens, skip_special_tokens=True + ) + decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True) + + decoded_preds, decoded_labels = postprocess_text( + decoded_preds, decoded_labels + ) + + rouge_score.add_batch(predictions=decoded_preds, references=decoded_labels) + + # Compute metrics + result = rouge_score.compute() + # Extract the median ROUGE scores + result = {key: value.mid.fmeasure * 100 for key, value in result.items()} + result = {k: round(v, 4) for k, v in result.items()} + print(f"Epoch {epoch}:", result) + + # Save and upload + accelerator.wait_for_everyone() + unwrapped_model = accelerator.unwrap_model(model) + unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) + if accelerator.is_main_process: + tokenizer.save_pretrained(output_dir) + repo.push_to_hub( + commit_message=f"Training in progress epoch {epoch}", blocking=False + ) +``` + +```python out +Epoch 0: {'rouge1': 5.6351, 'rouge2': 1.1625, 'rougeL': 5.4866, 'rougeLsum': 5.5005} +Epoch 1: {'rouge1': 9.8646, 'rouge2': 3.4106, 'rougeL': 9.9439, 'rougeLsum': 9.9306} +Epoch 2: {'rouge1': 11.0872, 'rouge2': 3.3273, 'rougeL': 11.0508, 'rougeLsum': 10.9468} +Epoch 3: {'rouge1': 11.8587, 'rouge2': 4.8167, 'rougeL': 11.7986, 'rougeLsum': 11.7518} +Epoch 4: {'rouge1': 12.9842, 'rouge2': 5.5887, 'rougeL': 12.7546, 'rougeLsum': 12.7029} +Epoch 5: {'rouge1': 13.4628, 'rouge2': 6.4598, 'rougeL': 13.312, 'rougeLsum': 13.2913} +Epoch 6: {'rouge1': 12.9131, 'rouge2': 5.8914, 'rougeL': 12.6896, 'rougeLsum': 12.5701} +Epoch 7: {'rouge1': 13.3079, 'rouge2': 6.2994, 'rougeL': 13.1536, 'rougeLsum': 13.1194} +Epoch 8: {'rouge1': 13.96, 'rouge2': 6.5998, 'rougeL': 13.9123, 'rougeLsum': 13.7744} +Epoch 9: {'rouge1': 14.1192, 'rouge2': 7.0059, 'rougeL': 14.1172, 'rougeLsum': 13.9509} +``` + +就是這樣! 運行此程序後,您將獲得與我們使用“Trainer”獲得的模型和結果非常相似的模型和結果。 + +{/if} + +## 使用您微調的模型 + +將模型推送到 Hub 後,您可以通過推理小部件或“管道”對象來使用它,如下所示: + +```python +from transformers import pipeline + +hub_model_id = "huggingface-course/mt5-small-finetuned-amazon-en-es" +summarizer = pipeline("summarization", model=hub_model_id) +``` + +我們可以將測試集中的一些示例(模型還沒有看到)提供給我們的管道,以瞭解生成摘要的質量。 首先讓我們實現一個簡單的函數來一起顯示評論、標題和生成的摘要: + +```python +def print_summary(idx): + review = books_dataset["test"][idx]["review_body"] + title = books_dataset["test"][idx]["review_title"] + summary = summarizer(books_dataset["test"][idx]["review_body"])[0]["summary_text"] + print(f"'>>> Review: {review}'") + print(f"\n'>>> Title: {title}'") + print(f"\n'>>> Summary: {summary}'") +``` + +讓我們看一下我們得到的一個英文例子: + +```python +print_summary(100) +``` + +```python out +'>>> Review: Nothing special at all about this product... the book is too small and stiff and hard to write in. The huge sticker on the back doesn’t come off and looks super tacky. I would not purchase this again. I could have just bought a journal from the dollar store and it would be basically the same thing. It’s also really expensive for what it is.' + +'>>> Title: Not impressed at all... buy something else' + +'>>> Summary: Nothing special at all about this product' +``` + +這還不錯! 我們可以看到,我們的模型實際上已經能夠通過增加部分新詞來執行抽象摘要。 也許我們模型最酷的方面是它是雙語的,所以我們還可以生成西班牙語評論的摘要: + +```python +print_summary(0) +``` + +```python out +'>>> Review: Es una trilogia que se hace muy facil de leer. Me ha gustado, no me esperaba el final para nada' + +'>>> Title: Buena literatura para adolescentes' + +'>>> Summary: Muy facil de leer' +``` + +摘要翻譯成了英文的“非常容易閱讀”,在這種情況下,我們可以看到它是直接從評論中提取的。 這顯示了 mT5 模型的多功能性,並讓您體驗了處理多語言語料庫的感覺! + +接下來,我們將把注意力轉向稍微複雜的任務:從頭開始訓練語言模型。 diff --git a/chapters/zh-TW/chapter7/6.mdx b/chapters/zh-TW/chapter7/6.mdx new file mode 100644 index 000000000..49a820da5 --- /dev/null +++ b/chapters/zh-TW/chapter7/6.mdx @@ -0,0 +1,906 @@ + + +# 從頭開始訓練因果語言模型 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +到目前為止,我們主要使用預訓練模型,並通過重用預訓練的權重來針對新用例對它們進行微調。正如我們在[第一章](/course/chapter1), 這通常稱為遷移學習,這是將 Transformer 模型應用於大多數標記數據稀疏的現實世界用例的非常成功的策略。在本章中,我們將採用不同的方法並從頭開始訓練一個全新的模型。如果您有大量數據並且它與用於可用模型的預訓練數據有很大不同,那麼這是一個很好的方法。然而,它也需要更多的計算資源來預訓練語言模型,而不僅僅是微調現有的模型。訓練新模型有意義的示例包括由音符、分子序列(如 DNA)或編程語言組成的數據集。後者最近受到關注,這要歸功於 TabNine 和 GitHub 的 Copilot 等工具,它們由 OpenAI 的 Codex 模型提供支持,可以生成長代碼序列。這種文本生成任務最好使用自迴歸或因果語言模型(例如 GPT-2)來解決。 + +在本節中,我們將構建代碼生成模型的縮小版本:我們將使用 Python 代碼的子集專注於單行完成而不是完整的函數或類。在 Python 中處理數據時,您會經常接觸 Python 數據科學堆棧,包括 `matplotlib` , `seaborn` , `pandas` , 和 `scikit-learn` 庫。在使用這些框架時,通常需要查找特定的命令,因此如果我們可以使用模型來為我們完成這些調用,那就太好了。 + + + +在[第六章](/course/chapter6) 我們創建了一個高效的分詞器來處理 Python 源代碼,但我們仍然需要一個大規模數據集來預訓練模型。在這裡,我們將我們的分詞器應用到源自 GitHub 存儲庫的 Python 代碼語料庫。然後我們將使用 `Trainer` API 和 🤗 Accelerate 來訓練模型。讓我們開始吧! + + + + +這實際上展示了使用本節中訓練並上傳到 Hub 的模型。你可以在[這裡](https://huggingface.co/huggingface-course/codeparrot-ds?text=plt.imshow%28)找到。請注意,由於在文本生成過程中發生了一些隨機化,您可能會得到略有不同的結果。 +## 收集數據 + +Python 代碼可以從 GitHub 等代碼存儲庫中獲得,我們可以通過抓取每個 Python 存儲庫來使用它們來創建數據集。這是在[Transformers textbook](https://learning.oreilly.com/library/view/natural-language-processing/9781098103231/)預訓練大型的GPT-2 模型。使用大約 180 GB 的 GitHub 轉儲,其中包含大約 2000 萬個 Python 文件,稱為 `codeparrot` ,作者構建了一個數據集,然後在[Hugging Face Hub](https://huggingface.co/datasets/transformersbook/codeparrot)上分享出來了. + +然而,對完整語料庫的訓練既耗時又費力,我們只需要與 Python 數據科學堆棧相關的數據集子集。所以,讓我們開始過濾 `codeparrot` 包含此堆棧中任何庫的所有文件的數據集。由於數據集的太大,我們希望避免下載它;因此反,我們將使用流功能來動態過濾它。為了使用前面提到的庫過濾代碼示例,我們將使用以下函數: + +```py +def any_keyword_in_string(string, keywords): + for keyword in keywords: + if keyword in string: + return True + return False +``` + +讓我們用兩個例子來測試一下: + +```py +filters = ["pandas", "sklearn", "matplotlib", "seaborn"] +example_1 = "import numpy as np" +example_2 = "import pandas as pd" + +print( + any_keyword_in_string(example_1, filters), any_keyword_in_string(example_2, filters) +) +``` + +```python out +False True +``` + +我們可以使用它來創建一個函數來流式傳輸數據集並過濾我們想要的元素: + +```py +def filter_streaming_dataset(dataset, filters): + filtered_dict = defaultdict(list) + total = 0 + for sample in tqdm(iter(dataset)): + total += 1 + if any_keyword_in_string(sample["content"], filters): + for k, v in sample.items(): + filtered_dict[k].append(v) + print(f"{len(filtered_dict['content'])/total:.2%} of data after filtering.") + return Dataset.from_dict(filtered_dict) +``` + +然後我們可以簡單地將此函數應用於流數據集: + +```py +# This cell will take a very long time to execute, so you should skip it and go to +# the next one! +from datasets import load_dataset + +split = "train" # "valid" +filters = ["pandas", "sklearn", "matplotlib", "seaborn"] + +data = load_dataset(f"transformersbook/codeparrot-{split}", split=split, streaming=True) +filtered_data = filter_streaming_dataset(data, filters) +``` + +```python out +3.26% of data after filtering. +``` + +這給我們留下了大約 3% 的原始數據集,這個數據集仍然相當可觀——結果數據集有 6 GB,包含 600,000 個 Python 腳本!過濾完整數據集可能需要 2-3 小時,具體取決於您的機器和帶寬。如果您不想自己經歷這個漫長的過程,我們在 Hub 上提供過濾後的數據集供您下載: + +```py +from datasets import load_dataset, DatasetDict + +ds_train = load_dataset("huggingface-course/codeparrot-ds-train", split="train") +ds_valid = load_dataset("huggingface-course/codeparrot-ds-valid", split="train") + +raw_datasets = DatasetDict( + { + "train": ds_train, # .shuffle().select(range(50000)), + "valid": ds_valid, # .shuffle().select(range(500)) + } +) + +raw_datasets +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'], + num_rows: 606720 + }) + valid: Dataset({ + features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'], + num_rows: 3322 + }) +}) +``` + + + +預訓練語言模型需要一段時間。我們建議您首先通過取消註釋以上兩行的註釋對數據樣本運行訓練循環,並確保訓練成功完成並存儲模型。沒有什麼比最後一步的訓練失敗更令人沮喪的了,因為你忘記創建一個文件夾或者因為保存路徑在訓練循環結束時有一個錯字! + + + +讓我們看一個來自數據集的例子。我們將只顯示每個字段的前 200 個字符: + +```py +for key in raw_datasets["train"][0]: + print(f"{key.upper()}: {raw_datasets['train'][0][key][:200]}") +``` + +```python out +'REPO_NAME: kmike/scikit-learn' +'PATH: sklearn/utils/__init__.py' +'COPIES: 3' +'SIZE: 10094' +'''CONTENT: """ +The :mod:`sklearn.utils` module includes various utilites. +""" + +from collections import Sequence + +import numpy as np +from scipy.sparse import issparse +import warnings + +from .murmurhash import murm +LICENSE: bsd-3-clause''' +``` + +我們可以看到 `content` 字段包含我們希望我們的模型訓練的代碼。現在我們有了一個數據集,我們需要預處理文本,使其採用適合預訓練的格式。 + +## 準備數據集 + + + +第一步是對數據進行標記,以便我們可以將其用於訓練。由於我們的目標主要是自動完成短函數調用,因此我們可以保持上下文大小相對較小。這樣做的好處是我們可以更快地訓練模型並且它需要的內存顯著減少。如果您的應用程序擁有更多上下文很重要(例如,如果您希望模型基於具有函數定義的文件編寫單元測試),請確保增加該數量,但請記住,這需要更大的 GPU 內存佔用。現在,讓我們將上下文大小固定為 128 個標記,而不是 GPT-2 或 GPT-3 中分別使用的 1,024 或 2,048 個標記。 + + +大多數文檔包含超過 128 個標記,因此簡單地將輸入截斷到最大長度將消除我們數據集的很大一部分。相反,我們將使用 `return_overflowing_tokens` 標記整個輸入並將其分成幾個塊的選項,就像我們在[第六章](/course/chapter6/4). 我們還將使用 `return_length` 選項自動返回每個創建的塊的長度。通常最後一個塊會小於上下文大小,我們會去掉這些塊以避免填充問題;因為無論如何我們都有大量數據。 + +
+Chunking a large texts in several pieces. + +
+ +讓我們通過查看前兩個示例來確切瞭解這是如何工作的: + +```py +from transformers import AutoTokenizer + +context_length = 128 +tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer") + +outputs = tokenizer( + raw_datasets["train"][:2]["content"], + truncation=True, + max_length=context_length, + return_overflowing_tokens=True, + return_length=True, +) + +print(f"Input IDs length: {len(outputs['input_ids'])}") +print(f"Input chunk lengths: {(outputs['length'])}") +print(f"Chunk mapping: {outputs['overflow_to_sample_mapping']}") +``` + +```python out +Input IDs length: 34 +Input chunk lengths: [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 117, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 41] +Chunk mapping: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +``` + +我們可以看 到,從這兩個示例中我們總共得到了 34 個片段。查看塊長度,我們可以看到兩個文檔末尾的塊都少於 128 個標記(分別為 117 和 41)。這些僅代表我們擁有的數據集的一小部分,因此我們可以安全地將它們扔掉。通過 `overflow_to_sample_mapping` 字段,我們還可以重建哪些塊屬於哪些輸入樣本。 + +通過這個操作,我們使用了一個方便的🤗 Datasets 中的` Dataset.map()` 函數,就是不需要一對一的映射;正如我們在[第三節](/course/chapter7/3),我們可以創建具有比輸入批次更多或更少元素的批次。這在執行更改元素數量的數據增強或數據過濾等操作時非常有用。在我們的例子中,當將每個元素標記為指定上下文大小的塊時,我們從每個文檔中創建了許多樣本。我們只需要確保刪除現有的列,因為它們的大小存在衝突。如果我們想保留它們,我們可以適當地重複它們,並在`Dataset.map()` 調用中返回它們: + +```py +def tokenize(element): + outputs = tokenizer( + element["content"], + truncation=True, + max_length=context_length, + return_overflowing_tokens=True, + return_length=True, + ) + input_batch = [] + for length, input_ids in zip(outputs["length"], outputs["input_ids"]): + if length == context_length: + input_batch.append(input_ids) + return {"input_ids": input_batch} + + +tokenized_datasets = raw_datasets.map( + tokenize, batched=True, remove_columns=raw_datasets["train"].column_names +) +tokenized_datasets +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['input_ids'], + num_rows: 16702061 + }) + valid: Dataset({ + features: ['input_ids'], + num_rows: 93164 + }) +}) +``` + +我們現在有 1670 萬個示例,每個示例有 128 個tokens ,總共相當於大約 21 億個tokens 。作為參考,OpenAI 的 GPT-3 和 Codex 模型分別在 300 和 1000 億個tokens 上訓練,其中 Codex 模型從 GPT-3 檢查點初始化。我們在本節中的目標不是與這些模型競爭,這些模型可以生成長而連貫的文本,而是創建一個縮小版本,為數據科學家提供快速自動完成功能。 + +現在我們已經準備好了數據集,讓我們設置模型! + + + + +✏️ **試試看!** 擺脫所有小於上下文大小的塊在這裡並不是什麼大問題,因為我們使用的是小上下文窗口。隨著上下文大小的增加(或者如果您有一個短文檔語料庫),被丟棄的塊的比例也會增加。準備數據的更有效方法是將所有標記化的樣本加入一個批次中,每個語料之間有一個`eos_token_id` 標記, 然後對連接的序列執行分塊。作為練習,修改 `tokenize()`函數以使用該方法。請注意,您需要設置`truncation=False` 和刪除標記生成器中的其他參數以獲取完整的標記 ID 序列。 + + + + +## 初始化新模型 + +我們的第一步是新初始化一個 GPT-2 模型。我們將對我們的模型使用與小型 GPT-2 模型相同的配置,因此我們加載預訓練配置,確保分詞器大小與模型詞彙量大小匹配並設置 `bos` 和 `eos` (序列的開始和結束)令牌 ID: + +{#if fw === 'pt'} + +```py +from transformers import AutoTokenizer, GPT2LMHeadModel, AutoConfig + +config = AutoConfig.from_pretrained( + "gpt2", + vocab_size=len(tokenizer), + n_ctx=context_length, + bos_token_id=tokenizer.bos_token_id, + eos_token_id=tokenizer.eos_token_id, +) +``` + +使用該配置,我們可以加載一個新模型。請注意,這是我們第一次不使用 `from_pretrained()` 函數,因為我們實際上是在自己初始化模型 + +```py +model = GPT2LMHeadModel(config) +model_size = sum(t.numel() for t in model.parameters()) +print(f"GPT-2 size: {model_size/1000**2:.1f}M parameters") +``` + +```python out +GPT-2 size: 124.2M parameters +``` + +{:else} + +```py +from transformers import AutoTokenizer, TFGPT2LMHeadModel, AutoConfig + +config = AutoConfig.from_pretrained( + "gpt2", + vocab_size=len(tokenizer), + n_ctx=context_length, + bos_token_id=tokenizer.bos_token_id, + eos_token_id=tokenizer.eos_token_id, +) +``` + +通過該配置,我們可以加載新模型。請注意,這是我們剛開始不使用`from_pretrained()`函數,因為我們實際上是在自己初始化模型: + +```py +model = TFGPT2LMHeadModel(config) +model(model.dummy_inputs) # Builds the model +model.summary() +``` + +```python out +_________________________________________________________________ +Layer (type) Output Shape Param # +================================================================= +transformer (TFGPT2MainLayer multiple 124242432 +================================================================= +Total params: 124,242,432 +Trainable params: 124,242,432 +Non-trainable params: 0 +_________________________________________________________________ +``` + +{/if} + +我們的模型有 1.24 億個參數,我們必須對其進行調整。在開始訓練之前,我們需要設置一個負責創建批次的數據整理器。我們可以使用 `DataCollatorForLanguageModeling` ,它是專為語言建模而設計(顧名思義)。除了堆疊和填充批次,它還負責創建語言模型標籤——在因果語言建模中,輸入也用作標籤(只是移動了一個元素),並且這個數據整理器在訓練期間即時創建它們,所以我們不需要複製 `input_ids`。 + +注意 `DataCollatorForLanguageModeling` 支持掩碼語言建模 (MLM) 和因果語言建模 (CLM)。默認情況下它為 MLM 準備數據,但我們可以通過設置`mlm=False`參數切換到 CLM : + +{#if fw === 'pt'} + +```py +from transformers import DataCollatorForLanguageModeling + +tokenizer.pad_token = tokenizer.eos_token +data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False) +``` + +{:else} + +```py +from transformers import DataCollatorForLanguageModeling + +tokenizer.pad_token = tokenizer.eos_token +data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False, return_tensors="tf") +``` + +{/if} + +讓我們看一個例子: + +```py +out = data_collator([tokenized_dataset["train"][i] for i in range(5)]) +for key in out: + print(f"{key} shape: {out[key].shape}") +``` + +{#if fw === 'pt'} + +```python out +input_ids shape: torch.Size([5, 128]) +attention_mask shape: torch.Size([5, 128]) +labels shape: torch.Size([5, 128]) +``` + +{:else} + +```python out +input_ids shape: (5, 128) +attention_mask shape: (5, 128) +labels shape: (5, 128) +``` + +{/if} + +我們可以看到示例已經堆疊在一起,並且所有張量都具有相同的形狀。 + +{#if fw === 'tf'} + +現在,我們可以使用`to_tf_dataset()`方法,使用上面創建的數據整理器將數據集轉換為TensorFlow數據集: + +```python +tf_train_dataset = tokenized_dataset["train"].to_tf_dataset( + columns=["input_ids", "attention_mask", "labels"], + collate_fn=data_collator, + shuffle=True, + batch_size=32, +) +tf_eval_dataset = tokenized_dataset["valid"].to_tf_dataset( + columns=["input_ids", "attention_mask", "labels"], + collate_fn=data_collator, + shuffle=False, + batch_size=32, +) +``` + +{/if} + + + +⚠️ 移動輸入和標籤以對齊它們發生在模型內部,因此數據整理器只需複製輸入以創建標籤。 + + + + +現在我們已經準備好實際訓練我們的模型的一切了——畢竟這不是那麼多工作!在我們開始訓練之前,我們應該登錄 Hugging Face。如果您在筆記本上工作,則可以使用以下實用程序功能: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +這將顯示一個小部件,您可以在其中輸入您的 Hugging Face 登錄憑據。 + +如果您不是在notebook上工作,只需在終端中輸入以下行: + +```bash +huggingface-cli login +``` + +{#if fw === 'pt'} + +剩下要做的就是配置訓練參數並啟動 `Trainer` .我們將使用餘弦學習率,並進行一些Warmup和有效批量大小為 256 ( `per_device_train_batch_size` * `gradient_accumulation_steps`)。當單個批次不適合內存時使用梯度累積,並通過多次向前/向後傳遞逐步建立梯度。當我們使用 🤗 Accelerate 創建訓練循環時,我們將看到這一點。 + +```py +from transformers import Trainer, TrainingArguments + +args = TrainingArguments( + output_dir="codeparrot-ds", + per_device_train_batch_size=32, + per_device_eval_batch_size=32, + evaluation_strategy="steps", + eval_steps=5_000, + logging_steps=5_000, + gradient_accumulation_steps=8, + num_train_epochs=1, + weight_decay=0.1, + warmup_steps=1_000, + lr_scheduler_type="cosine", + learning_rate=5e-4, + save_steps=5_000, + fp16=True, + push_to_hub=True, +) + +trainer = Trainer( + model=model, + tokenizer=tokenizer, + args=args, + data_collator=data_collator, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["valid"], +) +``` + +現在我們可以開始 `Trainer`並等待訓練完成。根據您是在整個訓練集還是在訓練集的一個子集上運行它,這將分別需要 20 或 2 個小時,因此請喝杯咖啡和一本好書來閱讀! + +```py +trainer.train() +``` + +訓練完成後,我們可以將模型和標記器推送到 Hub: + +```py +trainer.push_to_hub() +``` + +{:else} + +剩下要做的就是配置訓練超參數並調用 `compile()` 和 `fit()`。我們將使用帶有一些預熱的學習率調整策略來提高訓練的穩定性: + +```py +from transformers import create_optimizer +import tensorflow as tf + +num_train_steps = len(tf_train_dataset) +optimizer, schedule = create_optimizer( + init_lr=5e-5, + num_warmup_steps=1_000, + num_train_steps=num_train_steps, + weight_decay_rate=0.01, +) +model.compile(optimizer=optimizer) + +# Train in mixed-precision float16 +tf.keras.mixed_precision.set_global_policy("mixed_float16") +``` + +現在我們可以調用`model.fit(),`並等待訓練完成。你是在完整的訓練集還是他的子集上運行,這將分別需要20和2個小時,所以拿一些咖啡和一本好書來閱讀!訓練完成後,我們可以將模型和分詞器推送到中心: + +```py +from transformers.keras_callbacks import PushToHubCallback + +callback = PushToHubCallback(output_dir="codeparrot-ds", tokenizer=tokenizer) + +model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback]) +``` + +{/if} + + + +✏️ **試試看!** 除了`TrainingArguments` 之外,我們只需要大約30行代碼就可以從原始文本到訓練GPT-2。 用你自己的數據集試試看,看看你能不能得到好的結果! + + + + + +{#if fw === 'pt'} + +💡 如果您可以訪問具有多個 GPU 的機器,請嘗試在那裡運行代碼。 `Trainer`自動管理多臺機器,這可以極大地加快訓練速度。 + +{:else} + +💡 如果您有權訪問具有多個 GPU 的計算機,則可以嘗試使用 `MirroredStrategy` 上下文來大幅加快訓練速度。您需要創建一個`tf.distribute.MirroredStrategy`對象,並確保 `to_tf_dataset` 命令以及模型創建和對 `fit()`的調用都在其 `scope()` context. 上下文中運行。您可以查看有關此內容的文檔[here](https://www.tensorflow.org/guide/distributed_training#use_tfdistributestrategy_with_keras_modelfit). + +{/if} + + + +## 使用管道生成代碼 + +現在是關鍵的部分:讓我們看看經過訓練的模型的實際效果如何!我們可以在日誌中看到損失穩步下降,但為了讓模型進行測試,讓我們看看它在某些測試上的表現如何。為此,我們將模型包裝在文本生成中的`pipeline` ,如果有可用的,我們會將它放在 GPU 上進行快速生成: + +{#if fw === 'pt'} + +```py +import torch +from transformers import pipeline + +device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") +pipe = pipeline( + "text-generation", model="huggingface-course/codeparrot-ds", device=device +) +``` + +{:else} + +```py +from transformers import pipeline + +course_model = TFGPT2LMHeadModel.from_pretrained("huggingface-course/codeparrot-ds") +course_tokenizer = AutoTokenizer.from_pretrained("huggingface-course/codeparrot-ds") +pipe = pipeline( + "text-generation", model=course_model, tokenizer=course_tokenizer, device=0 +) +``` + +{/if} + +讓我們從創建散點圖的簡單任務開始: + +```py +txt = """\ +# create some data +x = np.random.randn(100) +y = np.random.randn(100) + +# create scatter plot with x, y +""" +print(pipe(txt, num_return_sequences=1)[0]["generated_text"]) +``` + +```python out +# create some data +x = np.random.randn(100) +y = np.random.randn(100) + +# create scatter plot with x, y +plt.scatter(x, y) + +# create scatter +``` + +結果看起來是正確的。它也適用於 `pandas` 類型?讓我們看看我們是否使用兩個數組可以創建一個 `DataFrame` : + +```py +txt = """\ +# create some data +x = np.random.randn(100) +y = np.random.randn(100) + +# create dataframe from x and y +""" +print(pipe(txt, num_return_sequences=1)[0]["generated_text"]) +``` + +```python out +# create some data +x = np.random.randn(100) +y = np.random.randn(100) + +# create dataframe from x and y +df = pd.DataFrame({'x': x, 'y': y}) +df.insert(0,'x', x) +for +``` + +很好,這是正確的答案——儘管它隨後再次插入了列 `x` 。由於生成的token數量有限,以下 `for` 循環被切斷。讓我們看看我們是否可以做一些更復雜的事情並讓模型幫助我們分組操作: + +```py +txt = """\ +# dataframe with profession, income and name +df = pd.DataFrame({'profession': x, 'income':y, 'name': z}) + +# calculate the mean income per profession +""" +print(pipe(txt, num_return_sequences=1)[0]["generated_text"]) +``` + +```python out +# dataframe with profession, income and name +df = pd.DataFrame({'profession': x, 'income':y, 'name': z}) + +# calculate the mean income per profession +profession = df.groupby(['profession']).mean() + +# compute the +``` + +不錯;這是正確的做法。最後,讓我們看看我們是否也可以將其用於 `scikit-learn` 並建立一個隨機森林模型: + +```py +txt = """ +# import random forest regressor from scikit-learn +from sklearn.ensemble import RandomForestRegressor + +# fit random forest model with 300 estimators on X, y: +""" +print(pipe(txt, num_return_sequences=1)[0]["generated_text"]) +``` + +```python out +# import random forest regressor from scikit-learn +from sklearn.ensemble import RandomForestRegressor + +# fit random forest model with 300 estimators on X, y: +rf = RandomForestRegressor(n_estimators=300, random_state=random_state, max_depth=3) +rf.fit(X, y) +rf +``` + +{#if fw === 'tf'} + +看看這幾個例子,似乎模型已經學習了Python數據科學堆棧的一些語法。當然,在將模型部署到現實世界之前,我們需要更徹底地評估模型,但這仍然是一個令人印象深刻的原型。 + +{:else} + +從這幾個例子來看,模型似乎已經學習了 Python 數據科學堆棧的一些語法(當然,在將模型部署到現實世界之前,我們需要對其進行更全面的評估)。然而,有時需要對模型訓練進行更多定製才能實現給定用例的必要性能。例如,如果我們想動態更新批量大小或有一個條件訓練循環來即時跳過壞示例怎麼辦?一種選擇是將 `Trainer` 並添加必要的更改,但有時從頭開始編寫訓練循環會更簡單。這就是🤗 Accelerate 的用武之地。 + +{/if} + +{#if fw === 'pt'} + +## 用 🤗 Accelerate 訓練 + +我們已經看到了如何使用 `Trainer` ,這可以允許一些自定義。然而,有時我們想要完全控制訓練循環,或者我們想要進行一些奇特的更改。在這種情況下 🤗 Accelerate 是一個不錯的選擇,在本節中,我們將逐步介紹使用它來訓練我們的模型的步驟。為了讓事情變得更有趣,我們還將在訓練循環中添加一些修改。 + + + +由於我們主要對數據科學庫的合理自動填充感興趣,因此對更多使用這些庫的訓練樣本給予更多權重是有意義的。我們可以通過使用關鍵字輕鬆識別這些示例,例如 `plt`、`pd`、`sk`、`fit`和`predict`等關鍵字,我們可以很容易地識別這些示例,這些關鍵字是matplotlib最常用的導入名稱。`Pyplot`, `pandas`和`sklearn`以及後者的擬合/預測模式。如果這些都表示為單個標記,我們可以輕鬆檢查它們是否出現在輸入序列中。標記可能有一個空格前綴,因此我們還將在標記器詞彙表中檢查這些版本。為了驗證它是否有效,我們將添加一個測試token ,該token 應拆分為多個tokens: + +```py +keytoken_ids = [] +for keyword in [ + "plt", + "pd", + "sk", + "fit", + "predict", + " plt", + " pd", + " sk", + " fit", + " predict", + "testtest", +]: + ids = tokenizer([keyword]).input_ids[0] + if len(ids) == 1: + keytoken_ids.append(ids[0]) + else: + print(f"Keyword has not single token: {keyword}") +``` + +```python out +'Keyword has not single token: testtest' +``` + +太好了,這似乎很好用!我們現在可以編寫一個自定義損失函數,它將輸入序列、logits 和我們剛剛選擇的關​​鍵標記作為輸入。首先,我們需要對齊 logits 和輸入:向右移動一個的輸入序列形成標籤,因為下一個標記是當前標記的標籤。我們可以通過從輸入序列的第二個標記開始標記來實現這一點,因為模型無論如何都不會對第一個標記進行預測。然後我們切斷最後一個 logit,因為我們沒有完整輸入序列之後的標記的標籤。有了這個,我們可以計算每個樣本的損失並計算每個樣本中所有關鍵字的出現次數。最後,我們使用出現次數作為權重計算所有樣本的加權平均值。由於我們不想扔掉所有沒有關鍵字的樣本,我們將權重加1: + +```py +from torch.nn import CrossEntropyLoss +import torch + + +def keytoken_weighted_loss(inputs, logits, keytoken_ids, alpha=1.0): + # Shift so that tokens < n predict n + shift_labels = inputs[..., 1:].contiguous() + shift_logits = logits[..., :-1, :].contiguous() + # Calculate per-token loss + loss_fct = CrossEntropyLoss(reduce=False) + loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1)) + # Resize and average loss per sample + loss_per_sample = loss.view(shift_logits.size(0), shift_logits.size(1)).mean(axis=1) + # Calculate and scale weighting + weights = torch.stack([(inputs == kt).float() for kt in keytoken_ids]).sum( + axis=[0, 2] + ) + weights = alpha * (1.0 + weights) + # Calculate weighted average + weighted_loss = (loss_per_sample * weights).mean() + return weighted_loss +``` + +在我們開始使用這個很棒的新損失函數進行訓練之前,我們需要準備一些東西: + +- 我們需要數據加載器來批量加載數據。 +- 我們需要設置權重衰減參數。 +- 有時我們想要求值,因此將求值代碼包裝在一個函數中是有意義的。 + +讓我們從數據加載器開始。我們只需要將數據集的格式設置為 `"torch"`,然後我們可以將它傳遞給 PyTorch `DataLoader` ,同時設置適當的批量大小: + +```py +from torch.utils.data.dataloader import DataLoader + +tokenized_dataset.set_format("torch") +train_dataloader = DataLoader(tokenized_dataset["train"], batch_size=32, shuffle=True) +eval_dataloader = DataLoader(tokenized_dataset["valid"], batch_size=32) +``` + +接下來,我們對參數進行分組,以便優化器知道哪些將獲得額外的權重衰減。通常,所有偏差和 LayerNorm 權重項都不受此限制;以下我們如何做到這一點: + +```py +weight_decay = 0.1 + + +def get_grouped_params(model, no_decay=["bias", "LayerNorm.weight"]): + params_with_wd, params_without_wd = [], [] + for n, p in model.named_parameters(): + if any(nd in n for nd in no_decay): + params_without_wd.append(p) + else: + params_with_wd.append(p) + return [ + {"params": params_with_wd, "weight_decay": weight_decay}, + {"params": params_without_wd, "weight_decay": 0.0}, + ] +``` + +由於我們希望在訓練期間定期在驗證集上評估模型,因此我們也為此編寫一個函數。它只是運行評估數據加載器並收集跨進程的所有損失: + +```py +def evaluate(): + model.eval() + losses = [] + for step, batch in enumerate(eval_dataloader): + with torch.no_grad(): + outputs = model(batch["input_ids"], labels=batch["input_ids"]) + + losses.append(accelerator.gather(outputs.loss)) + loss = torch.mean(torch.cat(losses)) + try: + perplexity = torch.exp(loss) + except OverflowError: + perplexity = float("inf") + return loss.item(), perplexity.item() +``` + +通過 `evaluate()` 函數我們定期可以獲取損失值和[perplexity](/course/chapter7/3)。接下來,我們重新定義我們的模型以確保我們再次從頭開始訓練: + +```py +model = GPT2LMHeadModel(config) +``` + +然後我們可以定義我們的優化器,使用之前的函數來分割權重衰減的參數: + +```py +from torch.optim import AdamW + +optimizer = AdamW(get_grouped_params(model), lr=5e-4) +``` + +現在讓我們準備模型、優化器和數據加載器,以便我們可以開始訓練: + +```py +from accelerate import Accelerator + +accelerator = Accelerator(fp16=True) + +model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare( + model, optimizer, train_dataloader, eval_dataloader +) +``` + + + +🚨 如果您在 TPU 上進行訓練,則需要將從上面的單元格開始的所有代碼移動到專用的訓練函數中。有關詳細信息,請參閱 [第 3 章](/course/chapter3) for more details. + + + +現在我們已經發送了我們的 `train_dataloader`到 `accelerator.prepare()` ,我們可以使用它的長度來計算訓練步驟的數量。請記住,我們應該始終在準備好dataloader後執行此操作,因為該方法會改變其長度。我們使用經典線性學習率調度: + +```py +num_train_epochs = 1 +num_update_steps_per_epoch = len(train_dataloader) +num_training_steps = num_train_epochs * num_update_steps_per_epoch + +lr_scheduler = get_scheduler( + name="linear", + optimizer=optimizer, + num_warmup_steps=1_000, + num_training_steps=num_training_steps, +) +``` + +最後,要將我們的模型推送到 Hub,我們需要創建一個 `Repository` 工作文件夾中的對象。如果您尚未登錄,請先登錄 Hugging Face。我們將從我們想要為模型提供的模型 ID 中確定存儲庫名稱(您可以自由地用自己的選擇替換 `repo_name` ;它只需要包含您的用戶名,可以使用`get_full_repo_name()`函數的查看目前的repo_name): + +```py +from huggingface_hub import Repository, get_full_repo_name + +model_name = "codeparrot-ds-accelerate" +repo_name = get_full_repo_name(model_name) +repo_name +``` + +```python out +'sgugger/codeparrot-ds-accelerate' +``` + +然後我們可以在本地文件夾中克隆該存儲庫。如果它已經存在,這個本地文件夾應該是我們正在使用的存儲庫的克隆: + +```py +output_dir = "codeparrot-ds-accelerate" +repo = Repository(output_dir, clone_from=repo_name) +``` + +我們現在可以上傳我們保存的任何內容 `output_dir` 通過調用 `repo.push_to_hub()` 方法。這將幫助我們在每個 epoch 結束時上傳中間模型。在我們訓練之前,讓我們運行一個快速測試,看看評估函數是否正常工作: + +```py +evaluate() +``` + +```python out +(10.934126853942871, 56057.14453125) +``` + +這些損失和困惑度的值非常高,但這並不奇怪,因為我們還沒有訓練過模型。有了這個,我們已經準備好編寫訓練腳本的核心部分:訓練循環。在訓練循環中,我們遍歷數據加載器並將批次傳遞給模型。有了 logits,我們就可以評估我們的自定義損失函數。我們通過梯度累積步驟的數量來縮放損失,以便在聚合更多步驟時不會產生更大的損失。在我們優化之前,我們還剪輯了梯度以獲得更好的收斂性。最後,每隔幾步,我們就會使用新的 `evaluate()` 函數評估模型: + +```py +from tqdm.notebook import tqdm + +gradient_accumulation_steps = 8 +eval_steps = 5_000 + +model.train() +completed_steps = 0 +for epoch in range(num_train_epochs): + for step, batch in tqdm( + enumerate(train_dataloader, start=1), total=len(train_dataloader) + ): + logits = model(batch["input_ids"]).logits + loss = keytoken_weighted_loss(batch["input_ids"], logits, keytoken_ids) + if step % 100 == 0: + accelerator.print( + { + "lr": get_lr(), + "samples": step * samples_per_step, + "steps": completed_steps, + "loss/train": loss.item() * gradient_accumulation_steps, + } + ) + loss = loss / gradient_accumulation_steps + accelerator.backward(loss) + if step % gradient_accumulation_steps == 0: + accelerator.clip_grad_norm_(model.parameters(), 1.0) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + completed_steps += 1 + if (step % (eval_steps * gradient_accumulation_steps)) == 0: + eval_loss, perplexity = evaluate() + accelerator.print({"loss/eval": eval_loss, "perplexity": perplexity}) + model.train() + accelerator.wait_for_everyone() + unwrapped_model = accelerator.unwrap_model(model) + unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) + if accelerator.is_main_process: + tokenizer.save_pretrained(output_dir) + repo.push_to_hub( + commit_message=f"Training in progress step {step}", blocking=False + ) +``` + +就是這樣 - 您現在擁有自己的因果語言模型(例如 GPT-2)的自定義訓練循環,您可以根據自己的需要進一步自定義。 + + + +✏️ **試試看!** 創建適合您的用例的自定義損失函數,或在訓練循環中添加另一個自定義步驟。 + + + + + +✏️ **試試看!** 在運行長時間的訓練實驗時,最好使用 TensorBoard 或 Weights Biases 等工具記錄重要指標。向訓練循環添加適當的日誌記錄,以便您始終可以檢查訓練的進行情況。going. + + + +{/if} \ No newline at end of file diff --git a/chapters/zh-TW/chapter7/7.mdx b/chapters/zh-TW/chapter7/7.mdx new file mode 100644 index 000000000..075c1f6ff --- /dev/null +++ b/chapters/zh-TW/chapter7/7.mdx @@ -0,0 +1,1210 @@ + + +# 問答 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +是時候看問答了! 這項任務有多種形式, 但我們將在本節中關注的一項稱為*提取*的問答。問題的答案就在 _給定的文檔_ 之中。 + + + +我們將使用 [SQuAD 數據集](https://rajpurkar.github.io/SQuAD-explorer/) 微調一個BERT模型, 其中包括群眾工作者對一組維基百科文章提出的問題。以下是一個小的測試樣例: + + + + +本節使用的代碼已經上傳到了Hub。你可以在 [這裡](https://huggingface.co/huggingface-course/bert-finetuned-squad?context=%F0%9F%A4%97+Transformers+is+backed+by+the+three+most+popular+deep+learning+libraries+%E2%80%94+Jax%2C+PyTorch+and+TensorFlow+%E2%80%94+with+a+seamless+integration+between+them.+It%27s+straightforward+to+train+your+models+with+one+before+loading+them+for+inference+with+the+other.&question=Which+deep+learning+libraries+back+%F0%9F%A4%97+Transformers%3F) 找到它並嘗試用它進行預測。 + + + +💡 像 BERT 這樣的純編碼器模型往往很擅長提取諸如 "誰發明了 Transformer 架構?"之類的事實性問題的答案。但在給出諸如 "為什麼天空是藍色的?" 之類的開放式問題時表現不佳。在這些更具挑戰性的情況下, T5 和 BART 等編碼器-解碼器模型通常使用以與 [文本摘要](/course/chapter7/5) 非常相似的方式合成信息。如果你對這種類型的*生成式*問答感興趣, 我們建議您查看我們基於 [ELI5 數據集](https://huggingface.co/datasets/eli5) 的 [演示](https://yjernite.github.io/lfqa.html)。 + + + +## 準備數據 + +最常用作抽取式問答的學術基準的數據集是 [SQuAD](https://rajpurkar.github.io/SQuAD-explorer/), 所以這就是我們將在這裡使用的。還有一個更難的 [SQuAD v2](https://huggingface.co/datasets/squad_v2) 基準, 其中包括沒有答案的問題。只要你自己的數據集包含上下文列、問題列和答案列, 你就應該能夠調整以下步驟。 + +### SQuAD 數據集 + +像往常一樣, 我們只需一步就可以下載和緩存數據集, 這要歸功於 `load_dataset()`: + +```py +from datasets import load_dataset + +raw_datasets = load_dataset("squad") +``` + +然後我們可以查看這個對象以, 瞭解有關 SQuAD 數據集的更多信息: + +```py +raw_datasets +``` + +```python out +DatasetDict({ + train: Dataset({ + features: ['id', 'title', 'context', 'question', 'answers'], + num_rows: 87599 + }) + validation: Dataset({ + features: ['id', 'title', 'context', 'question', 'answers'], + num_rows: 10570 + }) +}) +``` + +看起來我們擁有所需的 `context` 、`question` 和 `answers` 字段, 所以讓我們打印訓練集的第一個元素: + +```py +print("Context: ", raw_datasets["train"][0]["context"]) +print("Question: ", raw_datasets["train"][0]["question"]) +print("Answer: ", raw_datasets["train"][0]["answers"]) +``` + +```python out +Context: 'Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend "Venite Ad Me Omnes". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.' +Question: 'To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?' +Answer: {'text': ['Saint Bernadette Soubirous'], 'answer_start': [515]} +``` + +`context` 和 `question` 字段使用起來非常簡單。但是 `answers` 字段有點棘手, 因為它將字典與兩個都是列表的字段組成。這是在評估過程中 `squad` 指標所期望的格式; 如果你使用的是自己的數據, 則不必擔心將答案採用相同的格式。`text` 字段比較明顯, 而 `answer_start` 字段包含上下文中每個答案的起始字符索引。 + +在訓練期間, 只有一種可能的答案。我們可以使用 `Dataset.filter()` 方法: + +```py +raw_datasets["train"].filter(lambda x: len(x["answers"]["text"]) != 1) +``` + +```python out +Dataset({ + features: ['id', 'title', 'context', 'question', 'answers'], + num_rows: 0 +}) +``` + +然而, 對於評估, 每個樣本都有幾個可能的答案, 它們可能相同或不同: + +```py +print(raw_datasets["validation"][0]["answers"]) +print(raw_datasets["validation"][2]["answers"]) +``` + +```python out +{'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]} +{'text': ['Santa Clara, California', "Levi's Stadium", "Levi's Stadium in the San Francisco Bay Area at Santa Clara, California."], 'answer_start': [403, 355, 355]} +``` + +我們不會深入研究評估腳本, 因為它都會被一個 🤗 Datasets 指標包裹起來, 但簡短的版本是一些問題有幾個可能的答案, 這個腳本會將預測的答案與所有​​的可接受的答案並獲得最高分。例如, 我們看一下索引 2 處的樣本e: + +```py +print(raw_datasets["validation"][2]["context"]) +print(raw_datasets["validation"][2]["question"]) +``` + +```python out +'Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi\'s Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the "golden anniversary" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as "Super Bowl L"), so that the logo could prominently feature the Arabic numerals 50.' +'Where did Super Bowl 50 take place?' +``` + +我們可以看到, 答案確實可以是我們之前看到的三種可能性之一。 + +### 處理訓練數據 + + + +讓我們從預處理訓練數據開始。困難的部分將是為問題的答案生成標籤, 這將是與上下文中的答案相對應的標記的開始和結束位置。 + +但是, 我們不要超越自己。首先, 我們需要使用分詞器將輸入中的文本轉換為模型可以理解的 ID: + +```py +from transformers import AutoTokenizer + +model_checkpoint = "bert-base-cased" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +``` + +如前所述, 我們將對 BERT 模型進行微調, 但你可以使用任何其他模型類型, 只要它實現了快速標記器即可。你可以在 [this big table](https://huggingface.co/transformers/#supported-frameworks) 中看到所有快速版本的架構, 並檢查你正在使用的 `tokenizer` 對象確實由 🤗 Tokenizers 支持, 你可以查看它的 `is_fast` 屬性: + +```py +tokenizer.is_fast +``` + +```python out +True +``` + +我們可以將問題和上下文一起傳遞給我們的標記器, 它會正確插入特殊標記以形成如下句子: + +``` +[CLS] question [SEP] context [SEP] +``` + +讓我們仔細檢查一下: + +```py +context = raw_datasets["train"][0]["context"] +question = raw_datasets["train"][0]["question"] + +inputs = tokenizer(question, context) +tokenizer.decode(inputs["input_ids"]) +``` + +```python out +'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, ' +'the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin ' +'Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms ' +'upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred ' +'Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a ' +'replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette ' +'Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues ' +'and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]' +``` + +然後標籤將成為開始和結束答案的標記的索引, 並且模型的任務是預測輸入中每個標記的開始和結束 logit, 理論標籤如下: + +
+One-hot encoded labels for question answering. + +
+ +在這種情況下, 上下文不會太長, 但是數據集中的一些示例的上下文很長, 會超過我們設置的最大長度(在這種情況下為 384)。正如我們在 [第六章](/course/chapter6/4) 中所看到的, 當我們探索 `question-answering` 管道的內部結構時, 我們將通過從我們的數據集的一個樣本中創建幾個訓練特徵來處理長上下文, 它們之間有一個滑動窗口。 + +要使用當前示例查看其工作原理, 我們可以將長度限制為 100, 並使用 50 個標記的滑動窗口。提醒一下, 我們使用: + +- `max_length` 設置最大長度 (此處為 100) +- `truncation="only_second"` 用於當帶有上下文的問題太長時, 截斷上下文t (位於第二個位置) +- `stride` 設置兩個連續塊之間的重疊標記數 (這裡為 50) +- `return_overflowing_tokens=True` 讓標記器知道我們想要溢出的標記 + +```py +inputs = tokenizer( + question, + context, + max_length=100, + truncation="only_second", + stride=50, + return_overflowing_tokens=True, +) + +for ids in inputs["input_ids"]: + print(tokenizer.decode(ids)) +``` + +```python out +'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basi [SEP]' +'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin [SEP]' +'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 [SEP]' +'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP]. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]' +``` + +如我們所見, 我們的示例被分成四個輸入, 每個輸入都包含問題和上下文的一部分。 請注意, 問題的答案 ("Bernadette Soubirous") 僅出現在第三個也是最後一個輸入中, 因此通過以這種方式處理長上下文, 我們將創建一些答案不包含在上下文中的訓練示例。對於這些示例, 標籤將是 `start_position = end_position = 0` (所以我們預測 `[CLS]` 標記)。我們還將在答案被截斷的不幸情況下設置這些標籤, 以便我們只有它的開始(或結束)。對於答案完全在上下文中的示例, 標籤將是答案開始的標記的索引和答案結束的標記的索引。 + +數據集為我們提供了上下文中答案的開始字符, 通過添加答案的長度, 我們可以找到上下文中的結束字符。要將它們映射到令牌索引, 我們將需要使用我們在 [第六章](/course/chapter6/4) 中研究的偏移映射。我們可以讓標記器通過傳遞 `return_offsets_mapping=True` 來返回這些值: + +```py +inputs = tokenizer( + question, + context, + max_length=100, + truncation="only_second", + stride=50, + return_overflowing_tokens=True, + return_offsets_mapping=True, +) +inputs.keys() +``` + +```python out +dict_keys(['input_ids', 'token_type_ids', 'attention_mask', 'offset_mapping', 'overflow_to_sample_mapping']) +``` + +如我們所見, 我們取回了通常的輸入 ID、令牌類型 ID 和注意掩碼, 以及我們需要的偏移映射和一個額外的鍵, `overflow_to_sample_mapping`。當我們同時標記多個文本時, 相應的值將對我們有用(我們應該這樣做以受益於我們的標記器由 Rust 支持的事實)。由於一個樣本可以提供多個特徵, 因此它將每個特徵映射到其來源的示例。因為這裡我們只標記了一個例子, 我們得到一個 `0` 的列表: + +```py +inputs["overflow_to_sample_mapping"] +``` + +```python out +[0, 0, 0, 0] +``` + +但是, 如果我們標記更多示例, 這將變得更加有用: + +```py +inputs = tokenizer( + raw_datasets["train"][2:6]["question"], + raw_datasets["train"][2:6]["context"], + max_length=100, + truncation="only_second", + stride=50, + return_overflowing_tokens=True, + return_offsets_mapping=True, +) + +print(f"The 4 examples gave {len(inputs['input_ids'])} features.") +print(f"Here is where each comes from: {inputs['overflow_to_sample_mapping']}.") +``` + +```python out +'The 4 examples gave 19 features.' +'Here is where each comes from: [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3].' +``` + +正如我們所看到的, 前三個示例 (在訓練集中的索引 2、3 和 4 處) 每個都給出了四個特徵, 最後一個示例(在訓練集中的索引 5 處) 給出了 7 個特徵。 + +此信息將有助於將我們獲得的每個特徵映射到其相應的標籤。如前所述, 這些標籤是: + +- `(0, 0)` 如果答案不在上下文的相應範圍內 +- `(start_position, end_position)` 如果答案在上下文的相應範圍內, 則 `start_position` 是答案開頭的標記索引 (在輸入 ID 中), 並且 `end_position` 是答案結束的標記的索引 (在輸入 ID 中)。 + +為了確定是哪種情況以及標記的位置, 以及(如果相關的話)標記的位置, 我們首先在輸入 ID 中找到開始和結束上下文的索引。我們可以使用標記類型 ID 來執行此操作, 但由於這些 ID 不一定存在於所有模型中 (例如, DistilBERT 不需要它們), 我們將改為使用我們的標記器返回的 `BatchEncoding` 的 `sequence_ids()` 方法。 + +一旦我們有了這些標記索引, 我們就會查看相應的偏移量, 它們是兩個整數的元組, 表示原始上下文中的字符範圍。因此, 我們可以檢測此特徵中的上下文塊是在答案之後開始還是在答案開始之前結束(在這種情況下, 標籤是 `(0, 0)`)。如果不是這樣, 我們循環查找答案的第一個和最後一個標記: + +```py +answers = raw_datasets["train"][2:6]["answers"] +start_positions = [] +end_positions = [] + +for i, offset in enumerate(inputs["offset_mapping"]): + sample_idx = inputs["overflow_to_sample_mapping"][i] + answer = answers[sample_idx] + start_char = answer["answer_start"][0] + end_char = answer["answer_start"][0] + len(answer["text"][0]) + sequence_ids = inputs.sequence_ids(i) + + # Find the start and end of the context + idx = 0 + while sequence_ids[idx] != 1: + idx += 1 + context_start = idx + while sequence_ids[idx] == 1: + idx += 1 + context_end = idx - 1 + + # If the answer is not fully inside the context, label is (0, 0) + if offset[context_start][0] > start_char or offset[context_end][1] < end_char: + start_positions.append(0) + end_positions.append(0) + else: + # Otherwise it's the start and end token positions + idx = context_start + while idx <= context_end and offset[idx][0] <= start_char: + idx += 1 + start_positions.append(idx - 1) + + idx = context_end + while idx >= context_start and offset[idx][1] >= end_char: + idx -= 1 + end_positions.append(idx + 1) + +start_positions, end_positions +``` + +```python out +([83, 51, 19, 0, 0, 64, 27, 0, 34, 0, 0, 0, 67, 34, 0, 0, 0, 0, 0], + [85, 53, 21, 0, 0, 70, 33, 0, 40, 0, 0, 0, 68, 35, 0, 0, 0, 0, 0]) +``` + +讓我們看一些結果來驗證我們的方法是否正確。對於我們發現的第一個特徵, 我們將 `(83, 85)` 作為標籤, 讓我們將理論答案與從 83 到 85 (包括)的標記解碼範圍進行比較: + +```py +idx = 0 +sample_idx = inputs["overflow_to_sample_mapping"][idx] +answer = answers[sample_idx]["text"][0] + +start = start_positions[idx] +end = end_positions[idx] +labeled_answer = tokenizer.decode(inputs["input_ids"][idx][start : end + 1]) + +print(f"Theoretical answer: {answer}, labels give: {labeled_answer}") +``` + +```python out +'Theoretical answer: the Main Building, labels give: the Main Building' +``` + +所以這是一場比賽! 現在讓我們檢查索引 4, 我們將標籤設置為 `(0, 0)`, 這意味著答案不在該功能的上下文塊中 + +```py +idx = 4 +sample_idx = inputs["overflow_to_sample_mapping"][idx] +answer = answers[sample_idx]["text"][0] + +decoded_example = tokenizer.decode(inputs["input_ids"][idx]) +print(f"Theoretical answer: {answer}, decoded example: {decoded_example}") +``` + +```python out +'Theoretical answer: a Marian place of prayer and reflection, decoded example: [CLS] What is the Grotto at Notre Dame? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grot [SEP]' +``` + +事實上, 我們在上下文中看不到答案。 + + + +✏️ **輪到你了!** 使用 XLNet 架構時, 在左側應用填充, 並切換問題和上下文。將我們剛剛看到的所有代碼改編為 XLNet 架構 (並添加 `padding=True`)。請注意, `[CLS]` 標記可能不在應用填充的 0 位置。 + + + +現在我們已經逐步瞭解瞭如何預處理我們的訓練數據, 我們可以將其分組到一個函數中, 我們將應用於整個訓練數據集。我們會將每個特徵填充到我們設置的最大長度, 因為大多數上下文會很長 (並且相應的樣本將被分成幾個特徵), 所以在這裡應用動態填充沒有真正的好處: + +```py +max_length = 384 +stride = 128 + + +def preprocess_training_examples(examples): + questions = [q.strip() for q in examples["question"]] + inputs = tokenizer( + questions, + examples["context"], + max_length=max_length, + truncation="only_second", + stride=stride, + return_overflowing_tokens=True, + return_offsets_mapping=True, + padding="max_length", + ) + + offset_mapping = inputs.pop("offset_mapping") + sample_map = inputs.pop("overflow_to_sample_mapping") + answers = examples["answers"] + start_positions = [] + end_positions = [] + + for i, offset in enumerate(offset_mapping): + sample_idx = sample_map[i] + answer = answers[sample_idx] + start_char = answer["answer_start"][0] + end_char = answer["answer_start"][0] + len(answer["text"][0]) + sequence_ids = inputs.sequence_ids(i) + + # Find the start and end of the context + idx = 0 + while sequence_ids[idx] != 1: + idx += 1 + context_start = idx + while sequence_ids[idx] == 1: + idx += 1 + context_end = idx - 1 + + # If the answer is not fully inside the context, label is (0, 0) + if offset[context_start][0] > start_char or offset[context_end][1] < end_char: + start_positions.append(0) + end_positions.append(0) + else: + # Otherwise it's the start and end token positions + idx = context_start + while idx <= context_end and offset[idx][0] <= start_char: + idx += 1 + start_positions.append(idx - 1) + + idx = context_end + while idx >= context_start and offset[idx][1] >= end_char: + idx -= 1 + end_positions.append(idx + 1) + + inputs["start_positions"] = start_positions + inputs["end_positions"] = end_positions + return inputs +``` + +請注意, 我們定義了兩個常數來確定使用的最大長度以及滑動窗口的長度, 並且我們在標記化之前添加了一點清理: SQuAD 數據集中的一些問題在開頭有額外的空格, 並且不添加任何內容的結尾 (如果你使用像 RoBERTa 這樣的模型, 則在標記化時會佔用空間), 因此我們刪除了那些額外的空格。 + +為了將此函數應用於整個訓練集, 我們使用 `Dataset.map()` 方法與 `batched=True` 標誌。這是必要的, 因為我們正在更改數據集的長度(因為一個示例可以提供多個訓練特徵): + +```py +train_dataset = raw_datasets["train"].map( + preprocess_training_examples, + batched=True, + remove_columns=raw_datasets["train"].column_names, +) +len(raw_datasets["train"]), len(train_dataset) +``` + +```python out +(87599, 88729) +``` + +正如我們所見, 預處理增加了大約 1,000 個特徵。我們的訓練集現在可以使用了-- 讓我們深入研究驗證集的預處理! + +### 處理驗證數據 + +預處理驗證數據會稍微容易一些, 因為我們不需要生成標籤(除非我們想計算驗證損失, 但這個數字並不能真正幫助我們理解模型有多好)。真正的樂趣是將模型的預測解釋為原始上下文的跨度。為此, 我們只需要存儲偏移映射和某種方式來將每個創建的特徵與它來自的原始示例相匹配。由於原始數據集中有一個 ID 列, 我們將使用該 ID。 + +我們將在這裡添加的唯一內容是對偏移映射的一點點清理。它們將包含問題和上下文的偏移量, 但是一旦我們進入後處理階段, 我們將無法知道輸入 ID 的哪一部分對應於上下文以及哪一部分是問題(我們使用的 `sequence_ids()` 方法僅可用於標記器的輸出)。因此, 我們將與問題對應的偏移量設置為 `None`: + +```py +def preprocess_validation_examples(examples): + questions = [q.strip() for q in examples["question"]] + inputs = tokenizer( + questions, + examples["context"], + max_length=max_length, + truncation="only_second", + stride=stride, + return_overflowing_tokens=True, + return_offsets_mapping=True, + padding="max_length", + ) + + sample_map = inputs.pop("overflow_to_sample_mapping") + example_ids = [] + + for i in range(len(inputs["input_ids"])): + sample_idx = sample_map[i] + example_ids.append(examples["id"][sample_idx]) + + sequence_ids = inputs.sequence_ids(i) + offset = inputs["offset_mapping"][i] + inputs["offset_mapping"][i] = [ + o if sequence_ids[k] == 1 else None for k, o in enumerate(offset) + ] + + inputs["example_id"] = example_ids + return inputs +``` + +我們可以像以前一樣將此函數應用於整個驗證數據集: + +```py +validation_dataset = raw_datasets["validation"].map( + preprocess_validation_examples, + batched=True, + remove_columns=raw_datasets["validation"].column_names, +) +len(raw_datasets["validation"]), len(validation_dataset) +``` + +```python out +(10570, 10822) +``` + +I在這種情況下, 我們只添加了幾百個樣本, 因此驗證數據集中的上下文似乎有點短。 + +現在我們已經對所有數據進行了預處理, 我們可以開始訓練了。 + +{#if fw === 'pt'} + +## 使用 `Trainer` API 微調模型 + +這個例子的訓練代碼看起來很像前面幾節中的代碼 -- 最難的是編寫 `compute_metrics()` 函數。由於我們將所有樣本填充到我們設置的最大長度, 因此沒有數據整理器要定義, 所以這個度量計算真的是我們唯一需要擔心的事情。困難的部分是將模型預測後處理為原始示例中的文本範圍; 一旦我們這樣做了, 🤗 Datasets 庫中的指標將為我們完成大部分工作。 + +{:else} + +## 使用 Keras 微調模型 + +這個示例的訓練代碼看起來很像前幾節中的代碼, 但是計算指標將是唯一的挑戰。因為我們將所有的樣本填充到我們設置的最大長度, 所以不需要定義數據整理器, 所以這個度量計算實際上是我們唯一需要擔心的事情。困難的部分是將模型預測後處理成原始例子中的文本範圍; 一旦我們完成了這些, 🤗 Datasets 庫中的指標將為我們完成大部分工作。 + +{/if} + +### 後處理 + +{#if fw === 'pt'} + + + +{:else} + + + +{/if} + +該模型將在輸入ID中為答案的開始和結束位置輸出Logit, 正如我們在探索 [`question-answering` pipeline](/course/chapter6/4) 時看到的那樣。後處理步驟將類似於我們在那裡所做的, 所以這裡是我們採取的行動的快速提醒: + +- 我們屏蔽了與上下文之外的標記相對應的開始和結束 logits。 +- 然後, 我們使用 softmax 將開始和結束 logits 轉換為概率。 +- 我們通過取對應的兩個概率的乘積來給每個 `(start_token, end_token)` 組合賦值。 +- 我們尋找產生有效答案的最高分數的配對 (例如, `start_token` 低於 `end_token`)。 + +在這裡, 我們將稍微改變這個過程, 因為我們不需要計算實際分數 (只是預測的答案)。這意味著我們可以跳過 softmax 步驟。為了更快, 我們也不會對所有可能的 `(start_token, end_token)` 對進行評分, 而只會對對應於最高 `n_best` 的那些對進行評分 (使用 `n_best=20`)。由於我們將跳過 softmax, 因此這些分數將是 logit 分數, 並且將通過取 start 和 end logits 的總和來獲得 (而不是乘積, 因為規則 \\(\log(ab) = \log(a) + \log(b)\\))。 + +為了證明這一切, 我們需要一些預測。由於我們還沒有訓練我們的模型, 我們將使用 QA 管道的默認模型對一小部分驗證集生成一些預測。我們可以使用和之前一樣的處理函數; 因為它依賴於全局常量 `tokenizer`, 我們只需將該對象更改為我們要臨時使用的模型的標記器: + +```python +small_eval_set = raw_datasets["validation"].select(range(100)) +trained_checkpoint = "distilbert-base-cased-distilled-squad" + +tokenizer = AutoTokenizer.from_pretrained(trained_checkpoint) +eval_set = small_eval_set.map( + preprocess_validation_examples, + batched=True, + remove_columns=raw_datasets["validation"].column_names, +) +``` + +現在預處理已經完成, 我們將分詞器改回我們最初選擇的那個: + +```python +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +``` + +然後, 我們刪除 `eval_set` 中模型不期待的列, 用所有的小驗證集構建一個批次, 然後通過模型。如果 GPU 可用, 我們會使用它來加快速度: + +{#if fw === 'pt'} + +```python +import torch +from transformers import AutoModelForQuestionAnswering + +eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"]) +eval_set_for_model.set_format("torch") + +device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") +batch = {k: eval_set_for_model[k].to(device) for k in eval_set_for_model.column_names} +trained_model = AutoModelForQuestionAnswering.from_pretrained(trained_checkpoint).to( + device +) + +with torch.no_grad(): + outputs = trained_model(**batch) +``` + +由於 `Trainer` 將為我們提供 NumPy 數組的預測, 我們獲取開始和結束 logits 並將它們轉換為該格式 + +```python +start_logits = outputs.start_logits.cpu().numpy() +end_logits = outputs.end_logits.cpu().numpy() +``` + +{:else} + +```python +import tensorflow as tf +from transformers import TFAutoModelForQuestionAnswering + +eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"]) +eval_set_for_model.set_format("numpy") + +batch = {k: eval_set_for_model[k] for k in eval_set_for_model.column_names} +trained_model = TFAutoModelForQuestionAnswering.from_pretrained(trained_checkpoint) + +outputs = trained_model(**batch) +``` + +為了便於實驗, 讓我們將這些輸出轉換為 NumPy 數組: + +```python +start_logits = outputs.start_logits.numpy() +end_logits = outputs.end_logits.numpy() +``` + +{/if} + +現在, 我們需要在 `small_eval_set` 中找到每個示例的預測答案。一個示例可能已經在 `eval_set` 中拆分為多個特徵, 因此第一步是將 `small_eval_set` 中的每個示例映射到 `eval_set` 中相應的特徵: + +```python +import collections + +example_to_features = collections.defaultdict(list) +for idx, feature in enumerate(eval_set): + example_to_features[feature["example_id"]].append(idx) +``` + +有了這個, 我們就可以真正開始工作, 循環遍歷所有示例, 併為每個示例遍歷所有相關功能。正如我們之前所說, 我們將查看 `n_best` 開始 logits 和結束 logits 的 logit 分數, 不包括以下的位置: + +- 一個不在上下文中的答案 +- 長度為負的答案 +- 答案太長 (我們將可能性限制在 `max_answer_length=30`) + +一旦我們為一個示例獲得了所有可能的答案, 我們只需選擇一個具有最佳 logit 分數的答案: + +```python +import numpy as np + +n_best = 20 +max_answer_length = 30 +predicted_answers = [] + +for example in small_eval_set: + example_id = example["id"] + context = example["context"] + answers = [] + + for feature_index in example_to_features[example_id]: + start_logit = start_logits[feature_index] + end_logit = end_logits[feature_index] + offsets = eval_set["offset_mapping"][feature_index] + + start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist() + end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist() + for start_index in start_indexes: + for end_index in end_indexes: + # Skip answers that are not fully in the context + if offsets[start_index] is None or offsets[end_index] is None: + continue + # Skip answers with a length that is either < 0 or > max_answer_length. + if ( + end_index < start_index + or end_index - start_index + 1 > max_answer_length + ): + continue + + answers.append( + { + "text": context[offsets[start_index][0] : offsets[end_index][1]], + "logit_score": start_logit[start_index] + end_logit[end_index], + } + ) + + best_answer = max(answers, key=lambda x: x["logit_score"]) + predicted_answers.append({"id": example_id, "prediction_text": best_answer["text"]}) +``` + +預測答案的最終格式是我們將使用的度量標準所期望的格式。像往常一樣, 我們可以在 🤗 Datasets 庫的幫助下加載它: + +```python +from datasets import load_metric + +metric = load_metric("squad") +``` + +該指標期望我們上面看到的格式的預測答案 (一個字典列表, 其中一個鍵用於示例 ID, 一個鍵用於預測文本) 和以下格式的理論答案 (一個字典列表, 一個鍵示例的 ID 和可能答案的一鍵): + +```python +theoretical_answers = [ + {"id": ex["id"], "answers": ex["answers"]} for ex in small_eval_set +] +``` + +我們現在可以通過查看兩個列表的第一個元素來檢查我們是否得到了合理的結果: + +```python +print(predicted_answers[0]) +print(theoretical_answers[0]) +``` + +```python out +{'id': '56be4db0acb8001400a502ec', 'prediction_text': 'Denver Broncos'} +{'id': '56be4db0acb8001400a502ec', 'answers': {'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]}} +``` + +還不錯! 現在讓我們看看這個指標給我們的分數: + +```python +metric.compute(predictions=predicted_answers, references=theoretical_answers) +``` + +```python out +{'exact_match': 83.0, 'f1': 88.25} +``` + +同樣, 考慮到根據 [its paper](https://arxiv.org/abs/1910.01108v2), 在 SQuAD 上微調的 DistilBERT 在整個數據集上的得分分別為 79.1 和 86.9, 這是相當不錯的。 + +{#if fw === 'pt'} + +現在, 讓我們把剛才所做的一切放在 `compute_metrics()` 函數中, 我們將在 `Trainer` 中使用它。通常, `compute_metrics()` 函數只接收一個包含 logits 和 labels 的元組 `eval_preds`。這裡我們需要更多, 因為我們必須在特徵數據集中查找偏移量, 在原始上下文的示例數據集中查找, 因此我們將無法在訓練期間使用此函數獲得常規評估結果。我們只會在訓練結束時使用它來檢查結果。 + +`compute_metrics()` 函數將與前面相同的步驟分組; 我們只是添加一個小檢查, 以防我們沒有提出任何有效的答案 (在這種情況下, 我們預測一個空字符串)。 + +{:else} + +現在, 讓我們將剛才所做的一切放入 `compute_metrics()` 函數中, 我們將在訓練模型後使用該函數。我們需要傳遞的不僅僅是輸出日誌, 因為我們必須在特徵數據集中尋找偏移量, 在原始上下文的示例數據集中尋找: + +{/if} + +```python +from tqdm.auto import tqdm + + +def compute_metrics(start_logits, end_logits, features, examples): + example_to_features = collections.defaultdict(list) + for idx, feature in enumerate(features): + example_to_features[feature["example_id"]].append(idx) + + predicted_answers = [] + for example in tqdm(examples): + example_id = example["id"] + context = example["context"] + answers = [] + + # Loop through all features associated with that example + for feature_index in example_to_features[example_id]: + start_logit = start_logits[feature_index] + end_logit = end_logits[feature_index] + offsets = features[feature_index]["offset_mapping"] + + start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist() + end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist() + for start_index in start_indexes: + for end_index in end_indexes: + # Skip answers that are not fully in the context + if offsets[start_index] is None or offsets[end_index] is None: + continue + # Skip answers with a length that is either < 0 or > max_answer_length + if ( + end_index < start_index + or end_index - start_index + 1 > max_answer_length + ): + continue + + answer = { + "text": context[offsets[start_index][0] : offsets[end_index][1]], + "logit_score": start_logit[start_index] + end_logit[end_index], + } + answers.append(answer) + + # Select the answer with the best score + if len(answers) > 0: + best_answer = max(answers, key=lambda x: x["logit_score"]) + predicted_answers.append( + {"id": example_id, "prediction_text": best_answer["text"]} + ) + else: + predicted_answers.append({"id": example_id, "prediction_text": ""}) + + theoretical_answers = [{"id": ex["id"], "answers": ex["answers"]} for ex in examples] + return metric.compute(predictions=predicted_answers, references=theoretical_answers) +``` + +我們可以檢查它是否適用於我們的預測: + +```python +compute_metrics(start_logits, end_logits, eval_set, small_eval_set) +``` + +```python out +{'exact_match': 83.0, 'f1': 88.25} +``` + +看起來不錯! 現在讓我們用它來微調我們的模型。 + +### 微調模型 + +{#if fw === 'pt'} + +我們現在準備好訓練我們的模型了。讓我們首先創建它, 像以前一樣使用 `AutoModelForQuestionAnswering` 類: + +```python +model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint) +``` + +{:else} + +我們現在準備好訓練我們的模型了。讓我們首先創建它, 像以前一樣使用 `TFAutoModelForQuestionAnswering` 類: + +```python +model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint) +``` + +{/if} + +像往常一樣, 我們收到一個警告, 有些權重沒有使用(來自預訓練頭的), 而另一些是隨機初始化的 (用於問答頭的)。你現在應該已經習慣了, 但這意味著這個模型還沒有準備好使用, 需要微調 -- 我們即將這樣做! + +為了能夠將我們的模型推送到 Hub, 我們需要登錄 Hugging Face。 如果你在筆記本中運行此代碼, 則可以使用以下實用程序函數執行此操作, 該函數會顯示一個小部件, 你可以在其中輸入登錄憑據: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +如果你不在筆記本中工作, 只需在終端中鍵入以下行: + +```bash +huggingface-cli login +``` + +{#if fw === 'pt'} + +完成後, 我們就可以定義我們的 `TrainingArguments`。正如我們在定義函數來計算度量時所說的那樣, 由於 `compute_metrics()` 函數的簽名, 我們將不能有常規的求值循環。我們可以編寫 `Trainer` 的子類來完成這一任務(你可以在 [question answering example script](https://github.com/huggingface/transformers/blob/master/examples/pytorch/question-answering/trainer_qa.py)中找到一種方法), 但這對於本節來說有點太長了。相反, 我們只會在訓練結束時評估模型, 並在下面的"自定義訓練循環"向你展示如何進行常規評估。 + +T這確實時 `Trainer` API 顯示其侷限性和 🤗 Accelerate 庫的亮點所在: 根據特定用例自定義類可能很痛苦, 但調整完全公開的訓練循環很容易。 + +來看看我們的 `TrainingArguments`: + +```python +from transformers import TrainingArguments + +args = TrainingArguments( + "bert-finetuned-squad", + evaluation_strategy="no", + save_strategy="epoch", + learning_rate=2e-5, + num_train_epochs=3, + weight_decay=0.01, + fp16=True, + push_to_hub=True, +) +``` + +我們之前已經看到了其中的大部分: 我們設置了一些超參數 (比如學習率、訓練的 epoch 數和一些權重衰減), 並表明我們希望在每個 epoch 結束時保存模型, 跳過評估, 並將我們的結果上傳到模型中心。我們還使用 `fp16=True` 啟用混合精度訓練, 因為它可以在最近的 GPU 上很好地加快訓練速度。 + +{:else} + +現在, 我們可以創建我們的 TF 數據集。這次我們可以使用簡單的默認數據整理器: + +```python +from transformers import DefaultDataCollator + +data_collator = DefaultDataCollator(return_tensors="tf") +``` + +現在我們像往常一樣創建數據集。 + +```python +tf_train_dataset = train_dataset.to_tf_dataset( + columns=[ + "input_ids", + "start_positions", + "end_positions", + "attention_mask", + "token_type_ids", + ], + collate_fn=data_collator, + shuffle=True, + batch_size=16, +) +tf_eval_dataset = validation_dataset.to_tf_dataset( + columns=["input_ids", "attention_mask", "token_type_ids"], + collate_fn=data_collator, + shuffle=False, + batch_size=16, +) +``` + +接下來, 我們設置了我們的訓練超參數並編譯了我們的模型: + +```python +from transformers import create_optimizer +from transformers.keras_callbacks import PushToHubCallback +import tensorflow as tf + +# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied +# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset, +# not the original Hugging Face Dataset, so its len() is already num_samples // batch_size. +num_train_epochs = 3 +num_train_steps = len(tf_train_dataset) * num_train_epochs +optimizer, schedule = create_optimizer( + init_lr=2e-5, + num_warmup_steps=0, + num_train_steps=num_train_steps, + weight_decay_rate=0.01, +) +model.compile(optimizer=optimizer) + +# Train in mixed-precision float16 +tf.keras.mixed_precision.set_global_policy("mixed_float16") +``` + +最後, 我們準備好使用 `model.fit()` 進行訓練了。我們使用 `PushToHubCallback` 在每個時期之後將模型上傳到Hub。 + +{/if} + +默認情況下, 使用的存儲庫將在您的命名空間中, 並以您設置的輸出目錄命名, 因此在我們的例子中, 它將在 `"sgugger/bert-finetuned-squad"` 中。我們可以通過傳遞 `hub_model_id` 來覆蓋它; 例如, 為了將模型推送到 `huggingface_course` 組織, 我們使用了 `hub_model_id="huggingface_course/bert-finetuned-squad"` (這是我們在本節開頭鏈接的模型)。 + +{#if fw === 'pt'} + + + +💡 如果您使用的輸出目錄存在, 則它需要是您要推送到的存儲庫的本地克隆 (因此, 如果在定義 `Trainer` 時出錯, 請設置新名稱)。 + + + +最後, 我們只需將所有內容傳遞給 `Trainer` 類並啟動訓練: + +```python +from transformers import Trainer + +trainer = Trainer( + model=model, + args=args, + train_dataset=train_dataset, + eval_dataset=validation_dataset, + tokenizer=tokenizer, +) +trainer.train() +``` + +{:else} + +```python +from transformers.keras_callbacks import PushToHubCallback + +callback = PushToHubCallback(output_dir="bert-finetuned-squad", tokenizer=tokenizer) + +# We're going to do validation afterwards, so no validation mid-training +model.fit(tf_train_dataset, callbacks=[callback], epochs=num_train_epochs) +``` + +{/if} + +請注意, 在進行訓練時, 每次保存模型時 (這裡是每個 epoch) 它都會在後臺上傳到 Hub。這樣, 如有必要, 你將能夠在另一臺機器上恢復訓練。整個培訓需要一段時間 (在 Titan RTX 上需要一個多小時), 因此您可以喝杯咖啡或重讀課程中您發現在進行過程中更具挑戰性的部分內容。另請注意, 一旦第一個 epoch 完成, 你將看到一些權重上傳到 Hub, 你可以開始在其頁面上使用你的模型。 + +{#if fw === 'pt'} + +一旦訓練完成, 我們終於可以評估我們的模型(並祈禱我們沒有把所有的計算時間都花在任何事情上)。`Trainer` 的 `predict()` 方法將返回一個元組, 其中第一個元素將是模型的預測 (這裡是帶有開始和結束 logits 的對)。我們將其發送給 `compute_metrics()` 函數: + +```python +predictions, _ = trainer.predict(validation_dataset) +start_logits, end_logits = predictions +compute_metrics(start_logits, end_logits, validation_dataset, raw_datasets["validation"]) +``` + +{:else} + +一旦訓練完成, 我們終於可以評估我們的模型(並祈禱我們沒有把所有的計算時間都花在任何事情上)。我們的 `model` 的 `predict()` 方法將負責獲取預測, 並且由於我們之前已經完成了定義 `compute_metrics()` 函數的所以艱苦工作, 因此我們可以在一行中獲得結果: + +```python +predictions = model.predict(tf_eval_dataset) +compute_metrics( + predictions["start_logits"], + predictions["end_logits"], + validation_dataset, + raw_datasets["validation"], +) +``` + +{/if} + +```python out +{'exact_match': 81.18259224219489, 'f1': 88.67381321905516} +``` + +很好! 作為比較, BERT 文章中報告的該模型的基線分數是 80.8 和 88.5, 所以我們應該是正確的。 + +{#if fw === 'pt'} + +最後, 我們使用 `push_to_hub()` 方法確保我們上傳模型的最新版本: + +```py +trainer.push_to_hub(commit_message="Training complete") +``` + +如果你想檢查它, 這將返回它剛剛執行的提交的 URL: + +```python out +'https://huggingface.co/sgugger/bert-finetuned-squad/commit/9dcee1fbc25946a6ed4bb32efb1bd71d5fa90b68' +``` + +`Trainer` 還起草了包含所有評估結果的模型卡並上傳。 + +{/if} + +在這個階段, 你可以使用模型中心上的推理小部件來測試模型並與您的朋友、家人和最喜歡的寵物分享。你已經成功地微調了一個問答任務的模型 -- 恭喜! + + + +✏️ **輪到你了!** 嘗試另一種模型架構, 看看它是否在此任務上表現更好! + + + +{#if fw === 'pt'} + +如果你想更深入地瞭解訓練循環, 我們現在將向你展示如何使用 🤗 Accelerate 來做同樣的事情。 + +## 自定義訓練循環 + +現在讓我們來看一下完整的訓練循環, 這樣您就可以輕鬆地自定義所需的部分。它看起來很像 [第三章](/course/chapter3/4) 中的訓練循環, 除了評估循環。我們將能夠定期評估模型, 因為我們不再受 `Trainer` 類的限制。 + +### 為訓練做準備 + +首先, 我們需要從我們的數據集中構建 `DataLoader`。我們將這些數據集的格式設置為 `"torch"`, 並刪除模型未使用的驗證集中的列。然後, 我們可以使用 Transformers 提供的 `default_data_collator` 作為 `collate_fn`, 並打亂訓練集, 但不打亂驗證集58: + +```py +from torch.utils.data import DataLoader +from transformers import default_data_collator + +train_dataset.set_format("torch") +validation_set = validation_dataset.remove_columns(["example_id", "offset_mapping"]) +validation_set.set_format("torch") + +train_dataloader = DataLoader( + train_dataset, + shuffle=True, + collate_fn=default_data_collator, + batch_size=8, +) +eval_dataloader = DataLoader( + validation_set, collate_fn=default_data_collator, batch_size=8 +) +``` + +接下來我們重新實例化我們的模型, 以確保我們不會繼續之前的微調, 而是再次從 BERT 預訓練模型開始: + +```py +model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint) +``` + +然後我們需要一個優化器。像往常一樣, 我們使用經典的 `AdamW`, 它與 Adam 類似, 但對權重衰減的應用方式進行了修復: + +```py +from torch.optim import AdamW + +optimizer = AdamW(model.parameters(), lr=2e-5) +``` + +一旦我們擁有所有這些對象, 我們可以將它們發送給 `accelerator.prepare()` 方法。請記住, 如果您想在 Colab 筆記本中的 TPU 上進行訓練, 您需要將所有這些代碼移動到一個訓練函數中, 並且不應該執行任何實例化 `Accelerator` 的單元。我們可以通過傳遞 `fp16=True` 給 `Accelerator` (或者, 如果你將代碼作為腳本執行, 只需確保適當地填寫 🤗 Accelerate `config` )。 + +```py +from accelerate import Accelerator + +accelerator = Accelerator(fp16=True) +model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare( + model, optimizer, train_dataloader, eval_dataloader +) +``` + +從前面幾節中你應該知道, 我們只能使用 `train_dataloader` 長度來計算經過 `accelerator.prepare()` 方法後的訓練步驟的數量。我們使用與前幾節相同的線性時間表: + +```py +from transformers import get_scheduler + +num_train_epochs = 3 +num_update_steps_per_epoch = len(train_dataloader) +num_training_steps = num_train_epochs * num_update_steps_per_epoch + +lr_scheduler = get_scheduler( + "linear", + optimizer=optimizer, + num_warmup_steps=0, + num_training_steps=num_training_steps, +) +``` + +要將我們的模型推送到 Hub, 我們需要在工作文件夾中創建一個 `Repository` 對象。如果你尚未登錄, 請先登錄 Hugging Face Hub。我們將根據我們想要為模型提供的模型 ID 確定存儲庫名稱 (隨意用你自己的選擇替換 `repo_name`; 它只需要包含你的用戶名, 這就是函數 `get_full_repo_name()` 所做的): + +```py +from huggingface_hub import Repository, get_full_repo_name + +model_name = "bert-finetuned-squad-accelerate" +repo_name = get_full_repo_name(model_name) +repo_name +``` + +```python out +'sgugger/bert-finetuned-squad-accelerate' +``` + +然後我們可以將該存儲庫克隆到本地文件夾中。如果它已經存在, 這個本地文件夾應該是我們正在使用的存儲庫的克隆: + +```py +output_dir = "bert-finetuned-squad-accelerate" +repo = Repository(output_dir, clone_from=repo_name) +``` + +我們現在可以通過調用 `repo.push_to_hub()` 方法上傳我們保存在 `output_dir` 中的任何內容。這將幫助我們在每個 epoch 結束時上傳中間模型。 + +## 訓練循環 + +我們現在準備編寫完整的訓練循環。在定義了一個進度條來跟蹤訓練進行後, 循環分為三個部分: + +- 訓練本身是對 `train_dataloader` 的經典迭代, 前向傳遞模型, 然後反向傳遞和優化器步驟。 +- 在計算中, 我們在將 `start_logits` 和 `end_logits` 的所有值轉換為 NumPy 數組之前, 收集它們的所有值。評估循環完成後,我們將連接所有結果。請注意, 我們需要截斷, 因為 `Accelerator` 可能在最後添加了一些示例, 以確保我們在每個過程中擁有相同數量的示例。 +- 保存和上傳, 這裡我們先保存模型和分詞器, 然後調用 `repo.push_to_hub()`。正如我們之前所做的那樣, 我們使用參數 `blocking=False` 來告訴 🤗 Hub 庫推入一個異步進程。這樣, 訓練正常繼續, 並且這個 (長) 指令在後臺執行。 + +這是訓練循環的完整代碼: + +```py +from tqdm.auto import tqdm +import torch + +progress_bar = tqdm(range(num_training_steps)) + +for epoch in range(num_train_epochs): + # Training + model.train() + for step, batch in enumerate(train_dataloader): + outputs = model(**batch) + loss = outputs.loss + accelerator.backward(loss) + + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + progress_bar.update(1) + + # Evaluation + model.eval() + start_logits = [] + end_logits = [] + accelerator.print("Evaluation!") + for batch in tqdm(eval_dataloader): + with torch.no_grad(): + outputs = model(**batch) + + start_logits.append(accelerator.gather(outputs.start_logits).cpu().numpy()) + end_logits.append(accelerator.gather(outputs.end_logits).cpu().numpy()) + + start_logits = np.concatenate(start_logits) + end_logits = np.concatenate(end_logits) + start_logits = start_logits[: len(validation_dataset)] + end_logits = end_logits[: len(validation_dataset)] + + metrics = compute_metrics( + start_logits, end_logits, validation_dataset, raw_datasets["validation"] + ) + print(f"epoch {epoch}:", metrics) + + # Save and upload + accelerator.wait_for_everyone() + unwrapped_model = accelerator.unwrap_model(model) + unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) + if accelerator.is_main_process: + tokenizer.save_pretrained(output_dir) + repo.push_to_hub( + commit_message=f"Training in progress epoch {epoch}", blocking=False + ) +``` + +如果這是您第一次看到使用 🤗 Accelerate 保存的模型, 讓我們花點時間檢查一下它附帶的三行代碼: + +```py +accelerator.wait_for_everyone() +unwrapped_model = accelerator.unwrap_model(model) +unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) +``` + +第一行是不言自明的: 它告訴所有進程要等到每個人都處於那個階段才能繼續。這是為了確保我們在保存之前在每個過程中都有相同的模型。然後我們獲取 `unwrapped_model`, 這是我們定義的基礎模型。 `accelerator.prepare()` 方法將模型更改為在分佈式訓練中工作, 因此它將不再有 `save_pretrained()` 方法; `accelerator.unwrap_model()` 方法會撤銷該步驟。最後, 我們調用 `save_pretrained()`, 但告訴該方法使用 `accelerator.save()` 而不是 `torch.save()`。 + +完成後, 你應該擁有一個模型, 該模型產生的結果與使用 `Trainer` 訓練的模型非常相似。你可以在 [*huggingface-course/bert-finetuned-squad-accelerate*](https://huggingface.co/huggingface-course/bert-finetuned-squad-accelerate) 上查看我們使用此代碼訓練的模型。如果你想測試對訓練循環的任何調整, 你可以通過編輯上面顯示的代碼直接實現它們! + +{/if} + +## 使用微調模型 + +我們已經向您展示瞭如何將我們在模型中心微調的模型與推理小部件一起使用。要在 `pipeline` 中本地使用它, 你只需要指定模型標識符: + +```py +from transformers import pipeline + +# Replace this with your own checkpoint +model_checkpoint = "huggingface-course/bert-finetuned-squad" +question_answerer = pipeline("question-answering", model=model_checkpoint) + +context = """ +🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration +between them. It's straightforward to train your models with one before loading them for inference with the other. +""" +question = "Which deep learning libraries back 🤗 Transformers?" +question_answerer(question=question, context=context) +``` + +```python out +{'score': 0.9979003071784973, + 'start': 78, + 'end': 105, + 'answer': 'Jax, PyTorch and TensorFlow'} +``` + +很棒! 我們的模型與該管道的默認模型一樣有效! diff --git a/chapters/zh-TW/chapter7/8.mdx b/chapters/zh-TW/chapter7/8.mdx new file mode 100644 index 000000000..e2e651d3f --- /dev/null +++ b/chapters/zh-TW/chapter7/8.mdx @@ -0,0 +1,17 @@ +# 精通自然語言處理 + +如果你在課程中做到了這一步,恭喜你——你現在擁有了用 🤗 Transformers 和 Hugging Face 生態系統解決(幾乎)任何 NLP 任務所需的所有知識和工具! + +我們見過很多不同的數據整理器,所以我們製作了這個小視頻來幫助您找到每個任務使用哪一個: + + + +在完成核心 NLP 任務的快速入門後,您應該: + +* 瞭解哪種架構(編碼器、解碼器或編碼器-解碼器)最適合每個任務 +* 瞭解預訓練和微調語言模型之間的區別 +* 瞭解如何使用 `Trainer` API 和 🤗 Accelerate 或 TensorFlow 和 Keras 的分佈式訓練功能來訓練 Transformer 模型,具體選擇那一種方法取決於您所需要完成的任務。 +* 瞭解 ROUGE 和 BLEU 等指標在文本生成任務中的意義和侷限性 +* 知道如何在 Hub 上和使用 🤗 Transformers 中的“管道”與您的微調模型進行交互 + +儘管掌握了所有這些知識,但總有一天你會遇到代碼中的困難錯誤,或者對如何解決特定的 NLP 問題有疑問。幸運的是,Hugging Face 社區隨時為您提供幫助!在這部分課程的最後一章中,我們將探討如何調試 Transformer 模型並有效地尋求幫助。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter7/9.mdx b/chapters/zh-TW/chapter7/9.mdx new file mode 100644 index 000000000..4852756bd --- /dev/null +++ b/chapters/zh-TW/chapter7/9.mdx @@ -0,0 +1,311 @@ + + + + +# End-of-chapter quiz + +Let's test what you learned in this chapter! + +### 1.下列哪個任務可以被框定為令牌分類問題? + + +### 2.令牌分類預處理的哪一部分與其他預處理管道不同? +-100 作為我們希望在丟失時忽略的令牌的標籤。" + }, + { + text: "因為互聯網上有大量的文本", + explain: "的確如此! 但這並不是唯一的區別。", + correct: true + } + ]} +/> + +### 3.當我們對標記分類問題中的單詞進行標記並希望標記時,會出現什麼問題? +-100 標記為 < code > ,以便在丟失時忽略它們。" + }, + { + text: "每個單詞可以產生幾個標記,所以我們最終得到的標記比標籤多。", + explain: "這是主要的問題,我們需要將原始標籤與標記對齊。", + correct: true + }, + { + text: "因為目標是按順序排列的文本問題", + explain: "這是不正確的; 我們需要儘可能多的標籤,否則我們的模型就會出錯。" + } + ]} +/> + +### 4.“領域適應”是什麼意思? + + +### 5.掩碼語言建模問題中的標籤是什麼? + + +### 6.這些任務中的哪一個可以看作是一個順序到順序的問題? +-100 來標記特殊標記。", + explain: "這絕對是一個從序列到序列的問題。你能發現另一個嗎?", + correct: true + }, + { + text: "修正我侄子/朋友發來的信息,使它們用正確的英語", + explain: "這是一種翻譯問題,所以肯定是一個順序到順序的任務。但這不是唯一正確的答案!", + correct: true + } + ]} +/> + +### 7.對於序列到序列的問題,預處理數據的正確方法是什麼? + input = ... 和 < code > target = ... 。", + explain: "這可能是我們將來添加的一個 API,但現在不可能。" + }, + { + text: "輸入和目標都必須在對標記器的兩個獨立調用中進行預處理。", + explain: "不,這是在訓練一個模型; 這裡沒有適應性。" + }, + { + text: "因為我們在訓練之後計算度量", + explain: "不是在序列分類問題; 目標也是文本,我們需要轉換成數字!" + }, + { + text: "輸入必須發送到標記器,目標也是如此,但需要使用特殊的上下文管理器。", + explain: "沒錯,標記器需要由上下文管理器放入目標模式。", + correct: true + } + ]} +/> + +{#if fw === 'pt'} + +### 8.為什麼序列到序列問題有一個特定的“培訓者”子類? +-100 的標籤", + explain: "這根本不是習慣性的損失,而是損失總是通過計算得到的。" + }, + { + text: "當您擁有大量可用數據時,即使有一個經過預先訓練的模型可以處理這些數據", + explain: "沒錯。 Sequence-to-sequence models' predictions are often run using the generate() method.", + correct: true + }, + { + text: "文本是作為單詞給出的,所以我們只需要應用子詞的標記。", + explain: "< code > Trainer 並不關心這些,因為它們以前已經被預處理過。" + }, + { + text: "因為我們在序列到序列問題中使用了兩個模型", + explain: "我們確實在某種程度上使用了兩種模型,編碼器和解碼器,但是它們被組合在一個模型中。" + } + ]} +/> + +{:else} + +### 9.為什麼在 Transformer 模型上調用“ compile ()”時通常不需要指定損失? + input = ... 和 < code > target = ... 。", + explain: "沒錯!", + correct: true + }, + { + text: "因為我們在訓練之後計算指標", + explain: "這可以被定義為一個從序列到序列的問題,儘管這不是唯一正確的答案。" + }, + { + text: "因為損失是在“ model.fit ()”中指定的", + explain: "不,損失函數在運行‘ model.com pile ()’時是固定的,不能在‘ model.fit ()’中更改。" + } + ]} +/> + +{/if} + +### 10.你應該在什麼時候預先訓練一個新的模型? + + +### 11.為什麼在大量的文本上預先訓練一個語言模型是很容易的呢? + + +### 12.問答任務的預處理數據時,主要的挑戰是什麼? + + +### 13.問題回答中的後處理通常是怎樣進行的? + diff --git a/chapters/zh-TW/chapter8/1.mdx b/chapters/zh-TW/chapter8/1.mdx new file mode 100644 index 000000000..525a21b2e --- /dev/null +++ b/chapters/zh-TW/chapter8/1.mdx @@ -0,0 +1,12 @@ +# 介紹 + +既然您知道如何使用🤗 Transformers處理最常見的NLP任務,您應該能夠開始自己的項目了!在本章中,我們將探討遇到問題時該怎麼做。您將學習如何成功調試代碼或訓練,以及如果自己無法解決問題,如何向社區尋求幫助。如果您認為您在其中一個擁抱人臉庫中發現了錯誤,我們將向您展示報告它的最佳方式,以便儘快解決問題。 + +更準確地說,在本章中,您將學習: + +- 出現錯誤時要做的第一件事 +- 如何在網上尋求幫助[forums](https://discuss.huggingface.co/) +- 如何調試您的訓練管道 +- 如何寫一個好問題 + +當然,所有這些都與 🤗 Transformers 或Hugging Face 生態無關;本章的經驗教訓適用於大多數開源項目! diff --git a/chapters/zh-TW/chapter8/2.mdx b/chapters/zh-TW/chapter8/2.mdx new file mode 100644 index 000000000..d41597cd7 --- /dev/null +++ b/chapters/zh-TW/chapter8/2.mdx @@ -0,0 +1,364 @@ +# 出現錯誤時該怎麼辦 + + + +在本節中, 我們將研究當你嘗試從新調整的 Transformer 模型生成預測時可能發生的一些常見錯誤。這將為 [第四節](/course/chapter8/section4) 做準備, 我們將探索如何調試訓練階段本身。 + + + +我們為這一節準備了一個 [模板模型庫](https://huggingface.co/lewtun/distilbert-base-uncased-finetuned-squad-d5716d28), 如果你想運行本章中的代碼, 你首先需要將模型複製到你的 [Hugging Face Hub](https://huggingface.co) 賬號。為此, 首先通過在 Jupyter 筆記本中運行以下任一命令來登錄: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +或在你最喜歡的終端中執行以下操作: + +```bash +huggingface-cli login +``` + +這將提示你輸入用戶名和密碼, 並將在下面保存一個令牌 *~/.cache/huggingface/*。登錄後, 你可以使用以下功能複製模板存儲庫: + +```python +from distutils.dir_util import copy_tree +from huggingface_hub import Repository, snapshot_download, create_repo, get_full_repo_name + + +def copy_repository_template(): + # Clone the repo and extract the local path + template_repo_id = "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28" + commit_hash = "be3eaffc28669d7932492681cd5f3e8905e358b4" + template_repo_dir = snapshot_download(template_repo_id, revision=commit_hash) + # Create an empty repo on the Hub + model_name = template_repo_id.split("/")[1] + create_repo(model_name, exist_ok=True) + # Clone the empty repo + new_repo_id = get_full_repo_name(model_name) + new_repo_dir = model_name + repo = Repository(local_dir=new_repo_dir, clone_from=new_repo_id) + # Copy files + copy_tree(template_repo_dir, new_repo_dir) + # Push to Hub + repo.push_to_hub() +``` + +現在, 當你調用 `copy_repository_template()` 時, 它將在你的帳戶下創建模板存儲庫的副本。 + +## 從 🤗 Transformers 調試管道 + +要開始我們調試 Transformer 模型的奇妙世界之旅, 請考慮以下場景: 你正在與一位同事合作進行問答項目, 以幫助電子商務網站的客戶找到有關消費品的答案。你的同事給你發了一條消息, 比如: + +> 嗨! 我剛剛使用了抱抱臉課程的 [第七章](/course/chapter7/7) 中的技術進行了一個實驗, 並在 SQuAD 上獲得了一些很棒的結果! 我認為我們可以用這個模型作為我們項目的起點。Hub上的模型ID是 "lewtun/distillbert-base-uncased-finetuned-squad-d5716d28"。請隨意測試一下 :) + +你首先想到的是使用 🤗 Transformers 中的 `管道`: + +```python +from transformers import pipeline + +model_checkpoint = get_full_repo_name("distillbert-base-uncased-finetuned-squad-d5716d28") +reader = pipeline("question-answering", model=model_checkpoint) +``` + +```python out +""" +OSError: Can't load config for 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28'. Make sure that: + +- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models' + +- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file +""" +``` + +哦不對, 好像出了什麼問題! 如果你是編程新手, 這些類型的錯誤一開始看起來有點神秘 (甚至是一個 `OSError`?!)。這裡顯示的錯誤只是一個更大的錯誤報告的最後一部分, 稱為 _Python traceback_ (又名堆棧跟蹤)。例如, 如果你在 Google Colab 上運行此代碼, 你應該會看到類似於以下屏幕截圖的內容: + +
+A Python traceback. +
+ +這些報告中包含很多信息, 所以讓我們一起來看看關鍵部分。首先要注意的是, 應該從 _從底部到頂部_ 讀取回溯。如果你習慣於從上到下閱讀英文文本, 這可能聽起來很奇怪,但它反映了這樣一個事實,即回溯顯示了在下載模型和標記器時 `管道` 進行的函數調用序列。(查看 [第二章](/course/chapter2) 瞭解有關 `pipeline` 如何在後臺工作的更多詳細信息。) + + + +🚨 看到Google Colab 回溯中 "6 幀" 周圍的藍色框了嗎? 這是 Colab 的一個特殊功能, 它將回溯壓縮為"幀"。如果你似乎無法找到錯誤的來源, 請確保通過單擊這兩個小箭頭來展開完整的回溯。 + + + +這意味著回溯的最後一行指示最後一條錯誤消息並給出引發的異常的名稱。在這種情況下, 異常類型是`OSError`, 表示與系統相關的錯誤。如果我們閱讀隨附的錯誤消息, 我們可以看到模型的 *config.json* 文件似乎有問題, 我們給出了兩個修復它的建議: + +```python out +""" +Make sure that: + +- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models' + +- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file +""" +``` + + + +💡 如果你遇到難以理解的錯誤消息, 只需將該消息複製並粘貼到 Google 或 [Stack Overflow](https://stackoverflow.com/) 搜索欄中 (是的, 真的!)。你很可能不是第一個遇到錯誤的人, 這是找到社區中其他人發佈的解決方案的好方法。例如, 在 Stack Overflow 上搜索 `OSError: Can't load config for` 給出了幾個[hits](https://stackoverflow.com/search?q=OSError%3A+Can%27t+load+config+for+), 可能是用作解決問題的起點。 + + + +第一個建議是要求我們檢查模型ID是否真的正確, 所以首先要做的就是複製標識符並將其粘貼到Hub的搜索欄中: + +
+The wrong model name. +
+ +嗯, 看起來我們同事的模型確實不在 Hub 上... 啊哈, 但是模型名稱中有一個錯字! DistilBERT 的名稱中只有一個 "l", 所以讓我們解決這個問題並尋找 "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28": + +
+The right model name. +
+ +好的, 這很受歡迎。現在讓我們嘗試使用正確的模型 ID 再次下載模型: + +```python +model_checkpoint = get_full_repo_name("distilbert-base-uncased-finetuned-squad-d5716d28") +reader = pipeline("question-answering", model=model_checkpoint) +``` + +```python out +""" +OSError: Can't load config for 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28'. Make sure that: + +- 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models' + +- or 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file +""" +``` + +啊, 再次挫敗 -- 歡迎來到機器學習工程師的日常生活! 因為我們已經修復了模型 ID, 所以問題一定出在存儲庫本身。訪問 🤗 Hub 上存儲庫內容的一種快速方法是通過 `huggingface_hub` 庫的 `list_repo_files()` 方法: + +```python +from huggingface_hub import list_repo_files + +list_repo_files(repo_id=model_checkpoint) +``` + +```python out +['.gitattributes', 'README.md', 'pytorch_model.bin', 'special_tokens_map.json', 'tokenizer_config.json', 'training_args.bin', 'vocab.txt'] +``` + +有趣 -- 似乎沒有配置文件存儲庫中的 *config.json* 文件! 難怪我們的 `pipeline` 無法加載模型; 我們的同事一定是在微調後忘記將這個文件推送到 Hub。在這種情況下, 問題似乎很容易解決: 我們可以要求他們添加文件, 或者, 因為我們可以從模型 ID 中看到使用的預訓練模型是 [`distilbert-base-uncased`](https://huggingface.co/distilbert-base-uncased), 我們可以下載此模型的配置並將其推送到我們的存儲庫以查看是否可以解決問題。讓我們試試看。使用我們在 [第二章](/course/chapter2) 中學習的技術, 我們使用 `AutoConfig` 類下載模型的配置: + +```python +from transformers import AutoConfig + +pretrained_checkpoint = "distilbert-base-uncased" +config = AutoConfig.from_pretrained(pretrained_checkpoint) +``` + + + +🚨 我們在這裡採用的方法並不是萬無一失的, 因為我們的同事可能在微調模型之前已經調整了 `distilbert-base-uncased` 配置。在現實生活中, 我們想首先檢查它們, 但出於本節的目的, 我們假設它們使用默認配置。 + + + +然後我們可以使用配置的 `push_to_hub()` 方法將其推送到我們的模型存儲庫: + +```python +config.push_to_hub(model_checkpoint, commit_message="Add config.json") +``` + +現在我們可以通過從最新提交的 `main` 分支中加載模型來測試這是否有效: + +```python +reader = pipeline("question-answering", model=model_checkpoint, revision="main") + +context = r""" +Extractive Question Answering is the task of extracting an answer from a text +given a question. An example of a question answering dataset is the SQuAD +dataset, which is entirely based on that task. If you would like to fine-tune a +model on a SQuAD task, you may leverage the +examples/pytorch/question-answering/run_squad.py script. + +🤗 Transformers is interoperable with the PyTorch, TensorFlow, and JAX +frameworks, so you can use your favourite tools for a wide variety of tasks! +""" + +question = "What is extractive question answering?" +reader(question=question, context=context) +``` + +```python out +{'score': 0.38669535517692566, + 'start': 34, + 'end': 95, + 'answer': 'the task of extracting an answer from a text given a question'} +``` + +哇哦, 成功了!讓我們回顧一下你剛剛學到的東西: + +- Python 中的錯誤消息稱為 _tracebacks_ , 並從下到上閱讀。錯誤消息的最後一行通常包含定位問題根源所需的信息。 +- 如果最後一行沒有包含足夠的信息, 請按照您的方式進行回溯, 看看您是否可以確定源代碼中發生錯誤的位置。 +- 如果沒有任何錯誤消息可以幫助您調試問題, 請嘗試在線搜索類似問題的解決方案。 +- `huggingface_hub` +// 🤗 Hub? +庫提供了一套工具, 你可以使用這些工具與 Hub 上的存儲庫進行交互和調試。 + +現在你知道如何調試管道, 讓我們看一下模型本身前向傳遞中的一個更棘手的示例。 + +## 調試模型的前向傳遞 + +儘管 `pipeline` 對於大多數需要快速生成預測的應用程序來說非常有用, 有時您需要訪問模型的 logits (例如, 如果您有一些想要應用的自定義後處理)。為了看看在這種情況下會出現什麼問題, 讓我們首先從 `pipeline` 中獲取模型和標記器: + +```python +tokenizer = reader.tokenizer +model = reader.model +``` + +接下來我們需要一個問題, 那麼讓我們看看是否支持我們最喜歡的框架: + +```python +question = "Which frameworks can I use?" +``` + +正如我們在 [第七章](/course/chapter7) 中學習的, 我們需要採取的通常步驟是對輸入進行標記化, 提取開始和結束標記的對數, 然後解碼答案範圍: + +```python +import torch + +inputs = tokenizer(question, context, add_special_tokens=True) +input_ids = inputs["input_ids"][0] +outputs = model(**inputs) +answer_start_scores = outputs.start_logits +answer_end_scores = outputs.end_logits +# Get the most likely beginning of answer with the argmax of the score +answer_start = torch.argmax(answer_start_scores) +# Get the most likely end of answer with the argmax of the score +answer_end = torch.argmax(answer_end_scores) + 1 +answer = tokenizer.convert_tokens_to_string( + tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end]) +) +print(f"Question: {question}") +print(f"Answer: {answer}") +``` + +```python out +""" +--------------------------------------------------------------------------- +AttributeError Traceback (most recent call last) +/var/folders/28/k4cy5q7s2hs92xq7_h89_vgm0000gn/T/ipykernel_75743/2725838073.py in + 1 inputs = tokenizer(question, text, add_special_tokens=True) + 2 input_ids = inputs["input_ids"] +----> 3 outputs = model(**inputs) + 4 answer_start_scores = outputs.start_logits + 5 answer_end_scores = outputs.end_logits + +~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs) + 1049 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks + 1050 or _global_forward_hooks or _global_forward_pre_hooks): +-> 1051 return forward_call(*input, **kwargs) + 1052 # Do not call functions when jit is used + 1053 full_backward_hooks, non_full_backward_hooks = [], [] + +~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, start_positions, end_positions, output_attentions, output_hidden_states, return_dict) + 723 return_dict = return_dict if return_dict is not None else self.config.use_return_dict + 724 +--> 725 distilbert_output = self.distilbert( + 726 input_ids=input_ids, + 727 attention_mask=attention_mask, + +~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs) + 1049 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks + 1050 or _global_forward_hooks or _global_forward_pre_hooks): +-> 1051 return forward_call(*input, **kwargs) + 1052 # Do not call functions when jit is used + 1053 full_backward_hooks, non_full_backward_hooks = [], [] + +~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict) + 471 raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time") + 472 elif input_ids is not None: +--> 473 input_shape = input_ids.size() + 474 elif inputs_embeds is not None: + 475 input_shape = inputs_embeds.size()[:-1] + +AttributeError: 'list' object has no attribute 'size' +""" +``` + +噢, 看起來我們的代碼中有一個錯誤!但我們不怕一點調試。您可以在筆記本中使用 Python 調試器: + + + +或在終端中: + + + +在這裡, 閱讀錯誤消息告訴我們 `'list' object has no attribute 'size'`, 我們可以看到一個 `-->` 箭頭指向 `model(**inputs)` 中出現問題的行。你可以使用 Python 調試器以交互方式調試它, 但現在我們只需打印出一部分 `inputs`, 看看我們有什麼: + +```python +inputs["input_ids"][:5] +``` + +```python out +[101, 2029, 7705, 2015, 2064] +``` + +這當然看起來像一個普通的 Python `list`, 但讓我們仔細檢查一下類型: + +```python +type(inputs["input_ids"]) +``` + +```python out +list +``` + +是的, 這肯定是一個 Python `list`。那麼出了什麼問題呢? 回憶 [第二章](/course/chapter2) 🤗 Transformers 中的 `AutoModelForXxx` 類在 _tensors_ 上運行(PyTorch或者or TensorFlow), 一個常見的操作是使用 `Tensor.size()` 方法提取張量的維度, 例如, 在 PyTorch 中。讓我們再看看回溯, 看看哪一行觸發了異常: + +``` +~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict) + 471 raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time") + 472 elif input_ids is not None: +--> 473 input_shape = input_ids.size() + 474 elif inputs_embeds is not None: + 475 input_shape = inputs_embeds.size()[:-1] + +AttributeError: 'list' object has no attribute 'size' +``` + +看起來我們的代碼試圖調用 `input_ids.size()`, 但這顯然不適用於 Python `list`, 這只是一個容器。我們如何解決這個問題? 在 Stack Overflow 上搜索錯誤消息給出了很多相關的 [hits](https://stackoverflow.com/search?q=AttributeError%3A+%27list%27+object+has+no+attribute+%27size%27&s=c15ec54c-63cb-481d-a749-408920073e8f)。單擊第一個會顯示與我們類似的問題, 答案如下面的屏幕截圖所示: + +
+An answer from Stack Overflow. +
+ +答案建議我們添加 `return_tensors='pt'` 到標記器, 所以讓我們看看這是否適合我們: + +```python out +inputs = tokenizer(question, context, add_special_tokens=True, return_tensors="pt") +input_ids = inputs["input_ids"][0] +outputs = model(**inputs) +answer_start_scores = outputs.start_logits +answer_end_scores = outputs.end_logits +# Get the most likely beginning of answer with the argmax of the score +answer_start = torch.argmax(answer_start_scores) +# Get the most likely end of answer with the argmax of the score +answer_end = torch.argmax(answer_end_scores) + 1 +answer = tokenizer.convert_tokens_to_string( + tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end]) +) +print(f"Question: {question}") +print(f"Answer: {answer}") +``` + +```python out +""" +Question: Which frameworks can I use? +Answer: pytorch, tensorflow, and jax +""" +``` + +不錯, 成功了! 這是 Stack Overflow 非常有用的一個很好的例子: 通過識別類似的問題, 我們能夠從社區中其他人的經驗中受益。然而, 像這樣的搜索並不總是會產生相關的答案, 那麼在這種情況下你能做什麼呢? 幸運的是, 有一個受歡迎的開發者社區 [Hugging Face forums](https://discuss.huggingface.co/) 可以幫助你! 在下一節中, 我們將看看如何設計可能得到回答的優秀論壇問題。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter8/3.mdx b/chapters/zh-TW/chapter8/3.mdx new file mode 100644 index 000000000..8fa909369 --- /dev/null +++ b/chapters/zh-TW/chapter8/3.mdx @@ -0,0 +1,166 @@ +# 在論壇上尋求幫助 + + + + + +[Hugging Face 論壇](https://discuss.huggingface.co) 是從開源團隊和更廣泛的 Hugging Face 社區獲得幫助的好地方。以下是論壇某一天的主頁面: + +
+The Hugging Face forums. +
+ +在左側,您可以看到各種主題分組的所有類別,而右側顯示了最新的主題。主題是包含標題、類別和描述的帖子;它與我們在創建自己的數據集時看到的 GitHub 問題格式非常相似[Chapter 5](/course/chapter5).顧名思義,[Beginners](https://discuss.huggingface.co/c/beginners/5)類別主要面向剛開始使用 Hugging Face 庫和生態系統的人。歡迎對任何庫提出任何問題,無論是調試一些代碼還是尋求有關如何做某事的幫助。 (也就是說,如果您的問題特別涉及某個圖書館,您可能應該前往論壇上的相應圖書館類別。) + +同樣,the [Intermediate](https://discuss.huggingface.co/c/intermediate/6)和[Research](https://discuss.huggingface.co/c/research/7)類別用於更高級的問題,例如關於圖書館或您想討論的一些很酷的新 NLP 研究。 + +當然,我們也應該提到[Course](https://discuss.huggingface.co/c/course/20)類別,您可以在其中提出與 Hugging Face 課程相關的任何問題! + +選擇類別後,您就可以編寫第一個主題了。 你可以找一些[guidelines](https://discuss.huggingface.co/t/how-to-request-support/3128) 在有關如何執行此操作的論壇中,在本節中,我們將看看構成一個好的主題的一些功能。 + +## 寫一篇好的論壇帖子 + +作為一個運行示例,假設我們試圖從 Wikipedia 文章生成嵌入表示以創建自定義搜索引擎。像往常一樣,我們按如下方式加載分詞器和模型: + +```python +from transformers import AutoTokenizer, AutoModel + +model_checkpoint = "distilbert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) +model = AutoModel.from_pretrained(model_checkpoint) +``` + +現在我們嘗試將[變形金剛的維基百科](https://en.wikipedia.org/wiki/Transformers)的一整段進行嵌入表示(熱知識:變形金剛的英文就是 Transformers ,而現在 Transformers 作為一個 🤗 Python 庫也被越來越多人熟知): + +```python +text = """ +Generation One is a retroactive term for the Transformers characters that +appeared between 1984 and 1993. The Transformers began with the 1980s Japanese +toy lines Micro Change and Diaclone. They presented robots able to transform +into everyday vehicles, electronic items or weapons. Hasbro bought the Micro +Change and Diaclone toys, and partnered with Takara. Marvel Comics was hired by +Hasbro to create the backstory; editor-in-chief Jim Shooter wrote an overall +story, and gave the task of creating the characthers to writer Dennis O'Neil. +Unhappy with O'Neil's work (although O'Neil created the name "Optimus Prime"), +Shooter chose Bob Budiansky to create the characters. + +The Transformers mecha were largely designed by Shōji Kawamori, the creator of +the Japanese mecha anime franchise Macross (which was adapted into the Robotech +franchise in North America). Kawamori came up with the idea of transforming +mechs while working on the Diaclone and Macross franchises in the early 1980s +(such as the VF-1 Valkyrie in Macross and Robotech), with his Diaclone mechs +later providing the basis for Transformers. + +The primary concept of Generation One is that the heroic Optimus Prime, the +villainous Megatron, and their finest soldiers crash land on pre-historic Earth +in the Ark and the Nemesis before awakening in 1985, Cybertron hurtling through +the Neutral zone as an effect of the war. The Marvel comic was originally part +of the main Marvel Universe, with appearances from Spider-Man and Nick Fury, +plus some cameos, as well as a visit to the Savage Land. + +The Transformers TV series began around the same time. Produced by Sunbow +Productions and Marvel Productions, later Hasbro Productions, from the start it +contradicted Budiansky's backstories. The TV series shows the Autobots looking +for new energy sources, and crash landing as the Decepticons attack. Marvel +interpreted the Autobots as destroying a rogue asteroid approaching Cybertron. +Shockwave is loyal to Megatron in the TV series, keeping Cybertron in a +stalemate during his absence, but in the comic book he attempts to take command +of the Decepticons. The TV series would also differ wildly from the origins +Budiansky had created for the Dinobots, the Decepticon turned Autobot Jetfire +(known as Skyfire on TV), the Constructicons (who combine to form +Devastator),[19][20] and Omega Supreme. The Marvel comic establishes early on +that Prime wields the Creation Matrix, which gives life to machines. In the +second season, the two-part episode The Key to Vector Sigma introduced the +ancient Vector Sigma computer, which served the same original purpose as the +Creation Matrix (giving life to Transformers), and its guardian Alpha Trion. +""" + +inputs = tokenizer(text, return_tensors="pt") +logits = model(**inputs).logits +``` + +```python output +IndexError: index out of range in self +``` + +呃,我們遇到了一個問題——錯誤信息比我們看到的要神秘得多[section 2](/course/chapter8/section2)!我們無法確定完整回溯的正面或反面,因此我們決定轉向 Hugging Face 論壇尋求幫助。我們如何設計主題? + +首先,我們需要點擊右上角的“新建主題”按鈕(注意,要創建主題,我們需要登錄): + +
+Creating a new forum topic. +
+ +這會出現一個寫作界面,我們可以在其中輸入我們的主題標題,選擇一個類別,並起草內容: + +
+The interface for creating a forum topic. +
+ +由於錯誤似乎僅與 🤗 Transformers有關,因此我們將為該類別選擇此錯誤。我們第一次嘗試解釋這個問題可能看起來像這樣: + +
+Drafting the content for a new forum topic. +
+ +儘管本主題包含我們需要幫助的錯誤消息,但其編寫方式存在一些問題: + +1. 標題描述性不是很強,因此瀏覽論壇的任何人都無法在不閱讀正文的情況下分辨出主題的內容。 + +2. 正文沒有提供足夠的信息,說明錯誤來自何處以及如何重現錯誤。 + +3. 這個話題直接用一種有點苛刻的語氣標記了幾個人。 + +像這樣的主題不太可能很快得到答案(如果他們得到了答案),那麼讓我們看看如何改進它。我們將從選擇一個好標題的第一個問題開始。 + +### 選擇描述性標題 + +如果您想就代碼中的錯誤尋求幫助,一個好的經驗法則是在標題中包含足夠的信息,以便其他人可以快速確定他們是否認為他們可以回答您的問題。在我們的運行示例中,我們知道正在引發的異常的名稱,並有一些提示它是在模型的前向傳遞中觸發的,我們調用 **model(**inputs)** .為了傳達這一點,一個可能的標題可能是: + +> 自動建模正向傳遞中的索引錯誤的來源? + +這個標題告訴讀者在哪裡你認為錯誤來自,如果他們遇到了 **IndexError** 在此之前,他們很有可能知道如何調試它。當然,標題可以是您想要的任何內容,也可以是其他變體,例如: + +> 為什麼我的模型會產生索引錯誤? + +也可以。現在我們有了一個描述性的標題,讓我們來看看改善主體。 + +### 設置代碼段的格式 + +如:也可以。現在我們有了一個描述性的標題,讓我們來看看改善身體。在 IDE 中閱讀源代碼已經夠難的了,但是當將代碼複製粘貼為純文本時就更難了!幸運的是,Hugging Face 論壇支持使用 Markdown,因此您應該始終用三個反引號 (```) 將代碼塊括起來,以便更容易閱讀。讓我們這樣做來美化錯誤消息——在我們這樣做的時候,讓我們讓正文比我們的原始版本更有禮貌: + +
+Our revised forum topic, with proper code formatting. +
+ +正如您在屏幕截圖中看到的,將代碼塊括在反引號中會將原始文本轉換為格式化代碼,並帶有顏色樣式!另請注意,單個反引號可用於格式化內聯變量,就像我們所做的那樣 **distilbert-base-uncased** .這個主題看起來好多了,如果幸運的話,我們可能會在社區中找到可以猜測錯誤是什麼的人。然而,與其依靠運氣,不如讓我們在其完整的血腥細節中包含回溯,讓生活更輕鬆! + +### 包括完整的回溯 + +由於回溯的最後一行通常足以調試您自己的代碼,因此很容易在您的主題中提供它以“節省空間”。雖然本意是好的,但這實際上使它更難供其他人調試問題,因為回溯中較高的信息也非常有用。因此,一個好的做法是複製並粘貼所有的回溯,同時確保它的格式很好。由於這些回溯可能會很長,有些人更喜歡在解釋了源代碼之後再展示它們。我們開工吧。現在,我們的論壇主題如下所示: + +
+Our example forum topic, with the complete traceback. +
+ +這提供了更多信息,細心的讀者可能會指出問題似乎是由於回溯中的這一行而傳遞了一個長輸入: + +> 令牌索引序列長度長於為此模型指定的最大序列長度 (583 > 512)。 + +但是,通過提供觸發錯誤的實際代碼,我們可以讓他們更輕鬆。我們現在就這樣做。 + +### 提供可重複的示例 + +如果您曾經嘗試過調試其他人的代碼,那麼您可能首先嚐試重現他們報告的問題,以便您可以開始通過回溯來查明錯誤。在論壇上獲得(或提供)幫助時沒有什麼不同,所以如果你能提供一個重現錯誤的小例子真的很有幫助。有一半的時間,簡單地完成這個練習將幫助你找出問題所在。在任何情況下,我們的示例缺少的部分是顯示輸入我們提供給模型的。這樣做會為我們提供類似於以下完整示例的內容: + +
+The final version of our forum topic. +
+ +該主題現在包含相當多的信息,並且它的編寫方式更可能吸引社區的注意力並獲得有用的答案。有了這些基本指南,您現在可以創建很棒的主題來找到您的 🤗 Transformers問題的答案! + diff --git a/chapters/zh-TW/chapter8/4.mdx b/chapters/zh-TW/chapter8/4.mdx new file mode 100644 index 000000000..4d7b3c1c1 --- /dev/null +++ b/chapters/zh-TW/chapter8/4.mdx @@ -0,0 +1,787 @@ + + +# 調試訓練管道 + + + +你已經編寫了一個漂亮的腳本來訓練或微調給定任務的模型,盡職盡責地遵循 [Chapter 7](/course/chapter7) 中的建議。 但是當你啟動命令 `trainer.train()` 時,可怕的事情發生了:你得到一個錯誤😱! 或者更糟糕的是,一切似乎都很好,訓練運行沒有錯誤,但生成的模型很糟糕。 在本節中,我們將向您展示如何調試此類問題。 + +## 調試訓練管道 + + + +當您在 `trainer.train()` 中遇到錯誤時,它可能來自多個來源,因為 `Trainer` 通常會將很多東西放在一起組合運行。 它將datasets轉換為dataloaders,因此問題可能出在datasets中,或者在嘗試將datasets的元素一起批處理時出現問題。 然後它需要準備一批數據並將其提供給模型,因此問題可能出在模型代碼中。 之後,它會計算梯度並執行優化器,因此問題也可能出在您的優化器中。 即使訓練一切順利,如果您的評估指標有問題,評估期間仍然可能出現問題。 + +調試 `trainer.train()` 中出現的錯誤的最佳方法是手動檢查整個管道,看看哪裡出了問題。 錯誤通常很容易解決。 + +為了證明這一點,我們將使用以下腳本(嘗試)在 [MNLI 數據集](https://huggingface.co/datasets/glue)上微調 DistilBERT 模型: + +```py +from datasets import load_dataset, load_metric +from transformers import ( + AutoTokenizer, + AutoModelForSequenceClassification, + TrainingArguments, + Trainer, +) + +raw_datasets = load_dataset("glue", "mnli") + +model_checkpoint = "distilbert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) + + +def preprocess_function(examples): + return tokenizer(examples["premise"], examples["hypothesis"], truncation=True) + + +tokenized_datasets = raw_datasets.map(preprocess_function, batched=True) +model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint) + +args = TrainingArguments( + f"distilbert-finetuned-mnli", + evaluation_strategy="epoch", + save_strategy="epoch", + learning_rate=2e-5, + num_train_epochs=3, + weight_decay=0.01, +) + +metric = load_metric("glue", "mnli") + + +def compute_metrics(eval_pred): + predictions, labels = eval_pred + return metric.compute(predictions=predictions, references=labels) + + +trainer = Trainer( + model, + args, + train_dataset=raw_datasets["train"], + eval_dataset=raw_datasets["validation_matched"], + compute_metrics=compute_metrics, +) +trainer.train() +``` + +如果你嘗試執行它,你會遇到一個相當神秘的錯誤: + +```python out +'ValueError: You have to specify either input_ids or inputs_embeds' +``` + +### 檢查數據 + +這是不言而喻的,如果你的數據被破壞,“Trainer”將無法形成批次,更不用說訓練你的模型了。 所以首先,你需要看看你的訓練集中有什麼。 + +為了避免花費無數小時試圖檢查和修復不是錯誤來源的東西,我們建議您使用 `trainer.train_dataset` 進行檢查。 所以讓我們在這裡這樣做: + +```py +trainer.train_dataset[0] +``` + +```python out +{'hypothesis': 'Product and geography are what make cream skimming work. ', + 'idx': 0, + 'label': 1, + 'premise': 'Conceptually cream skimming has two basic dimensions - product and geography.'} +``` + +你注意到有什麼不對嗎? 與缺少有關 `input_ids` 的錯誤消息相結合,應該讓您意識到數據集裡是文本,而不是模型可以理解的數字。 在這個例子,輸出的原始錯誤信息非常具有誤導性,因為 `Trainer` 會自動刪除與模型簽名不匹配的列(即模型預期的參數)。 這意味著在這裡,除了標籤之外的所有東西都被丟棄了。 因此,創建批次然後將它們發送到模型沒有問題,而模型又抱怨它沒有收到正確的輸入。 + +為什麼沒有處理數據生成標記呢? 我們確實在數據集上使用了“Dataset.map()”方法來對每個樣本應用標記器。 但是如果你仔細看代碼,你會發現我們在將訓練和評估集傳遞給`Trainer`時犯了一個錯誤。 我們在這裡沒有使用 `tokenized_datasets`,而是使用了 `raw_datasets` 🤦。 所以讓我們解決這個問題! + +```py +from datasets import load_dataset, load_metric +from transformers import ( + AutoTokenizer, + AutoModelForSequenceClassification, + TrainingArguments, + Trainer, +) + +raw_datasets = load_dataset("glue", "mnli") + +model_checkpoint = "distilbert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) + + +def preprocess_function(examples): + return tokenizer(examples["premise"], examples["hypothesis"], truncation=True) + + +tokenized_datasets = raw_datasets.map(preprocess_function, batched=True) +model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint) + +args = TrainingArguments( + f"distilbert-finetuned-mnli", + evaluation_strategy="epoch", + save_strategy="epoch", + learning_rate=2e-5, + num_train_epochs=3, + weight_decay=0.01, +) + +metric = load_metric("glue", "mnli") + + +def compute_metrics(eval_pred): + predictions, labels = eval_pred + return metric.compute(predictions=predictions, references=labels) + + +trainer = Trainer( + model, + args, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["validation_matched"], + compute_metrics=compute_metrics, +) +trainer.train() +``` + +這個新代碼現在會給出一個不同的錯誤: + +```python out +'ValueError: expected sequence of length 43 at dim 1 (got 37)' +``` + +查看traceback,我們可以看到錯誤發生在數據整理步驟中: + +```python out +~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features) + 105 batch[k] = torch.stack([f[k] for f in features]) + 106 else: +--> 107 batch[k] = torch.tensor([f[k] for f in features]) + 108 + 109 return batch +``` + +所以,我們應該去研究一下那個。 然而,在我們這樣做之前,讓我們完成檢查我們的數據, 先確定它100%是正確的。 + +在調試課程的內容時,您應該始終做的一件事是查看模型的解碼輸入。 我們無法理解直接提供給它的數字,所以我們應該看看這些數字代表什麼。 例如,在計算機視覺中,這意味著查看您傳遞的圖片像素的解碼,在語音中意味著解碼後的音頻樣本,對於我們的 NLP 示例,這意味著使用我們的標記器解碼的輸入: + +```py +tokenizer.decode(trainer.train_dataset[0]["input_ids"]) +``` + +```python out +'[CLS] conceptually cream skimming has two basic dimensions - product and geography. [SEP] product and geography are what make cream skimming work. [SEP]' +``` + +所以這似乎是正確的。 您應該對輸入中的所有鍵執行此操作: + +```py +trainer.train_dataset[0].keys() +``` + +```python out +dict_keys(['attention_mask', 'hypothesis', 'idx', 'input_ids', 'label', 'premise']) +``` + +請注意,與模型接受的輸入不對應的鍵將被自動丟棄,因此這裡我們將僅保留 `input_ids`、`attention_mask` 和 `label`(將重命名為 `labels`)。 要仔細檢查模型輸入的列,您可以打印模型的類,然後查看其文檔: + +```py +type(trainer.model) +``` + +```python out +transformers.models.distilbert.modeling_distilbert.DistilBertForSequenceClassification +``` + +所以在我們的例子中,我們在[在這個頁面](https://huggingface.co/transformers/model_doc/distilbert.html#distilbertforsequenceclassification)可以檢查上接受的參數。 `Trainer` 也會記錄它丟棄的列。 + +我們通過解碼檢查了輸入 ID 是否正確。 接下來是檢查 `attention_mask`: + +```py +tokenizer.decode(trainer.train_dataset[0]["attention_mask"]) +``` + +```python out +[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +``` + +由於我們沒有在預處理中應用填充,這看起來非常自然。 為確保該注意掩碼沒有問題,讓我們檢查它與輸入 ID 的長度是否相同: + +```py +len(trainer.train_dataset[0]["attention_mask"]) == len( + trainer.train_dataset[0]["input_ids"] +) +``` + +```python out +True +``` + +那挺好的! 最後,讓我們檢查一下我們的標籤: + +```py +trainer.train_dataset[0]["label"] +``` + +```python out +1 +``` + +與輸入 ID 一樣,這是一個本身並沒有真正意義的數字。 正如我們之前看到的,整數和標籤名稱之間的映射存儲在數據集相應 *feature* 的 `names` 屬性中: + +```py +trainer.train_dataset.features["label"].names +``` + +```python out +['entailment', 'neutral', 'contradiction'] +``` + +所以`1`表示`neutral`,表示我們上面看到的兩句話並不矛盾,也沒有包含關係。 這似乎是正確的! + +我們這裡沒有令牌類型 ID,因為 DistilBERT 不需要它們; 如果您的模型中有一些,您還應該確保它們正確匹配輸入中第一句和第二句的位置。 + + + +✏️ **輪到你了!** 檢查訓練數據集的第二個元素是否正確。 + + + +我們在這裡只對訓練集進行檢查,但您當然應該以同樣的方式仔細檢查驗證集和測試集。 + +現在我們知道我們的數據集看起來不錯,是時候檢查訓練管道的下一步了。 + +### 從 datasets 到 dataloaders + +訓練管道中可能出錯的下一件事是當“Trainer”嘗試從訓練或驗證集形成批次時。 一旦你確定 `Trainer` 的數據集是正確的,你可以嘗試通過執行以下操作手動形成一個批次(可以將 `train` 替換為 `eval` 用於驗證數據加載器): + +```py +for batch in trainer.get_train_dataloader(): + break +``` + +此代碼創建訓練數據加載器,然後對其進行迭代,在第一次迭代時停止。 如果代碼執行沒有錯誤,那麼您就有了可以檢查的第一個訓練批次,如果代碼出錯,您可以確定問題出在數據加載器中,如下所示: + +```python out +~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features) + 105 batch[k] = torch.stack([f[k] for f in features]) + 106 else: +--> 107 batch[k] = torch.tensor([f[k] for f in features]) + 108 + 109 return batch + +ValueError: expected sequence of length 45 at dim 1 (got 76) +``` + +檢查trackback的最後一個堆棧的輸出應該足以給你一個線索,但讓我們做更多的挖掘。 批處理創建過程中的大多數問題是由於將示例整理到單個批處理中而出現的,因此在有疑問時首先要檢查的是您的 DataLoader 正在使用什麼 collate_fn: + +```py +data_collator = trainer.get_train_dataloader().collate_fn +data_collator +``` + +```python out + Dict[str, Any]> +``` + +所以,目前使用的是 `default_data_collator`,但這不是我們在這種情況下想要的。 我們希望將示例填充到批處理中最長的句子,這是由 `DataCollatorWithPadding` 整理器完成的。 而這個數據收集器應該是默認被 `Trainer` 使用的,為什麼這裡沒有使用呢? + +答案是因為我們沒有將 `tokenizer` 傳遞給 `Trainer`,所以它無法創建我們想要的 `DataCollatorWithPadding`。 在實踐中,您應該明確地傳遞您想要使用的數據整理器,以確保避免這些類型的錯誤。 讓我們調整我們的代碼來做到這一點: + +```py +from datasets import load_dataset, load_metric +from transformers import ( + AutoTokenizer, + AutoModelForSequenceClassification, + DataCollatorWithPadding, + TrainingArguments, + Trainer, +) + +raw_datasets = load_dataset("glue", "mnli") + +model_checkpoint = "distilbert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) + + +def preprocess_function(examples): + return tokenizer(examples["premise"], examples["hypothesis"], truncation=True) + + +tokenized_datasets = raw_datasets.map(preprocess_function, batched=True) +model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint) + +args = TrainingArguments( + f"distilbert-finetuned-mnli", + evaluation_strategy="epoch", + save_strategy="epoch", + learning_rate=2e-5, + num_train_epochs=3, + weight_decay=0.01, +) + +metric = load_metric("glue", "mnli") + + +def compute_metrics(eval_pred): + predictions, labels = eval_pred + return metric.compute(predictions=predictions, references=labels) + + +data_collator = DataCollatorWithPadding(tokenizer=tokenizer) + +trainer = Trainer( + model, + args, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["validation_matched"], + compute_metrics=compute_metrics, + data_collator=data_collator, + tokenizer=tokenizer, +) +trainer.train() +``` + +好消息? 我們沒有得到與以前相同的錯誤,這絕對是進步。 壞消息? 我們得到了一個臭名昭著的 CUDA 錯誤: + +```python out +RuntimeError: CUDA error: CUBLAS_STATUS_ALLOC_FAILED when calling `cublasCreate(handle)` +``` + +這很糟糕,因為 CUDA 錯誤通常很難調試。 我們稍後會看到如何解決這個問題,但首先讓我們完成對批處理創建的分析。 + +如果您確定您的數據整理器是正確的,則應嘗試將其應用於數據集的幾個樣本: + +```py +data_collator = trainer.get_train_dataloader().collate_fn +batch = data_collator([trainer.train_dataset[i] for i in range(4)]) +``` + +此代碼將失敗,因為 `train_dataset` 包含字符串列,`Trainer` 通常會刪除這些列。 您可以手動刪除它們,或者如果您想準確地修改 `Trainer` 在幕後所做的事情,您可以調用私有的 `Trainer._remove_unused_columns()` 方法來執行此操作: + +```py +data_collator = trainer.get_train_dataloader().collate_fn +actual_train_set = trainer._remove_unused_columns(trainer.train_dataset) +batch = data_collator([actual_train_set[i] for i in range(4)]) +``` + +如果錯誤仍然存在,您應該能夠手動調試數據整理器內部以確定具體的問題。 + +現在我們已經調試了批處理創建過程,是時候將數據傳遞給模型了! + +### 檢查模型 + +您應該能夠通過執行以下命令來獲得一個批次的數據: + +```py +for batch in trainer.get_train_dataloader(): + break +``` + +如果您在notebook中運行此代碼,您可能會收到與我們之前看到的類似的 CUDA 錯誤,在這種情況下,您需要重新啟動notebook並重新執行最後一個片段,而不運行 `trainer.train()` 行.這是關於 CUDA 錯誤的第二個最煩人的事情:它們會破壞您的Cuda內核,而且無法恢復。它們最煩人的事情是它們很難調試。 + +這是為什麼?它與 GPU 的工作方式有關。它們在並行執行大量操作方面非常有效,但缺點是當其中一條指令導致錯誤時,您不會立即知道。只有當程序在 GPU 上調用多個進程的同步處理時,它才會意識到出現問題,因此錯誤實際上是在與創建它的原因無關的地方引發的。例如,如果我們查看之前的trackback,錯誤是在向後傳遞期間引發的,但我們會在一分鐘內看到它實際上源於向前傳遞中的某些東西。 + +那麼我們如何調試這些錯誤呢?答案很簡單:我們沒有。除非您的 CUDA 錯誤是內存不足錯誤(這意味著您的 GPU 中沒有足夠的內存),否則您應該始終返回 CPU 進行調試。 + +為此,我們只需將模型放回 CPU 上並在我們的一批數據中調用它——“DataLoader”返回的那批數據尚未移動到 GPU: + +```python +outputs = trainer.model.cpu()(**batch) +``` + +```python out +~/.pyenv/versions/3.7.9/envs/base/lib/python3.7/site-packages/torch/nn/functional.py in nll_loss(input, target, weight, size_average, ignore_index, reduce, reduction) + 2386 ) + 2387 if dim == 2: +-> 2388 ret = torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index) + 2389 elif dim == 4: + 2390 ret = torch._C._nn.nll_loss2d(input, target, weight, _Reduction.get_enum(reduction), ignore_index) + +IndexError: Target 2 is out of bounds. +``` + +所以,思路越來越清晰了。 我們現在在損失計算中沒有出現 CUDA 錯誤,而是有一個“IndexError”(因此與我們之前所說的反向傳播無關)。 更準確地說,我們可以看到是Target 2 造成了錯誤,所以這是檢查模型標籤數量的好時機: + +```python +trainer.model.config.num_labels +``` + +```python out +2 +``` + +有兩個標籤,只允許 0 和 1 作為目標,但是根據錯誤信息我們得到一個 2。得到一個 2 實際上是正常的:如果我們記得我們之前提取的標籤名稱,有三個,所以我們有索引 0 , 1 和 2 在我們的數據集中。 問題是我們沒有告訴我們的模型,它應該創建三個標籤。 所以讓我們解決這個問題! + +```py +from datasets import load_dataset, load_metric +from transformers import ( + AutoTokenizer, + AutoModelForSequenceClassification, + DataCollatorWithPadding, + TrainingArguments, + Trainer, +) + +raw_datasets = load_dataset("glue", "mnli") + +model_checkpoint = "distilbert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) + + +def preprocess_function(examples): + return tokenizer(examples["premise"], examples["hypothesis"], truncation=True) + + +tokenized_datasets = raw_datasets.map(preprocess_function, batched=True) +model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3) + +args = TrainingArguments( + f"distilbert-finetuned-mnli", + evaluation_strategy="epoch", + save_strategy="epoch", + learning_rate=2e-5, + num_train_epochs=3, + weight_decay=0.01, +) + +metric = load_metric("glue", "mnli") + + +def compute_metrics(eval_pred): + predictions, labels = eval_pred + return metric.compute(predictions=predictions, references=labels) + + +data_collator = DataCollatorWithPadding(tokenizer=tokenizer) + +trainer = Trainer( + model, + args, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["validation_matched"], + compute_metrics=compute_metrics, + data_collator=data_collator, + tokenizer=tokenizer, +) +``` + +我們還沒有包含 `trainer.train()` 行,以便花時間檢查一切是否正常。 如果我們請求一個批次的數據並將其傳遞給我們的模型,它現在可以正常工作了! + +```py +for batch in trainer.get_train_dataloader(): + break + +outputs = trainer.model.cpu()(**batch) +``` + +下一步是回到 GPU 並檢查一切是否仍然有效: + +```py +import torch + +device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") +batch = {k: v.to(device) for k, v in batch.items()} + +outputs = trainer.model.to(device)(**batch) +``` + +如果仍然出現錯誤,請確保重新啟動notebook並僅執行腳本的最後一個版本。 + +### 執行一個優化器步驟 + +現在我們知道我們可以構建實際通過模型檢查的成批次的數據,我們已經為訓練管道的下一步做好準備:計算梯度並執行優化步驟。 + +第一部分只是在 loss 上調用 `backward()` 方法: + +```py +loss = outputs.loss +loss.backward() +``` + +在這個階段很少出現錯誤,但如果確實出現錯誤,請返回 CPU 以獲取有用的錯誤消息。 + +要執行優化步驟,我們只需要創建 `optimizer` 並調用它的 `step()` 方法: + +```py +trainer.create_optimizer() +trainer.optimizer.step() +``` + +同樣,如果您在 `Trainer` 中使用默認優化器,則在此階段您不應該收到錯誤,但如果您有自定義優化器,則可能會出現一些問題需要在這裡調試。 如果您在此階段遇到奇怪的 CUDA 錯誤,請不要忘記返回 CPU。 說到 CUDA 錯誤,前面我們提到了一個特殊情況。 現在讓我們來看看。 + +### 處理 CUDA out-of-memory錯誤 + +每當您收到以`RuntimeError: CUDA out of memory`開頭的錯誤消息時,這表明您的 GPU 內存不足。 這與您的代碼沒有直接關聯,並且它可能發生在運行良好的代碼中。 此錯誤意味著您試圖在 GPU 的內部存儲器中放入太多東西,這導致了錯誤。 與其他 CUDA 錯誤一樣,您需要重新啟動內核才能再次運行訓練。 + +要解決這個問題,您只需要使用更少的 GPU 空間——這往往說起來容易做起來難。 首先,確保您沒有同時在 GPU 上運行兩個模型(當然,除非您的問題需要這樣做)。 然後,您可能應該減少batch的大小,因為它直接影響模型的所有中間輸出的大小及其梯度。 如果問題仍然存在,請考慮使用較小版本的模型。 + + + +在課程的下一部分中,我們將介紹更先進的技術,這些技術可以幫助您減少內存佔用並讓您微調最大的模型。 + + + +### 評估模型 + +現在我們已經解決了代碼的所有問題,一切都很完美,訓練應該可以順利進行,對吧? 沒那麼快! 如果你運行 `trainer.train()` 命令,一開始一切看起來都不錯,但過一會兒你會得到以下信息: + +```py +# This will take a long time and error out, so you shouldn't run this cell +trainer.train() +``` + +```python out +TypeError: only size-1 arrays can be converted to Python scalars +``` + +您將意識到此錯誤出現在評估階段,因此這是我們需要調試的最後一件事。 + +您可以像這樣在訓練中獨立運行`Trainer`的評估循環: + +```py +trainer.evaluate() +``` + +```python out +TypeError: only size-1 arrays can be converted to Python scalars +``` + + + +💡 您應該始終確保在啟動 `trainer.train()` 之前 `trainer.evaluate()`是可以運行的,以避免在遇到錯誤之前浪費大量計算資源。 + + + +在嘗試調試評估循環中的問題之前,您應該首先確保您已經查看了數據,能夠正確地形成批處理,並且可以在其上運行您的模型。 我們已經完成了所有這些步驟,因此可以執行以下代碼而不會出錯: + +```py +for batch in trainer.get_eval_dataloader(): + break + +batch = {k: v.to(device) for k, v in batch.items()} + +with torch.no_grad(): + outputs = trainer.model(**batch) +``` + +稍等一會兒,錯誤出現,在評估階段結束時,如果我們查看trackback,我們會看到: + +```python trace +~/git/datasets/src/datasets/metric.py in add_batch(self, predictions, references) + 431 """ + 432 batch = {"predictions": predictions, "references": references} +--> 433 batch = self.info.features.encode_batch(batch) + 434 if self.writer is None: + 435 self._init_writer() +``` + +這告訴我們錯誤源自 `datasets/metric.py` 模塊——所以這是我們的 `compute_metrics()` 函數的問題。 它需要一個帶有 logits 和標籤的元組作為 NumPy 數組,所以讓我們嘗試輸入它: + +```py +predictions = outputs.logits.cpu().numpy() +labels = batch["labels"].cpu().numpy() + +compute_metrics((predictions, labels)) +``` + +```python out +TypeError: only size-1 arrays can be converted to Python scalars +``` + +我們得到同樣的錯誤,所以問題肯定出在那個函數上。 如果我們回顧它的代碼,我們會發現它只是將“預測”和“真實的標籤”轉發到“metric.compute()”。 那麼這種方法有問題嗎? 並不真地。 讓我們快速瀏覽一下形狀: + +```py +predictions.shape, labels.shape +``` + +```python out +((8, 3), (8,)) +``` + +我們的預測仍然是 logits,而不是實際的預測,這就是metrics返回這個(有點模糊)錯誤的原因。 修復很簡單; 我們只需要在 `compute_metrics()` 函數中添加一個 argmax: + +```py +import numpy as np + + +def compute_metrics(eval_pred): + predictions, labels = eval_pred + predictions = np.argmax(predictions, axis=1) + return metric.compute(predictions=predictions, references=labels) + + +compute_metrics((predictions, labels)) +``` + +```python out +{'accuracy': 0.625} +``` + +現在我們的錯誤已修復! 這是最後一個,所以我們的腳本現在將正確訓練模型。 + +作為參考,這裡是完全修正好的腳本: + +```py +import numpy as np +from datasets import load_dataset, load_metric +from transformers import ( + AutoTokenizer, + AutoModelForSequenceClassification, + DataCollatorWithPadding, + TrainingArguments, + Trainer, +) + +raw_datasets = load_dataset("glue", "mnli") + +model_checkpoint = "distilbert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) + + +def preprocess_function(examples): + return tokenizer(examples["premise"], examples["hypothesis"], truncation=True) + + +tokenized_datasets = raw_datasets.map(preprocess_function, batched=True) +model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3) + +args = TrainingArguments( + f"distilbert-finetuned-mnli", + evaluation_strategy="epoch", + save_strategy="epoch", + learning_rate=2e-5, + num_train_epochs=3, + weight_decay=0.01, +) + +metric = load_metric("glue", "mnli") + + +def compute_metrics(eval_pred): + predictions, labels = eval_pred + predictions = np.argmax(predictions, axis=1) + return metric.compute(predictions=predictions, references=labels) + + +data_collator = DataCollatorWithPadding(tokenizer=tokenizer) + +trainer = Trainer( + model, + args, + train_dataset=tokenized_datasets["train"], + eval_dataset=tokenized_datasets["validation_matched"], + compute_metrics=compute_metrics, + data_collator=data_collator, + tokenizer=tokenizer, +) +trainer.train() +``` + +在這種情況下,如果沒有更多錯誤,我們的腳本將微調一個應該給出合理結果的模型。 但是,如果訓練沒有任何錯誤,而訓練出來的模型根本表現不佳,我們該怎麼辦? 這是機器學習中最難的部分,我們將向您展示一些可以提供幫助的技術。 + + + +💡 如果您使用手動訓練循環,則相同的步驟也適用於調試訓練管道,而且更容易將它們分開。 但是,請確保您沒有忘記正確位置的 `model.eval()` 或 `model.train()`,或者每個步驟中的 `zero_grad()`! + + + +## 在訓練期間調試靜默(沒有任何錯誤提示)錯誤 + +我們可以做些什麼來調試一個沒有錯誤地完成但沒有得到好的結果的訓練? 我們會在這裡給你一些提示,但請注意,這種調試是機器學習中最難的部分,並且沒有神奇的答案。 + +### 檢查您的數據(再次!) + +只有在理論上可以從您的數據中學到任何東西時,您的模型才會學到一些東西。 如果存在損壞數據的錯誤或標籤是隨機屬性的,那麼您很可能不會在數據集上獲得任何知識。 因此,始終首先仔細檢查您的解碼輸入和標籤,然後問自己以下問題: + +- 解碼後的數據是否可以理解? +- 你認同這些標籤嗎? +- 有沒有一個標籤比其他標籤更常見? +- 如果模型預測隨機的答案/總是相同的答案,那麼loss/評估指標應該是多少? + + + +⚠️ 如果您正在進行分佈式訓練,請在每個過程中打印數據集的樣本,並三次檢查您是否得到相同的結果。 一個常見的錯誤是在數據創建中有一些隨機性來源,這使得每個進程都有不同版本的數據集。 + + + +查看您的數據後,查看模型的一些預測並對其進行解碼。 如果模型總是預測同樣的事情,那可能是因為你的數據集偏向一個類別(針對分類問題); 過採樣稀有類等技術可能會有所幫助。 + +如果您在初始模型上獲得的loss/評估指標與您期望的隨機預測的loss/評估指標非常不同,請仔細檢查您的loss或評估指標的計算方式,因為那裡可能存在錯誤。 如果您使用最後添加的多個loss,請確保它們具有相同的規模。 + +當您確定您的數據是完美的時,您可以通過一個簡單的測試來查看模型是否能夠對其進行訓練。 + +### 在一批上過度擬合你的模型 + +過度擬合通常是我們在訓練時儘量避免的事情,因為這意味著模型沒有學習識別我們想要的一般特徵,而只是記住了訓練樣本。 在這種情況下,一遍又一遍地嘗試在一個批次上訓練您的模型是一個很好的測試,可以檢查您的問題是否可以通過您嘗試訓練的模型來解決。 它還將幫助您查看您的初始學習率是否太高。 + +一旦你定義了你的 `Trainer` 之後,這樣做真的很容易; 只需獲取一批訓練數據,然後僅使用該批次運行一個小型手動訓練循環,大約 20 步: + +```py +for batch in trainer.get_train_dataloader(): + break + +batch = {k: v.to(device) for k, v in batch.items()} +trainer.create_optimizer() + +for _ in range(20): + outputs = trainer.model(**batch) + loss = outputs.loss + loss.backward() + trainer.optimizer.step() + trainer.optimizer.zero_grad() +``` + + + +💡 如果您的訓練數據不平衡,請確保構建一批包含所有標籤的訓練數據。 + + + +生成的模型在一個“批次”上應該有接近完美的結果。 讓我們計算結果預測的指標: + +```py +with torch.no_grad(): + outputs = trainer.model(**batch) +preds = outputs.logits +labels = batch["labels"] + +compute_metrics((preds.cpu().numpy(), labels.cpu().numpy())) +``` + +```python out +{'accuracy': 1.0} +``` + +100% 準確率,現在這是一個很好的過擬合示例(這意味著如果你在任何其他句子上嘗試你的模型,它很可能會給你一個錯誤的答案)! + +如果你沒有設法讓你的模型獲得這樣的完美結果,這意味著你構建問題或數據的方式有問題,所以你應該修復它。 只有當你可以通過過擬合測試時,你才能確定你的模型實際上可以學到一些東西。 + + + +⚠️ 在此測試之後,您將不得不重新創建您的模型和“Trainer”,因為獲得的模型可能無法在您的完整數據集上恢復和學習有用的東西。 + + + +### 在你有第一個基線之前不要調整任何東西 + +超參數調優總是被強調為機器學習中最難的部分,但這只是幫助您在指標上有所收穫的最後一步。 大多數情況下,`Trainer` 的默認超參數可以很好地為您提供良好的結果,因此在您獲得超出數據集基線的東西之前,不要開始進行耗時且昂貴的超參數搜索 . + +一旦你有一個足夠好的模型,你就可以開始稍微調整一下。 不要嘗試使用不同的超參數啟動一千次運行,而是比較一個超參數的不同值的幾次運行,以瞭解哪個影響最大。 + +如果您正在調整模型本身,不要嘗試任何您無法合理證明的事情。 始終確保您返回過擬合測試以驗證您的更改沒有產生任何意外後果。 + +### 請求幫忙 + +希望您會在本節中找到一些可以幫助您解決問題的建議,但如果不是這樣,請記住您可以隨時在 [論壇](https://discuss.huggingface.co/) 上向社區提問。 + +以下是一些可能有用的額外資源: + +- [“作為工程最佳實踐工具的再現性”](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p),作者:Joel Grus +- [“神經網絡調試清單”](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) 作者:Cecelia Shao +- [“如何對機器學習代碼進行單元測試”](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) by Chase Roberts +- [“訓練神經網絡的秘訣”](http://karpathy.github.io/2019/04/25/recipe/)作者:Andrej Karpathy + +當然,並不是你在訓練神經網絡時遇到的每一個問題都是你自己的錯! 如果您在 🤗 Transformers 或 🤗 Datasets 庫中遇到看起來不正確的內容,您可能遇到了錯誤。 你應該告訴我們這一切,在下一節中,我們將準確解釋如何做到這一點。 diff --git a/chapters/zh-TW/chapter8/4_tf.mdx b/chapters/zh-TW/chapter8/4_tf.mdx new file mode 100644 index 000000000..64a80718f --- /dev/null +++ b/chapters/zh-TW/chapter8/4_tf.mdx @@ -0,0 +1,489 @@ + + +# Debugging the training pipeline + + + +你已經編寫了一個漂亮的腳本來訓練或微調給定任務的模型,盡職盡責地遵循 [第七章](/course/chapter7) 中的建議。 但是當你啟動命令 `model.fit()` 時,可怕的事情發生了:你得到一個錯誤😱! 或者更糟糕的是,一切似乎都很好,訓練運行沒有錯誤,但生成的模型很糟糕。 在本節中,我們將向您展示如何調試此類問題。 + +## Debugging the training pipeline + + + +The problem when you encounter an error in `model.fit()` is that it could come from multiple sources, as training usually brings together a lot of things that you've been working on up until that point. The problem could be something wrong in your dataset, or some issue when trying to batch elements of the datasets together. Or it could be something wrong in the model code, or your loss function or optimizer. And even if everything goes well for training, something could still go wrong during the evaluation if there is a problem with your metric. + +The best way to debug an error that arises in `model.fit()` is to manually go through this whole pipeline to see where things went awry. The error is then often very easy to solve. + +To demonstrate this, we will use the following script that (tries to) fine-tune a DistilBERT model on the [MNLI dataset](https://huggingface.co/datasets/glue): + +當您在 `model.fit()` 中遇到錯誤時,問題在於它可能來自多個來源,因為訓練通常會彙集很多您在此之前一直在做的事情。 問題可能是您的數據集中有問題,或者是在嘗試將數據集的元素批處理在一起時出現問題。 或者模型代碼、損失函數或優化器中可能有問題。 即使訓練一切順利,如果您的指標有問題,評估期間仍然可能出現問題。 + +調試 `model.fit()` 中出現的錯誤的最佳方法是手動檢查整個管道,看看哪裡出了問題。 錯誤通常很容易解決。 + +為了證明這一點,我們將使用以下腳本(嘗試)在 [MNLI 數據集](https://huggingface.co/datasets/glue)上微調 DistilBERT 模型: + +```py +from datasets import load_dataset, load_metric +from transformers import ( + AutoTokenizer, + TFAutoModelForSequenceClassification, +) + +raw_datasets = load_dataset("glue", "mnli") + +model_checkpoint = "distilbert-base-uncased" +tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) + + +def preprocess_function(examples): + return tokenizer(examples["premise"], examples["hypothesis"], truncation=True) + + +tokenized_datasets = raw_datasets.map(preprocess_function, batched=True) + +train_dataset = tokenized_datasets["train"].to_tf_dataset( + columns=["input_ids", "labels"], batch_size=16, shuffle=True +) + +validation_dataset = tokenized_datasets["validation_matched"].to_tf_dataset( + columns=["input_ids", "labels"], batch_size=16, shuffle=True +) + +model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint) + +model.compile(loss="sparse_categorical_crossentropy", optimizer="adam") + +model.fit(train_dataset) +``` + +如果您嘗試執行它,在進行數據集轉換時可能會收到一些“VisibleDeprecationWarning”——這是我們已知的 UX 問題,因此請忽略它。 如果你在 2021 年 11 月之後閱讀這門課程並且它仍在繼續,那麼請在推特上 @carrigmat 上發送憤怒的推文,直到他修復它。 + +然而,更嚴重的問題是我們得到了一個徹底的錯誤。 它真的非常長: + +```python out +ValueError: No gradients provided for any variable: ['tf_distil_bert_for_sequence_classification/distilbert/embeddings/word_embeddings/weight:0', '...'] +``` + +這意味著什麼? 我們試圖訓練我們的數據,但我們沒有梯度? 這很令人困惑。 我們甚至不知道該如何開始調試類似的東西? 當你得到的錯誤並不能立即表明問題出在哪裡時,最好的解決方案通常是按順序檢查所有內容,確保在每個階段一切看起來都是正確的。 當然,開始的地方總是... + +### 檢查您的數據 + +這是不言而喻的,但如果您的數據已損壞,Keras 將無法為您修復它。 所以首先,你需要看看你的訓練集中有什麼。 + +儘管查看 `raw_datasets` 和 `tokenized_datasets` 很誘人,但我們強烈建議您在數據將要進入模型的地方直接查看數據。 這意味著應該從您使用 `to_tf_dataset()` 函數創建的 `tf.data.Dataset` 讀取輸出! 那麼我們該怎麼做呢? `tf.data.Dataset` 對象一次給我們整個批次並且不支持索引,所以我們不能只請求 `train_dataset[0]`。 但是,我們可以禮貌地向它要一批: + +```py +for batch in train_dataset: + break +``` + +`break` 在一次迭代後結束循環,因此這會抓取來自`train_dataset` 的第一批並將其保存為`batch`。 現在,讓我們看看裡面有什麼: + +```python out +{'attention_mask': , + 'label': , + 'input_ids': } +``` + +這看起來不錯,不是嗎?我們將 `labels` 、`attention_mask` 和 `input_ids` 傳遞給模型,這應該是計算輸出和計算損失所需的一切。那麼為什麼我們沒有梯度呢?仔細看:我們將單個字典作為輸入傳遞,但訓練批次通常是輸入張量或字典,加上標籤張量。我們的標籤只是我們輸入字典中的一個鍵。 + +這是一個問題嗎?實際上,並非總是如此!但這是您在使用 TensorFlow 訓練 Transformer 模型時會遇到的最常見問題之一。我們的模型都可以在內部計算損失,但要做到這一點,需要在輸入字典中傳遞標籤。這是當我們沒有為 `compile()` 指定損失值時使用的損失。另一方面,Keras 通常希望標籤與輸入字典分開傳遞,如果你不這樣做,損失計算通常會失敗。 + +問題現在變得更清楚了:我們傳遞了一個“損失”參數,這意味著我們要求 Keras 為我們計算損失,但我們將標籤作為模型的輸入傳遞,而不是放在 Keras 期望的地方的!我們需要二選一:要麼使用模型的內部損失並將標籤保留在原處,要麼繼續使用 Keras 損失,但將標籤移動到 Keras 期望的位置。為簡單起見,讓我們採用第一種方法。將對 `compile()` 的調用更改為: + +```py +model.compile(optimizer="adam") +``` + +現在我們將使用模型的內部損失,這個問題應該解決了! + + + +✏️ **輪到你了!** 作為我們解決其他問題後的可選挑戰,你可以嘗試回到這一步,讓模型使用原始 Keras 計算的損失而不是內部損失。 您需要將 `"labels"` 添加到 `to_tf_dataset()` 的 `label_cols` 參數,以確保正確輸出標籤,這將為您提供梯度——但我們指定的損失還有一個問題 . 訓練仍然會遇到這個問題,學習會非常緩慢,並且會在多次訓練損失時達到穩定狀態。 你能弄清楚它是什麼嗎? + +一個 ROT13 編碼的提示,如果你卡住了:Vs lbh ybbx ng gur bhgchgf bs FrdhraprPynffvsvpngvba zbqryf va Genafsbezref, gurve svefg bhgchg vf `ybtvgf`。 榮格納 ybtvgf? + +第二個提示:Jura lbh fcrpvsl bcgvzvmref, npgvingvbaf 是 ybffrf jvgu fgevatf, Xrenf frgf nyy gur nethzrag inyhrf gb gurve qrsnhygf。 Jung nethzragf qbrf FcnefrPngrtbevpnyPebffragebcl unir, naq jung ner gurve qrsnhygf? + + + +現在,讓我們嘗試訓練。 我們現在應該得到梯度,所以希望(這裡播放不祥的音樂)我們可以調用 `model.fit()` 一切都會正常工作! + +```python out + 246/24543 [..............................] - ETA: 15:52 - loss: nan +``` + +Oh no. + +`nan` 不是一個非常令人開心的損失值。 儘管如此,我們已經檢查了我們的數據,它看起來還不錯。 如果這不是問題,我們下一步該去哪裡? 顯而易見的下一步是... + +### 檢查你的模型 + +`model.fit()` 是 Keras 中一個非常方便的函數,但它為您做了很多事情,這使得準確找到問題發生的位置變得更加棘手。 如果您正在調試您的模型,一個真正有用的策略是隻將一個批次傳遞給模型,並詳細查看該批次的輸出。 如果模型拋出錯誤,另一個非常有用的提示是使用 `run_eagerly=True` `compile()` 模型。 這會使它變慢很多,但它會使錯誤消息更容易理解,因為它們會準確地指出問題發生在模型代碼的哪個位置。 + +不過,目前我們還不需要 `run_eagerly`。 讓我們通過模型運行我們之前得到的“批處理”,看看輸出是什麼樣子的: + +```py +model(batch) +``` + +```python out +TFSequenceClassifierOutput(loss=, logits=, hidden_states=None, attentions=None) +``` + +嗯,這很棘手。一切都是`nan`!但這很奇怪,不是嗎?我們所有的 logits 怎麼會變成“nan”? `nan` 的意思是“不是數字”。 `nan` 值經常出現在您執行禁止操作時,例如除以零。但是,在機器學習中瞭解 `nan` 非常重要的一件事是,該值傾向於*傳播*。如果將一個數字乘以 `nan`,則輸出也是 `nan`。如果你在輸出、損失或梯度的任何地方得到一個“nan”,那麼它會迅速傳播到你的整個模型中——因為當那個“nan”值通過你的網絡傳播回來時,你會得到nan 梯度,當使用這些梯度計算權重更新時,您將獲得 nan 權重,這些權重將計算更多的 nan 輸出!很快,整個網絡將只是“nan”的一大塊。一旦發生這種情況,就很難看出問題是從哪裡開始的。我們如何隔離“nan”第一次出現的地方? + +答案是嘗試*重新初始化*我們的模型。一旦我們開始訓練,我們就會在某個地方得到一個“nan”,它很快就會傳播到整個模型中。所以,讓我們從檢查點加載模型而不做任何權重更新,看看我們從哪裡得到一個 `nan` 值: + +```py +model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint) +model(batch) +``` + +當我們運行它時,我們得到: + +```py out +TFSequenceClassifierOutput(loss=, logits=, hidden_states=None, attentions=None) +``` + +*現在*我們到了某個地方! 我們的 logits 中沒有 `nan` 值,這令人放心。 但是我們確實在損失中看到了一些“nan”值! 這些樣本有什麼特別導致這個問題的嗎? 讓我們看看它們是哪些(請注意,如果您自己運行此代碼,您可能會得到不同的索引,因為數據集已被隨機打亂): + +```python +import numpy as np + +loss = model(batch).loss.numpy() +indices = np.flatnonzero(np.isnan(loss)) +indices +``` + +```python out +array([ 1, 2, 5, 7, 9, 10, 11, 13, 14]) +``` + +讓我們看看這些來自樣本的輸入id: + +```python +input_ids = batch["input_ids"].numpy() +input_ids[indices] +``` + +```python out +array([[ 101, 2007, 2032, 2001, 1037, 16480, 3917, 2594, 4135, + 23212, 3070, 2214, 10170, 1010, 2012, 4356, 1997, 3183, + 6838, 12953, 2039, 2000, 1996, 6147, 1997, 2010, 2606, + 1012, 102, 6838, 2001, 3294, 6625, 3773, 1996, 2214, + 2158, 1012, 102, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0], + [ 101, 1998, 6814, 2016, 2234, 2461, 2153, 1998, 13322, + 2009, 1012, 102, 2045, 1005, 1055, 2053, 3382, 2008, + 2016, 1005, 2222, 3046, 8103, 2075, 2009, 2153, 1012, + 102, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0], + [ 101, 1998, 2007, 1996, 3712, 4634, 1010, 2057, 8108, + 2025, 3404, 2028, 1012, 1996, 2616, 18449, 2125, 1999, + 1037, 9666, 1997, 4100, 8663, 11020, 6313, 2791, 1998, + 2431, 1011, 4301, 1012, 102, 2028, 1005, 1055, 5177, + 2110, 1998, 3977, 2000, 2832, 2106, 2025, 2689, 2104, + 2122, 6214, 1012, 102, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0], + [ 101, 1045, 2001, 1999, 1037, 13090, 5948, 2007, 2048, + 2308, 2006, 2026, 5001, 2043, 2026, 2171, 2001, 2170, + 1012, 102, 1045, 2001, 3564, 1999, 2277, 1012, 102, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0], + [ 101, 2195, 4279, 2191, 2039, 1996, 2181, 2124, 2004, + 1996, 2225, 7363, 1012, 102, 2045, 2003, 2069, 2028, + 2451, 1999, 1996, 2225, 7363, 1012, 102, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0], + [ 101, 2061, 2008, 1045, 2123, 1005, 1056, 2113, 2065, + 2009, 2428, 10654, 7347, 2030, 2009, 7126, 2256, 2495, + 2291, 102, 2009, 2003, 5094, 2256, 2495, 2291, 2035, + 2105, 1012, 102, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0], + [ 101, 2051, 1010, 2029, 3216, 2019, 2503, 3444, 1010, + 6732, 1996, 2265, 2038, 19840, 2098, 2125, 9906, 1998, + 2003, 2770, 2041, 1997, 4784, 1012, 102, 2051, 6732, + 1996, 2265, 2003, 9525, 1998, 4569, 1012, 102, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0], + [ 101, 1996, 10556, 2140, 11515, 2058, 1010, 2010, 2162, + 2252, 5689, 2013, 2010, 7223, 1012, 102, 2043, 1996, + 10556, 2140, 11515, 2058, 1010, 2010, 2252, 3062, 2000, + 1996, 2598, 1012, 102, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0], + [ 101, 13543, 1999, 2049, 6143, 2933, 2443, 102, 2025, + 13543, 1999, 6143, 2933, 2003, 2443, 102, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0]]) +``` + +嗯,這裡有很多東西,但沒有什麼不尋常的。 讓我們看看標籤: + +```python out +labels = batch['labels'].numpy() +labels[indices] +``` + +```python out +array([2, 2, 2, 2, 2, 2, 2, 2, 2]) +``` + +啊! `nan` 樣本都具有相同的標籤,即標籤 2。這是一個非常明顯的提示。 當我們的標籤為 2 時,我們會得到loss為 `nan`,這表明這是檢查模型中標籤數量的好時機: + +```python +model.config.num_labels +``` + +```python out +2 +``` + +現在我們看到了問題:模型認為只有兩個類,但標籤上升到 2,這意味著實際上有三個類(因為 0 也是一個類)。 這就是我們得到“nan”的方式——通過嘗試計算不存在的類的損失! 讓我們嘗試改變它並再次擬合模型: + +``` +model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3) +model.compile(optimizer='adam') +model.fit(train_dataset) +``` + +```python out + 869/24543 [>.............................] - ETA: 15:29 - loss: 1.1032 +``` + +我們在訓練! 沒有更多的'nan's,我們的損失正在減少......有點。 如果你觀察一段時間,你可能會開始有點不耐煩,因為損失值一直居高不下。 讓我們在這裡停止訓練並嘗試考慮可能導致此問題的原因。 在這一點上,我們很確定數據和模型都沒有問題,但是我們的模型並沒有很好地學習。 還剩下什麼? 是時候... + +### 檢查你的超參數 + +如果你回頭看上面的代碼,你可能根本看不到任何超參數,除了 `batch_size`,這似乎不是罪魁禍首。不過,不要被迷惑;總是有超參數,如果你看不到它們,那只是意味著你不知道它們的設置是什麼。特別要記住關於 Keras 的一個關鍵點:如果您使用字符串設置損失函數、優化器或激活函數,_它的所有參數都將設置為它們的默認值_。這意味著即使為此使用字符串非常方便,但在這樣做時您應該非常小心,因為它很容易對您隱藏關鍵的事情。 (任何嘗試上述方式的人都應該仔細注意這一事實。) + +在這種情況下,我們在哪裡設置了帶有字符串的參數?我們最初使用字符串設置損失,但我們不再這樣做了。但是,我們正在使用字符串設置優化器。難道這對我們隱瞞了什麼?讓我們看看[關於它的一些討論](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam)。 + +這裡有什麼需要注意的嗎?沒錯——學習率!當我們只使用字符串“adam”時,我們將獲得默認的學習率,即 0.001,即 1e-3。這對於transormer模型來說太高了!一般來說,我們建議您的模型嘗試 1e-5 和 1e-4 之間的學習率;這比我們在這裡實際使用的值小 10X 到 100X 之間。聽起來這可能是一個主要問題,所以讓我們嘗試減少它。為此,我們需要導入實際的“優化器”對象。當我們這樣做的時候,讓我們從檢查點重新初始化模型,以防高學習率的訓練損壞了它的權重: + +```python +from tensorflow.keras.optimizers import Adam + +model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint) +model.compile(optimizer=Adam(5e-5)) +``` + + + +💡您還可以從🤗 Transformers 中導入 `create_optimizer()` 函數,這將為您提供具有正確權重衰減以及學習率預熱和學習率衰減的 AdamW 優化器。 此優化器通常會產生比使用默認 Adam 優化器獲得的結果稍好一些的結果。 + + + +現在,我們可以嘗試使用新的、改進後的學習率來擬合模型: + +```python +model.fit(train_dataset) +``` + +```python out +319/24543 [..............................] - ETA: 16:07 - loss: 0.9718 +``` + +現在我們的損失真的在某個地方! 訓練終於看起來奏效了。 這裡有一個教訓:當你的模型正在運行但損失沒有下降,並且你確定你的數據沒問題時,檢查學習率和權重衰減等超參數是個好主意。 將其中任何一個設置得太高很可能導致訓練在高損失值下“停滯”。 + +## 其他潛在問題 + +我們已經涵蓋了上面腳本中的問題,但您可能會遇到其他幾個常見錯誤。 讓我們看一個(非常不完整的)列表。 + +### 處理內存不足錯誤 + +內存不足的跡象是“分配張量時出現 OOM”之類的錯誤——OOM 是“內存不足”的縮寫。 在處理大型語言模型時,這是一個非常常見的危險。 如果遇到這種情況,一個好的策略是將批量大小減半並重試。 但請記住,有些型號*非常*大。 例如,全尺寸 GPT-2 的參數為 1.5B,這意味著您將需要 6 GB 的內存來存儲模型,另外需要 6 GB 的內存用於梯度下降! 無論您使用什麼批量大小,訓練完整的 GPT-2 模型通常需要超過 20 GB 的 VRAM,而只有少數 GPU 擁有。 像“distilbert-base-cased”這樣更輕量級的模型更容易運行,訓練也更快。 + + + +在課程的下一部分中,我們將介紹更先進的技術,這些技術可以幫助您減少內存佔用並讓您微調最大的模型。 + + + +### TensorFlow 🦛餓餓 + +您應該注意的 TensorFlow 的一個特殊怪癖是,它會在您加載模型或進行任何訓練後立即為自己分配 *所有 * GPU 內存,然後根據需要分配該內存。這與其他框架的行為不同,例如 PyTorch,後者根據 CUDA 的需要分配內存,而不是在內部進行。 TensorFlow 方法的一個優點是,當您耗盡內存時,它通常會給出有用的錯誤,並且它可以從該狀態恢復而不會導致整個 CUDA 內核崩潰。但也有一個重要的缺點:如果你同時運行兩個 TensorFlow 進程,那麼**你將度過一段糟糕的時光**。 + +如果您在 Colab 上運行,則無需擔心這一點,但如果您在本地運行,這絕對是您應該小心的事情。特別要注意,關閉筆記本選項卡並不一定會關閉該筆記本!您可能需要選擇正在運行的筆記本(帶有綠色圖標的筆記本)並在目錄列表中手動關閉它們。任何使用 TensorFlow 的正在運行的筆記本仍可能佔用大量 GPU 內存,這意味著您啟動的任何新筆記本都可能會遇到一些非常奇怪的問題。 + +如果您開始運行之前正確的代碼卻收到有關 CUDA、BLAS 或 cuBLAS 的錯誤,這通常是罪魁禍首。您可以使用類似 `nvidia-smi` 的命令來檢查 - 當您關閉或重新啟動當前筆記本時,您的大部分內存是否空閒,或者是否仍在使用中?如果它仍在使用中,則有其他東西在佔用它! + +### 檢查您的數據(再次!) + +只有在理論上可以從您的數據中學到任何東西時,您的模型才會學到一些東西。 如果存在損壞數據的錯誤或標籤是隨機屬性的,那麼您很可能不會在數據集上獲得任何知識。這裡一個有用的工具是`tokenizer.decode()`。 這會將 `input_ids` 轉換回字符串,因此您可以查看數據並查看您的訓練數據是否正在教授您希望它教授的內容。 例如,像我們上面所做的那樣從 `tf.data.Dataset` 中獲取 `batch` 後,您可以像這樣解碼第一個元素: + +```py +input_ids = batch["input_ids"].numpy() +tokenizer.decode(input_ids[0]) +``` + +Then you can compare it with the first label, like so: + +```py +labels = batch["labels"].numpy() +label = labels[0] +``` +一旦您可以像這樣查看您的數據,您可以問自己以下問題: + +- 解碼後的數據是否可以理解? +- 你認同這些標籤嗎? +- 有沒有一個標籤比其他標籤更常見? +- 如果模型預測隨機的答案/總是相同的答案,那麼loss/評估指標應該是多少? + +查看您的數據後,查看模型的一些預測並對其進行解碼。 如果模型總是預測同樣的事情,那可能是因為你的數據集偏向一個類別(針對分類問題); 過採樣稀有類等技術可能會有所幫助。 + +如果您在初始模型上獲得的loss/評估指標與您期望的隨機預測的loss/評估指標非常不同,請仔細檢查您的loss或評估指標的計算方式,因為那裡可能存在錯誤。 如果您使用最後添加的多個loss,請確保它們具有相同的規模。 + +當您確定您的數據是完美的時,您可以通過一個簡單的測試來查看模型是否能夠對其進行訓練。 + +### 在一批上過度擬合你的模型 + +過度擬合通常是我們在訓練時儘量避免的事情,因為這意味著模型沒有學習識別我們想要的一般特徵,而只是記住了訓練樣本。 但是,一遍又一遍地嘗試在一個批次上訓練您的模型是一個很好的測試,可以檢查您構建的問題是否可以通過您嘗試訓練的模型來解決。 它還將幫助您查看您的初始學習率是否太高。 + +一旦你定義了你的“模型”,這樣做真的很容易; 只需獲取一批訓練數據,然後將該“批次”視為您的整個數據集,並在其上fit大量epoch: + +```py +for batch in train_dataset: + break + +# Make sure you have run model.compile() and set your optimizer, +# and your loss/metrics if you're using them + +model.fit(batch, epochs=20) +``` + + + +💡 如果您的訓練數據不平衡,請確保構建一批包含所有標籤的訓練數據。 + + + +生成的模型在“批次”上應該有接近完美的結果,損失迅速下降到 0(或您正在使用的損失的最小值)。 + +如果你沒有設法讓你的模型獲得這樣的完美結果,這意味著你構建問題或數據的方式有問題,所以你應該修復它。 只有當你設法通過過擬合測試時,你才能確定你的模型實際上可以學到一些東西。 + + + +⚠️ 在此測試之後,您將不得不重新創建您的模型和“Trainer”,因為獲得的模型可能無法在您的完整數據集上恢復和學習有用的東西。 + + + +### 在你有第一個基線之前不要調整任何東西 + +超參數調整總是被強調為機器學習中最難的部分,但這只是幫助您在指標上獲得一點點提升的最後一步。 例如將默認的 Adam 學習率 1e-3 與 Transformer 模型一起使用,當然會使學習進行得非常緩慢或完全停止,但大多數時候“合理”的超參數,例如從 1e-5 到 5e-5 的學習率,會很好地給你帶來好的結果。因此,在您獲得超出數據集基線的東西之前,不要開始進行耗時且昂貴的超參數搜索。 + +一旦你有一個足夠好的模型,你就可以開始稍微調整一下。 不要嘗試使用不同的超參數啟動一千次運行,而是比較一個超參數的不同值的幾次運行,以瞭解哪個影響最大。 + +如果您正在調整模型本身,不要嘗試任何您無法合理證明的事情。 始終確保您返回過擬合測試以驗證您的更改沒有產生任何意外後果。 + +### 請求幫忙 + +希望您會在本節中找到一些可以幫助您解決問題的建議,但如果不是這樣,請記住您可以隨時在 [論壇](https://discuss.huggingface.co/) 上向社區提問。 + +以下是一些可能有用的額外資源: + +- [“作為工程最佳實踐工具的再現性”](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p),作者:Joel Grus +- [“神經網絡調試清單”](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) 作者:Cecelia Shao +- [“如何對機器學習代碼進行單元測試”](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) by Chase Roberts +- [“訓練神經網絡的秘訣”](http://karpathy.github.io/2019/04/25/recipe/)作者:Andrej Karpathy + +當然,並不是你在訓練神經網絡時遇到的每一個問題都是你自己的錯! 如果您在 🤗 Transformers 或 🤗 Datasets 庫中遇到看起來不正確的內容,您可能遇到了錯誤。 你應該告訴我們這一切,在下一節中,我們將準確解釋如何做到這一點。 diff --git a/chapters/zh-TW/chapter8/5.mdx b/chapters/zh-TW/chapter8/5.mdx new file mode 100644 index 000000000..378a8ccb9 --- /dev/null +++ b/chapters/zh-TW/chapter8/5.mdx @@ -0,0 +1,85 @@ +# 如何寫一個好問題 + + + +當您遇到 Hugging Face 庫中的一個看起來不正確的東西時,您一定要告訴我們,以便我們可以修復它(就此而言,任何開源庫也是如此)。如果您不能完全確定錯誤是在您自己的代碼中還是在我們的某個庫中,那麼首先要檢查的是[forums](https://discuss.huggingface.co/).社區會幫助你解決這個問題,Hugging Face 團隊也會密切關注那裡的討論。 + + + +當您確定手頭有錯誤時,第一步是構建一個最小的可重現示例。 +## 創建一個最小的可重現示例 + +隔離產生錯誤的代碼段非常重要,因為 Hugging Face 團隊中沒有人是魔術師(目前),他們無法修復他們看不到的東西。顧名思義,最小的可重現示例應該是可重現的。這意味著它不應依賴於您可能擁有的任何外部文件或數據。嘗試用一些看起來像真實值的虛擬值替換您正在使用的數據,但仍然會產生相同的錯誤。 + + + +🚨🤗 Transformers 存儲庫中的許多問題都沒有解決,因為用於複製它們的數據不可訪問。 + + + +一旦你有一些自包含的東西,你可以嘗試將它減少到更少的代碼行,構建我們所謂的最小的可重複示例.雖然這需要你做更多的工作,但如果你提供一個漂亮的、簡短的錯誤重現器,你幾乎可以保證得到幫助和修復。 + +如果您覺得足夠舒服,請檢查發生錯誤的源代碼。您可能會找到問題的解決方案(在這種情況下,您甚至可以提出拉取請求來修復它),但更一般地說,這可以幫助維護人員在閱讀您的報告時更好地理解來源。 + +## 填寫問題模板 + +當您提交問題時,您會注意到有一個模板需要填寫。我們將按照[🤗 Transformers issues](https://github.com/huggingface/transformers/issues/new/choose)在這裡,但是如果您在另一個存儲庫中報告問題,則需要相同類型的信息。不要將模板留空:花時間填寫它可以最大限度地提高您獲得答案和解決問題的機會。 + +通常,在提交問題時,請始終保持禮貌。這是一個開源項目,因此您使用的是免費軟件,沒有人有任何義務幫助您。您可能會在您的問題中包含您認為合理的批評,但是維護人員很可能會認為它很糟糕並且不會急於幫助您。確保你閱讀了[code of conduct](https://github.com/huggingface/transformers/blob/master/CODE_OF_CONDUCT.md)項目的。 + +### 包括您的環境信息 + +🤗 Transformers 提供了一個實用程序來獲取我們需要的關於您的環境的所有信息。只需在終端中輸入以下內容: + +``` +transformers-cli env +``` + +你應該得到這樣的東西: + +```out +Copy-and-paste the text below in your GitHub issue and FILL OUT the two last points. + +- `transformers` version: 4.12.0.dev0 +- Platform: Linux-5.10.61-1-MANJARO-x86_64-with-arch-Manjaro-Linux +- Python version: 3.7.9 +- PyTorch version (GPU?): 1.8.1+cu111 (True) +- Tensorflow version (GPU?): 2.5.0 (True) +- Flax version (CPU?/GPU?/TPU?): 0.3.4 (cpu) +- Jax version: 0.2.13 +- JaxLib version: 0.1.65 +- Using GPU in script?: +- Using distributed or parallel set-up in script?: +``` + +您還可以添加一個 **!** 在開始的時候 **transformers-cli env** 命令從筆記本單元執行它,然後在問題的開頭複製並粘貼結果。 + +### 標記人員 + +通過輸入標記人員 **@** 其次是他們的 GitHub 句柄將向他們發送通知,以便他們會看到您的問題並可能會更快地回覆。適度使用它,因為如果您標記的人沒有直接鏈接,他們可能不喜歡收到通知。如果您查看了與您的錯誤相關的源文件,您應該在您認為對您的問題負責的行中標記最後一個進行更改的人(您可以通過查看 GitHub 上的所述行找到此信息,選擇它,然後單擊“查看 git blame”)。 + +否則,模板會提供要標記的人的建議。一般來說,不要標記超過三個人! + +### 包含一格可重複的示例 + +如果您已經設法創建了一個產生錯誤的獨立示例,那麼現在是包含它的時候了!鍵入一行包含三個反引號,後跟 **python** , 像這樣: + +``` +```python +``` + +然後粘貼您的最小可重現示例並鍵入一個帶有三個反引號的新行。這將確保您的代碼格式正確。如果您沒有設法創建可重現的示例,請以清晰的步驟解釋您是如何解決問題的。如果可以,請包含指向錯誤所在的 Google Colab 筆記本的鏈接。你分享的信息越多,維護者就越有能力回覆你。在所有情況下,您都應該複製並粘貼您收到的整個錯誤消息。如果您在 Colab 中工作,請記住,堆棧跟蹤中的某些幀可能會自動摺疊,因此請確保在複製之前展開它們。與代碼示例一樣,將該錯誤消息放在兩行之間,並帶有三個反引號,因此格式正確。 + +### 描述預期行為 + +用幾行解釋你期望得到什麼,以便維護人員完全掌握問題。這部分通常很明顯,所以應該用一句話來形容,但在某些情況下,您可能有很多話要說。 + +## 然後什麼? + +提交您的問題後,請確保快速檢查一切是否正常。如果您犯了錯誤,您可以編輯問題,或者如果您發現問題與您最初的想法不同,甚至可以更改其標題。如果你沒有得到答案,就沒有必要對人進行 ping 操作。如果幾天內沒有人幫助您,很可能沒有人能理解您的問題。不要猶豫,回到可重現的例子。你能不能讓它更短更切題?如果您在一週內沒有得到答覆,您可以留言溫和地尋求幫助,特別是如果您已編輯問題以包含有關該問題的更多信息。 + diff --git a/chapters/zh-TW/chapter8/6.mdx b/chapters/zh-TW/chapter8/6.mdx new file mode 100644 index 000000000..e6aa7e608 --- /dev/null +++ b/chapters/zh-TW/chapter8/6.mdx @@ -0,0 +1,7 @@ +# 第2部分完成! + +恭喜,您已經完成了課程的第二部分!我們正在積極研究第三個,所以訂閱我們的[newsletter](https://huggingface.curated.co/)以確保您不會錯過它的發佈。 + +。您現在應該能夠處理一系列 NLP 任務,並對它們進行微調或預訓練模型。不要忘記與社區分享您的結果[Model Hub](https://huggingface.co/models). + +我們迫不及待地想看看您將利用所獲得的知識構建什麼! diff --git a/chapters/zh-TW/chapter8/7.mdx b/chapters/zh-TW/chapter8/7.mdx new file mode 100644 index 000000000..de910a9f4 --- /dev/null +++ b/chapters/zh-TW/chapter8/7.mdx @@ -0,0 +1,190 @@ + + +# 章末測評 + +讓我們測試一下你在本章學到的東西! + +### 1.應該按照什麼順序讀取 Python 回溯? + + +### 2.什麼是最小可再生示例? + + +### 3.假設你嘗試運行以下代碼,它拋出一個錯誤: +```py +from transformers import GPT3ForSequenceClassification + +# ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py) +# --------------------------------------------------------------------------- +# ImportError Traceback (most recent call last) +# /var/folders/28/k4cy5q7s2hs92xq7_h89_vgm0000gn/T/ipykernel_30848/333858878.py in +# ----> 1 from transformers import GPT3ForSequenceClassification + +# ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py) +``` + +以下哪項可能是論壇主題標題尋求幫助的好選擇? + + GPT3ForSequenceClassification ?", + explain: "不錯的選擇!這個標題是簡潔的,並給讀者一個線索,什麼可能是錯誤的(即,gpt-3不支持在🤗 Transformers)。", + correct: true + }, + { + text: "Gpt-3在🤗 Transformers中支持嗎?", + explain: "好主意! 用問題作為主題標題是向社區傳達問題的好方法。", + correct: true + } + ]} +/> + +### 4.假設您試圖運行 'trainer.train ()',但是遇到了一個神秘的錯誤,這個錯誤不能準確地告訴您錯誤來自哪裡。下列哪一項是您應該首先在您的培訓管道中尋找錯誤的地方? + + +### 5.調試 CUDA 錯誤的最好方法是什麼? + + +### 6.修復 GitHub 上的問題最好的方法是什麼? + + +### 7.為什麼對一個批處理進行過度調試通常是一種好的調試技術? + + +### 8.為什麼在 🤗 Transformers 存儲庫中創建新問題時,使用 transformers-cli env 包含有關計算環境的詳細信息是個好主意? + \ No newline at end of file diff --git a/chapters/zh-TW/chapter9/1.mdx b/chapters/zh-TW/chapter9/1.mdx new file mode 100644 index 000000000..26099ac12 --- /dev/null +++ b/chapters/zh-TW/chapter9/1.mdx @@ -0,0 +1,36 @@ +# Gradio 簡介 + +在本章中,我們將學習如何為您的機器學習構建**交互式演示**模型。 + +為什麼首先要為您的機器學習模型構建演示或 GUI?演示可以帶來: + +- **機器學習開發人員**可以輕鬆地向包括非技術團隊或客戶在內的廣大受眾展示他們的工作 +- **研究人員**更輕鬆地重現機器學習模型和行為 +- **質量測試人員**或**最終用戶**更容易識別和調試模型的故障點 +- **不同的用戶**發現模型中的算法偏差 + +我們將使用 Gradio 庫為我們的模型構建演示。 Gradio 允許您完全使用 Python 為任何機器學習模型構建、自定義和共享基於 Web 的演示。 + +以下是一些使用 Gradio 構建的機器學習演示示例: + +* 一個**草圖識別**模型,它接收草圖並輸出它認為正在繪製的標籤: + + + +* 一個抽取式**問題回答**模型,它接受上下文段落和一個任務並輸出一個結果和一個概率分數(我們在[第7章](/course/chapter7/7)中討論了這種模型): + + + +* 一個**背景去除**模型,它接收圖像並輸出去除背景的圖像: + + + +本章分為兩個部分,包括_概念_和_應用程序_。在您瞭解每個部分的概念後,您將應用它來構建特定類型的演示,範圍從圖像分類到語音識別。當你讀完本章時,你將能夠用幾行 Python 代碼構建這些演示(以及更多!)。 + +👀 點擊 Hugging Face Spaces 以查看機器學習社區構建的許多機器學習演示的最新示例! + +## Gradio 方塊派對🥳 + +如果你想充分利用本章中的知識,就加入 Gradio 積木派對吧!這是由 Hugging Face 於**5 月 16 日至 31 日**舉辦的社區活動。在此活動中,您將使用 Gradio 構建酷炫的機器學習演示,並參與贏取 Hugging Face 禮物和獎品! + +查看 [活動描述](https://github.com/AK391/community-events/blob/main/gradio-blocks/README.md) 可以瞭解如何參與的詳細信息 - 我們迫不及待地想看看你構建的🤗演示! diff --git a/chapters/zh-TW/chapter9/2.mdx b/chapters/zh-TW/chapter9/2.mdx new file mode 100644 index 000000000..b465850a7 --- /dev/null +++ b/chapters/zh-TW/chapter9/2.mdx @@ -0,0 +1,112 @@ +# 構建你的第一個演示 + + + +讓我們從安裝 Gradio 開始吧! 由於它是一個 Python 包,只需運行: + +`$ pip install gradio ` + +您可以在任何地方運行 Gradio,無論是從您最喜歡的 Python IDE、Jupyter notebook 還是 Google Colab 🤯! +所以無論你在哪裡運行 Python,都可以安裝 Gradio! + +讓我們從一個簡單的“Hello World”示例開始,熟悉 Gradio 語法: + +```py +import gradio as gr + + +def greet(name): + return "Hello " + name + + +demo = gr.Interface(fn=greet, inputs="text", outputs="text") + +demo.launch() +``` + +讓我們看一下上面的代碼: + +-首先,我們定義一個名為 `greet()` 的函數。 在這種情況下,它是一個在您的名字前添加“Hello”的簡單函數,但它通常可以是 *any* Python 函數。 例如,在機器學習應用程序中,此函數將*調用模型以對輸入進行預測*並返回輸出。 +- 然後,我們創建一個帶有三個參數的 Gradio `Interface`,`fn`、`inputs` 和 `outputs`。 這些參數定義了預測函數,以及我們想要的輸入和輸出組件的_type_。 在我們的例子中,兩個組件都是簡單的文本框。 +- 然後我們在我們創建的 `Interface` 上調用 `launch()` 方法。 + +如果你運行這段代碼,下面的界面會自動出現在 Jupyter/Colab notebook 中,或者在瀏覽器中彈出 **[http://localhost:7860](http://localhost:7860/)** 如果運行 從一個腳本。 + + + +立即嘗試使用您自己的姓名或其他輸入來使用此 GUI! + +您會注意到,在這個 GUI 中,Gradio 自動推斷輸入參數的名稱 (`name`)並將其應用為文本框頂部的標籤。 如果你想改變它怎麼辦?或者,如果您想以其他方式自定義文本框? 在這種情況下,您可以實例化一個表示輸入組件的類對象。 + +看看下面的例子: + +```py +import gradio as gr + + +def greet(name): + return "Hello " + name + + +# We instantiate the Textbox class +textbox = gr.Textbox(label="Type your name here:", placeholder="John Doe", lines=2) + +gr.Interface(fn=greet, inputs=textbox, outputs="text").launch() +``` + + + +在這裡,我們創建了一個帶有標籤、佔位符和一組行數的輸入文本框。您可以對輸出文本框執行相同的操作,但我們現在將其保留。 + +我們已經看到,只需幾行代碼,Gradio 就可以讓您圍繞任何具有任何類型輸入或輸出的函數創建一個簡單的界面。 在本節中,我們從一個簡單的文本框開始,但在接下來的部分中,我們將介紹其他類型的輸入和輸出。 現在讓我們看看在 Gradio 應用程序中包含一些 NLP。 + + +## 🤖 包括模型預測 + +現在讓我們構建一個簡單的界面,讓您可以演示像 GPT-2 這樣的**文本生成**模型。 + +我們將使用 🤗 Transformers 中的 `pipeline()` 函數加載我們的模型。 +如果您需要快速複習,您可以返回到 [第 1 章中的那個部分](/course/chapter1/3#text-generation)。 + +首先,我們定義一個接受文本提示並返回文本完成的預測函數: + +```py +from transformers import pipeline + +model = pipeline("text-generation") + + +def predict(prompt): + completion = model(prompt)[0]["generated_text"] + return completion +``` + +此函數完成您提供的提示,您可以使用自己的輸入提示運行它以查看它是如何工作的。 這是一個示例(您可能會得到不同的完成): + +``` +predict("My favorite programming language is") +``` + +``` +>> My favorite programming language is Haskell. I really enjoyed the Haskell language, but it doesn't have all the features that can be applied to any other language. For example, all it does is compile to a byte array. +``` + +現在我們有了一個生成預測的函數,我們可以像之前一樣創建和啟動一個“接口”: + +```py +import gradio as gr + +gr.Interface(fn=predict, inputs="text", outputs="text").launch() +``` + + +就是這樣! 您現在可以使用此接口使用 GPT-2 模型生成文本,如下所示 🤯. + + + +繼續閱讀以瞭解如何使用 Gradio 構建其他類型的演示! \ No newline at end of file diff --git a/chapters/zh-TW/chapter9/3.mdx b/chapters/zh-TW/chapter9/3.mdx new file mode 100644 index 000000000..b05f6a556 --- /dev/null +++ b/chapters/zh-TW/chapter9/3.mdx @@ -0,0 +1,167 @@ +# 瞭解接口類 + + + +在本節中,我們將仔細研究 `Interface` 類,並瞭解用於創建其的主要參數。 + +## 如何創建接口 + +您會注意到 `Interface` 類有 3 個必需參數: + +`Interface(fn, inputs, outputs, ...)` + +這些參數是: + + - `fn`: 由 Gradio 接口包裝的預測函數。 該函數可以接受一個或多個參數並返回一個或多個值 + - `inputs`: 輸入組件類型。 Gradio 提供了許多預構建的組件,例如`"image"` 或`"mic"`。 + - `outputs`: 輸出組件類型。 同樣,Gradio 提供了許多預構建的組件,例如 `“圖像”`或“標籤”`。 + +有關組件的完整列表,[請參閱 Gradio 文檔](https://gradio.app/docs)。 每個預構建的組件都可以通過實例化該組件對應的類來定製。 + +例如,正如我們在 [上一節](/course/chapter9/2) 中看到的,您可以傳入一個 `Textbox(lines=7, label="Prompt")` 組件來創建一個包含 7 行和一個標籤的文本框,而不是將 `"textbox"` 傳遞給 `inputs` 參數。 +讓我們看另一個例子,這次是一個 `Audio` 組件。 + +## 一個帶音頻的簡單示例 + +如前所述,Gradio 提供了許多不同的輸入和輸出。 +因此,讓我們構建一個適用於音頻的“接口”。 + +在這個例子中,我們將構建一個音頻到音頻的函數,它需要一個音頻文件並簡單地反轉它。 + +我們將使用 `Audio` 組件作為輸入。 使用 `Audio` 組件時,您可以指定希望音頻的 `source` 是用戶上傳的文件還是用戶錄製聲音的麥克風。 在這種情況下,讓我們將其設置為“麥克風”。 只是為了好玩,我們會在我們的“音頻”中添加一個標籤,上面寫著“在這裡說話……”。 + +此外,我們希望將音頻作為 numpy 數組接收,以便我們可以輕鬆地“反轉”它。 所以我們將 `"type"` 設置為 `"numpy"`,它會傳遞輸入data 作為 (`sample_rate`, `data`) 的元組進入我們的函數。 + +我們還將使用 `Audio` 輸出組件,它可以自動將具有采樣率和 numpy 數據數組的元組渲染為可播放的音頻文件。 在這種情況下,我們不需要進行任何自定義,因此我們將使用字符串快捷方式“audio”。 + + +```py +import numpy as np +import gradio as gr + + +def reverse_audio(audio): + sr, data = audio + reversed_audio = (sr, np.flipud(data)) + return reversed_audio + + +mic = gr.Audio(source="microphone", type="numpy", label="Speak here...") +gr.Interface(reverse_audio, mic, "audio").launch() +``` + +上面的代碼會產生一個類似下面的界面(如果你的瀏覽器沒有 +詢問您的麥克風權限, open the demo in a separate tab.) + + + +您現在應該能夠錄製自己的聲音並聽到自己在反向說話 - 聽起來好怪👻! + +## 處理多個輸入和輸出 + +假設我們有一個更復雜的函數,有多個輸入和輸出。在下面的示例中,我們有一個接受下拉索引、滑塊值和數字的函數,並返回一個音調的音頻樣本。 + +看看我們如何傳遞輸入和輸出組件列表,看看你能不能跟上正在發生的事情。 + +這裡的關鍵是當你通過時: +* 輸入組件列表,每個組件依次對應一個參數。 +* 輸出組件列表,每個組件對應一個返回值。 + +下面的代碼片段顯示了三個輸入組件如何與 `generate_tone()` 函數的三個參數對齊: + +```py +import numpy as np +import gradio as gr + +notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] + + +def generate_tone(note, octave, duration): + sr = 48000 + a4_freq, tones_from_a4 = 440, 12 * (octave - 4) + (note - 9) + frequency = a4_freq * 2 ** (tones_from_a4 / 12) + duration = int(duration) + audio = np.linspace(0, duration, duration * sr) + audio = (20000 * np.sin(audio * (2 * np.pi * frequency))).astype(np.int16) + return (sr, audio) + + +gr.Interface( + generate_tone, + [ + gr.Dropdown(notes, type="index"), + gr.Slider(minimum=4, maximum=6, step=1), + gr.Textbox(type="number", value=1, label="Duration in seconds"), + ], + "audio", +).launch() +``` + + + + +### `launch()` 方法 + +到目前為止,我們已經使用了`launch()`方法來啟動界面,但是我們 +還沒有真正討論過它的作用。 + +默認情況下,`launch()` 方法將在 Web 服務器中啟動演示正在本地運行。 如果您在 Jupyter 或 Colab 筆記本中運行代碼,那麼Gradio 會將演示 GUI 嵌入到筆記本中,以便您輕鬆使用它。 + +您可以通過不同的參數自定義 `launch()` 的行為: + + - `inline` - whether to display the interface inline on Python notebooks. + - `inbrowser` - whether to automatically launch the interface in a new tab on the default browser. + - `share` - whether to create a publicly shareable link from your computer for the interface. Kind of like a Google Drive link! + +我們將在下一節中更詳細地介紹 `share` 參數! + +## ✏️ 讓我們應用它! + +讓我們構建一個界面,讓您演示 **speech-recognition** 模型。 +為了讓它變得有趣,我們將接受 *or* 麥克風輸入或上傳的文件。 + +像往常一樣,我們將使用 🤗 Transformers 中的 `pipeline()` 函數加載我們的語音識別模型。如果您需要快速複習,您可以返回 [第 1 章中的那個部分](/course/chapter1/3)。 接下來,我們將實現一個 `transcribe_audio()` 函數來處理音頻並返回轉錄。 最後,我們將把這個函數包裝在一個 `Interface` 中,其中 `Audio` 組件用於輸入,只有文本用於輸出。 總而言之,此應用程序的代碼如下: + +```py +from transformers import pipeline +import gradio as gr + +model = pipeline("automatic-speech-recognition") + + +def transcribe_audio(mic=None, file=None): + if mic is not None: + audio = mic + elif file is not None: + audio = file + else: + return "You must either provide a mic recording or a file" + transcription = model(audio)["text"] + return transcription + + +gr.Interface( + fn=transcribe_audio, + inputs=[ + gr.Audio(source="microphone", type="filepath", optional=True), + gr.Audio(source="upload", type="filepath", optional=True), + ], + outputs="text", +).launch() +``` + +如果您的瀏覽器沒有要求您提供麥克風權限,open the demo in a separate tab. + + + + +就是這樣! 您現在可以使用此界面來轉錄音頻。 注意這裡 +通過將 `optional` 參數作為 `True` 傳遞,我們允許用戶 +提供麥克風或音頻文件(或兩者都不提供,但這會返回錯誤消息)。 + +繼續看看如何與他人分享您的界面! \ No newline at end of file diff --git a/chapters/zh-TW/chapter9/4.mdx b/chapters/zh-TW/chapter9/4.mdx new file mode 100644 index 000000000..62a05c6f9 --- /dev/null +++ b/chapters/zh-TW/chapter9/4.mdx @@ -0,0 +1,144 @@ +# 與他人分享演示 + + + +現在您已經構建了一個演示,您可能希望與其他人分享它。 梯度演示 +可以通過兩種方式共享:使用 ***temporary share link*** 或 ***permanent hosting on Spaces***。 + +我們將很快介紹這兩種方法。 但在分享演示之前,您可能需要完善它 💅. + +### 打磨你的 Gradio 演示: + +
+Overview of a gradio interface + +
+ +為了給你的演示添加額外的內容,`Interface` 類支持一些可選參數: + - `title`:你可以給你的演示一個標題,它出現在輸入和輸出組件的上方。 + - `description`:您可以為界面提供描述(文本、Markdown 或 HTML),顯示在輸入和輸出組件的上方和標題下方。 + - `article`:您還可以編寫擴展文章(文本、Markdown 或 HTML)來解釋界面。如果提供,它會出現在輸入和輸出組件的_下方。 + - `theme`:不喜歡默認顏色?將主題設置為使用 `default`、`huggingface`、`grass`、`peach` 之一。您還可以添加 `dark-` 前綴,例如`dark-peach` 用於深色主題(或者只是 `dark` 用於默認的深色主題)。 + - `examples`:為了讓您的演示*更易於使用*,您可以為函數提供一些示例輸入。它們出現在 UI 組件下方,可用於填充界面。這些應該作為嵌套列表提供,其中外部列表​​由樣本組成,每個內部列表對應於每個輸入組件的輸入組成。 + - `live`:如果你想讓你的演示“活”,這意味著你的模型每次輸入更改時都會重新運行,你可以設置 `live=True`。這對使用快速模型很有意義(我們將在本節末尾看到一個示例) +使用上面的選項,我們最終得到了一個更完整的界面。 運行下面的代碼,以便與 Rick and Morty 聊天: + +```py +title = "Ask Rick a Question" +description = """ +The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything! + +""" + +article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of." + +gr.Interface( + fn=predict, + inputs="textbox", + outputs="text", + title=title, + description=description, + article=article, + examples=[["What are you doing?"], ["Where should we time travel to?"]], +).launch() +``` + +使用上面的選項,我們最終得到了一個更完整的界面。 試試下面的界面: + + + +### 使用臨時鏈接分享您的演示 +現在我們已經有了機器學習模型的工作演示,讓我們學習如何輕鬆共享指向我們界面的鏈接。 +通過在 `launch()` 方法中設置 `share=True` 可以輕鬆地公開共享接口: + +```python +gr.Interface(classify_image, "image", "label").launch(share=True) +``` + +這會生成一個公開的、可共享的鏈接,您可以將其發送給任何人! 當您發送此鏈接時,另一方的用戶可以在瀏覽器中試用該模型長達 72 小時。 因為處理發生在您的設備上(只要您的設備保持開啟!),您不必擔心打包任何依賴項。 如果您使用 Google Colab 筆記本工作,則始終會自動創建共享鏈接。 它通常看起來像這樣:**XXXXX.gradio.app**。 雖然鏈接是通過 Gradio 鏈接提供的,但我們只是您本地服務器的代理,不會存儲通過接口發送的任何數據。 + +但是請記住,這些鏈接是可公開訪問的,這意味著任何人都可以使用您的模型進行預測! 因此,請確保不要通過您編寫的函數公開任何敏感信息,或允許在您的設備上發生任何關鍵更改。 如果設置 `share=False`(默認值),則僅創建本地鏈接。 + +### 在 Hugging Face Spaces 上託管您的演示 + +可以傳遞給同事的共享鏈接很酷,但是如何永久託管您的演示並讓它存在於互聯網上自己的“空間”中? + +Hugging Face Spaces 提供了在互聯網上永久託管 Gradio 模型的基礎設施,**免費**! Spaces 允許您創建並推送到(公共或私人)存儲庫, +你的 Gradio 在哪裡 +接口代碼將存在於 `app.py` 文件中。 [閱讀分步教程](https://huggingface.co/blog/gradio-spaces) 開始使用,或觀看下面的示例視頻。 + + + +## ✏️ 讓我們應用它! + +使用到目前為止我們在各節中學到的知識,讓我們創建我們在[本章第一節](/course/chapter9/1)中看到的草圖識別演示。 讓我們為我們的界面添加一些自定義並設置 `share=True` 以創建一個我們可以傳遞的公共鏈接。 + +我們可以從 [class_names.txt](https://huggingface.co/spaces/dawood/Sketch-Recognition/blob/main/class_names.txt) 加載標籤,並從 [pytorch_model.bin](https://huggingface.co/spaces/dawood/Sketch-Recognition/blob/main/pytorch_model.bin)加載預訓練的 pytorch 模型 。 通過點擊鏈接並單擊文件預覽左上角的下載來下載這些文件。 讓我們看看下面的代碼,看看我們如何使用這些文件來加載我們的模型並創建一個`predict()`函數: +```py +from pathlib import Path +import torch +import gradio as gr +from torch import nn + +LABELS = Path("class_names.txt").read_text().splitlines() + +model = nn.Sequential( + nn.Conv2d(1, 32, 3, padding="same"), + nn.ReLU(), + nn.MaxPool2d(2), + nn.Conv2d(32, 64, 3, padding="same"), + nn.ReLU(), + nn.MaxPool2d(2), + nn.Conv2d(64, 128, 3, padding="same"), + nn.ReLU(), + nn.MaxPool2d(2), + nn.Flatten(), + nn.Linear(1152, 256), + nn.ReLU(), + nn.Linear(256, len(LABELS)), +) +state_dict = torch.load("pytorch_model.bin", map_location="cpu") +model.load_state_dict(state_dict, strict=False) +model.eval() + + +def predict(im): + x = torch.tensor(im, dtype=torch.float32).unsqueeze(0).unsqueeze(0) / 255.0 + with torch.no_grad(): + out = model(x) + probabilities = torch.nn.functional.softmax(out[0], dim=0) + values, indices = torch.topk(probabilities, 5) + return {LABELS[i]: v.item() for i, v in zip(indices, values)} +``` + +現在我們有了一個`predict()`函數。 下一步是定義並啟動我們的漸變界面: + +```py +interface = gr.Interface( + predict, + inputs="sketchpad", + outputs="label", + theme="huggingface", + title="Sketch Recognition", + description="Who wants to play Pictionary? Draw a common object like a shovel or a laptop, and the algorithm will guess in real time!", + article="

Sketch Recognition | Demo Model

", + live=True, +) +interface.launch(share=True) +``` + + + + +注意 `Interface` 中的 `live=True` 參數,這意味著草圖演示使 +每次有人在畫板上畫畫時的預測(沒有提交按鈕!)。 + +此外,我們還在 `launch()` 方法中設置了 `share=True` 參數。 +這將創建一個公共鏈接,您可以發送給任何人! 當您發送此鏈接時,對方的用戶可以嘗試草圖識別模型。 重申一下,您還可以在 Hugging Face Spaces 上託管模型,這就是我們能夠嵌入上面的演示的方式。 + +接下來,我們將介紹 Gradio 可用於 Hugging Face 生態系統的其他方式! \ No newline at end of file diff --git a/chapters/zh-TW/chapter9/5.mdx b/chapters/zh-TW/chapter9/5.mdx new file mode 100644 index 000000000..af733d52f --- /dev/null +++ b/chapters/zh-TW/chapter9/5.mdx @@ -0,0 +1,66 @@ +# 與 Hugging Face Hub 整合 + + + +為了讓你的生活更輕鬆, Gradio 直接與 Hugging Face Hub 和 Hugging Face Spaces 集成。你可以僅使用 *一行代碼* 從中心和空間加載演示。 + +### 從 Hugging Face Hub 加載模型 +首先, 從 Hugging Face 通過 Hub 提供的數千個模型中選擇一個, 如 [第四章](/course/chapter4/2) 中所述。 + +使用特殊的 `Interface.load()` 方法, 你可以傳遞 `"model/"` (或者, 等效的, `"huggingface/"`) 後面是模型名稱。例如, 這裡是為大型語言模型 [GPT-J](https://huggingface.co/EleutherAI/gpt-j-6B)構建演示的代碼, 添加幾個示例輸入: + +```py +import gradio as gr + +title = "GPT-J-6B" +description = "Gradio Demo for GPT-J 6B, a transformer model trained using Ben Wang's Mesh Transformer JAX. 'GPT-J' refers to the class of model, while '6B' represents the number of trainable parameters. To use it, simply add your text, or click one of the examples to load them. Read more at the links below." +article = "

GPT-J-6B: A 6 Billion Parameter Autoregressive Language Model

" +examples = [ + ["The tower is 324 metres (1,063 ft) tall,"], + ["The Moon's orbit around Earth has"], + ["The smooth Borealis basin in the Northern Hemisphere covers 40%"], +] +gr.Interface.load( + "huggingface/EleutherAI/gpt-j-6B", + inputs=gr.Textbox(lines=5, label="Input Text"), + title=title, + description=description, + article=article, + examples=examples, + enable_queue=True, +).launch() +``` + +上述代碼將生成以下界面: + + + +以這種方式加載模型使用 Hugging Face 的 [Inference API](https://huggingface.co/inference-api),而不是將模型加載到內存中。這對於像 GPT-J 或 T0pp這樣需要大量 RAM 的大型模型是理想的。 + +### 從 Hugging Face Spaces 空間加載 +要從hugs Face Hub加載任何空間並在本地重新創建它, 你可以將 `spaces/` 傳遞給 `Interface`, 再加上空間的名稱。 + +還記得第 1 節中刪除圖像背景的演示嗎? 讓我們從 Hugging Face Spaces 加載它: + +```py +gr.Interface.load("spaces/abidlabs/remove-bg").launch() +``` + + + +從Hub或Spaces加載演示的一個很酷的地方是, 你可以通過覆蓋任何參數來自定義它們。在這裡, 我們添加一個標題並讓它與網絡攝像頭一起使用: + +```py +gr.Interface.load( + "spaces/abidlabs/remove-bg", inputs="webcam", title="Remove your webcam background!" +).launch() +``` + + + +現在我們已經探索了幾種將Gradio與hugs Face Hub集成的方法, 讓我們來看看 `Interface` 類的一些高級功能。這就是下一節的主題! \ No newline at end of file diff --git a/chapters/zh-TW/chapter9/6.mdx b/chapters/zh-TW/chapter9/6.mdx new file mode 100644 index 000000000..77ad805e8 --- /dev/null +++ b/chapters/zh-TW/chapter9/6.mdx @@ -0,0 +1,97 @@ +# 高級接口功能 + + + +現在我們可以構建和共享一個基本接口, 讓我們來探索一些更高級的特性, 如狀態和解釋。 + +### 使用狀態保存數據 + +Gradio 支持 *會話狀態*, 其中數據在頁面加載中的多個提交中持續存在。會話狀態對於構建演示很有用, 例如, 你希望在用戶與模型交互時保留數據的聊天機器人。請注意, 會話狀態不會在模型的不同用戶之間共享數據。 + +要將數據存儲在會話狀態中, 你需要做三件事: + +1. 向函數中傳遞一個 *額外的參數* , 該參數表示接口的狀態。 +1. 在函數結束時, 將狀態的更新值作為 *額外的返回值* 返回。 +1. 在創建`接口`時添加 'state' 輸入和 'state' 輸出組件。 + +請參閱下面的聊天機器人示例: + +```py +import random + +import gradio as gr + + +def chat(message, history): + history = history or [] + if message.startswith("How many"): + response = random.randint(1, 10) + elif message.startswith("How"): + response = random.choice(["Great", "Good", "Okay", "Bad"]) + elif message.startswith("Where"): + response = random.choice(["Here", "There", "Somewhere"]) + else: + response = "I don't know" + history.append((message, response)) + return history, history + + +iface = gr.Interface( + chat, + ["text", "state"], + ["chatbot", "state"], + allow_screenshot=False, + allow_flagging="never", +) +iface.launch() +``` + + + +請注意輸出組件的狀態如何在提交之間保持不變。注意: 可以給 state 參數傳入一個默認值, 作為 state 的初始值。 + +### 通過解釋來理解預測 + +大多數機器學習模型都是黑盒子, 函數的內部邏輯對終端用戶是隱藏的。為了提高透明度, 我們通過簡單地將 Interface 類中的解釋關鍵字設置為默認值, 使向模型添加解釋變得非常容易。這允許你的用戶理解輸入的哪些部分負責輸出。看看下面這個簡單的接口, 它顯示了一個還包括解釋的圖像分類器: + +```py +import requests +import tensorflow as tf + +import gradio as gr + +inception_net = tf.keras.applications.MobileNetV2() # load the model + +# Download human-readable labels for ImageNet. +response = requests.get("https://git.io/JJkYN") +labels = response.text.split("\n") + + +def classify_image(inp): + inp = inp.reshape((-1, 224, 224, 3)) + inp = tf.keras.applications.mobilenet_v2.preprocess_input(inp) + prediction = inception_net.predict(inp).flatten() + return {labels[i]: float(prediction[i]) for i in range(1000)} + + +image = gr.Image(shape=(224, 224)) +label = gr.Label(num_top_classes=3) + +title = "Gradio Image Classifiction + Interpretation Example" +gr.Interface( + fn=classify_image, inputs=image, outputs=label, interpretation="default", title=title +).launch() +``` + +通過提交一個輸入, 然後單擊輸出組件下的Interpret來測試解釋功能。 + + + +除了Gradio提供的默認解釋方法之外, 你還可以為 `interpretation` 參數指定 `shap`, 並設置 `num_shap` 參數。這使用基於 Shapley 的解釋, 你可以在 [here](https://christophm.github.io/interpretable-ml-book/shap.html) 閱讀更多信息。最後, 還可以將自己的解釋函數傳入 `interpretation` 參數。在Gradio的入門頁面 [here](https://gradio.app/getting_started/) 中可以看到一個例子。 + +這結束了我們對Gradio的`Interface`類的深入研究。正如我們所看到的, 這個類使用幾行Python代碼創建機器學習演示變得簡單。然而, 有時你會想通過改變佈局或鏈接多個預測函數來定製你的demo。如果我們能以某種方式將 `接口` 分成可定製的 "塊", 那不是很好嗎? 幸運的是, 有! 這是最後一部分的主題。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter9/7.mdx b/chapters/zh-TW/chapter9/7.mdx new file mode 100644 index 000000000..9cf1dc7cd --- /dev/null +++ b/chapters/zh-TW/chapter9/7.mdx @@ -0,0 +1,236 @@ +# Gradio 塊簡介 + + + +在前面的部分中, 我們已經使用 `Interface` 類探索並創建了演示。在本節中, 我們將介紹我們 **新開發**的稱為`gradio.Blocks`低級API。 + +現在, `接口`和`塊`之間有什麼區別? + +- ⚡ `接口`: 一個高級 API, 讓你只需提供輸入和輸出列表即可創建完整的機器學習演示。 + +- 🧱 `塊`: :一個低級的 API, 它允許你完全控制你的應用程序的數據流和佈局。您可以使用`塊`(如 "構建塊")構建非常複雜的多步驟應用程序。 + + +### 為什麼要塊 🧱? + +正如我們在前幾節中看到的, `Interface` 類允許你使用幾行代碼輕鬆創建成熟的機器學習demo。`Interface` API 非常易於使用, 但缺乏 `Blocks` API 提供的靈活性。例如, 你可能想要: + +- 將相關演示組合為一個web應用程序中的多個選項卡 +- 更改demo的佈局, 例如指定輸入和輸出的位置 +- 具有多步驟接口, 其中一個模型的輸出成為下一個模型的輸入, 或者通常具有更靈活的數據流 +- 根據用戶輸入更改組件的屬性 (例如, 下拉列表中的選項) 或其可見性 + +我們將在下面探討所有這些概念。 + +### 使用塊創建簡單demo + +安裝 Gradio 後, 將以下代碼作為 Python 腳本、Jupyter 筆記本或 Colab 筆記本運行。 + +```py +import gradio as gr + + +def flip_text(x): + return x[::-1] + + +demo = gr.Blocks() + +with demo: + gr.Markdown( + """ + # Flip Text! + Start typing below to see the output. + """ + ) + input = gr.Textbox(placeholder="Flip this text") + output = gr.Textbox() + + input.change(fn=flip_text, inputs=input, outputs=output) + +demo.launch() +``` + + + +上述簡單示例介紹了塊的4個基本概念: + +1. 塊允許你允許你構建結合markdown、HTML、按鈕和交互組件的web應用程序, 只需在一個帶有gradio的Python中實例化對象。 + +🙋如果你不熟悉 Python 中的 `with` 語句, 我們建議你查看來自 Real Python 的極好的[教程](https://realpython.com/python-with-statement/)。看完後回到這裡 🤗 + +實例化組件的順序很重要, 因為每個元素都按照創建的順序呈現到 Web 應用程序中。(更復雜的佈局在下面討論) + +2. 你可以在代碼中的任何位置定義常規 Python 函數, 並使用`塊`在用戶輸入的情況下運行它們。在我們的示例中, 們有一個"翻轉"輸入文本的簡單函數, 但你可以編寫任何 Python 函數, 從簡單的計算到處理機器學習模型的預測。 + +3. 你可以將事件指定給任何`塊`組件。這將在組件被單擊、更改等情況下運行函數。當你分配一個事件時, 你傳入三個參數: `fn`: 應該被調用的函數, `inputs`: 輸入組件的(列表), 以及 `outputs`: 應該被調用的輸出組件的(列表)。 + + 在上面的示例中, 當名為 `input` 的 `Textbox` 中的值發生變化時, 我們運行 `flip_text()` 函數。該事件讀取`輸入`中的值, 將其作為名稱參數傳遞給 `flip_text()`, 然後它返回一個值, 該值被分配給我們的第二個名為 `output` 的 `Textbox`。 + + 要查看每個組件支持的事件列表, 請參閱 Gradio [文檔](https://www.gradio.app/docs/)。 + +4. 塊會根據你定義的事件觸發器自動確定組件是否應該是交互式的 (接受用戶輸入)。在我們的示例中, 第一個文本框是交互式的, 因為它的值由 `flip_text()` 函數使用。第二個文本框不是交互式的, 因為它的值從不用作輸入。在某些情況下, 你可能想要覆蓋它, 你可以通過傳遞一個布爾值給組件的`交互`參數(例如 `gr.Textbox(placeholder="Flip this text", interactive=True)`)。 + +### 自定義演示的佈局 + +我們如何使用`塊`來定製我們的演示的佈局? 默認情況下, `塊`在一列中垂直呈現創建的組件。你可以通過使用 `with gradio.Column():` 創建其他列或使用 `with gradio.Row():` 創建其他行並在這些上下文中創建組件來改變這一點。 + +你應該記住: 在 `列` 下創建的任何組件(這也是默認設置) 都將垂直佈局。在 `Row` 下創建的任何組件都將水平佈局, 類似於 [Web 開發中的 flexbox 模型](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox)。 + +最後, 你還可以使用 `with gradio.Tabs()` 上下文管理器為您的demo創建選項卡。在此上下文中, 您可以通過使用 `gradio.TabItem(name_of_tab):` 指定來創建多個選項卡。在 `gradio.TabItem(name_of_tab):` 中創建的任何組件都會出現在該選項卡中。 + +現在讓我們在demo中添加一個 `flip_image()`函數並添加一個翻轉圖像的新選項卡。下面是一個帶有 2 個選項卡的示例, 也使用了一個行: + +```py +import numpy as np +import gradio as gr + +demo = gr.Blocks() + + +def flip_text(x): + return x[::-1] + + +def flip_image(x): + return np.fliplr(x) + + +with demo: + gr.Markdown("Flip text or image files using this demo.") + with gr.Tabs(): + with gr.TabItem("Flip Text"): + with gr.Row(): + text_input = gr.Textbox() + text_output = gr.Textbox() + text_button = gr.Button("Flip") + with gr.TabItem("Flip Image"): + with gr.Row(): + image_input = gr.Image() + image_output = gr.Image() + image_button = gr.Button("Flip") + + text_button.click(flip_text, inputs=text_input, outputs=text_output) + image_button.click(flip_image, inputs=image_input, outputs=image_output) + +demo.launch() +``` + + + + +你會注意到, 在這個示例中, 我們還在每個選項卡中創建了一個 `Button` 組件, 並且我們為每個按鈕分配了一個點擊事件,這是實際運行該函數的事件。 + +### 探索事件和狀態 + +正如你可以控制佈局一樣, `塊` 可以讓你對觸發函數調用的事件進行細粒度控制。每個組件和許多佈局都有它們支持的特定事件。 + +例如, `Textbox` 組件有兩個事件: `change()` (當文本框內的值發生變化時), 和 `submit()` (當用戶在關注文本框時按下enter鍵)。更復雜的組件可以有更多的事件: 例如,`Audio`組件也有單獨的事件, 用於播放、清除、暫停音頻文件等。請參閱文檔瞭解每個組件支持的事件。 + +你可以將事件觸發器附加到這些事件中的一個、一個或多個。你可以通過在組件實例中調用事件名稱作為函數來創建一個事件觸發器 -- 例如 `textbox.change(...)` 或 `btn.click(...)`。如前所述, 該函數接受三個參數: + +- `fn`: 要運行的函數 +- `inputs`: 組件的(列表), 其值應作為函數的輸入參數提供。每個組件的值按順序映射到相應的函數參數。如果函數不帶任何參數, 則此參數可以為 None。 +- `outputs`: 應根據函數返回的值更新其值的組件(列表)。每個返回值按順序設置相應組件的值。如果函數不返回任何內容, 則此參數可以為None。 + +你甚至可以使輸入和輸出組件成為同一個組件, 就像我們在這個使用 GPT 模型進行文本補全的示例中所做的那樣: + +```py +import gradio as gr + +api = gr.Interface.load("huggingface/EleutherAI/gpt-j-6B") + + +def complete_with_gpt(text): + # Use the last 50 characters of the text as context + return text[:-50] + api(text[-50:]) + + +with gr.Blocks() as demo: + textbox = gr.Textbox(placeholder="Type here and press enter...", lines=4) + btn = gr.Button("Generate") + + btn.click(complete_with_gpt, textbox, textbox) + +demo.launch() +``` + + + +### 創建多步demo + +在某些情況下, 您可能需要一個 _多步驟的demo_, 其中重用一個函數的輸出作為下一個函數的輸入。使用 `塊` 很容易做到這一點, 因為你可以使用組件作為一個事件觸發器的輸入, 但作為另一個事件觸發器的輸出。看看下面示例中的文本組件, 它的值是語音到文本模型的結果, 但也被傳遞到情感分析模型: + +```py +from transformers import pipeline + +import gradio as gr + +asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h") +classifier = pipeline("text-classification") + + +def speech_to_text(speech): + text = asr(speech)["text"] + return text + + +def text_to_sentiment(text): + return classifier(text)[0]["label"] + + +demo = gr.Blocks() + +with demo: + audio_file = gr.Audio(type="filepath") + text = gr.Textbox() + label = gr.Label() + + b1 = gr.Button("Recognize Speech") + b2 = gr.Button("Classify Sentiment") + + b1.click(speech_to_text, inputs=audio_file, outputs=text) + b2.click(text_to_sentiment, inputs=text, outputs=label) + +demo.launch() +``` + + + +### 更新組件屬性 + +到目前為止, 我們已經瞭解瞭如何創建事件來更新另一個組件的值。但是, 如果您想更改組件的其他屬性, 例如文本框的可見性或單選按鈕組中的選項, 會發生什麼? 您可以通過返回組件類的 `update()` 方法而不是函數的常規返回值來做到這一點。 + +這很容易用一個例子來說明: + +```py +import gradio as gr + + +def change_textbox(choice): + if choice == "short": + return gr.Textbox.update(lines=2, visible=True) + elif choice == "long": + return gr.Textbox.update(lines=8, visible=True) + else: + return gr.Textbox.update(visible=False) + + +with gr.Blocks() as block: + radio = gr.Radio( + ["short", "long", "none"], label="What kind of essay would you like to write?" + ) + text = gr.Textbox(lines=2, interactive=True) + + radio.change(fn=change_textbox, inputs=radio, outputs=text) + block.launch() +``` + + + +我們剛剛探索了`塊`的所有核心概念! 就像 `參數一樣`, 你可以創建很酷的demo, 可以通過在`launch()`方法中使用`share=True`來共享, 或者部署在[Hugging Face Spaces](https://huggingface.co/spaces)上。 \ No newline at end of file diff --git a/chapters/zh-TW/chapter9/8.mdx b/chapters/zh-TW/chapter9/8.mdx new file mode 100644 index 000000000..86c4dc09b --- /dev/null +++ b/chapters/zh-TW/chapter9/8.mdx @@ -0,0 +1,19 @@ +# Gradio,回顧! + +關於使用 Gradio 構建酷炫的 ML 演示的章節到此結束 - 我們希望您喜歡它!回顧一下,在本章中,我們學習了: + +- 如何使用高級 `Interface` API 創建 Gradio 演示,以及如何配置不同的輸入和輸出模式。 +- 通過臨時鏈接和託管在 [Hugging Face Spaces](https://huggingface.co/spaces) 上共享 Gradio 演示的不同方式。 +- 如何將 Gradio 演示與 Hugging Face Hub 上的Model和Space集成在一起。 +- 高級功能,例如在演示中存儲狀態或提供身份驗證。 +- 如何使用 Gradio Blocks 完全控制演示的數據流和佈局。 + +如果您想測試您對本章所涵蓋概念的理解,請查看下一節中的測驗! + +## 下一步去哪裡? + +如果您想了解有關 Gradio 的更多信息,您可以 + +- 看看 repo 中的 [Demos](https://github.com/gradio-app/gradio/tree/main/demo),那裡有很多例子。 +- 請參閱 [指南](https://gradio.app/guides/) 頁面,您可以在其中找到有關酷炫和高級功能的指南。 +- 查看 [文檔](https://gradio.app/docs/) 頁面瞭解詳情。 diff --git a/chapters/zh-TW/chapter9/9.mdx b/chapters/zh-TW/chapter9/9.mdx new file mode 100644 index 000000000..730e6e334 --- /dev/null +++ b/chapters/zh-TW/chapter9/9.mdx @@ -0,0 +1,231 @@ + + + + +# 章末測驗 + + + +讓我們測試一下您在本章中學到了什麼! + +### 1.你能利用Grado做什麼? + share = True 參數,可以生成一個共享鏈接發送給任何人。", + correct: true + }, + { + text: "調試模型", + explain: "Grado演示的一個優點是能夠用真實數據測試模型,您可以實時更改並觀察模型的預測變化,從而幫助您調試模型。", + correct: true + }, + { + text: "訓練你的模型", + explain: "在你的模型被訓練之後,Grado 被設計用來進行模型推理。", + } + ]} +/> + +### 2.Grado只在 PyTorch 模型上工作 + + +### 3.你可以在哪裡發佈一個 GRadio 演示? + + +### 4.Gdio 主要是為 NLP 模型設計的 + + +### 5.下列哪些特性是由 Grado 支持的? + gr. Interface.load () 方法加載任何 Hugging Face 模型", + correct: true + } + ]} +/> + +### 6.下列哪一種是從 Hub 或 Spaces 加載 Huggging Face 模型的有效方法? + + +### 7.創建您的 Gradio 接口時,您必須添加以下步驟: + + +### 8.Gradio 庫包括以下哪些組件? + + +### 9.Gradio允許你做什麼? + + +### 10.你可以共享一個`Blocks`演示的公共鏈接,並創建一個`Blocks`的演示在HuggingFace空間。 + \ No newline at end of file diff --git a/chapters/zh-TW/events/2.mdx b/chapters/zh-TW/events/2.mdx new file mode 100644 index 000000000..cd1116004 --- /dev/null +++ b/chapters/zh-TW/events/2.mdx @@ -0,0 +1,165 @@ +# Part 2 發佈活動 + +對於課程第 2 部分的發佈,我們在微調 sprint 之前組織了一場現場活動,為期兩天的會談。 如果你錯過了,你可以趕上下面列出的講座! + +## Day 1: Transformer 的高級API以及如何訓練它們 + +**Thomas Wolf:** *遷移學習和Transformers庫的誕生* + +
+ +
+ +

+一張圖總結 Thom 的演講 +

+ +Thomas Wolf 是 Hugging Face 的聯合創始人兼首席科學官。 Thomas Wolf 和 Hugging Face 團隊創建的工具被 5,000 多個研究機構使用,包括 Facebook 人工智能研究、谷歌研究、DeepMind、亞馬遜研究、蘋果、艾倫人工智能研究所以及大多數大學系。 Thomas Wolf 是人工智能領域有史以來最大的研究合作的發起人和高級主席:[“BigScience”](https://bigscience.huggingface.co),以及一組廣泛使用的 [庫和工具](https://github.com/huggingface/)。 Thomas Wolf 還是一位多產的教育家、人工智能和自然語言處理領域的思想領袖,並且經常受邀在世界各地的會議上發表演講 [https://thomwolf.io](https://thomwolf.io )。 + +**Jay Alammar:** *Transformers模型的圖解* + +
+ +
+ +

+一張圖總結 Jay 的演講 +

+ +通過他廣受歡迎的 ML 博客,Jay 幫助數百萬研究人員和工程師直觀地理解了機器學習工具和概念,從基礎(最終出現在 NumPy、Pandas 文檔)到前沿(Transformers、BERT、GPT-3)。 + +**Margaret Mitchell:** *關於機器學習開發中的價值觀* + +
+ +
+ +

+一張圖總結 Margaret 的演講 +

+ +Margaret Mitchell 是一名從事人工智能倫理研究的研究員,目前專注於以倫理為依據的人工智能開發。她在自然語言生成、輔助技術、計算機視覺和人工智能倫理方面發表了 50 多篇論文,並在會話生成和情感分類領域擁有多項專利。她之前曾在 Google AI 擔任員工研究科學家,在那裡她創立並共同領導了 Google 的倫理 AI 小組,專注於基礎 AI 倫理研究和在 Google 內部實施 AI 倫理。在加入谷歌之前,她是微軟研究院的一名研究員,專注於計算機視覺到語言的生成;並且是約翰霍普金斯大學的博士後,專注於貝葉斯建模和信息提取。她擁有阿伯丁大學計算機科學博士學位和華盛頓大學計算語言學碩士學位。在獲得學位的同時,她還於 2005 年至 2012 年在俄勒岡健康與科學大學從事機器學習、神經系統疾病和輔助技術方面的工作。她在多樣性、包容性、計算機科學和倫理學的交叉領域領導了許多研討會和倡議。她的工作獲得了國防部長阿什卡特和美國盲人基金會的獎勵,並被多家科技公司實施。她喜歡園藝、狗和貓。 + +**Matthew Watson 和 Chen Qian:** *使用 Keras 的 NLP 工作流程* + +
+ +
+ +

+一張圖總結 Matt 和 Chen 的演講 +

+ +Matthew Watson 是 Keras 團隊的機器學習工程師,專注於高級建模 API。 他在本科期間學習計算機圖形學,並在斯坦福大學獲得碩士學位。 作為一名幾乎是英語專業的學生,他轉向計算機科學,熱衷於跨學科工作並使 NLP 為更廣泛的受眾所接受。 + +Chen Qian 是 Keras 團隊的一名軟件工程師,專注於高級建模 API。 Chen 在斯坦福大學獲得電氣工程碩士學位,他對簡化 ML 任務和大規模 ML 的代碼實現特別感興趣。 + +**Mark Saroufim:** *如何使用 Pytorch 訓練模型* + +
+ +
+ +

+一張圖總結 Mark 的演講 +

+ +Mark Saroufim 是 Pytorch 的合作伙伴工程師,致力於開發 OSS 生產工具,包括 TorchServe 和 Pytorch Enterprise。 Mark 是 Graphcore、[yuri.ai](http://yuri.ai/)、Microsoft 和 NASA 的 JPL 的應用科學家和產品經理。 他熱衷於讓編程更有趣。 + +**Jakob Uszkoreit:** *它沒有壞所以不要修復讓我們打破它* + +
+ +
+ +

+一張圖總結 Jakob 的演講 +

+ +Jakob Uszkoreit 是 Inceptive 的聯合創始人。 Inceptive 在緊密循環中使用大規模深度學習和高通量實驗設計用於疫苗和治療的 RNA 分子,目標是使基於 RNA 的藥物更容易獲得、更有效和更廣泛適用。 此前,Jakob 在谷歌工作了十多年,領導谷歌大腦、研究和搜索領域的研發團隊,致力於深度學習基礎、計算機視覺、語言理解和機器翻譯。 + +## Day 2: 可以使用的工具 + +**Lewis Tunstall:** *使用 🤗 Transformers Trainer 讓訓練更加簡單* + +
+ +
+ +Lewis 是 Hugging Face 的機器學習工程師,專注於開發開源工具並讓更廣泛的社區可以訪問它們。 他還是 O'Reilly 即將出版的有關於Transform的合著者,您可以在 Twitter (@_lewtun) 上關注他,瞭解 NLP 提示和技巧! + +**Matthew Carrigan:** *用於 🤗 Transformers 和 🤗 Datasets的新 TensorFlow 特性* + +
+ +
+ +Matt 負責Transformers的TensorFlow維護,並將最終領導一場針對現任PyTorch派系的政變,可能會通過他的推特賬戶@carrigmat進行協調。 + +**Lysandre Debut:** *使用Hugging Face Hub 作為協作和共享機器學習項目* + +
+ +
+ +

+一張圖總結 Lysandre 的演講 +

+ +Lysandre 是 Hugging Face 的機器學習工程師,他參與了許多開源項目。 他的目標是通過使用非常簡單的 API 開發強大的工具,讓每個人都可以使用機器學習。 + +**Lucile Saulnier:** *使用 🤗 Transformers 和 🤗 Tokenizers 獲取您自己的tokenizer* + +
+ +
+ +Lucile 是 Hugging Face 的機器學習工程師,負責開發和支持開源工具的使用。 她還積極參與了自然語言處理領域的許多研究項目,例如協作訓練模型和 BigScience。 + +**Sylvain Gugger:** *使用 🤗 Accelerate* 增強您的 PyTorch 訓練循環* + +
+ +
+ +Sylvain 是 Hugging Face 的研究工程師,也是🤗 Transformers 的核心維護者之一,也是🤗 Accelerate 的開發者。 他喜歡讓模型訓練變得更容易。 + +**Merve Noyan:** *使用 🤗 Spaces 展示您的模型演示* + +
+ +
+ +Merve 是 Hugging Face 的開發者倡導者,致力於開發工具並圍繞它們構建內容,以使每個人的機器學習民主化。 + +**Abubakar Abid:** *快速構建機器學習應用程序* + +
+ +
+ +

+一張圖總結 Abubakar 的演講 +

+ +Abubakar Abid 是 [Gradio](www.gradio.app) 的首席執行官。 他於 2015 年獲得麻省理工學院電氣工程和計算機科學學士學位,並於 2021 年獲得斯坦福大學應用機器學習博士學位。作為 Gradio 的首席執行官,Abubakar 致力於使機器學習模型更易於演示、調試和部署。 + +**Mathieu Desvé:** *AWS ML Vision:讓所有客戶都可以使用機器學習* + +
+ +
+ +

+一張圖總結 Mathieu 的演講 +

+ +技術愛好者,有空閒時間的創客。 我喜歡挑戰和解決客戶和用戶的問題,每天和有才華的人一起學習。 自 2004 年以來,我在前端、後端、基礎設施、運營和管理等多個職位上工作。 嘗試以敏捷的方式解決公共技術和管理問題。 + +**Philipp Schmid:** *使用 Amazon SageMaker 和🤗 Transformers 進行託管訓練* + +
+ +
+ +Philipp Schmid 是 Hugging Face 的機器學習工程師和技術主管,負責領導與 Amazon SageMaker 團隊的合作。 他熱衷於使尖端 NLP 模型民主化和生產化,並提高深度學習的易用性。 \ No newline at end of file diff --git a/subtitles/README.md b/subtitles/README.md index 002948954..833481ede 100644 --- a/subtitles/README.md +++ b/subtitles/README.md @@ -37,8 +37,8 @@ For example, in the `zh-CN` subtitles, each block has the following format: ``` 1 00:00:05,850 --> 00:00:07,713 -- 欢迎来到 Hugging Face 课程。 -- Welcome to the Hugging Face Course. +欢迎来到 Hugging Face 课程。 +Welcome to the Hugging Face Course. ``` To upload the SRT file to YouTube, we need the subtitle in monolingual format, i.e. the above block should read: @@ -46,7 +46,7 @@ To upload the SRT file to YouTube, we need the subtitle in monolingual format, i ``` 1 00:00:05,850 --> 00:00:07,713 -- 欢迎来到 Hugging Face 课程。 +欢迎来到 Hugging Face 课程。 ``` To handle this, we provide a script that converts the bilingual SRT files to monolingual ones. To perform the conversion, run: diff --git a/subtitles/en/33_the-push-to-hub-api-(pytorch).srt b/subtitles/en/33_the-push-to-hub-api-(pytorch).srt index a2fcf8caf..3c27675f3 100644 --- a/subtitles/en/33_the-push-to-hub-api-(pytorch).srt +++ b/subtitles/en/33_the-push-to-hub-api-(pytorch).srt @@ -40,7 +40,7 @@ password, then click login, 10 00:00:26,640 --> 00:00:28,620 -this will store a notification token +this will store a authentication token 11 00:00:28,620 --> 00:00:30,670 @@ -446,7 +446,7 @@ with the from_pretrained method 103 00:04:21,113 --> 00:04:22,923 -of with the pipeline function. +or with the pipeline function. 104 00:04:34,350 --> 00:04:36,780 diff --git a/subtitles/en/41_text-embeddings-&-semantic-search.srt b/subtitles/en/41_text-embeddings-&-semantic-search.srt index 51c9d9b29..5fc6dc369 100644 --- a/subtitles/en/41_text-embeddings-&-semantic-search.srt +++ b/subtitles/en/41_text-embeddings-&-semantic-search.srt @@ -194,12 +194,12 @@ average the token embeddings 44 00:01:49,650 --> 00:01:52,500 -which is called mean pooling +which is called mean_pooling and this is what we do here. 45 00:01:53,370 --> 00:01:55,800 -With mean pooling the only +With mean_pooling the only thing we need to make sure 46 @@ -210,7 +210,7 @@ padding tokens in the average, 47 00:01:58,410 --> 00:02:01,860 which is why you can see the -attention mask being used here. +attention_mask being used here. 48 00:02:01,860 --> 00:02:05,100 @@ -313,7 +313,7 @@ we take a small sample 70 00:02:56,070 --> 00:02:57,780 -from the SQUAD dataset and apply +from the squad dataset and apply 71 00:02:57,780 --> 00:03:00,180 diff --git a/subtitles/en/metadata_tasks.csv b/subtitles/en/metadata_tasks.csv new file mode 100644 index 000000000..2af9c9a82 --- /dev/null +++ b/subtitles/en/metadata_tasks.csv @@ -0,0 +1,7 @@ +id,title,link,srt_filename +wVHdVlPScxA,🤗 Tasks: Token Classification,https://www.youtube.com/watch?v=wVHdVlPScxA&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=1,subtitles/en/tasks_00_🤗-tasks-token-classification.srt +ajPx5LwJD-I,🤗 Tasks: Question Answering,https://www.youtube.com/watch?v=ajPx5LwJD-I&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=2,subtitles/en/tasks_01_🤗-tasks-question-answering.srt +Vpjb1lu0MDk,🤗 Tasks: Causal Language Modeling,https://www.youtube.com/watch?v=Vpjb1lu0MDk&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=3,subtitles/en/tasks_02_🤗-tasks-causal-language-modeling.srt +mqElG5QJWUg,🤗 Tasks: Masked Language Modeling,https://www.youtube.com/watch?v=mqElG5QJWUg&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=4,subtitles/en/tasks_03_🤗-tasks-masked-language-modeling.srt +yHnr5Dk2zCI,🤗 Tasks: Summarization,https://www.youtube.com/watch?v=yHnr5Dk2zCI&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=5,subtitles/en/tasks_04_🤗-tasks-summarization.srt +1JvfrvZgi6c,🤗 Tasks: Translation,https://www.youtube.com/watch?v=1JvfrvZgi6c&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=6,subtitles/en/tasks_05_🤗-tasks-translation.srt diff --git a/subtitles/en/raw/tasks.md b/subtitles/en/raw/tasks.md new file mode 100644 index 000000000..a95d2429a --- /dev/null +++ b/subtitles/en/raw/tasks.md @@ -0,0 +1,77 @@ +Note: the following transcripts are associated with Merve Noyan's videos in the Hugging Face Tasks playlist: https://www.youtube.com/playlist?list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf + +Token Classification video + +Welcome to the Hugging Face tasks series! In this video we’ll take a look at the token classification task. +Token classification is the task of assigning a label to each token in a sentence. There are various token classification tasks and the most common are Named Entity Recognition and Part-of-Speech Tagging. +Let’s take a quick look at the Named Entity Recognition task. The goal of this task is to find the entities in a piece of text, such as person, location, or organization. This task is formulated as labelling each token with one class for each entity, and another class for tokens that have no entity. +Another token classification task is part-of-speech tagging. The goal of this task is to label the words for a particular part of a speech, such as noun, pronoun, adjective, verb and so on. This task is formulated as labelling each token with parts of speech. +Token classification models are evaluated on Accuracy, Recall, Precision and F1-Score. The metrics are calculated for each of the classes. We calculate true positive, true negative and false positives to calculate precision and recall, and take their harmonic mean to get F1-Score. Then we calculate it for every class and take the overall average to evaluate our model. +An example dataset used for this task is ConLL2003. Here, each token belongs to a certain named entity class, denoted as the indices of the list containing the labels. +You can extract important information from invoices using named entity recognition models, such as date, organization name or address. +For more information about the Token classification task, check out the Hugging Face course. + + +Question Answering video + +Welcome to the Hugging Face tasks series. In this video, we will take a look at the Question Answering task. +Question answering is the task of extracting an answer in a given document. +Question answering models take a context, which is the document you want to search in, and a question and return an answer. Note that the answer is not generated, but extracted from the context. This type of question answering is called extractive. +The task is evaluated on two metrics, exact match and F1-Score. +As the name implies, exact match looks for an exact match between the predicted answer and the correct answer. +A common metric used is the F1-Score, which is calculated over tokens that are predicted correctly and incorrectly. It is calculated over the average of two metrics called precision and recall which are metrics that are used widely in classification problems. +An example dataset used for this task is called SQuAD. This dataset contains contexts, questions and the answers that are obtained from English Wikipedia articles. +You can use question answering models to automatically answer the questions asked by your customers. You simply need a document containing information about your business and query through that document with the questions asked by your customers. +For more information about the Question Answering task, check out the Hugging Face course. + + +Causal Language Modeling video + +Welcome to the Hugging Face tasks series! In this video we’ll take a look at Causal Language Modeling. +Causal language modeling is the task of predicting the next +word in a sentence, given all the previous words. This task is very similar to the autocorrect function that you might have on your phone. +These models take a sequence to be completed and outputs the complete sequence. +Classification metrics can’t be used as there’s no single correct answer for completion. Instead, we evaluate the distribution of the text completed by the model. +A common metric to do so is the cross-entropy loss. Perplexity is also a widely used metric and it is calculated as the exponential of the cross-entropy loss. +You can use any dataset with plain text and tokenize the text to prepare the data. +Causal language models can be used to generate code. +For more information about the Causal Language Modeling task, check out the Hugging Face course. + + +Masked Language Modeling video + +Welcome to the Hugging Face tasks series! In this video we’ll take a look at Masked Language Modeling. +Masked language modeling is the task of predicting which words should fill in the blanks of a sentence. +These models take a masked text as the input and output the possible values for that mask. +Masked language modeling is handy before fine-tuning your model for your task. For example, if you need to use a model in a specific domain, say, biomedical documents, models like BERT will treat your domain-specific words as rare tokens. If you train a masked language model using your biomedical corpus and then fine tune your model on a downstream task, you will have a better performance. +Classification metrics can’t be used as there’s no single correct answer to mask values. Instead, we evaluate the distribution of the mask values. +A common metric to do so is the cross-entropy loss. Perplexity is also a widely used metric and it is calculated as the exponential of the cross-entropy loss. +You can use any dataset with plain text and tokenize the text to mask the data. +For more information about the Masked Language Modeling, check out the Hugging Face course. + + +Summarization video + +Welcome to the Hugging Face tasks series. In this video, we will take a look at the Text Summarization task. +Summarization is a task of producing a shorter version of a document while preserving the relevant and important information in the document. +Summarization models take a document to be summarized and output the summarized text. +This task is evaluated on the ROUGE score. It’s based on the overlap between the produced sequence and the correct sequence. +You might see this as ROUGE-1, which is the overlap of single tokens and ROUGE-2, the overlap of subsequent token pairs. ROUGE-N refers to the overlap of n subsequent tokens. Here we see an example of how overlaps take place. +An example dataset used for this task is called Extreme Summarization, XSUM. This dataset contains texts and their summarized versions. +You can use summarization models to summarize research papers which would enable researchers to easily pick papers for their reading list. +For more information about the Summarization task, check out the Hugging Face course. + + +Translation video + +Welcome to the Hugging Face tasks series. In this video, we will take a look at the Translation task. +Translation is the task of translating text from one language to another. +These models take a text in the source language and output the translation of that text in the target language. +The task is evaluated on the BLEU score. +The score ranges from 0 to 1, in which 1 means the translation perfectly matched and 0 did not match at all. +BLEU is calculated over subsequent tokens called n-grams. Unigram refers to a single token while bi-gram refers to token pairs and n-grams refer to n subsequent tokens. +Machine translation datasets contain pairs of text in a language and translation of the text in another language. +These models can help you build conversational agents across different languages. +One option is to translate the training data used for the chatbot and train a separate chatbot. +You can put one translation model from your user’s language to the language your chatbot is trained on, translate the user inputs and do intent classification, take the output of the chatbot and translate it from the language your chatbot was trained on to the user’s language. +For more information about the Translation task, check out the Hugging Face course. diff --git "a/subtitles/en/tasks_00_\360\237\244\227-tasks-token-classification.srt" "b/subtitles/en/tasks_00_\360\237\244\227-tasks-token-classification.srt" new file mode 100644 index 000000000..ee6e6f207 --- /dev/null +++ "b/subtitles/en/tasks_00_\360\237\244\227-tasks-token-classification.srt" @@ -0,0 +1,116 @@ +1 +00:00:04,520 --> 00:00:07,400 +Welcome to the Hugging Face tasks series! + +2 +00:00:07,400 --> 00:00:11,870 +In this video we’ll take a look at the token +classification task. + +3 +00:00:11,870 --> 00:00:17,900 +Token classification is the task of assigning +a label to each token in a sentence. + +4 +00:00:17,900 --> 00:00:23,310 +There are various token classification tasks +and the most common are Named Entity Recognition + +5 +00:00:23,310 --> 00:00:26,430 +and Part-of-Speech Tagging. + +6 +00:00:26,430 --> 00:00:31,640 +Let’s take a quick look at the Named Entity +Recognition task. + +7 +00:00:31,640 --> 00:00:38,400 +The goal of this task is to find the entities +in a piece of text, such as person, location, + +8 +00:00:38,400 --> 00:00:40,210 +or organization. + +9 +00:00:40,210 --> 00:00:45,250 +This task is formulated as labelling each +token with one class for each entity, and + +10 +00:00:45,250 --> 00:00:51,719 +another class for tokens that have no entity. + +11 +00:00:51,719 --> 00:00:55,670 +Another token classification task is part-of-speech +tagging. + +12 +00:00:55,670 --> 00:01:01,399 +The goal of this task is to label the words +for a particular part of a speech, such as + +13 +00:01:01,399 --> 00:01:05,900 +noun, pronoun, adjective, verb and so on. + +14 +00:01:05,900 --> 00:01:11,270 +This task is formulated as labelling each +token with parts of speech. + +15 +00:01:11,270 --> 00:01:19,659 +Token classification models are evaluated +on Accuracy, Recall, Precision and F1-Score. + +16 +00:01:19,659 --> 00:01:22,950 +The metrics are calculated for each of the +classes. + +17 +00:01:22,950 --> 00:01:28,040 +We calculate true positive, true negative +and false positives to calculate precision + +18 +00:01:28,040 --> 00:01:31,829 +and recall, and take their harmonic mean to +get F1-Score. + +19 +00:01:31,829 --> 00:01:42,329 +Then we calculate it for every class and take +the overall average to evaluate our model. + +20 +00:01:42,329 --> 00:01:45,680 +An example dataset used for this task is ConLL2003. + +21 +00:01:45,680 --> 00:01:51,750 +Here, each token belongs to a certain named +entity class, denoted as the indices of the + +22 +00:01:51,750 --> 00:01:55,380 +list containing the labels. + +23 +00:01:55,380 --> 00:02:00,720 +You can extract important information from +invoices using named entity recognition models, + +24 +00:02:00,720 --> 00:02:07,070 +such as date, organization name or address. + +25 +00:02:07,070 --> 00:02:16,840 +For more information about the Token classification +task, check out the Hugging Face course. diff --git "a/subtitles/en/tasks_01_\360\237\244\227-tasks-question-answering.srt" "b/subtitles/en/tasks_01_\360\237\244\227-tasks-question-answering.srt" new file mode 100644 index 000000000..6416fde12 --- /dev/null +++ "b/subtitles/en/tasks_01_\360\237\244\227-tasks-question-answering.srt" @@ -0,0 +1,87 @@ +1 +00:00:04,400 --> 00:00:06,480 +Welcome to the Hugging Face tasks series.   + +2 +00:00:07,200 --> 00:00:10,080 +In this video, we will take a look  +at the Question Answering task.  + +3 +00:00:13,120 --> 00:00:17,200 +Question answering is the task of  +extracting an answer in a given document.  + +4 +00:00:21,120 --> 00:00:25,600 +Question answering models take a context,  +which is the document you want to search in,   + +5 +00:00:26,240 --> 00:00:31,440 +and a question and return an answer.  +Note that the answer is not generated,   + +6 +00:00:31,440 --> 00:00:37,600 +but extracted from the context. This type  +of question answering is called extractive.  + +7 +00:00:42,320 --> 00:00:46,960 +The task is evaluated on two  +metrics, exact match and F1-Score.  + +8 +00:00:49,680 --> 00:00:52,320 +As the name implies, exact match looks for an   + +9 +00:00:52,320 --> 00:00:57,840 +exact match between the predicted  +answer and the correct answer.  + +10 +00:01:00,080 --> 00:01:05,520 +A common metric used is the F1-Score, which  +is calculated over tokens that are predicted   + +11 +00:01:05,520 --> 00:01:10,960 +correctly and incorrectly. It is calculated  +over the average of two metrics called   + +12 +00:01:10,960 --> 00:01:16,560 +precision and recall which are metrics that  +are used widely in classification problems.  + +13 +00:01:20,880 --> 00:01:28,240 +An example dataset used for this task is called  +SQuAD. This dataset contains contexts, questions   + +14 +00:01:28,240 --> 00:01:32,080 +and the answers that are obtained  +from English Wikipedia articles.  + +15 +00:01:35,440 --> 00:01:39,520 +You can use question answering models to  +automatically answer the questions asked   + +16 +00:01:39,520 --> 00:01:46,480 +by your customers. You simply need a document  +containing information about your business   + +17 +00:01:47,200 --> 00:01:53,840 +and query through that document with  +the questions asked by your customers.  + +18 +00:01:55,680 --> 00:02:06,160 +For more information about the Question Answering  +task, check out the Hugging Face course. diff --git "a/subtitles/en/tasks_02_\360\237\244\227-tasks-causal-language-modeling.srt" "b/subtitles/en/tasks_02_\360\237\244\227-tasks-causal-language-modeling.srt" new file mode 100644 index 000000000..06dc54e12 --- /dev/null +++ "b/subtitles/en/tasks_02_\360\237\244\227-tasks-causal-language-modeling.srt" @@ -0,0 +1,63 @@ +1 +00:00:04,560 --> 00:00:06,640 +Welcome to the Hugging Face tasks series!   + +2 +00:00:07,200 --> 00:00:10,400 +In this video we’ll take a look  +at Causal Language Modeling.  + +3 +00:00:13,600 --> 00:00:16,880 +Causal language modeling is  +the task of predicting the next  + +4 +00:00:16,880 --> 00:00:21,920 +word in a sentence, given all the  +previous words. This task is very   + +5 +00:00:21,920 --> 00:00:29,920 +similar to the autocorrect function  +that you might have on your phone.  + +6 +00:00:29,920 --> 00:00:34,720 +These models take a sequence to be  +completed and outputs the complete sequence.  + +7 +00:00:38,640 --> 00:00:44,160 +Classification metrics can’t be used as there’s  +no single correct answer for completion.   + +8 +00:00:44,960 --> 00:00:49,280 +Instead, we evaluate the distribution  +of the text completed by the model.  + +9 +00:00:50,800 --> 00:00:55,440 +A common metric to do so is the  +cross-entropy loss. Perplexity is   + +10 +00:00:55,440 --> 00:01:01,280 +also a widely used metric and it is calculated  +as the exponential of the cross-entropy loss.  + +11 +00:01:05,200 --> 00:01:11,840 +You can use any dataset with plain text  +and tokenize the text to prepare the data.  + +12 +00:01:15,040 --> 00:01:18,240 +Causal language models can  +be used to generate code.  + +13 +00:01:22,480 --> 00:01:33,200 +For more information about the Causal Language  +Modeling task, check out the Hugging Face course. diff --git "a/subtitles/en/tasks_03_\360\237\244\227-tasks-masked-language-modeling.srt" "b/subtitles/en/tasks_03_\360\237\244\227-tasks-masked-language-modeling.srt" new file mode 100644 index 000000000..28f376b68 --- /dev/null +++ "b/subtitles/en/tasks_03_\360\237\244\227-tasks-masked-language-modeling.srt" @@ -0,0 +1,85 @@ +1 +00:00:04,660 --> 00:00:07,589 +Welcome to the Hugging Face tasks series! + +2 +00:00:07,589 --> 00:00:13,730 +In this video we’ll take a look at Masked +Language Modeling. + +3 +00:00:13,730 --> 00:00:20,720 +Masked language modeling is the task of predicting +which words should fill in the blanks of a + +4 +00:00:20,720 --> 00:00:23,500 +sentence. + +5 +00:00:23,500 --> 00:00:32,870 +These models take a masked text as the input +and output the possible values for that mask. + +6 +00:00:32,870 --> 00:00:37,550 +Masked language modeling is handy before fine-tuning +your model for your task. + +7 +00:00:37,550 --> 00:00:43,579 +For example, if you need to use a model in +a specific domain, say, biomedical documents, + +8 +00:00:43,579 --> 00:00:49,050 +models like BERT will treat your domain-specific +words as rare tokens. + +9 +00:00:49,050 --> 00:00:54,220 +If you train a masked language model using +your biomedical corpus and then fine tune + +10 +00:00:54,220 --> 00:01:02,929 +your model on a downstream task, you will +have a better performance. + +11 +00:01:02,929 --> 00:01:07,799 +Classification metrics can’t be used as +there’s no single correct answer to mask + +12 +00:01:07,799 --> 00:01:08,799 +values. + +13 +00:01:08,799 --> 00:01:12,900 +Instead, we evaluate the distribution of the +mask values. + +14 +00:01:12,900 --> 00:01:16,590 +A common metric to do so is the cross-entropy +loss. + +15 +00:01:16,590 --> 00:01:22,010 +Perplexity is also a widely used metric and +it is calculated as the exponential of the + +16 +00:01:22,010 --> 00:01:27,240 +cross-entropy loss. + +17 +00:01:27,240 --> 00:01:35,680 +You can use any dataset with plain text and +tokenize the text to mask the data. + +18 +00:01:35,680 --> 00:01:44,710 +For more information about the Masked Language +Modeling, check out the Hugging Face course. diff --git "a/subtitles/en/tasks_04_\360\237\244\227-tasks-summarization.srt" "b/subtitles/en/tasks_04_\360\237\244\227-tasks-summarization.srt" new file mode 100644 index 000000000..0c16f7f85 --- /dev/null +++ "b/subtitles/en/tasks_04_\360\237\244\227-tasks-summarization.srt" @@ -0,0 +1,68 @@ +1 +00:00:04,560 --> 00:00:06,640 +Welcome to the Hugging Face tasks series.   + +2 +00:00:07,280 --> 00:00:10,720 +In this video, we will take a look  +at the Text Summarization task.  + +3 +00:00:13,680 --> 00:00:16,480 +Summarization is a task of  +producing a shorter version   + +4 +00:00:16,480 --> 00:00:21,600 +of a document while preserving the relevant  +and important information in the document.  + +5 +00:00:25,040 --> 00:00:29,840 +Summarization models take a document to be  +summarized and output the summarized text.  + +6 +00:00:33,360 --> 00:00:40,240 +This task is evaluated on the ROUGE score. It’s  +based on the overlap between the produced sequence   + +7 +00:00:40,240 --> 00:00:48,000 +and the correct sequence. +You might see this as ROUGE-1,   + +8 +00:00:48,000 --> 00:00:55,600 +which is the overlap of single tokens and ROUGE-2,  +the overlap of subsequent token pairs. ROUGE-N   + +9 +00:00:55,600 --> 00:01:02,960 +refers to the overlap of n subsequent tokens.  +Here we see an example of how overlaps take place.  + +10 +00:01:06,160 --> 00:01:11,280 +An example dataset used for this task is  +called Extreme Summarization, XSUM. This   + +11 +00:01:11,280 --> 00:01:14,480 +dataset contains texts and  +their summarized versions.  + +12 +00:01:17,680 --> 00:01:21,280 +You can use summarization models  +to summarize research papers which   + +13 +00:01:21,280 --> 00:01:25,680 +would enable researchers to easily  +pick papers for their reading list.  + +14 +00:01:29,040 --> 00:01:39,520 +For more information about the Summarization  +task, check out the Hugging Face course. diff --git "a/subtitles/en/tasks_05_\360\237\244\227-tasks-translation.srt" "b/subtitles/en/tasks_05_\360\237\244\227-tasks-translation.srt" new file mode 100644 index 000000000..ff491e24c --- /dev/null +++ "b/subtitles/en/tasks_05_\360\237\244\227-tasks-translation.srt" @@ -0,0 +1,96 @@ +1 +00:00:04,569 --> 00:00:07,529 +Welcome to the Hugging Face tasks series. + +2 +00:00:07,529 --> 00:00:11,840 +In this video, we will take a look at the +Translation task. + +3 +00:00:11,840 --> 00:00:19,420 +Translation is the task of translating text +from one language to another. + +4 +00:00:19,420 --> 00:00:24,420 +These models take a text in the source language +and output the translation of that text in + +5 +00:00:24,420 --> 00:00:28,609 +the target language. + +6 +00:00:28,609 --> 00:00:31,619 +The task is evaluated on the BLEU score. + +7 +00:00:31,619 --> 00:00:38,430 +The score ranges from 0 to 1, in which 1 means +the translation perfectly matched and 0 did + +8 +00:00:38,430 --> 00:00:40,110 +not match at all. + +9 +00:00:40,110 --> 00:00:45,320 +BLEU is calculated over subsequent tokens +called n-grams. + +10 +00:00:45,320 --> 00:00:51,629 +Unigram refers to a single token while bi-gram +refers to token pairs and n-grams refer to + +11 +00:00:51,629 --> 00:00:56,219 +n subsequent tokens. + +12 +00:00:56,219 --> 00:01:01,859 +Machine translation datasets contain pairs +of text in a language and translation of the + +13 +00:01:01,859 --> 00:01:05,910 +text in another language. + +14 +00:01:05,910 --> 00:01:11,290 +These models can help you build conversational +agents across different languages. + +15 +00:01:11,290 --> 00:01:16,110 +One option is to translate the training data +used for the chatbot and train a separate + +16 +00:01:16,110 --> 00:01:19,970 +chatbot. + +17 +00:01:19,970 --> 00:01:24,950 +You can put one translation model from your +user’s language to the language your chatbot + +18 +00:01:24,950 --> 00:01:31,360 +is trained on, translate the user inputs and +do intent classification, take the output + +19 +00:01:31,360 --> 00:01:39,399 +of the chatbot and translate it from the language +your chatbot was trained on to the user’s + +20 +00:01:39,399 --> 00:01:40,850 +language. + +21 +00:01:40,850 --> 00:01:49,720 +For more information about the Translation +task, check out the Hugging Face course. diff --git a/subtitles/fr/63_data-processing-for-causal-language-modeling.srt b/subtitles/fr/63_data-processing-for-causal-language-modeling.srt index ebf9da64e..c4d912776 100644 --- a/subtitles/fr/63_data-processing-for-causal-language-modeling.srt +++ b/subtitles/fr/63_data-processing-for-causal-language-modeling.srt @@ -116,11 +116,11 @@ et nous ne perdons aucune séquence car elles sont trop courtes. Jusqu'à prése 30 00:03:05,840 --> 00:03:10,720 -des entrées pour la modélisation causale du langage, mais pas des étiquettes nécessaires à l'entraînement supervisée. +des entrées pour la modélisation du langage causal, mais pas des étiquettes nécessaires à l'entraînement supervisée. 31 00:03:11,600 --> 00:03:16,480 -Lorsque nous effectuons une modélisation causale du langage, nous n'avons pas besoin d'étiquettes supplémentaires pour les séquences d'entrée +Lorsque nous effectuons une modélisation du langage causal, nous n'avons pas besoin d'étiquettes supplémentaires pour les séquences d'entrée 32 00:03:16,480 --> 00:03:22,080 @@ -168,4 +168,4 @@ Donc vous voyez qu'il n'y a pas de magie 43 00:04:21,600 --> 00:04:27,840 -impliquée dans le traitement des données pour la modélisation du langage causal et ne nécessite que quelques étapes simples ! \ No newline at end of file +impliquée dans le traitement des données pour la modélisation du langage causal et ne nécessite que quelques étapes simples ! diff --git a/subtitles/fr/metadata_tasks.csv b/subtitles/fr/metadata_tasks.csv new file mode 100644 index 000000000..c40e0d858 --- /dev/null +++ b/subtitles/fr/metadata_tasks.csv @@ -0,0 +1,7 @@ +id,title,link,srt_filename +wVHdVlPScxA,🤗 Tasks: Token Classification,https://www.youtube.com/watch?v=wVHdVlPScxA&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=1,subtitles/fr/tasks_00_🤗-tasks-token-classification.srt +ajPx5LwJD-I,🤗 Tasks: Question Answering,https://www.youtube.com/watch?v=ajPx5LwJD-I&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=2,subtitles/fr/tasks_01_🤗-tasks-question-answering.srt +Vpjb1lu0MDk,🤗 Tasks: Causal Language Modeling,https://www.youtube.com/watch?v=Vpjb1lu0MDk&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=3,subtitles/fr/tasks_02_🤗-tasks-causal-language-modeling.srt +mqElG5QJWUg,🤗 Tasks: Masked Language Modeling,https://www.youtube.com/watch?v=mqElG5QJWUg&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=4,subtitles/fr/tasks_03_🤗-tasks-masked-language-modeling.srt +yHnr5Dk2zCI,🤗 Tasks: Summarization,https://www.youtube.com/watch?v=yHnr5Dk2zCI&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=5,subtitles/fr/tasks_04_🤗-tasks-summarization.srt +1JvfrvZgi6c,🤗 Tasks: Translation,https://www.youtube.com/watch?v=1JvfrvZgi6c&list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf&index=6,subtitles/fr/tasks_05_🤗-tasks-translation.srt diff --git "a/subtitles/fr/tasks_00_\360\237\244\227-tasks-token-classification.srt" "b/subtitles/fr/tasks_00_\360\237\244\227-tasks-token-classification.srt" new file mode 100644 index 000000000..bf391083f --- /dev/null +++ "b/subtitles/fr/tasks_00_\360\237\244\227-tasks-token-classification.srt" @@ -0,0 +1,99 @@ +1 +00:00:04,520 --> 00:00:07,400 +Bienvenue dans la série d'Hugging Face sur les tâches ! + +2 +00:00:07,400 --> 00:00:11,870 +Dans cette vidéo, nous allons jeter un coup d'œil à la tâche de classification de tokens. + +3 +00:00:11,870 --> 00:00:17,900 +La classification de tokens consiste à attribuer une étiquette à chaque token d'une phrase + +4 +00:00:17,900 --> 00:00:23,310 +Il existe plusieurs tâches de classification de tokens, les plus courantes étant la reconnaissance d’entités nommées + +5 +00:00:23,310 --> 00:00:26,430 +et le « part-of-speech ». + +6 +00:00:26,430 --> 00:00:31,640 +Jetons un coup d'œil rapide à la tâche de reconnaissance d'entités nommées + +7 +00:00:31,640 --> 00:00:38,400 +L'objectif de cette tâche est de trouver les entités dans un texte, comme une personne, un lieu + +8 +00:00:38,400 --> 00:00:40,210 +ou une organisation. + +9 +00:00:40,210 --> 00:00:45,250 +Cette tâche est formulée comme l'étiquetage de chaque token avec une classe pour chaque entité, + +10 +00:00:45,250 --> 00:00:51,719 +et une autre classe pour les tokens qui n'ont pas d'entité. + +11 +00:00:51,719 --> 00:00:55,670 +Une autre tâche de classification de tokens est le « part-of-speech ». + +12 +00:00:55,670 --> 00:01:01,399 +L'objectif de cette tâche est d'étiqueter les mots pour une partie particulière du texte, comme + +13 +00:01:01,399 --> 00:01:05,900 +un nom, un pronom, un adjectif, un verbe, etc. + +14 +00:01:05,900 --> 00:01:11,270 +Cette tâche est formulée comme l'étiquetage de chaque token avec les parties du texte. + +15 +00:01:11,270 --> 00:01:19,659 +Les modèles de classification de tokens sont évalués sur l'exactitude, le rappel, la précision et le score F1. + +16 +00:01:19,659 --> 00:01:22,950 +Les métriques sont calculées pour chacune des classes. + +17 +00:01:22,950 --> 00:01:28,040 +Nous calculons les vrais positifs, les vrais négatifs et les faux positifs pour calculer la précision + +18 +00:01:28,040 --> 00:01:31,829 +et le rappel, et prenons leur moyenne harmonique pour obtenir le score F1. + +19 +00:01:31,829 --> 00:01:42,329 +Ensuite, nous les calculons pour chaque classe et prenons la moyenne globale pour évaluer notre modèle. + +20 +00:01:42,329 --> 00:01:45,680 +Un exemple de jeu de données utilisé pour cette tâche est ConLL2003. + +21 +00:01:45,680 --> 00:01:51,750 +Ici, chaque token appartient à une certaine classe d'entités nommées, désignées par les indices de la + +22 +00:01:51,750 --> 00:01:55,380 +liste contenant les étiquettes. + +23 +00:01:55,380 --> 00:02:00,720 +Vous pouvez extraire des informations importantes de factures à l'aide de modèles de reconnaissance d'entités nommées, + +24 +00:02:00,720 --> 00:02:07,070 +telles que la date, le nom de l'organisation ou l'adresse. + +25 +00:02:07,070 --> 00:02:16,840 +Pour plus d'informations sur la tâche de classification de tokens, consultez le cours d'Hugging Face. diff --git "a/subtitles/fr/tasks_01_\360\237\244\227-tasks-question-answering.srt" "b/subtitles/fr/tasks_01_\360\237\244\227-tasks-question-answering.srt" new file mode 100644 index 000000000..da7060062 --- /dev/null +++ "b/subtitles/fr/tasks_01_\360\237\244\227-tasks-question-answering.srt" @@ -0,0 +1,71 @@ +1 +00:00:04,400 --> 00:00:06,480 +Bienvenue dans la série d'Hugging Face sur les tâches ! + +2 +00:00:07,200 --> 00:00:10,080 +Dans cette vidéo, nous allons examiner la tâche de réponse aux questions. + +3 +00:00:13,120 --> 00:00:17,200 +La réponse aux questions consiste à extraire une réponse dans un document donné. + +4 +00:00:21,120 --> 00:00:25,600 +Les modèles de réponse aux questions prennent un contexte, qui est le document dans lequel vous souhaitez effectuer une recherche, + +5 +00:00:26,240 --> 00:00:31,440 +et une question et renvoient une réponse. Notez que la réponse n'est pas générée, + +6 +00:00:31,440 --> 00:00:37,600 +mais extraite du contexte. Ce type de réponse aux questions est appelé extractive. + +7 +00:00:42,320 --> 00:00:46,960 +La tâche est évaluée sur deux statistiques, la correspondance exacte et le score F1. + +8 +00:00:49,680 --> 00:00:52,320 +Comme son nom l'indique, la correspondance exacte recherche une + +9 +00:00:52,320 --> 00:00:57,840 +correspondance exacte entre la réponse prédite et la bonne réponse. + +10 +00:01:00,080 --> 00:01:05,520 +Une métrique couramment utilisée est le F1-Score, qui est calculé sur des tokens prédits + +11 +00:01:05,520 --> 00:01:10,960 +correctement et incorrectement. Il est calculé sur la moyenne de deux métriques appelées + +12 +00:01:10,960 --> 00:01:16,560 +précision et rappel, qui sont des métriques largement utilisées dans les problèmes de classification. + +13 +00:01:20,880 --> 00:01:28,240 +Un exemple de jeu de données utilisé pour cette tâche est appelé SQuAD. Ce jeu de données contient des contextes, des questions + +14 +00:01:28,240 --> 00:01:32,080 +et les réponses obtenues à partir d'articles de Wikipédia en anglais. + +15 +00:01:35,440 --> 00:01:39,520 +Vous pouvez utiliser des modèles de réponse aux questions pour répondre automatiquement aux questions posées + +16 +00:01:39,520 --> 00:01:46,480 +par vos clients. Vous avez simplement besoin d'un document contenant des informations sur votre entreprise + +17 +00:01:47,200 --> 00:01:53,840 +et interrogez ce document avec les questions posées par vos clients. + +18 +00:01:55,680 --> 00:02:06,160 +Pour plus d'informations sur la tâche de réponse aux questions, consultez le cours d'Hugging Face. diff --git "a/subtitles/fr/tasks_02_\360\237\244\227-tasks-causal-language-modeling.srt" "b/subtitles/fr/tasks_02_\360\237\244\227-tasks-causal-language-modeling.srt" new file mode 100644 index 000000000..27e05d726 --- /dev/null +++ "b/subtitles/fr/tasks_02_\360\237\244\227-tasks-causal-language-modeling.srt" @@ -0,0 +1,51 @@ +1 +00:00:04,560 --> 00:00:06,640 +Bienvenue dans la série d'Hugging Face sur les tâches ! + +2 +00:00:07,200 --> 00:00:10,400 +Dans cette vidéo, nous allons jeter un œil à la modélisation du langage causal. + +3 +00:00:13,600 --> 00:00:16,880 +La modélisation du langage causal consiste à prédire le + +4 +00:00:16,880 --> 00:00:21,920 +mot suivant dans une phrase, compte tenu de tous les mots précédents. Cette tâche est très + +5 +00:00:21,920 --> 00:00:29,920 +similaire à la fonction de correction automatique que vous pourriez avoir sur votre téléphone. + +6 +00:00:29,920 --> 00:00:34,720 +Ces modèles prennent une séquence à compléter et génèrent la séquence complète. + +7 +00:00:38,640 --> 00:00:44,160 +Les métriques de classification ne peuvent pas être utilisées, car il n'y a pas de réponse correcte unique pour la complétion. + +8 +00:00:44,960 --> 00:00:49,280 +Au lieu de cela, nous évaluons la distribution du texte complété par le modèle. + +9 +00:00:50,800 --> 00:00:55,440 +Une métrique courante pour ce faire est la perte d'entropie croisée. La perplexité est + +10 +00:00:55,440 --> 00:01:01,280 +aussi une métrique largement utilisée et elle est calculée comme l'exponentielle de la perte d'entropie croisée. + +11 +00:01:05,200 --> 00:01:11,840 +Vous pouvez utiliser n'importe quel jeu de données avec du texte brut et tokeniser le texte pour préparer les données. + +12 +00:01:15,040 --> 00:01:18,240 +Les modèles de langage causal peuvent être utilisés pour générer du code. + +13 +00:01:22,480 --> 00:01:33,200 +Pour plus d'informations sur la tâche de modélisation du langage causal, consultez le cours d'Hugging Face. diff --git "a/subtitles/fr/tasks_03_\360\237\244\227-tasks-masked-language-modeling.srt" "b/subtitles/fr/tasks_03_\360\237\244\227-tasks-masked-language-modeling.srt" new file mode 100644 index 000000000..ca32dc906 --- /dev/null +++ "b/subtitles/fr/tasks_03_\360\237\244\227-tasks-masked-language-modeling.srt" @@ -0,0 +1,71 @@ +1 +00:00:04,660 --> 00:00:07,589 +Bienvenue dans la série d'Hugging Face sur les tâches ! + +2 +00:00:07,589 --> 00:00:13,730 +Dans cette vidéo, nous allons jeter un œil à la modélisation du langage masqué. + +3 +00:00:13,730 --> 00:00:20,720 +La modélisation du langage masqué consiste à prédire quels mots doivent remplir les blancs d'une + +4 +00:00:20,720 --> 00:00:23,500 +phrase. + +5 +00:00:23,500 --> 00:00:32,870 +Ces modèles prennent un texte masqué en entrée et génèrent les valeurs possibles pour ce masque. + +6 +00:00:32,870 --> 00:00:37,550 +La modélisation en langage masqué est pratique avant de finetuner votre modèle pour votre tâche. + +7 +00:00:37,550 --> 00:00:43,579 +Par exemple, si vous devez utiliser un modèle dans un domaine spécifique, par exemple des documents biomédicaux, des + +8 +00:00:43,579 --> 00:00:49,050 +modèles comme BERT traiteront vos mots spécifiques à un domaine comme des tokens rares. + +9 +00:00:49,050 --> 00:00:54,220 +Si vous entraînez un modèle de langage masqué à l'aide de votre corpus biomédical, puis finetunez + +10 +00:00:54,220 --> 00:01:02,929 +votre modèle sur une tâche en aval, vous obtiendrez de meilleures performances. + +11 +00:01:02,929 --> 00:01:07,799 +Les métriques de classification ne peuvent pas être utilisées car il n'y a pas de réponse correcte unique aux + +12 +00:01:07,799 --> 00:01:08,799 +valeurs du masque. + +13 +00:01:08,799 --> 00:01:12,900 +Au lieu de cela, nous évaluons la distribution des valeurs du masque. + +14 +00:01:12,900 --> 00:01:16,590 +Une métrique courante pour ce faire est la perte d'entropie croisée. + +15 +00:01:16,590 --> 00:01:22,010 +La perplexité est aussi une métrique largement utilisée et elle est calculée comme l'exponentielle de la + +16 +00:01:22,010 --> 00:01:27,240 +perte d'entropie croisée. + +17 +00:01:27,240 --> 00:01:35,680 +Vous pouvez utiliser n'importe quel jeu de données avec du texte brut et tokeniser le texte pour masquer les données. + +18 +00:01:35,680 --> 00:01:44,710 +Pour plus d'informations sur la modélisation du langage masqué, consultez le cours d'Hugging Face. diff --git "a/subtitles/fr/tasks_04_\360\237\244\227-tasks-summarization.srt" "b/subtitles/fr/tasks_04_\360\237\244\227-tasks-summarization.srt" new file mode 100644 index 000000000..8a19f28bd --- /dev/null +++ "b/subtitles/fr/tasks_04_\360\237\244\227-tasks-summarization.srt" @@ -0,0 +1,55 @@ +1 +00:00:04,560 --> 00:00:06,640 +Bienvenue dans la série d'Hugging Face sur les tâches ! + +2 +00:00:07,280 --> 00:00:10,720 +Dans cette vidéo, nous allons examiner la tâche de résumé de texte. + +3 +00:00:13,200 --> 00:00:16,480 +Le résumé consiste à produire une version plus courte + +4 +00:00:16,480 --> 00:00:21,600 +d'un document tout en préservant les informations pertinentes et importantes dans le document. + +5 +00:00:25,040 --> 00:00:29,840 +Les modèles de résumé prennent un document à résumer et génèrent le texte résumé. + +6 +00:00:33,360 --> 00:00:40,240 +Cette tâche est évaluée sur le score ROUGE. Il est basé sur le chevauchement entre la séquence produite + +7 +00:00:40,240 --> 00:00:48,000 +et la séquence correcte. Vous pouvez voir ceci comme ROUGE-1, + +8 +00:00:48,000 --> 00:00:55,600 +qui est le chevauchement de tokens uniques et ROUGE-2, le chevauchement de paires de tokens successives. ROUGE-N + +9 +00:00:55,600 --> 00:01:02,960 +fait référence au chevauchement de N tokens successifs. Ici, nous voyons un exemple de la façon dont les chevauchements ont lieu. + +10 +00:01:06,160 --> 00:01:11,280 +Un exemple de jeu de données utilisé pour cette tâche s'appelle Extreme Summarization (XSUM). + +11 +00:01:11,280 --> 00:01:14,480 +Ce jeu de données contient des textes et leurs versions résumées. + +12 +00:01:17,680 --> 00:01:21,280 +Vous pouvez utiliser des modèles de résumé pour résumer les articles de recherche, ce + +13 +00:01:21,280 --> 00:01:25,680 +qui permettrait aux chercheurs de choisir facilement des articles pour leur liste de lecture. + +14 +00:01:29,040 --> 00:01:39,520 +Pour plus d'informations sur la tâche de résumé de textes, consultez le cours d'Hugging Face. diff --git "a/subtitles/fr/tasks_05_\360\237\244\227-tasks-translation.srt" "b/subtitles/fr/tasks_05_\360\237\244\227-tasks-translation.srt" new file mode 100644 index 000000000..06a851321 --- /dev/null +++ "b/subtitles/fr/tasks_05_\360\237\244\227-tasks-translation.srt" @@ -0,0 +1,83 @@ +1 +00:00:04,569 --> 00:00:07,529 +Bienvenue dans la série d'Hugging Face sur les tâches ! + +2 +00:00:07,529 --> 00:00:11,840 +Dans cette vidéo, nous allons jeter un œil à la tâche de traduction. + +3 +00:00:11,840 --> 00:00:19,420 +La traduction est la tâche de traduire un texte d'une langue à une autre. + +4 +00:00:19,420 --> 00:00:24,420 +Ces modèles prennent un texte dans la langue source et génèrent la traduction de ce texte dans + +5 +00:00:24,420 --> 00:00:28,609 +la langue cible. + +6 +00:00:28,609 --> 00:00:31,619 +La tâche est évaluée sur le score BLEU. + +7 +00:00:31,619 --> 00:00:38,430 +Le score varie de 0 à 1, où 1 signifie que la traduction correspond parfaitement et 0 ne + +8 +00:00:38,430 --> 00:00:40,110 +correspond pas du tout. + +9 +00:00:40,110 --> 00:00:45,320 +BLEU est calculé sur les tokens successifs appelés n-grammes. + +10 +00:00:45,320 --> 00:00:51,629 +« unigram » fait référence à un seul token tandis que bi-gramme fait référence à des paires de tokens et n-grammes fait référence à + +11 +00:00:51,629 --> 00:00:56,219 +n tokens successifs. + +12 +00:00:56,219 --> 00:01:01,859 +Les jeux de données de traduction automatique contiennent des paires de texte dans une langue et la traduction du + +13 +00:01:01,859 --> 00:01:05,910 +texte dans une autre langue. + +14 +00:01:05,910 --> 00:01:11,290 +Ces modèles peuvent vous aider à créer des agents conversationnels dans différentes langues. + +15 +00:01:11,290 --> 00:01:16,110 +Une option consiste à traduire les données d'entraînement utilisées pour le chatbot et à entraîner un + +16 +00:01:16,110 --> 00:01:19,970 +chatbot séparé. + +17 +00:01:19,970 --> 00:01:24,950 +Vous pouvez mettre un modèle de traduction de la langue de votre utilisateur vers la langue dans laquelle votre chatbot + +18 +00:01:24,950 --> 00:01:31,360 +est entraîné, traduire les entrées de l'utilisateur et effectuer une classification d'intention, prendre la sortie + +19 +00:01:31,360 --> 00:01:39,399 +du chatbot et la traduire de la langue dans laquelle votre chatbot a été entraîné vers la + +20 +00:01:39,399 --> 00:01:40,850 +langue de l'utilisateur. + +21 +00:01:40,850 --> 00:01:49,720 +Pour plus d'informations sur la tâche de traduction, consultez le cours d'Hugging Face. diff --git a/subtitles/fr/titles-and-descriptions.txt b/subtitles/fr/titles-and-descriptions.txt index ad102d6bf..3ff3a64c4 100644 --- a/subtitles/fr/titles-and-descriptions.txt +++ b/subtitles/fr/titles-and-descriptions.txt @@ -1005,9 +1005,9 @@ Vous n'avez pas de compte Hugging Face ? Inscrivez-vous maintenant : http://hugg -Traitement des données pour la modélisation causale du langage +Traitement des données pour la modélisation du langage causal -Dans cette vidéo, nous allons voir comment prétraiter un jeu de données pour une tâche de modélisation causale du langage. +Dans cette vidéo, nous allons voir comment prétraiter un jeu de données pour une tâche de modélisation du langage causal. Intervenant : Leandro von Werra Traduction : Loïck Bourdois Cette vidéo fait partie du cours Hugging Face : http://huggingface.co/course/fr/chapter7 @@ -1210,4 +1210,98 @@ Vidéos connexes : - Utilisation d'un débogueur dans un terminal : https://youtu.be/5PkZ4rbHL6c - Demander de l'aide sur les forums : https://youtu.be/S2EEG3JIt2A Vous avez une question ? Consultez le forum d’Hugging Face : https://discuss.huggingface.co/c/course/20 -Vous n'avez pas de compte Hugging Face ? Inscrivez-vous maintenant : http://huggingface.co/join \ No newline at end of file +Vous n'avez pas de compte Hugging Face ? Inscrivez-vous maintenant : http://huggingface.co/join + + + +🤗 Tasks : Classification de tokens + +Cette vidéo fait partie du cours d’Hugging Face : http://huggingface.co/course/fr +Intervenante : Merve Noyan +Traduction : Loïck Bourdois +Un aperçu de la tâche de classification de tokens. +Vous pouvez en savoir plus sur la classification de tokens dans cette section du cours : https://huggingface.co/course/fr/chapter7/2 +Vidéos connexes : +- Dans le pipeline de classification de tokens (PyTorch) : https://youtu.be/0E7ltQB7fM8 +- Dans le pipeline de classification de tokens (TensorFlow) : https://youtu.be/PrX4CjrVnNc +- Traitement des données pour la classification de tokens : https://youtu.be/iY2AZYdZAr0 +Vous n'avez pas de compte Hugging Face ? Inscrivez-vous maintenant : http://huggingface.co/join +Vous avez une question ? Consultez le forum d’Hugging Face : https://discuss.huggingface.co/c/course/20 + + + +🤗 Tasks : Réponse aux questions + +Cette vidéo fait partie du cours d’Hugging Face : http://huggingface.co/course/fr +Intervenante : Merve Noyan +Traduction : Loïck Bourdois +Un aperçu de la tâche de réponse aux questions. +Vous pouvez en savoir plus sur la réponse aux questions dans cette section du cours : https://huggingface.co/course/fr/chapter7/7 +Vidéos connexes : +- Dans le pipeline de réponse aux questions (PyTorch) : https://youtu.be/_wxyB3j3mk4 +- Dans le pipeline de réponse aux questions (TensorFlow) : https://youtu.be/b3u8RzBCX9Y +- Traitement des données pour la réponse aux questions : https://youtu.be/qgaM0weJHpA +- L'étape de post-traitement en réponse aux questions (PyTorch) : https://youtu.be/BNy08iIWVJM +- L'étape de post-traitement en réponse aux questions (TensorFlow) : https://youtu.be/VN67ZpN33Ss +Vous n'avez pas de compte Hugging Face ? Inscrivez-vous maintenant : http://huggingface.co/join +Vous avez une question ? Consultez le forum d’Hugging Face : https://discuss.huggingface.co/c/course/20 + + + +🤗 Tasks : Modélisation du langage causal + +Cette vidéo fait partie du cours d’Hugging Face : http://huggingface.co/course/fr +Intervenante : Merve Noyan +Traduction : Loïck Bourdois +Un aperçu de la tâche de modélisation du langage causal. +Vous pouvez en savoir plus sur la modélisation du langage causal dans cette section du cours : https://huggingface.co/course/fr/chapter7/6 +Vidéos connexes : +- Traitement des données pour la modélisation du langage causal : https://youtu.be/ma1TrR7gE7I +- Qu'est-ce que la perplexité ? : https://youtu.be/NURcDHhYe98 +Vous n'avez pas de compte Hugging Face ? Inscrivez-vous maintenant : http://huggingface.co/join +Vous avez une question ? Consultez le forum d’Hugging Face : https://discuss.huggingface.co/c/course/20 + + + +🤗 Tasks : Modélisation du langage masqué + +Cette vidéo fait partie du cours d’Hugging Face : http://huggingface.co/course/fr +Intervenante : Merve Noyan +Traduction : Loïck Bourdois +Un aperçu de la tâche de modélisation du langage masqué. +Vous pouvez en savoir plus sur la modélisation du langage masqué dans cette section du cours : https://huggingface.co/course/fr/chapter7/3 +Vidéos connexes : +- Traitement des données pour la modélisation du langage masqué : https://youtu.be/8PmhEIXhBvI +- Qu'est-ce que la perplexité ? : https://youtu.be/NURcDHhYe98 +Vous n'avez pas de compte Hugging Face ? Inscrivez-vous maintenant : http://huggingface.co/join +Vous avez une question ? Consultez le forum d’Hugging Face : https://discuss.huggingface.co/c/course/20 + + + +🤗 Tasks : Résumé de textes + +Cette vidéo fait partie du cours d’Hugging Face : http://huggingface.co/course/fr +Intervenante : Merve Noyan +Traduction : Loïck Bourdois +Un aperçu de la tâche de résumé de textes. +Vous pouvez en savoir plus sur le résumé de textes dans cette section du cours : https://huggingface.co/course/fr/chapter7/5 +Vidéos connexes : +- Traitement des données pour le résumé : https://youtu.be/1m7BerpSq8A +- Qu'est-ce que la métrique ROUGE ? https://youtu.be/TMshhnrEXlg +Vous n'avez pas de compte Hugging Face ? Inscrivez-vous maintenant : http://huggingface.co/join +Vous avez une question ? Consultez le forum d’Hugging Face : https://discuss.huggingface.co/c/course/20 + + + +🤗 Tasks : Traduction + +Cette vidéo fait partie du cours d’Hugging Face : http://huggingface.co/course/fr +Intervenante : Merve Noyan +Traduction : Loïck Bourdois +Un aperçu de la tâche de traduction. +Vous pouvez en savoir plus sur la traduction dans cette section du cours : https://huggingface.co/course/fr/chapter7/4 +Vidéos connexes : +- Traitement des données pour la traduction : https://youtu.be/XAR8jnZZuUs +- Qu'est-ce que la métrique BLEU ? : https://youtu.be/M05L1DhFqcw +Vous n'avez pas de compte Hugging Face ? Inscrivez-vous maintenant : http://huggingface.co/join +Vous avez une question ? Consultez le forum d’Hugging Face : https://discuss.huggingface.co/c/course/20 \ No newline at end of file diff --git a/subtitles/ru/00_welcome-to-the-hugging-face-course.srt b/subtitles/ru/00_welcome-to-the-hugging-face-course.srt new file mode 100644 index 000000000..7f7cf0c28 --- /dev/null +++ b/subtitles/ru/00_welcome-to-the-hugging-face-course.srt @@ -0,0 +1,411 @@ +1 +00:00:05,850 --> 00:00:07,713 +Добро пожаловать на курс Hugging Face. + +2 +00:00:08,550 --> 00:00:10,320 +Этот курс был разработан, чтобы научить вас + +3 +00:00:10,320 --> 00:00:12,750 +всему, что касается экосистемы Hugging Face, + +4 +00:00:12,750 --> 00:00:14,700 +как использовать набор данных и хаб моделей, + +5 +00:00:14,700 --> 00:00:16,803 +а также все наши библиотеки с открытым исходным кодом. + +6 +00:00:18,300 --> 00:00:19,950 +Вот содержание. + +7 +00:00:19,950 --> 00:00:22,770 +Как вы можете видеть, он разделен на три раздела, + +8 +00:00:22,770 --> 00:00:25,110 +которые постепенно становятся все более сложными. + +9 +00:00:25,110 --> 00:00:28,500 +На данном этапе выпущены первые два раздела. + +10 +00:00:28,500 --> 00:00:30,120 +Итак, сначала мы научим вас основам + +11 +00:00:30,120 --> 00:00:32,250 +как использовать модель Transformer, + +12 +00:00:32,250 --> 00:00:34,230 +дообучить ее на собственном наборе данных + +13 +00:00:34,230 --> 00:00:36,960 + и поделиться результатом с сообществом. + +14 +00:00:36,960 --> 00:00:39,420 +Во-вторых, мы глубже погрузимся в наши библиотеки + +15 +00:00:39,420 --> 00:00:42,360 +и научим вас решать любые задачи NLP. + +16 +00:00:42,360 --> 00:00:44,430 +Мы активно работаем над последним разделом + +17 +00:00:44,430 --> 00:00:47,280 +и надеемся, что он будет готов для вас к весне 2022 года. + +18 +00:00:48,510 --> 00:00:50,880 +Первая глава не требует технических знаний + +19 +00:00:50,880 --> 00:00:52,320 +и является хорошим введением, чтобы изучить, + +20 +00:00:52,320 --> 00:00:54,180 +что могут модели Transformers + +21 +00:00:54,180 --> 00:00:56,883 +и как они могут быть полезны для вас или вашей компании. + +22 +00:00:58,050 --> 00:01:01,110 +Следующие главы требуют хорошего знания Python + +23 +00:01:01,110 --> 00:01:02,130 +и некоторых базовых знаний в + +24 +00:01:02,130 --> 00:01:04,350 +Машинном обучении и Глубоком обучении. + +25 +00:01:04,350 --> 00:01:07,110 +Если вы не знаете, что такое тренировочное и валидационное множества + +26 +00:01:07,110 --> 00:01:09,360 +или что означает градиентный спуск, + +27 +00:01:09,360 --> 00:01:11,340 +вам следует изучить вводный курс, + +28 +00:01:11,340 --> 00:01:14,863 +например, опубликованный на сайтах deeplearning.ai или fast.ai. + +29 +00:01:16,200 --> 00:01:17,910 +Также будет лучше, если вы владеете основами + +30 +00:01:17,910 --> 00:01:21,150 +одного из фреймворков глубокого обучения, PyTorch или TensorFlow. + +31 +00:01:21,150 --> 00:01:23,520 +Каждая часть материала, представленного в этом курсе, + +32 +00:01:23,520 --> 00:01:25,590 +имеет версию на обоих этих фреймворках, + +33 +00:01:25,590 --> 00:01:26,730 +поэтому вы сможете выбрать тот, + +34 +00:01:26,730 --> 00:01:28,230 +с которым вам удобнее работать. + +35 +00:01:29,550 --> 00:01:31,740 +Это команда, которая разработала данный курс. + +36 +00:01:31,740 --> 00:01:33,120 +Теперь я предоставлю каждому из выступающих + +37 +00:01:33,120 --> 00:01:34,570 +возможность кратко представиться. + +38 +00:01:37,230 --> 00:01:38,880 +- Привет, меня зовут Мэтью, + +39 +00:01:38,880 --> 00:01:41,610 +и я инженер по машинному обучению в компании Hugging Face. + +40 +00:01:41,610 --> 00:01:43,200 +Я работаю в команде по работе с открытым исходным кодом + +41 +00:01:43,200 --> 00:01:45,180 +и отвечаю там за поддержку, в частности, + +42 +00:01:45,180 --> 00:01:47,280 +кода TensorFlow. + +43 +00:01:47,280 --> 00:01:50,130 +Ранее я работал инженером по машинному обучению в компании Parsley, + +44 +00:01:50,130 --> 00:01:52,620 +которая недавно была приобретена компанией Automatic, + +45 +00:01:52,620 --> 00:01:54,210 +а до этого был постдокторантом-исследователем + +46 +00:01:54,210 --> 00:01:57,000 +в Тринити-колледже в Дублине в Ирландии, + +47 +00:01:57,000 --> 00:02:00,093 +занимался компьютерной генетикой и заболеваниями сетчатки. + +48 +00:02:02,400 --> 00:02:03,870 +- Привет, я Лисандр. + +49 +00:02:03,870 --> 00:02:05,640 +Я инженер по машинному обучению в Hugging Face + +50 +00:02:05,640 --> 00:02:08,700 +и, в частности, являюсь частью команды по работе с открытым исходным кодом. + +51 +00:02:08,700 --> 00:02:10,890 +Я работаю в Hugging Face уже несколько лет, + +52 +00:02:10,890 --> 00:02:12,300 +и вместе с членами моей команды + +53 +00:02:12,300 --> 00:02:13,890 +я работал над большинством инструментов, + +54 +00:02:13,890 --> 00:02:15,790 +которые вы увидите в этом курсе. + +55 +00:02:18,270 --> 00:02:20,130 +- Привет, я Сильвен. + +56 +00:02:20,130 --> 00:02:22,140 +Я инженер-исследователь в Hugging Face + +57 +00:02:22,140 --> 00:02:25,830 +и один из главных сопровождающих библиотеки Transformers. + +58 +00:02:25,830 --> 00:02:28,110 +Ранее я работал в компании fast.ai, + +59 +00:02:28,110 --> 00:02:30,420 +где помогал разрабатывать библиотеку fast.ai, + +60 +00:02:30,420 --> 00:02:32,220 +а также онлайн-книгу. + +61 +00:02:32,220 --> 00:02:35,340 +До этого я был учителем математики и информатики + +62 +00:02:35,340 --> 00:02:36,173 +во Франции. + +63 +00:02:38,550 --> 00:02:41,340 +- Привет, меня зовут Саша, и я исследователь в компании Hugging Face, + +64 +00:02:41,340 --> 00:02:42,420 +работаю над этическим, + +65 +00:02:42,420 --> 00:02:46,230 +экологическим и социальным воздействием моделей машинного обучения. + +66 +00:02:46,230 --> 00:02:49,020 +Ранее я была постдокторантом-исследователем в университете Mila + +67 +00:02:49,020 --> 00:02:50,400 +в Монреале, + +68 +00:02:50,400 --> 00:02:53,040 +а также работала в качестве исследователя прикладного ИИ + +69 +00:02:53,040 --> 00:02:55,140 +для UN Global Pulse. + +70 +00:02:55,140 --> 00:02:57,300 +Я участвовала в таких проектах, как CodeCarbon + +71 +00:02:57,300 --> 00:02:59,790 +и Machine Learning Impacts Calculator + +72 +00:02:59,790 --> 00:03:02,390 +для оценки углеродного следа машинного обучения. + +73 +00:03:05,160 --> 00:03:07,650 +- Привет, меня зовут Мерве, и я являюсь адвокатом разработчиков + +74 +00:03:07,650 --> 00:03:09,390 +в компании Hugging Face. + +75 +00:03:09,390 --> 00:03:12,480 +Ранее я работала инженером по машинному обучению, + +76 +00:03:12,480 --> 00:03:15,360 +создавая инструменты NLP и чат-боты. + +77 +00:03:15,360 --> 00:03:17,670 +В настоящее время я работаю над улучшением хаба + +78 +00:03:17,670 --> 00:03:19,563 +и демократизацией машинного обучения. + +79 +00:03:22,140 --> 00:03:23,670 +- Привет всем. + +80 +00:03:23,670 --> 00:03:27,210 +Меня зовут Люсиль, и я инженер по машинному обучению + +81 +00:03:27,210 --> 00:03:28,353 +в Hugging Face. + +82 +00:03:29,580 --> 00:03:32,550 +Если в двух предложениях рассказать, кто я такая, + +83 +00:03:32,550 --> 00:03:35,590 +я занимаюсь разработкой и поддержкой инструментов с открытым исходным кодом, + +84 +00:03:36,600 --> 00:03:39,595 +а также участвую в нескольких исследовательских проектах + +85 +00:03:39,595 --> 00:03:41,795 +в области Natural Language Processing. + +86 +00:03:44,610 --> 00:03:45,540 +- Хорошего дня. + +87 +00:03:45,540 --> 00:03:47,550 +Меня зовут Льюис, я инженер по машинному обучению + +88 +00:03:47,550 --> 00:03:50,130 + в команде разработчиков открытого программного обеспечения Hugging Face. + +89 +00:03:50,130 --> 00:03:53,490 +Я увлечен разработкой инструментов для сообщества NLP, + +90 +00:03:53,490 --> 00:03:55,050 +и вы можете увидеть меня + +91 +00:03:55,050 --> 00:03:56,910 +на многих мероприятиях Hugging Face. + +92 +00:03:56,910 --> 00:03:58,470 +До присоединения к Hugging Face + +93 +00:03:58,470 --> 00:03:59,790 +я несколько лет занимался разработкой + +94 +00:03:59,790 --> 00:04:01,860 +приложений машинного обучения для стартапов + +95 +00:04:01,860 --> 00:04:04,230 +и предприятий в области NLP, + +96 +00:04:04,230 --> 00:04:07,260 +топологического анализа данных и временных рядов. + +97 +00:04:07,260 --> 00:04:10,110 +В прошлой жизни я был физиком-теоретиком, + +98 +00:04:10,110 --> 00:04:11,760 +исследовал столкновения частиц + +99 +00:04:11,760 --> 00:04:13,560 +на Большом адронном коллайдере и так далее. + +100 +00:04:15,900 --> 00:04:18,450 +- Привет, меня зовут Леандро, я инженер по машинному обучению + +101 +00:04:18,450 --> 00:04:21,030 +в команде открытого кода Hugging Face. + +102 +00:04:21,030 --> 00:04:23,460 +До прихода в Hugging Face я работал специалистом по анализу данных + +103 +00:04:23,460 --> 00:04:26,733 +в Швейцарии и преподавал науку о данных в университете. diff --git a/subtitles/ru/01_the-pipeline-function.srt b/subtitles/ru/01_the-pipeline-function.srt new file mode 100644 index 000000000..311e5dfe9 --- /dev/null +++ b/subtitles/ru/01_the-pipeline-function.srt @@ -0,0 +1,400 @@ +1 +00:00:00,069 --> 00:00:01,341 + + +2 +00:00:01,341 --> 00:00:02,449 + + +3 +00:00:02,449 --> 00:00:05,880 + + +4 +00:00:05,880 --> 00:00:07,080 +- Функция pipeline (конвеер). + +5 +00:00:09,540 --> 00:00:12,020 +Функция pipeline является наиболее высокоуровневым API + +6 +00:00:12,020 --> 00:00:14,010 +библиотеки Transformers. + +7 +00:00:14,010 --> 00:00:16,050 +Она объединяет все шаги + +8 +00:00:16,050 --> 00:00:18,873 +перехода от необработанных текстов к пригодным для использования прогнозам. + +9 +00:00:20,228 --> 00:00:22,980 +Используемая модель лежит в основе конвейера, + +10 +00:00:22,980 --> 00:00:24,390 +но конвейер также включает + +11 +00:00:24,390 --> 00:00:26,610 +всю необходимую пред-обработку, + +12 +00:00:26,610 --> 00:00:30,240 +поскольку модель ожидает не тексты, а числа, + +13 +00:00:30,240 --> 00:00:32,040 +а также некоторую постобработку, + +14 +00:00:32,040 --> 00:00:34,533 +чтобы сделать вывод модели человекочитаемым. + +15 +00:00:35,910 --> 00:00:37,593 +Давайте рассмотрим первый пример + +16 +00:00:37,593 --> 00:00:39,693 +с конвейером анализа настроений. + +17 +00:00:40,740 --> 00:00:44,670 +Этот конвейер выполняет классификацию текста на заданном входе + +18 +00:00:44,670 --> 00:00:46,953 +и определяет, является ли он позитивным или негативным. + +19 +00:00:47,910 --> 00:00:51,750 +Здесь он приписывает положительную оценку данному тексту + +20 +00:00:51,750 --> 00:00:54,413 +с достоверностью 95%. + +21 +00:00:55,650 --> 00:00:58,470 +Вы можете передать множество текстов в один конвейер, + +22 +00:00:58,470 --> 00:01:00,270 +которые будут обработаны и переданы + +23 +00:01:00,270 --> 00:01:02,673 +через модель вместе как батч (пакет). + +24 +00:01:03,570 --> 00:01:05,970 +На выходе получается список отдельных результатов + +25 +00:01:05,970 --> 00:01:07,923 +в том же порядке, что и входные тексты. + +26 +00:01:08,790 --> 00:01:12,270 +Здесь мы находим ту же метку и оценку для первого текста, + +27 +00:01:12,270 --> 00:01:14,443 +а второй текст оценивается как отрицательный + +28 +00:01:14,443 --> 00:01:17,243 +с достоверностью 99,9%. + +29 +00:01:18,720 --> 00:01:20,700 +Конвейер zero-shot классификации + +30 +00:01:20,700 --> 00:01:23,610 +это более общий конвейер классификации текста, + +31 +00:01:23,610 --> 00:01:26,370 +он позволяет вам предоставлять нужные метки. + +32 +00:01:26,370 --> 00:01:29,850 +Здесь мы хотим классифицировать наш входной текст по меткам, + +33 +00:01:29,850 --> 00:01:32,643 +образование, политика и бизнес. + +34 +00:01:33,540 --> 00:01:35,580 +Конвейер успешно распознает + +35 +00:01:35,580 --> 00:01:38,280 +это скорее образование, чем другие метки, + +36 +00:01:38,280 --> 00:01:40,643 +с достоверностью 84%. + +37 +00:01:41,670 --> 00:01:43,110 +Переходим к другим задачам, + +38 +00:01:43,110 --> 00:01:45,030 +конвейер генерации текста будет + +39 +00:01:45,030 --> 00:01:46,533 +автоматически заполнять заданную подсказку. + +40 +00:01:47,460 --> 00:01:49,980 +Вывод генерируется с некоторой долей случайности, + +41 +00:01:49,980 --> 00:01:52,800 +поэтому он меняется каждый раз, когда вы вызываете объект генератора + +42 +00:01:52,800 --> 00:01:53,763 +для заданной строки. + +43 +00:01:54,990 --> 00:01:57,123 +До сих пор мы использовали API конвейера + +44 +00:01:57,123 --> 00:02:00,360 +с моделью по умолчанию, связанной с каждой задачей, + +45 +00:02:00,360 --> 00:02:02,880 +но вы можете использовать его с любой моделью, которая была предварительно обучена + +46 +00:02:02,880 --> 00:02:04,263 +или дообучена на этой задаче. + +47 +00:02:06,540 --> 00:02:10,350 +Зайдя в хаб моделей, huggingface.co/models + +48 +00:02:10,350 --> 00:02:13,350 +вы можете отфильтровать доступные модели по задаче. + +49 +00:02:13,350 --> 00:02:17,190 +Модель по умолчанию, использованная в нашем предыдущем примере, была gpt2, + +50 +00:02:17,190 --> 00:02:19,290 +но существует множество других моделей, + +51 +00:02:19,290 --> 00:02:20,523 +и не только на английском языке. + +52 +00:02:21,450 --> 00:02:23,670 +Давайте вернемся к конвейеру генерации текста + +53 +00:02:23,670 --> 00:02:26,193 +и загрузим в него другую модель, distilgpt2. + +54 +00:02:27,060 --> 00:02:28,950 +Это облегченная версия gpt2 + +55 +00:02:28,950 --> 00:02:30,603 +созданная командой Hugging Face. + +56 +00:02:31,740 --> 00:02:34,110 +При применении конвейера к данной строке, + +57 +00:02:34,110 --> 00:02:36,360 +мы можем указать несколько аргументов + +58 +00:02:36,360 --> 00:02:39,240 +такие как максимальная длина генерируемых текстов, + +59 +00:02:39,240 --> 00:02:41,700 +или количество предложений, которые мы хотим вернуть, + +60 +00:02:41,700 --> 00:02:44,150 +поскольку в процессе генерации присутствует некоторая случайность. + +61 +00:02:46,080 --> 00:02:48,750 +Генерирование текстов путем угадывания следующего слова в предложении + +62 +00:02:48,750 --> 00:02:51,450 +было целью предварительного обучения в GPT-2. + +63 +00:02:51,450 --> 00:02:55,140 +Конвейер заполнения маски является целью предварительного обучения BERT, + +64 +00:02:55,140 --> 00:02:57,363 +которая заключается в угадывании значения замаскированного слова. + +65 +00:02:58,260 --> 00:03:01,020 +В этом случае мы спрашиваем два наиболее вероятных значения + +66 +00:03:01,020 --> 00:03:03,660 +для пропущенных слов, согласно модели, + +67 +00:03:03,660 --> 00:03:07,053 +и получаем в качестве возможных вариантов ответов "mathematical" или "computational". + +68 +00:03:08,280 --> 00:03:10,170 +Еще одна задача, которую может выполнить модель Transformers + +69 +00:03:10,170 --> 00:03:12,660 +классифицировать каждое слово в предложении + +70 +00:03:12,660 --> 00:03:14,970 +вместо предложения в целом. + +71 +00:03:14,970 --> 00:03:18,390 +Одним из примеров этого является Named Entity Recognition (распознавание именованных сущностей), + +72 +00:03:18,390 --> 00:03:20,820 +которая представляет собой задачу идентификации сущностей, + +73 +00:03:20,820 --> 00:03:25,323 +таких как люди, организации или места в предложении. + +74 +00:03:26,400 --> 00:03:30,570 +Здесь модель правильно находит персону, "Sylvain", + +75 +00:03:30,570 --> 00:03:32,453 +организацию, "Hugging Face", + +76 +00:03:32,453 --> 00:03:35,010 +а также местоположение, "Brooklyn", + +77 +00:03:35,010 --> 00:03:36,303 +внутри входного текста. + +78 +00:03:37,661 --> 00:03:40,230 +Аргумент grouped_entities=True используется + +79 +00:03:40,230 --> 00:03:42,330 +для того, чтобы заставить конвейер сгруппировать + +80 +00:03:42,330 --> 00:03:44,790 +различные слова, связанные с одним и тем же объектом, + +81 +00:03:44,790 --> 00:03:46,353 +например, "Hugging" и "Face". + +82 +00:03:48,270 --> 00:03:50,670 +Еще одна задача, доступная с помощью API конвейера + +83 +00:03:50,670 --> 00:03:52,920 +является extractive question answering (экстрактивный ответ на вопрос). + +84 +00:03:52,920 --> 00:03:55,380 +Предоставляется контекст и вопрос, + +85 +00:03:55,380 --> 00:03:58,290 +модель определит участок текста в контексте + +86 +00:03:58,290 --> 00:04:00,190 +содержащий ответ на вопрос. + +87 +00:04:01,650 --> 00:04:03,960 +Получение кратких резюме очень длинных статей + +88 +00:04:03,960 --> 00:04:06,540 +это то, с чем также может помочь библиотека Transformers, + +89 +00:04:06,540 --> 00:04:08,140 +с конвейером суммаризации. + +90 +00:04:09,480 --> 00:04:12,570 +Наконец, последняя задача, поддерживаемая API конвейера + +91 +00:04:12,570 --> 00:04:14,130 +это перевод. + +92 +00:04:14,130 --> 00:04:16,170 +Здесь мы используем французско-английскую модель + +93 +00:04:16,170 --> 00:04:17,460 +найденную в хабе моделей + +94 +00:04:17,460 --> 00:04:19,893 +для получения английской версии нашего входного текста. + +95 +00:04:21,600 --> 00:04:23,490 +Вот краткий обзор всех задач + +96 +00:04:23,490 --> 00:04:25,500 +которые мы рассмотрели в этом видео. + +97 +00:04:25,500 --> 00:04:27,390 +Попробуйте использовать виджеты инференса + +98 +00:04:27,390 --> 00:04:28,327 +в хабе моделей. + +99 +00:04:30,459 --> 00:04:33,475 + + +100 +00:04:33,475 --> 00:04:35,175 + + diff --git a/subtitles/ru/02_the-carbon-footprint-of-transformers.srt b/subtitles/ru/02_the-carbon-footprint-of-transformers.srt new file mode 100644 index 000000000..bbe7ff90e --- /dev/null +++ b/subtitles/ru/02_the-carbon-footprint-of-transformers.srt @@ -0,0 +1,516 @@ +1 +00:00:05,580 --> 00:00:08,820 +- Итак, давайте поговорим об углеродном следе трансформеров. + +2 +00:00:08,820 --> 00:00:10,530 +Возможно, вы видели заголовки, подобные этому + +3 +00:00:10,530 --> 00:00:13,530 +что обучение одной модели ИИ может выбросить столько углерода, + +4 +00:00:13,530 --> 00:00:16,020 +сколько пять автомобилей за весь срок службы. + +5 +00:00:16,020 --> 00:00:19,440 +Так когда же это правда и всегда ли это так? + +6 +00:00:19,440 --> 00:00:21,803 +На самом деле, это зависит от нескольких вещей. + +7 +00:00:21,803 --> 00:00:23,430 +Самое главное, это зависит + +8 +00:00:23,430 --> 00:00:24,960 +от типа энергии, которую вы используете. + +9 +00:00:24,960 --> 00:00:26,267 +Если вы используете возобновляемые источники энергии, такие как + +10 +00:00:26,267 --> 00:00:30,670 +солнце, ветер, гидроэлектроэнергия, вы действительно + +11 +00:00:30,670 --> 00:00:33,810 +не выбрасываете углерод вообще, очень, очень мало. + +12 +00:00:33,810 --> 00:00:36,769 +Если вы используете невозобновляемые источники энергии, такие как уголь + +13 +00:00:36,769 --> 00:00:39,570 +то их углеродный след намного выше + +14 +00:00:39,570 --> 00:00:43,260 +потому что, по сути, вы выделяете большое количество парниковых газов. + +15 +00:00:43,260 --> 00:00:44,670 +Другой аспект - время обучения. + +16 +00:00:44,670 --> 00:00:47,232 +Поэтому чем дольше вы обучаете, тем больше энергии вы используете, + +17 +00:00:47,232 --> 00:00:50,250 +тем больше углерода вы выбрасываете, верно? + +18 +00:00:50,250 --> 00:00:51,270 +Таким образом, это действительно увеличивает + +19 +00:00:51,270 --> 00:00:53,520 +особенно если вы тренируете большие модели + +20 +00:00:53,520 --> 00:00:56,460 +в течение часов, дней и недель. + +21 +00:00:56,460 --> 00:00:58,380 +Используемое вами оборудование также имеет значение + +22 +00:00:58,380 --> 00:01:00,930 +потому что некоторые GPU, например, более эффективны + +23 +00:01:00,930 --> 00:01:05,460 +чем другие и правильно используют эффективность. + +24 +00:01:05,460 --> 00:01:07,500 +Поэтому их постоянное использование на сто процентов + +25 +00:01:07,500 --> 00:01:10,650 +может реально снизить потребление энергии. + +26 +00:01:10,650 --> 00:01:13,290 +И опять же, уменьшить углеродный след. + +27 +00:01:13,290 --> 00:01:15,870 +Есть и другие аспекты, такие как ввод/вывод, + +28 +00:01:15,870 --> 00:01:17,730 +такие как данные, и так далее, и тому подобное. + +29 +00:01:17,730 --> 00:01:20,940 +Но это основные три, на которых вам следует сосредоточиться. + +30 +00:01:20,940 --> 00:01:23,340 +Поэтому, когда я говорю об источниках энергии и углеродоемкости, + +31 +00:01:23,340 --> 00:01:24,420 +что это означает на самом деле? + +32 +00:01:24,420 --> 00:01:27,480 +Итак, если вы посмотрите на верхнюю часть экрана, вы + +33 +00:01:27,480 --> 00:01:30,480 +вы увидите углеродный след + +34 +00:01:30,480 --> 00:01:33,860 +облачного вычислительного центра в Мумбаи, Индия, + +35 +00:01:33,860 --> 00:01:38,700 +который выбрасывает 920 граммов CO2 на киловатт-час. + +36 +00:01:38,700 --> 00:01:40,110 +Это почти один килограмм + +37 +00:01:40,110 --> 00:01:43,680 +CO2 на киловатт-час используемой электроэнергии. + +38 +00:01:43,680 --> 00:01:45,150 +Если сравнить с Канадой, Монреалем, + +39 +00:01:45,150 --> 00:01:48,720 +где я сейчас нахожусь, то 20 граммов CO2 на килограмм-час. + +40 +00:01:48,720 --> 00:01:50,040 +Так что это очень, очень большая разница. + +41 +00:01:50,040 --> 00:01:54,240 +Почти в 40 раз больше углерода выбрасывается + +42 +00:01:54,240 --> 00:01:55,950 +в Мумбаи по сравнению с Монреалем. + +43 +00:01:55,950 --> 00:01:57,720 +И это может очень, очень сильно увеличиться. + +44 +00:01:57,720 --> 00:01:59,820 +Например, если вы обучаете модель в течение нескольких недель + +45 +00:01:59,820 --> 00:02:01,920 +вы умножаете в 40 раз + +46 +00:02:01,920 --> 00:02:03,450 +углерод, который вы выбрасываете. + +47 +00:02:03,450 --> 00:02:05,070 +Поэтому выбор правильного экземпляра + +48 +00:02:05,070 --> 00:02:07,080 +выбор низкоуглеродного компьютерного экземпляра + +49 +00:02:07,080 --> 00:02:09,690 +это действительно самая важная вещь, которую вы можете сделать. + +50 +00:02:09,690 --> 00:02:13,020 +И вот тут-то и может возникнуть реальная проблема + +51 +00:02:13,020 --> 00:02:15,930 +если вы обучаете в очень интенсивном + +52 +00:02:15,930 --> 00:02:17,580 +регионе с высоким выбросом углерода + +53 +00:02:19,170 --> 00:02:21,750 +другие элементы, которые следует рассмотреть, например + +54 +00:02:21,750 --> 00:02:22,770 +использование предварительно обученных моделей + +55 +00:02:22,770 --> 00:02:25,590 +это эквивалент повторного использования в машинном обучении. + +56 +00:02:25,590 --> 00:02:28,292 +Когда у вас есть предварительно обученные модели, используя их, + +57 +00:02:28,292 --> 00:02:30,120 +вы вообще не выбрасываете углерод, верно? + +58 +00:02:30,120 --> 00:02:31,230 +Вы ничего не переобучаете. + +59 +00:02:31,230 --> 00:02:33,450 +Так что это еще и выполнение домашней работы + +60 +00:02:33,450 --> 00:02:35,574 +и изучие того, что уже существует. + +61 +00:02:35,574 --> 00:02:37,890 +Дообучение вместо обучения с нуля. + +62 +00:02:37,890 --> 00:02:38,723 +Поэтому еще раз, + +63 +00:02:38,723 --> 00:02:40,590 +если вы нашли модель, которая почти то, что вам нужно, + +64 +00:02:40,590 --> 00:02:43,530 +но не совсем, то добучение последней пары слоев, + +65 +00:02:43,530 --> 00:02:45,210 +чтобы она действительно соответствовала вашей цели, вместо того, + +66 +00:02:45,210 --> 00:02:46,500 +чтобы обучать большой трансформер + +67 +00:02:46,500 --> 00:02:48,810 +с нуля, может действительно помочь, + +68 +00:02:48,810 --> 00:02:51,270 +начинайте с небольших экспериментов + +69 +00:02:51,270 --> 00:02:52,800 +и отлаживайте по ходу дела. + +70 +00:02:52,800 --> 00:02:54,630 +Это означает, что, например, + +71 +00:02:54,630 --> 00:02:58,770 +вы убедитесь в правильности кодировки данных, убедитесь в + +72 +00:02:58,770 --> 00:03:01,170 +том, что нет мелких ошибок, которые + +73 +00:03:01,170 --> 00:03:03,840 +могут появиться после 16 часов обучения, + +74 +00:03:03,840 --> 00:03:05,820 +начинайте с малого и убедитесь + +75 +00:03:05,820 --> 00:03:08,760 +в том, что то что вы делаете, что делает ваш код, является стабильным. + +76 +00:03:08,760 --> 00:03:11,430 +И, наконец, сделайте обзор литературы, + +77 +00:03:11,430 --> 00:03:13,740 +чтобы выбрать диапазоны гиперпараметров, а затем + +78 +00:03:13,740 --> 00:03:15,900 +выполнить случайный поиск вместо поиска по сетке. + +79 +00:03:15,900 --> 00:03:18,420 +Так, случайный поиск комбинаций гиперпараметров + +80 +00:03:18,420 --> 00:03:21,300 +на самом деле оказался столь же эффективным + +81 +00:03:21,300 --> 00:03:24,000 +в поиске оптимальной конфигурации, как и поиск по сетке. + +82 +00:03:24,000 --> 00:03:27,510 +Но очевидно, что вы не проверяете все возможные комбинации, + +83 +00:03:27,510 --> 00:03:29,520 +а только их подмножество. + +84 +00:03:29,520 --> 00:03:31,800 +Так что это тоже может помочь. + +85 +00:03:31,800 --> 00:03:32,760 +Итак, если мы вернемся + +86 +00:03:32,760 --> 00:03:36,300 +к оригинальной статье Струбелла и других в 2019 году + +87 +00:03:36,300 --> 00:03:39,180 +печально известной статье о пяти автомобилях за время их эксплуатации. + +88 +00:03:39,180 --> 00:03:40,013 +Если вы просто посмотрите + +89 +00:03:40,013 --> 00:03:43,606 +на трансформер с 200 миллионами параметров, + +90 +00:03:43,606 --> 00:03:46,950 +то его углеродный след составит около 200 фунтов CO2, + +91 +00:03:46,950 --> 00:03:47,940 +что значительно + +92 +00:03:47,940 --> 00:03:49,980 +но это не больше, чем у пяти автомобилей, верно? + +93 +00:03:49,980 --> 00:03:52,893 +Это даже не трансатлантический перелет. + +94 +00:03:52,893 --> 00:03:55,020 +Как это действительно увеличивается, когда вы делаете + +95 +00:03:55,020 --> 00:03:56,190 +поиск архитектуры нейронной сети, + +96 +00:03:56,190 --> 00:03:58,560 +когда вы делаете настройку гиперпараметров, и + +97 +00:03:58,560 --> 00:04:00,930 +это перебор всех возможных комбинаций + +98 +00:04:00,930 --> 00:04:01,763 +и так далее, и тому подобное. + +99 +00:04:01,763 --> 00:04:02,596 +И вот откуда + +100 +00:04:02,596 --> 00:04:05,400 +например, 600 000 фунтов CO2. + +101 +00:04:05,400 --> 00:04:08,490 +Так что здесь все действительно складывается. + +102 +00:04:08,490 --> 00:04:11,880 +Итак, если вы поступаете разумно и осознанно, + +103 +00:04:11,880 --> 00:04:16,410 +то ваш углеродный след не будет таким большим, как + +104 +00:04:16,410 --> 00:04:20,040 +предполагалось в статье, а некоторые инструменты помогут вам + +105 +00:04:20,040 --> 00:04:22,111 +определить, сколько CO2 выбрасываете именно вы. + +106 +00:04:22,111 --> 00:04:24,270 +Существует веб-инструмент под названием machine + +107 +00:04:24,270 --> 00:04:26,430 +learning submissions calculator, который позволяет вам + +108 +00:04:26,430 --> 00:04:29,010 +вручную ввести, например, какое оборудование вы использовали, + +109 +00:04:29,010 --> 00:04:30,488 +сколько часов вы его использовали, + +110 +00:04:30,488 --> 00:04:34,260 +где оно было расположено - локально или в облаке. + +111 +00:04:34,260 --> 00:04:35,640 +И затем он даст вам оценку того, + +112 +00:04:35,640 --> 00:04:37,560 +сколько CO2 вы выбросили. + +113 +00:04:37,560 --> 00:04:40,200 +Другой инструмент, который делает это программно, + +114 +00:04:40,200 --> 00:04:41,190 +называется Code Carbon. + +115 +00:04:41,190 --> 00:04:45,112 +Поэтому вы можете установить его с помощью PIP, вы можете зайти на GitHub + +116 +00:04:45,112 --> 00:04:48,120 +и, по сути, он работает параллельно с вашим кодом. + +117 +00:04:48,120 --> 00:04:49,085 +Так что, по сути, вы вызываете его + +118 +00:04:49,085 --> 00:04:51,060 +и затем проводите все свое обучение. + +119 +00:04:51,060 --> 00:04:53,760 +И в конце он предоставит вам оценку + +120 +00:04:53,760 --> 00:04:57,210 +CSV-файл с оценкой ваших выбросов. + +121 +00:04:57,210 --> 00:04:59,250 +И он даст вам несколько сравнений. + +122 +00:04:59,250 --> 00:05:01,230 +У него есть визуальный пользовательский интерфейс, где вы можете реально посмотреть + +123 +00:05:01,230 --> 00:05:04,680 +как это сравнимо с вождением автомобиля или просмотром телевизора. + +124 +00:05:04,680 --> 00:05:06,060 +Так что это может дать вам представление + +125 +00:05:06,060 --> 00:05:07,740 +о масштабах ваших выбросов. + +126 +00:05:07,740 --> 00:05:09,930 +И на самом деле, code carbon уже интегрирован в AutoML, + +127 +00:05:09,930 --> 00:05:12,270 +и, надеюсь, люди будут использовать его + +128 +00:05:12,270 --> 00:05:15,240 +из коробки и легко отслеживать свои выбросы на протяжении всего + +129 +00:05:15,240 --> 00:05:17,523 +процесса обучения и внедрения трансформеров. + diff --git a/subtitles/ru/03_what-is-transfer-learning.srt b/subtitles/ru/03_what-is-transfer-learning.srt new file mode 100644 index 000000000..5ca2b54ba --- /dev/null +++ b/subtitles/ru/03_what-is-transfer-learning.srt @@ -0,0 +1,360 @@ +1 +00:00:00,189 --> 00:00:02,856 + + +2 +00:00:05,550 --> 00:00:07,293 +Что такое трансфертное обучение? + +3 +00:00:09,480 --> 00:00:10,920 +Идея трансферного обучения + +4 +00:00:10,920 --> 00:00:12,570 +состоит в том, чтобы использовать знания, полученные + +5 +00:00:12,570 --> 00:00:15,543 +моделью, обученной на большом количестве данных для другой задачи. + +6 +00:00:16,410 --> 00:00:20,130 +Модель A будет обучена специально для задачи A. + +7 +00:00:20,130 --> 00:00:22,200 +Теперь предположим, что вы хотите обучить модель B + +8 +00:00:22,200 --> 00:00:23,970 +для другой задачи. + +9 +00:00:23,970 --> 00:00:27,330 +Одним из вариантов может быть обучение модели с нуля. + +10 +00:00:27,330 --> 00:00:30,633 +Это может потребовать большого количества вычислений, времени и данных. + +11 +00:00:31,470 --> 00:00:34,260 +Вместо этого мы можем инициализировать модель B + +12 +00:00:34,260 --> 00:00:36,570 +с теми же весами, что и модель A, + +13 +00:00:36,570 --> 00:00:39,213 +передавая знания модели A на задачу B. + +14 +00:00:41,040 --> 00:00:42,690 +При обучении с нуля, + +15 +00:00:42,690 --> 00:00:45,870 +все веса модели инициализируются случайным образом. + +16 +00:00:45,870 --> 00:00:48,870 +В этом примере мы обучаем модель BERT + +17 +00:00:48,870 --> 00:00:50,220 +на задаче распознавания того, + +18 +00:00:50,220 --> 00:00:52,203 +похожи или нет два предложения. + +19 +00:00:54,116 --> 00:00:56,730 +Слева - обучение с нуля, + +20 +00:00:56,730 --> 00:01:00,000 +а справа - дообучение предварительно обученной модели. + +21 +00:01:00,000 --> 00:01:02,220 +Как мы видим, использование трансфертного обучения + +22 +00:01:02,220 --> 00:01:05,160 +и предварительно обученной модели дает лучшие результаты. + +23 +00:01:05,160 --> 00:01:07,140 +И неважно, будем ли мы обучать дольше. + +24 +00:01:07,140 --> 00:01:10,620 +Точность обучения с нуля составляет около 70%, + +25 +00:01:10,620 --> 00:01:13,293 +в то время как предварительно обученная модель легко преодолевает отметку в 86%. + +26 +00:01:14,460 --> 00:01:16,140 +Это связано с тем, что предварительно обученные модели + +27 +00:01:16,140 --> 00:01:18,420 +обычно обучаются на больших объемах данных + +28 +00:01:18,420 --> 00:01:21,000 +которые обеспечивают модели статистическое понимание + +29 +00:01:21,000 --> 00:01:23,413 +языка, используемого во время предварительного обучения. + +30 +00:01:24,450 --> 00:01:25,950 +В компьютерном зрении + +31 +00:01:25,950 --> 00:01:28,080 +трансфертное обучение успешно применяется + +32 +00:01:28,080 --> 00:01:30,060 +уже почти десять лет. + +33 +00:01:30,060 --> 00:01:32,850 +Модели часто предварительно обучаются на наборе данных ImageNet, + +34 +00:01:32,850 --> 00:01:36,153 +содержащем 1,2 миллиона фотографий. + +35 +00:01:37,170 --> 00:01:41,130 +Каждое изображение классифицируется по одной из 1000 меток. + +36 +00:01:41,130 --> 00:01:44,010 +Подобное обучение на размеченных данных + +37 +00:01:44,010 --> 00:01:45,663 +называется обучением с учителем. + +38 +00:01:47,340 --> 00:01:49,140 +В обработке естественного языка (NLP), + +39 +00:01:49,140 --> 00:01:51,870 +трансфертное обучение появилось совсем недавно. + +40 +00:01:51,870 --> 00:01:54,480 +Ключевое отличие от ImageNet заключается в том, что предварительное обучение + +41 +00:01:54,480 --> 00:01:56,460 +обычно осуществляется самостоятельно, + +42 +00:01:56,460 --> 00:01:58,770 +что означает, что оно не требует аннотации от человека + +43 +00:01:58,770 --> 00:01:59,673 +для меток. + +44 +00:02:00,780 --> 00:02:02,700 +Очень распространенной целью предварительного обучения + +45 +00:02:02,700 --> 00:02:05,310 +является угадывание следующего слова в предложении. + +46 +00:02:05,310 --> 00:02:07,710 +Для этого нужно только много-много текста. + +47 +00:02:07,710 --> 00:02:10,710 +Например, GPT-2 была предварительно обучена таким образом + +48 +00:02:10,710 --> 00:02:12,900 +используя содержание 45 миллионов ссылок + +49 +00:02:12,900 --> 00:02:14,673 +размещенных пользователями в Reddit. + +50 +00:02:16,560 --> 00:02:19,590 +Другим примером задачи предварительного cамообучения под наблюдением + +51 +00:02:19,590 --> 00:02:22,470 +является предсказание значения случайно замаскированных слов. + +52 +00:02:22,470 --> 00:02:24,540 +Это похоже на тесты "заполни пустое место", + +53 +00:02:24,540 --> 00:02:26,760 +которые вы, возможно, выполняли в школе. + +54 +00:02:26,760 --> 00:02:29,880 +BERT был предварительно обучен таким образом, используя английскую Википедию + +55 +00:02:29,880 --> 00:02:31,893 +и 11 000 неопубликованных книг. + +56 +00:02:33,120 --> 00:02:36,450 +На практике трансферное обучение применяется к заданной модели + +57 +00:02:36,450 --> 00:02:39,090 +путем отбрасывания ее головы, + +58 +00:02:39,090 --> 00:02:42,150 +то есть последних слоев, сфокусированных на цели предварительного обучения, + +59 +00:02:42,150 --> 00:02:45,360 +и замены ее новой, случайно инициализированной головой, + +60 +00:02:45,360 --> 00:02:46,860 +подходящей для поставленной задачи. + +61 +00:02:47,970 --> 00:02:51,570 +Например, когда мы ранее проводили дообучение модели BERT, + +62 +00:02:51,570 --> 00:02:54,060 +мы удалили голову, которая классифицировала слова-маски, + +63 +00:02:54,060 --> 00:02:56,790 +и заменили ее классификатором с двумя выходами. + +64 +00:02:56,790 --> 00:02:58,563 +Поскольку наша задача имеет две метки. + +65 +00:02:59,700 --> 00:03:02,490 +Чтобы быть максимально эффективной, используемая предварительно обученная модель + +66 +00:03:02,490 --> 00:03:03,770 +должна быть максимально похожа + +67 +00:03:03,770 --> 00:03:06,270 +на задачу, для которой она дообучается. + +68 +00:03:06,270 --> 00:03:08,190 +Например, если проблема + +69 +00:03:08,190 --> 00:03:10,860 +состоит в классификации немецких предложений, + +70 +00:03:10,860 --> 00:03:13,053 +лучше всего использовать предварительно обученную немецкую модель. + +71 +00:03:14,370 --> 00:03:16,649 +Но вместе с хорошим приходит и плохое. + +72 +00:03:16,649 --> 00:03:19,380 +Предварительно обученная модель передает не только свои знания, + +73 +00:03:19,380 --> 00:03:21,693 +но и любую предвзятость, которую она может содержать. + +74 +00:03:22,530 --> 00:03:24,300 +ImageNet в основном содержит изображения + +75 +00:03:24,300 --> 00:03:26,850 +из Соединенных Штатов и Западной Европы. + +76 +00:03:26,850 --> 00:03:28,020 +Поэтому модели, дообученные с его помощью + +77 +00:03:28,020 --> 00:03:31,710 +обычно лучше работают с изображениями из этих стран. + +78 +00:03:31,710 --> 00:03:33,690 +OpenAI также изучил смещение + +79 +00:03:33,690 --> 00:03:36,120 +в прогнозах своей модели GPT-3 + +80 +00:03:36,120 --> 00:03:36,953 +которая была предварительно обучена + +81 +00:03:36,953 --> 00:03:38,750 +с использованием задачи "Угадай следующее слово". + +82 +00:03:39,720 --> 00:03:41,040 +Изменение пола в строке подсказке + +83 +00:03:41,040 --> 00:03:44,250 +с "He was very" на "She was very" + +84 +00:03:44,250 --> 00:03:47,550 +изменило предсказания с преимущественно нейтральных прилагательных + +85 +00:03:47,550 --> 00:03:49,233 +на почти только физические. + +86 +00:03:50,400 --> 00:03:52,367 +В карточке модели GPT-2 + +87 +00:03:52,367 --> 00:03:54,990 +OpenAI также признает ее необъективность + +88 +00:03:54,990 --> 00:03:56,730 +и не рекомендует использовать ее + +89 +00:03:56,730 --> 00:03:58,803 +в системах, взаимодействующих с людьми. + +90 +00:04:01,040 --> 00:04:03,707 + + diff --git a/subtitles/ru/04_the-transformer-architecture.srt b/subtitles/ru/04_the-transformer-architecture.srt new file mode 100644 index 000000000..bc8dd997f --- /dev/null +++ b/subtitles/ru/04_the-transformer-architecture.srt @@ -0,0 +1,256 @@ +1 +00:00:00,000 --> 00:00:02,750 + + +2 +00:00:05,010 --> 00:00:07,323 +- Давайте изучим архитектуру трансформера. + +3 +00:00:09,150 --> 00:00:12,030 +Этот видео является вводным в серию видео о кодерах, + +4 +00:00:12,030 --> 00:00:15,510 +декодерах и кодер-декодерах. + +5 +00:00:15,510 --> 00:00:16,343 +В этой серии, + +6 +00:00:16,343 --> 00:00:18,900 +мы попытаемся понять, что представляет собой трансформерная сеть, + +7 +00:00:18,900 --> 00:00:22,770 +и постараемся объяснить это простыми, высокоуровневыми терминами. + +8 +00:00:22,770 --> 00:00:25,800 +Понимание нейронных сетей не требуется, + +9 +00:00:25,800 --> 00:00:29,343 +может помочь только понимание основ векторов и тензоров. + +10 +00:00:32,250 --> 00:00:33,270 +Для начала + +11 +00:00:33,270 --> 00:00:34,530 +мы возьмем эту диаграмму + +12 +00:00:34,530 --> 00:00:36,630 +из оригинальной статьи о трансформерах, + +13 +00:00:36,630 --> 00:00:40,140 +озаглавленной "Внимание - все, что вам нужно". + +14 +00:00:40,140 --> 00:00:41,010 +Как мы увидим здесь, + +15 +00:00:41,010 --> 00:00:42,780 +мы можем использовать только некоторые его части, + +16 +00:00:42,780 --> 00:00:44,630 +в зависимости от того, что мы пытаемся сделать. + +17 +00:00:45,480 --> 00:00:47,610 +Мы не будем углубляться в конкретные слои, + +18 +00:00:47,610 --> 00:00:48,990 +составляющие эту архитектуру, + +19 +00:00:48,990 --> 00:00:51,390 +но попытаемся понять различные способы + +20 +00:00:51,390 --> 00:00:52,893 +использования этой архитектуры. + +21 +00:00:55,170 --> 00:00:56,003 +Для начала давайте + +22 +00:00:56,003 --> 00:00:58,260 +разделим эту архитектуру на две части. + +23 +00:00:58,260 --> 00:00:59,910 +Слева находится кодер, + +24 +00:00:59,910 --> 00:01:01,980 +а справа - декодер. + +25 +00:01:01,980 --> 00:01:03,330 +Их можно использовать вместе, + +26 +00:01:03,330 --> 00:01:05,330 +но можно и независимо. + +27 +00:01:06,180 --> 00:01:08,610 +Давайте разберемся, как они работают. + +28 +00:01:08,610 --> 00:01:11,460 +Кодер принимает входные данные, представляющие собой текст. + +29 +00:01:11,460 --> 00:01:13,620 +Он преобразует этот текст, эти слова, + +30 +00:01:13,620 --> 00:01:15,675 +в числовые представления. + +31 +00:01:15,675 --> 00:01:17,400 +Эти числовые представления + +32 +00:01:17,400 --> 00:01:20,460 +могут также называться эмбеддингами, или признаками. + +33 +00:01:20,460 --> 00:01:23,100 +Мы увидим, что он использует механизм самовнимания + +34 +00:01:23,100 --> 00:01:24,483 +в качестве основного компонента. + +35 +00:01:25,500 --> 00:01:27,120 +Мы рекомендуем вам посмотреть видео + +36 +00:01:27,120 --> 00:01:29,700 +о кодерах специально для того, чтобы понять + +37 +00:01:29,700 --> 00:01:31,680 +что такое это числовое представление, + +38 +00:01:31,680 --> 00:01:33,690 +а также как оно работает. + +39 +00:01:33,690 --> 00:01:36,660 +Мы изучим механизм самовнимания более подробно, + +40 +00:01:36,660 --> 00:01:38,913 +а также его двунаправленные свойства. + +41 +00:01:40,650 --> 00:01:42,780 +Декодер аналогичен кодеру. + +42 +00:01:42,780 --> 00:01:45,630 +Он также может принимать текстовые входы. + +43 +00:01:45,630 --> 00:01:48,210 +Он использует аналогичный механизм, что и кодер, + +44 +00:01:48,210 --> 00:01:51,150 +который также является маскированным самовниманием. + +45 +00:01:51,150 --> 00:01:52,590 +Он отличается от кодера + +46 +00:01:52,590 --> 00:01:54,990 +своим однонаправленным свойством + +47 +00:01:54,990 --> 00:01:58,590 +и традиционно используется в авторегрессионной манере. + +48 +00:01:58,590 --> 00:02:01,650 +Здесь мы также рекомендуем вам посмотреть видео о декодерах, + +49 +00:02:01,650 --> 00:02:04,000 +особенно для того, чтобы понять, как все это работает. + +50 +00:02:06,810 --> 00:02:07,890 +Комбинирование этих двух частей + +51 +00:02:07,890 --> 00:02:10,200 +дает так называемый кодер-декодер, + +52 +00:02:10,200 --> 00:02:12,720 +или трансформер последовательности в последовательность. + +53 +00:02:12,720 --> 00:02:14,280 +Кодер принимает входные данные + +54 +00:02:14,280 --> 00:02:17,850 +и вычисляет высокоуровневое представление этих входов. + +55 +00:02:17,850 --> 00:02:20,252 +Эти выходы затем передаются в декодер. + +56 +00:02:20,252 --> 00:02:22,860 +Декодер использует выход кодера, + +57 +00:02:22,860 --> 00:02:26,370 +наряду с другими входными данными для создания прогноза. + +58 +00:02:26,370 --> 00:02:27,900 +Затем он прогнозирует выход, + +59 +00:02:27,900 --> 00:02:30,248 +который он будет повторно использовать в будущих итерациях, + +60 +00:02:30,248 --> 00:02:32,662 +отсюда и термин "авторегрессивный". + +61 +00:02:32,662 --> 00:02:34,740 +Наконец, чтобы получить представление + +62 +00:02:34,740 --> 00:02:36,690 +о кодерах-декодерах в целом, + +63 +00:02:36,690 --> 00:02:39,670 +мы рекомендуем вам ознакомиться с видео о кодерах-декодерах. + +64 +00:02:39,670 --> 00:02:42,420 + + diff --git a/subtitles/ru/05_transformer-models-encoders.srt b/subtitles/ru/05_transformer-models-encoders.srt new file mode 100644 index 000000000..0f5ee4cf3 --- /dev/null +++ b/subtitles/ru/05_transformer-models-encoders.srt @@ -0,0 +1,407 @@ +1 +00:00:00,253 --> 00:00:03,003 + +2 +00:00:04,440 --> 00:00:07,830 +- В этом видео мы изучим архитектуру кодера. + +3 +00:00:07,830 --> 00:00:11,070 +Примером популярной архитектуры, использующей только кодер, является BERT, + +4 +00:00:11,070 --> 00:00:13,323 +который является самой популярной моделью такого рода. + +5 +00:00:14,550 --> 00:00:16,950 +Для начала давайте разберемся, как это работает. + +6 +00:00:18,360 --> 00:00:20,910 +Мы воспользуемся небольшим примером, используя три слова. + +7 +00:00:20,910 --> 00:00:23,823 +Мы используем их в качестве входных данных и пропустим через кодер. + +8 +00:00:25,290 --> 00:00:28,173 +Мы получили числовое представление каждого слова. + +9 +00:00:29,970 --> 00:00:32,700 +Вот, например, кодер преобразует три слова + +10 +00:00:32,700 --> 00:00:37,350 +"Welcome to NYC" в эти три последовательности цифр. + +11 +00:00:37,350 --> 00:00:40,350 +Кодер выдает ровно одну последовательность чисел + +12 +00:00:40,350 --> 00:00:41,493 +на каждое входное слово. + +13 +00:00:42,330 --> 00:00:44,880 +Это числовое представление можно также назвать + +14 +00:00:44,880 --> 00:00:47,163 +вектор признаков или тензор признаков. + +15 +00:00:49,080 --> 00:00:51,030 +Давайте погрузимся в это представление. + +16 +00:00:51,030 --> 00:00:52,740 +Он содержит один вектор на каждое слово + +17 +00:00:52,740 --> 00:00:54,540 +которое было пропущено через кодер. + +18 +00:00:56,130 --> 00:00:58,620 +Каждый из этих векторов является числовым представлением + +19 +00:00:58,620 --> 00:01:00,033 +рассматриваемого слова. + +20 +00:01:01,080 --> 00:01:03,300 +Размерность этого вектора определяется + +21 +00:01:03,300 --> 00:01:05,520 +архитектурой модели. + +22 +00:01:05,520 --> 00:01:08,703 +Для базовой модели BERT это значение равно 768. + +23 +00:01:10,650 --> 00:01:13,230 +Эти представления содержат значение слова, + +24 +00:01:13,230 --> 00:01:15,240 +но с учетом контекста. + +25 +00:01:15,240 --> 00:01:18,570 +Например, вектор, приписываемый слову "to" + +26 +00:01:18,570 --> 00:01:22,290 +не является представлением только слова "to". + +27 +00:01:22,290 --> 00:01:25,650 +Он также учитывает окружающие слова, + +28 +00:01:25,650 --> 00:01:27,363 +которые мы называем контекстом. + +29 +00:01:28,650 --> 00:01:30,780 +Например, он смотрит на левый контекст, + +30 +00:01:30,780 --> 00:01:32,970 +слова слева от изучаемого нами, + +31 +00:01:32,970 --> 00:01:34,980 +здесь слово "Welcome", + +32 +00:01:34,980 --> 00:01:37,497 +и контекст справа, здесь слово "NYC", + +33 +00:01:38,348 --> 00:01:42,000 +и выводит значение для слова с учетом его контекста. + +34 +00:01:42,000 --> 00:01:45,420 +Таким образом, это контекстуализированное значение. + +35 +00:01:45,420 --> 00:01:48,810 +Можно сказать, что вектор из 768 значений + +36 +00:01:48,810 --> 00:01:51,993 +хранит значение слова в тексте. + +37 +00:01:53,310 --> 00:01:56,073 +Это происходит благодаря механизму самовнимания. + +38 +00:01:57,240 --> 00:02:00,630 +Механизм самовнимания связан с различными позициями, + +39 +00:02:00,630 --> 00:02:02,850 +или различным словам в одной последовательности + +40 +00:02:02,850 --> 00:02:06,003 +для того, чтобы вычислить представление этой последовательности. + +41 +00:02:07,200 --> 00:02:09,000 +Как мы уже видели, это означает, что + +42 +00:02:09,000 --> 00:02:11,130 +на результирующее представление слова + +43 +00:02:11,130 --> 00:02:13,983 +повлияли другие слова в последовательности. + +44 +00:02:15,840 --> 00:02:18,030 +Мы не будем углубляться в детали, + +45 +00:02:18,030 --> 00:02:19,680 +но предлагаем вам ознакомиться с некоторыми дополнительными материалами, + +46 +00:02:19,680 --> 00:02:21,330 +если вы хотите лучше понять, + +47 +00:02:21,330 --> 00:02:22,953 +что происходит под капотом. + +48 +00:02:25,050 --> 00:02:27,480 +Так почему же следует использовать кодер? + +49 +00:02:27,480 --> 00:02:29,370 +Кодеры могут использоваться в качестве автономных моделей + +50 +00:02:29,370 --> 00:02:31,263 +в самых разнообразных задачах. + +51 +00:02:32,100 --> 00:02:33,360 +Например, BERT, + +52 +00:02:33,360 --> 00:02:35,670 +возможно, самая известная модель трансформера, + +53 +00:02:35,670 --> 00:02:37,590 +является отдельной моделью кодера, + +54 +00:02:37,590 --> 00:02:38,820 +и на момент выпуска + +55 +00:02:38,820 --> 00:02:40,440 +она была передовой + +56 +00:02:40,440 --> 00:02:42,780 +во многих задачах классификации последовательностей, + +57 +00:02:42,780 --> 00:02:44,190 +задачах ответа на вопросы, + +58 +00:02:44,190 --> 00:02:46,743 +и моделировании языка с маской, и это лишь некоторые из них. + +59 +00:02:48,150 --> 00:02:50,460 +Идея заключается в том, что кодеры очень сильны + +60 +00:02:50,460 --> 00:02:52,470 +в извлечении векторов, которые несут + +61 +00:02:52,470 --> 00:02:55,350 +значимую информацию о последовательности. + +62 +00:02:55,350 --> 00:02:57,870 +Этот вектор затем может быть обработан в дальнейшем + +63 +00:02:57,870 --> 00:03:00,070 +дополнительными нейронами, чтобы понять его смысл. + +64 +00:03:01,380 --> 00:03:02,850 +Давайте рассмотрим несколько примеров + +65 +00:03:02,850 --> 00:03:04,563 +где кодер действительно блистает. + +66 +00:03:06,210 --> 00:03:09,900 +Прежде всего, Masked Language Modeling, или MLM. + +67 +00:03:09,900 --> 00:03:11,970 +Это задача предсказания скрытого слова + +68 +00:03:11,970 --> 00:03:13,590 +в последовательности слов. + +69 +00:03:13,590 --> 00:03:15,630 +Здесь, например, мы скрыли слово + +70 +00:03:15,630 --> 00:03:17,247 +между "My" и "is". + +71 +00:03:18,270 --> 00:03:21,120 +Это одна из целей обучения BERT. + +72 +00:03:21,120 --> 00:03:24,393 +Он был обучен предсказывать скрытые слова в последовательности. + +73 +00:03:25,230 --> 00:03:27,930 +В этом сценарии кодеры проявляют себя особенно ярко, поскольку + +74 +00:03:27,930 --> 00:03:31,140 +двунаправленная информация имеет здесь решающее значение. + +75 +00:03:31,140 --> 00:03:32,947 +Если бы у нас не было слов справа, + +76 +00:03:32,947 --> 00:03:34,650 +это "Sylvain" и ".", + +77 +00:03:34,650 --> 00:03:35,940 +то очень мало шансов, + +78 +00:03:35,940 --> 00:03:38,580 +что BERT смог бы определить имя + +79 +00:03:38,580 --> 00:03:40,500 +как правильное слово. + +80 +00:03:40,500 --> 00:03:42,270 +Кодер должен хорошо понимать + +81 +00:03:42,270 --> 00:03:45,360 +последовательности, чтобы предсказать замаскированное слово + +82 +00:03:45,360 --> 00:03:48,840 +поскольку даже если текст грамматически правильный, + +83 +00:03:48,840 --> 00:03:50,610 +он не обязательно имеет смысл + +84 +00:03:50,610 --> 00:03:52,413 +в контексте последовательности. + +85 +00:03:55,230 --> 00:03:56,580 +Как упоминалось ранее, + +86 +00:03:56,580 --> 00:03:59,520 +кодеры хорошо справляются с классификацией последовательностей. + +87 +00:03:59,520 --> 00:04:02,883 +Анализ настроений является примером классификации последовательностей. + +88 +00:04:04,410 --> 00:04:09,410 +Цель модели - определить настроение последовательности. + +89 +00:04:09,540 --> 00:04:11,280 +Это может варьироваться от присвоения последовательности + +90 +00:04:11,280 --> 00:04:12,960 +рейтинга от одной до пяти звезд + +91 +00:04:12,960 --> 00:04:15,900 +если проводится анализ отзывов, до присвоения положительного + +92 +00:04:15,900 --> 00:04:17,820 +или отрицательного рейтинга последовательности + +93 +00:04:17,820 --> 00:04:19,220 +что и показано здесь. + +94 +00:04:20,280 --> 00:04:22,950 +Например, здесь, даны две последовательности, + +95 +00:04:22,950 --> 00:04:25,860 +мы используем модель для вычисления прогноза, + +96 +00:04:25,860 --> 00:04:27,420 +и классифицируем последовательности + +97 +00:04:27,420 --> 00:04:30,393 +между двумя классами, положительным и отрицательным. + +98 +00:04:31,230 --> 00:04:33,450 +Хотя эти две последовательности очень похожи + +99 +00:04:33,450 --> 00:04:35,220 +и содержат одни и те же слова, + +100 +00:04:35,220 --> 00:04:37,170 +смысл совершенно разный, + +101 +00:04:37,170 --> 00:04:40,143 +и модель кодера способна уловить эту разницу. + +102 +00:04:41,404 --> 00:04:44,154 + + diff --git a/subtitles/ru/06_transformer-models-decoders.srt b/subtitles/ru/06_transformer-models-decoders.srt new file mode 100644 index 000000000..54ecf410b --- /dev/null +++ b/subtitles/ru/06_transformer-models-decoders.srt @@ -0,0 +1,348 @@ +1 +00:00:03,750 --> 00:00:07,140 +- В этом видео мы изучим архитектуру декодера. + +2 +00:00:07,140 --> 00:00:07,973 +Примером + +3 +00:00:07,973 --> 00:00:11,338 +популярной архитектуры только декодера является GPT-2. + +4 +00:00:11,338 --> 00:00:14,160 +Для того чтобы понять, как работают декодеры + +5 +00:00:14,160 --> 00:00:17,430 +мы рекомендуем посмотреть видео о кодерах. + +6 +00:00:17,430 --> 00:00:19,980 +Они чрезвычайно похожи на декодеры. + +7 +00:00:19,980 --> 00:00:21,210 +Декодер можно использовать + +8 +00:00:21,210 --> 00:00:23,760 +большинства тех же задач, что и кодер, + +9 +00:00:23,760 --> 00:00:27,330 +хотя, как правило, с небольшой потерей производительности. + +10 +00:00:27,330 --> 00:00:28,890 +Давайте воспользуемся тем же подходом, который мы использовали + +11 +00:00:28,890 --> 00:00:30,300 +с кодером, чтобы попытаться + +12 +00:00:30,300 --> 00:00:32,670 +понять архитектурные различия + +13 +00:00:32,670 --> 00:00:34,803 +между кодером и декодером. + +14 +00:00:35,777 --> 00:00:38,910 +Мы используем небольшой пример, используя три слова. + +15 +00:00:38,910 --> 00:00:41,050 +Мы пропускаем их через декодер. + +16 +00:00:41,050 --> 00:00:44,793 +Мы получаем числовое представление для каждого слова. + +17 +00:00:46,410 --> 00:00:49,350 +Здесь, например, декодер преобразует три слова. + +18 +00:00:49,350 --> 00:00:53,545 +"Welcome to NYC" в эти три последовательности цифр. + +19 +00:00:53,545 --> 00:00:56,040 +Декодер выдает ровно одну последовательность + +20 +00:00:56,040 --> 00:00:58,740 +чисел на каждое входное слово. + +21 +00:00:58,740 --> 00:01:00,630 +Это числовое представление может также + +22 +00:01:00,630 --> 00:01:03,783 +назвать вектором признаков или тензором признаков. + +23 +00:01:04,920 --> 00:01:07,200 +Давайте погрузимся в это представление. + +24 +00:01:07,200 --> 00:01:08,490 +Оно содержит один вектор + +25 +00:01:08,490 --> 00:01:11,340 +на каждое слово, прошедшее через декодер. + +26 +00:01:11,340 --> 00:01:14,250 +Каждый из этих векторов является числовым представлением + +27 +00:01:14,250 --> 00:01:15,573 +рассматриваемого слова. + +28 +00:01:16,920 --> 00:01:18,562 +Размерность этого вектора определяется + +29 +00:01:18,562 --> 00:01:20,703 +архитектурой модели. + +30 +00:01:22,860 --> 00:01:26,040 +Декодер отличается от кодера главным образом + +31 +00:01:26,040 --> 00:01:28,200 +своим механизмом самовнимания. + +32 +00:01:28,200 --> 00:01:30,843 +Он использует так называемое маскированное самовнимание. + +33 +00:01:31,860 --> 00:01:34,650 +Здесь, например, если мы сосредоточимся на слове "to" + +34 +00:01:34,650 --> 00:01:37,620 +мы увидим, что вектор абсолютно не изменен + +35 +00:01:37,620 --> 00:01:39,690 +словом "NYC". + +36 +00:01:39,690 --> 00:01:41,731 +Это происходит потому, что все слова справа, также известные + +37 +00:01:41,731 --> 00:01:45,276 +как правильный контекст слова, маскируются, а + +38 +00:01:45,276 --> 00:01:49,230 +вместо того чтобы извлекать пользу из всех слов слева и справа. + +39 +00:01:49,230 --> 00:01:51,600 +Таким образом, двунаправленный контекст. + +40 +00:01:51,600 --> 00:01:55,020 +Декодеры имеют доступ только к одному контексту + +41 +00:01:55,020 --> 00:01:58,203 +который может быть левым или правым контекстом. + +42 +00:01:59,539 --> 00:02:03,356 +Механизм маскированного самовнимания отличается + +43 +00:02:03,356 --> 00:02:04,320 +от механизма самовнимания тем, + +44 +00:02:04,320 --> 00:02:07,110 +что использует дополнительную маску, скрывающую контекст + +45 +00:02:07,110 --> 00:02:09,390 +по обе стороны от слова, + +46 +00:02:09,390 --> 00:02:12,810 +при этом на числовое представление слова не влияют + +47 +00:02:12,810 --> 00:02:14,853 +слова в скрытом контексте. + +48 +00:02:16,260 --> 00:02:18,330 +Так когда же следует использовать декодер? + +49 +00:02:18,330 --> 00:02:22,380 +Декодеры, как и кодеры, могут быть использованы как самостоятельные модели + +50 +00:02:22,380 --> 00:02:25,020 +поскольку они генерируют числовое представление. + +51 +00:02:25,020 --> 00:02:28,320 +Они также могут использоваться для решения самого широкого круга задач. + +52 +00:02:28,320 --> 00:02:31,260 +Однако сила декодера заключается в том, что + +53 +00:02:31,260 --> 00:02:34,530 +слово может иметь доступ только к своему левому контексту. + +54 +00:02:34,530 --> 00:02:36,690 + + +55 +00:02:36,690 --> 00:02:39,120 +Они по своей природе хороши в генерации текста: + +56 +00:02:39,120 --> 00:02:41,010 +способности генерировать слово + +57 +00:02:41,010 --> 00:02:45,000 +или последовательность слов, учитывая известную последовательность слов. + +58 +00:02:45,000 --> 00:02:45,833 +Это известно как + +59 +00:02:45,833 --> 00:02:49,083 +каузальное языковое моделирование или генерация естественного языка. + +60 +00:02:50,430 --> 00:02:53,520 +Вот пример того, как работает каузальное языковое моделирование. + +61 +00:02:53,520 --> 00:02:56,410 +Мы начинаем с вводного слова, которым является "my", + +62 +00:02:57,339 --> 00:02:59,973 +используем его в качестве входного сигнала для декодера. + +63 +00:03:00,810 --> 00:03:04,260 +Модель выводит вектор чисел + +64 +00:03:04,260 --> 00:03:07,230 +и этот вектор содержит информацию о последовательности, + +65 +00:03:07,230 --> 00:03:08,733 +которая здесь является одним словом. + +66 +00:03:09,780 --> 00:03:11,430 +Мы применяем небольшое преобразование + +67 +00:03:11,430 --> 00:03:13,110 +к этому вектору так, чтобы он отображался + +68 +00:03:13,110 --> 00:03:16,500 +на все слова, известные модели, это отображение + +69 +00:03:16,500 --> 00:03:19,890 +которое, как мы увидим позже, называется "голова языкового моделирования". + +70 +00:03:19,890 --> 00:03:21,930 +Мы определили, что модель считает, + +71 +00:03:21,930 --> 00:03:25,053 +что наиболее вероятное следующее слово это "name". + +72 +00:03:26,250 --> 00:03:28,710 +Затем мы берем это новое слово и добавляем его + +73 +00:03:28,710 --> 00:03:33,480 +к начальной последовательности из "my", и теперь у нас есть "my name". + +74 +00:03:33,480 --> 00:03:36,870 +Именно здесь возникает авторегрессивный аспект. + +75 +00:03:36,870 --> 00:03:38,490 +Авторегрессионные модели + +76 +00:03:38,490 --> 00:03:42,513 +повторно используют свои прошлые выходы в качестве входов на следующих этапах. + +77 +00:03:43,452 --> 00:03:46,980 +И снова мы выполняем точно такую же операцию. + +78 +00:03:46,980 --> 00:03:49,500 +Мы пропускаем эту последовательность через декодер + +79 +00:03:49,500 --> 00:03:51,993 +и извлекаем наиболее вероятное следующее слово. + +80 +00:03:52,978 --> 00:03:57,978 +В данном случае это слово "is", мы повторяем операцию + +81 +00:03:58,230 --> 00:04:02,040 +пока не будем удовлетворены, начиная с одного слова. + +82 +00:04:02,040 --> 00:04:04,590 +Теперь мы сгенерировали полное предложение. + +83 +00:04:04,590 --> 00:04:07,890 +Мы решаем остановиться на этом, но могли бы продолжать еще какое-то время. + +84 +00:04:07,890 --> 00:04:12,890 +GPT-2, например, имеет максимальный размер контекста 1024. + +85 +00:04:13,170 --> 00:04:16,830 +В конечном итоге мы могли бы сгенерировать до 1024 слов, + +86 +00:04:16,830 --> 00:04:19,050 +и декодер все еще будет помнить о + +87 +00:04:19,050 --> 00:04:21,003 +первых словах в этой последовательности. + diff --git a/subtitles/ru/07_transformer-models-encoder-decoders.srt b/subtitles/ru/07_transformer-models-encoder-decoders.srt new file mode 100644 index 000000000..37003b346 --- /dev/null +++ b/subtitles/ru/07_transformer-models-encoder-decoders.srt @@ -0,0 +1,260 @@ +1 +00:00:04,160 --> 00:00:07,200 +В этом видео мы изучим архитектуру кодера-декодера. + +2 +00:00:08,160 --> 00:00:16,160 +Примером популярной модели кодера-декодера является T5. Для того чтобы понять, как работает кодер-декодер + +3 +00:00:16,160 --> 00:00:21,680 +мы рекомендуем вам ознакомиться с видео о кодерах и декодерах как самостоятельных моделях. + +4 +00:00:22,400 --> 00:00:30,320 +Понимание того, как они ведут себя по отдельности, поможет понять, как ведет себя кодер-декодер. + +5 +00:00:30,320 --> 00:00:35,360 +Давайте начнем с того, что мы видели о кодере. Кодер принимает слова в качестве входа, + +6 +00:00:36,000 --> 00:00:40,640 +прогоняет их через кодер и извлекает числовое представление + +7 +00:00:40,640 --> 00:00:47,360 +для каждого пропущенного через него слова. Теперь мы знаем, что числовое представление содержит информацию + +8 +00:00:47,360 --> 00:00:54,000 +о смысле последовательности. Давайте отложим это в сторону и добавим к диаграмме декодер. + +9 +00:00:56,480 --> 00:01:00,160 +В этом сценарии мы используем декодер таким образом, которого раньше не видели. + +10 +00:01:00,720 --> 00:01:07,600 +Мы передаем выходы кодера непосредственно на него! Дополнительно к выходам кодера, + +11 +00:01:07,600 --> 00:01:13,040 +мы также передаем декодеру последовательность. При запросе декодера на вывод без + +12 +00:01:13,040 --> 00:01:17,360 +начальной последовательности, мы можем передать ему значение, указывающее на начало последовательности. + +13 +00:01:18,000 --> 00:01:23,520 +И именно здесь происходит волшебство кодера-декодера. Кодер принимает на вход последовательность. + +14 +00:01:24,560 --> 00:01:30,480 +Он вычисляет прогноз и выдает числовое представление. Затем он посылает + +15 +00:01:30,480 --> 00:01:38,000 +его декодеру. Он, в некотором смысле, закодировал последовательность. А декодер, в свою очередь, + +16 +00:01:38,000 --> 00:01:42,960 +используя этот вход наряду с обычной входной последовательностью, попытается декодировать последовательность. + +17 +00:01:44,720 --> 00:01:50,400 +Декодер декодирует последовательность и выводит слово. На данный момент нам не нужно понимать + +18 +00:01:50,400 --> 00:01:55,440 +смысл этого слова, но мы можем понять, что декодер по сути декодирует то, что вывел кодер. + +19 +00:01:55,440 --> 00:02:02,160 +Слово "Start of sequence word" указывает на то, что он должен начать декодирование последовательности. + +20 +00:02:03,600 --> 00:02:10,240 +Теперь, когда у нас есть и вектор признаков, и начальное сгенерированное слово, нам больше не нужен + +21 +00:02:10,240 --> 00:02:17,760 +кодер. Как мы уже видели на примере декодера, он может действовать в авторегрессивной манере; + +22 +00:02:18,640 --> 00:02:24,960 +слово, которое он только что вывел, теперь может быть использовано в качестве входа. Это, в сочетании с + +23 +00:02:24,960 --> 00:02:30,800 +числовым представлением, выводимым кодером, может быть использовано для генерации второго слова. + +24 +00:02:33,200 --> 00:02:38,880 +Обратите внимание, что первое слово все еще здесь, поскольку модель все еще выводит его. Однако оно выделено серым цветом, + +25 +00:02:38,880 --> 00:02:45,120 +поскольку оно нам больше не нужно. Мы можем продолжать и продолжать, например, пока декодер + +26 +00:02:45,120 --> 00:02:50,720 +не выдаст значение, которое мы считаем "stopping value", например, точку, означающую конец последовательности. + +27 +00:02:53,440 --> 00:02:58,080 +Здесь мы увидели весь механизм трансформера кодер-декодер: давайте пройдемся по нему + +28 +00:02:58,080 --> 00:03:05,120 +еще раз. У нас есть начальная последовательность, которая отправляется на кодер. Затем выходной сигнал кодера + +29 +00:03:05,120 --> 00:03:12,240 +передается декодеру для декодирования. Если кодер мы теперь можем выбросить после одного использования, + +30 +00:03:12,240 --> 00:03:17,840 +то декодер будет использоваться несколько раз: пока мы не сгенерируем все слова, которые нам нужны. + +31 +00:03:20,000 --> 00:03:25,120 +Рассмотрим конкретный случай; на примере Translation Language Modeling; также называемого трансдукцией; + +32 +00:03:25,120 --> 00:03:30,800 +акт перевода последовательности. Здесь мы хотим перевести английскую последовательность "Welcome" + +33 +00:03:30,800 --> 00:03:38,400 +"to NYC" на французский язык. Мы используем модель трансформера, которая обучена для этой задачи в явном виде. + +34 +00:03:38,400 --> 00:03:43,520 +Мы используем кодер для создания представления английского предложения. Мы передаем это + +35 +00:03:43,520 --> 00:03:48,880 +декодеру и, используя слово "start of sequence", просим его вывести первое слово. + +36 +00:03:50,720 --> 00:03:52,960 +Он выводит "Bienvenue", что означает "Welcome". + +37 +00:03:55,280 --> 00:04:02,480 +Затем мы используем "Bienvenue" в качестве входной последовательности для декодера. Это, наряду с вектором признаков, + +38 +00:04:04,320 --> 00:04:08,480 +позволяет декодеру предсказать второе слово, "à", которое в английском языке означает "to". + +39 +00:04:10,160 --> 00:04:14,400 +Наконец, мы просим декодер предсказать третье слово; он предсказывает "NYC", + +40 +00:04:14,400 --> 00:04:20,240 +что снова является правильным. Мы перевели предложение! Где кодер-декодер действительно + +41 +00:04:20,240 --> 00:04:24,880 +блистает, так это в том, что у нас есть кодер и декодер, которые часто не имеют общих весов. + +42 +00:04:27,280 --> 00:04:31,440 +Таким образом, у нас есть целый блок (кодер), который можно обучить понимать последовательность + +43 +00:04:31,440 --> 00:04:36,480 +и извлекать релевантную информацию. Например, для сценария перевода, который мы рассматривали ранее, + +44 +00:04:36,480 --> 00:04:44,160 +это означает разбор и понимание того, что было сказано на английском языке; извлечение + +45 +00:04:44,160 --> 00:04:49,040 +информации из этого языка и помещение всего этого в вектор, насыщенный информацией. + +46 +00:04:50,880 --> 00:04:57,280 +С другой стороны, у нас есть декодер, единственной целью которого является декодирование признака, выводимого + +47 +00:04:57,280 --> 00:05:03,760 +кодером. Этот декодер может быть специализирован для совершенно другого языка или даже модальности, + +48 +00:05:03,760 --> 00:05:11,760 +например, изображения или речи. Кодеры-декодеры являются особенными по нескольким причинам. Во-первых, + +49 +00:05:11,760 --> 00:05:17,040 +они способны справляться с задачами преобразования последовательности в последовательность, такими как перевод, который мы только что видели. + +50 +00:05:18,640 --> 00:05:23,880 +Во-вторых, веса между частями кодера и декодера не обязательно являются общими. Давайте + +51 +00:05:24,480 --> 00:05:31,200 +возьмем другой пример перевода. Здесь мы переводим "Transformers are powerful" на французский язык. + +52 +00:05:32,240 --> 00:05:36,560 +Во-первых, это означает, что из последовательности из трех слов мы можем сгенерировать + +53 +00:05:36,560 --> 00:05:42,240 +последовательность из четырех слов. Кто-то может возразить, что с этим можно справиться с помощью декодера, + +54 +00:05:42,240 --> 00:05:46,960 +который будет генерировать перевод авторегрессивным способом, и он будет прав! + +55 +00:05:49,840 --> 00:05:53,840 +Другим примером того, где трансформеры последовательности в последовательность проявляют себя с лучшей стороны, является суммаризация. + +56 +00:05:54,640 --> 00:05:58,560 +Здесь у нас есть очень длинная последовательность, как правило, полный текст, + +57 +00:05:58,560 --> 00:06:03,840 +и мы хотим обобщить его. Поскольку кодер и декодер разделены, + +58 +00:06:03,840 --> 00:06:08,880 +мы можем иметь разные длины контекста (например, очень длинный контекст для кодера, который + +59 +00:06:08,880 --> 00:06:13,840 +обрабатывает текст, и меньший контекст для декодера, который обрабатывает обобщенный текст). + +60 +00:06:16,240 --> 00:06:20,480 +Существует множество моделей преобразования последовательности в последовательность. + +61 +00:06:20,480 --> 00:06:24,160 +Здесь приведено несколько примеров популярных моделей кодеров-декодеров, доступных в библиотеке трансформеров. + +62 +00:06:26,320 --> 00:06:31,200 +Кроме того, вы можете загрузить кодер и декодер внутри модели кодера-декодера! + +63 +00:06:31,200 --> 00:06:35,040 +Поэтому, в зависимости от конкретной задачи, которую вы ставите перед собой, + +64 +00:06:35,040 --> 00:06:40,240 +вы можете использовать конкретные кодеры и декодеры, которые зарекомендовали себя с лучшей стороны + +65 +00:06:40,240 --> 00:06:49,850 +в этих конкретных задачах. На этом мы завершаем разговор о кодерах-декодерах. Спасибо за просмотр! + diff --git a/subtitles/zh-CN/03_what-is-transfer-learning.srt b/subtitles/zh-CN/03_what-is-transfer-learning.srt index 8a8a0b509..4ddaf9c5d 100644 --- a/subtitles/zh-CN/03_what-is-transfer-learning.srt +++ b/subtitles/zh-CN/03_what-is-transfer-learning.srt @@ -5,22 +5,22 @@ 2 00:00:05,550 --> 00:00:07,293 -- 什么是迁移学习? +- 什么是转移学习? - What is transfer learning? 3 00:00:09,480 --> 00:00:10,920 -迁移学习的思想 +转移学习的思想 The idea of transfer learning 4 00:00:10,920 --> 00:00:12,570 -是利用所获得的知识 +是利用在另一项任务上使用大量数据训练的模型结果 is to leverage the knowledge acquired 5 00:00:12,570 --> 00:00:15,543 -通过在另一项任务上使用大量数据训练的模型。 +来获取知识。 by a model trained with lots of data on another task. 6 @@ -30,12 +30,12 @@ The model A will be trained specifically for task A. 7 00:00:20,130 --> 00:00:22,200 -现在假设你想训练模型 B +现在假设您想为了另一个任务 Now let's say you want to train a model B 8 00:00:22,200 --> 00:00:23,970 -为了不同的任务。 +训练模型 B。 for a different task. 9 @@ -45,17 +45,17 @@ One option would be to train the model from scratch. 10 00:00:27,330 --> 00:00:30,633 -这可能需要大量的计算、时间和数据。 +但这可能需要大量的计算、时间和数据。 This could take lots of computation, time and data. 11 00:00:31,470 --> 00:00:34,260 -相反,我们可以初始化模型 B +我们可以有另一种选择,初始化模型 B Instead, we could initialize model B 12 00:00:34,260 --> 00:00:36,570 -与模型 A 具有相同的权重, +它与模型 A 具有相同的权重, with the same weights as model A, 13 @@ -75,37 +75,37 @@ all the model's weight are initialized randomly. 16 00:00:45,870 --> 00:00:48,870 -在这个例子中,我们正在训练一个 BERT 模型 +在这个例子中,我们正在基于识别任务上 In this example, we are training a BERT model 17 00:00:48,870 --> 00:00:50,220 -在识别任务上 +训练一个 BERT 模型 on the task of recognizing 18 00:00:50,220 --> 00:00:52,203 -两个句子是否相似。 +来判断两个句子是否相似。 if two sentences are similar or not. 19 00:00:54,116 --> 00:00:56,730 -在左边,它是从头开始训练的, +左边的例子是从头开始训练的, On the left, it's trained from scratch, 20 00:00:56,730 --> 00:01:00,000 -在右侧,它正在微调预训练模型。 +右边则代表正在微调预训练模型。 and on the right it's fine-tuning a pretrained model. 21 00:01:00,000 --> 00:01:02,220 -正如我们所见,使用迁移学习 +正如我们所见,使用转移学习 As we can see, using transfer learning 22 00:01:02,220 --> 00:01:05,160 -并且预训练模型产生了更好的结果。 +和预训练模型产生了更好的结果。 and the pretrained model yields better results. 23 @@ -120,7 +120,7 @@ The training from scratch is capped around 70% accuracy 25 00:01:10,620 --> 00:01:13,293 -而预训练模型轻松击败了 86%。 +而预训练模型轻松达到了 86%。 while the pretrained model beats the 86% easily. 26 @@ -130,17 +130,17 @@ This is because pretrained models 27 00:01:16,140 --> 00:01:18,420 -通常接受大量数据的训练 +通常基于大量数据进行训练 are usually trained on large amounts of data 28 00:01:18,420 --> 00:01:21,000 -为模型提供统计理解 +这些数据为模型在预训练期间 that provide the model with a statistical understanding 29 00:01:21,000 --> 00:01:23,413 -预训练期间使用的语言。 +提供了对语言使用的统计理解。 of the language used during pretraining. 30 @@ -150,7 +150,7 @@ In computer vision, 31 00:01:25,950 --> 00:01:28,080 -迁移学习已成功应用 +转移学习已成功应用 transfer learning has been applied successfully 32 @@ -165,7 +165,7 @@ Models are frequently pretrained on ImageNet, 34 00:01:32,850 --> 00:01:36,153 -包含 120 万张照片图像的数据集。 +它是一种包含 120 万张照片图像的数据集。 a dataset containing 1.2 millions of photo images. 35 @@ -190,12 +190,12 @@ In Natural Language Processing, 39 00:01:49,140 --> 00:01:51,870 -迁移学习是最近才出现的。 +转移学习是最近才出现的。 transfer learning is a bit more recent. 40 00:01:51,870 --> 00:01:54,480 -与 ImageNet 的一个关键区别是预训练 +它与 ImageNet 的一个关键区别是预训练 A key difference with ImageNet is that the pretraining 41 @@ -205,12 +205,12 @@ is usually self-supervised, 42 00:01:56,460 --> 00:01:58,770 -这意味着它不需要人工注释 +这意味着它不需要人工对标签 which means it doesn't require humans annotations 43 00:01:58,770 --> 00:01:59,673 -对于标签。 +进行注释。 for the labels. 44 @@ -225,7 +225,7 @@ is to guess the next word in a sentence. 46 00:02:05,310 --> 00:02:07,710 -这只需要大量的文本。 +它只需要大量的输入文本。 Which only requires lots and lots of text. 47 @@ -235,12 +235,12 @@ GPT-2 for instance, was pretrained this way 48 00:02:10,710 --> 00:02:12,900 -使用 4500 万个链接的内容 +它使用 4500 万个用户在 Reddit 上发布的 using the content of 45 millions links 49 00:02:12,900 --> 00:02:14,673 -用户在 Reddit 上发布。 +链接的内容。 posted by users on Reddit. 50 @@ -260,42 +260,42 @@ Which is similar to fill-in-the-blank tests 53 00:02:24,540 --> 00:02:26,760 -你可能在学校做过。 +您可能在学校做过。 you may have done in school. 54 00:02:26,760 --> 00:02:29,880 -BERT 是使用英文维基百科以这种方式进行预训练的 +BERT 是使用英文维基百科和 11,000 本未出版的书籍 BERT was pretrained this way using the English Wikipedia 55 00:02:29,880 --> 00:02:31,893 -和 11,000 本未出版的书籍。 +进行预训练的。 and 11,000 unpublished books. 56 00:02:33,120 --> 00:02:36,450 -在实践中,迁移学习应用于给定模型 +在实践中,转移学习是通过抛弃原模型的头部 In practice, transfer learning is applied on a given model 57 00:02:36,450 --> 00:02:39,090 -通过扔掉它的头,也就是说, +即其针对预训练目标的最后几层 by throwing away its head, that is, 58 00:02:39,090 --> 00:02:42,150 -它的最后一层专注于预训练目标, +并用一个新的、随机初始化的头部 its last layers focused on the pretraining objective, 59 00:02:42,150 --> 00:02:45,360 -并用一个新的、随机初始化的头替换它 +来替换它来应用的 and replacing it with a new, randomly initialized head 60 00:02:45,360 --> 00:02:46,860 -适合手头的任务。 +这个新的头部适用于当前的任务。 suitable for the task at hand. 61 @@ -320,37 +320,37 @@ Since our task had two labels. 65 00:02:59,700 --> 00:03:02,490 -为了尽可能高效,使用预训练模型 +为了尽可能高效 To be as efficient as possible, the pretrained model used 66 00:03:02,490 --> 00:03:03,770 -应该尽可能相似 +所使用的预训练模型 should be as similar as possible 67 00:03:03,770 --> 00:03:06,270 -对其进行微调的任务。 +应尽可能与其微调的任务相似。 to the task it's fine-tuned on. 68 00:03:06,270 --> 00:03:08,190 -例如,如果问题 +例如,如果当前需要 For instance, if the problem 69 00:03:08,190 --> 00:03:10,860 -是对德语句子进行分类, +对德语句子进行分类, is to classify German sentences, 70 00:03:10,860 --> 00:03:13,053 -最好使用德国预训练模型。 +最好使用德语预训练模型。 it's best to use a German pretrained model. 71 00:03:14,370 --> 00:03:16,649 -但好事也有坏事。 +但有好事也有坏事。 But with the good comes the bad. 72 @@ -360,87 +360,87 @@ The pretrained model does not only transfer its knowledge, 73 00:03:19,380 --> 00:03:21,693 -以及它可能包含的任何偏见。 +同时也转移了它可能包含的任何偏见。 but also any bias it may contain. 74 00:03:22,530 --> 00:03:24,300 -ImageNet 主要包含图像 +ImageNet 主要包含来自美国和西欧 ImageNet mostly contains images 75 00:03:24,300 --> 00:03:26,850 -来自美国和西欧。 +的图像。 coming from the United States and Western Europe. 76 00:03:26,850 --> 00:03:28,020 -所以模型用它微调 +所以基于它进行微调的模型 So models fine-tuned with it 77 00:03:28,020 --> 00:03:31,710 -通常会在来自这些国家 / 地区的图像上表现更好。 +通常会在来自这些国家或地区的图像上表现更好。 usually will perform better on images from these countries. 78 00:03:31,710 --> 00:03:33,690 -OpenAI 还研究了偏差 +OpenAI 还研究了 OpenAI also studied the bias 79 00:03:33,690 --> 00:03:36,120 -在其 GPT-3 模型的预测中 +其使用猜测下一个单词目标 in the predictions of its GPT-3 model 80 00:03:36,120 --> 00:03:36,953 -这是预训练的 +预训练的 GPT-3 模型中 which was pretrained 81 00:03:36,953 --> 00:03:38,750 -使用猜测下一个单词目标。 +预测的偏差。 using the guess the next word objective. 82 00:03:39,720 --> 00:03:41,040 -更改提示的性别 +将提示的性别 Changing the gender of the prompt 83 00:03:41,040 --> 00:03:44,250 -从他非常到她非常 +从“他”更改到“她” from he was very to she was very 84 00:03:44,250 --> 00:03:47,550 -改变了大多数中性形容词的预测 +会使预测从主要是中性形容词 changed the predictions from mostly neutral adjectives 85 00:03:47,550 --> 00:03:49,233 -几乎只有物理的。 +变为几乎只有物理上的形容词。 to almost only physical ones. 86 00:03:50,400 --> 00:03:52,367 -在他们的 GPT-2 模型的模型卡中, +在他们的 GPT-2 的模型卡中, In their model card of the GPT-2 model, 87 00:03:52,367 --> 00:03:54,990 -OpenAI 也承认它的偏见 +OpenAI 也承认了它的偏见 OpenAI also acknowledges its bias 88 00:03:54,990 --> 00:03:56,730 -并且不鼓励使用它 +并且不鼓励在与人类交互的系统中 and discourages its use 89 00:03:56,730 --> 00:03:58,803 -在与人类交互的系统中。 +使用它。 in systems that interact with humans. 90 diff --git a/subtitles/zh-CN/05_transformer-models-encoders.srt b/subtitles/zh-CN/05_transformer-models-encoders.srt index 7c3118cf1..f75cd6686 100644 --- a/subtitles/zh-CN/05_transformer-models-encoders.srt +++ b/subtitles/zh-CN/05_transformer-models-encoders.srt @@ -1,6 +1,6 @@ 1 00:00:00,253 --> 00:00:03,003 -(介绍引人注目) +(引人注目的介绍) (intro striking) 2 @@ -10,12 +10,12 @@ 3 00:00:07,830 --> 00:00:11,070 -一个流行的仅编码器架构的例子是 BURT +一个流行的仅使用编码器架构的例子是 BURT An example of a popular encoder only architecture is BURT 4 00:00:11,070 --> 00:00:13,323 -这是同类产品中最受欢迎的型号。 +这是同类产品中最受欢迎的模型。 which is the most popular model of its kind. 5 @@ -25,37 +25,37 @@ Let's first start by understanding how it works. 6 00:00:18,360 --> 00:00:20,910 -我们将使用一个使用三个词的小例子。 +我们将使用一个三个单词的小例子。 We'll use a small example using three words. 7 00:00:20,910 --> 00:00:23,823 -我们使用这些作为输入并将它们传递给编码器。 +我们使用这些作为输入传递给编码器。 We use these as inputs and pass them through the encoder. 8 00:00:25,290 --> 00:00:28,173 -我们检索每个单词的数字表示。 +得到了每个单词的数值表示。 We retrieve a numerical representation of each word. 9 00:00:29,970 --> 00:00:32,700 -例如,在这里,编码器转换这三个词, +例如,在这里,编码器将这三个词, Here, for example, the encoder converts those three words, 10 00:00:32,700 --> 00:00:37,350 -欢迎来到纽约,在这三个数字序列中。 +welcome to NYC,转换为这三个数字序列。 welcome to NYC, in these three sequences of numbers. 11 00:00:37,350 --> 00:00:40,350 -编码器只输出一个数字序列 +编码器对于每个输入单词 The encoder outputs exactly one sequence of numbers 12 00:00:40,350 --> 00:00:41,493 -每个输入词。 +精确输出一个数字序列。 per input word. 13 @@ -65,7 +65,7 @@ This numerical representation can also be called 14 00:00:44,880 --> 00:00:47,163 -特征向量或特征张量。 +特征向量(feature vector)或特征张量(feature tensor)。 a feature vector, or a feature tensor. 15 @@ -85,22 +85,22 @@ that was passed through the encoder. 18 00:00:56,130 --> 00:00:58,620 -这些向量中的每一个都是一个数字表示 +每个向量都是 Each of these vector is a numerical representation 19 00:00:58,620 --> 00:01:00,033 -有问题的词。 +该词的数字表示。 of the word in question. 20 00:01:01,080 --> 00:01:03,300 -该向量的维度被定义 +该向量的维度由 The dimension of that vector is defined 21 00:01:03,300 --> 00:01:05,520 -通过模型的架构。 +模型的架构所决定。 by the architecture of the model. 22 @@ -115,17 +115,17 @@ These representations contain the value of a word, 24 00:01:13,230 --> 00:01:15,240 -但语境化。 +但包含上下文化的处理。 but contextualized. 25 00:01:15,240 --> 00:01:18,570 -例如,归因于单词 “to” 的向量 +例如,与单词 "to" 相关联的向量 For example, the vector attributed to the word "to" 26 00:01:18,570 --> 00:01:22,290 -不只是 “to” 这个词的代表。 +不只是 “to” 这个词的表示。 isn't the representation of only the "to" word. 27 @@ -150,7 +150,7 @@ the words on the left of the one we're studying, 31 00:01:32,970 --> 00:01:34,980 -这里是 “欢迎” 这个词, +这里是 “Welcome” 这个词, here the word "Welcome", 32 @@ -180,22 +180,22 @@ holds the meaning of the word within the text. 37 00:01:53,310 --> 00:01:56,073 -由于自我注意机制,它做到了这一点。 +由于自注意力机制,它做到了这一点。 It does this thanks to the self-attention mechanism. 38 00:01:57,240 --> 00:02:00,630 -自注意力机制涉及到不同的位置, +自注意力机制指的是与单个序列中的不同位置 The self-attention mechanism relates to different positions, 39 00:02:00,630 --> 00:02:02,850 -或单个序列中的不同单词 +或不同单词相关联 or different words in a single sequence 40 00:02:02,850 --> 00:02:06,003 -为了计算该序列的表示。 +以计算该序列的表示形式。 in order to compute a representation of that sequence. 41 @@ -220,17 +220,17 @@ We won't dive into the specifics here 45 00:02:18,030 --> 00:02:19,680 -这将提供一些进一步的阅读 +我们会提供一些进一步的阅读资料 which will offer some further readings 46 00:02:19,680 --> 00:02:21,330 -如果你想获得更好的理解 +如果您想对底层发生了什么 if you want to get a better understanding 47 00:02:21,330 --> 00:02:22,953 -在引擎盖下发生的事情。 +有更好的理解。 at what happens under the hood. 48 @@ -250,17 +250,17 @@ in a wide variety of tasks. 51 00:02:32,100 --> 00:02:33,360 -例如,伯特, +例如,BERT, For example, BERT, 52 00:02:33,360 --> 00:02:35,670 -可以说是最著名的变压器模型, +可以说是最著名的 transformer 模型, arguably the most famous transformer model, 53 00:02:35,670 --> 00:02:37,590 -是一个独立的编码器模型, +它是一个独立的编码器模型, is a standalone encoder model, 54 @@ -270,12 +270,12 @@ and at the time of release, 55 00:02:38,820 --> 00:02:40,440 -这将是最先进的 +它是许多 it'd be the state of the art 56 00:02:40,440 --> 00:02:42,780 -在许多序列分类任务中, +序列分类任务 in many sequence classification tasks, 57 @@ -285,32 +285,32 @@ question answering tasks, 58 00:02:44,190 --> 00:02:46,743 -掩码语言建模仅举几例。 +和掩码语言建模等任务中的最先进技术。 and mask language modeling to only cite of few. 59 00:02:48,150 --> 00:02:50,460 -这个想法是编码器非常强大 +编码器非常擅长 The idea is that encoders are very powerful 60 00:02:50,460 --> 00:02:52,470 -在提取携带载体 +提取包含有意义信息的 at extracting vectors that carry 61 00:02:52,470 --> 00:02:55,350 -关于序列的有意义的信息。 +关于序列的向量。 meaningful information about a sequence. 62 00:02:55,350 --> 00:02:57,870 -然后可以在路上处理这个向量 +这个向量可以被传递给后续的神经元来进一步处理 This vector can then be handled down the road 63 00:02:57,870 --> 00:03:00,070 -通过额外的神经元来理解它们。 +以便理解其中包含的信息。 by additional neurons to make sense of them. 64 @@ -330,22 +330,22 @@ First of all, Masked Language Modeling, or MLM. 67 00:03:09,900 --> 00:03:11,970 -这是预测隐藏词的任务 +这是在一个单词序列中 It's the task of predicting a hidden word 68 00:03:11,970 --> 00:03:13,590 -在一个单词序列中。 +预测隐藏词的任务。 in a sequence of word. 69 00:03:13,590 --> 00:03:15,630 -在这里,例如,我们隐藏了这个词 +在这里,例如,我们在 “My” 和 “is” 之间 Here, for example, we have hidden the word 70 00:03:15,630 --> 00:03:17,247 -在 “我的” 和 “是” 之间。 +隐藏了这个词。 between "My" and "is". 71 @@ -360,7 +360,7 @@ It was trained to predict hidden words in a sequence. 73 00:03:25,230 --> 00:03:27,930 -编码器尤其在这种情况下大放异彩 +编码器在这种情况下尤其大放异彩 Encoders shine in this scenario in particular 74 @@ -375,32 +375,32 @@ If we didn't have the words on the right, 76 00:03:32,947 --> 00:03:34,650 -“是”、“Sylvain” 和 “.”, +“is”、“Sylvain” 和 “.”, "is", "Sylvain" and the ".", 77 00:03:34,650 --> 00:03:35,940 -那么机会就很小 +那么BERT 将能够识别名称的 then there is very little chance 78 00:03:35,940 --> 00:03:38,580 -BERT 将能够识别名称 +作为正确的词 that BERT would have been able to identify name 79 00:03:38,580 --> 00:03:40,500 -作为正确的词。 +的机会就很小。 as the correct word. 80 00:03:40,500 --> 00:03:42,270 -编码器需要有很好的理解 +为了预测一个掩码单词 The encoder needs to have a good understanding 81 00:03:42,270 --> 00:03:45,360 -序列以预测掩码词 +编码器需要对序列有很好的理解 of the sequence in order to predict a masked word 82 @@ -410,12 +410,12 @@ as even if the text is grammatically correct, 83 00:03:48,840 --> 00:03:50,610 -它不一定有意义 +但不一定符合 it does not necessarily make sense 84 00:03:50,610 --> 00:03:52,413 -在序列的上下文中。 +序列的上下文。 in the context of the sequence. 85 @@ -440,22 +440,22 @@ The model's aim is to identify the sentiment of a sequence. 89 00:04:09,540 --> 00:04:11,280 -它的范围可以从给出一个序列, +它可以从给出的一个序列, It can range from giving a sequence, 90 00:04:11,280 --> 00:04:12,960 -从一颗星到五颗星的评级 +做出一颗星到五颗星的评级 a rating from one to five stars 91 00:04:12,960 --> 00:04:15,900 -如果进行评论分析以给予肯定, +如果进行评论分析 if doing review analysis to giving a positive, 92 00:04:15,900 --> 00:04:17,820 -或对序列的负面评价 +来对一个序列进行积极或消极的评级 or negative rating to a sequence 93 @@ -495,7 +495,7 @@ containing the same words, 100 00:04:35,220 --> 00:04:37,170 -意义完全不同, +意义却完全不同, the meaning is entirely different, 101 @@ -505,6 +505,6 @@ and the encoder model is able to grasp that difference. 102 00:04:41,404 --> 00:04:44,154 -(结尾引人注目) +(引人注目的结尾) (outro striking) diff --git a/subtitles/zh-CN/06_transformer-models-decoders.srt b/subtitles/zh-CN/06_transformer-models-decoders.srt index 4f81ed010..7a22241f2 100644 --- a/subtitles/zh-CN/06_transformer-models-decoders.srt +++ b/subtitles/zh-CN/06_transformer-models-decoders.srt @@ -5,13 +5,13 @@ 2 00:00:07,140 --> 00:00:07,973 -一个例子 +一种流行的仅包含解码器架构 An example 3 00:00:07,973 --> 00:00:11,338 -一种流行的解码器唯一架构是 GPT 两种。 -of a popular decoder only architecture is GPT two. +的例子是 GPT-2。 +of a popular decoder only architecture is GPT-2. 4 00:00:11,338 --> 00:00:14,160 @@ -20,7 +20,7 @@ In order to understand how decoders work 5 00:00:14,160 --> 00:00:17,430 -我们建议你观看有关编码器的视频。 +我们建议您观看有关编码器的视频。 we recommend taking a look at the video regarding encoders. 6 @@ -35,7 +35,7 @@ One can use a decoder 8 00:00:21,210 --> 00:00:23,760 -对于大多数与编码器相同的任务 +执行与编码器相同的大多数任务 for most of the same tasks as an encoder 9 @@ -55,12 +55,12 @@ with the encoder to try 12 00:00:30,300 --> 00:00:32,670 -并了解架构差异 +并了解在编码器和解码器之间 and understand the architectural differences 13 00:00:32,670 --> 00:00:34,803 -在编码器和解码器之间。 +的架构差异。 between an encoder and decoder. 14 @@ -70,12 +70,12 @@ We'll use a small example using three words. 15 00:00:38,910 --> 00:00:41,050 -我们通过他们的解码器传递它们。 +我们通过解码器传递它们。 We pass them through their decoder. 16 00:00:41,050 --> 00:00:44,793 -我们检索每个单词的数字表示。 +我们检索每个单词的数值表示。 We retrieve a numerical representation for each word. 17 @@ -85,17 +85,17 @@ Here for example, the decoder converts the three words. 18 00:00:49,350 --> 00:00:53,545 -欢迎来到纽约,欢迎来到这三个数字序列。 +Welcome to NYC,这三个数字序列。 Welcome to NYC, and these three sequences of numbers. 19 00:00:53,545 --> 00:00:56,040 -解码器只输出一个序列 +解码器针对每个输入词汇 The decoder outputs exactly one sequence 20 00:00:56,040 --> 00:00:58,740 -每个输入词的数字。 +只输出一个数列。 of numbers per input word. 21 @@ -105,7 +105,7 @@ This numerical representation can also 22 00:01:00,630 --> 00:01:03,783 -称为特征向量或特征传感器。 +称为特征向量(feature vector)或特征传感器(feature sensor)。 be called a feature vector or a feature sensor. 23 @@ -115,32 +115,32 @@ Let's dive in this representation. 24 00:01:07,200 --> 00:01:08,490 -它包含一个向量 +它包含了每个通过解码器 It contains one vector 25 00:01:08,490 --> 00:01:11,340 -每个通过解码器的单词。 +传递的单词的一个向量。 per word that was passed through the decoder. 26 00:01:11,340 --> 00:01:14,250 -这些向量中的每一个都是一个数字表示 +这些向量中的每一个单词 Each of these vectors is a numerical representation 27 00:01:14,250 --> 00:01:15,573 -有问题的词。 +都是一个数值表示。 of the word in question. 28 00:01:16,920 --> 00:01:18,562 -该向量的维度被定义 +这个向量的维度 The dimension of that vector is defined 29 00:01:18,562 --> 00:01:20,703 -通过模型的架构。 +由模型的架构所决定。 by the architecture of the model. 30 @@ -150,12 +150,12 @@ Where the decoder differs from the encoder is principally 31 00:01:26,040 --> 00:01:28,200 -具有自我注意机制。 +具有自注意力机制。 with its self attention mechanism. 32 00:01:28,200 --> 00:01:30,843 -它使用所谓的掩蔽自我关注。 +它使用所谓的掩蔽自注意力。 It's using what is called masked self attention. 33 @@ -165,27 +165,27 @@ Here, for example, if we focus on the word "to" 34 00:01:34,650 --> 00:01:37,620 -我们会看到 vector 是绝对未修改的 +我们会发现它的向量 we'll see that is vector is absolutely unmodified 35 00:01:37,620 --> 00:01:39,690 -用纽约的话来说。 +完全未被 NYC 单词修改。 by the NYC word. 36 00:01:39,690 --> 00:01:41,731 -那是因为右边所有的话,也都知道 +那是因为右边所有的话,即 That's because all the words on the right, also known 37 00:01:41,731 --> 00:01:45,276 -因为这个词的正确上下文被掩盖了 +单词的右侧上下文都被屏蔽了 as the right context of the word is masked rather 38 00:01:45,276 --> 00:01:49,230 -而不是受益于左右所有的话。 +而没有从左侧和右侧的所有单词中受益。 than benefiting from all the words on the left and right. 39 @@ -205,32 +205,32 @@ which can be the left context or the right context. 42 00:01:59,539 --> 00:02:03,356 -Masked self attention 机制不同 +掩蔽自注意力机制不同于 The masked self attention mechanism differs 43 00:02:03,356 --> 00:02:04,320 -来自 self attention 机制 +自注意力机制 from the self attention mechanism 44 00:02:04,320 --> 00:02:07,110 -通过使用额外的掩码来隐藏上下文 +通过使用额外的掩码在单词的两边 by using an additional mask to hide the context 45 00:02:07,110 --> 00:02:09,390 -在单词的两边 +来隐藏上下文 on either side of the word 46 00:02:09,390 --> 00:02:12,810 -单词数值表示不会受到影响 +通过隐藏上下文中的单词 the words numerical representation will not be affected 47 00:02:12,810 --> 00:02:14,853 -通过隐藏上下文中的单词。 +单词数值表示不会受到影响。 by the words in the hidden context. 48 @@ -245,7 +245,7 @@ Decoders like encoders can be used as standalone models 50 00:02:22,380 --> 00:02:25,020 -因为它们生成数字表示。 +因为它们生成数值表示。 as they generate a numerical representation. 51 @@ -265,42 +265,42 @@ A word can only have access to its left context 54 00:02:34,530 --> 00:02:36,690 -只能访问他们的左上下文。 +因为它只有左侧的上下文信息。 having only access to their left context. 55 00:02:36,690 --> 00:02:39,120 -他们天生擅长文本生成 +它们天生擅长文本生成 They're inherently good at text generation 56 00:02:39,120 --> 00:02:41,010 -生成单词的能力 +即在已知的词序列基础上生成一个单词 the ability to generate a word 57 00:02:41,010 --> 00:02:45,000 -或给定已知单词序列的单词序列。 +或单词序列的能力。 or a sequence of words given a known sequence of words. 58 00:02:45,000 --> 00:02:45,833 -这是众所周知的 +这被称为 This is known 59 00:02:45,833 --> 00:02:49,083 -作为因果语言建模或自然语言生成。 +因果语言建模或自然语言生成。 as causal language modeling or natural language generation. 60 00:02:50,430 --> 00:02:53,520 -这是因果语言建模如何工作的示例。 +下面是一个展示因果语言模型的工作原理的示例。 Here's an example of how causal language modeling works. 61 00:02:53,520 --> 00:02:56,410 -我们从一个词开始,这是我的 +我们从一个词 my 开始, We start with an initial word, which is my 62 @@ -320,52 +320,52 @@ and this vector contains information about the sequence 65 00:03:07,230 --> 00:03:08,733 -这是一个词。 +这里的序列是一个单词。 which is here a single word. 66 00:03:09,780 --> 00:03:11,430 -我们应用一个小的转换 +我们对该向量 We apply a small transformation 67 00:03:11,430 --> 00:03:13,110 -到那个向量,以便它映射 +应用一个小的转换 to that vector so that it maps 68 00:03:13,110 --> 00:03:16,500 -到模型已知的所有单词,这是一个映射 +使其映射到模型已知的所有单词 to all the words known by the model, which is a mapping 69 00:03:16,500 --> 00:03:19,890 -我们稍后会看到称为语言建模头。 +这个映射我们稍后会看到,称为语言模型头部信息 that we'll see later called a language modeling head. 70 00:03:19,890 --> 00:03:21,930 -我们确定该模型相信 +我们发现模型认为 We identify that the model believes 71 00:03:21,930 --> 00:03:25,053 -最有可能的后续单词是 name。 +接下来最有可能的单词是 “name”。 that the most probable following word is name. 72 00:03:26,250 --> 00:03:28,710 -然后我们取那个新词并添加它 +然后我们把这个新单词加到原始的序列 my 后面 We then take that new word and add it 73 00:03:28,710 --> 00:03:33,480 -到我的初始序列,我们现在以我的名字命名。 +我们得到了 my name。 to the initial sequence from my, we are now at my name. 74 00:03:33,480 --> 00:03:36,870 -这就是自回归方面的用武之地。 +这就是自回归(auto-regressive)的作用所在。 This is where the auto regressive aspect comes in. 75 @@ -375,7 +375,7 @@ Auto regressive models. 76 00:03:38,490 --> 00:03:42,513 -我们使用他们过去的输出作为输入和以下步骤。 +我们使用它们过去的输出作为输入和接下来的步骤。 We use their past outputs as inputs and the following steps. 77 @@ -395,7 +395,7 @@ and retrieve the most probable following word. 80 00:03:52,978 --> 00:03:57,978 -本例中就是 “是” 这个词,我们重复操作 +本例中就是 “is” 这个词,我们重复操作 In this case, it is the word "is", we repeat the operation 81 @@ -410,13 +410,13 @@ We've now generated a full sentence. 83 00:04:04,590 --> 00:04:07,890 -我们决定就此打住,但我们可以继续一段时间。 +我们决定就此打住,但我们也可以继续一段时间。 We decide to stop there, but we could continue for a while. 84 00:04:07,890 --> 00:04:12,890 -例如,GPT 2 的最大上下文大小为 1,024。 -GPT two, for example, has a maximum context size of 1,024. +例如,GPT-2 的最大上下文大小为 1,024。 +GPT-2, for example, has a maximum context size of 1,024. 85 00:04:13,170 --> 00:04:16,830 @@ -425,11 +425,11 @@ We could eventually generate up to a 1,024 words 86 00:04:16,830 --> 00:04:19,050 -并且解码器仍然会有一些记忆 +并且解码器仍然会对这个序列的前几个单词 and the decoder would still have some memory 87 00:04:19,050 --> 00:04:21,003 -这个序列中的第一个单词。 +有一些记忆。 of the first words in this sequence. diff --git a/subtitles/zh-CN/07_transformer-models-encoder-decoders.srt b/subtitles/zh-CN/07_transformer-models-encoder-decoders.srt index 28b847a27..c4303f575 100644 --- a/subtitles/zh-CN/07_transformer-models-encoder-decoders.srt +++ b/subtitles/zh-CN/07_transformer-models-encoder-decoders.srt @@ -10,42 +10,42 @@ 3 00:00:05,063 --> 00:00:07,638 -我们将研究编码器 - 解码器架构。 +我们将研究编码-解码器架构。 we'll study the encoder-decoder architecture. 4 00:00:07,638 --> 00:00:12,243 -流行的编码器 - 解码器模型的一个示例是 T5。 +流行的编码-解码器模型的一个示例是 T5。 An example of a popular encoder-decoder model is T5. 5 00:00:13,770 --> 00:00:16,980 -为了理解编码器 - 解码器是如何工作的, +为了理解编码-解码器是如何工作的, In order to understand how the encoder-decoder works, 6 00:00:16,980 --> 00:00:18,630 -我们建议你查看视频 +我们建议您查看 we recommend you check out the videos 7 00:00:18,630 --> 00:00:22,590 -“Encoders and Decoders as standalone models”。 +将编码-解码器作为独立的模型(Encoders and Decoders as standalone models)这一视频。 on encoders and decoders as standalone models. 8 00:00:22,590 --> 00:00:24,990 -了解他们如何单独工作 +了解它们如何单独工作 Understanding how they work individually 9 00:00:24,990 --> 00:00:28,323 -将有助于理解编码器 - 解码器的工作原理。 +将有助于理解编码-解码器的工作原理。 will help understanding how an encoder-decoder works. 10 00:00:30,510 --> 00:00:33,390 -让我们从我们所看到的编码器开始。 +让我们从我们已了解的编码器开始。 Let's start from what we've seen about the encoder. 11 @@ -55,27 +55,27 @@ The encoder takes words as inputs, 12 00:00:36,240 --> 00:00:38,520 -通过编码器投射它们, +通过编码器进行转换, casts them through the encoder, 13 00:00:38,520 --> 00:00:40,800 -并检索数字表示 +并检索每个单词的 and retrieves a numerical representation 14 00:00:40,800 --> 00:00:42,663 -对于通过它的每个单词。 +数值表示。 for each word cast through it. 15 00:00:43,560 --> 00:00:46,470 -我们现在知道这个数字表示 +我们现在知道这个数值表示 We now know that this numerical representation 16 00:00:46,470 --> 00:00:49,473 -包含有关序列含义的信息。 +包含关于序列意义的信息。 holds information about the meaning of the sequence. 17 @@ -90,12 +90,12 @@ In this scenario, 19 00:00:57,510 --> 00:00:59,190 -我们以某种方式使用解码器 +我们以某种我们以前没见过的方式 we're using the decoder in a manner 20 00:00:59,190 --> 00:01:00,960 -我们以前没见过。 +使用解码器。 that we haven't seen before. 21 @@ -105,37 +105,37 @@ We're passing the outputs of the encoder directly to it. 22 00:01:05,356 --> 00:01:07,770 -除了编码器输出, +另外,在给解码器输入序列的同时 Additionally to the encoder outputs, 23 00:01:07,770 --> 00:01:10,800 -我们还给解码器一个序列。 +我们还需要编码器的输出。 we also give the decoder a sequence. 24 00:01:10,800 --> 00:01:12,840 -当提示解码器输出时 +在不给定初始序列的情况下 When prompting the decoder for an output 25 00:01:12,840 --> 00:01:14,190 -没有初始序列, +向解码器提示输出时, with no initial sequence, 26 00:01:14,190 --> 00:01:16,140 -我们可以给它指示的值 +我们可以给它一个 we can give it the value that indicates 27 00:01:16,140 --> 00:01:18,060 -序列的开始。 +表示序列开头的值。 the start of a sequence. 28 00:01:18,060 --> 00:01:20,919 -这就是编码器 - 解码器魔术发生的地方。 +这就是编码-解码器魔术发生的地方。 And that's where the encoder-decoder magic happens. 29 @@ -150,7 +150,7 @@ It computes a prediction, 31 00:01:25,980 --> 00:01:28,858 -并输出一个数字表示。 +并输出一个数值表示。 and outputs a numerical representation. 32 @@ -165,7 +165,7 @@ It has, in a sense, encoded that sequence. 34 00:01:36,300 --> 00:01:38,130 -反过来,解码器, +反过来,解码器 And the decoder, in turn, 35 @@ -235,7 +235,7 @@ As we have seen before with the decoder, 48 00:02:15,540 --> 00:02:18,720 -它可以以自动回归的方式起作用。 +它可以以自回归的方式起作用。 it can act in an auto-regressive manner. 49 @@ -245,17 +245,17 @@ The word it has just output can now be used as an input. 50 00:02:22,933 --> 00:02:26,188 -这个,结合数值表示 +这个编码器输出的数值表示 This, in combination with the numerical representation 51 00:02:26,188 --> 00:02:28,560 -编码器输出, +和初始化的值结合, output by the encoder, 52 00:02:28,560 --> 00:02:31,203 -现在可用于生成第二个单词。 +可以被用于生成第二个单词。 can now be used to generate a second word. 53 @@ -285,27 +285,27 @@ We can continue on and on, for example, 58 00:02:44,070 --> 00:02:46,320 -直到解码器输出一个值 +直到解码器输出一个 until the decoder outputs a value 59 00:02:46,320 --> 00:02:48,540 -我们考虑一个停止值, +我们认为是停止值的数值, that we consider a stopping value, 60 00:02:48,540 --> 00:02:51,093 -就像一个点,表示序列的结尾。 +比如句号表示序列的结束。 like a dot meaning the end of a sequence. 61 00:02:53,580 --> 00:02:55,926 -在这里,我们已经看到了完整的机制 +在这里,我们已经看到了编码-解码 transformer Here, we've seen the full mechanism 62 00:02:55,926 --> 00:02:57,540 -编码器 - 解码器变压器。 +完整的机制。 of the encoder-decoder transformer. 63 @@ -315,12 +315,12 @@ Let's go over it one more time. 64 00:02:59,280 --> 00:03:02,773 -我们有一个发送到编码器的初始序列。 +我们有一个初始序列被送到编码器中。 We have an initial sequence that is sent to the encoder. 65 00:03:02,773 --> 00:03:06,450 -然后将该编码器输出发送到解码器 +编码器的输出发送到解码器 That encoder output is then sent to the decoder 66 @@ -330,32 +330,32 @@ for it to be decoded. 67 00:03:08,760 --> 00:03:12,450 -虽然它现在可以在一次使用后丢弃编码器, +虽然在一次使用后可以丢弃编码器, While it can now discard the encoder after a single use, 68 00:03:12,450 --> 00:03:14,427 -解码器将被多次使用 +但解码器将被多次使用 the decoder will be used several times 69 00:03:14,427 --> 00:03:17,763 -直到我们生成了我们需要的每一个词。 +直到我们生成了所需要的每一个词。 until we have generated every word that we need. 70 00:03:19,288 --> 00:03:21,510 -那么让我们看一个具体的例子 +那么让我们结合翻译语言建模 So let's see a concrete example 71 00:03:21,510 --> 00:03:23,460 -与翻译语言建模。 +看一个具体的例子。 with Translation Language Modeling. 72 00:03:23,460 --> 00:03:24,930 -也称为转导, +也称为传导, Also called transduction, 73 @@ -370,42 +370,42 @@ Here, we would like to translate this English sequence 75 00:03:30,577 --> 00:03:33,067 -法语 “欢迎来到纽约”。 +“Welcome to NYC”到法语。 "Welcome to NYC" in French. 76 00:03:33,067 --> 00:03:35,460 -我们正在使用变压器模型 +我们正在使用 transformer 模型 We're using a transformer model 77 00:03:35,460 --> 00:03:38,070 -明确针对该任务进行了培训。 +明确针对该任务进行了训练。 that is trained for that task explicitly. 78 00:03:38,070 --> 00:03:40,560 -我们使用编码器来创建表示 +我们使用编码器来创建英语句子 We use the encoder to create a representation 79 00:03:40,560 --> 00:03:42,240 -的英语句子。 +的表示。 of the English sentence. 80 00:03:42,240 --> 00:03:44,730 -我们把它投给解码器, +我们使用编码器来创建英语句子的表示形式, We cast this to the decoder, 81 00:03:44,730 --> 00:03:46,620 -使用序列字的开头, +然后将其传递给解码器, with the use of the start of sequence word, 82 00:03:46,620 --> 00:03:49,173 -我们要求它输出第一个单词。 +在使用开始序列单词的情况下,请求它输出第一个单词。 we ask it to output the first word. 83 @@ -445,7 +445,7 @@ Finally, we ask the decoder to predict a third word 90 00:04:13,590 --> 00:04:15,330 -它预测纽约市,这是正确的。 +它预测 NYC,这是正确的。 It predicts NYC, which is correct. 91 @@ -455,7 +455,7 @@ We've translated the sentence. 92 00:04:18,288 --> 00:04:20,760 -编码器 - 解码器真正发挥作用的地方, +编码-解码器真正发挥作用的地方, Where the encoder-decoder really shines, 93 @@ -475,7 +475,7 @@ Therefore, we have an entire block, the encoder, 96 00:04:29,460 --> 00:04:31,650 -可以训练以理解序列 +可以被训练,从而理解序列 that can be trained to understand the sequence 97 @@ -515,12 +515,12 @@ On the other hand, we have the decoder, 104 00:04:53,370 --> 00:04:56,850 -其唯一目的是解码数字表示 +它的唯一目的是解码 whose sole purpose is to decode the numerical representation 105 00:04:56,850 --> 00:04:58,203 -编码器输出。 +编码器输出的数值表示。 output by the encoder. 106 @@ -540,12 +540,12 @@ or even modality like images or speech. 109 00:05:07,170 --> 00:05:10,473 -编码器 - 解码器之所以特殊,有几个原因。 +编码-解码器之所以特殊,有几个原因。 Encoders-decoders are special for several reasons. 110 00:05:11,310 --> 00:05:15,570 -首先,他们能够管理任务的顺序, +首先,它们能够管理任务的顺序, Firstly, they're able to manage sequence to sequence tasks, 111 @@ -555,12 +555,12 @@ like translation that we have just seen. 112 00:05:18,358 --> 00:05:20,940 -其次,编码器之间的权重 +其次,编码器和解码器之间的权重 Secondly, the weights between the encoder 113 00:05:20,940 --> 00:05:24,540 -并且解码器部分不一定共享。 +并不一定共享。 and the decoder parts are not necessarily shared. 114 @@ -570,12 +570,12 @@ Let's take another example of translation. 115 00:05:27,172 --> 00:05:30,810 -这里我们用法语翻译 Transformers are powerful +这里我们用法语 Here we're translating "Transformers are powerful" 116 00:05:30,810 --> 00:05:32,048 -这里我们用法语翻译 Transformers are powerful +翻译 Transformers are powerful in French. 117 @@ -595,12 +595,12 @@ One could argue that this could be handled with a decoder 120 00:05:42,480 --> 00:05:44,160 -那会产生翻译 +那会以自回归的方式 that would generate the translation 121 00:05:44,160 --> 00:05:46,260 -以自回归的方式, +生成翻译结果, in an auto-regressive manner, 122 @@ -610,12 +610,12 @@ and they would be right. 123 00:05:49,980 --> 00:05:51,930 -基于 Transformers 的 Seq2Seq 模型的 +基于 Transformers Seq2Seq 模型的 Another example of where sequence to sequence 124 00:05:51,930 --> 00:05:54,810 -另一个亮点是总结 +另一个亮点是进行总结的能力 transformers shine is in summarization. 125 @@ -650,7 +650,7 @@ which handles the text, 131 00:06:10,230 --> 00:06:12,210 -和解码器的较小上下文 +尔解码器上下文则较小 and a smaller context for the decoder 132 @@ -660,47 +660,47 @@ which handles the summarized sequence. 133 00:06:16,470 --> 00:06:18,840 -有很多序列模型。 +有很多序列到序列的模型。 There are a lot of sequence to sequence models. 134 00:06:18,840 --> 00:06:20,310 -这包含一些例子 +这包含了 Transformers 库中 This contains a few examples 135 00:06:20,310 --> 00:06:22,500 -流行的编码器 - 解码器模型 +几个受欢迎的 of popular encoder-decoder models 136 00:06:22,500 --> 00:06:24,400 -在 Transformers 库中可用。 +编码-解码器模型的示例。 available in the transformers library. 137 00:06:25,829 --> 00:06:29,940 -此外,你可以加载编码器和解码器 +此外,您可以在编码-解码器模型中 Additionally, you can load an encoder and a decoder 138 00:06:29,940 --> 00:06:32,130 -在编码器 - 解码器模型中。 +加载编码器和解码器。 inside an encoder-decoder model. 139 00:06:32,130 --> 00:06:35,190 -因此,根据你针对的具体任务, +因此,根据您要解决的具体任务, Therefore, according to the specific task you are targeting, 140 00:06:35,190 --> 00:06:38,700 -你可以选择使用特定的编码器和解码器, +您可能会选择使用在这些具体任务上证明 you may choose to use specific encoders and decoders, 141 00:06:38,700 --> 00:06:42,613 -在这些特定任务中证明了它们的价值。 +其价值的特定编码器和解码器。 which have proven their worth on these specific tasks. 142 diff --git a/subtitles/zh-CN/08_what-happens-inside-the-pipeline-function-(pytorch).srt b/subtitles/zh-CN/08_what-happens-inside-the-pipeline-function-(pytorch).srt index ca6c0276f..48d021499 100644 --- a/subtitles/zh-CN/08_what-happens-inside-the-pipeline-function-(pytorch).srt +++ b/subtitles/zh-CN/08_what-happens-inside-the-pipeline-function-(pytorch).srt @@ -5,7 +5,8 @@ 2 00:00:05,340 --> 00:00:07,563 -- 管道函数内部发生了什么? +- pipeline 函数内部发生了什么? +*[译者注: pipeline 作为 流水线 的意思] - What happens inside the pipeline function? 3 @@ -25,22 +26,22 @@ of the Transformers library. 6 00:00:15,090 --> 00:00:16,860 -更具体地说,我们将看看 +详细来讲,我们将看 More specifically, we will look 7 00:00:16,860 --> 00:00:19,200 -在情绪分析管道中, +在情绪分析的 pipeline 中, at the sentiment analysis pipeline, 8 00:00:19,200 --> 00:00:22,020 -以及它是如何从以下两个句子开始的, +它是如何从以下两个句子开始的, and how it went from the two following sentences, 9 00:00:22,020 --> 00:00:23,970 -正负标签 +将正负标签 to the positive and negative labels 10 @@ -50,12 +51,12 @@ with their respective scores. 11 00:00:26,760 --> 00:00:29,190 -正如我们在管道演示中看到的那样, +正如我们在 pipeline 展示中看到的那样, As we have seen in the pipeline presentation, 12 00:00:29,190 --> 00:00:31,860 -管道分为三个阶段。 +pipeline 分为三个阶段。 there are three stages in the pipeline. 13 @@ -65,7 +66,7 @@ First, we convert the raw texts to numbers 14 00:00:34,620 --> 00:00:37,173 -该模型可以理解使用分词器。 +该模型可以通过使用分词器理解。 the model can make sense of using a tokenizer. 15 @@ -75,17 +76,17 @@ Then those numbers go through the model, 16 00:00:40,530 --> 00:00:41,943 -输出逻辑。 +输出 logits 。 which outputs logits. 17 00:00:42,780 --> 00:00:45,600 -最后,后处理步骤变换 +最后,后处理步骤转换 Finally, the post-processing steps transforms 18 00:00:45,600 --> 00:00:48,150 -那些登录到标签和分数。 +那些 logits 包含标签和分数。 those logits into labels and scores. 19 @@ -100,17 +101,18 @@ and how to replicate them using the Transformers library, 21 00:00:53,640 --> 00:00:56,043 -从第一阶段开始,标记化。 +从第一阶段开始,分词化。 beginning with the first stage, tokenization. 22 00:00:57,915 --> 00:01:00,360 -令牌化过程有几个步骤。 +分词化过程有几个步骤。 The tokenization process has several steps. 23 00:01:00,360 --> 00:01:04,950 -首先,文本被分成称为标记的小块。 +首先,文本被分成小块, 称之为 token。 +*[译者注: 后面 token-* 均翻译成 分词-*] First, the text is split into small chunks called tokens. 24 @@ -120,7 +122,7 @@ They can be words, parts of words or punctuation symbols. 25 00:01:08,550 --> 00:01:11,580 -然后 tokenizer 将有一些特殊的标记, +然后分词器将有一些特殊的 token , Then the tokenizer will had some special tokens, 26 @@ -130,17 +132,17 @@ if the model expect them. 27 00:01:13,500 --> 00:01:16,860 -这里的模型在开头使用期望 CLS 令牌 +这里的模型在开头使用期望 CLS token Here the model uses expects a CLS token at the beginning 28 00:01:16,860 --> 00:01:19,743 -以及用于分类的句子末尾的 SEP 标记。 +以及用于分类的句子末尾的 SEP token。 and a SEP token at the end of the sentence to classify. 29 00:01:20,580 --> 00:01:24,180 -最后,标记器将每个标记与其唯一 ID 匹配 +最后,分词器将每个 token 与其唯一 ID 匹配 Lastly, the tokenizer matches each token to its unique ID 30 @@ -180,7 +182,7 @@ Here the checkpoint used by default 37 00:01:45,360 --> 00:01:47,280 -用于情绪分析管道 +用于情绪分析的 pipeline for the sentiment analysis pipeline 38 @@ -250,7 +252,7 @@ Looking at the result, we see we have a dictionary 51 00:02:25,590 --> 00:02:26,670 -用两把钥匙。 +和两个主键 with two keys. 52 @@ -265,7 +267,7 @@ with zero where the padding is applied. 54 00:02:32,550 --> 00:02:34,260 -第二把钥匙,注意面具, +第二个键值,注意力掩码, The second key, attention mask, 55 @@ -280,7 +282,7 @@ so the model does not pay attention to it. 57 00:02:38,940 --> 00:02:42,090 -这就是标记化步骤中的全部内容。 +这就是分词化步骤中的全部内容。 This is all what is inside the tokenization step. 58 @@ -350,7 +352,7 @@ for our classification problem. 71 00:03:15,030 --> 00:03:19,230 -这里的张量有两个句子,每个句子有 16 个标记, +这里的张量有两个句子,每个句子有 16 个 token , Here the tensor has two sentences, each of 16 tokens, 72 @@ -425,12 +427,12 @@ This is because each model 86 00:03:57,270 --> 00:04:00,810 -每个模型都会返回 logits。 +每个模型都会返回 logits 。 of the Transformers library returns logits. 87 00:04:00,810 --> 00:04:02,250 -为了理解这些逻辑, +为了理解这些 logits , To make sense of those logits, 88 @@ -490,7 +492,7 @@ correspond to the negative label, 99 00:04:32,250 --> 00:04:34,140 -秒,索引一, +然后第二个,索引一, and the seconds, index one, 100 @@ -505,7 +507,7 @@ This is how our classifier built 102 00:04:37,950 --> 00:04:40,230 -使用管道功能选择了那些标签 +使用 pipeline 功能选择了那些标签 with the pipeline function picked those labels 103 diff --git a/subtitles/zh-CN/09_what-happens-inside-the-pipeline-function-(tensorflow).srt b/subtitles/zh-CN/09_what-happens-inside-the-pipeline-function-(tensorflow).srt index 1983a6ea6..4e6d58930 100644 --- a/subtitles/zh-CN/09_what-happens-inside-the-pipeline-function-(tensorflow).srt +++ b/subtitles/zh-CN/09_what-happens-inside-the-pipeline-function-(tensorflow).srt @@ -75,7 +75,8 @@ Then, those numbers go through the model, 16 00:00:42,600 --> 00:00:44,550 -输出逻辑。 +输出 logits 。 +*[译者注: logits 作为逻辑值的意思] which outputs logits. 17 @@ -95,22 +96,22 @@ Let's look in detail at those three steps, 20 00:00:52,590 --> 00:00:55,200 -以及如何使用 Transformers 库复制它们, +以及如何使用 Transformers 库复现它们, and how to replicate them using the Transformers library, 21 00:00:55,200 --> 00:00:57,903 -从第一阶段开始,标记化。 +从第一阶段开始,分词化。 beginning with the first stage, tokenization. 22 00:00:59,905 --> 00:01:02,520 -令牌化过程有几个步骤。 +分词化过程有几个步骤。 The tokenization process has several steps. 23 00:01:02,520 --> 00:01:06,900 -首先,文本被分成称为标记的小块。 +首先,文本被分成称为 token 的小块。 First, the text is split into small chunks called token. 24 @@ -120,7 +121,7 @@ They can be words, parts of words or punctuation symbols. 25 00:01:10,800 --> 00:01:14,310 -然后 tokenizer 将有一些特殊的标记 +然后分词器将有一些特殊的 token Then the tokenizer will had some special tokens 26 @@ -130,12 +131,12 @@ if the model expect them. 27 00:01:16,440 --> 00:01:20,430 -在这里,所使用的模型在开头需要一个 CLS 令牌 +在这里,所使用的模型在开头需要一个 CLS token Here, the model used expects a CLS token at the beginning 28 00:01:20,430 --> 00:01:23,910 -以及用于分类的句子末尾的 SEP 标记。 +以及用于分类的句子末尾的 SEP token。 and a SEP token at the end of the sentence to classify. 29 @@ -170,7 +171,8 @@ which will download and cache the configuration 35 00:01:41,940 --> 00:01:44,913 -以及与给定检查点相关联的词汇表。 +以及与给定 checkpoint 相关联的词汇表。 +*[译者注: 在深度学习中, checkpoint 作为检查点是用来备份模型的, 后不翻译] and the vocabulary associated to a given checkpoint. 36 @@ -180,13 +182,13 @@ Here, the checkpoint used by default 37 00:01:48,180 --> 00:01:50,310 -用于情绪分析管道 +用于情绪分析的 pipeline for the sentiment analysis pipeline 38 00:01:50,310 --> 00:01:54,510 -是 distilbert base uncased finetuned sst2 英语, -is distilbert base uncased finetuned sst2 English, +是 distilbert-base-uncased-finetuned-sst2-English, +is distilbert-base-uncased-finetuned-sst2-English, 39 00:01:54,510 --> 00:01:55,960 @@ -195,7 +197,7 @@ which is a bit of a mouthful. 40 00:01:56,820 --> 00:01:59,760 -我们实例化一个与该检查点关联的分词器, +我们实例化一个与该 checkpoint 关联的分词器, We instantiate a tokenizer associated with that checkpoint, 41 @@ -270,7 +272,7 @@ with zeros where the padding is applied. 55 00:02:36,750 --> 00:02:38,550 -第二把钥匙,注意面具, +第二把钥匙,注意力掩码, The second key, attention mask, 56 @@ -320,7 +322,7 @@ However, the AutoModel API will only instantiate 65 00:03:04,830 --> 00:03:06,540 -模特的身体, +模型的主体, the body of the model, 66 @@ -360,7 +362,7 @@ Here the tensor has two sentences, 73 00:03:24,210 --> 00:03:26,070 -每十六个令牌, +每十六个 token, each of sixteen token, 74 @@ -435,12 +437,12 @@ This is because each model of the Transformers library 88 00:04:06,090 --> 00:04:07,830 -返回逻辑。 +返回 logits 。 returns logits. 89 00:04:07,830 --> 00:04:09,480 -为了理解这些逻辑, +为了理解这些 logits , To make sense of those logits, 90 diff --git a/subtitles/zh-CN/10_instantiate-a-transformers-model-(pytorch).srt b/subtitles/zh-CN/10_instantiate-a-transformers-model-(pytorch).srt index f058fdebf..efa2fc2d7 100644 --- a/subtitles/zh-CN/10_instantiate-a-transformers-model-(pytorch).srt +++ b/subtitles/zh-CN/10_instantiate-a-transformers-model-(pytorch).srt @@ -10,107 +10,108 @@ 3 00:00:08,483 --> 00:00:11,790 -在本视频中,我们将了解如何创建用户模型 +在本视频中,我们会带您了解我们如何能创建自己的模型 In this video, we'll look at how we can create a user model 4 00:00:11,790 --> 00:00:13,290 -来自变形金刚图书馆。 +用 Transformers 库创。 from the Transformers library. 5 00:00:14,310 --> 00:00:17,100 -正如我们之前看到的 AutoModel 类允许 +正如我们之前看到的,AutoModel 类允许 As we have seen before the AutoModel class allows 6 00:00:17,100 --> 00:00:19,140 -你实例化一个预训练模型 +您去实例化一个预训练的模型。 you to instantiate a pretrained model 7 00:00:19,140 --> 00:00:21,513 -从 Hugging Face Hub 上的任何检查站。 +从 Hugging Face Hub 的任何 checkpoint +*[译者注: checkpoint 意思是 检查点, 作为训练模型在训练时的备份] from any checkpoint on the Hugging Face Hub. 8 00:00:22,350 --> 00:00:23,910 -它会选择正确的模型类 +它会挑选合适的模型类 It'll pick the right model class 9 00:00:23,910 --> 00:00:26,654 -从库中实例化适当的体系结构 +从开源库中, 以实例化对应的结构 from the library to instantiate the proper architecture 10 00:00:26,654 --> 00:00:29,793 -和大量的权重作为内部的预训练模型。 +和权重来作为预训练模型。 and loads of weights as the pretrained model inside. 11 00:00:30,690 --> 00:00:33,810 -正如我们所见,当给定一个 BERT 检查点时 +正如我们所见,当给定一个 BERT checkpoint 时, As we can see, when given a BERT checkpoint 12 00:00:33,810 --> 00:00:38,043 -我们最终得到一个 BertModel,类似地,对于 GPT-2 或 BART。 -we end up with a BertModel and similarly, for GPT-2 or BART. +我们最终会得到一个 BertModel ,类似地,模型 GPT-2 或 BERT 也可以这么做。 +we end up with a BertModel and similarly, for GPT-2 or BERT. 13 00:00:40,020 --> 00:00:42,360 -在幕后,这个 API 可以取名字 +背后的信息是,这个 API 可以接受 Behind the scenes,this API can take the name 14 00:00:42,360 --> 00:00:44,250 -集线器上的检查点 +Hub 上一个 checkpoint 的名字 of a checkpoint on the Hub 15 00:00:44,250 --> 00:00:46,980 -在这种情况下,它将下载并缓存配置 -in which case it will download and cache the configuration +在这种情况下,它将下载和缓存配置文件 +in which case it will download and cache the configuration file 16 00:00:46,980 --> 00:00:48,843 -文件以及模型权重文件。 -file as well as a model weights file. +以及模型权重文件。 +as well as a model weights file. 17 00:00:49,698 --> 00:00:52,710 -你还可以指定本地文件夹的路径 +您也可以指定一个本地文件夹的路径, You can also specify the path to a local folder 18 00:00:52,710 --> 00:00:55,290 -包含一个有效的配置文件和一个 -that contains a valid configuration file and a +这个文件夹包含一个有效的配置文件和 +that contains a valid configuration file and 19 00:00:55,290 --> 00:00:56,390 -权重文件的模型。 -model of weights file. +一个权重模型文件。 +a model of weights file. 20 00:00:57,600 --> 00:00:59,479 -要实例化预训练模型, +为了实例化预训练的模型, To instantiate the pretrained model, 21 00:00:59,479 --> 00:01:01,950 -AutoModel API 将首先打开配置 +AutoModel API 会先打开配置文件 the AutoModel API will first open the configuration 22 00:01:01,950 --> 00:01:05,403 -文件来查看应该使用的配置类。 +查看应该使用的配置类。 file to look at a configuration class that should be used. 23 00:01:06,420 --> 00:01:08,580 -配置类取决于类型 +配置类取决于模型的类型, The configuration class depends on the type 24 @@ -120,42 +121,42 @@ of the model BERT, GPT-2 or BART for instance. 25 00:01:13,680 --> 00:01:15,930 -一旦它有一个合适的配置类, +一旦有了一个合适的配置类, Once it has a proper configuration class, 26 00:01:15,930 --> 00:01:18,390 -它可以实例化该配置 +就可以实例化那个配置, it can instantiate that configuration 27 00:01:18,390 --> 00:01:21,900 -这是了解如何创建模型的蓝图。 +其包含一张关于如何创建模型的蓝图。 which is a blueprint to know how to create the model. 28 00:01:21,900 --> 00:01:24,240 -它还使用这个配置类来 +它还使用这个配置类 It also uses this configuration class to 29 00:01:24,240 --> 00:01:27,150 -找到合适的模型类,然后合并 +去寻找合适的模型类,其然后被 find the proper model class, which is then combined 30 00:01:27,150 --> 00:01:29,823 -使用加载的配置加载模型。 +和加载后的配置一起来加载模型。 with the loaded configuration to load the model. 31 00:01:30,904 --> 00:01:33,210 -该模型还不是预训练模型 +这个模型还不是一个预训练模型, This model is not yet a pretrained model 32 00:01:33,210 --> 00:01:35,883 -因为它刚刚用随机权重初始化。 +因为它刚刚用随机权重完成了初始化。 as it has just been initialized with random weights. 33 @@ -165,23 +166,23 @@ The last step is to load the weight from the model file 34 00:01:39,810 --> 00:01:40,923 -在这个模型里面。 +到模型里 inside this model. 35 00:01:42,330 --> 00:01:44,250 -轻松加载模型的配置 +为了方便地加载一个模型的配置 To easily load the configuration of a model 36 -00:01:44,250 --> 00:01:46,410 -从任何检查点或文件夹包含 -from any checkpoint or folder containing +00:01:44,250 --> 00:01:48,210 +从任何 checkpoint 或包含配置文件的文件夹中 +from any checkpoint or folder containing the configuration file. 37 -00:01:46,410 --> 00:01:48,210 -配置文件。 -the configuration file. +00:01:48,210 --> 00:01:48,210 +. +. 38 00:01:48,210 --> 00:01:50,373 @@ -195,52 +196,52 @@ Like the AutoModel class, 40 00:01:52,693 --> 00:01:55,693 -它将从库中选择正确的配置类。 +它将从开源库中挑选合适的配置类。 it will pick the right configuration class from the library. 41 00:01:57,060 --> 00:01:59,220 -我们也可以使用特定的类对应 +我们也可以使用一个特定的类来对应 We can also use a specific class corresponding 42 00:01:59,220 --> 00:02:01,470 -到一个检查站,但我们需要改变 +一个 checkpoint ,但每次我们需要 to a checkpoint, but we will need to change 43 00:02:01,470 --> 00:02:03,000 -每次我们想尝试的代码 +改代码, 每当我们想尝试 the code each time we want to try 44 00:02:03,000 --> 00:02:04,550 -不同的模型架构。 +不同的模型结构时. a different model architecture. 45 00:02:06,030 --> 00:02:07,860 -正如我们之前所说,配置 +正如我们刚才所说的,一个模型的配置就是 As we said before, the configuration 46 00:02:07,860 --> 00:02:10,350 -模型的蓝图包含所有 +一张蓝图,其包括了 of a model is a blueprint that contains all the 47 00:02:10,350 --> 00:02:13,830 -创建模型架构所需的信息。 +创建模型架构所需的所有信息。 information necessary to create the model architecture. 48 00:02:13,830 --> 00:02:15,990 -例如,关联的 BERT 模型 +例如,关联到 bert-base-cased checkpoint 的 For instance, the BERT model associated 49 00:02:15,990 --> 00:02:19,980 -bert-base-cased 检查点有 12 层, +BERT 模型有 12 层, with the bert-base-cased checkpoint has 12 layers, 50 @@ -255,52 +256,52 @@ Once we have the configuration, 52 00:02:29,910 --> 00:02:31,950 -我们可以创建一个具有相同架构的模型 -we can create a model that does the same architecture +我们就可以创建一个和 checkpoint 有着同样架构的模型, +we can create a model that does the same architecture as our checkpoint, 53 00:02:31,950 --> 00:02:35,280 -作为我们的检查点,但是是随机初始化的。 -as our checkpoint, but is randomly initialized. +但是模型是随机初始化的。 +but is randomly initialized. 54 00:02:35,280 --> 00:02:36,660 -然后我们可以从头开始训练它。 +然后我们可以从头开始训练它, We can then train it from scratch. 55 00:02:36,660 --> 00:02:38,010 -像任何生物 PyTorch 模块一样 +就像任何 bio PyTorch 模块一样 Like any bio PyTorch module 56 00:02:39,497 --> 00:02:40,380 -我们也可以改变任何部分 +我们也可以通过改变 We can also change any part 57 00:02:40,380 --> 00:02:43,200 -通过使用关键字参数的配置。 +配置的任何部分, 使用关键字参数 of the configuration by using keyword arguments. 58 00:02:43,200 --> 00:02:46,138 -第二段代码实例化 +第二段代码实例化了 The second snippet of code instantiates 59 00:02:46,138 --> 00:02:48,360 -随机初始化的 BERT 模型 +一个随机初始化的 BERT 模型, a randomly initialized BERT model 60 00:02:48,360 --> 00:02:50,403 -有 10 层而不是 12 层。 +这个模型有 10 层而非 12 层。 with 10 layers instead of 12. 61 00:02:51,409 --> 00:02:55,051 -训练或微调后保存模型非常容易。 +一个模型被训练或微调后,想要保存这个模型是很容易的。 Saving a model once it's trained or fine-tuned is very easy. 62 @@ -310,31 +311,31 @@ We just have to use a safe pretrained method. 63 00:02:58,500 --> 00:03:01,417 -此处模型将保存在名为 +这里模型将保存在当前工作目录下 Here the model will be saved in a folder named 64 00:03:01,417 --> 00:03:04,473 -当前工作目录中的 “my-bert-model”。 +一个名为 "my-bert-model" 的文件夹中。 "my-bert-model" inside the current working directory. 65 00:03:05,400 --> 00:03:08,255 -然后可以使用表单重新加载这样的模型 +然后,已保存的模型可以使用 Such a model can then be reloaded using the form 66 00:03:08,255 --> 00:03:09,596 -预训练方法。 +from_pretrained 函数重新加载进来。 pretrained method. 67 00:03:09,596 --> 00:03:11,250 -了解如何轻松处理此模型 +如果您要学习如何轻松地应用这个模型, To learn how to easily approach this model 68 00:03:11,250 --> 00:03:13,473 -为此,请查看对视频的推送。 +请查看课程中的相关视频。 to that, check out the push to a video. diff --git a/subtitles/zh-CN/11_instantiate-a-transformers-model-(tensorflow).srt b/subtitles/zh-CN/11_instantiate-a-transformers-model-(tensorflow).srt index c14d2347f..07dae038e 100644 --- a/subtitles/zh-CN/11_instantiate-a-transformers-model-(tensorflow).srt +++ b/subtitles/zh-CN/11_instantiate-a-transformers-model-(tensorflow).srt @@ -25,12 +25,12 @@ As we've seen before, the TFAutoModel class 6 00:00:17,850 --> 00:00:20,100 -允许你实例化预训练模型 +允许你实例化预训练模型, allows you to instantiate a pre-trained model 7 00:00:20,100 --> 00:00:22,503 -从 Hugging Face Hub 上的任何检查站。 +从 Hugging Face Hub 上的任何一个 checkpoint。 from any checkpoint on the Hugging Face Hub. 8 @@ -40,7 +40,7 @@ It will pick the right model class from the library 9 00:00:25,620 --> 00:00:27,750 -实例化适当的架构 +来实例化适当的结构 to instantiate the proper architecture 10 @@ -50,7 +50,7 @@ and load the weights of the pre-trained model inside. 11 00:00:31,200 --> 00:00:34,020 -正如我们所见,当给定一个 BERT 检查点时, +正如我们所见,当给定一个 BERT 的 checkpoint 时, As we can see, when given a BERT checkpoint, 12 @@ -65,12 +65,12 @@ and similarly for GPT2 or BART. 14 00:00:40,170 --> 00:00:42,510 -在幕后,这个 API 可以取名字 +在幕后,这个 API 接受 Behind the scenes, this API can take the name 15 00:00:42,510 --> 00:00:44,040 -集线器上的检查点, +Hub 上的一个 checkpoint 的名字, of a checkpoint on the Hub, 16 @@ -95,7 +95,7 @@ that contains a valid configuration file 20 00:00:54,090 --> 00:00:55,340 -和模型权重文件。 +和模型的权重文件。 and a model weights file. 21 @@ -105,12 +105,12 @@ To instantiate the pre-trained model, 22 00:00:58,167 --> 00:01:02,400 -TFAutoModel API 将首先打开配置文件 +TFAutoModel API 会首先打开配置文件 the TFAutoModel API will first open the configuration file 23 00:01:02,400 --> 00:01:05,253 -查看应该使用的配置类。 +以查看应该使用的配置类。 to look at the configuration class that should be used. 24 @@ -145,7 +145,7 @@ It also uses this configuration class 30 00:01:22,770 --> 00:01:24,750 -找到合适的模型类, +来找到合适的模型类, to find the proper model class, 31 @@ -175,23 +175,22 @@ The last step is to load the weights 36 00:01:36,690 --> 00:01:38,973 -来自该模型中的模型文件。 +从该模型中的模型文件中。 from the model file inside this model. 37 00:01:40,230 --> 00:01:42,270 -轻松加载模型的配置 +为了轻松加载模型的配置, To easily load the configuration of a model 38 00:01:42,270 --> 00:01:44,220 -从任何检查点或文件夹 -from any checkpoint or a folder - +从任何 checkpoint 或 +from any checkpoint or 39 00:01:44,220 --> 00:01:46,170 -包含配置文件, -containing the configuration file, +包含配置文件的文件夹, +a folder containing the configuration file, 40 00:01:46,170 --> 00:01:47,790 @@ -215,7 +214,7 @@ We can also use the specific class 44 00:01:56,040 --> 00:01:57,840 -对应一个检查点, +对应一个 checkpoint , corresponding to a checkpoint, 45 @@ -225,7 +224,7 @@ but we will need to change the code 46 00:01:59,430 --> 00:02:02,230 -每次我们都想尝试不同的模型架构。 +每当我们想尝试不同的模型结构时。 each time we want to try a different model architecture. 47 @@ -240,7 +239,7 @@ is a blueprint that contains all the information necessary 49 00:02:08,610 --> 00:02:11,070 -创建模型架构。 +以创建模型结构。 to create the model architecture. 50 @@ -250,7 +249,7 @@ For instance, the BERT model 51 00:02:12,750 --> 00:02:15,510 -与 bert-base-cased 检查点关联 +与 bert-base-cased 的 checkpoint 关联 associated with the bert-base-cased checkpoint 52 @@ -270,17 +269,17 @@ Once we have the configuration, 55 00:02:26,670 --> 00:02:28,890 -我们可以创建一个具有相同架构的模型 +我们可以创建一个具有相同结构的模型 we can create a model that has the same architecture 56 00:02:28,890 --> 00:02:32,160 -作为我们的检查点,但随机初始化。 +作为我们的 checkpoint ,但是随机初始化的。 as our checkpoint but is randomly initialized. 57 00:02:32,160 --> 00:02:36,030 -然后我们可以像任何 TensorFlow 模型一样从头开始训练它。 +我们然后就可以像任何 TensorFlow 模型一样从头开始训练它。 We can then train it from scratch like any TensorFlow model. 58 @@ -300,7 +299,7 @@ The second snippet of code instantiates 61 00:02:43,110 --> 00:02:44,970 -随机初始化的 BERT 模型 +一个随机初始化的 BERT 模型 a randomly initialized BERT model 62 diff --git a/subtitles/zh-CN/12_tokenizers-overview.srt b/subtitles/zh-CN/12_tokenizers-overview.srt index 92326f9b7..7940fd193 100644 --- a/subtitles/zh-CN/12_tokenizers-overview.srt +++ b/subtitles/zh-CN/12_tokenizers-overview.srt @@ -20,7 +20,8 @@ 5 00:00:04,920 --> 00:00:06,720 -我们将看一下分词器。 +我们将看一下分词器 +*[译者注: token, tokenization, tokenizer 等词均译成了 分词*, 实则不翻译最佳] we'll take a look at the tokenizers. 6 @@ -45,7 +46,7 @@ cannot read or understand text in its raw form, 10 00:00:18,540 --> 00:00:20,253 -他们只能使用数字。 +他们只能理解数字。 they can only work with numbers. 11 @@ -55,7 +56,7 @@ So the tokenizer's objective 12 00:00:23,220 --> 00:00:25,923 -将文本翻译成数字。 +将是把文本翻译成数字。 will be to translate the text into numbers. 13 @@ -65,22 +66,22 @@ There are several possible approaches to this conversion, 14 00:00:30,240 --> 00:00:31,110 -和目标 +并且目标 and the objective 15 00:00:31,110 --> 00:00:33,453 -就是找到最有意义的表示。 +是找到最有意义的表示。 is to find the most meaningful representation. 16 00:00:36,240 --> 00:00:39,390 -我们将看看三种不同的标记化算法。 +我们将看看三种不同的分词化算法。 We'll take a look at three distinct tokenization algorithms. 17 00:00:39,390 --> 00:00:40,530 -我们一对一比较, +我们对其一对一比较, We compare them one to one, 18 diff --git a/subtitles/zh-CN/13_word-based-tokenizers.srt b/subtitles/zh-CN/13_word-based-tokenizers.srt index 2fcf95891..7a8104067 100644 --- a/subtitles/zh-CN/13_word-based-tokenizers.srt +++ b/subtitles/zh-CN/13_word-based-tokenizers.srt @@ -16,11 +16,12 @@ 4 00:00:03,549 --> 00:00:05,603 - 让我们来看看基于单词的分词。 +*[译者注: token, tokenization, tokenizer 等词均译成了 分词*, 实则不翻译最佳] - Let's take a look at word-based tokenization. 5 00:00:07,650 --> 00:00:09,780 -基于单词的标记化是这个想法 +基于单词的分词化的想法是 Word-based tokenization is the idea 6 @@ -35,7 +36,7 @@ by splitting on spaces or other specific rules, 8 00:00:16,020 --> 00:00:17,163 -像标点符号。 +比如标点符号。 like punctuation. 9 @@ -45,22 +46,22 @@ In this algorithm, each word has a specific number 10 00:00:21,810 --> 00:00:23,463 -或归因于它的 ID。 +或者说他的 ID。 or ID attributed to it. 11 00:00:24,360 --> 00:00:27,270 -在这里,我们有 ID 250, +在这里,"let's" 的 ID 是 250, Here, let's has the ID 250, 12 00:00:27,270 --> 00:00:30,150 -确实有 861,并且标记化 + "do" 是 861,并且分词化 do has 861, and tokenization 13 00:00:30,150 --> 00:00:33,393 -后面跟感叹号的有 345。 +后面跟感叹号的是 345。 followed by an exclamation mark has 345. 14 @@ -110,7 +111,7 @@ and their meaning is close. 23 00:01:03,210 --> 00:01:05,550 -然而,基于单词的标记化, +然而,基于单词的分词化, The word-based tokenization, however, 24 @@ -135,7 +136,7 @@ This is unfortunate as we would like the model 28 00:01:15,090 --> 00:01:18,240 -了解这些词确实相关, +理解这些词是确实相关的, to understand that these words are indeed related, 29 @@ -195,12 +196,12 @@ that represents the word's meaning, 40 00:01:50,190 --> 00:01:52,170 -并跟踪这些映射 +并实现保持这些映射 and keeping track of these mappings 41 00:01:52,170 --> 00:01:54,990 -需要大量的权重 +需要很大的模型 requires an enormous number of weights 42 @@ -220,7 +221,7 @@ we can opt for our tokenizer to ignore certain words 45 00:02:04,440 --> 00:02:06,093 -我们不一定需要。 +我们不一定需要的。 that we don't necessarily need. 46 @@ -260,7 +261,7 @@ into numbers, but any other word will be converted 53 00:02:29,370 --> 00:02:31,530 -到词汇外的词, +作为词汇外的词, to the out-of-vocabulary word, 54 @@ -280,7 +281,7 @@ The model will have the exact same representation 57 00:02:39,900 --> 00:02:42,390 -对于它不知道的所有单词, +对于所有它不知道的单词, for all words that it doesn't know, 58 diff --git a/subtitles/zh-CN/14_character-based-tokenizers.srt b/subtitles/zh-CN/14_character-based-tokenizers.srt index 0b07937f9..ea74b563f 100644 --- a/subtitles/zh-CN/14_character-based-tokenizers.srt +++ b/subtitles/zh-CN/14_character-based-tokenizers.srt @@ -5,22 +5,23 @@ 2 00:00:04,260 --> 00:00:07,200 -- 在深入研究基于字符的标记化之前, +- 在深入研究基于字符的分词化之前, +*[译者注: token, tokenization, tokenizer 等词均译成了 分词*, 实则不翻译最佳] - Before diving in character-based tokenization, 3 00:00:07,200 --> 00:00:10,350 -理解为什么这种标记化很有趣 +理解为什么这种分词化很有趣 understanding why this kind of tokenization is interesting 4 00:00:10,350 --> 00:00:13,533 -需要了解基于单词的标记化的缺陷。 +需要了解基于单词的分词化的缺陷。 requires understanding the flaws of word-based tokenization. 5 00:00:14,640 --> 00:00:16,320 -如果你还没有看过第一个视频 +如果你还没有看过第一个视频, If you haven't seen the first video 6 @@ -30,12 +31,12 @@ on word-based tokenization 7 00:00:17,880 --> 00:00:21,450 -我们建议你在观看此视频之前检查一下。 +我们建议你在观看此视频之前看一下。 we recommend you check it out before looking at this video. 8 00:00:21,450 --> 00:00:24,250 -好的,让我们看一下基于字符的标记化。 +好的,让我们看一下基于字符的分词化。 Okay, let's take a look at character-based tokenization. 9 @@ -60,7 +61,7 @@ while the number of characters stays low. 13 00:00:38,610 --> 00:00:41,313 -首先让我们看一下英语, +首先, 让我们看一下英语, To begin let's take a look at the English language, 14 @@ -75,7 +76,7 @@ so we would need a very large vocabulary 16 00:00:47,730 --> 00:00:49,413 -包含所有单词。 +来包含所有单词。 to encompass all words. 17 @@ -95,7 +96,7 @@ which includes letters, numbers and special characters. 20 00:00:59,760 --> 00:01:02,190 -即使是有很多不同字符的语言 +即使是有大量不同字符的语言 Even languages with a lot of different characters 21 @@ -105,12 +106,12 @@ like the Chinese languages can have dictionaries 22 00:01:04,800 --> 00:01:08,130 -多达 20,000 个不同的字符 +多达 20,000 个不同的汉字 with up to 20,000 different characters 23 00:01:08,130 --> 00:01:11,523 -但超过 375,000 个不同的单词。 +超过 375,000 个不同的词语。 but more than 375,000 different words. 24 @@ -120,7 +121,7 @@ So character-based vocabularies 25 00:01:14,310 --> 00:01:16,293 -让我们使用更少的不同标记 +让我们使用更少的不同分词 let us use fewer different tokens 26 @@ -135,12 +136,12 @@ we would otherwise use. 28 00:01:23,250 --> 00:01:25,830 -这些词汇也比较全 +这些词汇也更全面 These vocabularies are also more complete 29 00:01:25,830 --> 00:01:28,950 -比他们基于单词的词汇对应物。 +相较于其基于单词的词汇。 than their word-based vocabularies counterparts. 30 @@ -150,7 +151,7 @@ As our vocabulary contains all characters 31 00:01:31,410 --> 00:01:33,960 -用在一种语言中,甚至是看不见的词 +在一种语言中的,甚至是看不见的词 used in a language, even words unseen 32 @@ -160,12 +161,12 @@ during the tokenizer training can still be tokenized, 33 00:01:36,990 --> 00:01:39,633 -因此词汇表外的标记将不那么频繁。 +因此溢出的分词将不那么频繁。 so out-of-vocabulary tokens will be less frequent. 34 00:01:40,680 --> 00:01:42,840 -这包括正确标记化的能力 +这包括正确分词化 This includes the ability to correctly tokenize 35 @@ -175,7 +176,7 @@ misspelled words, rather than discarding them 36 00:01:45,210 --> 00:01:46,623 -立即未知。 +作为未知的。 as unknown straight away. 37 @@ -220,22 +221,22 @@ have a lot of information held in single characters, 45 00:02:12,750 --> 00:02:15,360 -但对于其他像基于罗马的语言, +但对于其他像基于字母的语言, but for others like roman-based languages, 46 00:02:15,360 --> 00:02:17,760 -该模型必须理解多个标记 -the model will have to make sense of multiple tokens +该模型必须一次性理解多个分词 +the model will have to make sense of multiple tokens at a time 47 00:02:17,760 --> 00:02:20,670 -一次获取以其他方式持有的信息 -at a time to get the information otherwise held +以获取信息 +to get the information otherwise held 48 00:02:20,670 --> 00:02:21,753 -一句话。 +在一句话中。 in a single word. 49 @@ -250,7 +251,7 @@ their sequences are translated into very large amount 51 00:02:29,520 --> 00:02:31,593 -模型要处理的标记数。 +模型要处理的分词。 of tokens to be processed by the model. 52 @@ -260,12 +261,12 @@ And this can have an impact on the size of the context 53 00:02:36,810 --> 00:02:40,020 -该模型将随身携带,并会减小尺寸 +该模型将装载,并会减小 the model will carry around, and will reduce the size 54 00:02:40,020 --> 00:02:42,030 -我们可以用作模型输入的文本, +可以用作模型输入文本的尺寸, of the text we can use as input for our model, 55 @@ -280,22 +281,21 @@ This tokenization, while it has some issues, 57 00:02:46,650 --> 00:02:48,720 -在过去看到了一些非常好的结果 +但在过去看到了一些非常好的结果 has seen some very good results in the past 58 00:02:48,720 --> 00:02:50,490 -所以在接近时应该考虑 -and so it should be considered when approaching - +所以应该被考虑 +and so it should be considered 59 00:02:50,490 --> 00:02:52,680 -解决问题的新问题 -a new problem as it solves issues +当碰到新问题时, 来解决问题. +when approaching a new problem as it solves issues 60 00:02:52,680 --> 00:02:54,843 -在基于词的算法中遇到。 +在基于词的算法中遇到的。 encountered in the word-based algorithm. 61 diff --git a/subtitles/zh-CN/15_subword-based-tokenizers.srt b/subtitles/zh-CN/15_subword-based-tokenizers.srt index 2fa836c93..7b53f8219 100644 --- a/subtitles/zh-CN/15_subword-based-tokenizers.srt +++ b/subtitles/zh-CN/15_subword-based-tokenizers.srt @@ -1,21 +1,23 @@ 1 00:00:06,450 --> 00:00:09,540 - 让我们来看看基于子词的分词。 +*[译者注: token, tokenization, tokenizer 等词均译成了 分词*, 实则不翻译最佳] - Let's take a look at subword based tokenization. 2 00:00:09,540 --> 00:00:11,610 了解为什么基于子词的分词是 -Understanding why subword based tokenization is +Understanding why subword based tokenization 3 00:00:11,610 --> 00:00:13,980 -有趣需要理解缺陷 -interesting requires understanding the flaws +是有趣的需要理解 +is interesting requires understanding the flaws + 4 00:00:13,980 --> 00:00:17,340 -基于单词和基于校正器的标记化。 +基于单词和基于校正器分词化的缺陷。 of word based and corrector based tokenization. 5 @@ -25,63 +27,63 @@ If you haven't seen the first videos 6 00:00:18,780 --> 00:00:22,020 -基于单词和基于字符的标记化 +关于基于单词和基于字符的标记化 on word based and character based tokenization 7 00:00:22,020 --> 00:00:23,130 -我们建议你检查它们 -we recommend you check them +我们建议你观看它们 +we recommend you check them out 8 00:00:23,130 --> 00:00:24,780 在看这个视频之前。 -out before looking at this video. +before looking at this video. 9 00:00:27,840 --> 00:00:31,493 -基于子词的标记化介于基于字符之间 +基于子词的分词化介于基于字符 Subword based tokenization lies in between character based 10 00:00:31,493 --> 00:00:35,280 -和基于词的分词算法。 +和基于单词的分词算法之间。 and word based tokenization algorithms. 11 00:00:35,280 --> 00:00:37,410 -这个想法是找到一个中间立场 +这个想法是找到一个中间场 The idea is to find a middle ground 12 00:00:37,410 --> 00:00:39,486 -在非常大的词汇表之间 -between very large vocabularies +在很大的词汇表, +between very large vocabularies, 13 00:00:39,486 --> 00:00:42,600 -大量的词汇标记 +大量的词汇分词 a large quantity of out vocabulary tokens 14 00:00:42,600 --> 00:00:45,360 -并且在非常相似的词中失去意义 +还有在非常相似的词之间意义差 and a loss of meaning across very similar words 15 00:00:45,360 --> 00:00:48,630 -用于基于词的分词器和非常长的序列 +对基于单词的分词器和非常长的序列 for word based tokenizers and very long sequences 16 00:00:48,630 --> 00:00:51,330 -以及意义不大的单个标记。 -as well as less meaningful individual tokens. +以及意义不大的单个标记 +as well as less meaningful individual tokens 17 00:00:51,330 --> 00:00:53,133 -对于基于字符的分词器。 -For character based tokenizers. +对于基于字符的分词器之间。 +for character based tokenizers. 18 00:00:54,840 --> 00:00:57,960 @@ -95,7 +97,7 @@ Frequently used words should not be split 20 00:01:00,000 --> 00:01:01,500 -分成更小的子词 +成更小的子词 into smaller subwords 21 @@ -110,7 +112,7 @@ into meaningful subwords. 23 00:01:06,510 --> 00:01:08,460 -一个例子是狗这个词。 +一个例子是 dog 这个词。 An example is the word dog. 24 @@ -120,42 +122,42 @@ We would like to have our tokenizer to have a single ID 25 00:01:11,190 --> 00:01:12,600 -对于狗这个词 +对于 dog 这个词 for the word dog rather 26 00:01:12,600 --> 00:01:15,363 -而不是将其拆分为校正器 DO 和 G。 -than splitting it into correctors D O and G. +而不是将其拆分为字母 d o 和 g。 +than splitting it into characters d o and g. 27 00:01:16,650 --> 00:01:19,260 -然而,当遇到狗这个词时 +然而,当遇到 dog 这个词时 However, when encountering the word dogs 28 00:01:19,260 --> 00:01:22,710 -我们希望我们的标记化从根本上理解这一点 +我们希望我们的分词从词根上理解这一点 we would like our tokenize to understand that at the root 29 00:01:22,710 --> 00:01:24,120 -这还是狗这个词。 +这还是 dog 这个词。 this is still the word dog. 30 00:01:24,120 --> 00:01:27,030 -添加 S 后,意思略有改变 -With an added S, that slightly changes the meaning +添加 s 后,意思略有改变 +With an added "s", that slightly changes the meaning 31 00:01:27,030 --> 00:01:28,923 -同时保持最初的想法。 +同时保持最初的意思。 while keeping the original idea. 32 00:01:30,600 --> 00:01:34,080 -另一个例子是像标记化这样的复杂词 +另一个例子是像 tokenization 这样的复杂词 Another example is a complex word like tokenization 33 @@ -165,17 +167,17 @@ which can be split into meaningful subwords. 34 00:01:37,140 --> 00:01:37,973 -根 -The root +这个词的根 +The root of the word 35 00:01:37,973 --> 00:01:40,590 -这个词的是记号,-ization 完成根 -of the word is token and -ization completes the root +是 token ,以及 -ization 完整了这个词 +is token and -ization completes the root 36 00:01:40,590 --> 00:01:42,870 -赋予它稍微不同的含义。 +以赋予它稍微不同的含义。 to give it a slightly different meaning. 37 @@ -195,42 +197,42 @@ labeled as the start of the word 40 00:01:49,950 --> 00:01:52,530 -和化作为标记的附加信息 -and ization as additional information labeled +和 -ization 作为标记的附加信息 +and -ization as additional information labeled 41 00:01:52,530 --> 00:01:54,393 -作为单词的完成。 +作为单词的完整化。 as a completion of the word. 42 00:01:55,826 --> 00:01:58,740 -反过来,该模型现在将能够有意义 +如此一来,该模型现在将能够有作用 In turn, the model will now be able to make sense 43 00:01:58,740 --> 00:02:01,080 -不同情况下的令牌。 +在不同情况下的分词。 of token in different situations. 44 00:02:01,080 --> 00:02:04,602 -它会理解这个词的 token, tokens, tokenizing +它会理解这个词的形式: token, tokens, tokenizing It will understand that the word's token, tokens, tokenizing 45 00:02:04,602 --> 00:02:08,760 -和标记化具有相似的含义并且是相关联的。 +和 tokenization 具有相似的含义并且是相关联的。 and tokenization have a similar meaning and are linked. 46 00:02:08,760 --> 00:02:12,450 -它还将理解标记化、现代化 +它还将理解 tokenization 、modernization It's will also understand that tokenization, modernization 47 00:02:12,450 --> 00:02:16,200 -和免疫,它们都有相同的后缀 +和 immunization ,都有相同的后缀 and immunization, which all have the same suffixes 48 @@ -240,62 +242,62 @@ are probably used in the same syntactic situations. 49 00:02:20,610 --> 00:02:23,130 -基于子词的分词器通常有办法 +基于子词的分词器通常有办法来 Subword based tokenizers generally have a way to 50 00:02:23,130 --> 00:02:25,890 -识别哪些标记是单词的开头 +识别哪些分词是单词的开头 identify which tokens are a start of word 51 00:02:25,890 --> 00:02:28,443 -以及哪些标记完成单词的开头。 +以及哪些分词完成了单词的开始。 and which tokens complete start of words. 52 00:02:29,520 --> 00:02:31,140 -所以这里以令牌为开始 -So here token as the start +所以这里以分词作为单词的开始 +So here token as the start of a word -53 +53 00:02:31,140 --> 00:02:35,100 -病房和哈希哈希化作为奖励的完成。 -of a ward and hash hash ization as completion of award. +以及 ##ization 的开始作为单词的完成。 +and ##ization as completion of a word. 54 00:02:35,100 --> 00:02:38,103 -这里 hash 哈希前缀表示化是一部分 -Here, the hash hash prefix indicates that ization is part +这里 ## 表示 -ization 是单词的一部分 +Here, the ## prefix indicates that ization is part of a word 55 00:02:38,103 --> 00:02:41,013 -奖项而不是它的开始。 -of award rather than the beginning of it. +而不是它的开始。 +rather than the beginning of it. 56 00:02:41,910 --> 00:02:43,110 -hash 散列来了 -The hash hash comes + ## 记号 +The ## comes 57 00:02:43,110 --> 00:02:47,013 -来自基于词片算法的 BERT 分词器。 +来自基于单词片算法的 BERT 分词器。 from the BERT tokenizer based on the word piece algorithm. 58 00:02:47,850 --> 00:02:50,700 -其他标记化使用其他前缀可以是 +其他分词器使用其他前缀, 可以是 Other tokenizes use other prefixes which can be 59 00:02:50,700 --> 00:02:52,200 -放置以表示单词的一部分 +用来表示单词的一部分 placed to indicate part of words 60 00:02:52,200 --> 00:02:55,083 -喜欢在这里或单词的开头。 +比如在这里或单词的开头。 like in here or start of words instead. 61 @@ -310,12 +312,11 @@ of different algorithms that can be used 63 00:02:58,740 --> 00:03:00,090 -用于子词标记化 +用于子词分词化 for subword tokenization - 64 00:03:00,090 --> 00:03:02,670 -大多数模型都获得了最先进的结果 +大多数模型都获得了目前最先进的结果 and most models obtaining state-of-the-art results 65 @@ -325,7 +326,7 @@ in English today 66 00:03:03,780 --> 00:03:06,663 -使用某种子词标记化算法。 +使用一些子词标记化算法。 use some kind of subword tokenization algorithms. 67 @@ -335,17 +336,17 @@ These approaches help in reducing the vocabulary sizes 68 00:03:10,953 --> 00:03:13,636 -通过跨不同的词共享信息 +通过不同词之间共享的信息 by sharing information across different words 69 00:03:13,636 --> 00:03:15,960 -有前缀的能力 +有能力以理解前缀 having the ability to have prefixes 70 00:03:15,960 --> 00:03:18,630 -和后缀这样理解。 +和后缀如斯。 and suffixes understood as such. 71 @@ -355,6 +356,6 @@ They keep meaning across very similar words 72 00:03:20,700 --> 00:03:23,103 -通过识别相似的标记,将它们组合起来。 +通过识别相似的分词,将它们组合起来。 by recognizing similar tokens, making them up. diff --git a/subtitles/zh-CN/16_the-tokenization-pipeline.srt b/subtitles/zh-CN/16_the-tokenization-pipeline.srt index a359fd213..4db8dcce1 100644 --- a/subtitles/zh-CN/16_the-tokenization-pipeline.srt +++ b/subtitles/zh-CN/16_the-tokenization-pipeline.srt @@ -5,22 +5,23 @@ 2 00:00:05,610 --> 00:00:06,873 -- 分词器管道。 +- 分词器的 pipeline 。 +*[译者注: token, tokenization, tokenizer 等词均译成了 分词*, 实则不翻译最佳] - The tokenizer pipeline. 3 00:00:07,920 --> 00:00:10,570 -在本视频中,我们将了解分词器如何将 +在本视频中,我们将了解分词器如何转换 In this video, we'll look at how a tokenizer converts 4 00:00:11,433 --> 00:00:12,480 -原始文本到数字, +从原始文本到数字, raw texts to numbers, 5 00:00:12,480 --> 00:00:14,970 -Transformer 模型可以理解, +Transformer 模型可以理解成, that a Transformer model can make sense of, 6 @@ -35,12 +36,12 @@ Here is a quick overview 8 00:00:18,690 --> 00:00:21,630 -tokenizer 对象内部发生的事情: +对于 tokenizer 对象内部发生的事情: of what happens inside the tokenizer object: 9 00:00:21,630 --> 00:00:24,360 -首先,文本被分成标记, +首先,文本被分成分词, first, the text is split into tokens, 10 @@ -50,12 +51,12 @@ which are words, parts of words, or punctuation symbols. 11 00:00:28,440 --> 00:00:31,500 -然后标记器添加潜在的特殊标记 +然后分词器添加潜在的特殊分词 Then the tokenizer adds potential special tokens 12 00:00:31,500 --> 00:00:34,680 -并将每个令牌转换为各自唯一的 ID +并将每个分词转换为各自唯一的 ID and converts each token to their unique respective ID 13 @@ -65,17 +66,17 @@ as defined by the tokenizer's vocabulary. 14 00:00:37,710 --> 00:00:40,380 -正如我们将要看到的,它并不是按照这个顺序发生的, +正如我们将要看到的,它并不完全是按照这个顺序发生的, As we'll see, it doesn't quite happen in this order, 15 00:00:40,380 --> 00:00:43,233 -但这样做更利于理解。 +但这样更利于理解。 but doing it like this is better for understandings. 16 00:00:44,280 --> 00:00:47,670 -第一步是将我们的输入文本拆分为标记。 +第一步是将我们的输入文本拆分为分词。 The first step is to split our input text into tokens. 17 @@ -90,7 +91,7 @@ To do that, the tokenizer may first perform some operations, 19 00:00:54,030 --> 00:00:56,880 -喜欢将所有单词小写,然后遵循一组规则 +比如将所有单词小写,然后遵循一组规则 like lowercasing all words, then follow a set of rules 20 @@ -105,7 +106,7 @@ Most of the Transformer models uses 22 00:01:02,286 --> 00:01:04,890 -单词标记化算法,这意味着 +单词分词化算法,这意味着 a word tokenization algorithm, which means 23 @@ -115,12 +116,12 @@ that one given word can be split 24 00:01:06,750 --> 00:01:10,050 -在几个标记中,例如 tokenize here。 +在几个分词中,例如这里的分词。 in several tokens like tokenize here. 25 00:01:10,050 --> 00:01:12,570 -查看下面的“标记化算法”视频链接 +查看下面的 “分词化算法” 视频链接 Look at the "Tokenization algorithms" video link below 26 @@ -130,17 +131,17 @@ for more information. 27 00:01:14,760 --> 00:01:17,820 -我们在ize前面看到的##前缀是 -The # # prefix we see in front of ize is +我们在 ize 前面看到的 ## 前缀是 +The ## prefix we see in front of ize is 28 00:01:17,820 --> 00:01:19,830 -Bert 用来表示的约定 -a convention used by Bert to indicate +BERT 的约定, 用来表示 +a convention used by BERT to indicate 29 00:01:19,830 --> 00:01:22,762 -这个标记不是单词的开头。 +这个分词不是单词的开头。 this token is not the beginning of the word. 30 @@ -155,7 +156,7 @@ for instance, ALBERT tokenizers will add a long underscore 32 00:01:29,984 --> 00:01:31,620 -在所有令牌前 +在所有分词前 in front of all the tokens 33 @@ -170,17 +171,17 @@ by all sentencepiece tokenizers. 35 00:01:38,580 --> 00:01:41,040 -标记化管道的第二步是 +分词化管道的第二步是 The second step of the tokenization pipeline is 36 00:01:41,040 --> 00:01:43,470 -将这些令牌映射到它们各自的 ID +将这些分词映射到它们各自的 ID to map those tokens to their respective IDs 37 00:01:43,470 --> 00:01:45,770 -由分词器的词汇定义。 +由分词器的词汇定义的。 as defined by the vocabulary of the tokenizer. 38 @@ -205,7 +206,7 @@ We have to make sure we use the same mapping 42 00:01:54,390 --> 00:01:56,520 -就像模型被预训练时一样。 +和模型被预训练时一致。 as when the model was pretrained. 43 @@ -225,7 +226,7 @@ that we don't have the exact same results 46 00:02:03,540 --> 00:02:05,580 -就像我们的第一张幻灯片一样 +就像我们的第一张幻灯片一样, 或许 as in our first slide, or not 47 @@ -235,18 +236,18 @@ as this looks like a list of random numbers anyway, 48 00:02:07,920 --> 00:02:10,680 -在这种情况下,请允许我重温一下你的记忆。 +在这种情况下,请允许我重温一下你的回忆。 in which case, allow me to refresh your memory. 49 -00:02:10,680 --> 00:02:12,350 -我们有一个开头的数字和一个数字 -We had a the number at the beginning and a number +00:02:10,680 --> 00:02:15,350 +我们有缺少开头的数字和一个结尾的数字 +We had a the number at the beginning and a number at the end, that are missing, 50 -00:02:12,350 --> 00:02:17,130 -最后缺少的是特殊标记。 -at the end that are missing, those are the special tokens. +00:02:15,350 --> 00:02:17,130 +是特殊标记。 +those are the special tokens. 51 00:02:17,130 --> 00:02:20,340 @@ -255,12 +256,12 @@ The special tokens are added by the prepare_for_model method 52 00:02:20,340 --> 00:02:22,350 -它知道这个令牌的索引 +它知道这个分词的索引 which knows the indices of this token 53 00:02:22,350 --> 00:02:25,680 -在词汇表中并添加适当的数字。 +在词汇表中, 并仅添加适当的数字。 in the vocabulary and just adds the proper numbers. 54 @@ -285,7 +286,7 @@ at how the tokenizer has changed your text, 58 00:02:33,870 --> 00:02:35,280 -通过使用解码方法 +通过使用 decode 方法 by using the decode method 59 @@ -300,7 +301,7 @@ As for the prefix for beginning 61 00:02:39,423 --> 00:02:44,160 -词的/词的一部分,特殊标记因情况而异 +词的/词的部分,对于特殊标记, 依赖于 of words/ part of words, for special tokens vary depending 62 @@ -315,8 +316,8 @@ So that tokenizer uses CLS and SEP, 64 00:02:48,810 --> 00:02:52,417 -但是 roberta tokenizer 使用类似 HTML 的锚点 -but the roberta tokenizer uses HTML-like anchors +但是 RoBERTa 分词器使用类似 HTML 的锚点 +but the RoBERTa tokenizer uses HTML-like anchors 65 00:02:52,417 --> 00:02:55,230 @@ -345,12 +346,12 @@ on your input texts. 70 00:03:03,870 --> 00:03:05,310 -分词器的输出不 +分词器的输出不只是 The output of a tokenizer don't 71 00:03:05,310 --> 00:03:07,853 -但是,只包含输入 ID。 +仅包含输入 ID, 然而. just contain the input IDs, however. 72 @@ -360,17 +361,17 @@ To learn what the attention mask is, 73 00:03:09,750 --> 00:03:12,360 -查看“一起批量输入”视频。 +查看 “一起批量输入” 视频。 check out the "Batch input together" video. 74 00:03:12,360 --> 00:03:14,220 -要了解令牌类型 ID, +要了解分词类型 ID, To learn about token type IDs, 75 00:03:14,220 --> 00:03:16,570 -观看“处理句子对”视频。 +观看 “处理句子对” 视频。 look at the "Process pairs of sentences" video. 76 diff --git a/subtitles/zh-CN/17_batching-inputs-together-(pytorch).srt b/subtitles/zh-CN/17_batching-inputs-together-(pytorch).srt index b4fdd4d2b..885fc5296 100644 --- a/subtitles/zh-CN/17_batching-inputs-together-(pytorch).srt +++ b/subtitles/zh-CN/17_batching-inputs-together-(pytorch).srt @@ -20,7 +20,7 @@ to batch input sequences together. 5 00:00:12,137 --> 00:00:15,420 -一般来说,我们想要通过我们的模型的句子 +一般来说,我们想要输入模型的句子 In general, the sentences we want to pass through our model 6 @@ -30,12 +30,12 @@ won't all have the same lengths. 7 00:00:17,670 --> 00:00:19,740 -在这里,我们使用我们看到的模型 +在这里,我们使用模型 Here, we are using the model we saw 8 00:00:19,740 --> 00:00:22,080 -在情绪分析管道中 +在情绪分析 pipeline 中讲的 in the sentiment analysis pipeline 9 @@ -45,7 +45,8 @@ and want to classify two sentences. 10 00:00:24,900 --> 00:00:27,360 -将它们标记化并映射每个标记时 +将它们分词化并映射每个分词时 +*[译者注: token, tokenization, tokenizer 等词均译成了 分词*, 实则不翻译最佳] When tokenizing them and mapping each token 11 @@ -60,12 +61,12 @@ we get two lists of different lengths. 13 00:00:33,240 --> 00:00:35,340 -尝试创建张量或 NumPy 数组 +尝试创建 tensor 或 NumPy 数组 Trying to create a tensor or a NumPy array 14 00:00:35,340 --> 00:00:38,220 -从这两个列表中将导致错误, +从这两个列表中, 将导致错误, from those two lists will result in an error, 15 @@ -75,12 +76,12 @@ because all arrays and tensors should be rectangular. 16 00:00:42,240 --> 00:00:44,160 -克服此限制的一种方法 +突破此限制的一种方法 One way to overcome this limit 17 00:00:44,160 --> 00:00:45,690 -是造第二句 +是让第二句 is to make the second sentence 18 @@ -90,7 +91,7 @@ the same length as the first 19 00:00:47,640 --> 00:00:50,463 -通过根据需要多次添加特殊令牌。 +通过根据需要多次添加特殊分词。 by adding a special token as many times as necessary. 20 @@ -105,12 +106,12 @@ to the length of the second, 22 00:00:55,710 --> 00:00:58,140 -但我们会让他们失去很多信息 -but we would them lose a lot of information +但我们会失去很多信息 +but we would then lose a lot of information 23 00:00:58,140 --> 00:01:01,083 -这可能是正确分类句子所必需的。 +而这可能是正确分类句子所必需的。 that might be necessary to properly classify the sentence. 24 @@ -135,7 +136,7 @@ The value used to pad the second sentence 28 00:01:11,850 --> 00:01:13,740 -不应随意挑选; +不应被随意挑选; should not be picked randomly; 29 @@ -155,7 +156,7 @@ Now that we have padded our sentences, 32 00:01:22,800 --> 00:01:24,303 -我们可以和他们一起做一批。 +我们可以和他们做成一批。 we can make a batch with them. 33 @@ -165,7 +166,7 @@ If we pass the two sentences to the model separately 34 00:01:28,320 --> 00:01:30,120 -然而,并批在一起, +和并批在一起,然而 and batched together however, 35 @@ -185,9 +186,8 @@ here, the second one. 38 00:01:36,390 --> 00:01:39,420 -它在 Transformers 库的后面?不。 -这是 Transformers -It's at the back in the Transformers Library? No. +是 Transformers 有问题?不。 +It's at the backend in the Transformers Library? No. 39 00:01:39,420 --> 00:01:40,770 @@ -206,7 +206,7 @@ this should not come as a total surprise; 42 00:01:45,210 --> 00:01:48,277 -在计算每个标记的上下文表示时, +在计算每个分词的上下文表示时, when computing the contextual representation of each token, 43 @@ -226,7 +226,7 @@ If we have just the sentence 46 00:01:53,850 --> 00:01:56,970 -或者添加了几个填充标记的句子, +或者添加了几个填充 token 的句子, or the sentence with several padding tokens added, 47 @@ -246,7 +246,7 @@ we need to indicate to the attention layers 50 00:02:05,340 --> 00:02:08,070 -他们应该忽略那些填充标记。 +他们应该忽略那些填充 token 。 that they should ignore those padding tokens. 51 @@ -261,27 +261,27 @@ a tensor with the same shape as the input IDs, 53 00:02:13,320 --> 00:02:14,733 -用零和一个。 +用 0 和 1 。 with zeros and ones. 54 00:02:15,780 --> 00:02:18,120 -那些表示注意层的标记 +1 的分词表示注意层 Ones indicate the tokens the attention layers 55 00:02:18,120 --> 00:02:20,100 -应结合上下文考虑 +应该结合上下文考虑 should consider in the context 56 00:02:20,100 --> 00:02:22,100 -并将他们应该忽略的标记归零。 +并且 0 的分词他们应该忽略。 and zeros the tokens they should ignore. 57 00:02:23,520 --> 00:02:26,760 -现在,将这个注意掩码与输入 ID 一起传递 +现在,将这个注意掩码与输入 ID 一起传入 Now, passing this attention mask along with the input ID 58 @@ -291,7 +291,7 @@ will give us the same results 59 00:02:28,170 --> 00:02:31,170 -就像我们将两个句子分别发送给模型一样。 +就像我们将两个句子单独发送给模型一样。 as when we sent the two sentences individually to the model. 60 @@ -306,7 +306,7 @@ when you apply it to several sentences 62 00:02:36,900 --> 00:02:38,613 -标志 padding=True。 +设参数 padding=True。 with the flag padding=True. 63 @@ -316,7 +316,7 @@ It will apply the padding with the proper value 64 00:02:41,490 --> 00:02:43,140 -到较小的句子 +对较小的句子 to the smaller sentences 65 diff --git a/subtitles/zh-CN/18_batching-inputs-together-(tensorflow).srt b/subtitles/zh-CN/18_batching-inputs-together-(tensorflow).srt index a672d8f66..fbbb2b247 100644 --- a/subtitles/zh-CN/18_batching-inputs-together-(tensorflow).srt +++ b/subtitles/zh-CN/18_batching-inputs-together-(tensorflow).srt @@ -5,17 +5,17 @@ 2 00:00:05,310 --> 00:00:07,590 -- 如何一起批量输入。 +- 如何批量一起输入。 - How to batch inputs together. 3 00:00:07,590 --> 00:00:09,150 -在这个视频中,我们将看到 +在本视频中,我们将看到 In this video, we'll see 4 00:00:09,150 --> 00:00:11,050 -如何将输入序列一起批处理。 +如何将输入序列一起批量处理。 how to batch input sequences together. 5 @@ -25,17 +25,17 @@ In general, the sentences we want to pass 6 00:00:14,910 --> 00:00:18,000 -通过我们的模型不会都具有相同的长度。 +进入我们的模型不会都具有相同的长度。 through our model won't all have the same lengths. 7 00:00:18,000 --> 00:00:20,310 -在这里,我们使用我们看到的模型 +在这里,我们使用模型 Here, we are using the model we saw 8 00:00:20,310 --> 00:00:22,650 -在情绪分析管道中 +在情绪分析 pipeline 中的 in the sentiment analysis pipeline 9 @@ -45,7 +45,8 @@ and want to classify two sentences. 10 00:00:25,860 --> 00:00:27,870 -将它们标记化并映射每个标记时 +将它们分词化并映射每个标记时 +*[译者注: token, tokenization, tokenizer 等词均译成了 分词*, 实则不翻译最佳] When tokenizing them and mapping each token 11 @@ -60,7 +61,7 @@ we get two lists of different lengths. 13 00:00:33,360 --> 00:00:35,070 -尝试创建张量和 NumPy 数组 +尝试创建 tensor 和 NumPy 数组 Trying to create a tensor and NumPy array 14 @@ -75,7 +76,7 @@ because all arrays and tensors should be rectangular. 16 00:00:42,510 --> 00:00:43,920 -克服此限制的一种方法 +克服此困难的一种方法 One way to overcome this limit 17 @@ -85,7 +86,7 @@ is to make the second sentence the same length as the first 18 00:00:47,340 --> 00:00:50,373 -通过根据需要多次添加特殊令牌。 +通过根据需要多次添加特殊分词。 by adding a special token as many times as necessary. 19 @@ -95,7 +96,7 @@ Another way would be to truncate the first sequence 20 00:00:53,340 --> 00:00:56,550 -到秒的长度,但我们会失去很多 +到第二个的长度,但我们会失去很多 to the length of the second, but we would then lose a lot 21 @@ -105,17 +106,17 @@ of information that may be necessary 22 00:00:58,590 --> 00:01:01,230 -正确地对句子进行分类。 +来正确地对句子进行分类。 to properly classify the sentence. 23 00:01:01,230 --> 00:01:04,710 -一般来说,我们只会截断更长的句子 +一般来说,我们只会截断句子 In general, we only truncate sentences when they are longer 24 00:01:04,710 --> 00:01:07,083 -超过模型可以处理的最大长度。 +超过模型可以处理的最大长度的。 than the maximum length the model can handle. 25 @@ -125,7 +126,7 @@ The value used to pad the second sentence 26 00:01:10,320 --> 00:01:12,390 -不应随意挑选。 +不应被随意挑选。 should not be picked randomly. 27 @@ -155,7 +156,7 @@ If we pass the two sentences to the model separately 32 00:01:26,730 --> 00:01:29,130 -或批在一起,但是,我们注意到 +或并批在一起,但是,我们注意到 or batched together, however, we notice 33 @@ -175,7 +176,7 @@ Here, the second one. 36 00:01:34,440 --> 00:01:36,690 -期待 transformer 库中的单词? +希望是 transformer 库中的单词? Expect the word in the transformer library? 37 @@ -190,12 +191,12 @@ If you remember that Transformer models make heavy use 39 00:01:39,720 --> 00:01:43,800 -注意力层,它不应该完全令人惊讶。 +注意力层,它应该不足为奇。 of attention layers, it should not come as a total surprise. 40 00:01:43,800 --> 00:01:47,100 -在计算每个标记的上下文表示时, +在计算每个分词的上下文表示时, When computing the contextual representation of each token, 41 @@ -215,7 +216,7 @@ If we have just a sentence 44 00:01:52,252 --> 00:01:55,650 -或者添加了几个填充标记的句子, +或者添加了几个填充 token 的句子, or the sentence with several padding tokens added, 45 @@ -235,7 +236,7 @@ we need to indicate to the attention layers 48 00:02:03,750 --> 00:02:06,660 -他们应该忽略那些填充标记。 +他们应该忽略那些填充 token 。 that they should ignore those padding tokens. 49 @@ -245,17 +246,17 @@ This is done by creating an attention mask, 50 00:02:08,970 --> 00:02:11,700 -与输入 ID 具有相同形状的张量 +与输入 ID 具有相同形状的 tensor a tensor with the same shape as the input IDs 51 00:02:11,700 --> 00:02:13,173 -用零和一个。 +用 0 和 1 。 with zeros and ones. 52 00:02:14,640 --> 00:02:16,830 -那些表示注意层的标记 +1 的 token 表示注意层 Ones indicate the tokens the attention layers 53 @@ -265,12 +266,12 @@ should consider in the context, 54 00:02:18,660 --> 00:02:20,823 -和零,他们应该忽略的标记。 +并且 0 的 token 他们应该忽略。 and zeros, the tokens they should ignore. 55 00:02:21,810 --> 00:02:23,290 -现在,通过这个注意力面具 +现在,通过这个注意力掩码 Now, passing this attention mask 56 @@ -295,8 +296,8 @@ when you apply it to several sentences 60 00:02:35,583 --> 00:02:37,713 -标志填充等于 true。 -with the flag padding equals true. +设置参数 padding=True。 +with the flag padding=True. 61 00:02:38,640 --> 00:02:39,690 @@ -305,7 +306,7 @@ It will apply the padding 62 00:02:39,690 --> 00:02:42,180 -对较小的句子具有适当的价值 +对较小的句子具有适当值 with the proper value to the smaller sentences 63 diff --git a/subtitles/zh-CN/20_hugging-face-datasets-overview-(tensorflow).srt b/subtitles/zh-CN/20_hugging-face-datasets-overview-(tensorflow).srt index 8d655495b..dcc4aa9db 100644 --- a/subtitles/zh-CN/20_hugging-face-datasets-overview-(tensorflow).srt +++ b/subtitles/zh-CN/20_hugging-face-datasets-overview-(tensorflow).srt @@ -5,7 +5,7 @@ 2 00:00:05,371 --> 00:00:09,690 -- 本节将带来 Hugging Face Datasets 库的快速概览。 +- Hugging Face Datasets 库: 快速概览。 - The Hugging Face Datasets library: A Quick overview. 3 @@ -15,12 +15,12 @@ The Hugging Face Datasets library 4 00:00:10,917 --> 00:00:12,870 -是一个提供 API 的库 +是一个库, 提供 API is a library that provides an API 5 00:00:12,870 --> 00:00:15,150 -快速下载许多公共数据集 +来快速下载许多公共数据集 to quickly download many public datasets 6 @@ -45,12 +45,12 @@ you can directly download and cache a dataset 10 00:00:26,010 --> 00:00:28,023 -来自其在数据集中心的标识符。 +来自其在数据集 Hub 的 ID 。 from its identifier on the Dataset hub. 11 00:00:29,160 --> 00:00:33,690 -这里我们从 GLUE 基准中获取 MRPC 数据集, +这里我们从 GLUE benchmark 中获取 MRPC 数据集, Here we fetch the MRPC dataset from the GLUE benchmark, 12 @@ -75,7 +75,7 @@ is a DatasetDict, which is a sort of dictionary 16 00:00:45,090 --> 00:00:46,940 -包含我们数据集的每个分割。 +包含我们数据集的每个拆分。 containing each split of our dataset. 17 @@ -90,17 +90,17 @@ This split is then an instance of the Dataset class, 19 00:00:54,540 --> 00:00:57,423 -有列,这里是 sentence1,sentence2, +有列: sentence1,sentence2, with columns, here sentence1, sentence2, 20 00:00:58,350 --> 00:01:00,813 -标签和 idx,以及行。 +label 和 idx,以及行。 label and idx, and rows. 21 00:01:02,160 --> 00:01:05,220 -我们可以通过索引访问给定的元素。 +我们可以访问给定的元素, 通过索引。 We can access a given element by its index. 22 @@ -120,7 +120,7 @@ which means that even if your dataset is huge 25 00:01:14,460 --> 00:01:16,219 -你不会离开 RAM, +你不会内存溢出, you won't get out of RAM, 26 @@ -130,7 +130,7 @@ only the elements you request are loaded in memory. 27 00:01:19,920 --> 00:01:24,510 -访问数据集的一部分就像访问一个元素一样简单。 +访问数据集的一个切片就像访问一个元素一样简单。 Accessing a slice of your dataset is as easy as one element. 28 @@ -160,7 +160,7 @@ The features attribute of a Dataset 33 00:01:37,080 --> 00:01:39,840 -为我们提供有关其专栏的更多信息。 +为我们提供有关其列的更多信息。 gives us more information about its columns. 34 @@ -180,7 +180,7 @@ and names for the labels. 37 00:01:46,110 --> 00:01:49,623 -0 代表不等价,1 代表等价。 +0 代表不相等,1 代表相等。 0 stands for not equivalent and 1 for equivalent. 38 @@ -190,12 +190,12 @@ To pre-process all the elements of our dataset, 39 00:01:54,090 --> 00:01:55,980 -我们需要将它们标记化。 +我们需要将它们 token 化。 we need to tokenize them. 40 00:01:55,980 --> 00:01:58,470 -看看视频 “预处理句子对” +看看视频 “Pre-process sentence pairs” Have a look at the video "Pre-process sentence pairs" 41 @@ -205,7 +205,7 @@ for a refresher, but you just have to send the two sentences 42 00:02:01,800 --> 00:02:04,833 -带有一些额外的关键字参数的分词器。 +给 tokenizer, 带有一些额外的关键字参数。 to the tokenizer with some additional keyword arguments. 43 @@ -215,7 +215,7 @@ Here we indicate a maximum length of 128 44 00:02:09,300 --> 00:02:11,460 -和垫输入短于这个长度, +和垫全输入短于这个长度的, and pad inputs shorter than this length, 45 @@ -255,12 +255,12 @@ or update existing ones. 52 00:02:30,060 --> 00:02:32,520 -加快预处理并利用 +为加快预处理并利用 To speed up pre-processing and take advantage 53 00:02:32,520 --> 00:02:35,130 -事实上,我们的分词器由 Rust 支持 +我们的分词器由 Rust 支持的事实 of the fact our tokenizer is backed by Rust 54 @@ -280,12 +280,12 @@ in our tokenize function, using the batched=True argument. 57 00:02:45,300 --> 00:02:46,980 -由于分词器可以处理列表 +由于 tokenizer 可以处理列表 Since the tokenizer can handle a list 58 00:02:46,980 --> 00:02:50,280 -第一句话或第二句话的 tokenize_function +第一句话或第二句话, tokenize_function of first or second sentences, the tokenize_function 59 @@ -295,7 +295,7 @@ does not need to change for this. 60 00:02:52,740 --> 00:02:55,410 -你还可以将多处理与 map 方法一起使用, +你还可以将多进程与 map 方法一起使用, You can also use multiprocessing with the map method, 61 @@ -305,7 +305,7 @@ check out its documentation linked below. 62 00:02:58,740 --> 00:03:02,130 -完成后,我们几乎可以开始训练了, +完成后,我们几乎准备好训练了, Once this is done, we are almost ready for training, 63 @@ -320,12 +320,12 @@ with the remove_columns method, 65 00:03:06,120 --> 00:03:08,580 -将标签重命名为标签,因为模型 +将 label 重命名为 labels ,因为模型是 rename label to labels, since the models 66 00:03:08,580 --> 00:03:11,430 -从变形金刚图书馆期望, + transformers 库中的,期望如此 from the transformers library expect that, 67 @@ -335,7 +335,7 @@ and set the output format to our desired backend, 68 00:03:14,040 --> 00:03:15,893 -手电筒、tensorflow 或 numpy。 +torch、tensorflow 或 numpy。 torch, tensorflow or numpy. 69 diff --git a/subtitles/zh-CN/21_preprocessing-sentence-pairs-(pytorch).srt b/subtitles/zh-CN/21_preprocessing-sentence-pairs-(pytorch).srt index 8541dbc5e..2e8e2d741 100644 --- a/subtitles/zh-CN/21_preprocessing-sentence-pairs-(pytorch).srt +++ b/subtitles/zh-CN/21_preprocessing-sentence-pairs-(pytorch).srt @@ -10,27 +10,27 @@ 3 00:00:09,150 --> 00:00:11,340 -我们已经看到了如何标记单个句子 +我们已经看到了如何分词化单个句子 We have seen how to tokenize single sentences 4 00:00:11,340 --> 00:00:12,877 并将它们放在一起, -and batch them together in the, +and batch them together in the 5 00:00:12,877 --> 00:00:15,810 -“批量输入视频。” -"Batching inputs together video." +在 “Batching inputs together” 视频中 +"Batching inputs together" video. 6 00:00:15,810 --> 00:00:18,330 -如果你觉得这段代码不熟悉, +如果这段代码对你而言不熟悉, If this code look unfamiliar to you, 7 00:00:18,330 --> 00:00:20,030 -请务必再次检查该视频。 +请务必再次查看该视频。 be sure to check that video again. 8 @@ -40,23 +40,23 @@ Here will focus on tasks that classify pair of sentences. 9 00:00:25,620 --> 00:00:28,470 -例如,我们可能想要对两个文本进行分类 +例如,我们可能想要对两个文本是否被释义 For instance, we may want to classify whether two texts 10 00:00:28,470 --> 00:00:30,360 -是否被释义。 +进行分类。 are paraphrased or not. 11 00:00:30,360 --> 00:00:32,880 -这是从 Quora 问题对中获取的示例 -Here is an example taken from the Quora Question Pairs +这是从 Quora Question Pairs 数据集中获取的示例 +Here is an example taken from the Quora Question Pairs dataset 12 00:00:32,880 --> 00:00:37,530 -数据集,它专注于识别重复的问题。 -dataset, which focuses on identifying duplicate questions. +它专注于识别重复的问题。 +which focuses on identifying duplicate questions. 13 00:00:37,530 --> 00:00:40,650 @@ -65,7 +65,7 @@ In the first pair, the two questions are duplicates, 14 00:00:40,650 --> 00:00:42,000 -在第二个他们不是。 +在第二个它们不是。 in the second they are not. 15 @@ -75,7 +75,7 @@ Another pair classification problem is 16 00:00:45,540 --> 00:00:47,400 -当我们想知道两个句子是否 +当我们想知道两个句子是 when we want to know if two sentences are 17 @@ -90,7 +90,7 @@ a problem called natural language inference or NLI. 19 00:00:53,970 --> 00:00:57,000 -在这个例子中,取自 MultiNLI 数据集, +在这个取自 MultiNLI 数据集的例子中, In this example, taken from the MultiNLI data set, 20 @@ -100,7 +100,7 @@ we have a pair of sentences for each possible label. 21 00:00:59,880 --> 00:01:02,490 -矛盾,自然的或必然的, +矛盾,自然的或蕴涵, Contradiction, natural or entailment, 22 @@ -115,32 +115,32 @@ implies the second. 24 00:01:06,930 --> 00:01:08,820 -所以分类成对的句子是一个问题 +所以分类成对的句子是一个 So classifying pairs of sentences is a problem 25 00:01:08,820 --> 00:01:10,260 -值得研究。 +值得研究的问题。 worth studying. 26 00:01:10,260 --> 00:01:12,630 -事实上,在 GLUE 基准测试中, +事实上,在 GLUE 基准中, In fact, in the GLUE benchmark, 27 00:01:12,630 --> 00:01:15,750 -这是文本分类的学术基准 +这是文本分类问题的学术界基准 which is an academic benchmark for text classification 28 00:01:15,750 --> 00:01:17,910 -10 个数据集中有 8 个是重点 -eight of the 10 data sets are focused +10 个数据集中有 8 个是关注 +8 of the 10 datasets are focused 29 00:01:17,910 --> 00:01:19,953 -在使用句子对的任务上。 +使用句子对的任务。 on tasks using pairs of sentences. 30 @@ -165,7 +165,7 @@ they often have an objective related to sentence pairs. 34 00:01:31,230 --> 00:01:34,320 -例如,在预训练期间显示 BERT +例如,在预训练期间 BERT 见到 For instance, during pretraining BERT is shown 35 @@ -175,12 +175,12 @@ pairs of sentences and must predict both 36 00:01:36,810 --> 00:01:39,930 -随机屏蔽令牌的价值,以及第二个是否 +随机掩蔽的标记值,以及第二个是否 the value of randomly masked tokens, and whether the second 37 00:01:39,930 --> 00:01:41,830 -句子是否从头开始。 +句子是否接着第一个句子。 sentence follow from the first or not. 38 @@ -200,32 +200,32 @@ You just have to pass them as two arguments 41 00:01:51,270 --> 00:01:52,120 -到分词器。 +到 tokenizer 。 to the tokenizer. 42 00:01:53,430 --> 00:01:55,470 -在输入 ID 和注意掩码之上 +在我们已经研究过的输入 ID On top of the input IDs and the attention mask 43 00:01:55,470 --> 00:01:56,970 -我们已经研究过, +和注意掩码之上, we studied already, 44 00:01:56,970 --> 00:01:59,910 -它返回一个名为令牌类型 ID 的新字段, +它返回一个名为标记类型 ID 的新字段, it returns a new field called token type IDs, 45 00:01:59,910 --> 00:02:01,790 -它告诉模型哪些令牌属于 +它告诉模型哪些标记属于 which tells the model which tokens belong 46 00:02:01,790 --> 00:02:03,630 -对于第一句话, +第一句话, to the first sentence, 47 @@ -240,32 +240,32 @@ Zooming in a little bit, here has an input IDs 49 00:02:09,840 --> 00:02:12,180 -与它们对应的标记对齐, +与它们对应的 token 对齐, aligned with the tokens they correspond to, 50 00:02:12,180 --> 00:02:15,213 -它们各自的令牌类型 ID 和注意掩码。 +它们各自的标记类型 ID 和注意掩码。 their respective token type ID and attention mask. 51 00:02:16,080 --> 00:02:19,260 -我们可以看到标记器还添加了特殊标记。 +我们可以看到分词器还添加了特殊标记。 We can see the tokenizer also added special tokens. 52 00:02:19,260 --> 00:02:22,620 -所以我们有一个 CLS 标记,来自第一句话的标记, +所以我们有一个 CLS token ,来自第一句话的 token , So we have a CLS token, the tokens from the first sentence, 53 00:02:22,620 --> 00:02:25,770 -一个 SEP 令牌,第二句话中的令牌, +一个 SEP 标记,第二句话中的标记, a SEP token, the tokens from the second sentence, 54 00:02:25,770 --> 00:02:27,003 -和最终的 SEP 令牌。 +和最终的 SEP 标记。 and a final SEP token. 55 @@ -275,12 +275,12 @@ If we have several pairs of sentences, 56 00:02:30,570 --> 00:02:32,840 -我们可以通过传递列表将它们标记在一起 +我们可以通过第一句话的传递列表 we can tokenize them together by passing the list 57 00:02:32,840 --> 00:02:36,630 -第一句话,然后是第二句话的列表 +将它们标记在一起,然后是第二句话的列表 of first sentences, then the list of second sentences 58 @@ -290,7 +290,7 @@ and all the keyword arguments we studied already 59 00:02:39,300 --> 00:02:40,353 -像填充 = 真。 +例如 padding=True。 like padding=True. 60 @@ -300,17 +300,17 @@ Zooming in at the result, 61 00:02:43,140 --> 00:02:45,030 -我们还可以看到标记化添加的填充 -we can see also tokenize added padding +我们可以看到分词器如何添加填充 +we can see how the tokenizer added padding 62 00:02:45,030 --> 00:02:48,090 -到第二对句子来制作两个输出 +到第二对句子使得两个输出的 to the second pair sentences to make the two outputs 63 00:02:48,090 --> 00:02:51,360 -相同的长度,并正确处理令牌类型 ID +长度相同,并正确处理标记类型 ID the same length, and properly dealt with token type IDs 64 diff --git a/subtitles/zh-CN/22_preprocessing-sentence-pairs-(tensorflow).srt b/subtitles/zh-CN/22_preprocessing-sentence-pairs-(tensorflow).srt index 9bccf528e..cbc4612cc 100644 --- a/subtitles/zh-CN/22_preprocessing-sentence-pairs-(tensorflow).srt +++ b/subtitles/zh-CN/22_preprocessing-sentence-pairs-(tensorflow).srt @@ -20,7 +20,7 @@ and batch them together 5 00:00:13,020 --> 00:00:15,660 -在 “一起批量输入” 视频中。 +在 “Batching inputs together” 视频中。 in the "Batching inputs together" video. 6 @@ -75,7 +75,7 @@ In the first pair, the two questions are duplicates; 16 00:00:40,650 --> 00:00:43,620 -第二,他们不是。 +在第二对中,他们不是。 in the second, they are not. 17 @@ -90,7 +90,7 @@ is when we want to know if two sentences 19 00:00:46,980 --> 00:00:49,290 -逻辑上是否相关, +逻辑上相关,或反之 are logically related or not, 20 @@ -185,7 +185,7 @@ BERT is shown pairs of sentences 38 00:01:36,690 --> 00:01:39,900 -并且必须预测随机屏蔽标记的值 +并且必须预测随机屏蔽 token 的值 and must predict both the value of randomly masked tokens 39 @@ -195,12 +195,12 @@ and whether the second sentence 40 00:01:41,250 --> 00:01:42,903 -是否从第一个开始。 +从第一个开始。 follows from the first or not. 41 00:01:44,070 --> 00:01:47,100 -幸运的是,来自 Transformers 库的分词器 +幸运的是,来自 Transformers 库的 tokenizer Fortunately, the tokenizer from the Transformers library 42 @@ -215,7 +215,7 @@ you just have to pass them as two arguments 44 00:01:52,650 --> 00:01:53,613 -到分词器。 +到 tokenizer 。 to the tokenizer. 45 @@ -225,17 +225,17 @@ On top of the input IDs 46 00:01:56,040 --> 00:01:58,440 -和我们已经研究过的注意力面具, +和我们已经研究过的注意力掩码, and the attention mask we studied already, 47 00:01:58,440 --> 00:02:01,530 -它返回一个名为令牌类型 ID 的新字段, +它返回一个名为 token 类型 ID 的新字段, it returns a new field called token type IDs, 48 00:02:01,530 --> 00:02:03,210 -它告诉模型哪些标记 +它告诉模型哪些 token which tells the model which tokens 49 @@ -255,32 +255,32 @@ Zooming in a little bit, here are the input IDs, 52 00:02:11,430 --> 00:02:13,710 -与它们对应的标记对齐, +与它们对应的 token 对齐, aligned with the tokens they correspond to, 53 00:02:13,710 --> 00:02:17,193 -它们各自的令牌类型 ID 和注意掩码。 +它们各自的 token 类型 ID 和注意掩码。 their respective token type ID and attention mask. 54 00:02:18,540 --> 00:02:21,300 -我们可以看到标记器还添加了特殊标记 +我们可以看到 tokenizer 还添加了特殊 token We can see the tokenizer also added special tokens 55 00:02:21,300 --> 00:02:25,230 -所以我们有一个 CLS 标记,来自第一句话的标记, +所以我们有一个 CLS token ,来自第一句话的 token , so we have a CLS token, the tokens from the first sentence, 56 00:02:25,230 --> 00:02:28,590 -一个 SEP 令牌,第二句话中的令牌, +一个 SEP token ,第二句话中的 token , a SEP token, the tokens from the second sentence, 57 00:02:28,590 --> 00:02:30,153 -和最终的 SEP 令牌。 +和最终的 SEP token 。 and a final SEP token. 58 @@ -310,7 +310,7 @@ and all the keyword arguments we studied already, 63 00:02:43,050 --> 00:02:44,133 -像填充 = 真。 +像 padding=True 。 like padding=True. 64 @@ -320,7 +320,7 @@ Zooming in at the result, 65 00:02:46,770 --> 00:02:49,050 -我们可以看到分词器是如何添加填充的 +我们可以看到 tokenizer 是如何添加填充的 we can see how the tokenizer added padding 66 @@ -335,7 +335,7 @@ to make the two outputs the same length. 68 00:02:53,490 --> 00:02:55,620 -它还可以正确处理令牌类型 IDS +它还可以正确处理 token 类型 IDS It also properly dealt with token type IDS 69 diff --git a/subtitles/zh-CN/23_what-is-dynamic-padding.srt b/subtitles/zh-CN/23_what-is-dynamic-padding.srt index 30efe21df..4dba99a5f 100644 --- a/subtitles/zh-CN/23_what-is-dynamic-padding.srt +++ b/subtitles/zh-CN/23_what-is-dynamic-padding.srt @@ -15,12 +15,12 @@ In the "Batching Inputs together" video, 4 00:00:10,890 --> 00:00:12,720 -我们已经看到能够对输入进行分组 +我们已经看到为了能够对(不同长度的同批次)输入进行分组 we have seen that to be able to group inputs 5 00:00:12,720 --> 00:00:15,300 -同一批不同长度的, +(同一批不同长度的), of different lengths in the same batch, 6 @@ -40,12 +40,12 @@ Here, for instance, the longest sentence is the third one, 9 00:00:24,600 --> 00:00:27,270 -我们需要添加五个、两个或七个填充令牌 +我们需要添加五个、两个或七个填充标记 and we need to add five, two, or seven pad tokens 10 00:00:27,270 --> 00:00:30,090 -到其他句子有四个句子 +到其他句子使得四个句子具有 to the other sentences to have four sentences 11 @@ -65,12 +65,12 @@ there are various padding strategies we can apply. 14 00:00:37,560 --> 00:00:39,540 -最明显的一种是填充所有元素 +最明显的一种是填充整个数据集所有的样本 The most obvious one is to pad all the elements 15 00:00:39,540 --> 00:00:40,923 -数据集的相同长度: +达到相同的长度: of the dataset to the same length: 16 @@ -80,67 +80,67 @@ the length of the longest sample. 17 00:00:44,070 --> 00:00:45,330 -这会给我们批次 +我们得到具有相同形状的批次 This will then give us batches 18 00:00:45,330 --> 00:00:46,890 -都具有相同的形状 + that all have the same shape 19 00:00:46,890 --> 00:00:49,800 -由最大序列长度决定。 +(其长度)由最大序列长度决定。 determined by the maximum sequence length. 20 00:00:49,800 --> 00:00:52,893 -缺点是批次由短句组成 +缺点是(如果)批次样本由短句组成 The downside is that batches composed from short sentences 21 00:00:52,893 --> 00:00:54,960 -会有很多填充令牌 +将带来很多填充符号 will have a lot of padding tokens 22 00:00:54,960 --> 00:00:57,660 -这将在模型中引入更多计算 +并且在模型中引入更多不必要的计算。 which will introduce more computations in the model 23 00:00:57,660 --> 00:00:58,910 -我们最终不需要。 + we ultimately don't need. 24 00:01:00,060 --> 00:01:03,300 -为了避免这种情况,另一种策略是填充元素 +为了避免这种情况,另一种策略是填充(短样本)符号 To avoid this, another strategy is to pad the elements 25 00:01:03,300 --> 00:01:05,280 -当我们把它们批在一起时, +当把它们放在一批时, when we batch them together, 26 00:01:05,280 --> 00:01:08,190 -到批次中最长的句子。 +达到本批次中最长句子的长度。 to the longest sentence inside the batch. 27 00:01:08,190 --> 00:01:12,000 -这样,由短输入组成的批次会更小 +这样,由短样本输入组成的批次大小 This way, batches composed of short inputs will be smaller 28 00:01:12,000 --> 00:01:13,920 -比包含最长句子的批次 +会比按整个数据集最长句子的长度(补齐)批次更小 than the batch containing the longest sentence 29 00:01:13,920 --> 00:01:15,510 -在数据集中。 + in the dataset. 30 @@ -155,7 +155,7 @@ The downside is that all batches 32 00:01:20,490 --> 00:01:22,140 -然后会有不同的形状, +会有不同的形状, will then have different shapes, 33 @@ -170,7 +170,7 @@ Let's see how to apply both strategies in practice. 35 00:01:29,370 --> 00:01:31,280 -我们实际上已经看到了如何应用固定填充 +我们实际上已经知道了如何使用固定填充 We have actually seen how to apply fixed padding 36 @@ -190,22 +190,22 @@ after loading the dataset and tokenizer, 39 00:01:38,250 --> 00:01:40,680 -我们将标记化应用于所有数据集 +我们将符号化应用于所有数据集 we applied the tokenization to all the dataset 40 00:01:40,680 --> 00:01:42,480 -带填充和截断 +包括填充和截断 with padding and truncation 41 00:01:42,480 --> 00:01:45,273 -制作所有长度为 128 的样本。 +保证所有样本的长度为 128 。 to make all samples of length 128. 42 00:01:46,530 --> 00:01:48,360 -结果,如果我们传递这个数据集 +最后,如果我们传递这个数据集 As a result, if we pass this dataset 43 @@ -215,7 +215,8 @@ to a PyTorch DataLoader, 44 00:01:50,520 --> 00:01:55,503 -我们得到形状批量大小的批次,这里是 16,乘以 128。 +我们得到形状为 batch_size 乘以 16 乘以 128 的批次。 + we get batches of shape batch size, here 16, by 128. 45 @@ -230,7 +231,7 @@ we must defer the padding to the batch preparation, 47 00:02:01,440 --> 00:02:04,740 -所以我们从标记化函数中删除了那部分。 +所以我们从标记函数中删除了那部分。 so we remove that part from our tokenize function. 48 @@ -295,7 +296,7 @@ We pass it to the PyTorch DataLoader as a collate function, 60 00:02:35,310 --> 00:02:37,620 -然后观察生成的批次 +然后观察到生成的批次 then observe that the batches generated 61 @@ -310,12 +311,13 @@ all way below the 128 from before. 63 00:02:42,660 --> 00:02:44,820 -动态批处理几乎总是更快 +动态批处理几乎在 CPU 和 GPU 上更快, + Dynamic batching will almost always be faster 64 00:02:44,820 --> 00:02:47,913 -在 CPU 和 GPU 上,所以如果可以的话你应该应用它。 +所以如果可以的话你应该应用它。 on CPUs and GPUs, so you should apply it if you can. 65 @@ -330,7 +332,7 @@ if you run your training script on TPU 67 00:02:53,490 --> 00:02:55,293 -或者需要成批的固定形状。 +或者需要固定形状的批次输入。 or need batches of fixed shapes. 68 diff --git a/subtitles/zh-CN/24_the-trainer-api.srt b/subtitles/zh-CN/24_the-trainer-api.srt index 2ffaf8c72..de4af9a41 100644 --- a/subtitles/zh-CN/24_the-trainer-api.srt +++ b/subtitles/zh-CN/24_the-trainer-api.srt @@ -15,28 +15,28 @@ 4 00:00:05,698 --> 00:00:06,548 -- 所以培训师 API。 -- So Trainer API. +- Trainer API。 +- The Trainer API. 5 00:00:08,070 --> 00:00:10,040 -所以 Transformers Library 提供了一个 Trainer API -So Transformers Library provides a Trainer API +Transformers Library 提供了一个 Trainer API +The Transformers Library provides a Trainer API 6 00:00:10,040 --> 00:00:13,320 -让你轻松找到调谐变压器模型 -that allows you to easily find tune transformers models +让你能够轻松的微调 Transformer 模型 +that allows you to easily fine-tune transformer models 7 00:00:13,320 --> 00:00:14,193 -在你的数据集上。 -on your dataset. +在你自己的数据集上。 +on your own dataset. 8 00:00:15,150 --> 00:00:17,250 -所以 Trainer 类接受你的数据集, -So Trainer class takes your datasets, +Trainer 类接受你的数据集, +The Trainer class take your datasets, 9 00:00:17,250 --> 00:00:19,900 @@ -50,28 +50,28 @@ and can perform the training on any kind of setup, 11 00:00:23,310 --> 00:00:26,654 -CPU、GPU、多个 GPU、TPU -CPU, GPU, multiple GPUs, TPUs +(CPU、GPU、多个 GPU、TPUs) +(CPU, GPU, multiple GPUs, TPUs) 12 00:00:26,654 --> 00:00:28,680 -也可以计算预测 +也可以在任意数据集上进行推理 can also compute the predictions 13 00:00:28,680 --> 00:00:31,710 -在任何数据集上,如果你提供了指标 +如果你提供了指标 on any dataset and if you provided metrics 14 00:00:31,710 --> 00:00:33,813 -在任何数据集上评估你的模型。 +就可以在任意数据集上评估你的模型。 evaluate your model on any dataset. 15 00:00:34,950 --> 00:00:36,930 -你还可以涉及最终数据处理 -You can also involve final data processing +Trainer 也可以负责最后的数据处理 +It can also handle final data processing 16 00:00:36,930 --> 00:00:38,670 @@ -80,13 +80,13 @@ such as dynamic padding, 17 00:00:38,670 --> 00:00:40,377 -只要你提供分词器 +只要你提供 tokenizer as long as you provide the tokenizer 18 00:00:40,377 --> 00:00:42,693 -或给定数据整理器。 -or given data collator. +或给定 data collator。 +or a given data collator. 19 00:00:43,572 --> 00:00:45,900 @@ -95,53 +95,53 @@ We will try this API on the MRPC dataset, 20 00:00:45,900 --> 00:00:48,492 -因为它相对较小且易于预处理。 +因为该数据集相对较小且易于预处理。 since it's relatively small and easy to preprocess. 21 00:00:48,492 --> 00:00:49,325 -正如我们在数据集概述视频中看到的那样, +正如我们在 Datasets 概述视频中看到的那样, As we saw in the Datasets overview video, 22 00:00:49,325 --> 00:00:54,325 -但是我们可以对其进行预处理。 -however we can preprocess it. +我们可以像这样对其进行预处理。 +here is how we can preprocess it. 23 00:00:54,511 --> 00:00:57,030 -我们在预处理期间不应用填充, +我们在预处理过程中不进行填充, We do not apply padding during the preprocessing, 24 00:00:57,030 --> 00:00:58,590 -因为我们将使用动态填充 +因为我们将进行动态填充 as we will use dynamic padding 25 00:00:58,590 --> 00:01:00,083 -在 DataCollatorWithPadding 之前。 -before DataCollatorWithPadding. +使用我们的 DataCollatorWithPadding。 +with our DataCollatorWithPadding. 26 00:01:01,170 --> 00:01:02,790 -请注意,我们不执行最后的步骤 +请注意,我们不执行一些最终的数据处理步骤 Note that we don't do the final steps 27 00:01:02,790 --> 00:01:04,830 -重命名删除列 -of renaming removing columns +像是重命名列/删除列 +of renaming/removing columns 28 00:01:04,830 --> 00:01:06,873 -或将格式设置为火炬张量。 +或将格式设置为 torch 张量。 or set the format to torch tensors. 29 00:01:07,710 --> 00:01:10,560 -所以 Trainer 会自动为我们做这一切 -So Trainer will do all of this automatically for us +Trainer 会自动为我们做这一切 +The Trainer will do all of this automatically for us 30 00:01:10,560 --> 00:01:12,633 @@ -150,7 +150,7 @@ by analyzing the model signature. 31 00:01:14,054 --> 00:01:16,650 -创建培训师之前的最后一步是 +实例化 Trainer 前的最后一步是 The last step before creating the Trainer are 32 @@ -165,62 +165,62 @@ and some training hyperparameters. 34 00:01:20,250 --> 00:01:22,653 -我们在模型 API 视频中看到了第一个。 +我们在 model API 视频中学会了如何定义模型。 We saw to do the first in the model API video. 35 00:01:23,734 --> 00:01:26,790 -第二次我们使用 TrainingArguments 类。 +对于第二点,我们使用 TrainingArguments 类。 For the second we use the TrainingArguments class. 36 00:01:26,790 --> 00:01:28,710 -它只需要一个文件夹的路径 +Trainer 只需要一个文件夹的路径 It only takes a path to a folder 37 00:01:28,710 --> 00:01:30,900 -结果和检查点将保存在哪里, +用以保存结果和检查点, where results and checkpoint will be saved, 38 00:01:30,900 --> 00:01:33,060 -但你也可以自定义所有超参数 +但你也可以自定义你的 Trainer 会使用的 but you can also customize all the hyperparameters 39 00:01:33,060 --> 00:01:34,470 -你的教练会使用, +所有超参数 your Trainer will use, 40 00:01:34,470 --> 00:01:37,270 -学习权重、训练影响数等。等等。 -learning weight, number of training impacts, et. cetera. +比如学习率,训练几个 epoch 等等。 +learning rate, number of training epochs etc. 41 00:01:38,190 --> 00:01:39,660 -创建培训师非常容易 -It's been very easy to create a Trainer +接下来实例化一个 Trainer 并开始训练 +It's then very easy to create a Trainer 42 00:01:39,660 --> 00:01:41,400 -并开展培训。 +就非常简单了。 and launch a training. 43 00:01:41,400 --> 00:01:43,170 -你应该显示一个进度条 -You should display a progress bar +这会显示一个进度条 +This should display a progress bar 44 00:01:43,170 --> 00:01:45,900 -如果你在 GPU 上运行,几分钟后 -and after a few minutes if you're running on a GPU +几分钟后(如果你在 GPU 上运行) +and after a few minutes (if you're running on a GPU) 45 00:01:45,900 --> 00:01:48,000 -你应该完成培训。 +你会完成训练。 you should have the training finished. 46 @@ -235,12 +235,12 @@ as you will only get a training loss 48 00:01:52,710 --> 00:01:54,300 -这并没有真正告诉你任何事情 +这并没有真正告诉你 which doesn't really tell you anything 49 00:01:54,300 --> 00:01:56,820 -关于你的模型表现如何。 +你的模型表现如何。 about how well your model is performing. 50 @@ -260,12 +260,12 @@ To get those metrics, 53 00:02:02,160 --> 00:02:03,810 -我们将首先收集预测 +我们将首先使用预测方法 we will first gather the predictions 54 00:02:03,810 --> 00:02:06,513 -使用预测方法在整个评估集上。 +在整个评估集上收集预测结果。 on the whole evaluation set using the predict method. 55 @@ -275,23 +275,23 @@ It returns a namedtuple with three fields, 56 00:02:10,020 --> 00:02:12,990 -预测,其中包含预测模型。 -Prediction, which contains the model of predictions. +Prediction (其中包含模型的预测), +Prediction(which contains the model predictions), 57 00:02:12,990 --> 00:02:15,030 -Label_IDs,其中包含标签 -Label_IDs, which contains the labels +Label_IDs (其中包含标签 +Label_IDs(which contains the labels 58 00:02:15,030 --> 00:02:16,800 -如果你的数据集有它们 -if your dataset had them +如果你的数据集有的话) +if your dataset had them) 59 00:02:16,800 --> 00:02:18,570 -和此处为空的指标。 -and metrics which is empty here. +和指标(在本示例中是空的) +and metrics (which is empty here). 60 00:02:18,570 --> 00:02:20,520 @@ -300,17 +300,17 @@ We're trying to do that. 61 00:02:20,520 --> 00:02:22,470 -预测是模型的对数 +预测结果是模型 The predictions are the logits of the models 62 00:02:22,470 --> 00:02:24,143 -对于数据集中的所有句子。 +对于数据集中的所有句子所输出的 logits。 for all the sentences in the dataset. 63 00:02:24,143 --> 00:02:27,513 -所以一个形状为 408 x 2 的 NumPy 数组。 +所以是一个形状为 408 x 2 的 NumPy 数组。 So a NumPy array of shape 408 by 2. 64 @@ -320,7 +320,7 @@ To match them with our labels, 65 00:02:30,270 --> 00:02:31,590 -我们需要采取最大的 logit +我们需要取最大的 logit we need to take the maximum logit 66 @@ -330,8 +330,8 @@ for each prediction 67 00:02:32,850 --> 00:02:35,820 -知道预测了这两个类别中的哪一个。 -to know which of the two classes was predicted. +(知道两个类别中的哪一个类是所预测的结果) +(to know which of the two classes was predicted.) 68 00:02:35,820 --> 00:02:37,683 @@ -340,23 +340,23 @@ We do this with the argmax function. 69 00:02:38,640 --> 00:02:41,550 -然后我们可以使用数据集库中的指标。 +然后我们可以使用 Datasets library 中的指标。 Then we can use a metric from the Datasets library. 70 00:02:41,550 --> 00:02:43,500 -它可以像数据集一样轻松加载 +它可以像数据集一样被轻松地加载 It can be loaded as easily as a dataset 71 00:02:43,500 --> 00:02:45,360 -具有负载度量功能 -with the load metric function +使用 load_metric 函数 +with the load_metric function 72 00:02:45,360 --> 00:02:49,500 -并且每个返回用于数据集的评估指标。 -and each returns the evaluation metric used for the dataset. +并且返回用于该数据集的评估指标。 +and it returns the evaluation metric used for the dataset. 73 00:02:49,500 --> 00:02:51,600 @@ -365,13 +365,13 @@ We can see our model did learn something 74 00:02:51,600 --> 00:02:54,363 -因为它的准确率为 85.7%。 +因为它有 85.7% 的准确率。 as it is 85.7% accurate. 75 00:02:55,440 --> 00:02:57,870 -在训练期间监控评估矩阵, -To monitor the evaluation matrix during training, +为了在训练期间监控评估指标, +To monitor the evaluation metrics during training, 76 00:02:57,870 --> 00:02:59,829 @@ -385,52 +385,52 @@ that does the same step as before. 78 00:03:02,670 --> 00:03:04,728 -它需要一个带有预测和标签的命名元组 +它接收一个带有预测和标签的命名元组 It takes a namedtuple with predictions and labels 79 00:03:04,728 --> 00:03:06,327 -并且必须返回一个字典 +并且返回一个字典 and must return a dictionary 80 00:03:06,327 --> 00:03:08,427 -使用我们想要跟踪的指标。 +包含我们想要跟踪的指标。 with the metrics we want to keep track of. 81 00:03:09,360 --> 00:03:11,490 -通过 epoch 评估策略 +通过将评估策略设置为 epoch By passing the epoch evaluation strategy 82 00:03:11,490 --> 00:03:13,080 -对于我们的训练论点, -to our training arguments, +对于我们的 TrainingArguments, +to our TrainingArguments, 83 00:03:13,080 --> 00:03:14,490 -我们告诉培训师评估 +我们告诉 Trainer 去进行评估 we tell the Trainer to evaluate 84 00:03:14,490 --> 00:03:15,903 -在每个纪元的末尾。 +在每个 epoch 结束的时候。 at the end of every epoch. 85 00:03:17,280 --> 00:03:18,587 -在笔记本中启动培训 +在 notebook 中启动训练 Launching a training inside a notebook 86 00:03:18,587 --> 00:03:20,640 -然后会显示一个进度条 +会显示一个进度条 will then display a progress bar 87 00:03:20,640 --> 00:03:23,643 -并在你通过每个纪元时完成你在这里看到的表格。 +并在你运行完每个 epoch 时将数据填到你看到的这个表格。 and complete the table you see here as you pass every epoch. 88 diff --git a/subtitles/zh-CN/26_fine-tuning-with-tensorflow.srt b/subtitles/zh-CN/26_fine-tuning-with-tensorflow.srt index a4c18d3a4..bb4ddce17 100644 --- a/subtitles/zh-CN/26_fine-tuning-with-tensorflow.srt +++ b/subtitles/zh-CN/26_fine-tuning-with-tensorflow.srt @@ -20,12 +20,12 @@ It's very quick. 5 00:00:12,510 --> 00:00:14,490 -如果你看过我们的管道视频, +如果你看过我们关于 pipeline 的视频, And if you've watched our pipeline videos, 6 00:00:14,490 --> 00:00:18,150 -我将在下面链接,过程非常相似。 +我将在下面链接之,过程非常相似。 which I'll link below, the process is very similar. 7 @@ -50,7 +50,7 @@ So to learn more about transfer learning, 11 00:00:28,710 --> 00:00:31,320 -前往 “什么是迁移学习?” 视频, +前往 “What is transfer learning?” 视频, head to the 'What is transfer learning?' video, 12 @@ -85,7 +85,7 @@ as the foundation for our training today. 18 00:00:44,850 --> 00:00:46,770 -但这条怪物线是什么, +但这条怪物般的一行是什么, But what is this monstrosity line, 19 @@ -170,12 +170,12 @@ And secondly, it needs to know how many classes 35 00:01:23,940 --> 00:01:26,693 -你的问题有,因为这将决定大小, +你的问题是有的,因为这将决定大小, your problem has, because that will determine the size, 36 00:01:26,693 --> 00:01:29,610 -输出头中的神经元数量。 +对输出头中的神经元数量。 the number of neurons in the output head. 37 @@ -185,12 +185,12 @@ So if you want to follow along with the data 38 00:01:31,530 --> 00:01:34,500 -来自我们的数据集视频,我将在下面链接, +来自我们有关数据集的视频,我将在下面链接, from our datasets videos, which I'll link below, 39 00:01:34,500 --> 00:01:37,440 -那么你将有两个班级,积极的和消极的, +那么你将有两个类别,积极的和消极的, then you'll have two classes, positive and negative, 40 @@ -255,7 +255,7 @@ loss function. 52 00:02:07,260 --> 00:02:09,930 -所以这是一口,但它是标准的损失函数 +所以这是一点点,但它是标准的损失函数 So that's a mouthful, but it's the standard loss function 53 @@ -275,7 +275,7 @@ to output large values for the right class, 56 00:02:17,730 --> 00:02:20,910 -以及错误类别的低值。 +以及为错误的类别输出低值。 and low values for the wrong classes. 57 @@ -290,7 +290,7 @@ like we did with the optimizer. 59 00:02:26,010 --> 00:02:27,600 -但这里有一个风险, +但这里有一个问题, But there's a risk there, 60 @@ -300,7 +300,7 @@ there's a very common trap people fall into, 61 00:02:30,090 --> 00:02:32,580 -这是默认情况下,这种损失假设 +就是默认情况下,这种损失假设 which is that by default, this loss assumes 62 @@ -335,7 +335,7 @@ But you probably seen these before 68 00:02:47,790 --> 00:02:49,950 -在关于管道的视频中。 +在关于 pipeline 的视频中。 in the video about pipelines. 69 @@ -375,7 +375,7 @@ But for now, remember to set from_logits to true. 76 00:03:09,480 --> 00:03:11,430 -compile 需要知道的第二件事 +编译需要知道的第二件事 The second thing compile needs to know 77 @@ -385,7 +385,7 @@ is the optimizer you want. 78 00:03:13,230 --> 00:03:15,120 -在我们的例子中,我们使用亚当, +在我们的例子中,我们使用 adam , In our case, we use adam, 79 @@ -395,7 +395,7 @@ which is sort of the standard optimizer 80 00:03:16,830 --> 00:03:18,720 -这些天用于深度学习。 +用于现代深度学习。 for deep learning these days. 81 @@ -490,7 +490,7 @@ So we don't need to specify separate labels, 99 00:04:00,420 --> 00:04:01,570 -当我们称呼健康时。 +当我们调用 fit 时。 when we're calling fit. 100 @@ -510,7 +510,7 @@ like the number of epochs for training 103 00:04:09,900 --> 00:04:12,420 -你可以传递一些其他参数来适应。 +你可以传递一些其他参数给 fit 。 where there's some other arguments you can pass to fit. 104 diff --git a/subtitles/zh-CN/27_learning-rate-scheduling-with-tensorflow.srt b/subtitles/zh-CN/27_learning-rate-scheduling-with-tensorflow.srt index 700402578..9ce0a5587 100644 --- a/subtitles/zh-CN/27_learning-rate-scheduling-with-tensorflow.srt +++ b/subtitles/zh-CN/27_learning-rate-scheduling-with-tensorflow.srt @@ -65,7 +65,7 @@ which will make your training 14 00:00:31,080 --> 00:00:33,303 -更加一致地成功。 +更加稳定地成功。 much more consistently successful. 15 @@ -95,23 +95,23 @@ by default, Adam uses a learning rate 20 00:00:48,030 --> 00:00:51,540 -10 的负 3,1 E 负 3, -of 10 to the minus 3, 1 E minus 3, +10 的 -3 次方,1e-3, +of 10 to the minus 3, 1e-3, 21 00:00:51,540 --> 00:00:54,660 -这对于训练变压器模型来说非常高。 +这对于训练 transformer 模型来说非常高。 and that's very high for training transformer models. 22 00:00:54,660 --> 00:00:58,200 -我们将从 5 乘 10 到负 5 开始, +我们将从 5 乘 10 的负 5次方 开始, We're going to start at 5 by 10 to the minus 5, 23 00:00:58,200 --> 00:01:02,700 -5 E - 5,比默认值低 20 倍。 -5 E minus 5, which is 20 times lower than the default. +5e-5,比默认值低 20 倍。 +5e-5, which is 20 times lower than the default. 24 00:01:02,700 --> 00:01:06,330 @@ -130,7 +130,7 @@ if we decay the learning rate down to a tiny value, 27 00:01:11,160 --> 00:01:13,920 -甚至在培训过程中为零。 +甚至在训练过程中为零。 or even to zero , over the course of training. 28 @@ -145,17 +145,17 @@ this Polynomial Decay schedule thing is doing. 30 00:01:18,540 --> 00:01:21,570 -等会儿我会告诉你衰变是什么样子的, +等会儿我会告诉你衰减是什么样子的, So I'll show you what that decay looks like in a second, 31 00:01:21,570 --> 00:01:23,160 -但首先我们需要告诉调度程序 +但首先我们需要告诉规划程序 but first we need to tell the scheduler 32 00:01:23,160 --> 00:01:25,290 -培训将持续多长时间, +训练将持续多长时间, how long training is going to be, 33 @@ -190,12 +190,12 @@ and then we multiply that 39 00:01:39,570 --> 00:01:41,220 -按纪元数 +按 epoch 数 by the number of epochs 40 00:01:41,220 --> 00:01:42,930 -获得批次总数 +获得 batch 总数 to get the total number of batches 41 @@ -210,7 +210,7 @@ Once we know how many training steps we're taking, 43 00:01:47,880 --> 00:01:50,580 -我们只是将所有这些信息传递给调度程序 +我们只是将所有这些信息传递给规划程序 we just pass all that information to the scheduler 44 @@ -230,12 +230,12 @@ Well, it looks like this, 47 00:01:59,610 --> 00:02:02,160 -它从 5 E 减 5 开始, -it starts at 5 E minus 5, +它从 5e-5 开始, +it starts at 5e-5, 48 00:02:02,160 --> 00:02:05,490 -这意味着 10 的 5 乘以负 5, +这意味着 5 乘以 10^{-5}, which means 5 times 10 to the minus 5, 49 @@ -270,7 +270,7 @@ this is actually constant or a linear decay, 55 00:02:18,690 --> 00:02:20,310 -我知道这个名字是多项式的, +我知道这个名字有 "多项式", and I know the name is polynomial, 56 @@ -425,7 +425,7 @@ which is getting passed to to the learning rate argument 86 00:03:27,540 --> 00:03:29,100 -该优化器。 +对该优化器的。 of that optimizer. 87 @@ -445,7 +445,7 @@ so this is going to be sparse categorical crossentropy 90 00:03:37,050 --> 00:03:39,840 -如果你正在关注微调视频。 +如果你正在关注 fine-tuning (微调) 的视频。 if you're following along from the fine-tuning video. 91 @@ -480,7 +480,7 @@ Remember, because we compiled the model 97 00:03:51,600 --> 00:03:54,300 -使用新的优化器和新的学习率计划, +使用新的优化器和新的学习率规划, with the new optimizer and the new learning rate schedule, 98 @@ -490,7 +490,7 @@ we actually don't need to change anything at all 99 00:03:56,190 --> 00:03:57,360 -当我们称呼合适时, +当我们调用 fit 时, when we call fit, 100 @@ -515,7 +515,7 @@ with a nice, smooth learning rate decay, 104 00:04:04,740 --> 00:04:06,330 -从好的价值开始, +从好的值开始, starting from a good value, 105 diff --git a/subtitles/zh-CN/28_tensorflow-predictions-and-metrics.srt b/subtitles/zh-CN/28_tensorflow-predictions-and-metrics.srt index 9d157d4d1..b6d06f85d 100644 --- a/subtitles/zh-CN/28_tensorflow-predictions-and-metrics.srt +++ b/subtitles/zh-CN/28_tensorflow-predictions-and-metrics.srt @@ -15,7 +15,7 @@ and as always, there'll be links below 4 00:00:09,000 --> 00:00:10,740 -如果你想检查那些, +如果你想回看那些, if you want to check those out, 5 @@ -25,7 +25,7 @@ we showed you how to initialize and fine-tune 6 00:00:13,230 --> 00:00:15,690 -TensorFlow 中的转换器模型。 +TensorFlow 中的 transformer 模型。 a transformer model in TensorFlow. 7 @@ -55,13 +55,13 @@ so let's see how to do that. 12 00:00:25,560 --> 00:00:28,320 -同样,如果你熟悉 Keras,那么好消息 -Again, if you're familiar with Keras, the good news +同样,如果你熟悉 Keras,那么好消息是 +Again, if you're familiar with Keras, the good news is 13 00:00:28,320 --> 00:00:31,860 -是因为只有标准的 Keras 模型, -is that because there are just standard Keras models, +因为只有标准的 Keras 模型, +that because there are just standard Keras models, 14 00:00:31,860 --> 00:00:34,770 @@ -75,7 +75,7 @@ as shown here. 16 00:00:36,990 --> 00:00:40,560 -你只需将标记化的文本传递给此方法, +你只需将分词化的文本传递给此方法, You simply pass in tokenized text to this method, 17 @@ -105,17 +105,17 @@ but most of the time the thing you want 22 00:00:50,310 --> 00:00:52,290 -是输出逻辑。 +是输出的 logits 。 is the output logits. 23 00:00:52,290 --> 00:00:54,900 -如果你在登录之前没有遇到过它们, -If you haven't come across them before logits, +如果你在之前没有遇到过它们,logits +If you haven't come across them before, logits, 24 00:00:54,900 --> 00:00:57,630 -有时对 logits 发音,因为没有人确定, +有时发音成 logits ,因为没有人确定, sometimes pronounced to logits because no one's sure, 25 @@ -125,7 +125,7 @@ are the outputs of the last layer of the network 26 00:01:00,390 --> 00:01:03,150 -因为在应用 softmax 之前。 +因为在使用 softmax 之前。 because before a softmax has been applied. 27 @@ -135,27 +135,27 @@ So if you want to turn the logits 28 00:01:04,710 --> 00:01:06,900 -进入模型的概率输出, +进成模型的概率输出, into the model's probability outputs, 29 00:01:06,900 --> 00:01:09,423 -你只需应用一个 softmax,就像这样。 +你只需使用一个 softmax,就像这样。 you just apply a softmax, like so. 30 00:01:10,981 --> 00:01:12,630 -如果我们想改变这些概率怎么办 +如果我们想这些概率 What if we want to turn those probabilities 31 00:01:12,630 --> 00:01:14,370 -进入课堂预测? +进成类别预测? into class predictions? 32 00:01:14,370 --> 00:01:16,410 -同样,它非常简单。 +同样,它非常直接。 Again, it's very straightforward. 33 @@ -195,7 +195,7 @@ one in the first position, and so on. 40 00:01:37,350 --> 00:01:40,380 -所以这些是我们的类预测表明类零, +所以这些是我们的类预测表明第零类, So these are our class predictions indicating class zero, 41 @@ -205,7 +205,7 @@ class one, and so on. 42 00:01:42,300 --> 00:01:45,090 -事实上,如果你想要的只是课堂预测, +事实上,如果你想要的只是类别预测, In fact, if class predictions are all you want, 43 @@ -260,12 +260,12 @@ for the model's predictions. 53 00:02:09,060 --> 00:02:10,920 -如果你关注我们的数据集 +如果你关注我们有关数据集 If you're following along with our datasets 54 00:02:10,920 --> 00:02:12,390 -和微调视频, +和微调的视频, and fine tuning videos, 55 @@ -280,7 +280,7 @@ which is part of the GLUE benchmark. 57 00:02:17,130 --> 00:02:19,050 -每个 GLUE 数据集 +GLUE 数据集的每一个 Each of the GLUE datasets 58 @@ -310,12 +310,12 @@ For the MRPC dataset, the built-in metrics are accuracy 63 00:02:33,720 --> 00:02:35,790 -它只是衡量时间的百分比 +它只是衡量百分比, 其 which just measures the percentage of the time 64 00:02:35,790 --> 00:02:37,830 -模型的预测是正确的, +模型的预测是正确的次数, the model's prediction was correct, 65 @@ -325,12 +325,12 @@ and the F1 score, 66 00:02:39,780 --> 00:02:41,610 -这是一个稍微复杂的措施 +这是一个稍微复杂的度量 which is a slightly more complex measure 67 00:02:41,610 --> 00:02:43,920 -衡量模型权衡的程度 +衡量模型如何权衡 that measures how well the model trades off 68 @@ -355,7 +355,7 @@ and to the ground truth labels, 72 00:02:53,220 --> 00:02:56,880 -我们在一个简单的 Python dict 中得到我们的结果。 +我们在一个简单的 Python 字典中得到我们的结果。 and we get our results in a straightforward Python dict. 73 @@ -400,12 +400,12 @@ on the fly while you're training, 81 00:03:10,470 --> 00:03:11,910 -这给了你一个非常有用的见解 +这给了你一个非常有用的理解 which gives you a very useful insight 82 00:03:11,910 --> 00:03:13,740 -了解培训的进展情况。 +了解训练的进展情况。 into how training is going. 83 @@ -445,7 +445,7 @@ or you can import the actual metric objects 90 00:03:30,810 --> 00:03:33,240 -并向他们传递具体的论点。 +并向他们传递具体的参数。 and pass specific arguments to them. 91 @@ -470,7 +470,7 @@ Once a model has been compiled with a metric, 95 00:03:43,140 --> 00:03:45,360 -它将报告该训练指标, +它将报告该指标对于训练, it will report that metric for training, 96 @@ -515,7 +515,7 @@ by default in Keras, 104 00:04:02,850 --> 00:04:04,473 -比如 F1 分数。 +比如 F1 score. such as the F1 score. 105 diff --git a/subtitles/zh-CN/29_write-your-training-loop-in-pytorch.srt b/subtitles/zh-CN/29_write-your-training-loop-in-pytorch.srt index 732483f13..8d956c8b9 100644 --- a/subtitles/zh-CN/29_write-your-training-loop-in-pytorch.srt +++ b/subtitles/zh-CN/29_write-your-training-loop-in-pytorch.srt @@ -15,8 +15,8 @@ 4 00:00:05,460 --> 00:00:08,486 -- 使用 PyTorch 编写你自己的训练循环。 -- Write your own training loop with PyTorch. +使用 PyTorch 编写你自己的训练循环。 +Write your own training loop with PyTorch. 5 00:00:08,486 --> 00:00:09,960 @@ -25,7 +25,7 @@ In this video, we'll look at 6 00:00:09,960 --> 00:00:12,750 -我们如何进行与培训师视频中相同的微调, +我们如何进行与训练器视频中相同的微调, how we can do the same fine-tuning as in the Trainer video, 7 @@ -35,12 +35,12 @@ but without relying on that class. 8 00:00:14,760 --> 00:00:17,790 -这样,你就可以轻松自定义每个步骤 +这样,你就可以根据你的需要轻松自定义 This way, you'll be able to easily customize each step 9 00:00:17,790 --> 00:00:20,310 -到你需要的训练循环。 +训练循环的每个步骤。 to the training loop to your needs. 10 @@ -50,12 +50,12 @@ This is also very useful 11 00:00:21,660 --> 00:00:22,740 -手动调试某些东西 +对于手动调试 to manually debug something 12 00:00:22,740 --> 00:00:24,590 -Trainer API 出了问题。 +Trainer API 出现的问题。 that went wrong with the Trainer API. 13 @@ -85,8 +85,8 @@ That number is not useful in its own, 18 00:00:39,316 --> 00:00:40,260 -用于计算 -that is used to compute +它是用于计算 +but is used to compute 19 00:00:40,260 --> 00:00:42,150 @@ -95,12 +95,12 @@ the ingredients of our model weights, 20 00:00:42,150 --> 00:00:43,440 -那是损失的导数 +即,损失关于 that is the derivative of the loss 21 00:00:44,610 --> 00:00:47,160 -关于每个模型的重量。 +每个模型权重的导数。 with respect to each model weight. 22 @@ -125,7 +125,7 @@ We then repeat the process 26 00:00:54,510 --> 00:00:56,880 -与一批新的训练数据。 +使用一批新的训练数据。 with a new batch of training data. 27 @@ -165,7 +165,7 @@ Check out the videos link below 34 00:01:12,630 --> 00:01:14,280 -如果你还没有看到它们。 +如果你还没有看过它们。 if you haven't seen them already. 35 @@ -180,8 +180,8 @@ which will be responsible to convert 37 00:01:20,610 --> 00:01:23,253 -我们数据集的元素到补丁中。 -the elements of our dataset into patches. +我们数据集的元素到批次数据中。 +the elements of our dataset into batches. 38 00:01:24,450 --> 00:01:27,960 @@ -190,17 +190,17 @@ We use our DataColletorForPadding as a collate function, 39 00:01:27,960 --> 00:01:29,460 -并洗牌训练集 +并打乱训练集的次序 and shuffle the training set 40 00:01:29,460 --> 00:01:31,080 -确保我们不会检查样品 +确保我们不会每个纪元 to make sure we don't go over the samples 41 00:01:31,080 --> 00:01:33,870 -在一个时代 * 以相同的顺序。 +以相同的顺序遍历样本。 in the same order at a epoch*. 42 @@ -216,16 +216,16 @@ we try to grab a batch of data, and inspect it. 44 00:01:40,080 --> 00:01:43,050 就像我们的数据集元素一样,它是一个字典, -Like our data set elements, it's a dictionary, +Like our dataset elements, it's a dictionary, 45 00:01:43,050 --> 00:01:46,260 -但这些时候值不是一个整数列表 -but these times the values are not a single list of integers +但这里的值不是一个整数列表 +but this times the values are not a single list of integers 46 00:01:46,260 --> 00:01:49,053 -但是按序列长度形状批量大小的张量。 +而是形状为批量大小乘以序列长度的张量。 but a tensor of shape batch size by sequence length. 47 @@ -240,7 +240,7 @@ For that, we'll need to actually create a model. 49 00:01:56,730 --> 00:01:58,740 -如 Model API 视频中所示, +如模型 API 视频中所示, As seen in the Model API video, 50 @@ -250,12 +250,12 @@ we use the from_pretrained method, 51 00:02:00,540 --> 00:02:03,270 -并将标签数量调整为类别数量 +并将标签数量调整为这个数据集 and adjust the number of labels to the number of classes 52 00:02:03,270 --> 00:02:06,810 -我们有这个数据集,这里有两个。 +拥有的类别数量,这里是 2。 we have on this data set, here two. 53 @@ -275,7 +275,7 @@ and check there is no error. 56 00:02:13,320 --> 00:02:14,940 -如果提供标签, +如果提供了标签, If the labels are provided, 57 @@ -290,12 +290,12 @@ always returns a loss directly. 59 00:02:19,525 --> 00:02:21,090 -我们将能够做 loss.backward () +我们将能够做损失的反向传播 We will be able to do loss.backward () 60 00:02:21,090 --> 00:02:22,860 -计算所有梯度, +以计算所有梯度, to compute all the gradients, 61 @@ -325,17 +325,17 @@ Using the previous loss, 66 00:02:36,150 --> 00:02:39,060 -并使用 loss.backward () 计算梯度, +并使用 loss.backward() 计算梯度, and computing the gradients with loss.backward (), 67 00:02:39,060 --> 00:02:41,130 -我们检查我们是否可以执行优化器步骤 +我们检查我们是否可以无误 we check that we can do the optimizer step 68 00:02:41,130 --> 00:02:42,030 -没有任何错误。 +执行优化器步骤。 without any error. 69 @@ -360,7 +360,7 @@ We could already write our training loop, 73 00:02:52,080 --> 00:02:53,220 -但我们还要添加两件事 +但我们还要再做两件事 but we will add two more things 74 @@ -390,7 +390,7 @@ is just a convenience function 79 00:03:06,150 --> 00:03:07,800 -轻松构建这样的调度程序。 +轻松构建这样的调度器。 to easily build such a scheduler. 80 @@ -400,7 +400,7 @@ You can again use 81 00:03:09,683 --> 00:03:11,860 -取而代之的是任何 PyTorch 学习率调度程序。 +取而代之的是任何 PyTorch 学习率调度器。 any PyTorch learning rate scheduler instead. 82 @@ -426,7 +426,7 @@ The first step is to get one, 86 00:03:21,270 --> 00:03:23,283 例如通过使用协作笔记本。 -for instance by using a collab notebook. +for instance by using a colab notebook. 87 00:03:24,180 --> 00:03:26,040 @@ -435,12 +435,12 @@ Then you need to actually send your model, 88 00:03:26,040 --> 00:03:28,923 -并使用火炬设备对其进行训练数据。 +并使用 torch 设备对其进行训练数据。 and training data on it by using a torch device. 89 00:03:29,790 --> 00:03:30,840 -仔细检查以下行 +仔细检查以下代码 Double-check the following lines 90 @@ -560,12 +560,12 @@ then go through all the data in the evaluation data loader. 113 00:04:23,850 --> 00:04:25,530 -正如我们在培训师视频中看到的那样, +正如我们在训练器视频中看到的那样, As we have seen in the Trainer video, 114 00:04:25,530 --> 00:04:26,850 -模型输出 logits, +模型输出逻辑斯蒂, the model outputs logits, 115 @@ -610,7 +610,7 @@ Congratulations, you have now fine-tuned a model 123 00:04:44,490 --> 00:04:45,633 -靠你自己。 +全靠你自己。 all by yourself. 124 diff --git a/subtitles/zh-CN/30_supercharge-your-pytorch-training-loop-with-accelerate.srt b/subtitles/zh-CN/30_supercharge-your-pytorch-training-loop-with-accelerate.srt index 0402b95da..0061b8020 100644 --- a/subtitles/zh-CN/30_supercharge-your-pytorch-training-loop-with-accelerate.srt +++ b/subtitles/zh-CN/30_supercharge-your-pytorch-training-loop-with-accelerate.srt @@ -5,12 +5,12 @@ 2 00:00:05,460 --> 00:00:07,470 -- 使用 Hugging Face Accelerate 增强 +- 增强你的 PyTorch 训练循环 - Supercharge your PyTorch training loop 3 00:00:07,470 --> 00:00:08,943 -你的 PyTorch 训练循环 +使用 Hugging Face Accelerate with Hugging Face Accelerate. 4 @@ -20,12 +20,12 @@ There are multiple setups 5 00:00:12,600 --> 00:00:14,580 -你可以在其上进行培训: +你可以在其上进行训练: on which you can run your training: 6 00:00:14,580 --> 00:00:17,910 -它可能在 CPU、GPU、TPU 上, +它可以在 CPU、GPU、TPU 上, it could be on CPU, GPUs, TPUs, 7 @@ -35,7 +35,7 @@ distributed on one machine with several devices, 8 00:00:20,610 --> 00:00:23,220 -甚至几台机器,通常称为节点, +甚至几台机器,通常称为节点 (node) , or even several machines, often called nodes, 9 @@ -45,7 +45,7 @@ each with multiple devices. 10 00:00:26,340 --> 00:00:28,200 -最重要的是,还有新的调整 +在这之上,还有新的调整 On top of that, there are new tweaks 11 @@ -55,22 +55,22 @@ to make your training faster or more efficient, 12 00:00:30,810 --> 00:00:32,763 -比如混合精度和 DeepSpeed。 +比如混合精度和 DeepSpeed 。 like mixed precision and DeepSpeed. 13 00:00:33,840 --> 00:00:36,600 -每一个设置或训练调整 +这每一个设置或训练调整 Each of those setups or training tweaks 14 00:00:36,600 --> 00:00:38,760 -要求你更改训练循环的代码 +都要求你更改训练循环的代码 requires you to change the code of your training loop 15 00:00:38,760 --> 00:00:41,733 -以某种方式学习新的 API。 +以某种方式, 或者学习新的 API。 in one way or another and to learn a new API. 16 @@ -85,7 +85,7 @@ and there are several third-party libraries that can help. 18 00:00:49,590 --> 00:00:50,760 -那些问题 +它们的问题 The problem with those 19 @@ -95,12 +95,12 @@ is that they can feel like a black box 20 00:00:53,100 --> 00:00:55,320 -并且实施调整可能并不容易 +并且实现调整可能并不容易 and that it might not be easy to implement the tweak 21 00:00:55,320 --> 00:00:56,820 -到你需要的训练循环。 +到你需要的训练循环上。 to the training loop you need. 22 @@ -110,12 +110,12 @@ Accelerate has been designed specifically 23 00:00:59,760 --> 00:01:02,790 -让你保持对训练循环的完全控制 +以让你保持对训练循环的完全控制 to let you retain full control over your training loop 24 00:01:02,790 --> 00:01:04,833 -并尽可能不打扰。 +并尽可能不打乱。 and be as non-intrusive as possible. 25 @@ -135,7 +135,7 @@ Accelerate will handle all the setups 28 00:01:14,730 --> 00:01:17,180 -和第一张幻灯片中提到的培训调整。 +和第一张幻灯片中提到的训练调整。 and training tweaks mentioned on the first slide. 29 @@ -155,7 +155,7 @@ More specifically, you have to import and instantiate 32 00:01:25,980 --> 00:01:27,360 -加速器对象, +一个 accelerator 对象, an accelerator object, 33 @@ -170,7 +170,7 @@ for your specific setup. 35 00:01:31,380 --> 00:01:33,780 -然后你必须把模型发给它, +然后你必须发给它模型 Then you have to send it the model, 36 @@ -190,7 +190,7 @@ Accelerate handles device placement, 39 00:01:42,870 --> 00:01:44,370 -所以你不需要把你的批次 +所以你不需要把你的分批 so you don't need to put your batch 40 @@ -205,7 +205,7 @@ Finally, you have to replace the loss.backward line 42 00:01:50,640 --> 00:01:54,300 -通过 accelerator.backwardloss, +成 accelerator.backwardloss, by accelerator.backwardloss, 43 @@ -260,32 +260,32 @@ to the accelerator.prepare method, like for training. 53 00:02:22,170 --> 00:02:23,430 -然后你可以关闭这条线 +然后你可以关闭这行 Then you can dismiss the line 54 00:02:23,430 --> 00:02:26,160 -将批次放在适当的设备上, +将分批放在适当的设备上, that places the batch on the proper device, 55 00:02:26,160 --> 00:02:27,870 -在通过你的预测之前 +在输入你的预测 and just before passing your predictions 56 00:02:27,870 --> 00:02:31,110 -和指标的标签,使用 accelerator.gather +和指标的标签之前,使用 accelerator.gather and labels to your metric, use accelerator.gather 57 00:02:31,110 --> 00:02:33,300 -收集预测 +来收集预测 to gather together the predictions 58 00:02:33,300 --> 00:02:34,803 -和每个过程的标签。 +和每个进程的标签。 and labels from each process. 59 @@ -295,7 +295,7 @@ A distributed training script 60 00:02:37,890 --> 00:02:41,040 -必须在不同的过程中多次启动, +必须在不同的进程中多次启动, has to be launched several times on different processes, 61 @@ -330,7 +330,7 @@ In a terminal, run accelerate config 67 00:02:57,270 --> 00:02:58,650 -并回答小问卷 +并回答小问题 and answer the small questionnaire 68 @@ -340,7 +340,7 @@ to generate a configuration file 69 00:03:00,330 --> 00:03:02,073 -与所有相关信息, +含所有相关信息, with all the relevant information, 70 @@ -355,7 +355,7 @@ followed by the path to your training script. 72 00:03:08,580 --> 00:03:12,000 -在笔记本中,你可以使用笔记本启动器功能 +在 notebook 中,你可以使用 notebook 启动器函数 In a notebook, you can use the notebook launcher function 73 diff --git a/subtitles/zh-CN/31_navigating-the-model-hub.srt b/subtitles/zh-CN/31_navigating-the-model-hub.srt index 3b9e03f36..a4f1c2a5b 100644 --- a/subtitles/zh-CN/31_navigating-the-model-hub.srt +++ b/subtitles/zh-CN/31_navigating-the-model-hub.srt @@ -5,12 +5,12 @@ 2 00:00:04,050 --> 00:00:05,910 -- [Instructor] 在这段视频中,我们将回顾 +- [Instructor] 在本视频中,我们将回顾 - [Instructor] In this video, we're going to go over 3 00:00:05,910 --> 00:00:08,013 -HuggingFace 模型中心导航。 +HuggingFace Model Hub 导航。 the HuggingFace Model Hub navigation. 4 @@ -20,42 +20,42 @@ This is the huggingface.co landing page. 5 00:00:13,260 --> 00:00:16,020 -要访问模型中心,请单击模型选项卡 -To access the model hub, click on the models tab +要访问 Model Hub,请在右上角 +To access the Model Hub, click on the Models tab 6 00:00:16,020 --> 00:00:17,463 -在右上角。 +单击 Models 选项卡。 in the upper right corner. 7 00:00:18,960 --> 00:00:21,030 -你应该面对这个网络界面, +你会看到这个网页界面, You should be facing this web interface, 8 00:00:21,030 --> 00:00:23,193 -可以分成几个部分。 +它被分为几个部分。 which can be split into several parts. 9 00:00:24,480 --> 00:00:26,790 -在左侧,你会找到类别, +在左侧,你会找到类别 (category), On the left, you'll find categories, 10 00:00:26,790 --> 00:00:29,090 -你可以使用它来定制你的模型搜索。 +你可以使用它来自定义模型搜索。 which you can use to tailor your model search. 11 00:00:29,970 --> 00:00:32,970 -第一类是任务。 +第一类是任务 (tasks)。 The first category is the tasks. 12 00:00:32,970 --> 00:00:36,660 -集线器上的模型可用于各种各样的任务。 +hub 上的模型可用于各种各样的任务。 Models on the hub may be used for a wide variety of tasks. 13 @@ -90,12 +90,12 @@ or automatic speech recognition for speech. 19 00:00:54,840 --> 00:00:57,870 -第二类是图书馆。 +第二类是库 (Library)。 The second category is the libraries. 20 00:00:57,870 --> 00:01:00,990 -集线器上的模型通常共享三个主干之一, +hub 上的模型通常共享三个主要框架之一, Models on the hub usually share one of three backbones, 21 @@ -110,7 +110,7 @@ However, other backbones, such as rust or ONNX also exist. 23 00:01:09,540 --> 00:01:11,850 -最后,这个选项卡也可以使用 +最后,这个选项卡也可以 Finally, this tab can also be used 24 @@ -120,13 +120,13 @@ to specify from which high-level framework the models comes. 25 00:01:16,140 --> 00:01:19,260 -这包括变形金刚,但不限于此。 +这包括 Transformers ,但也不仅仅是这些。 This includes Transformers, but it isn't limited to it. 26 00:01:19,260 --> 00:01:21,060 -模型集线器用于托管 -The model hub is used to host +Model Hub 用于托管 +The Model Hub is used to host 27 00:01:21,060 --> 00:01:22,920 @@ -135,32 +135,32 @@ a lot of different frameworks models, 28 00:01:22,920 --> 00:01:24,600 -我们正在积极寻求举办 +我们正在积极寻求 and we're actively looking to host 29 00:01:24,600 --> 00:01:25,893 -其他框架模型。 +支持其他框架模型。 other frameworks models. 30 00:01:28,530 --> 00:01:31,890 -第三类是数据集选项卡。 +第三类是数据集 (Datasets) 选项卡。 The third category is the datasets tab. 31 00:01:31,890 --> 00:01:35,070 -从此选项卡中选择数据集意味着过滤模型 +从此选项卡中选择数据集 Selecting a dataset from this tab means filtering the models 32 00:01:35,070 --> 00:01:37,683 -这样他们就可以在该特定数据集上接受培训。 +意味筛选在特定数据集上训练的模型。 so that they were trained on that specific dataset. 33 00:01:39,180 --> 00:01:42,300 -第四类是语言选项卡。 +第四类是语言 (Language) 选项卡。 The fourth category is the languages tab. 34 @@ -170,33 +170,33 @@ Selecting a language from this tab 35 00:01:43,800 --> 00:01:45,990 -意味着过滤模型以便它们处理 +意味着筛选模型以便它们适配 means filtering the models so that they handle 36 00:01:45,990 --> 00:01:47,090 -选择的语言。 +所选的语言。 the language selected. 37 00:01:48,600 --> 00:01:51,750 -最后,最后一个类别允许选择许可证 +最后,最后一个类别允许选择模型之间 Finally, the last category allows to choose the license 38 00:01:51,750 --> 00:01:53,313 -与之共享模型。 +共享的许可证 (license)。 with which the model is shared. 39 00:01:56,700 --> 00:01:58,770 -在右侧,你会找到可用的型号 +在右侧,你可以在 Model Hub  On the right, you'll find the models available 40 00:01:58,770 --> 00:02:00,480 -在模型中心。 -on the model hub. +找到可用的模型。 +on the Model Hub. 41 00:02:00,480 --> 00:02:03,750 @@ -210,7 +210,7 @@ When clicking on a model, 43 00:02:04,890 --> 00:02:07,230 -你应该面对它的模型卡。 +你可以看到它的模型卡。 you should be facing its model card. 44 @@ -220,12 +220,12 @@ The model card contains information about the model, 45 00:02:09,990 --> 00:02:13,263 -它的描述、预期用途、限制和偏见。 +它的描述、预期用途、限制和偏差。 its description, intended use, limitations and biases. 46 00:02:14,310 --> 00:02:17,580 -它还可以显示有关如何使用模型的代码片段, +它还可以展示与如何使用模型相关的代码片段, It can also show code snippets on how to use the model, 47 @@ -235,7 +235,7 @@ as well as any relevant information; 48 00:02:20,070 --> 00:02:22,080 -培训程序,数据处理, +训练过程,数据处理, training procedure, data processing, 49 @@ -255,22 +255,22 @@ The better crafted a model card is, 52 00:02:30,360 --> 00:02:33,270 -其他用户越容易利用你的模型 +越方便其他用户在他们的应用程序中 the easier it will be for other users to leverage your model 53 00:02:33,270 --> 00:02:34,443 -在他们的应用程序中。 +利用你的模型。 in their applications. 54 00:02:35,820 --> 00:02:38,553 -模型卡右侧是推理 API。 +模型卡右侧是 inference API。 On the right of the model card is the inference API. 55 00:02:39,540 --> 00:02:41,040 -可以使用此推理 API +可以使用此 interence API This inference API can be used 56 @@ -280,7 +280,7 @@ to play with the model directly. 57 00:02:43,290 --> 00:02:45,690 -随意修改文字,点击计算 +随意修改文字,点击 compute Feel free to modify the text and click on compute 58 @@ -305,12 +305,12 @@ that is relevant to the categories we have just seen. 62 00:03:01,320 --> 00:03:04,410 -文件和版本选项卡显示体系结构 -The files & versions tab displays the architecture +Files and versions 选项卡显示 +The Files and versions tab displays the architecture 63 00:03:04,410 --> 00:03:06,213 -该模型的存储库。 +模型仓库的架构。 of the repository of that model. 64 @@ -320,42 +320,42 @@ Here, we can see all the files that define this model. 65 00:03:10,920 --> 00:03:13,650 -你将看到 Git 存储库的所有常用功能: +你将看到 Git repository 的所有常用功能: You'll see all usual features of a Git repository: 66 00:03:13,650 --> 00:03:15,093 -可用的分支机构, +可用的 branch, the branches available, 67 00:03:17,160 --> 00:03:18,520 -提交历史 +commit 历史 the commit history 68 00:03:20,760 --> 00:03:22,683 -以及提交差异。 +以及 commit diff。 as well as the commit diff. 69 00:03:25,740 --> 00:03:27,510 -三个不同的按钮可用 +在模型卡的顶部 Three different buttons are available 70 00:03:27,510 --> 00:03:29,760 -在模型卡的顶部。 +有三个不同的按钮可用。 at the top of the model card. 71 00:03:29,760 --> 00:03:31,170 -第一个显示如何使用 +第一个显示如何在代码中 The first one shows how to use 72 00:03:31,170 --> 00:03:33,093 -以编程方式推理 API。 +使用 inference API。 the inference API programmatically. 73 @@ -365,12 +365,12 @@ The second one shows how to train this model in SageMaker. 74 00:03:42,870 --> 00:03:44,820 -最后一个显示了如何加载该模型 +最后一个显示了如何在适当的库中 And the last one shows how to load that model 75 00:03:44,820 --> 00:03:46,860 -在适当的库中。 +加载该模型。 within the appropriate library. 76 diff --git a/subtitles/zh-CN/32_managing-a-repo-on-the-model-hub.srt b/subtitles/zh-CN/32_managing-a-repo-on-the-model-hub.srt index e59ddd96f..e0997dfa7 100644 --- a/subtitles/zh-CN/32_managing-a-repo-on-the-model-hub.srt +++ b/subtitles/zh-CN/32_managing-a-repo-on-the-model-hub.srt @@ -5,7 +5,7 @@ 2 00:00:06,210 --> 00:00:08,280 -管理模型存储库 +管理模型仓库 to manage a model repository 3 @@ -15,7 +15,7 @@ on the Hugging Face Hub Model Hub. 4 00:00:10,920 --> 00:00:13,020 -为了处理存储库 +为了处理仓库 In order to handle a repository 5 @@ -25,32 +25,32 @@ you should first have a Hugging Face account. 6 00:00:15,450 --> 00:00:17,610 -创建新帐户的链接可用 +在描述中有创建新帐户 A link to create a new account is available 7 00:00:17,610 --> 00:00:18,573 -在说明中。 +的链接。 in the description. 8 00:00:20,130 --> 00:00:22,980 -登录后,你可以创建一个新的存储库 +登录后,你可以创建一个新的仓库 Once you are logged in, you can create a new repository 9 00:00:22,980 --> 00:00:25,890 -通过单击新模型选项。 -by clicking on the new model option. +通过单击 New Model 选项。 +by clicking on the New Model option. 10 00:00:25,890 --> 00:00:29,400 -你应该面对与以下类似的模式。 -You should be facing a similar modal to the following. +你会看到类似下面的模型。 +You should be facing a similar model to the following. 11 00:00:29,400 --> 00:00:33,240 -在所有者输入中,你可以放置自己的命名空间 +在 Owner 输入框中,你可以放置自己的命名空间 In the owner input, you can put either your own namespace 12 @@ -60,12 +60,12 @@ or any of your organization's namespaces. 13 00:00:36,660 --> 00:00:39,330 -模型名称是模型标识符 -The model name is the model identifier +Model name 是模型标识符 +The Model name is the model identifier 14 00:00:39,330 --> 00:00:40,320 -然后将被使用 +它将被用于 that will then be used 15 @@ -75,7 +75,7 @@ to identify your model on the chosen namespace. 16 00:00:44,130 --> 00:00:47,700 -最后的选择是在公共和私人之间。 +最后可以在 Public(公共) 和 Private(私有) 之间选择。 The final choice is between public and private. 17 @@ -95,12 +95,12 @@ as this makes your model easily accessible and shareable. 20 00:00:54,960 --> 00:00:57,630 -你的命名空间的所有者是唯一的 +你的命名空间的所有者 The owners of your namespace are the only ones 21 00:00:57,630 --> 00:00:59,523 -谁可以更新和更改你的模型。 +是唯一可以更新和更改你的模型。 who can update and change your model. 22 @@ -120,7 +120,7 @@ only the owners of your namespace 25 00:01:06,000 --> 00:01:08,280 -将对你的模型有可见性。 +对你的模型有可见性。 will have visibility over your model. 26 @@ -135,7 +135,7 @@ and will not be able to use it. 28 00:01:15,030 --> 00:01:17,030 -让我们创建一个虚拟模型来玩。 +让我们创建一个虚拟模型来试试看。 Let's create a dummy model to play with. 29 @@ -155,17 +155,17 @@ Three tabs are available to you. 32 00:01:24,360 --> 00:01:27,960 -你面对的是第一个,这是模型卡页面。 -You're facing the first one, which is the model card page. +你面对的是第一个,这是 Model card 页面。 +You're facing the first one, which is the Model card page. 33 00:01:27,960 --> 00:01:29,970 -这是你用来展示模型的页面 +这是你用来向全世界展示模型 This is the page you use to showcase your model 34 00:01:29,970 --> 00:01:31,110 -致全世界。 +的页面。 to the world. 35 @@ -175,17 +175,17 @@ We'll see how it can be completed in a bit. 36 00:01:34,500 --> 00:01:37,503 -第二个是文件和版本选项卡。 -The second one is the files and versions tab. +第二个是 Files and Versions 选项卡。 +The second one is the Files and Versions tab. 37 00:01:38,340 --> 00:01:40,920 -你的模型本身就是一个 Git 存储库。 +你的模型本身就是一个 Git 仓库。 Your model itself is a Git repository. 38 00:01:40,920 --> 00:01:43,230 -如果你不知道什么是 Git 存储库, +如果你不知道什么是 Git 仓库, If you're unaware of what is a Git repository, 39 @@ -200,22 +200,22 @@ If you have never used Git before, 41 00:01:48,120 --> 00:01:50,100 -我们建议看介绍 +我们建议观看视频描述中 we recommend looking at an introduction 42 00:01:50,100 --> 00:01:52,600 -就像本视频描述中提供的那样。 +提供的介绍内容。 like the one provided in this video's description. 43 00:01:53,850 --> 00:01:56,910 -Git 存储库允许你查看发生的更改 +Git 仓库支持按照时间推移 The Git repository allows you to see the changes happening 44 00:01:56,910 --> 00:02:00,900 -随着时间的推移在此文件夹中,因此术语版本。 +查看本文件夹中的变化,也就是版本。 over time in this folder, hence the term versions. 45 @@ -225,12 +225,12 @@ We'll see how to add files and versions in a bit. 46 00:02:07,020 --> 00:02:09,570 -最后一个选项卡是设置选项卡, +最后一个选项卡是 Settings 选项卡, The final tab is the settings tab, 47 00:02:09,570 --> 00:02:12,120 -这使你可以管理模型的可见性 +可以管理模型的可见性 which allows you to manage your model's visibility 48 @@ -240,27 +240,27 @@ and availability. 49 00:02:14,790 --> 00:02:17,673 -让我们首先从将文件添加到存储库开始。 +让我们首先从将文件添加到仓库开始。 Let's first start by adding files to the repository. 50 00:02:18,540 --> 00:02:19,560 -可以添加文件 +还好有 add file 按钮 Files can be added 51 00:02:19,560 --> 00:02:23,340 -多亏了添加文件按钮,通过网络界面。 +通过网页操作即可添加文件。 through the web interface thanks to the add file button. 52 00:02:23,340 --> 00:02:27,060 -添加的文件可以是任何类型,python,JSON,文本, +添加的文件可以是任何类型,python,JSON,纯文本, The added files can be of any type, python, JSON, text, 53 00:02:27,060 --> 00:02:27,893 -你的名字。 +任君选择。 you name it. 54 @@ -270,32 +270,32 @@ Alongside your added file and its content, 55 00:02:31,170 --> 00:02:33,363 -你应该命名你的更改或提交。 +你还应该命名你的 change 或 commit。 you should name your change or commit. 56 00:02:36,330 --> 00:02:38,400 -一般添加文件比较简单 +通常,使用 Hugging Face Hub Python 库 Generally, adding files is simpler 57 00:02:38,400 --> 00:02:40,770 -通过使用 Hugging Face Hub Python 库 +或使用命令行添加文件 by using the Hugging Face Hub Python library 58 00:02:40,770 --> 00:02:43,050 -或使用命令行。 +比较简单。 or by using the command-line. 59 00:02:43,050 --> 00:02:44,310 -我们将展示如何做到这一点 +我们将展示如何使用 We'll showcase how to do this 60 00:02:44,310 --> 00:02:46,290 -使用 Hugging Face Hub Python 库, +Hugging Face Hub Python 库做到这一点 using the Hugging Face Hub Python library, 61 @@ -305,7 +305,7 @@ and there is a link in the description 62 00:02:48,060 --> 00:02:49,800 -到这个视频的前一个版本, +可以指向这个视频的前一个版本, to the previous version of this video, 63 @@ -325,7 +325,7 @@ into your Hugging Face account, 66 00:02:56,460 --> 00:02:59,523 -通过命令行或在 Python 运行时中。 +可以通过命令行或者 Python 运行时中操作。 either through the command-line or in a Python runtime. 67 @@ -335,7 +335,7 @@ The first approach we'll take a look at 68 00:03:06,390 --> 00:03:08,880 -正在使用上传文件的方法。 +正在使用 upload_file 方法。 is using the upload file method. 69 @@ -345,12 +345,12 @@ This offers an extremely simple API 70 00:03:10,770 --> 00:03:12,630 -通过集线器上传文件。 +通过 hub 上传文件。 to upload files through the hub. 71 00:03:12,630 --> 00:03:14,190 -三个必需的参数 +其中三个必需的参数 The three required parameters 72 @@ -360,13 +360,13 @@ are the current location of the file, 73 00:03:18,570 --> 00:03:21,300 -该文件在存储库中的路径, +该文件在仓库中的路径, the path of that file in the repository, 74 00:03:21,300 --> 00:03:24,050 -以及你要推送到的存储库的想法。 -and the idea of the repository to which you're pushing. +以及你要推送到的仓库的标识符。 +and the id of the repository to which you're pushing. 75 00:03:25,650 --> 00:03:27,930 @@ -375,37 +375,37 @@ There are a few additional parameters. 76 00:03:27,930 --> 00:03:29,100 -令牌参数, +token 参数, The token parameter, 77 00:03:29,100 --> 00:03:31,200 -如果你想指定一个不同的令牌 +如果你想指定一个和登录时 if you would like to specify a different token 78 00:03:31,200 --> 00:03:33,650 -比登录时保存在缓存中的那个, +所保存的不同的 token, than the one saved in your cache with your login, 79 00:03:34,830 --> 00:03:36,750 -回购类型参数, +repo_type 参数, the repo type parameter, 80 00:03:36,750 --> 00:03:40,503 -如果你想推送到数据集或空间。 -if you would like to push to a data set or a space. +如果你想推送到 dataset 或 space。 +if you would like to push to a dataset or a space. 81 00:03:42,300 --> 00:03:45,690 -我们将上传一个名为 readme.md 的文件到存储库 +我们将使用这种方法上传一个名为 readme.md 的文件 We'll upload a file called readme.md to the repository 82 00:03:45,690 --> 00:03:47,190 -使用这种方法。 +到仓库。 using this method. 83 @@ -415,12 +415,12 @@ We first start by saving a file with that name, 84 00:03:49,710 --> 00:03:51,210 -其中包含一些信息 +其中包含一些关于 which contains some information 85 00:03:51,210 --> 00:03:52,920 -关于存储库本身。 +仓库本身的信息。 about the repository itself. 86 @@ -435,7 +435,7 @@ Now that the file is saved, 88 00:03:57,420 --> 00:04:00,513 -让我们使用上传文件方法将其上传到集线器。 +让我们使用 upload_file 方法将其上传到 hub。 let's use the upload file method to upload it to the hub. 89 @@ -455,12 +455,12 @@ The file upload was a success. 92 00:04:10,170 --> 00:04:13,500 -除了这个方法之外还有一个删除文件的方法 +除了这个方法之外还有一个 delete_file 方法 Alongside this method exists a delete file method 93 00:04:13,500 --> 00:04:16,170 -这样你就可以完全管理你的存储库。 +这样你就可以完全管理你的仓库。 so that you may manage your repository fully. 94 @@ -480,7 +480,7 @@ the file was indeed deleted. 97 00:04:29,070 --> 00:04:32,730 -这种只使用这两种方法的方法非常简单。 +这两种方法操作起来非常简单。 This approach using only these two methods is super simple. 98 @@ -515,12 +515,12 @@ let's take a look at the second method 104 00:04:45,540 --> 00:04:47,643 -这是存储库实用程序。 +这是仓库实用程序。 which is the repository utility. 105 00:04:48,600 --> 00:04:51,840 -此类是 Git 和 Git LFS 方法的包装器, +该类封装了 Git 和 Git LFS 方法, This class is a wrapper over Git and Git LFS methods, 106 @@ -535,7 +535,7 @@ and offers a flexible API 108 00:04:55,500 --> 00:04:57,990 -管理你的在线存储库。 +管理你的在线仓库。 to manage your online repositories. 109 @@ -545,22 +545,22 @@ Let's take a look at how it works. 110 00:05:03,870 --> 00:05:08,369 -我们首先从实例化存储库实用程序开始。 +我们首先从实例化仓库实用程序开始。 We first start by instantiating the repository utility. 111 00:05:08,369 --> 00:05:10,380 -我们从参数中提供克隆, +为了克隆我们刚刚创建的仓库 We provide the clone from parameter, 112 00:05:10,380 --> 00:05:13,383 -为了克隆我们刚刚创建的存储库。 +我们可以通过传递参数进行克隆。 in order to clone the repository we just created. 113 00:05:14,400 --> 00:05:18,750 -存储库现已克隆到本地文件夹中。 +仓库现已克隆到本地文件夹中。 The repository is now cloned in the local folder. 114 @@ -575,7 +575,7 @@ offers quite a few methods which are useful for us. 116 00:05:25,920 --> 00:05:28,800 -我们有兴趣将模型推送到中心。 +我们有兴趣将模型推送到 hub。 We're interested in pushing a model to the hub. 117 @@ -585,7 +585,7 @@ I'll start by loading a model and tokenizer 118 00:05:31,170 --> 00:05:32,643 -我几个小时前训练过。 +这是几个小时前训练过的。 I trained a few hours ago. 119 @@ -595,42 +595,42 @@ We'll now follow the traditional Git approach 120 00:05:36,810 --> 00:05:38,670 -首先提取最新的更改 +首先 pull 最新的更改内容 by first pulling latest changes 121 00:05:38,670 --> 00:05:40,053 -使用 Git pull 方法。 -using the Git pull method. +使用 git_pull 方法。 +using the git_pull method. 122 00:05:40,980 --> 00:05:43,170 -我们刚刚克隆了存储库, +我们刚刚克隆了仓库, We just cloned the repository, 123 00:05:43,170 --> 00:05:45,780 -所以除非这是一个超级活跃的存储库, +所以除非这是一个超级活跃的仓库, so unless this is a super active repository, 124 00:05:45,780 --> 00:05:48,660 -不太可能有新的变化可用。 +否则不太可能内容的变化。 it's unlikely that new changes are available. 125 00:05:48,660 --> 00:05:51,000 -但拉取最新的更改总是一个好主意 +但在做任何新的事情之前养成 pull 最新内容 But it's always a good idea to pull the latest changes 126 00:05:51,000 --> 00:05:52,300 -在做任何新的事情之前。 +的好习惯也是不错的。 before doing anything new. 127 00:05:53,220 --> 00:05:55,200 -现在我们已经拉取了存储库, +现在我们已经 pull 了仓库, Now that we have pulled the repository, 128 @@ -670,53 +670,53 @@ If we were using the command-line, 135 00:06:12,150 --> 00:06:14,250 -有一些 Git LFS 特定的命令 +我们将不得不调用一些 there are a few Git LFS specific commands 136 00:06:14,250 --> 00:06:15,600 -我们将不得不调用。 +特定的 Git LFS 命令。 we would have to invoke. 137 00:06:15,600 --> 00:06:17,940 -但是在这里,Hugging Face 枢纽包 +但是在这里,huggingface_hub 包 But here, the Hugging Face hub package 138 00:06:17,940 --> 00:06:20,070 -负责所有这些。 +会处理所有这些。 takes care of all of that. 139 00:06:20,070 --> 00:06:24,420 -我们将从使用 Git add 方法暂存文件开始。 -We'll start by staging the files using the Git add method. +我们将从使用 git_add 方法暂存文件开始。 +We'll start by staging the files using the git_add method. 140 00:06:24,420 --> 00:06:27,600 -然后我们将使用 Git commit 方法提交这些更改, +然后我们将使用 git_commit 方法提交这些更改, We'll then commit these changes using Git commit method, 141 00:06:27,600 --> 00:06:30,690 -并提供有用的提交信息。 +并提供有用的 commit 信息。 and providing a helpful commit message. 142 00:06:30,690 --> 00:06:33,210 -最后,我们将更改推送到远程, +最后,我们将更改推送到远端, Finally, we'll push the changes to the remote, 143 00:06:33,210 --> 00:06:34,953 -使用 Git 推送方法。 +使用 git_push 方法。 using the Git push method. 144 00:06:45,090 --> 00:06:47,430 -如果我们回到文件和版本选项卡, -If we go back to the files and versions tab, +如果我们回到 Files and Versions 选项卡, +If we go back to the Files and Versions tab, 145 00:06:47,430 --> 00:06:49,950 @@ -725,7 +725,7 @@ we can now see the newly committed files. 146 00:06:49,950 --> 00:06:52,600 -我们甚至可以在推理 API 中使用模型。 +我们甚至可以在 inference API 中使用模型。 We can even play with the model in the inference API. 147 @@ -735,7 +735,7 @@ Unfortunately, the front page of our model 148 00:06:55,770 --> 00:06:57,540 -还是很空虚。 +还是显得非常空。 is still very empty. 149 @@ -745,13 +745,13 @@ Let's add a README markdown file 150 00:06:59,280 --> 00:07:00,753 -完成它一点点。 +让整体显得完整一点点。 to complete it a little bit. 151 00:07:01,710 --> 00:07:04,200 -这个 README 被称为模型卡 -This README is known as the model card +这个 README 被称为 Model card +This README is known as the Model card 152 00:07:04,200 --> 00:07:06,030 @@ -760,12 +760,12 @@ and it's arguably as important 153 00:07:06,030 --> 00:07:09,330 -作为模型存储库中的模型和标记器文件。 +作为模型仓库中的模型和分词器文件。 as the model and tokenizer files in the model repository. 154 00:07:09,330 --> 00:07:11,280 -这是中心定义 +这是你的模型的综合定义 It is the central definition 155 @@ -795,12 +795,12 @@ may build their artifacts. 160 00:07:23,220 --> 00:07:25,590 -我们只会在此处添加标题和简短描述 +为了简单起见我们只会在此处添加标题 We'll only add a title and a small description here 161 00:07:25,590 --> 00:07:27,060 -为了简单起见, +和简短描述, for simplicity's sake, 162 @@ -810,7 +810,7 @@ but we encourage you to add information relevant 163 00:07:29,370 --> 00:07:30,990 -模型是如何训练的, +说明模型是如何训练的, to how was the model trained, 164 @@ -820,7 +820,7 @@ it's intended use and limitations, 165 00:07:33,120 --> 00:07:36,180 -以及它识别出的潜在偏见, +以及目前一直的潜在偏差, as well as it's identified potential biases, 166 @@ -835,7 +835,7 @@ and code samples on how to use your model. 168 00:07:41,460 --> 00:07:44,130 -为模型中心贡献模型的出色工作。 +为 Model Hub 贡献出色的模型。 Great work contributing a model to the Model Hub. 169 diff --git a/subtitles/zh-CN/33_the-push-to-hub-api-(pytorch).srt b/subtitles/zh-CN/33_the-push-to-hub-api-(pytorch).srt index e27897b99..475489f28 100644 --- a/subtitles/zh-CN/33_the-push-to-hub-api-(pytorch).srt +++ b/subtitles/zh-CN/33_the-push-to-hub-api-(pytorch).srt @@ -15,13 +15,13 @@ 4 00:00:05,130 --> 00:00:06,830 -- [Instructor] 所以推送到 hub API。 -- [Instructor] So push to hub API. +- [Instructor] push_to_hub API。 +- [Instructor] So push_to_hub API. 5 00:00:08,310 --> 00:00:10,533 -让我们看一下推送到集线器 API。 -Let's have a look at the push to hub API. +让我们看一下 push_to_hub API。 +Let's have a look at the push_to_hub API. 6 00:00:11,730 --> 00:00:14,640 @@ -30,12 +30,12 @@ You will need to be logged in with your Hugging Face account 7 00:00:14,640 --> 00:00:17,400 -你可以通过执行第一个单元格来做到这一点, +你可以通过执行第一个单元格里的操作来登录, which you can do by executing this first cell, 8 00:00:17,400 --> 00:00:21,123 -或者在终端中输入 huggingface-cli login。 +或者在 terminal 中输入 huggingface-cli login。 or by typing huggingface-cli login in a terminal. 9 @@ -45,37 +45,37 @@ Just enter you username and password, then click login, 10 00:00:26,640 --> 00:00:28,620 -这将存储一个通知令牌 -this will store a notification token +登录后将存储一个授权 token +this will store a authentication token 11 00:00:28,620 --> 00:00:30,670 -在你正在使用的机器的缓存中。 +到你正在使用的机器的缓存中。 in the cache of the machine you're using. 12 00:00:31,890 --> 00:00:35,790 -现在,让我们对 BERT 模型进行微调 +现在,让我们基于 GLUE COLA 数据集 Now, let's launch a fine tuning of a BERT model 13 00:00:35,790 --> 00:00:37,920 -在 GLUE COLA 数据集上。 +对 BERT 模型进行微调。 on the GLUE COLA dataset. 14 00:00:37,920 --> 00:00:39,600 -我们不会讨论微调代码 +我们不会深入探讨微调代码 We won't go over the fine tuning code 15 00:00:39,600 --> 00:00:42,270 -因为你可以在任何变压器教程中找到它, +你可以在任何 transformer 教程 because you can find it in any transformer tutorial, 16 00:00:42,270 --> 00:00:44,670 -或通过查看下面的视频链接。 +或查看下面的视频链接找到相关参考。 or by looking at the videos link below. 17 @@ -85,7 +85,7 @@ What interests us here is 18 00:00:46,470 --> 00:00:48,970 -我们如何在训练期间利用模型中心。 +如何在训练期间利用 model hub。 how we can leverage the model hub during training. 19 @@ -95,72 +95,72 @@ This is done with the "push_to_hub=true" argument 20 00:00:52,980 --> 00:00:55,530 -传入你的 TrainingArguments。 +将该参数添加到你的 TrainingArguments。 passed in your TrainingArguments. 21 00:00:55,530 --> 00:00:57,240 -这将自动上传你的模型 +每次保存时将自动上传 This will automatically upload your model 22 00:00:57,240 --> 00:00:59,400 -每次保存到集线器, +你的模型到 Hub,在我们的示例中, to the Hub each time it is saved, 23 00:00:59,400 --> 00:01:01,323 -所以我们案例中的每个时代。 +每个 epoch 都会如此操作。 so every epoch in our case. 24 00:01:02,280 --> 00:01:04,860 -这允许你从不同的机器恢复训练 +如果当前的被打断 This allows you to resume training from a different machine 25 00:01:04,860 --> 00:01:06,873 -如果当前的被打断。 +这允许你从不同的机器恢复训练之前的训练。 if the current one gets interrupted. 26 00:01:08,220 --> 00:01:10,440 -该模型将在你的名称空间中更新 -The model will be updated in your name space +该模型将使用 +The model will be updated in your namespace 27 00:01:10,440 --> 00:01:14,640 -使用你默认选择的输出目录的名称。 +你默认选择的输出目录的名称在你的 namespace 中更新。 with the name of the output directory you picked by default. 28 00:01:14,640 --> 00:01:16,020 -你可以选择其他名称 +你可以通过将其 You can choose another name 29 00:01:16,020 --> 00:01:19,113 -通过将其传递给 hub_model_id 参数。 +传递给 hub_model_id 参数选择其他名称。 by passing it to the hub_model_id argument. 30 00:01:20,070 --> 00:01:23,370 -你还可以推动你所属的组织内部 +你还可以通过传递完整的仓库名称 You can also push inside an organization you are a member of 31 00:01:23,370 --> 00:01:25,740 -通过传递完整的存储库名称, +将模型 push 到你所属的组织内部, by passing a full repository name, 32 00:01:25,740 --> 00:01:28,933 -以组织名称 /, +使用 organization/ 的形式, with the name of the organization/, 33 00:01:28,933 --> 00:01:30,433 -你要选择的型号 ID。 +再加上你所选的 model ID。 the model ID you want to pick. 34 @@ -175,38 +175,38 @@ and wait a little bit. 36 00:01:36,960 --> 00:01:39,033 -我会从视频中缩短等待时间。 +视频中会跳过等待的过程。 I'll cut the waiting time from the video. 37 00:01:43,260 --> 00:01:46,350 -请注意,模型是异步推送的, +请注意,模型是异步 push 的, Note that the model is pushed asynchronously, 38 00:01:46,350 --> 00:01:47,730 -意味着训练继续 +意味着当你的模型上传到 hub 时, meaning that the training continues 39 00:01:47,730 --> 00:01:49,730 -当你的模型上传到集线器时。 +训练将继续进行。 while your model is uploaded to the hub. 40 00:01:51,060 --> 00:01:52,950 -当你的第一次提交完成时, +当你的第一次 commit 完成时, When your first commit is finished, 41 00:01:52,950 --> 00:01:55,650 -你可以去中心检查你的模型 +你可以通过查看你的 namespace you can go inspect your model on the Hub 42 00:01:55,650 --> 00:01:57,960 -通过查看你的名称空间, -by looking inside your name space, +去 Hub 检查你的模型, +by looking inside your namespace, 43 00:01:57,960 --> 00:01:59,943 @@ -215,22 +215,22 @@ and you'll find it at the very top. 44 00:02:01,980 --> 00:02:04,200 -你甚至可以开始使用它的推理小部件 +你甚至可以在继续训练的同时 You can even start playing with its inference widget 45 00:02:04,200 --> 00:02:06,630 -在继续训练的同时。 +开始使用它的 inference 小部件。 while it's continuing the training. 46 00:02:06,630 --> 00:02:09,270 Cola 数据集让模型确定 -The Cola data set tasks the model with determining +The Cola dataset tasks the model with determining 47 00:02:09,270 --> 00:02:11,970 -如果句子在语法上是正确的。 +句子在语法上是否是正确的。 if the sentence is grammatically correct on that. 48 @@ -240,102 +240,102 @@ So we pick an example of incorrect sentence to test it. 49 00:02:15,510 --> 00:02:16,950 -请注意,这需要一些时间 +请注意,第一次尝试使用它时, Note that it'll take a bit of time 50 00:02:16,950 --> 00:02:18,750 -在推理 API 中加载模型, +这需要一些时间才能在 inference API 中 to load your model inside the inference APIs, 51 00:02:18,750 --> 00:02:20,880 -所以第一次尝试使用它。 +完成模型加载。 so first time you try to use it. 52 00:02:20,880 --> 00:02:23,280 -我们将按时间从视频中删减。 +我们将根据时间从视频中删掉。 We'll cut by time from the video. 53 00:02:23,280 --> 00:02:24,870 -标签有问题, +标签有点问题, There is something wrong with the labels, 54 00:02:24,870 --> 00:02:27,360 -但我们稍后会在本视频中修复它。 +我们稍后会在本视频中修复它。 but we'll fix it later in this video. 55 00:02:27,360 --> 00:02:29,520 -一旦你的训练结束, +一旦你的训练完成, Once your training is finished, 56 00:02:29,520 --> 00:02:31,770 -你应该和教练一起做最后一击 +你应该使用 trainer.push_to_hub 方法 you should do one last push with the trainer 57 00:02:31,770 --> 00:02:33,840 -推到一个方法。 +最后再提交一次。 that pushed to a method. 58 00:02:33,840 --> 00:02:35,430 -这是有两个原因的。 +这其中有两个原因。 This is for two reason. 59 00:02:35,430 --> 00:02:36,750 -首先,这将确保 +首先,若你尚未完成 First, this will make sure 60 00:02:36,750 --> 00:02:39,180 -你正在预测模型的最终版本 +这将确保你正在预测模型的 you are predicting the final version of your model 61 00:02:39,180 --> 00:02:40,680 -如果你还没有。 +最终版本。 if you didn't already. 62 00:02:40,680 --> 00:02:42,480 -例如,如果你曾经保存 +例如,如果你曾经是在每一步保存 For instance, if you used to save 63 00:02:42,480 --> 00:02:46,980 -每一步策略而不是每秒, +而不是每秒保存, every in step strategy instead of every second, 64 00:02:46,980 --> 00:02:48,180 -这将起草一张模型卡 +这将创建一个 model card this will draft a model card 65 00:02:48,180 --> 00:02:51,120 -那将是你的模型回购的登陆页面。 +那将是你的 model repo 的最初始页面。 that will be the landing page of your model repo. 66 00:02:51,120 --> 00:02:52,260 -提交完成后, +commit 完成后, Once the commit is done, 67 00:02:52,260 --> 00:02:54,810 -让我们回到我们的模型页面并刷新。 +让我们回到我们的 model 页面并刷新。 let's go back on our model page and refresh. 68 00:02:54,810 --> 00:02:56,820 -我们可以看到制图者模型卡 +我们可以看到 model card 的草稿 We can see the drafters model card 69 @@ -345,17 +345,17 @@ which includes information, 70 00:02:58,080 --> 00:03:00,381 -我们发现调整了哪一种模型。 +以及哪个模型被调整过。 and which one model we find tuned. 71 00:03:00,381 --> 00:03:03,570 -所以最终评估损失和指标, +接下来是最终的评估 loss 和 metric, So final evaluation loss and metric, 72 00:03:03,570 --> 00:03:06,300 -使用的训练超参数, +使用过的训练超参数, the training hyperparameter used, 73 @@ -380,42 +380,42 @@ On top of all that information, 77 00:03:16,860 --> 00:03:19,740 -培训师还包括一些解释的元数据 +trainer 还包括一些 metadata,它可以 the trainer also included some metadata that is interpreted 78 00:03:19,740 --> 00:03:22,650 -通过模型云中的 Hugging Face 网站。 +通过 model cloud 上的 HuggingFace 网站解析。 by the Hugging Face website in the model cloud. 79 00:03:22,650 --> 00:03:26,010 -你获得了一个漂亮的小部件中报告的指标的价值 +你将会得到一个漂亮的 widget 所返回的相关指标数值 You get the value of the metrics reported in a nice widget 80 00:03:26,010 --> 00:03:29,640 -以及带有代码的论文排行榜的链接。 +以及一个链接指向 leaderboard(Paper with Code)。 as well as a link to a leaderboard with paper with code. 81 00:03:29,640 --> 00:03:32,550 -所以 Tensorboard runs 也被推送了 +并且 Tensorboard 的运行结果也包含 So the Tensorboard runs have also been pushed 82 00:03:32,550 --> 00:03:34,560 -到这份报告,我们可以看看他们 +在这份报告中,我们可以在 Model Hub 中 to this report, and we can look at them 83 00:03:34,560 --> 00:03:36,000 -直接从模型中心 +通过点击子菜单中的 directly from the model hub 84 00:03:36,000 --> 00:03:38,850 -通过单击训练指标子菜单。 +Training metrics 查看报告。 by clicking on the training metrics sub menu. 85 @@ -430,52 +430,52 @@ to fine-tune your model, 87 00:03:42,510 --> 00:03:43,770 -你可以使用 push_to_hub 方法 +你可以在模型上直接 you can use a push_to_hub method 88 00:03:43,770 --> 00:03:46,427 -在模型上,并直接标记器。 +使用 push_to_hub 方法和分词器。 on the model, and tokenizer directly. 89 00:03:46,427 --> 00:03:50,160 -让我们测试一下以修复推理小部件中的所有标签。 +让我们测试一下以修复 inference widget 中的所有标签。 Let's test this to fix all labels in the inference widget. 90 00:03:50,160 --> 00:03:52,740 -推理小部件使用不同的标签名称 +inference widget 使用不同的标签名称 The inference widget was using different names for labels 91 00:03:52,740 --> 00:03:54,810 -因为我们没有注明对应 +因为我们没有在整数和标签名称之间 because we did not indicate the correspondence 92 00:03:54,810 --> 00:03:57,030 -在整数和标签名称之间。 +注明关联性。 between integer and label names. 93 00:03:57,030 --> 00:03:58,740 -我们可以在配置中解决这个问题 +当推送模型配置到 hub 时, We can fix this in the configuration 94 00:03:58,740 --> 00:04:01,350 -通过坐在 label2id, -by sitting the label2id, +我们可以通过将 label2id +by setting the label2id, 95 00:04:01,350 --> 00:04:04,170 -和 id2label 字段通过适当的值 +和 id2label 字段设置为合适的值 and id2label fields through the proper values 96 00:04:04,170 --> 00:04:06,933 -将模型配置推送到集线器时。 +在配置中解决这个问题。 when pushing the model config to the hub. 97 @@ -490,7 +490,7 @@ and the model is now showing the proper label. 99 00:04:13,380 --> 00:04:15,240 -现在模型在集线器上, +现在模型在 hub 上, Now that the model is on the hub, 100 @@ -500,7 +500,7 @@ we can use it from anywhere 101 00:04:17,370 --> 00:04:19,920 -就像我们对任何其他 Transformer 模型一样 +就像我们对任何其他 Transformer 模型 as we would any other Transformer model 102 @@ -510,12 +510,12 @@ with the from_pretrained method 103 00:04:21,113 --> 00:04:22,923 -具有管道功能。 -of with the pipeline function. +或者使用 pipeline 函数。 +or with the pipeline function. 104 00:04:34,350 --> 00:04:36,780 -我们只需要使用集线器的标识符, +我们只需要使用 hub 的标识符, We just have to use the identifier from the hub, 105 @@ -525,12 +525,12 @@ and we can see that the model configuration and weights 106 00:04:39,450 --> 00:04:42,483 -以及标记化的文件会自动下载。 +以及分词处理后的文件会自动下载。 as well as the tokenized files are automatically downloaded. 107 00:04:53,880 --> 00:04:55,950 -在下一次培训中尝试 push_to_hub API +在下一次训练中尝试 push_to_hub API Try the push_to_hub API in the next training 108 diff --git a/subtitles/zh-CN/34_the-push-to-hub-api-(tensorflow).srt b/subtitles/zh-CN/34_the-push-to-hub-api-(tensorflow).srt index e05f61932..fe6d2061c 100644 --- a/subtitles/zh-CN/34_the-push-to-hub-api-(tensorflow).srt +++ b/subtitles/zh-CN/34_the-push-to-hub-api-(tensorflow).srt @@ -5,37 +5,37 @@ 2 00:00:05,100 --> 00:00:07,080 -- [旁白] 嗨,这将是一个视频 +- [旁白] 嗨,本视频将带领大家了解 - [Narrator] Hi, this is going to be a video 3 00:00:07,080 --> 00:00:09,420 -关于 push_to_hub API +push_to_hub API 适用于 about the push_to_hub API 4 00:00:09,420 --> 00:00:10,670 -适用于 Tensorflow 和 Keras。 +Tensorflow 和 Keras 的版本。 for Tensorflow and Keras. 5 00:00:11,820 --> 00:00:14,850 -因此,首先,我们将打开我们的笔记本。 +那么首先,打开我们的 jupyter。 So, to get started, we'll open up our notebook. 6 00:00:14,850 --> 00:00:16,920 -你需要做的第一件事就是登录 +你需要做的第一件事就是 And the first thing you'll need to do is log in to 7 00:00:16,920 --> 00:00:18,170 -你的 HuggingFace 帐户, +登录你的 HuggingFace 帐户, your HuggingFace account, 8 00:00:19,043 --> 00:00:20,663 -例如笔记本登录功能。 +例如调用 notebook_login 函数。 for example with the notebook login function. 9 @@ -45,22 +45,22 @@ So to use that, you simply call the function, 10 00:00:24,630 --> 00:00:26,010 -弹出窗口将出现。 +随后会弹出窗口。 the popup will emerge. 11 00:00:26,010 --> 00:00:28,800 -你将输入你的用户名和密码, +你需要输入你的用户名和密码, You will enter your username and password, 12 00:00:28,800 --> 00:00:31,425 -我要在这里从我的密码管理器中取出, +这里我使用密码管理器拷贝密码, which I'm going to pull out of my password manager here, 13 00:00:31,425 --> 00:00:33,108 -然后你登录。 +然后就登录了。 and you log in. 14 @@ -70,7 +70,7 @@ The next two cells are just 15 00:00:35,670 --> 00:00:37,080 -为训练做好一切准备。 +为训练做好准备。 getting everything ready for training. 16 @@ -80,22 +80,22 @@ So we're just going to load a dataset, 17 00:00:38,940 --> 00:00:41,100 -我们要标记那个数据集, +我们要对数据集进行分词, we're going to tokenize that dataset, 18 00:00:41,100 --> 00:00:42,990 -然后我们将加载我们的模型并编译 +然后我们将加载我们的模型并 and then we're going to load our model and compile 19 00:00:42,990 --> 00:00:45,660 -它与标准的 Adam 优化器。 +使用标准 Adam 优化器进行编译。 it with the standard Adam optimizer. 20 00:00:45,660 --> 00:00:47,560 -所以我将运行所有这些。 +现在我们会运行这些代码。 So I'm just going to run all of those. 21 @@ -135,22 +135,22 @@ So the first is with the PushToHubCallback. 28 00:01:08,190 --> 00:01:10,107 -所以在 Keras 中回调 +所以 Keras 中的回调 So a callback in Keras 29 00:01:10,107 --> 00:01:13,710 -是在训练期间定期调用的函数。 +也会在训练过程中被频繁调用。 is a function that's called regularly during training. 30 00:01:13,710 --> 00:01:17,400 -你可以将其设置为在一定数量的步骤后调用, +你可以将其设置为在一定数量的 step 后或者在每个 epoch 被调用, You can set it to be called after a certain number of steps, 31 00:01:17,400 --> 00:01:21,427 -或每个时期,甚至只是在训练结束时一次。 +甚至只是在训练结束时调用一次。 or every epoch, or even just once at the end of training. 32 @@ -160,7 +160,7 @@ So a lot of callbacks in Keras, for example, 33 00:01:25,080 --> 00:01:28,050 -控制学习率在高原上衰减, +在平稳期控制学习率衰减, control learning rate decaying on plateau, 34 @@ -175,12 +175,12 @@ So this callback, by default, 36 00:01:32,520 --> 00:01:35,760 -每个时期都会将你的模型保存到 Hub 一次。 +在每个 epoch 都会将你的模型保存到 Hub 一次。 will save your model to the Hub once every epoch. 37 00:01:35,760 --> 00:01:37,080 -这真的很有帮助, +这真的非常有用, And that's really helpful, 38 @@ -190,27 +190,27 @@ especially if your training is very long, 39 00:01:38,790 --> 00:01:40,800 -因为那意味着你可以从那个保存中恢复, +因为那意味着你可以从那个存储中恢复, because that means you can resume from that save, 40 00:01:40,800 --> 00:01:43,290 -所以你得到了你的模型的自动云保存。 +所以你可以实现将你的模型进行自动云存储。 so you get this automatic cloud-saving of your model. 41 00:01:43,290 --> 00:01:45,027 -你甚至可以进行推理 +你甚至可以利用 And you can even run inference 42 00:01:45,027 --> 00:01:47,730 -使用模型的检查点 +该回调所上传的模型存储点(checkpoint) with the checkpoints of your model 43 00:01:47,730 --> 00:01:50,208 -已通过此回调上传。 +进行推断过程。 that have been uploaded by this callback. 44 @@ -220,17 +220,17 @@ And that means you can, 45 00:01:52,260 --> 00:01:54,150 -你知道,运行一些测试输入 +你懂的,运行一些测试输入 y'know, run some test inputs 46 00:01:54,150 --> 00:01:56,100 -并实际查看你的模型是如何工作的 +并实际查看你的模型在训练的各个阶段 and actually see how your model works 47 00:01:56,100 --> 00:01:57,990 -在训练的各个阶段, +是如何工作的, at various stages during training, 48 @@ -250,17 +250,17 @@ and it takes just a few arguments. 51 00:02:05,670 --> 00:02:08,250 -所以第一个参数是临时目录 +第一个参数是临时目录 So the first argument is the temporary directory 52 00:02:08,250 --> 00:02:10,260 -该文件将被保存到 +文件在上传到 Hub 之前 that files are going to be saved to 53 00:02:10,260 --> 00:02:12,150 -在将它们上传到 Hub 之前。 +将被保存到该目录。 before they're uploaded to the Hub. 54 @@ -280,12 +280,12 @@ is the keyword argument hub_model_id. 57 00:02:19,080 --> 00:02:21,330 -所以这就是它将被保存的名称 +也就是在 HuggingFace Hub 上 So that's the name it's going to be saved under 58 00:02:21,330 --> 00:02:23,006 -在 HuggingFace Hub 上。 +它将被保存的名称。 on the HuggingFace Hub. 59 @@ -295,12 +295,12 @@ You can also upload to an organization account 60 00:02:26,267 --> 00:02:29,370 -只需添加组织名称 +只需在带有斜杠的仓库名称之前 just by adding the organization name 61 00:02:29,370 --> 00:02:32,460 -在带有斜杠的存储库名称之前,就像这样。 +添加组织名称,就像这样。 before the repository name with a slash, like this. 62 @@ -315,17 +315,17 @@ to upload to the HuggingFace organization, 64 00:02:36,000 --> 00:02:37,170 -如果你这样做请提交错误 +如果你有权限可以上传, if you do please file a bug 65 00:02:37,170 --> 00:02:38,973 -并非常紧急地通知我们。 +请提交 bug 并尽快通知我们。 and let us know extremely urgently. 66 00:02:40,830 --> 00:02:42,960 -但如果你确实有权访问你自己的组织, +但如果你确实有权限访问你自己的组织, But if you do have access to your own organization, 67 @@ -345,22 +345,22 @@ instead of to your own personal set of models. 70 00:02:50,760 --> 00:02:53,520 -所以,一旦你回电了, +所以,一旦你实现了回调, So, once you've made your callback, 71 00:02:53,520 --> 00:02:56,310 -你只需将它添加到回调列表 +你只需在调用 model.fit 时 you simply add it to the callbacks list 72 00:02:56,310 --> 00:02:58,080 -当你调用 model.fit 时。 +将它添加到回调列表。 when you're calling model.fit. 73 00:02:58,080 --> 00:03:01,110 -一切都从那里为你上传, +所有内容均为从那里上传, And everything is uploaded for you from there, 74 @@ -395,7 +395,7 @@ You can just call this manually whenever you want to 80 00:03:13,680 --> 00:03:15,240 -将模型上传到集线器。 +将模型上传到 hub。 upload a model to the hub. 81 @@ -405,37 +405,37 @@ So we recommend running this after the end of training, 82 00:03:18,949 --> 00:03:21,870 -只是为了确保你有一条提交消息 +只是为了确保你有一条 commit 消息 just to make sure that you have a commit message 83 00:03:21,870 --> 00:03:24,060 -保证这是最终版本 +保证这是训练结束时 to guarantee that this was the final version 84 00:03:24,060 --> 00:03:26,143 -训练结束时的模型。 +模型的最终版本。 of the model at the end of training. 85 00:03:26,143 --> 00:03:27,930 -它只是确保,你知道, +它只是为了确保,你懂的, And it just makes sure that, you know, 86 00:03:27,930 --> 00:03:30,480 -你正在使用最终的训练结束模型 +你当前正在使用的是最终训练结束的模型 you're working with the definitive end-of-training model 87 00:03:30,480 --> 00:03:32,190 -而不是不小心使用检查点 +而不是意外从某个地方拿到的 and not accidentally using a checkpoint 88 00:03:32,190 --> 00:03:34,224 -从沿途的某个地方。 +模型的某个存储点的版本。 from somewhere along the way. 89 @@ -455,7 +455,7 @@ just because training is going to take a couple of minutes. 92 00:03:43,080 --> 00:03:44,580 -所以我会跳到最后, +所以我会直接跳到最后, So I'll skip forward to the end of that, 93 @@ -465,17 +465,17 @@ when the models have all been uploaded, 94 00:03:46,320 --> 00:03:48,390 -我会告诉你怎么做 +我会告诉你 and I'm gonna show you how you can 95 00:03:48,390 --> 00:03:50,010 -访问 Hub 中的模型, +如何访问 Hub 中的模型, access the models in the Hub, 96 00:03:50,010 --> 00:03:52,713 -以及你可以从那里用它们做的其他事情。 +以及其他能够在那里利用模型做到的事情。 and the other things you can do with them from there. 97 @@ -505,17 +505,17 @@ So everything's looking good. 102 00:04:05,910 --> 00:04:09,960 -所以现在如果我们转到我在 HuggingFace 上的个人资料, +那么现在如果我们转到我在 HuggingFace 上的个人资料, So now if we drop over to my profile on HuggingFace, 103 00:04:09,960 --> 00:04:12,630 -你只需点击个人资料按钮就可以到达那里 +你只需点击 profile 按钮就可以 and you can get there just by clicking the profile button 104 00:04:12,630 --> 00:04:13,680 -在下拉列表中。 +在下拉列表中打开。 in the dropdown. 105 @@ -555,52 +555,52 @@ is the Glue CoLA dataset, 112 00:04:34,320 --> 00:04:36,210 -CoLA 是代表 +CoLA 是 Corpus of Linguistic Acceptability(语言可接受性语料库) and CoLA is an acronym standing for 113 00:04:36,210 --> 00:04:39,420 -语言可接受性语料库。 +的首字母缩写。 the Corpus of Linguistic Acceptability. 114 00:04:39,420 --> 00:04:42,480 -所以这意味着正在训练模型来决定 +所以这意味着正在训练模型用来决定 So what that means is the model is being trained to decide 115 00:04:42,480 --> 00:04:46,350 -如果一个句子在语法或语言上没问题, +一个句子在语法或语言上是正确的呢, if a sentence is grammatically or linguistically okay, 116 00:04:46,350 --> 00:04:48,171 -或者它是否有问题。 +还是有问题的呢。 or if there's a problem with it. 117 00:04:48,171 --> 00:04:52,890 -例如,我们可以说,“这是一个合法的句子。” +例如,我们可以说,“This is a legitimate sentence.” For example, we could say, "This is a legitimate sentence." 118 00:04:52,890 --> 00:04:54,180 -希望它意识到 +并且希望它判断 And hopefully it realizes that 119 00:04:54,180 --> 00:04:56,080 -这实际上是一个合法的判决。 +这实际上是一个合理的句子。 this is in fact a legitimate sentence. 120 00:04:57,630 --> 00:05:00,240 -所以加载模型可能需要几秒钟 +所以当你第一次调用模型时, So it might take a couple of seconds for the model to load 121 00:05:00,240 --> 00:05:03,060 -当你第一次调用它时。 +加载模型可能需要几秒钟。 when you call it for the first time. 122 @@ -615,7 +615,7 @@ Okay, we're back. 124 00:05:09,060 --> 00:05:12,407 -所以模型加载了,我们得到了一个输出, +所以模型加载了,我们得到了一个输出结果, So the model loaded and we got an output, 125 @@ -625,17 +625,17 @@ but there's an obvious problem here. 126 00:05:14,340 --> 00:05:16,888 -所以这些标签并没有真正告诉我们 +这些标签并没有真正告诉我们 So these labels aren't really telling us 127 00:05:16,888 --> 00:05:19,740 -模型实际分配了哪些类别 +模型实际为这个输入的句子 what categories the model has actually assigned 128 00:05:19,740 --> 00:05:21,655 -到这个输入句子。 +分配了哪些类别。 to this input sentence. 129 @@ -650,7 +650,7 @@ we want to make sure the model config 131 00:05:26,010 --> 00:05:28,980 -每个标签类别都有正确的名称, +针对每个标签类别都有正确的名称, has the correct names for each of the label classes, 132 @@ -660,7 +660,7 @@ and then we want to upload that config. 133 00:05:30,707 --> 00:05:32,220 -所以我们可以在这里做。 +所以我们可以在这里实现。 So we can do that down here. 134 @@ -675,7 +675,7 @@ we can get that from the dataset we loaded, 136 00:05:36,547 --> 00:05:39,627 -从它具有的特性属性。 +它其中包含特性属性。 from the features attribute it has. 137 @@ -690,7 +690,7 @@ And then we can create dictionaries 139 00:05:44,865 --> 00:05:47,452 -并将它们分配给模型配置。 +并将它们设置到模型配置中。 and just assign them to the model config. 140 @@ -700,7 +700,7 @@ And then we can just push our updated config, 141 00:05:50,790 --> 00:05:54,690 -这将覆盖 Hub 存储库中的现有配置。 +这将覆盖 Hub 仓库中的现有配置。 and that'll override the existing config in the Hub repo. 142 @@ -710,7 +710,7 @@ So that's just been done. 143 00:05:56,368 --> 00:05:58,320 -所以现在,如果我们回到这里, +那么现在,如果我们回到这里, So now, if we go back here, 144 @@ -725,7 +725,7 @@ because the outputs for sentences are sometimes cached. 146 00:06:03,540 --> 00:06:06,030 -所以,如果我们想产生新的结果 +所以,如果我们想生成新的结果 And so, if we want to generate new results 147 @@ -735,7 +735,7 @@ I'm going to use something slightly different. 148 00:06:07,590 --> 00:06:09,783 -因此,让我们尝试一个不正确的句子。 +那么,让我们尝试换一个不正确的句子。 So let's try an incorrect sentence. 149 @@ -745,7 +745,7 @@ So this is not valid English grammar 150 00:06:13,538 --> 00:06:15,030 -希望模型能看到这一点。 +希望模型能发现这一点。 and hopefully the model will see that. 151 @@ -755,12 +755,12 @@ It's going to reload here, 152 00:06:16,958 --> 00:06:18,630 -所以我要在这里缩短几秒钟, +所以在这里稍微快几秒, so I'm going to cut a couple of seconds here, 153 00:06:18,630 --> 00:06:20,933 -然后我们会看到模型会说什么。 +然后我们会看到模型会返回什么结果。 and then we'll see what the model is going to say. 154 @@ -770,12 +770,12 @@ Okay. 155 00:06:23,820 --> 00:06:26,580 -所以这个模型,它的信心不是很好, +所以这个模型,它的置信度不是很好, So the model, it's confidence isn't very good, 156 00:06:26,580 --> 00:06:28,830 -因为我们当然没有真正优化 +因为我们还没有真正优化 because of course we didn't really optimize 157 @@ -785,22 +785,22 @@ our hyperparameters at all. 158 00:06:30,630 --> 00:06:32,190 -但它决定了这句话 +但它返回的结果显示这句话 But it has decided that this sentence 159 00:06:32,190 --> 00:06:35,094 -不可接受的可能性大于可接受的可能性。 +不可接受的程度大于可接受的程度。 is more likely to be unacceptable than acceptable. 160 00:06:35,094 --> 00:06:38,160 -大概如果我们更努力地训练 +如果我们更努力地训练 Presumably if we tried a bit harder with training 161 00:06:38,160 --> 00:06:40,080 -我们可以获得更低的验证损失, +我们可以获得更低的验证集 loss, we could get a much lower validation loss, 162 @@ -835,12 +835,12 @@ So let's try, "This is a valid English sentence". 168 00:07:00,150 --> 00:07:02,100 -我们看到现在模型正确地决定了 +我们看到现在模型的结果是正确的 And we see that now the model correctly decides 169 00:07:02,100 --> 00:07:04,290 -它被接受的可能性非常高, +它的可接受度非常高, that it has a very high probability of being acceptable, 170 @@ -850,42 +850,42 @@ and a very low probability of being unacceptable. 171 00:07:06,900 --> 00:07:09,930 -所以你可以使用这个推理 API +所以你可以使用这个 inference API So you can use this inference API 172 00:07:09,930 --> 00:07:12,810 -即使有训练期间上传的检查点, +甚至可用于训练期间上传的存储点, even with the checkpoints that are uploaded during training, 173 00:07:12,810 --> 00:07:14,546 -所以看看如何 +能够看到在每个训练的 epoch 时 so it can be very interesting to see how 174 00:07:14,546 --> 00:07:17,690 -模型对样本输入的预测发生变化 +不同的样本输入所输出的预测结果 the model's predictions for sample inputs change 175 00:07:17,690 --> 00:07:20,579 -在每个训练阶段。 +也是非常有意思的。 with each epoch of training. 176 00:07:20,579 --> 00:07:23,370 -另外,我们上传的模型 +另外,你也可以访问 Also, the model we've uploaded 177 00:07:23,370 --> 00:07:25,740 -你将可以访问,并且 +我们上传的模型 is going to be accessible to you and, 178 00:07:25,740 --> 00:07:28,046 -如果公开分享给其他任何人。 +如果公开分享,其它任何人都可以访问。 if it's shared publicly, to anyone else. 179 @@ -895,17 +895,17 @@ So if you want to load that model, 180 00:07:29,788 --> 00:07:32,500 -你或其他任何人需要做的一切 +你或其他任何人所需要做的 all you or anyone else needs to do 181 00:07:34,290 --> 00:07:37,440 -只是将它加载到管道中, +就是将它加载到 pipeline, is just to load it in either a pipeline, 182 00:07:37,440 --> 00:07:40,925 -或者你可以加载它,例如, +或者你可以使用其它方式加载它,例如, or you can just load it with, for example, 183 @@ -915,12 +915,12 @@ TFAutoModelForSequenceClassification. 184 00:07:46,920 --> 00:07:49,989 -然后对于名称,你只需通过 +然后对于名称,你只需传入 And then for the name you would just simply pass 185 00:07:49,989 --> 00:07:53,325 -你要上传的存储库的路径。 +想要上传的 repo 的路径即可。 the path to the repo you want to upload. 186 @@ -935,12 +935,12 @@ So if I want to use this model again, 188 00:07:58,710 --> 00:08:00,667 -如果我想从集线器加载它, +如果我想从 hub 加载它, if I want to load it from the hub, 189 00:08:00,667 --> 00:08:01,763 -我只是运行这一行代码。 +仅需运行这一行代码。 I just run this one line of code. 190 @@ -965,42 +965,42 @@ or do anything else you wanna do. 194 00:08:14,340 --> 00:08:17,700 -所以这是对如何的快速概述, +以上内容就是关于 So that was a quick overview of how, 195 00:08:17,700 --> 00:08:19,470 -在你训练之后或训练期间, +在你训练期间或者训练之后 after your training or during your training, 196 00:08:19,470 --> 00:08:21,420 -你可以将模型上传到 Hub, +如何将模型上传到 Hub, you can upload models to the Hub, 197 00:08:21,420 --> 00:08:22,440 -你可以在那里检查站, +可以添加存储点, you can checkpoint there, 198 00:08:22,440 --> 00:08:24,240 -你可以从那里恢复训练, +可以恢复训练, you can resume training from there, 199 00:08:24,240 --> 00:08:26,790 -你可以得到推理结果 +以及从所上传模型 and you can get inference results 200 00:08:26,790 --> 00:08:28,384 -来自你上传的模型。 +获得推理结果。 from the models you've uploaded. 201 00:08:28,384 --> 00:08:31,084 -所以谢谢你,我希望在以后的视频中见到你。 +谢谢大家,希望在以后的视频再会。 So thank you, and I hope to see you in a future video. 202 diff --git a/subtitles/zh-CN/35_loading-a-custom-dataset.srt b/subtitles/zh-CN/35_loading-a-custom-dataset.srt index fd3a794e2..7e6c8815f 100644 --- a/subtitles/zh-CN/35_loading-a-custom-dataset.srt +++ b/subtitles/zh-CN/35_loading-a-custom-dataset.srt @@ -20,8 +20,8 @@ 5 00:00:08,430 --> 00:00:09,750 -尽管 Hugging Face Hub 主持 -Although the Hugging Face Hub hosts +尽管 Hugging Face Hub 上承载了 +Although the HuggingFace Hub hosts 6 00:00:09,750 --> 00:00:11,730 @@ -30,27 +30,27 @@ over a thousand public datasets, 7 00:00:11,730 --> 00:00:12,930 -你经常需要处理数据 +你可能仍然需要经常处理存储在你的笔记本电脑 you'll often need to work with data 8 00:00:12,930 --> 00:00:15,900 -存储在你的笔记本电脑或某些远程服务器上。 +或存储在远程服务器上的数据。 that is stored on your laptop or some remote server. 9 00:00:15,900 --> 00:00:18,060 -在本视频中,我们将探讨数据集库如何 +在本视频中,我们将探讨如何利用 Datasets 库 In this video, we'll explore how the Datasets library 10 00:00:18,060 --> 00:00:20,310 -可用于加载不可用的数据集 +加载 Hugging Face Hub 以外 can be used to load datasets that aren't available 11 00:00:20,310 --> 00:00:21,510 -在 Hugging Face Hub 上。 +的数据集。 on the Hugging Face Hub. 12 @@ -75,22 +75,22 @@ To load a dataset in one of these formats, 16 00:00:31,200 --> 00:00:32,730 -你只需要提供格式的名称 +你只需要向 load_dataset 函数 you just need to provide the name of the format 17 00:00:32,730 --> 00:00:34,350 -到 load_dataset 函数, +提供格式的名称, to the load_dataset function, 18 00:00:34,350 --> 00:00:35,790 -连同 data_files 参数 +并且连同 data_files 参数一起传入 along with a data_files argument 19 00:00:35,790 --> 00:00:37,610 -指向一个或多个文件路径或 URL。 +该参数指向一个或多个文件路径或 URL。 that points to one or more filepaths or URLs. 20 @@ -105,7 +105,7 @@ In this example, we first download a dataset 22 00:00:45,960 --> 00:00:48,963 -关于来自 UCI 机器学习库的葡萄酒质量。 +该数据集是来自 UCI 机器学习库的葡萄酒质量数据。 about wine quality from the UCI machine learning repository. 23 @@ -150,7 +150,7 @@ so here we've also specified 31 00:01:06,750 --> 00:01:09,030 -分隔符是分号。 +分号作为分隔符。 that the separator is a semi-colon. 32 @@ -165,7 +165,7 @@ is loaded automatically as a DatasetDict object, 34 00:01:13,020 --> 00:01:15,920 -CSV 文件中的每一列都表示为一个特征。 +CSV 文件中的每一列都代表一个特征。 with each column in the CSV file represented as a feature. 35 @@ -175,7 +175,7 @@ If your dataset is located on some remote server like GitHub 36 00:01:20,280 --> 00:01:22,050 -或其他一些存储库, +或其他一些数据仓库, or some other repository, 37 @@ -205,12 +205,12 @@ This format is quite common in NLP, 42 00:01:35,100 --> 00:01:36,750 -你通常会找到书籍和戏剧 +你常常会发现书籍和戏剧 and you'll typically find books and plays 43 00:01:36,750 --> 00:01:39,393 -只是一个包含原始文本的文件。 +只是一个包含原始文本的独立文件。 are just a single file with raw text inside. 44 @@ -220,7 +220,7 @@ In this example, we have a text file of Shakespeare plays 45 00:01:43,020 --> 00:01:45,330 -存储在 GitHub 存储库中。 +存储在 GitHub 仓库中。 that's stored on a GitHub repository. 46 @@ -245,12 +245,12 @@ As you can see, these files are processed line-by-line, 50 00:01:55,110 --> 00:01:57,690 -所以原始文本中的空行也被表示 +所以原始文本中的空行 so empty lines in the raw text are also represented 51 00:01:57,690 --> 00:01:58,953 -作为数据集中的一行。 +也按照数据集中的一行表示。 as a row in the dataset. 52 @@ -270,12 +270,12 @@ where every row in the file is a separate JSON object. 55 00:02:09,510 --> 00:02:11,100 -对于这些文件,你可以加载数据集 +对于这些文件,你可以通过选择 JSON 加载脚本 For these files, you can load the dataset 56 00:02:11,100 --> 00:02:13,020 -通过选择 JSON 加载脚本 +来加载数据集 by selecting the JSON loading script 57 @@ -285,12 +285,12 @@ and pointing the data_files argument to the file or URL. 58 00:02:17,160 --> 00:02:19,410 -在这个例子中,我们加载了一个 JSON 行文件 +在这个例子中,我们加载了一个多行 JSON 的文件 In this example, we've loaded a JSON lines files 59 00:02:19,410 --> 00:02:21,710 -基于 Stack Exchange 问题和答案。 +其内容基于 Stack Exchange 问题和答案。 based on Stack Exchange questions and answers. 60 @@ -310,27 +310,27 @@ so the load_dataset function allow you to specify 63 00:02:31,200 --> 00:02:32,733 -要加载哪个特定密钥。 +要加载哪个特定关键词。 which specific key to load. 64 00:02:33,630 --> 00:02:35,910 -例如,用于问答的 SQuAD 数据集 +例如,用于问答的 SQuAD 数据集有它的格式, For example, the SQuAD dataset for question and answering 65 00:02:35,910 --> 00:02:38,340 -有它的格式,我们可以通过指定来加载它 +我们可以通过指定我们感兴趣的数据字段 has its format, and we can load it by specifying 66 00:02:38,340 --> 00:02:40,340 -我们对数据字段感兴趣。 +我们对 data 字段感兴趣。 that we're interested in the data field. 67 00:02:41,400 --> 00:02:42,780 -最后一件事要提 +最后要和大家分享的内容是 There is just one last thing to mention 68 @@ -340,7 +340,7 @@ about all of these loading scripts. 69 00:02:44,910 --> 00:02:46,410 -你可以有不止一次分裂, +你可以有不止一次数据切分, You can have more than one split, 70 @@ -350,7 +350,7 @@ you can load them by treating data files as a dictionary, 71 00:02:49,080 --> 00:02:52,140 -并将每个拆分名称映射到其对应的文件。 +并将每个拆分的名称映射到其对应的文件。 and map each split name to its corresponding file. 72 @@ -360,22 +360,22 @@ Everything else stays completely unchanged 73 00:02:53,970 --> 00:02:55,350 -你可以看到一个加载的例子 +你可以看到一个例子, and you can see an example of loading 74 00:02:55,350 --> 00:02:58,283 -此 SQuAD 的训练和验证拆分均在此处。 +加载此 SQuAD 的训练和验证分解步骤都在这里。 both the training and validation splits for this SQuAD here. 75 00:02:59,550 --> 00:03:02,310 -这样,你现在可以从笔记本电脑加载数据集, +这样,你现在可以加载来自笔记本电脑的数据集,来自 Hugging Face Hub 的数据集, And with that, you can now load datasets from your laptop, 76 00:03:02,310 --> 00:03:04,653 -Hugging Face Hub,或任何其他地方。 +或来自任何其他地方的数据集。 the Hugging Face Hub, or anywhere else want. 77 diff --git "a/subtitles/zh-CN/37_datasets-+-dataframes-=-\342\235\244\357\270\217.srt" "b/subtitles/zh-CN/37_datasets-+-dataframes-=-\342\235\244\357\270\217.srt" index 2964fc8ab..383793324 100644 --- "a/subtitles/zh-CN/37_datasets-+-dataframes-=-\342\235\244\357\270\217.srt" +++ "b/subtitles/zh-CN/37_datasets-+-dataframes-=-\342\235\244\357\270\217.srt" @@ -15,7 +15,7 @@ 4 00:00:05,340 --> 00:00:07,833 -- 数据集和数据帧等于爱。 +- 数据集加上数据帧等于真爱。 - Datasets and DataFrames equals love. 5 @@ -30,32 +30,32 @@ will cover most of the cases needed to train a model, 7 00:00:14,040 --> 00:00:15,660 -有时你需要切换到图书馆 +有时你需要切换到其它库 there are times when you'll need to switch to a library 8 00:00:15,660 --> 00:00:18,240 -像 Pandas 一样访问更强大的功能 +比如 Pandas 来获得更强大的功能 like Pandas to access more powerful features 9 00:00:18,240 --> 00:00:20,970 -或用于可视化的高级 API。 +或针对计算机视觉使用更高级的 API。 or high level APIs for visualization. 10 00:00:20,970 --> 00:00:23,220 -幸运的是,Datasets 库是专为 +幸运的是,Datasets 库是专门为了 Fortunately, the Datasets library is designed 11 00:00:23,220 --> 00:00:25,710 -与 Pandas 等库互操作, +和外部库进行互操作所设计的,比如 Pandas to be interoperable with libraries like Pandas, 12 00:00:25,710 --> 00:00:29,790 -以及 NumPy、PyTorch、TensorFlow 和 JAX。 +此外还有 NumPy、PyTorch、TensorFlow 和 JAX。 as well as NumPy, PyTorch, TensorFlow and JAX. 13 @@ -65,12 +65,12 @@ In this video, we'll take a look 14 00:00:30,930 --> 00:00:32,550 -我们如何快速切换数据 +我们如何将数据格式快速切换到 Pandas DataFrames at how we can quickly switch our data 15 00:00:32,550 --> 00:00:34,263 -到 Pandas DataFrames 并返回。 +并且再切换回来。 to Pandas DataFrames and back. 16 @@ -85,12 +85,12 @@ Supreme Court cases from Switzerland. 18 00:00:40,830 --> 00:00:43,020 -像往常一样,我们从集线器下载我们的数据集 +像往常一样,我们使用 load_dataset 函数 As usual, we download our dataset from the hub 19 00:00:43,020 --> 00:00:44,940 -使用 load_dataset 函数。 +从 hub 下载我们的数据集。 using the load_dataset function. 20 @@ -100,12 +100,12 @@ And you can see that the first element of the training set 21 00:00:46,980 --> 00:00:48,510 -是一个普通的 Python 字典 +是一个普通的 Python 字典类型 is an ordinary Python dictionary 22 00:00:48,510 --> 00:00:50,110 -与各种兴趣领域。 +里面包含各种我们需要的字段。 with various fields of interest. 23 @@ -115,12 +115,12 @@ Now, suppose that before we train any models, 24 00:00:53,670 --> 00:00:55,590 -我们想稍微探索一下数据。 +我们想稍微浏览一下数据。 we'd like to explore the data a bit. 25 00:00:55,590 --> 00:00:57,390 -例如,我们可能有兴趣知道 +例如,我们可能希望知道 For example, we might be interested in knowing 26 @@ -135,17 +135,17 @@ or we might wanna know how the languages 28 00:01:01,380 --> 00:01:02,930 -分布在各个地区。 +在各个地区是如何分布的。 are distributed across regions. 29 00:01:04,500 --> 00:01:05,333 -回答这些问题 +使用原生 Arrow 类型 Answering these questions 30 00:01:05,333 --> 00:01:07,530 -使用原生 Arrow 格式并不容易, +很难回答这些问题, with the native Arrow format isn't easy, 31 @@ -155,7 +155,7 @@ but we can quickly switch to Pandas to get our answers. 32 00:01:10,500 --> 00:01:13,500 -它的工作方式是通过使用 set_format 方法, +通过使用 set_format 方法即可实现, The way this works is that by using the set_format method, 33 @@ -165,7 +165,7 @@ we will change the output format of the dataset 34 00:01:15,480 --> 00:01:18,930 -从 Python 字典到 Pandas DataFrame。 +从 Python 字典类型到 Pandas DataFrame 类型。 from Python dictionaries to Pandas DataFrames. 35 @@ -185,37 +185,37 @@ so we can slice the whole dataset 38 00:01:24,540 --> 00:01:26,583 -获取语料库的单个 DataFrame。 +来获取语料库的单个 DataFrame。 to get a single DataFrame of the corpus. 39 00:01:28,080 --> 00:01:29,520 -这在引擎盖下的工作方式, +其底层工作原理 The way this works under the hood, 40 00:01:29,520 --> 00:01:31,080 -是数据集库发生了变化 +是 datasets 库改变了 is that the datasets library changes 41 00:01:31,080 --> 00:01:33,900 -数据集的神奇 __getitem__ 方法。 +数据集中神奇的 __getitem__ 方法。 the magic __getitem__ method of the dataset. 42 00:01:33,900 --> 00:01:35,640 -__getitem__ 方法是一种特殊的方法 +对于 Python 容器来说 __getitem__ 方法 The __getitem__ method is a special method 43 00:01:35,640 --> 00:01:37,320 -对于允许你的 Python 容器 +是一种特殊的方法,它允许你指定 for Python containers that allows you 44 00:01:37,320 --> 00:01:39,870 -指定索引的工作方式。 +以何种方式使用索引。 to specify how indexing works. 45 @@ -230,7 +230,7 @@ starts off by returning a Python dictionary 47 00:01:45,150 --> 00:01:47,520 -然后在应用 set_format 之后, +然后在调用 set_format 之后, and then after applying set_format, 48 @@ -240,17 +240,17 @@ we change __getitem__ to return DataFrames instead. 49 00:01:52,080 --> 00:01:54,690 -Datasets 库还提供了一个 to_pandas 方法 +如果你想做格式转换 The Datasets library also provides a to_pandas method 50 00:01:54,690 --> 00:01:56,250 -如果你想做格式转换 +并一次性对数据集进行切片 if you wanna do the format conversion 51 00:01:56,250 --> 00:01:58,113 -并一次性对数据集进行切片。 +Datasets 库还提供了一个 to_pandas 方法。 and slicing of the dataset in one go. 52 @@ -280,37 +280,37 @@ is that once you're done with your Pandas analysis, 57 00:02:10,830 --> 00:02:14,460 -你应该将输出格式重置回箭头表。 +你应该将输出格式重置回 Arrow 表。 you should reset the output format back to Arrow tables. 58 00:02:14,460 --> 00:02:16,350 -如果你不这样做,你可能会遇到问题 +如果不这样做,当你尝试词元化你的文本 If you don't, you can run into problems 59 00:02:16,350 --> 00:02:17,910 -如果你尝试标记化你的文本 +可能会遇到问题 if you try to tokenize your text 60 00:02:17,910 --> 00:02:19,260 -因为它不再代表 +因为它不再是字典中的 because it is no longer represented 61 00:02:19,260 --> 00:02:20,610 -作为字典中的字符串。 +字符串。 as strings in a dictionary. 62 00:02:21,750 --> 00:02:24,780 -通过重置输出格式,我们得到箭头表 +通过重置输出格式,我们得到 Arrow 表 By resetting the output format we get back Arrow tables 63 00:02:24,780 --> 00:02:26,580 -我们可以毫无问题地标记化。 +我们可以毫无顾虑地进行词元化。 and we can tokenize without problem. 64 diff --git a/subtitles/zh-CN/38_saving-and-reloading-a-dataset.srt b/subtitles/zh-CN/38_saving-and-reloading-a-dataset.srt index 4039d68b0..d6e25801c 100644 --- a/subtitles/zh-CN/38_saving-and-reloading-a-dataset.srt +++ b/subtitles/zh-CN/38_saving-and-reloading-a-dataset.srt @@ -25,17 +25,17 @@ and explore the ways to reload the saved data. 6 00:00:17,310 --> 00:00:20,100 -下载数据集时,处理脚本和数据 +下载数据集时,所需的处理脚本和数据都会本地存储 When you download a dataset, the processing scripts and data 7 00:00:20,100 --> 00:00:22,470 -本地存储在你的计算机上。 +在你的计算机上。 are stored locally on your computer. 8 00:00:22,470 --> 00:00:24,000 -缓存允许数据集库 +缓存允许 Datasets 库 The cache allows the Datasets library 9 @@ -50,27 +50,27 @@ or processing the entire dataset every time you use it. 11 00:00:28,620 --> 00:00:31,170 -现在,数据以箭头表的形式存储 +现在,数据以 Arrow 表的形式存储 Now, the data is stored in the form of Arrow tables 12 00:00:31,170 --> 00:00:32,490 -可以找到谁的位置 +通过访问数据集的 whose location can be found 13 00:00:32,490 --> 00:00:35,730 -通过访问数据集的 cache_files 属性。 +cache_files 属性可以找到它的位置。 by accessing the dataset's cache_files attribute. 14 00:00:35,730 --> 00:00:38,430 -在这个例子中,我们下载了 allocine 数据集 +在这个例子中,我们从 Hugging Face Hub In this example, we've downloaded the allocine dataset 15 00:00:38,430 --> 00:00:40,080 -来自 Hugging Face Hub,你可以看到 +下载了 allocine 数据集,你可以看到 from the Hugging Face Hub, and you can see 16 @@ -80,17 +80,17 @@ that there are three Arrow files 17 00:00:41,430 --> 00:00:43,473 -存储在缓存中,每个拆分一个。 +存储在缓存中,每个文件对应一个分片数据。 stored in the cache, one for each split. 18 00:00:45,360 --> 00:00:47,460 -但在很多情况下,你会想保存你的数据集 +但在很多情况下,你希望在不同的物理地址或者以不同的格式 But in many cases, you'll wanna save your dataset 19 00:00:47,460 --> 00:00:49,890 -在不同的位置或格式。 +保存你的数据集。 in a different location or format. 20 @@ -115,12 +115,12 @@ with the CSV and JSON formats, both of which are great 24 00:00:58,770 --> 00:01:00,810 -如果你只是想快速节省一小笔钱 +如果你只是想快速保存一个小规模 if you just wanna quickly save a small 25 00:01:00,810 --> 00:01:02,790 -或中型数据集。 +或中等规模的数据集。 or medium-sized dataset. 26 @@ -135,12 +135,12 @@ you'll wanna save it in either the Arrow or Parquet formats. 28 00:01:07,860 --> 00:01:09,660 -如果你打算重新加载,箭头文件很棒 +如果你打算重新加载或在不久的将来处理数据, Arrow files are great if you plan to reload 29 00:01:09,660 --> 00:01:11,850 -或在不久的将来处理数据。 +Arrow 文件就很棒。 or process the data in the near future. 30 @@ -175,22 +175,22 @@ As you can see in this example, 36 00:01:26,910 --> 00:01:29,790 -我们只需提供我们希望将数据保存到的路径 +只需提供我们希望将数据保存到的路径 we simply provide the path we wish to save the data to 37 00:01:29,790 --> 00:01:30,720 -和数据集库 +然后 Datasets 库 and the Datasets library 38 00:01:30,720 --> 00:01:32,340 -会自动创建一个目录 +会针对每个分片数据自动创建一个目录 will automatically create a directory 39 00:01:32,340 --> 00:01:35,790 -对于每个拆分来存储箭头表和元数据。 +来存储 Arrow 表和元数据。 for each split to store the Arrow table and the metadata. 40 @@ -200,7 +200,7 @@ Since we're dealing with a dataset_dict object 41 00:01:37,680 --> 00:01:39,090 -有多个拆分, +其中包含多个分片数据, that has multiple splits, 42 @@ -215,7 +215,7 @@ in the dataset_dict.json file. 44 00:01:44,250 --> 00:01:46,710 -现在,当我们想要重新加载 Arrow 数据集时, +现在,当想要重新加载 Arrow 数据集时, Now, when we wanna reload the Arrow datasets, 45 @@ -225,12 +225,12 @@ we use the load_from_disk function. 46 00:01:48,870 --> 00:01:51,210 -我们只需传递数据集目录的路径, +只需传递数据集目录的路径, We simply pass the path of our dataset directory, 47 00:01:51,210 --> 00:01:53,583 -瞧,原始数据集已恢复。 +啊瞧,原始数据集已恢复。 and voila, the original dataset is recovered. 48 @@ -250,7 +250,7 @@ In this case, you'll need to loop 51 00:02:02,280 --> 00:02:04,170 -在 dataset_dict 对象的拆分上 +dataset_dict 对象的分片数据 over the splits of the dataset_dict object 52 @@ -270,12 +270,12 @@ you can pass keyword arguments to configure the output. 55 00:02:13,980 --> 00:02:16,230 -在这个例子中,我们设置了索引参数 +在这个例子中,我们将索引参数设置为 None In this example, we've set the index argument 56 00:02:16,230 --> 00:02:18,480 -为 None 以防止数据集的索引列 +以防止数据集的索引列 to None to prevent the dataset's index column 57 @@ -290,7 +290,7 @@ To reload our CSV files, 59 00:02:24,240 --> 00:02:27,180 -然后我们就使用熟悉的 load_dataset 函数 +就使用熟悉的 load_dataset 函数 we just then use the familiar load_dataset function 60 @@ -305,7 +305,7 @@ and the data_files argument, 62 00:02:30,360 --> 00:02:34,020 -它指定与每个拆分关联的文件名。 +它指定与每个分片数据关联的文件名。 which specifies the file names associated with each split. 63 @@ -315,7 +315,7 @@ As you can see in this example, 64 00:02:35,400 --> 00:02:37,320 -通过提供所有拆分及其文件名, +通过提供所有分片数据及其文件名, by providing all the splits and their file names, 65 @@ -330,7 +330,7 @@ Now, to save a dataset in the JSON 67 00:02:43,560 --> 00:02:46,710 -或 Parquet 格式与 CSV 的情况非常相似。 +或保存为 Parquet 格式与 CSV 的情况非常相似。 or Parquet formats is very similar to the CSV case. 68 @@ -345,7 +345,7 @@ or the to_parquet function for Parquet ones. 70 00:02:52,740 --> 00:02:55,740 -就像 CSV 案例一样,我们需要遍历拆分 +就像 CSV 案例一样,我们需要遍历分片数据 And just like the CSV case, we need to loop over the splits 71 @@ -365,7 +365,7 @@ we can reload them again 74 00:03:03,990 --> 00:03:06,960 -使用 load_dataset 函数中的适当脚本。 +使用 load_dataset 函数中的合适的脚本。 with the appropriate script in the load_dataset function. 75 @@ -380,7 +380,7 @@ This example shows 77 00:03:11,910 --> 00:03:14,560 -我们如何以任何一种格式重新加载我们的保存数据集。 +我们如何以任何一种格式重新加载我们保存的数据集。 how we can reload our save datasets in either format. 78 diff --git a/subtitles/zh-CN/39_memory-mapping-&-streaming.srt b/subtitles/zh-CN/39_memory-mapping-&-streaming.srt index e39a21f86..5c8f58909 100644 --- a/subtitles/zh-CN/39_memory-mapping-&-streaming.srt +++ b/subtitles/zh-CN/39_memory-mapping-&-streaming.srt @@ -15,7 +15,7 @@ 4 00:00:05,640 --> 00:00:07,203 -- 内存映射和流。 +- 内存映射和流式数据。 - Memory mapping and streaming. 5 @@ -30,22 +30,22 @@ at two core features of the Datasets library 7 00:00:11,520 --> 00:00:14,220 -允许你加载和处理庞大的数据集 +在不耗尽笔记本电脑的 CPU 资源的前提下 that allow you to load and process huge datasets 8 00:00:14,220 --> 00:00:16,263 -而不会炸毁笔记本电脑的 CPU。 +允许你加载和处理庞大的数据集。 without blowing up your laptop's CPU. 9 00:00:18,300 --> 00:00:20,280 -如今,发现自己并不罕见 +如今,工作上处理多达数个 GB 体量的数据集 Nowadays, it's not uncommon to find yourself 10 00:00:20,280 --> 00:00:22,950 -使用多 GB 大小的数据集, +已经不是什么新鲜事了, working with multi-GB sized datasets, 11 @@ -55,12 +55,12 @@ especially if you're planning to pretrain 12 00:00:24,420 --> 00:00:28,110 -从头开始像 BERT 或 GPT-2 这样的转换器。 +类似 BERT 或 GPT-2 这样的 transformer。 a transformer like BERT or GPT-2 from scratch. 13 00:00:28,110 --> 00:00:31,260 -在这些情况下,即使加载数据也可能是一个挑战。 +在这些场景下,即使加载数据也可能是一个挑战。 In these cases, even loading the data can be a challenge. 14 @@ -95,12 +95,12 @@ Arrow is designed for high-performance data processing 20 00:00:49,110 --> 00:00:51,360 -并代表每个类似表格的数据集 +并代表每个类似表格的 and represents each table-like dataset 21 00:00:51,360 --> 00:00:52,773 -使用基于列的格式。 +基于列格式的数据集。 with a column-based format. 22 @@ -110,12 +110,12 @@ As you can see in this example, column-based formats 23 00:00:56,130 --> 00:00:59,280 -将表格的元素分组到连续的 RAM 块中 +将表格的元素分组缓存到连续的 RAM 块中 group the elements of a table in consecutive blocks of RAM 24 00:00:59,280 --> 00:01:01,563 -这解锁了快速访问和处理。 +这实现了快速访问和处理。 and this unlocks fast access and processing. 25 @@ -130,7 +130,7 @@ but some datasets are so large 27 00:01:07,110 --> 00:01:09,600 -你甚至不能把它们放在你的硬盘上。 +你甚至不能把它们完全放在你的硬盘上。 that you can't even fit them on your hard disk. 28 @@ -145,7 +145,7 @@ a streaming API that allows you to progressively download 30 00:01:14,820 --> 00:01:17,700 -原始数据一次一个元素。 +可以每次下载原始数据的一个元素。 the raw data one element at a time. 31 @@ -155,7 +155,7 @@ The result is a special object called an IterableDataset 32 00:01:20,430 --> 00:01:22,180 -我们很快就会看到更多细节。 +我们接下来就会看到更多细节。 that we'll see in more detail soon. 33 @@ -165,12 +165,12 @@ Let's start by looking at why Arrow is so powerful. 34 00:01:26,670 --> 00:01:28,860 -第一个特点是它处理每个数据集 +第一个特点是它将每个数据集 The first feature is that it treats every dataset 35 00:01:28,860 --> 00:01:30,153 -作为内存映射文件。 +作为内存映射文件处理。 as a memory-mapped file. 36 @@ -200,7 +200,7 @@ to access segments of an extremely large file 41 00:01:41,280 --> 00:01:44,080 -无需先将整个文件读入内存。 +而无需先将整个文件读入内存。 without having to read the whole file into memory first. 42 @@ -220,7 +220,7 @@ to work with the same large dataset 45 00:01:51,840 --> 00:01:54,333 -不以任何方式移动或复制它。 +而无需以任何方式移动或复制它。 without moving it or copying it in any way. 46 @@ -235,17 +235,17 @@ makes it extremely fast for iterating over a dataset. 48 00:02:00,600 --> 00:02:02,640 -这个例子,你可以看到我们迭代 +这个例子,你可以看到我们使用 And this example, you can see that we iterate 49 00:02:02,640 --> 00:02:05,160 -大约一分钟内超过 1500 万行 +普通的笔记本电脑在一分钟之内迭代 over 15 million rows in about a minute 50 00:02:05,160 --> 00:02:06,780 -仅使用标准笔记本电脑。 +大约超过 1500 万行数据。 just using a standard laptop. 51 @@ -260,12 +260,12 @@ Let's now take a look at how we can stream a large dataset. 53 00:02:12,660 --> 00:02:14,520 -你需要做的唯一更改是设置 +你需要做的唯一修改是 The only change you need to make is to set 54 00:02:14,520 --> 00:02:17,910 -load_dataset () 函数中的 streaming=True 参数。 +设置 load_dataset () 函数中的 streaming=True 参数。 the streaming=True argument in the load_dataset () function. 55 @@ -295,12 +295,12 @@ which means we can't index it to access elements, 60 00:02:28,530 --> 00:02:30,180 -但相反我们迭代它 +但相反我们使用 iter but instead we iterate on it 61 00:02:30,180 --> 00:02:32,850 -使用 iter 和 next 方法。 +和 next 方法迭代它。 using the iter and next methods. 62 @@ -320,7 +320,7 @@ which means you can progressively iterate 65 00:02:37,410 --> 00:02:40,360 -通过庞大的数据集,而无需先下载它。 +庞大的数据集,而无需提前下载它。 through a huge dataset without having to download it first. 66 @@ -345,7 +345,7 @@ and then apply the map () method with the tokenizer. 70 00:02:49,830 --> 00:02:53,283 -要获得第一个标记化示例,我们应用 iter 和 next。 +要获得第一个词元化示例,我们应用 iter 和 next。 To get the first tokenized example, we apply iter and next. 71 @@ -355,12 +355,12 @@ The main difference with an IterableDataset is that 72 00:02:57,210 --> 00:02:59,970 -而不是使用 select () 方法返回示例, +并未使用 select () 方法返回示例, instead of using a select () method to return examples, 73 00:02:59,970 --> 00:03:01,530 -我们使用 take () 和 skip () 方法 +而是使用 take () 和 skip () 方法 we use the take () and skip () methods 74 diff --git a/subtitles/zh-CN/40_uploading-a-dataset-to-the-hub.srt b/subtitles/zh-CN/40_uploading-a-dataset-to-the-hub.srt index 860785521..563602e72 100644 --- a/subtitles/zh-CN/40_uploading-a-dataset-to-the-hub.srt +++ b/subtitles/zh-CN/40_uploading-a-dataset-to-the-hub.srt @@ -5,7 +5,7 @@ 2 00:00:05,490 --> 00:00:07,950 -- 将数据集上传到中心。 +- 将数据集上传到 hub。 - Uploading a dataset to the hub. 3 @@ -30,7 +30,7 @@ The first thing you need to do 7 00:00:14,670 --> 00:00:17,400 -是在集线器上创建一个新的数据集存储库。 +是在 hub 上创建一个新的数据集仓库。 is create a new dataset repository on the hub. 8 @@ -40,7 +40,7 @@ So, just click on your profile icon 9 00:00:19,260 --> 00:00:21,750 -并选择新建数据集按钮。 +并选择 New Dataset 按钮。 and select the New Dataset button. 10 @@ -50,17 +50,17 @@ Next, we need to assign an owner of the dataset. 11 00:00:24,750 --> 00:00:26,970 -默认情况下,这将是你的中心帐户, +默认情况下,所有者是你的 hub 帐户, By default, this will be your hub account, 12 00:00:26,970 --> 00:00:28,170 -但你也可以创建数据集 +但你也可以 but you can also create datasets 13 00:00:28,170 --> 00:00:30,585 -在你所属的任何组织下。 +以你所属的组织的名义创建数据集。 under any organization that you belong to. 14 @@ -80,12 +80,12 @@ Public datasets can be accessed by anyone 17 00:00:39,810 --> 00:00:41,670 -而私人数据集只能被访问 +而私人数据集只能 while private datasets can only be accessed 18 00:00:41,670 --> 00:00:43,653 -由你或你的组织成员。 +允许你或你的组织成员访问。 by you or members of your organization. 19 @@ -95,7 +95,7 @@ And with that, we can go ahead and create the dataset. 20 00:00:48,690 --> 00:00:51,060 -现在你在集线器上有一个空的数据集存储库, +现在你在 hub 上有一个空的数据集仓库, Now that you have an empty dataset repository on the hub, 21 @@ -110,17 +110,17 @@ You can do this with git, 23 00:00:55,050 --> 00:00:57,960 -但最简单的方法是选择上传文件按钮。 +但最简单的方法是选择 Upload file 按钮。 but the easiest way is by selecting the Upload file button. 24 00:00:57,960 --> 00:00:59,160 -然后,你可以继续 +然后,你可以继续下一步 And then, you can just go ahead 25 00:00:59,160 --> 00:01:02,243 -并直接从你的机器上传文件。 +直接从你的机器上传文件。 and upload the files directly from your machine. 26 @@ -130,12 +130,12 @@ After you've uploaded your files, 27 00:01:03,846 --> 00:01:05,670 -你会看到它们出现在存储库中 +你会在 Files and versions 选项卡下 you'll see them appear in the repository 28 00:01:05,670 --> 00:01:07,320 -在文件和版本选项卡下。 +看到它们出现在仓库中。 under the Files and versions tab. 29 @@ -145,27 +145,27 @@ The last step is to create a dataset card. 30 00:01:11,370 --> 00:01:13,590 -记录良好的数据集更有用 +对其他人来说,归档良好的数据集貌似更有用, Well-documented datasets are more likely to be useful 31 00:01:13,590 --> 00:01:15,600 -给其他人,因为他们提供了决定的背景 +因为他们提供了影响决策的上下文信息 to others as they provide the context to decide 32 00:01:15,600 --> 00:01:17,370 -数据集是否相关 +包括数据集是否与需求相关 whether the dataset is relevant 33 00:01:17,370 --> 00:01:18,450 -或者有没有偏见 +或者有没有误差 or whether there are any biases 34 00:01:18,450 --> 00:01:20,673 -或与使用数据集相关的风险。 +或使用该数据可能遇到的风险。 or risks associated with using the dataset. 35 @@ -175,12 +175,12 @@ On the Hugging Face Hub, 36 00:01:22,710 --> 00:01:25,650 -此信息存储在每个存储库的自述文件中。 +此信息存储在每个仓库的 README 文件中。 this information is stored in each repositories README file. 37 00:01:25,650 --> 00:01:27,988 -你应该采取两个主要步骤。 +你需要执行两个主要步骤。 There are two main steps that you should take. 38 @@ -190,27 +190,27 @@ First, you need to create some metadata 39 00:01:30,651 --> 00:01:32,010 -这将允许你的数据集 +这将允许其他人在 hub 上 that will allow your dataset 40 00:01:32,010 --> 00:01:34,590 -其他人可以在集线器上轻松找到。 +轻松找到你的数据集。 to be easily found by others on the hub. 41 00:01:34,590 --> 00:01:35,670 -你可以创建此元数据 +你可以使用数据集标记应用程序 You can create this metadata 42 00:01:35,670 --> 00:01:37,860 -使用数据集标记应用程序, +创建此元数据, using the datasets tagging application, 43 00:01:37,860 --> 00:01:40,620 -我们将在视频说明中链接到它。 +我们将在视频说明信息中包含它的链接。 which we'll link to in the video description. 44 @@ -230,12 +230,12 @@ and we provide a template 47 00:01:45,240 --> 00:01:47,090 -我们还将在视频中链接到。 +相关链接也会包含在下面的视频信息内容中。 that we'll also link to in the video. 48 00:01:48,480 --> 00:01:50,280 -一旦你的数据集在集线器上, +一旦你的数据集在 hub 上, And once your dataset is on the hub, 49 @@ -245,12 +245,12 @@ you can load it using the trusty load_dataset function. 50 00:01:53,400 --> 00:01:55,015 -只需提供你的存储库的名称 +只需提供你的仓库的名称 Just provide the name of your repository 51 00:01:55,015 --> 00:01:57,843 -和一个 data_files 参数,你就可以开始了。 +和一个 data_files 参数,你就可以开始使用了。 and a data_files argument, and you're good to go. 52 diff --git a/subtitles/zh-CN/41_text-embeddings-&-semantic-search.srt b/subtitles/zh-CN/41_text-embeddings-&-semantic-search.srt index 0b8958068..4c43f0e18 100644 --- a/subtitles/zh-CN/41_text-embeddings-&-semantic-search.srt +++ b/subtitles/zh-CN/41_text-embeddings-&-semantic-search.srt @@ -20,17 +20,17 @@ represent text as embedding vectors 5 00:00:12,810 --> 00:00:15,420 -以及如何使用这些向量来查找相似文档 +以及在语料库中如何使用这些向量 and how these vectors can be used to find similar documents 6 00:00:15,420 --> 00:00:16,293 -在语料库中。 +来查找相似文档。 in a corpus. 7 00:00:17,730 --> 00:00:19,890 -文本嵌入只是一种奇特的说法 +文本嵌入只是一种时髦的说法 Text embeddings are just a fancy way of saying 8 @@ -40,7 +40,7 @@ that we can represent text as an array of numbers 9 00:00:22,170 --> 00:00:23,640 -称为矢量。 +称之为矢量。 called a vector. 10 @@ -65,12 +65,12 @@ to the encoder and get three vectors as the output. 14 00:00:34,830 --> 00:00:37,050 -读课文,我们可以看到遛狗 +读一下输入文本,我们可以看到 walking the dog Reading the text, we can see that walking the dog 15 00:00:37,050 --> 00:00:39,450 -好像跟遛猫最像, +和 walking the cat 从字面上感觉很像, seems to be most similar to walking the cat, 16 @@ -85,12 +85,12 @@ The trick to do the comparison 18 00:00:44,040 --> 00:00:45,630 -是计算相似性度量 +是在每对嵌入向量之间 is to compute a similarity metric 19 00:00:45,630 --> 00:00:48,210 -在每对嵌入向量之间。 +计算相似性度量。 between each pair of embedding vectors. 20 @@ -100,12 +100,12 @@ These vectors usually live in a very high-dimensional space, 21 00:00:51,120 --> 00:00:53,190 -所以相似性度量可以是任何可以衡量的东西 +所以相似性度量可以是任何可用于 so a similarity metric can be anything that measures 22 00:00:53,190 --> 00:00:55,740 -矢量之间的某种距离。 +衡量矢量之间的某种距离的属性。 some sort of distance between vectors. 23 @@ -125,7 +125,7 @@ to measure how close they are. 26 00:01:02,610 --> 00:01:05,250 -在这个例子中,我们的嵌入向量存在于 3D 中 +在这个例子中,我们的嵌入向量存在于三维空间中 In this example, our embedding vectors live in 3D 27 @@ -135,7 +135,7 @@ and we can see that the orange and Grey vectors 28 00:01:07,110 --> 00:01:09,560 -彼此靠近并且具有较小的角度。 +彼此靠近并且具有更小的角度。 are close to each other and have a smaller angle. 29 @@ -150,12 +150,12 @@ is that Transformer models like BERT will actually return 31 00:01:15,180 --> 00:01:16,983 -每个标记一个嵌入向量。 +每个词元一个嵌入向量。 one embedding vector per token. 32 00:01:17,880 --> 00:01:20,700 -例如在句子中,“我带我的狗去散步,” +例如在句子中,“I took my dog for a walk,” For example in the sentence, "I took my dog for a walk," 33 @@ -180,58 +180,58 @@ and each vector has 384 dimensions. 37 00:01:33,750 --> 00:01:36,210 -但我们真正想要的是一个单一的嵌入向量 +但我们真正想要的是对于每个句子 But what we really want is a single embedding vector 38 00:01:36,210 --> 00:01:37,353 -对于每个句子。 +对应一个单一的嵌入向量。 for each sentence. 39 00:01:38,940 --> 00:01:42,060 -为了解决这个问题,我们可以使用一种称为池化的技术。 +为了解决这个问题,我们可以使用一种称为 pooling 的技术。 To deal with this, we can use a technique called pooling. 40 00:01:42,060 --> 00:01:43,050 -最简单的池化方法 +最简单的 pooling 方法 The simplest pooling method 41 00:01:43,050 --> 00:01:44,520 -就是把令牌嵌入 +就是把词元嵌入 is to just take the token embedding 42 00:01:44,520 --> 00:01:46,203 -特殊的 CLS 令牌。 +特殊的 CLS 词元。 of the special CLS token. 43 00:01:47,100 --> 00:01:49,650 -或者,我们可以对令牌嵌入进行平均 +或者,我们可以对词元嵌入进行平均 Alternatively, we can average the token embeddings 44 00:01:49,650 --> 00:01:52,500 -这就是所谓的均值池,这就是我们在这里所做的。 -which is called mean pooling and this is what we do here. +这就是所谓的 mean_pooling,也就是我们在这里所做的。 +which is called mean_pooling and this is what we do here. 45 00:01:53,370 --> 00:01:55,800 -使用均值池是我们唯一需要确保的事情 -With mean pooling the only thing we need to make sure +使用 mean_pooling 时我们唯一需要确保的事情 +With mean_pooling the only thing we need to make sure 46 00:01:55,800 --> 00:01:58,410 -是我们不在平均值中包含填充标记, +是我们不在平均值中包含 padding 词元, is that we don't include the padding tokens in the average, 47 00:01:58,410 --> 00:02:01,860 -这就是为什么你可以看到这里使用的注意力掩码。 -which is why you can see the attention mask being used here. +这就是为什么你可以看到这里用到了 attention_mask。 +which is why you can see the attention_mask being used here. 48 00:02:01,860 --> 00:02:05,100 @@ -250,12 +250,12 @@ And once we have our sentence embeddings, 51 00:02:09,810 --> 00:02:11,730 -我们可以计算余弦相似度 +我们可以针对每对向量 we can compute the cosine similarity 52 00:02:11,730 --> 00:02:13,113 -对于每对向量。 +计算余弦相似度。 for each pair of vectors. 53 @@ -265,12 +265,12 @@ In this example we use the function from scikit-learn 54 00:02:16,350 --> 00:02:19,140 -你可以看到 “I tok my dog for a walk” 这句话 +你可以看到 “I took my dog for a walk” 这句话 and you can see that the sentence "I took my dog for a walk" 55 00:02:19,140 --> 00:02:22,140 -确实与 “我带我的猫去散步” 有很强的重叠。 +确实与 “I took my cat for a walk” 有很明显的重叠。 has indeed a strong overlap with "I took my cat for a walk". 56 @@ -285,22 +285,22 @@ We can actually take this idea one step further 58 00:02:27,180 --> 00:02:29,220 -通过比较问题之间的相似性 +通过比较问题和文档语料库 by comparing the similarity between a question 59 00:02:29,220 --> 00:02:31,170 -和文档语料库。 +之间的相似性。 and a corpus of documents. 60 00:02:31,170 --> 00:02:33,810 -例如,假设我们嵌入每个帖子 +例如,假设我们在 Hugging Face 论坛中 For example, suppose we embed every post 61 00:02:33,810 --> 00:02:35,430 -在 Hugging Face 论坛中。 +嵌入每个帖子。 in the Hugging Face forums. 62 @@ -325,12 +325,12 @@ because it allows us to compare queries with context. 66 00:02:47,040 --> 00:02:48,450 -创建语义搜索引擎 +使用 datasets 库 To create a semantic search engine 67 00:02:48,450 --> 00:02:51,030 -在数据集库中其实很简单。 +创建语义搜索引擎其实很简单。 is actually quite simple in the datasets library. 68 @@ -340,22 +340,22 @@ First we need to embed all the documents. 69 00:02:53,340 --> 00:02:56,070 -在这个例子中,我们取了一个小样本 +在这个例子中,我们取了 And in this example, we take a small sample 70 00:02:56,070 --> 00:02:57,780 -来自 SQUAD 数据集并应用 -from the SQUAD dataset and apply +一个来自 squad 数据集的小样本 +from the squad dataset and apply 71 00:02:57,780 --> 00:03:00,180 -与以前相同的嵌入逻辑。 +并按照与以前相同的嵌入逻辑使用。 the same embedding logic as before. 72 00:03:00,180 --> 00:03:02,280 -这为我们提供了一个名为嵌入的新列, +这为我们提供了一个名为 embeddings 的新列, This gives us a new column called embeddings, 73 @@ -370,12 +370,12 @@ Once we have our embeddings, 75 00:03:07,260 --> 00:03:10,200 -我们需要一种方法来为查询找到最近的邻居。 +我们需要一种方法来为查询找到最近的相邻数据。 we need a way to find nearest neighbors for a query. 76 00:03:10,200 --> 00:03:13,170 -数据集库提供了一个名为 FAISS 的特殊对象 +datasets 库提供了一个名为 FAISS 的特殊对象 The datasets library provides a special object called FAISS 77 @@ -395,7 +395,7 @@ we've now found the 3 most similar articles 80 00:03:21,870 --> 00:03:23,320 -这可能会存储答案。 +其中可能会包含答案。 which might store the answer. 81 diff --git a/subtitles/zh-CN/42_training-a-new-tokenizer.srt b/subtitles/zh-CN/42_training-a-new-tokenizer.srt index 2b0d28498..9b8bb0032 100644 --- a/subtitles/zh-CN/42_training-a-new-tokenizer.srt +++ b/subtitles/zh-CN/42_training-a-new-tokenizer.srt @@ -10,7 +10,7 @@ 3 00:00:08,700 --> 00:00:11,820 -训练分词器的目的是什么, +训练 tokenizer 的目的是什么, what is the purpose of training a tokenizer, 4 @@ -30,7 +30,7 @@ You will ask yourself the question, 7 00:00:20,677 --> 00:00:23,040 -“我应该训练一个新的分词器吗?”, +“我应该训练一个新的 tokenizer 吗?”, "Should I train a new tokenizer?", 8 @@ -40,37 +40,37 @@ when you plan to train a new model from scratch. 9 00:00:29,520 --> 00:00:34,020 -训练有素的分词器不适合你的语料库 +一个训练过的分词器会不适合你的语料库 A trained tokenizer would not be suitable for your corpus 10 00:00:34,020 --> 00:00:37,080 -如果你的语料库使用不同的语言, +如果你的语料库使用一个不同的语言, if your corpus is in a different language, 11 00:00:37,080 --> 00:00:42,060 -使用新字符,例如重音符号或大写字母, +使用新字符,比如重音符号或大写字母, uses new characters, such as accents or upper cased letters, 12 00:00:42,060 --> 00:00:47,060 -有特定的词汇,例如医学或法律, +有特定的词汇,例如医学的或法律的, has a specific vocabulary, for example medical or legal, 13 00:00:47,100 --> 00:00:49,050 -或使用不同的风格, +或使用一个不同的风格, or uses a different style, 14 00:00:49,050 --> 00:00:51,873 -例如,来自另一个世纪的语言。 +例如,来自另一个世纪时的语言。 a language from another century for example. 15 00:00:56,490 --> 00:00:58,320 -如果我接受训练的分词器 +如果我让 tokenizer 训练在 If I take the tokenizer trained on 16 @@ -85,22 +85,22 @@ and ignore its normalization step, 18 00:01:04,260 --> 00:01:07,650 -然后我们可以看到标记化操作 +然后我们就能看到 tokenization 操作 then we can see that the tokenization operation 19 00:01:07,650 --> 00:01:09,277 -关于英语句子, +在英语句子上, on the English sentence, 20 00:01:09,277 --> 00:01:12,480 -“这是一个适合我们分词器的句子”, +“这是一个适合我们 tokenizer 的句子”, "Here is a sentence adapted to our tokenizer", 21 00:01:12,480 --> 00:01:15,600 -产生一个相当令人满意的令牌列表, +产生一个相当令人满意的 token 列表, produces a rather satisfactory list of tokens, 22 @@ -110,12 +110,12 @@ in the sense that this sentence of eight words 23 00:01:18,510 --> 00:01:20,643 -被标记为九个标记。 +被标记成了九个 token 。 is tokenized into nine tokens. 24 00:01:22,920 --> 00:01:26,340 -另一方面,如果我使用相同的分词器 +另一方面,如果我使用相同的 tokenizer On the other hand, if I use this same tokenizer 25 @@ -125,37 +125,37 @@ on a sentence in Bengali, we see that 26 00:01:29,370 --> 00:01:33,690 -要么一个词被分成许多子标记, +要么一个词被分成许多子 token , either a word is divided into many sub tokens, 27 00:01:33,690 --> 00:01:36,270 -或者分词器不知道其中之一 +或者 tokenizer 不知道任何一个 or that the tokenizer does not know one of 28 00:01:36,270 --> 00:01:39,873 -unicode 字符并仅返回未知标记。 +unicode 字符并仅返回未知 token 。 the unicode characters and returns only an unknown token. 29 00:01:41,220 --> 00:01:44,970 -一个常用词被分成许多标记的事实 +一个常用词被分成许多 token 的事实 The fact that a common word is split into many tokens 30 00:01:44,970 --> 00:01:47,910 -可能会有问题,因为语言模型 +可能是问题的,因为语言模型 can be problematic, because language models 31 00:01:47,910 --> 00:01:51,903 -只能处理有限长度的令牌序列。 +只能处理有限长度的 token 序列。 can only handle a sequence of tokens of limited length. 32 00:01:52,830 --> 00:01:55,830 -过度拆分初始文本的分词器 +过度拆分初始文本的 tokenizer A tokenizer that excessively splits your initial text 33 @@ -165,7 +165,7 @@ may even impact the performance of your model. 34 00:01:59,760 --> 00:02:02,280 -未知的令牌也有问题, +未知的 token 也有问题, Unknown tokens are also problematic, 35 @@ -185,17 +185,17 @@ In this other example, we can see that 38 00:02:13,440 --> 00:02:17,100 -分词器替换包含字符的单词 +tokenizer 替换包含字符的单词 the tokenizer replaces words containing characters 39 00:02:17,100 --> 00:02:20,973 -带有重音和带有未知标记的大写字母。 +带有重音和带有未知 token 的大写字母。 with accents and capital letters with unknown tokens. 40 00:02:22,050 --> 00:02:24,770 -最后,如果我们再次使用这个分词器 +最后,如果我们再次使用这个 tokenizer Finally, if we use again this tokenizer 41 @@ -205,32 +205,32 @@ to tokenize medical vocabulary, we see again that 42 00:02:28,170 --> 00:02:31,800 -一个单词被分成许多子标记, +一个单词被分成许多子 token , a single word is divided into many sub tokens, 43 00:02:31,800 --> 00:02:34,803 -四个用于扑热息痛,四个用于咽炎。 +四个用于 "paracetamol" ,四个用于 "pharyngitis" 。 four for paracetamol, and four for pharyngitis. 44 00:02:37,110 --> 00:02:39,360 -当前使用的大多数分词器 +目前的大多数 tokenizer 被 Most of the tokenizers used by the current 45 00:02:39,360 --> 00:02:42,540 -需要训练最先进的语言模型 +最先进的语言模型使用的, 需要被训练 state of the art language models need to be trained 46 00:02:42,540 --> 00:02:45,360 -在与使用的语料库相似的语料库上 +在语料库上, 其与使用的语料库如要相似于 on a corpus that is similar to the one used 47 00:02:45,360 --> 00:02:47,463 -预训练语言模型。 +预训练语言模型的。 to pre-train the language model. 48 @@ -250,12 +250,12 @@ And the way to learn these rules and use them 51 00:02:56,160 --> 00:02:58,233 -取决于所选的分词器模型。 +取决于所选的 tokenizer 模型。 depends on the chosen tokenizer model. 52 00:03:00,630 --> 00:03:04,590 -因此,要训练一个新的分词器,首先需要 +因此,要训练一个新的 tokenizer ,首先需要 Thus, to train a new tokenizer, it is first necessary 53 @@ -265,7 +265,7 @@ to build a training corpus composed of raw texts. 54 00:03:08,910 --> 00:03:12,423 -然后,你必须为你的分词器选择一种架构。 +然后,你必须为你的 tokenizer 选择一种结构。 Then, you have to choose an architecture for your tokenizer. 55 @@ -275,12 +275,12 @@ Here there are two options. 56 00:03:15,900 --> 00:03:19,710 -最简单的是重用与那个相同的架构 +最简单的是重用与那个结构, 其相同于 The simplest is to reuse the same architecture as the one 57 00:03:19,710 --> 00:03:22,863 -另一个已经训练过的模型使用的分词器。 +另一个已经训练过的模型使用的 tokenizer 。 of a tokenizer used by another model already trained. 58 @@ -290,22 +290,22 @@ Otherwise it is also possible 59 00:03:25,980 --> 00:03:28,560 -完全设计你的分词器。 +彻底设计你的分词器。 to completely design your tokenizer. 60 00:03:28,560 --> 00:03:31,683 -但这需要更多的经验和关注。 +但这需要更多的经验和观察。 But it requires more experience and attention. 61 00:03:33,750 --> 00:03:36,660 -一旦选择了架构,你就可以 +一旦选择了结构,你就可以 Once the architecture is chosen, you can thus 62 00:03:36,660 --> 00:03:39,513 -在你构成的语料库上训练这个分词器。 +在你构建的语料库上训练这个 tokenizer 。 train this tokenizer on your constituted corpus. 63 @@ -315,7 +315,7 @@ Finally, the last thing that you need to do is to save 64 00:03:43,440 --> 00:03:46,443 -能够使用此分词器的学习规则。 +能够使用此 tokenizer 的学习规则。 the learned rules to be able to use this tokenizer. 65 @@ -340,7 +340,7 @@ this type of text is very specific, 69 00:04:02,386 --> 00:04:04,473 -并值得一个训练有素的分词器。 +并值得一个训练过的 tokenizer 。 and deserves a tokenizer trained on it. 70 @@ -360,12 +360,12 @@ For that we are going to use the method 73 00:04:13,747 --> 00:04:18,240 -“train_new_from_iterator”,所有快速分词器 +“train_new_from_iterator”,所有快速的 tokenizer "train_new_from_iterator" that all the fast tokenizers 74 00:04:18,240 --> 00:04:20,040 -图书馆有,因此, +在库中有的,因此, of the library have and thus, 75 @@ -380,7 +380,7 @@ This is the simplest method in our case 77 00:04:26,100 --> 00:04:28,983 -有一个适合 Python 代码的分词器。 +有一个适合 Python 代码的 tokenizer 。 to have a tokenizer adapted to Python code. 78 @@ -450,12 +450,12 @@ on our Python functions corpus, we can load 91 00:05:09,630 --> 00:05:12,351 -GPT-2 分词器架构。 +GPT-2 分词器结构。 the GPT-2 tokenizer architecture. 92 00:05:12,351 --> 00:05:16,560 -这里 old_tokenizer 不适应我们的语料库。 +这里 old_tokenizer 不适合我们的语料库。 Here old_tokenizer is not adapted to our corpus. 93 @@ -470,12 +470,12 @@ one more line to train it on our new corpus. 95 00:05:21,780 --> 00:05:24,720 -大多数标记化的共同论点 +有一点很普遍的是大多数 分词化 An argument that is common to most of the tokenization 96 00:05:24,720 --> 00:05:28,980 -目前使用的算法是词汇表的大小。 +算法, 目前使用的是根据词汇表的大小。 algorithms used at the moment is the size of the vocabulary. 97 @@ -490,12 +490,12 @@ Finally, once the training is finished, 99 00:05:35,760 --> 00:05:38,850 -我们只需要在本地保存我们的新分词器, +我们只需要在本地保存我们的新 tokenizer , we just have to save our new tokenizer locally, 100 00:05:38,850 --> 00:05:41,730 -或将其发送到集线器以便能够重用它 +或将其发送到 hub 以便能够重用它 or send it to the hub to be able to reuse it 101 @@ -510,12 +510,12 @@ Finally, let's see together on an example if it was useful 103 00:05:48,990 --> 00:05:53,073 -重新训练一个类似于 GPT-2 的分词器。 +重新训练一个类似于 GPT-2 的 tokenizer 。 to re-train a tokenizer similar to GPT-2 one. 104 00:05:55,110 --> 00:05:57,660 -使用 GPT-2 的原始分词器 +使用 GPT-2 的原始 tokenizer With the original tokenizer of GPT-2 105 @@ -525,12 +525,12 @@ we see that all spaces are isolated, 106 00:06:00,330 --> 00:06:01,920 -和方法名称 randn, +和方法名称 "randn", and the method name randn, 107 00:06:01,920 --> 00:06:04,833 -在 Python 代码中比较常见,分为两部分。 +在 Python 代码中相对比较常见,分为两部分。 relatively common in Python code, is split in two. 108 @@ -540,12 +540,12 @@ With our new tokenizer, single and double indentations 109 00:06:09,060 --> 00:06:10,890 -已经学习和方法 randn +已经学习和方法 "randn" have been learned and the method randn 110 00:06:10,890 --> 00:06:13,770 -被代币化为一个代币。 +被分词化为一个 token 。 is tokenized into one token. 111 diff --git a/subtitles/zh-CN/43_why-are-fast-tokenizers-called-fast.srt b/subtitles/zh-CN/43_why-are-fast-tokenizers-called-fast.srt index 26a06bbaf..2e113cf9c 100644 --- a/subtitles/zh-CN/43_why-are-fast-tokenizers-called-fast.srt +++ b/subtitles/zh-CN/43_why-are-fast-tokenizers-called-fast.srt @@ -5,7 +5,7 @@ 2 00:00:05,340 --> 00:00:08,460 -- 为什么快速分词器被称为快速? +- 为什么快速 tokenizer 被称 "快速"? - Why are fast tokenizers called fast? 3 @@ -15,12 +15,12 @@ In this video, we'll see exactly how much faster, 4 00:00:10,950 --> 00:00:13,800 -另外,比较了所谓的快速组织者 +另外,所谓的快速 tokenizer 被比较 also, so-called fast organizers are compared 5 00:00:13,800 --> 00:00:15,153 -给他们慢的同行。 +和他们慢的参照物。 to their slow counterparts. 6 @@ -40,12 +40,12 @@ We'll see how long it takes for the fast and slow versions 9 00:00:25,890 --> 00:00:28,143 -一个 BERT 分词器来处理它们。 +一个 BERT tokenizer 来处理它们。 of a BERT tokenizer to process them all. 10 00:00:29,670 --> 00:00:31,380 -我们定义我们的快速和慢速令牌组织者 +我们定义我们的快速和慢速的 tokenizer We define our fast and slow token organizer 11 @@ -55,37 +55,37 @@ using the AutoTokenizer API. 12 00:00:33,717 --> 00:00:37,110 -快速分词器在可用时是默认的。 +快速 tokenizer 在可用时是默认的。 The fast tokenizer is a default when available. 13 00:00:37,110 --> 00:00:40,443 -所以我们通过,使用_fast=False 来定义慢速的。 +所以我们通过设置 use_fast=False 来定义成慢速。 So we pass along, use_fast=False to define the slow one. 14 00:00:41,430 --> 00:00:43,530 -在笔记本中,我们可以为执行计时 +在笔记本中,我们可以计时执行 In a notebook, we can time the execution 15 00:00:43,530 --> 00:00:46,800 -本身带有时间魔法命令,就像这样。 +让其本身附带时间魔术命令,就像这样。 of itself with a time magic command, like this. 16 00:00:46,800 --> 00:00:49,350 处理整个数据集快四倍 -Processing the whole data set is four times faster +Processing the whole dataset is four times faster 17 00:00:49,350 --> 00:00:50,970 -使用快速分词器。 +使用快速 tokenizer 。 with a fast tokenizer. 18 00:00:50,970 --> 00:00:54,000 -确实更快,但不是很令人印象深刻。 +确实更快,但不够吸引人。 That quicker indeed, but not very impressive. 19 @@ -95,22 +95,22 @@ This is because we passed along the texts 20 00:00:55,380 --> 00:00:57,240 -一次一个到分词器。 +一次一个到 tokenizer 。 to the tokenizer one at a time. 21 00:00:57,240 --> 00:00:59,730 -这是快速组织者的常见错误 +这是快速 tokenizer 的常见错误 This is a common mistake to do with fast organizers 22 00:00:59,730 --> 00:01:02,550 -由 Rust 支持,因此能够确定优先级 +由 Rust 支持,因此能够确定优先化 which are backed by Rust, and thus able to prioritize 23 00:01:02,550 --> 00:01:05,370 -多个文本的标记化。 +多个文本的 tokenization 。 the tokenization of multiple texts. 24 @@ -130,7 +130,7 @@ with just one container, it's very inefficient. 27 00:01:13,140 --> 00:01:15,810 -为了释放我们快速分词器的全部速度, +为了释放我们快速 tokenizer 的全部速度, To unleash the full speed of our fast tokenizers, 28 @@ -145,41 +145,41 @@ with the batched=True argument of the map method. 30 00:01:22,620 --> 00:01:25,950 -现在这些都是令人印象深刻的结果,所以快速分词器 +现在这些都是令人印象深刻的结果,所以快速 tokenizer Now those are impressive results, so the fast tokenizer 31 00:01:25,950 --> 00:01:28,410 -处理需要 4 秒的数据集需要 12 秒 +需要 12 秒处理, 而需要 4 takes 12 second to process the dataset that takes four 32 00:01:28,410 --> 00:01:30,093 -分钟到慢分词器。 +分钟, 对于慢速 tokenizer 。 minute to the slow tokenizer. 33 00:01:31,440 --> 00:01:33,510 -总结此表中的结果, +结果总结于此表中, Summarizing the results in this table, 34 00:01:33,510 --> 00:01:36,630 -你可以看到为什么我们快速调用这些分词器。 +你可以看到为什么我们称 tokenizer 为快速。 you can see why we have called those tokenizers fast. 35 00:01:36,630 --> 00:01:38,760 -这仅用于标记化文本。 +这仅用于分词化文本。 And this is only for tokenizing texts. 36 00:01:38,760 --> 00:01:40,710 -如果你需要训练一个新的分词器, +如果你需要训练一个新的 tokenizer , If you ever need to train a new tokenizer, 37 00:01:40,710 --> 00:01:42,523 -他们也很快做到这一点。 +它们也很快做到这一点。 they do this very quickly too. diff --git a/subtitles/zh-CN/44_fast-tokenizer-superpowers.srt b/subtitles/zh-CN/44_fast-tokenizer-superpowers.srt index 70ea63f74..f008cfb0c 100644 --- a/subtitles/zh-CN/44_fast-tokenizer-superpowers.srt +++ b/subtitles/zh-CN/44_fast-tokenizer-superpowers.srt @@ -1,16 +1,16 @@ 1 00:00:05,010 --> 00:00:06,270 -- 快速分词器 +- 快速 tokenizer - The fast tokenizers 2 00:00:06,270 --> 00:00:08,580 -变形金刚图书馆的速度很快, +在 transformers 库中的, 速度很快, of the Transformers library are fast, 3 00:00:08,580 --> 00:00:11,490 -但他们也实现了非常有用的功能 +同时他们也实现了非常有用的功能 but they also implement features that will be super useful 4 @@ -30,7 +30,7 @@ First, let's have a look 7 00:00:18,650 --> 00:00:21,690 -在分词器的通常输出中。 +在 tokenizer 的通常输出中。 at the usual output of a tokenizer. 8 @@ -50,7 +50,7 @@ For instance, 11 00:00:29,010 --> 00:00:31,856 -这里两个句子的标记化是相同的 +这里两个句子的分词化是相同的 here the tokenization is the same for the two sentences 12 @@ -65,17 +65,17 @@ Just having the input IDs is thus not enough 14 00:00:39,150 --> 00:00:42,330 -如果我们想将一些标记与一段文本相匹配, +如果我们想将一些 token 与一段文本相匹配, if we want to match some tokens with a span of text, 15 00:00:42,330 --> 00:00:43,320 -我们需要做的事情 +我们将需要做的事情 something we'll need to do 16 00:00:43,320 --> 00:00:46,111 -例如,在处理问题回答时。 +例如,在处理问答时。 when tackling question answering, for instance. 17 @@ -85,23 +85,23 @@ It's also difficult to know 18 00:00:47,592 --> 00:00:50,850 -当两个标记是否属于同一个词时。 +当两个 token 是否属于同一个词时。 when two tokens belong to the same word or not. 19 00:00:50,850 --> 00:00:52,860 -当你只看输出时看起来很容易 +是容易的, 当你只看 It looks easy when you just look at the output 20 00:00:52,860 --> 00:00:55,650 -一个 BERT 分词器,我们只需要看一下 +一个 BERT 分词器的输出,我们只需要看一下 of a BERT tokenizer where we just need to look 21 00:00:55,650 --> 00:00:56,779 -对于散列散列。 -for the hash hash. +对于 ## 。 +for the ##. 22 00:00:56,779 --> 00:00:59,040 @@ -110,7 +110,7 @@ But other tokenizers have different ways 23 00:00:59,040 --> 00:01:00,987 -标记部分单词。 +分词化部分单词。 to tokenize parts of words. 24 @@ -120,7 +120,7 @@ For instance, RoBERTa adds this special G symbol 25 00:01:04,470 --> 00:01:06,491 -在单词的开头标记标记 +在单词的开头 token 标记 to mark the tokens at the beginning of the word 26 @@ -135,12 +135,12 @@ for the same purpose. 28 00:01:11,150 --> 00:01:14,760 -值得庆幸的是,快速分词器会跟踪这个词 +值得庆幸的是,快速 tokenizer 会跟踪这个词 Thankfully, the fast tokenizers keep track of the word 29 00:01:14,760 --> 00:01:16,230 -每个令牌来自, +每个 token 来自, each token comes from, 30 @@ -155,22 +155,22 @@ The output is not necessarily clear, 32 00:01:21,870 --> 00:01:24,076 -但像这样聚集在一张漂亮的桌子上, +但像这样聚集在一张漂亮的表格上, but assembled together in a nice table like this, 33 00:01:24,076 --> 00:01:26,853 -我们可以查看每个标记的单词位置。 +我们可以查看每个 token 的单词位置。 we can look at the word position for each token. 34 00:01:27,930 --> 00:01:30,220 -更好的是,快速标记器会跟踪 +更好的是,快速 tokenizer 会跟踪 Even better, the fast tokenizers keep track 35 00:01:30,220 --> 00:01:33,198 -每个标记来自的字符范围, +每个 token 来自的字符范围, of the span of characters each token comes from, 36 @@ -180,7 +180,7 @@ and we can get them when calling it on one 37 00:01:35,760 --> 00:01:37,221 -或通过添加几个文本 +或几个文本通过添加 or several text by adding 38 @@ -190,13 +190,13 @@ the return_offsets_mapping=True argument. 39 00:01:40,470 --> 00:01:42,312 -在这种情况下,我们可以看到我们是如何跳仓的 +在这种情况下,我们可以看到我们是如何变换位置的 In this instance, we can see how we jump positions 40 00:01:42,312 --> 00:01:45,650 -在 hash hash token 和 super token 之间, -between the hash hash token and the super token, +在 ## token 和 super token 之间, +between the ## token and the super token, 41 00:01:45,650 --> 00:01:49,992 @@ -205,7 +205,7 @@ because of the multiple spaces in the initial sentence. 42 00:01:49,992 --> 00:01:52,110 -为了实现这一点,快速分词器 +为了实现这一点,快速 tokenizer To enable this, the fast tokenizers 43 @@ -215,27 +215,27 @@ store additional information at each step 44 00:01:54,270 --> 00:01:55,440 -他们的内部管道。 +他们的内部管线( pipeline )。 of their internal pipeline. 45 00:01:55,440 --> 00:01:57,951 -该内部管道包括规范化, +该内部管线包括规范化, That internal pipeline consists of normalization, 46 00:01:57,951 --> 00:02:00,360 -我们对文本进行一些清洁, +我们对文本进行一些整理, where we apply some cleaning to the text, 47 00:02:00,360 --> 00:02:02,621 -喜欢小写或删除口音; +比如小写或删除口音; like lower casing or removing the accents; 48 00:02:02,621 --> 00:02:04,088 -预标记化, +预分词化, pre-tokenization, 49 @@ -245,12 +245,12 @@ which is where we split the texts into words; 50 00:02:06,530 --> 00:02:09,360 -然后我们应用分词器的模型, +然后我们应用 tokenizer 的模型, then we apply the model of the tokenizer, 51 00:02:09,360 --> 00:02:11,725 -这是单词被分成标记的地方, +这是单词被分成 token 的地方, which is where the words are split into tokens, 52 @@ -260,22 +260,22 @@ before finally doing the post processing, 53 00:02:13,748 --> 00:02:16,023 -添加特殊标记的地方。 +添加特殊 token 的地方。 where special tokens are added. 54 00:02:17,100 --> 00:02:19,050 -从管道的开始到结束, +从管线的开始到结束, From the beginning to the end of the pipeline, 55 00:02:19,050 --> 00:02:21,390 -标记器跟踪每个文本范围 +tokenizer 跟踪每个文本范围 the tokenizer keeps track of each span of text 56 00:02:21,390 --> 00:02:23,853 -对应于每个单词,然后是每个标记。 +对应于每个单词,然后是每个 token 。 that corresponds to each word, then each token. 57 @@ -295,12 +295,12 @@ when doing masked language modeling 60 00:02:29,549 --> 00:02:32,407 -一种获得最先进结果的变体 +一种获得最 SOTA 的变体 one variation that gets state-of-the-art results 61 00:02:32,407 --> 00:02:35,040 -是屏蔽给定单词的所有标记 +是屏蔽给定单词的所有 token is to mask all the tokens of a given word 62 @@ -325,7 +325,7 @@ we'll need to convert the labels we have on words, 66 00:02:45,090 --> 00:02:47,250 -每个标记上的标签。 +每个 token 上的标签。 to labels on each tokens. 67 @@ -340,7 +340,7 @@ it will be super useful when we need to convert 69 00:02:50,610 --> 00:02:53,436 -将句子中的标记位置转换为一段文本, +将句子中的 token 位置转换为一段文本, token positions in a sentence into a span of text, 70 @@ -350,17 +350,17 @@ which we'll need to know when we're looking 71 00:02:55,800 --> 00:02:56,813 -在回答问题时 +在问答时 at question answering 72 00:02:56,813 --> 00:02:58,680 -或者在对相应的标记进行分组时 +或者在对相应的 token 进行分组时 or when grouping the tokens corresponding 73 00:02:58,680 --> 00:03:01,023 -到令牌分类中的同一实体。 +到 token 分类中的同一实体。 to the same entity in token classification. 74 diff --git a/subtitles/zh-CN/45_inside-the-token-classification-pipeline-(pytorch).srt b/subtitles/zh-CN/45_inside-the-token-classification-pipeline-(pytorch).srt index 4f9310845..22106ec0e 100644 --- a/subtitles/zh-CN/45_inside-the-token-classification-pipeline-(pytorch).srt +++ b/subtitles/zh-CN/45_inside-the-token-classification-pipeline-(pytorch).srt @@ -20,27 +20,27 @@ 5 00:00:06,210 --> 00:00:08,283 -在令牌分类管道内。 +在 token 分类管线( pipeline )内。 inside the token classification pipeline. 6 00:00:10,080 --> 00:00:11,580 -在管道视频中, +在有关管线视频中, In the pipeline video, 7 00:00:11,580 --> 00:00:13,320 -我们研究了不同的应用 +我们学习了不同的应用 we looked at the different applications 8 00:00:13,320 --> 00:00:15,960 -Transformers 库支持开箱即用, +transformers 库支持开箱即用的, the Transformers library supports out of the box, 9 00:00:15,960 --> 00:00:18,780 -其中之一是令牌分类, +其中之一是 token 分类, one of them being token classification, 10 @@ -55,17 +55,17 @@ whether they correspond to a person, an organization 12 00:00:24,510 --> 00:00:25,353 -或位置。 +或一个位置。 or a location. 13 00:00:26,670 --> 00:00:28,920 -我们甚至可以将相应的标记组合在一起 +我们甚至可以将相应的 token 组合在一起 We can even group together the tokens corresponding 14 00:00:28,920 --> 00:00:32,040 -到同一个实体,例如所有令牌 +到同一个实体,例如所有 token to the same entity, for instance all the tokens 15 @@ -75,12 +75,12 @@ that formed the word Sylvain here, or Hugging and Face. 16 00:00:37,290 --> 00:00:40,230 -令牌分类管道的工作方式相同 + token 分类管线的工作方式相同 The token classification pipeline works the same way 17 00:00:40,230 --> 00:00:42,630 -作为我们研究的文本分类管道 +和我们研究的文本分类的管线 as the text classification pipeline we studied 18 @@ -95,7 +95,7 @@ There are three steps. 20 00:00:45,930 --> 00:00:49,623 -标记化、模型和后处理。 +分词化、模型和后处理。 The tokenization, the model, and the postprocessing. 21 @@ -105,12 +105,12 @@ The first two steps are identical 22 00:00:52,530 --> 00:00:54,630 -到文本分类管道, +到文本分类管线, to the text classification pipeline, 23 00:00:54,630 --> 00:00:57,300 -除了我们使用自动标记分类模型 +除了我们使用 auto (自动) 的 token 分类模型 except we use an auto token classification model 24 @@ -120,7 +120,7 @@ instead of a sequence classification one. 25 00:01:00,150 --> 00:01:03,720 -我们标记我们的文本,然后将其提供给模型。 +我们分词化我们的文本,然后将其提供给模型。 We tokenize our text then feed it to the model. 26 @@ -140,13 +140,13 @@ for each of the possible nine labels 29 00:01:10,770 --> 00:01:13,983 -对于句子中的每个标记,此处为 19。 +对于句子中的每个 token ,此处为 19。 for every token in the sentence, here 19. 30 00:01:15,300 --> 00:01:18,090 -与 Transformers 库的所有其他模型一样, -Like all the other models of the Transformers library, +与 transformers 库的所有其他模型一样, +Like all the other models of the transformers library, 31 00:01:18,090 --> 00:01:19,830 @@ -155,12 +155,12 @@ our model outputs logits, 32 00:01:19,830 --> 00:01:23,073 -我们使用 SoftMax 将其转化为预测。 +我们使用 SoftMax 将其转化为预测值。 which we turn into predictions by using a SoftMax. 33 00:01:23,940 --> 00:01:26,190 -我们还获得了每个标记的预测标签 +我们还获得了每个 token 的预测标签 We also get the predicted label for each token 34 @@ -195,7 +195,7 @@ in its id2label field. 40 00:01:37,740 --> 00:01:41,430 -使用它,我们可以将每个标记映射到其相应的标签。 +使用它,我们可以将每个 token 映射到其相应的标签。 Using it, we can map every token to its corresponding label. 41 @@ -250,7 +250,7 @@ if you don't know about them already. 51 00:02:00,300 --> 00:02:02,280 -然后,遍历每个标记 +然后,遍历每个 token Then, looping through each token 52 @@ -265,12 +265,12 @@ we can build the list of results we got 54 00:02:06,120 --> 00:02:07,320 -用我们的第一条管道。 +用我们的第一条管线。 with our first pipeline. 55 00:02:08,460 --> 00:02:10,560 -最后一步是将标记组合在一起 +最后一步是将 token 组合在一起 The last step is to group together tokens 56 @@ -290,7 +290,7 @@ I-PER and B-PER, for instance. 59 00:02:18,450 --> 00:02:20,100 -它让我们知道一个令牌是否是 +它让我们知道一个 token 是否是 It allows us to know if a token is 60 @@ -305,7 +305,7 @@ Note, that there are two ways of labeling used 62 00:02:25,350 --> 00:02:26,850 -用于令牌分类。 +用于 token 分类。 for token classification. 63 diff --git a/subtitles/zh-CN/46_inside-the-token-classification-pipeline-(tensorflow).srt b/subtitles/zh-CN/46_inside-the-token-classification-pipeline-(tensorflow).srt index e216a08ff..6cab3657c 100644 --- a/subtitles/zh-CN/46_inside-the-token-classification-pipeline-(tensorflow).srt +++ b/subtitles/zh-CN/46_inside-the-token-classification-pipeline-(tensorflow).srt @@ -10,12 +10,12 @@ 3 00:00:06,143 --> 00:00:08,133 -在令牌分类管道内。 +在 token 分类管线(pipeline)内。 inside the token classification pipeline. 4 00:00:09,780 --> 00:00:11,430 -在管道视频中, +在有关管线的视频中, In the pipeline video, 5 @@ -25,12 +25,12 @@ we looked at the different applications 6 00:00:13,230 --> 00:00:16,050 -Transformers 库支持开箱即用。 -the Transformers library supports out of the box. +transformers 库支持开箱即用的。 +the transformers library supports out of the box. 7 00:00:16,050 --> 00:00:18,660 -其中之一是令牌分类。 +其中之一是 token 分类。 One of them being token classification. 8 @@ -50,7 +50,7 @@ an organization, or location. 11 00:00:27,690 --> 00:00:29,250 -我们甚至可以将令牌组合在一起 +我们甚至可以将 token 组合在一起 We can even group together the tokens 12 @@ -60,7 +60,7 @@ corresponding to the same entity. 13 00:00:31,320 --> 00:00:34,890 -例如,这里构成单词 Sylvain 的所有标记 +例如,这里构成单词 Sylvain 的所有 token For instance, all the tokens that form the word Sylvain here 14 @@ -70,12 +70,12 @@ or Hugging and Face. 15 00:00:37,320 --> 00:00:39,720 -因此,令牌分类管道 +因此,token 分类管线 So, token classification pipeline 16 00:00:39,720 --> 00:00:42,480 -与文本分类管道的工作方式相同 +与文本分类管线的工作方式相同 works the same way as a text classification pipeline 17 @@ -90,7 +90,7 @@ There are three steps. 19 00:00:46,500 --> 00:00:50,043 -标记化、模型和后处理。 +分词化、模型和后处理。 Tokenization, the model, and the post processing. 20 @@ -105,17 +105,17 @@ to the text classification pipeline, 22 00:00:55,230 --> 00:00:58,230 -除了我们使用自动标记分类模型 +除了我们使用 auto 的 token 分类模型 except we use an auto token classification model 23 00:00:58,230 --> 00:01:00,303 -而不是序列分类。 +而不是分类。 instead of a sequence classification one. 24 00:01:01,560 --> 00:01:04,593 -我们标记我们的文本,然后将其提供给模型。 +我们分词化我们的文本,然后将其提供给模型。 We tokenize our text, then feed it to the model. 25 @@ -135,7 +135,7 @@ we get one number for each of the possible nine levels 28 00:01:12,270 --> 00:01:14,250 -对于句子中的每个标记。 +对于句子中的每个 token 。 for every token in the sentence. 29 @@ -145,12 +145,12 @@ Here, 19. 30 00:01:17,070 --> 00:01:19,710 -与 Transformers 库的所有其他模型一样, -Like all the other models of the Transformers library, +与 transformers 库的所有其他模型一样, +Like all the other models of the transformers library, 31 00:01:19,710 --> 00:01:22,560 -我们的模型输出我们需要转换的逻辑 +我们的模型输出我们需要转换的 logits our model outputs logits which we need to turn 32 @@ -160,7 +160,7 @@ into predictions by using a SoftMax. 33 00:01:25,830 --> 00:01:28,170 -我们还获得了每个标记的预测标签 +我们还获得了每个 token 的预测标签 We also get the predicted label for each token 34 @@ -195,12 +195,12 @@ in its id2label field. 40 00:01:42,090 --> 00:01:45,600 -使用它,我们可以将每个标记映射到其相应的标签。 +使用它,我们可以将每个 token 映射到其相应的标签。 Using it, we can map every token to its corresponding label. 41 00:01:45,600 --> 00:01:48,630 -标签 O 对应 “无实体” +标签 O 对应 “no entity” (没有实体) The label O corresponds to "no entity" 42 @@ -235,7 +235,7 @@ We'll need to use the offset mapping 48 00:01:59,880 --> 00:02:01,110 -分词器得到那些。 +对于 tokenizer 以得到那些。 of the tokenizer to get those. 49 @@ -250,7 +250,7 @@ if you don't know about them already. 51 00:02:05,340 --> 00:02:06,990 -然后,遍历每个标记 +然后,遍历每个 token Then, looping through each token 52 @@ -265,12 +265,12 @@ we can build the list of results 54 00:02:10,590 --> 00:02:12,140 -我们得到了我们的第一条管道。 +我们得到了我们的第一条管线。 we got with our first pipeline. 55 00:02:13,650 --> 00:02:15,840 -最后一步是将标记组合在一起 +最后一步是将 token 组合在一起 The last step is to group together tokens 56 @@ -290,7 +290,7 @@ I-PER and B-PER for instance. 59 00:02:23,940 --> 00:02:25,530 -它让我们知道一个令牌是否 +它让我们知道一个 token 是否 It allows us to know if a token 60 @@ -305,7 +305,7 @@ Note that there are two ways 62 00:02:29,850 --> 00:02:32,490 -用于标记分类的标签。 +用于 token 分类的标签。 of labeling used for token classification. 63 diff --git a/subtitles/zh-CN/47_inside-the-question-answering-pipeline-(pytorch).srt b/subtitles/zh-CN/47_inside-the-question-answering-pipeline-(pytorch).srt index 5ce8d0e78..69ebc63b6 100644 --- a/subtitles/zh-CN/47_inside-the-question-answering-pipeline-(pytorch).srt +++ b/subtitles/zh-CN/47_inside-the-question-answering-pipeline-(pytorch).srt @@ -1,21 +1,21 @@ 1 00:00:04,230 --> 00:00:07,699 -- 让我们来看看问答管道的内部情况。 +- 让我们来看看问答管线的内部情况。 - Let's have a look inside the question answering pipeline. 2 00:00:07,699 --> 00:00:10,680 -问答管道可以提取答案 +问答管线可以提取答案 The question answering pipeline can extracts answers 3 00:00:10,680 --> 00:00:14,190 -从给定的上下文或文本段落中提出问题, +对问题, 从给定的上下文或文本段落中, to questions from a given context or passage of text, 4 00:00:14,190 --> 00:00:16,540 -就像变形金刚回购自述文件的这一部分。 +就像 transformers 仓库的 README 文档的这部分。 like this part of the transformers repo README. 5 @@ -25,7 +25,7 @@ It also works for very long contexts, 6 00:00:20,310 --> 00:00:23,850 -即使答案在最后,就像这个例子一样。 +即使答案很靠后,就像这个例子一样。 even if the answer is at the very end, like in this example. 7 @@ -35,12 +35,12 @@ In this video, we will see why. 8 00:00:26,820 --> 00:00:29,460 -问答管道遵循相同的步骤 +问答管线遵循相同的步骤 The question answering pipeline follows the same steps 9 00:00:29,460 --> 00:00:31,050 -与其他管道一样: +与其他 pipeline 一样: as the other pipelines: 10 @@ -55,7 +55,7 @@ fed to the model then some post-processing is applied. 12 00:00:37,955 --> 00:00:41,730 -令牌化和模型步骤应该很熟悉。 +分词化和模型步骤应该很熟悉。 The tokenization and model steps should be familiar. 13 @@ -75,12 +75,12 @@ but one key difference with text classification 16 00:00:49,392 --> 00:00:52,980 -是我们的模型输出两个名为 start logits 的张量 +是我们的模型输出两个张量, 名为 start logits is that our model outputs two tensors named start logits 17 00:00:52,980 --> 00:00:54,570 -并结束登录。 +和 end logits 。 and end logits. 18 @@ -95,7 +95,7 @@ Well, this is the way the model finds the answer 20 00:00:57,930 --> 00:00:58,803 -到这个问题。 +对这个问题。 to the question. 21 @@ -105,27 +105,27 @@ First, let's have a look at the model inputs. 22 00:01:02,130 --> 00:01:04,350 -它的数字与令牌化相关联 +它的数字相关联于分词化 Its numbers associated with the tokenization 23 00:01:04,350 --> 00:01:06,843 -问题后跟上下文 +问题, 对应于上下文 of the question followed by the context 24 00:01:06,843 --> 00:01:09,723 -使用通常的 CLS 和 SEP 特殊令牌。 +使用通常的 CLS 和 SEP 特殊 token 。 with the usual CLS and SEP special tokens. 25 00:01:10,620 --> 00:01:13,320 -答案是那些令牌的一部分。 +答案是那些 token 的一部分。 The answer is a part of those tokens. 26 00:01:13,320 --> 00:01:15,510 -所以我们要求模型预测哪个令牌开始 +所以我们要求模型预测哪个 token 开始 So we ask the model to predict which token starts 27 @@ -140,7 +140,7 @@ For our two logit outputs, 29 00:01:19,650 --> 00:01:22,803 -理论标签是粉色和紫色的向量。 +理论上的标签是粉色和紫色的向量。 the theoretical labels are the pink and purple vectors. 30 @@ -155,17 +155,17 @@ we will need to apply a SoftMax, 32 00:01:28,436 --> 00:01:30,360 -就像在文本分类管道中一样。 +就像在文本分类管线中一样。 like in the text classification pipeline. 33 00:01:30,360 --> 00:01:33,390 -我们只是屏蔽不属于上下文的标记 +我们只是掩蔽不属于上下文的 token We just mask the tokens that are not part of the context 34 00:01:33,390 --> 00:01:36,855 -在此之前,不屏蔽初始 CLS 令牌 +在此之前,不掩蔽初始 CLS token before doing that, leaving the initial CLS token unmasked 35 @@ -190,23 +190,23 @@ since its exponential will then be zero. 39 00:01:48,957 --> 00:01:50,580 -现在,每个开始的概率 +现在,每个概率, 对于开始的 Now, the probability for each start 40 00:01:50,580 --> 00:01:53,550 -和对应于可能答案的结束位置, +和对于结束的可能答案的位置, and end position corresponding to a possible answer, 41 00:01:53,550 --> 00:01:55,050 -我们给的分数就是产品 -we give a score that is the product +我们给的分数就是 +we give a score that is the 42 00:01:55,050 --> 00:01:57,630 -开始概率和结束概率 -of the start probabilities and end probabilities +开始概率和结束概率之乘积 +product of the start probabilities and end probabilities 43 00:01:57,630 --> 00:01:58,803 @@ -230,12 +230,12 @@ Here is the code to find the best score 47 00:02:07,080 --> 00:02:08,820 -一个可能的答案。 +对一个可能的答案。 for a possible answer. 48 00:02:08,820 --> 00:02:11,430 -一旦我们有了令牌的开始和结束位置, +一旦我们有了这些 token 的开始和结束位置, Once we have the start and end positions of the tokens, 49 @@ -275,7 +275,7 @@ the whole answer, being truncated. 56 00:02:29,100 --> 00:02:31,050 -所以我们不丢弃截断的标记 +所以我们不丢弃截断的 token So we don't discard the truncated tokens 57 @@ -300,12 +300,12 @@ If we take disjoint chunks of texts, 61 00:02:41,430 --> 00:02:43,530 -我们最终可能会得到分裂的答案 +我们最终可能会得到拆分的答案 we might end up with the answer being split 62 00:02:43,530 --> 00:02:45,330 -两个特征之间。 +于两个特征之间。 between two features. 63 @@ -325,7 +325,7 @@ the answer to the question. 66 00:02:52,830 --> 00:02:55,260 -标记器自动为我们完成所有这些 +分词器自动为我们完成所有这些 The tokenizers do all of this for us automatically 67 @@ -340,7 +340,7 @@ The stride argument controls 69 00:02:59,700 --> 00:03:02,070 -重叠标记的数量。 +重叠 token 的数量。 the number of overlapping tokens. 70 @@ -365,7 +365,7 @@ for each feature, we get the answer with a score 74 00:03:10,636 --> 00:03:12,453 -对于他们每个人, +对于他们每个, for each of them, 75 diff --git a/subtitles/zh-CN/48_inside-the-question-answering-pipeline-(tensorflow).srt b/subtitles/zh-CN/48_inside-the-question-answering-pipeline-(tensorflow).srt index cdb7d661a..c42c93ce5 100644 --- a/subtitles/zh-CN/48_inside-the-question-answering-pipeline-(tensorflow).srt +++ b/subtitles/zh-CN/48_inside-the-question-answering-pipeline-(tensorflow).srt @@ -5,12 +5,12 @@ 2 00:00:05,490 --> 00:00:08,440 -- 让我们来看看问答管道的内部情况。 +- 让我们来看看问答 pipeline(管线) 的内部情况。 - Let's have a look inside the question answering pipeline. 3 00:00:09,780 --> 00:00:11,370 -问答管道 +问答管线 The question answering pipeline 4 @@ -25,7 +25,7 @@ from a given context or passage of text 6 00:00:16,020 --> 00:00:18,370 -就像变形金刚回购自述文件的这一部分。 +就像 transformer 仓库的 README 文件的这一部分。 like this part of the Transformers repo README. 7 @@ -35,7 +35,7 @@ It also works for very long context, 8 00:00:21,180 --> 00:00:24,720 -即使答案在最后,就像这个例子一样。 +即使答案靠后,就像这个例子一样。 even if the answer is at the very end, like in this example. 9 @@ -45,12 +45,12 @@ In this video, we'll see why. 10 00:00:27,840 --> 00:00:29,310 -问答管道 +问答管线 The question answering pipeline 11 00:00:29,310 --> 00:00:32,130 -遵循与其他管道相同的步骤。 +遵循与其他管线相同的步骤。 follows the same steps as the other pipelines. 12 @@ -65,7 +65,7 @@ fed to the model then some post-processing is applied. 14 00:00:39,540 --> 00:00:42,840 -所以标记化和模型步骤应该很熟悉。 +所以分词化和模型步骤应该很熟悉。 So tokenization and model steps should be familiar. 15 @@ -90,7 +90,7 @@ is that our model outputs two tensors 19 00:00:52,380 --> 00:00:55,230 -命名开始 logits 和结束 logits。 +命名 start logits 和 end logits。 named start logits and end logits. 20 @@ -105,7 +105,7 @@ Well, this is the way the model finds the answer 22 00:00:58,170 --> 00:00:59,043 -到这个问题。 +对这个问题。 to the question. 23 @@ -114,28 +114,28 @@ to the question. First, let's have a look at the model inputs. 24 -00:01:02,610 --> 00:01:04,800 -它是与标记化相关的数字 -It's numbers associated with the tokenization +00:01:02,610 --> 00:01:05,800 +它是与问题分词化相关的数字 +It's numbers associated with the tokenization of the question 25 -00:01:04,800 --> 00:01:05,850 -的问题, -of the question, +00:01:05,800 --> 00:01:05,850 +, +, 26 00:01:05,850 --> 00:01:07,753 -其次是上下文 +对于该上下文 followed by the context 27 00:01:07,753 --> 00:01:10,233 -使用通常的 CLS 和 SEP 特殊令牌。 +使用通常的 CLS 和 SEP 特殊 token 。 with the usual CLS and SEP special tokens. 28 00:01:11,130 --> 00:01:13,203 -答案是那些令牌的一部分。 +答案是那些 token 的一部分。 The answer is a part of those tokens. 29 @@ -145,7 +145,7 @@ So we ask the model to predict 30 00:01:15,330 --> 00:01:17,040 -哪个标记开始回答 +哪个 token 开始回答 which token starts the answer 31 @@ -175,12 +175,12 @@ we will need to apply a SoftMax, 36 00:01:28,596 --> 00:01:31,020 -就像在文本分类管道中一样。 +就像在文本分类管线中一样。 like in the text classification pipeline. 37 00:01:31,020 --> 00:01:32,310 -我们只是屏蔽令牌 +我们只是掩蔽 token We just mask the tokens 38 @@ -190,12 +190,12 @@ that are not part of the context before doing that, 39 00:01:35,940 --> 00:01:38,310 -不屏蔽初始 CLS 令牌 +不掩蔽初始 CLS token leaving the initial CLS token unmasked 40 00:01:38,310 --> 00:01:40,773 -因为我们用它来预测一个不可能的答案。 +我们用它来预测一个不可能的答案。 as we use it to predict an impossible answer. 41 @@ -230,13 +230,13 @@ will give a score that is a product 47 00:01:57,540 --> 00:01:58,680 -开始概率 -of the start probabilities +开始概率和结束概率 +of the start probabilities and end probabilities 48 -00:01:58,680 --> 00:02:00,873 -并在这些位置结束概率。 -and end probabilities at those position. +00:01:59,680 --> 00:02:00,873 +在这些位置。 +at those position. 49 00:02:01,920 --> 00:02:04,530 @@ -255,12 +255,12 @@ Here is the code to find the best score 52 00:02:09,510 --> 00:02:11,280 -一个可能的答案。 +对一个可能的答案。 for a possible answer. 53 00:02:11,280 --> 00:02:13,830 -一旦我们有了令牌的开始和结束位置, +一旦我们有了 token 的开始和结束位置, Once we have the start and end position for the tokens, 54 @@ -300,7 +300,7 @@ the whole answer, being truncated. 61 00:02:32,190 --> 00:02:34,020 -所以我们不丢弃截断的标记 +所以我们不丢弃截断的 token So we don't discard the truncated tokens 62 @@ -360,7 +360,7 @@ with the return overflowing tokens option. 73 00:03:01,920 --> 00:03:02,753 -步幅论证 +步幅参数 The stride argument 74 diff --git a/subtitles/zh-CN/49_what-is-normalization.srt b/subtitles/zh-CN/49_what-is-normalization.srt index edff41bf9..1c0c83dfa 100644 --- a/subtitles/zh-CN/49_what-is-normalization.srt +++ b/subtitles/zh-CN/49_what-is-normalization.srt @@ -10,27 +10,27 @@ 3 00:00:07,380 --> 00:00:09,930 -什么是标准化组件 +什么是规范化组件 what is the normalizer component 4 00:00:09,930 --> 00:00:13,023 -我们会在每个分词器的开头找到它。 +我们会在每个 tokenizer 的开头找到它。 that we'd find at the beginning of each tokenizer. 5 00:00:14,550 --> 00:00:16,830 -归一化操作包括 +规范化操作包括 The normalization operation consists 6 00:00:16,830 --> 00:00:19,890 -在应用一系列规范化规则时 +在应用一系列规范化规则 in applying a succession of normalization rules 7 00:00:19,890 --> 00:00:20,853 -到原始文本。 +到原始文本时。 to the raw text. 8 @@ -50,12 +50,12 @@ and use of our language model. 11 00:00:33,090 --> 00:00:37,470 -让我们用不同的字体来看一个非常多样化的句子, +让我们来看一个非常多样化的句子,用不同的字体 Let's take a very diverse sentence with different fonts, 12 00:00:37,470 --> 00:00:39,780 -大写和小写字符, +大写和小写的字符, upper and lower case characters, 13 @@ -65,12 +65,12 @@ accents, punctuation and multiple spaces, 14 00:00:43,920 --> 00:00:46,683 -看看几个分词器是如何规范化它的。 +看看几个 tokenizer 是如何规范化它的。 to see how several tokenizer normalize it. 15 00:00:48,488 --> 00:00:50,730 -来自 FNet 模型的分词器 +来自 FNet 模型的 tokenizer The tokenizer from the FNet model 16 @@ -110,7 +110,7 @@ with several font variants and keeps the multiple spaces, 23 00:01:12,090 --> 00:01:14,223 -但它删除了所有的口音。 +但它删除了所有的重音。 but it removes all the accents. 24 @@ -120,12 +120,12 @@ And if we continue to test this normalization 25 00:01:18,870 --> 00:01:23,040 -与模型相关的许多其他分词器 +与模型相关的许多其他 tokenizer of many other tokenizers associated to models 26 00:01:23,040 --> 00:01:25,110 -我们可以在集线器上找到, +我们可以在 Hub 上找到, that we can find on the Hub, 27 @@ -135,12 +135,12 @@ we see that they also propose other kind of normalization. 28 00:01:33,900 --> 00:01:35,850 -使用快速分词器, +使用快速 tokenizer , With the fast tokenizers, 29 00:01:35,850 --> 00:01:39,060 -很容易观察到选择的归一化 +很容易观察到选择的规范化 it's very easy to observe the normalization chosen 30 @@ -150,12 +150,12 @@ for the currently loaded tokenizer. 31 00:01:42,330 --> 00:01:46,140 -事实上,每个快速分词器的实例 +事实上,每个快速 tokenizer 的实例 Indeed, each instance of a fast tokenizer 32 00:01:46,140 --> 00:01:48,030 -有一个底层分词器 +有一个底层 tokenizer has an underlying tokenizer 33 @@ -175,12 +175,12 @@ This object has itself a normalizer attribute 36 00:01:58,470 --> 00:02:01,830 -我们可以使用 normalize_str 方法 +我们可以使用, 多亏 normalize_str 方法 that we can use thanks to the normalize_str method 37 00:02:01,830 --> 00:02:03,153 -规范化一个字符串。 +以规范化一个字符串。 to normalize a string. 38 @@ -190,12 +190,12 @@ It is thus very practical that this normalization, 39 00:02:08,700 --> 00:02:11,070 -这是在训练时使用的 +使用在训练时 which was used at the time of the training 40 00:02:11,070 --> 00:02:12,903 -分词器的保存, +保存分词器, of the tokenizer was saved, 41 @@ -205,7 +205,7 @@ and that it applies automatically 42 00:02:16,200 --> 00:02:19,233 -当你要求训练有素的分词器对文本进行分词时。 +当你要求训练过的 tokenizer 对文本进行分词时。 when you ask a trained tokenizer to tokenize a text. 43 @@ -215,7 +215,7 @@ For example, if we hadn't included the albert normalizer, 44 00:02:25,500 --> 00:02:28,770 -我们会有很多未知的代币 +我们会有很多未知的 token we would have had a lot of unknown tokens 45 @@ -230,12 +230,12 @@ with accents and capital letters. 47 00:02:35,730 --> 00:02:38,370 -这种转变也可能是检测不到的 +这种转变也可能是无法检测的 This transformation can also be undetectable 48 00:02:38,370 --> 00:02:40,050 -带有简单的打印。 +通过简单的打印出来。 with a simple print. 49 @@ -250,12 +250,12 @@ text is only a succession of 0 and 1, 51 00:02:45,840 --> 00:02:47,820 -碰巧不同的继承 +碰巧是不同的承接 and it happens that different successions 52 00:02:47,820 --> 00:02:51,363 -0 和 1 呈现相同的打印字符。 +让 0 和 1 呈现相同的打印字符。 of 0 and 1 render the same printed character. 53 @@ -275,7 +275,7 @@ into a sequence of code points. 56 00:03:04,530 --> 00:03:09,530 -在我们的示例中,2 个字节使用 UTF-8 解码 +在我们的示例中,2 个字节通过 UTF-8 解码 In our example, the 2 bytes is decoded using UTF-8 57 @@ -305,7 +305,7 @@ Let's repeat the same operation 62 00:03:23,790 --> 00:03:26,577 -有了这个由 3 个字节组成的新序列,。 +有了这个由 3 个字节组成的新序列, with this new sequence composed of 3 bytes,. 63 @@ -340,7 +340,7 @@ But it's annoying because what appears to us 69 00:03:45,000 --> 00:03:46,680 -成为一个单一的角色 +为了成为一个单一的字符 to be a single character 70 @@ -365,7 +365,7 @@ that allow erasing some of these differences. 74 00:04:05,730 --> 00:04:08,223 -这些标准通常由分词器使用。 +这些标准通常由 tokenizer 使用。 These standards are often used by tokenizers. 75 @@ -400,7 +400,7 @@ However, you must be aware that some normalizations 81 00:04:25,980 --> 00:04:30,363 -如果他们不适应他们的语料库,可能会非常有害。 +如果他们不适配他们的语料库,可能会非常有害。 can be very harmful if they are not adapted to their corpus. 82 @@ -415,7 +415,7 @@ For example, if you take the French sentence, 84 00:04:38,790 --> 00:04:42,510 -并使用 bert-base-uncase 分词器对其进行规范化 +并使用 bert-base-uncase tokenizer 对其进行规范化 and normalize it with the bert-base-uncase tokenizer 85 @@ -435,17 +435,17 @@ which means "An unworthy father". 88 00:04:53,460 --> 00:04:56,760 -如果你观看此视频是为了构建自己的分词器, +如果你观看此视频是为了构建自己的 tokenizer , If you watched this video to build your own tokenizer, 89 00:04:56,760 --> 00:04:59,610 -选择与否没有绝对的规则 +没有绝对的规则选择与否 there are no absolute rules to choose or not 90 00:04:59,610 --> 00:05:02,970 -新分词器的规范化, +一个新 tokenizer 的规范化, a normalization for a new tokenizer, 91 diff --git a/subtitles/zh-CN/50_what-is-pre-tokenization.srt b/subtitles/zh-CN/50_what-is-pre-tokenization.srt index 6b2870fe3..1068c69c2 100644 --- a/subtitles/zh-CN/50_what-is-pre-tokenization.srt +++ b/subtitles/zh-CN/50_what-is-pre-tokenization.srt @@ -1,6 +1,6 @@ 1 00:00:05,550 --> 00:00:08,910 -- 标记化管道涉及几个步骤 +- 分词化管线涉及几个步骤 - The tokenization pipeline involves several steps 2 @@ -15,22 +15,22 @@ In this video, we will see what happens 4 00:00:14,280 --> 00:00:16,293 -在预标记化步骤中。 +在预分词化步骤中。 during the pre-tokenization step. 5 00:00:18,390 --> 00:00:22,110 -pre-tokenization 操作是执行的操作 +预分词化操作是操作执行在 The pre-tokenization operation is the operation performed 6 00:00:22,110 --> 00:00:24,630 -文本归一化后 +文本规范化后 after the normalization of the text 7 00:00:24,630 --> 00:00:27,633 -在应用标记化算法之前。 +在应用分词化算法之前。 and before the application of the tokenization algorithm. 8 @@ -55,12 +55,12 @@ Let's look at how several tokenizers 12 00:00:41,310 --> 00:00:43,143 -在此示例中进行预标记。 +在此示例中进行预分词。 pre-tokenize in this example. 13 00:00:46,200 --> 00:00:50,820 -gpt2 pre-tokenization 在空格上划分文本 +gpt2 pre-tokenization 把文本划分到空格 The gpt2 pre-tokenization divides the text on spaces 14 @@ -80,7 +80,7 @@ by capital G with a dot above. 17 00:01:07,170 --> 00:01:09,540 -Albert 的预标记化将文本划分 +Albert 的预分词化将文本划分 Albert's pre-tokenization divides the text 18 @@ -115,12 +115,12 @@ But unlike the previous tokenizers, 24 00:01:31,260 --> 00:01:33,780 -空间没有变换 +空格没有变换 spaces are not transformed 25 00:01:33,780 --> 00:01:37,293 -并集成到使用此预标记器生成的令牌中。 +并集成到使用此预标记器生成的 token 中。 and integrated into tokens produced with this pre-tokenizer. 26 @@ -135,7 +135,7 @@ we could observe the two main type of operation 28 00:01:45,330 --> 00:01:47,073 -预标记化带来的; +预分词化带来的; brought by the pre-tokenization; 29 @@ -160,7 +160,7 @@ Finally, the backend tokenizer of the fast tokenizers 33 00:02:04,230 --> 00:02:07,680 -还允许测试预标记化操作 +还允许测试预分词化操作 also allows to test the pre-tokenization operation 34 @@ -175,12 +175,12 @@ We notice that the output of this operation 36 00:02:14,970 --> 00:02:18,450 -由令牌和偏移量组成, +由 token 和偏移量组成, is composed of both tokens and offsets, 37 00:02:18,450 --> 00:02:21,960 -允许将标记链接到它在文本中的位置 +允许将 token 链接到它在文本中的位置 which allow to link the tokens to its position in the text 38 @@ -190,22 +190,22 @@ given in input of the method. 39 00:02:25,650 --> 00:02:28,860 -此操作定义了最大的令牌 +此操作定义了最大的 token This operation defines the largest tokens 40 00:02:28,860 --> 00:02:31,740 -可以通过标记化产生, +可以通过分词化产生, that can be produced by the tokenization, 41 00:02:31,740 --> 00:02:36,090 -或者换句话说,子令牌的障碍 +或者换句话说,子 token 的障碍 or in those words, the barriers of the sub-tokens 42 00:02:36,090 --> 00:02:37,653 -届时将生产。 +届时将产生。 which will be produced then. 43 @@ -215,6 +215,6 @@ And that's all for the characteristic 44 00:02:41,850 --> 00:02:43,203 -预分词器。 +对预分词器。 of the pre-tokenizers. diff --git a/subtitles/zh-CN/51_byte-pair-encoding-tokenization.srt b/subtitles/zh-CN/51_byte-pair-encoding-tokenization.srt index 513b464a9..9bad1de88 100644 --- a/subtitles/zh-CN/51_byte-pair-encoding-tokenization.srt +++ b/subtitles/zh-CN/51_byte-pair-encoding-tokenization.srt @@ -10,12 +10,12 @@ 3 00:00:06,720 --> 00:00:10,464 -如果你想了解什么是字节对编码 +如果你想了解什么是字节对编码(BPE)算法 if you want to understand what the Byte Pair Encoding 4 00:00:10,464 --> 00:00:13,263 -子词标记化算法是, +子词分词化算法是, subword tokenization algorithm is, 5 @@ -25,7 +25,7 @@ how to train it 6 00:00:15,505 --> 00:00:17,790 -以及文本的标记化是如何完成的 +以及文本的分词化是如何完成的 and how the tokenization of a text is done 7 @@ -65,12 +65,12 @@ into a sequence of'subword units' 14 00:00:38,100 --> 00:00:41,970 -哪些是参考语料库中频繁出现的单位 +其是参考语料库中频繁出现的单位 which are units that appear frequently in a reference corpus 15 00:00:41,970 --> 00:00:44,613 -也就是我们用来训练它的语料库。 +也是我们用来训练它的语料库。 which is, the corpus we used to train it. 16 @@ -90,7 +90,7 @@ We will not train our tokenizer on this raw text 19 00:00:56,940 --> 00:00:59,490 -但我们首先将其标准化 +但我们首先将其规范化 but we will first normalize it 20 @@ -120,7 +120,7 @@ by gathering together the same words 25 00:01:10,350 --> 00:01:12,450 -并通过维护一个柜台, +并通过维护一个柜, and by maintaining a counter, 26 @@ -130,12 +130,12 @@ here represented in blue. 27 00:01:17,340 --> 00:01:19,860 -要了解培训的工作原理, +要了解训练的工作原理, To understand how the training works, 28 00:01:19,860 --> 00:01:23,730 -我们认为这个玩具语料库由以下单词组成: +我们认为这个小语料库由以下单词组成: we consider this toy corpus composed of the following words: 29 @@ -170,7 +170,7 @@ into a list of elementary units that compose them, 35 00:01:45,210 --> 00:01:47,013 -在这里,人物。 +在这里, 字符。 here, the characters. 36 @@ -190,7 +190,7 @@ Let's now see how to increase it. 39 00:02:05,520 --> 00:02:08,250 -我们回到我们分裂的语料库, +我们回到我们拆分的语料库, We return to our split corpus, 40 @@ -200,7 +200,7 @@ we will go through the words one by one 41 00:02:11,340 --> 00:02:14,313 -并计算令牌对的所有出现次数。 +并计算 token 对的所有出现次数。 and count all the occurrences of token pairs. 42 @@ -210,7 +210,7 @@ The first pair is composed of the token 'h' and 'u', 43 00:02:20,130 --> 00:02:23,067 -第二个 'u' 和 'g', +第二个 "u" 和 "g", the second 'u' and 'g', 44 @@ -245,7 +245,7 @@ We note our first merging rule 50 00:02:54,593 --> 00:02:57,243 -然后我们将新标记添加到我们的词汇表中。 +然后我们将新 token 添加到我们的词汇表中。 and we add the new token to our vocabulary. 51 @@ -255,7 +255,7 @@ We can then apply this merging rule to our splits. 52 00:03:04,260 --> 00:03:07,350 -你可以看到我们已经合并了所有的令牌对 +你可以看到我们已经合并了所有的 token 对 You can see that we have merged all the pairs of tokens 53 @@ -270,7 +270,7 @@ And now, we just have to reproduce the same steps 55 00:03:18,150 --> 00:03:19,353 -与我们的新分裂。 +与我们的新拆分。 with our new splits. 56 @@ -280,7 +280,7 @@ We calculate the frequency of occurrence 57 00:03:23,460 --> 00:03:25,023 -每对标记, +对每对 token , of each pair of tokens, 58 @@ -295,12 +295,12 @@ we note it in our merge rules, 60 00:03:36,000 --> 00:03:39,360 -我们将新的标记添加到词汇表中 +我们将新的 token 添加到词汇表中 we add the new one token the vocabulary 61 00:03:39,360 --> 00:03:41,880 -然后我们合并所有的标记对 +然后我们合并所有的 token 对 and then we merge all the pairs of tokens 62 @@ -320,7 +320,7 @@ until we reach the desired vocabulary size. 65 00:04:05,671 --> 00:04:10,671 -在这里,当我们的词汇量达到 21 个标记时,我们就停止了。 +在这里,当我们的词汇量达到 21 个 token 时,我们就停止了。 Here, we stopped when our vocabulary reached 21 tokens. 66 @@ -335,7 +335,7 @@ are now divided into far fewer tokens 68 00:04:17,040 --> 00:04:20,280 -被分成了更少的 tokens +被分成了更少的 token than at the beginning of the training. 69 @@ -390,7 +390,7 @@ Then, we'll go through our merge rules 79 00:04:52,020 --> 00:04:54,690 -直到我们有一个我们可以申请。 +直到我们有一个我们可以应用。 until we have one we can apply. 80 @@ -400,7 +400,7 @@ Here, we can merge the letters 'h' and 'u'. 81 00:04:57,930 --> 00:05:01,467 -在这里,我们可以合并 2 个令牌以获得新令牌 “hug”。 +在这里,我们可以合并 2 个 token 以获得新 token “hug”。 And here, we can merge 2 tokens to get the new token 'hug'. 82 @@ -410,7 +410,7 @@ When we get to the end of our merge rules, 83 00:05:05,760 --> 00:05:07,563 -标记化完成。 +分词化完成。 the tokenization is finished. 84 @@ -425,7 +425,7 @@ I hope that now the BPE algorithm 86 00:05:14,850 --> 00:05:16,413 -没有更多的秘密给你! +对你而言不再是秘密! has no more secret for you! 87 diff --git a/subtitles/zh-CN/52_wordpiece-tokenization.srt b/subtitles/zh-CN/52_wordpiece-tokenization.srt index 26873d619..60e15875e 100644 --- a/subtitles/zh-CN/52_wordpiece-tokenization.srt +++ b/subtitles/zh-CN/52_wordpiece-tokenization.srt @@ -10,12 +10,12 @@ 3 00:00:08,370 --> 00:00:11,851 -WordPiece 算法及其执行方式 +对 WordPiece 算法及其执行方式 of the WordPiece algorithm, and how it performs 4 00:00:11,851 --> 00:00:15,150 -一旦训练,文本的标记化。 +一旦训练,文本的分词化。 the tokenization of a text, once trained. 5 @@ -45,7 +45,7 @@ So we base our explanations 10 00:00:33,510 --> 00:00:36,903 -根据我们自己对已发表文献的解释。 +根据我们自己对已发表文献的理解。 on our own interpretation of the published literature. 11 @@ -100,7 +100,7 @@ We add two hashtags in front of the letters 21 00:01:14,190 --> 00:01:16,083 -那不开始一个词。 +那不开启一个词。 that do not start a word. 22 @@ -120,7 +120,7 @@ We will list all the existing pairs in our corpus. 25 00:01:30,990 --> 00:01:32,640 -一旦我们有了这份清单, +一旦我们有了这份表格, Once we have this list, 26 @@ -150,22 +150,22 @@ the first pair composed of the letters H and U. 31 00:01:48,510 --> 00:01:51,390 -一对的分数简单地等于频率 +一对的分数单纯地等于频率 The score of a pair is simply equal to the frequency 32 00:01:51,390 --> 00:01:54,510 -一对的外观除以产品 +对这对出现的, 除以乘积 of appearance of the pair, divided by the product 33 00:01:54,510 --> 00:01:57,330 -第一个标记出现的频率, +对第一个 token 出现的频率, of the frequency of appearance of the first token, 34 00:01:57,330 --> 00:02:00,063 -通过第二个标记的出现频率。 +乘上第二个 token 的出现频率。 by the frequency of appearance of the second token. 35 @@ -220,7 +220,7 @@ the pair with the highest score, after merging it of course. 45 00:02:40,140 --> 00:02:43,863 -现在我们可以将同样的融合应用于我们的拆分语料库。 +现在我们可以将同样的操作应用于我们的拆分语料库。 And now we can apply this same fusion to our split corpus. 46 @@ -250,7 +250,7 @@ to see the evolution of our vocabulary, 51 00:02:58,957 --> 00:03:01,773 -以及劈叉长度的演变。 +以及拆分长度的演变。 and also the evolution of the length of the splits. 52 @@ -275,7 +275,7 @@ WordPiece follows these rules. 56 00:03:20,310 --> 00:03:22,530 -我们将寻找最长的令牌 +我们将寻找最长的 token We will look for the longest possible token 57 @@ -285,7 +285,7 @@ at the beginning of the word. 58 00:03:24,960 --> 00:03:28,920 -然后我们重新开始我们的话的剩余部分, +然后我们重新开始我们的词语的剩余部分, Then we start again on the remaining part of our word, 59 @@ -295,7 +295,7 @@ and so on until we reach the end. 60 00:03:32,100 --> 00:03:35,973 -就是这样。 Huggingface 分为四个子令牌。 +就是这样。 Huggingface 分为四个子 token 。 And that's it. Huggingface is divided into four sub-tokens. 61 diff --git a/subtitles/zh-CN/53_unigram-tokenization.srt b/subtitles/zh-CN/53_unigram-tokenization.srt index 48f3099f5..fef8e79b8 100644 --- a/subtitles/zh-CN/53_unigram-tokenization.srt +++ b/subtitles/zh-CN/53_unigram-tokenization.srt @@ -10,12 +10,12 @@ 3 00:00:06,420 --> 00:00:09,881 -我们将一起研究 “Unigram 语言模型” +我们将一起研究 “Unigram 语言模型 we will study together 'the Unigram Language Model 4 00:00:09,881 --> 00:00:13,288 -子词标记化算法 '。 +子词分词化算法 " 。 subword tokenization algorithm'. 5 @@ -25,7 +25,7 @@ The overall training strategy 6 00:00:15,567 --> 00:00:18,450 -Unigram 语言模型分词器 +对一个 Unigram 语言模型分词器 of a Unigram Language Model tokenizer 7 @@ -35,7 +35,7 @@ is to start with a very large vocabulary 8 00:00:21,480 --> 00:00:24,240 -然后在每次迭代中删除标记 +然后在每次迭代中删除 token and then to remove tokens at each iteration 9 @@ -55,7 +55,7 @@ we will calculate a loss on our training corpus 12 00:00:30,930 --> 00:00:33,480 -感谢 Unigram 模型。 +多亏了 Unigram 模型。 thanks to the Unigram model. 13 @@ -70,12 +70,12 @@ we can use it to choose how to reduce the vocabulary. 15 00:00:41,550 --> 00:00:43,620 -所以我们看看损失的演变 +所以我们看看损失的变化 So we look at the evolution of the loss 16 00:00:43,620 --> 00:00:47,103 -通过依次从词汇表中删除每个标记。 +通过依次从词汇表中删除每个 token 。 by removing in turn each token from the vocabulary. 17 @@ -110,8 +110,8 @@ The Unigram Language Model 23 00:01:06,030 --> 00:01:08,493 -是一种统计语言调制解调器。 -is a type of Statistical Language Modem. +是一种统计语言模型。 +is a type of Statistical Language Model. 24 00:01:09,450 --> 00:01:10,980 @@ -125,12 +125,12 @@ will assign a probability to a text 26 00:01:13,530 --> 00:01:18,090 -考虑到文本实际上是一系列标记。 +考虑到文本实际上是一系列 token 。 considering that the text is in fact a sequence of tokens. 27 00:01:18,090 --> 00:01:21,090 -可以想象的最简单的标记序列 +可以想象的最简单的 token 序列 The simplest sequences of tokens to imagine 28 @@ -170,7 +170,7 @@ is equal to the product of the probabilities 35 00:01:42,210 --> 00:01:43,953 -组成它的令牌。 +对组成它的 token 。 of the tokens that compose it. 36 @@ -185,7 +185,7 @@ which would not be adapted to the generation of text 38 00:01:53,850 --> 00:01:57,840 -因为这个模型总是会生成相同的令牌, +因为这个模型总是会生成相同的 token , since this model would always generate the same token, 39 @@ -195,7 +195,7 @@ the one which has the greatest probability. 40 00:02:01,320 --> 00:02:03,360 -然而,要进行标记化, +然而,要进行分词化, Nevertheless, to do tokenization, 41 @@ -320,17 +320,17 @@ At each iteration, 65 00:03:15,120 --> 00:03:17,430 -我们估计代币的概率 +我们估计 token 的概率 we estimate the probabilities of the tokens 66 00:03:17,430 --> 00:03:18,430 -词汇的 +对词汇 of the vocabulary 67 00:03:20,130 --> 00:03:23,100 -然后我们删除 p 百分比的标记 +然后我们删除 p 百分比的 token and then we remove the p-percent of tokens 68 @@ -365,7 +365,7 @@ The probability of a token simply estimated 74 00:03:42,360 --> 00:03:44,760 -按此令牌出现的次数 +按此 token 出现的次数 by the number of appearance of this token 75 @@ -375,7 +375,7 @@ in our training corpus 76 00:03:46,440 --> 00:03:50,133 -除以所有令牌出现的总数。 +除以所有 token 出现的总数。 divided by the total number of appearance of all the tokens. 77 @@ -405,7 +405,7 @@ and how the loss is calculated on our corpus. 82 00:04:09,088 --> 00:04:12,263 -我们的文本 “Hug” 的 Unigram LM 标记化 +我们的文本 “Hug” 的 Unigram LM 分词化 The Unigram LM tokenization of our text 'Hug' 83 @@ -425,12 +425,12 @@ To find it, the simplest way to proceed 86 00:04:21,750 --> 00:04:24,120 -将列出所有可能的细分 +将列出所有可能的分割 would be to list all the possible segmentations 87 00:04:24,120 --> 00:04:25,800 -我们的文字 Hug +对我们的文本 "Hug" , of our text 'Hug', 88 @@ -450,7 +450,7 @@ With the current vocabulary, 91 00:04:34,920 --> 00:04:38,640 -两个标记化获得完全相同的概率。 +两个分词化获得完全相同的概率。 two tokenizations get exactly the same probability. 92 @@ -470,7 +470,7 @@ To compute the loss on our training corpus, 95 00:04:46,380 --> 00:04:48,570 -我们需要像刚才那样进行标记化 +我们需要像刚才那样进行分词化 we need to tokenize as we just did 96 @@ -480,7 +480,7 @@ all the remaining words in the corpus. 97 00:04:52,290 --> 00:04:56,430 -损失就是语料库中所有单词的总和 +损失就是所有单词的总和, 语料库中 The loss is then the sum over all the words in the corpus 98 @@ -495,7 +495,7 @@ multiplied by the opposite of the log of the probability 100 00:05:02,670 --> 00:05:05,463 -与单词的标记化相关联。 +与单词的分词化相关联的。 associated with the tokenization of the word. 101 @@ -510,7 +510,7 @@ Remember, our initial goal was to reduce the vocabulary. 103 00:05:18,630 --> 00:05:21,870 -为此,我们将从词汇表中删除一个标记 +为此,我们将从词汇表中删除一个 token To do this, we will remove a token from the vocabulary 104 @@ -525,12 +525,12 @@ Let's remove for example, the token 'ug'. 106 00:05:31,920 --> 00:05:35,370 -我们注意到 “hug” 的标记化 +我们注意到 “hug” 的分词化 We notice that the tokenization for 'hug' 107 00:05:35,370 --> 00:05:39,990 -字母 h 和元组 ug 现在是不可能的。 +字母 "h" 和元组 "ug" 现在是不可能的。 with the letter 'h' and the tuple 'ug' is now impossible. 108 @@ -540,12 +540,12 @@ Nevertheless, as we saw earlier 109 00:05:42,240 --> 00:05:45,180 -两个标记化具有相同的概率, +两个分词化具有相同的概率, that two tokenizations had the same probability, 110 00:05:45,180 --> 00:05:47,730 -我们仍然可以选择剩余的标记化 +我们仍然可以选择剩余的分词化 we can still choose the remaining tokenization 111 @@ -585,7 +585,7 @@ if we continue the calculation, 118 00:06:10,080 --> 00:06:13,050 -我们会注意到我们可以删除任何令牌 +我们会注意到我们可以删除任何 token we would notice that we could remove any token 119 @@ -610,7 +610,7 @@ So we estimate again the probability of each token 123 00:06:27,300 --> 00:06:30,630 -在计算每个代币对损失的影响之前。 +在计算每个 token 对损失的影响之前。 before calculating the impact of each token on the loss. 124 @@ -620,17 +620,17 @@ For example, if we remove now 125 00:06:33,990 --> 00:06:36,290 -由字母 “h” 和 “u” 组成的令牌, +由字母 “h” 和 “u” 组成的 token , the token composed of the letters 'h' and 'u', 126 00:06:37,350 --> 00:06:41,013 -hug 只剩下一种可能的标记化。 -there is only one possible tokenization left for hug. +"hug" 只剩下一种可能的分词化。 +there is only one possible tokenization left for "hug". 127 00:06:41,940 --> 00:06:44,700 -词汇表中其他词的标记化 +词汇表中其他词的分词化 The tokenization of the other words of the vocabulary 128 @@ -645,7 +645,7 @@ In the end, 130 00:06:47,393 --> 00:06:49,200 -我们通过删除令牌获得 +我们通过删除 token 获得 we obtain by removing the token 131 @@ -655,22 +655,22 @@ composed of the letters 'h' and 'u' from the vocabulary, 132 00:06:52,749 --> 00:06:56,430 -亏损 168。 +损失为 168。 a loss of 168. 133 00:06:56,430 --> 00:06:59,490 -最后,要选择要删除的令牌, +最后,要选择要删除的 token , Finally, to choose which token to remove, 134 00:06:59,490 --> 00:07:02,490 -我们将为词汇表的每个剩余标记, +我们将为词汇表的每个剩余 token , we will for each remaining token of the vocabulary, 135 00:07:02,490 --> 00:07:04,800 -这不是基本令牌, +这不是基本 token , which is not an elementary token, 136 @@ -685,17 +685,17 @@ Then, compare these losses between them. 138 00:07:11,730 --> 00:07:13,800 -我们将删除的令牌 +我们将删除的 token The token which we will remove 139 00:07:13,800 --> 00:07:17,340 -是对损失影响最小的代币, +是对损失影响最小的 token , is the token which impacts the least the loss, 140 00:07:17,340 --> 00:07:18,870 -这里是令牌 “bu”。 +这里是 token “bu”。 here the token 'bu'. 141 @@ -715,12 +715,12 @@ p-percent of the tokens by iteration. 144 00:07:29,356 --> 00:07:33,000 -可以在本次迭代中删除的第二个标记 +可以在本次迭代中删除的第二个 token The second token that could be removed at this iteration 145 00:07:33,000 --> 00:07:34,317 -是标记 “du”。 +是 token “du”。 is the token 'du'. 146 @@ -765,7 +765,7 @@ before comparing them to keep the best one 154 00:07:58,770 --> 00:08:01,440 -但是我们使用维特比算法 +但是我们使用 Viterbi 算法 but we use the Viterbi algorithm 155 diff --git a/subtitles/zh-CN/54_building-a-new-tokenizer.srt b/subtitles/zh-CN/54_building-a-new-tokenizer.srt index 7faa3a529..5928f79fd 100644 --- a/subtitles/zh-CN/54_building-a-new-tokenizer.srt +++ b/subtitles/zh-CN/54_building-a-new-tokenizer.srt @@ -10,17 +10,17 @@ In this video, we will see how 3 00:00:07,500 --> 00:00:11,310 -你可以从头开始创建自己的分词器。 +你可以从头开始创建自己的 tokenizer(分词器) 。 you can create your own tokenizer from scratch. 4 00:00:11,310 --> 00:00:15,000 -要创建自己的分词器,你必须考虑 +要创建自己的 tokenizer ,你必须考虑 To create your own tokenizer, you will have to think about 5 00:00:15,000 --> 00:00:18,180 -令牌化中涉及的每个操作。 +分词化中涉及的每个操作。 each of the operations involved in tokenization. 6 @@ -50,27 +50,27 @@ I advise you to go and see the videos linked below. 11 00:00:34,531 --> 00:00:37,110 -后处理收集所有修改 +后处理包括所有修改 The post processing gathers all the modifications 12 00:00:37,110 --> 00:00:40,860 -我们将对标记化文本执行。 +我们将对分词化文本执行的。 that we will carry out on the tokenized text. 13 00:00:40,860 --> 00:00:43,890 -它可以包括添加特殊令牌, +它可以包括添加特殊 token , It can include the addition of special tokens, 14 00:00:43,890 --> 00:00:46,290 -创建一个意图面具, +创建一个意图掩码, the creation of an intention mask, 15 00:00:46,290 --> 00:00:48,903 -还会生成令牌 ID 列表。 +还会生成 token 的 ID 列表。 but also the generation of a list of token IDs. 16 @@ -90,22 +90,22 @@ from the sequence of IDs in a sentence. 19 00:00:58,890 --> 00:01:01,800 -例如,你可以看到主题标签 +例如,你可以看到 hash 标签 For example, you can see that the hashtags 20 00:01:01,800 --> 00:01:04,260 -已被删除,并且令牌 +已被删除,并且 token have been removed, and the tokens 21 00:01:04,260 --> 00:01:07,323 -今天的作文词都归在了一起。 +今天的词都归在了一起。 composing the word today have been grouped together. 22 00:01:10,860 --> 00:01:13,440 -在快速分词器中,所有这些组件 +在快速 tokenizer 中,所有这些组件 In a fast tokenizer, all these components 23 @@ -115,12 +115,12 @@ are gathered in the backend_tokenizer attribute. 24 00:01:17,370 --> 00:01:20,070 -正如你在这个小代码片段中看到的那样, +正如你在这个小片代码中看到的那样, As you can see with this small code snippet, 25 00:01:20,070 --> 00:01:22,020 -它是分词器的一个实例 +它是 tokenizer 的一个实例 it is an instance of a tokenizer 26 @@ -130,7 +130,7 @@ from the tokenizers library. 27 00:01:25,740 --> 00:01:28,263 -因此,要创建自己的分词器, +因此,要创建自己的 tokenizer , So, to create your own tokenizer, 28 @@ -140,22 +140,22 @@ you will have to follow these steps. 29 00:01:33,270 --> 00:01:35,433 -首先,创建一个训练数据集。 +第一,创建一个训练数据集。 First, create a training dataset. 30 00:01:36,690 --> 00:01:39,000 -二、创建和训练分词器 +第二, 创建和训练 tokenizer Second, create and train a tokenizer 31 00:01:39,000 --> 00:01:41,700 -与变压器库。 +用 transformer 库。 with the transformer library. 32 00:01:41,700 --> 00:01:46,700 -第三,将此分词器加载到转换器分词器中。 +第三,将此 tokenizer 加载为 transformer 的 tokenizer 中。 And third, load this tokenizer into a transformer tokenizer. 33 @@ -195,17 +195,17 @@ perfect for the example. 40 00:02:12,210 --> 00:02:13,920 -我们主要攻击这里, +我们主要修改这里, We attack here the big part, 41 00:02:13,920 --> 00:02:17,373 -使用标记器库设计我们的标记器。 +使用 tokenizer 库设计我们的 tokenizer 。 the design of our tokenizer with the tokenizer library. 42 00:02:18,750 --> 00:02:22,020 -我们首先初始化一个分词器实例 +我们首先初始化一个 tokenizer 实例 We start by initializing a tokenizer instance 43 @@ -255,7 +255,7 @@ and the second one isolating the punctuation marks. 52 00:03:03,360 --> 00:03:06,360 -现在,我们可以定义允许我们的培训师 +现在,我们可以定义允许我们的训练方法 Now, we can define the trainer that will allow us 53 @@ -265,7 +265,7 @@ to train the WordPiece model chosen at the beginning. 54 00:03:11,160 --> 00:03:12,600 -为了开展培训, +为了开展训练, To carry out the training, 55 @@ -280,7 +280,7 @@ Here we choose 25,000. 57 00:03:17,910 --> 00:03:21,270 -我们还需要公布特殊代币 +我们还需要公布特殊 token And we also need to announce the special tokens 58 @@ -305,7 +305,7 @@ Once the model has been trained, we can retrieve 62 00:03:42,570 --> 00:03:46,560 -特殊类别和分离标记的 ID, +特殊类别和分离 token 的 ID, the IDs of the special class and separation tokens, 63 @@ -320,12 +320,12 @@ Thanks to the TemplateProcessing class, 65 00:03:52,860 --> 00:03:57,210 -我们可以在每个序列的开头添加 CLS 标记, +我们可以在每个序列的开头添加 CLS token , we can add the CLS token at the beginning of each sequence, 66 00:03:57,210 --> 00:04:00,120 -和序列末尾的 SEP 令牌, +和序列末尾的 SEP token , and the SEP token at the end of the sequence, 67 @@ -345,12 +345,12 @@ which will allow us to remove the hashtags 70 00:04:12,690 --> 00:04:14,610 -在令牌的开头 +在 token 的开头 at the beginning of the tokens 71 00:04:14,610 --> 00:04:17,193 -必须重新附加到以前的令牌。 +必须重新附加到以前的 token 。 that must be reattached to the previous token. 72 @@ -365,12 +365,12 @@ You have all the necessary lines of code 74 00:04:25,110 --> 00:04:29,403 -使用分词器库定义你自己的分词器。 +使用 tokenizer 库定义你自己的 tokenizer 。 to define your own tokenizer with the tokenizer library. 75 00:04:30,960 --> 00:04:32,280 -现在我们有了一个全新的分词器 +现在我们有了一个全新的 tokenizer Now that we have a brand new tokenizer 76 @@ -380,7 +380,7 @@ with the tokenizer library, we just have to load it 77 00:04:35,400 --> 00:04:38,463 -从 transformers 库中转换为快速分词器。 +从 transformers 库中转换为快速 tokenizer 。 into a fast tokenizer from the transformers library. 78 @@ -390,7 +390,7 @@ Here again, we have several possibilities. 79 00:04:42,630 --> 00:04:44,430 -我们可以在泛型类中加载它, +我们可以在一般类中加载它, We can load it in the generic class, 80 @@ -410,7 +410,7 @@ I really hope this video has helped you understand 83 00:04:59,670 --> 00:05:02,133 -如何创建自己的分词器, +如何创建自己的 tokenizer , how you can create your own tokenizer, 84 @@ -420,12 +420,12 @@ and that you are ready now to navigate 85 00:05:06,240 --> 00:05:08,070 -分词器库文档 + tokenizer 库文档 the tokenizer library documentation 86 00:05:08,070 --> 00:05:11,367 -为你的全新分词器选择组件。 +为你的全新 tokenizer 选择组件。 to choose the components for your brand new tokenizer. 87 diff --git a/subtitles/zh-CN/68_data-collators-a-tour.srt b/subtitles/zh-CN/68_data-collators-a-tour.srt index c5d93c002..16805049c 100644 --- a/subtitles/zh-CN/68_data-collators-a-tour.srt +++ b/subtitles/zh-CN/68_data-collators-a-tour.srt @@ -60,22 +60,22 @@ What are data collators? 13 00:00:27,600 --> 00:00:30,480 -数据整理者整理数据。 +数据整理器整理数据。 Data collators collate data. 14 00:00:30,480 --> 00:00:31,800 -那不是很有帮助。 +好像和没说一样。 That's not that helpful. 15 00:00:31,800 --> 00:00:35,023 -但更具体地说,他们整理了一份样本清单 +更具体地说,他们整理了一份样本清单 But to be more specific, they put together a list of samples 16 00:00:35,023 --> 00:00:37,830 -成一个单一的训练小批量。 +组成一个单独的小批量训练数据。 into a single training minibatch. 17 @@ -85,7 +85,7 @@ For some tasks, 18 00:00:38,910 --> 00:00:41,670 -数据整理器可以非常简单。 +数据整理器可以非常简单明了。 the data collator can be very straightforward. 19 @@ -95,17 +95,17 @@ For example, when you're doing sequence classification, 20 00:00:44,820 --> 00:00:47,010 -数据整理器提供你真正需要的一切 +你真正需要数据整理器做的是 all you really need from your data collator 21 00:00:47,010 --> 00:00:49,860 -是它将你的样品填充到相同的长度 +将你的样本数据填充到相同的长度 is that it pads your samples to the same length 22 00:00:49,860 --> 00:00:52,413 -并将它们连接成一个张量。 +并将它们连接成一个单独的 Tensor。 and concatenates them into a single Tensor. 23 @@ -115,12 +115,12 @@ But for other workflows, data collators can be quite complex 24 00:00:57,750 --> 00:00:59,910 -因为他们处理一些预处理 +因为他们为特定任务 as they handle some of the preprocessing 25 00:00:59,910 --> 00:01:02,340 -该特定任务所需。 +完成一些预处理操作。 needed for that particular task. 26 @@ -130,12 +130,12 @@ So, if you want to use a data collator, 27 00:01:04,800 --> 00:01:07,860 -对于 PyTorch 用户,你通常通过数据整理器 +对于 PyTorch 用户,你通常将数据整理器 for PyTorch users, you usually pass the data collator 28 00:01:07,860 --> 00:01:09,780 -到你的 Trainer 对象。 +传递到你的 Trainer 对象。 to your Trainer object. 29 @@ -155,7 +155,7 @@ is to pass it to the to_tf_dataset method of your dataset. 32 00:01:16,860 --> 00:01:20,198 -这会给你一个 tensorflow_tf_data.dataset +这会给你 tensorflow_tf_data.dataset And this will give you a tensorflow_tf_data.dataset 33 @@ -180,17 +180,17 @@ Also note that all of our collators 37 00:01:30,180 --> 00:01:32,610 -采用 return_tensors 参数。 +可接受 return_tensors 参数。 take a return_tensors argument. 38 00:01:32,610 --> 00:01:35,737 -你可以将其设置为 “pt” 以获取 PyTorch 张量, +你可以将其设置为 “pt” 以获取 PyTorch Tensor, You can set this to "pt" to get PyTorch Tensors, 39 00:01:35,737 --> 00:01:37,920 -"tf" 获取 TensorFlow 张量, +"tf" 获取 TensorFlow Tensor, "tf" to get TensorFlow Tensors, 40 @@ -210,12 +210,12 @@ the default value is "pt", 43 00:01:44,460 --> 00:01:47,160 -所以 PyTorch 用户甚至不必设置这个参数 +所以 PyTorch 用户甚至大多数情况下 so PyTorch users don't even have to set this argument 44 00:01:47,160 --> 00:01:48,270 -大多数时候。 +不必设置这个参数。 most of the time. 45 @@ -225,7 +225,7 @@ And so as a result, they're often totally unaware 46 00:01:50,820 --> 00:01:52,713 -这个论点甚至存在。 +这个参数的存在。 that this argument even exists. 47 @@ -245,7 +245,7 @@ are often the most blind to its existence. 50 00:02:00,690 --> 00:02:01,920 -不过还好,回来了。 +好吧好吧,扯远了。 But okay, coming back. 51 @@ -255,17 +255,17 @@ Let's see how some specific data collators work in action. 52 00:02:06,540 --> 00:02:08,070 -虽然再次记住如果没有 +虽然再次记住 Although again, remember if none 53 00:02:08,070 --> 00:02:09,900 -的内置数据整理器可以满足你的需求, +如果内置数据整理器无法满足你的需求, of the built-in data collators do what you need, 54 00:02:09,900 --> 00:02:13,650 -你总是可以自己写,而且它们通常很短。 +你可以自己实现数据整理器,而且它们通常很短。 you can always write your own and they're often quite short. 55 @@ -280,12 +280,12 @@ These are DefaultDataCollator and DataCollatorWithPadding. 57 00:02:21,420 --> 00:02:22,830 -这些是你应该使用的 +如果在准备训练之前 These are the ones you should use 58 00:02:22,830 --> 00:02:24,720 -如果你的标签很简单 +你的标签很简单 if your labels are straightforward 59 @@ -295,7 +295,7 @@ and your data doesn't need any special processing 60 00:02:27,300 --> 00:02:29,673 -在准备训练之前。 +就可以使用上述的数据整理器 before being ready for training. 61 @@ -305,7 +305,7 @@ Notice that because different models 62 00:02:31,272 --> 00:02:33,690 -有不同的填充标记, +有不同的填充词元, have different padding tokens, 63 @@ -320,7 +320,7 @@ so it knows how to pad sequences properly. 65 00:02:40,150 --> 00:02:44,790 -默认的数据整理器不需要 Tokenizer 来工作, +默认的数据整理器不需要 Tokenizer 工作, The default data collator doesn't need a Tokenizer to work, 66 @@ -360,12 +360,12 @@ they're usually designed to handle one specific task. 73 00:02:59,490 --> 00:03:01,050 -所以,我要在这里展示一对。 +所以,我要在这里展示两个整理器。 And so, I'm going to show a couple here. 74 00:03:01,050 --> 00:03:04,320 -这些是 DataCollatorForTokenClassification +它们是 DataCollatorForTokenClassification These are DataCollatorForTokenClassification 75 @@ -375,7 +375,7 @@ and DataCollatorForSeqToSeq. 76 00:03:06,447 --> 00:03:09,540 -以及这些任务需要特殊整理器的原因 +而这些任务需要特殊整理器的原因 And the reason these tasks need special collators 77 @@ -385,7 +385,7 @@ is because their labels are variable in length. 78 00:03:12,600 --> 00:03:15,960 -在令牌分类中,每个令牌都有一个标签, +在词元分类中,每个词元都有一个标签, In token classification there's one label for each token, 79 @@ -400,32 +400,32 @@ is the length of the sequence. 81 00:03:20,280 --> 00:03:23,520 -而在 SeqToSeq 中,标签是一系列标记 +而在 SeqToSeq 中,标签是一系列词元 While in SeqToSeq the labels are a sequence of tokens 82 00:03:23,520 --> 00:03:24,780 -可以是可变长度, +它可以是可变长度, that can be variable length, 83 00:03:24,780 --> 00:03:25,800 -那可能会非常不同 +那可能会和输入序列 that can be very different 84 00:03:25,800 --> 00:03:28,200 -从输入序列的长度。 +的长度不同。 from the length of the input sequence. 85 00:03:28,200 --> 00:03:32,880 -所以在这两种情况下,我们处理整理那批 +所以在这两种情况下,我们通过填充标签 So in both of these cases, we handle collating that batch 86 00:03:32,880 --> 00:03:35,280 -也通过填充标签, +处理整理那批数据, by padding the labels as well, 87 @@ -435,17 +435,17 @@ as you can see here in this example. 88 00:03:37,410 --> 00:03:40,770 -因此,需要填充输入和标签 +因此,如果我们想加入可变长度的样本 So, inputs and the labels will need to be padded 89 00:03:40,770 --> 00:03:43,860 -如果我们想加入可变长度的样本 +到同一个小批量数据, if we want to join samples of variable length 90 00:03:43,860 --> 00:03:45,120 -进入同一个小批量。 +就需要填充输入和标签。 into the same minibatch. 91 @@ -460,17 +460,17 @@ and that's exactly what these data collators will do for us 93 00:03:50,460 --> 00:03:52,383 -你知道,为了这个特定的任务。 +你懂的,为了这个特定的任务。 you know, for this particular task. 94 00:03:53,820 --> 00:03:56,070 -所以,有一个最终的数据整理器 +那么,还有最后一个 So, there's one final data collator 95 00:03:56,070 --> 00:03:58,560 -我也想在本次讲座中向你展示。 +想在这里与大家分享的数据整理器。 I want to show you as well just in this lecture. 96 @@ -480,17 +480,17 @@ And that's the DataCollatorForLanguageModeling. 97 00:04:01,410 --> 00:04:03,390 -所以,这非常重要,首先, +嗯,它非常重要,首先, So, it's very important, and it's firstly, 98 00:04:03,390 --> 00:04:05,820 -因为语言模型是如此基础 +因为语言模型是我们这段时间以来 because language models are just so foundational 99 00:04:05,820 --> 00:04:09,720 -这些天我们用 NLP 所做的一切。 +处理 NLP 所涉及的最基本的概念。 to do for everything we do with NLP these days. 100 @@ -500,7 +500,7 @@ But secondly, because it has two modes 101 00:04:12,060 --> 00:04:14,760 -做两件截然不同的事情。 +可以完成两件截然不同的事情。 that do two very different things. 102 @@ -510,7 +510,7 @@ So you choose which mode you want with the mlm argument. 103 00:04:19,230 --> 00:04:22,470 -将其设置为 True 以进行掩码语言建模, +将其设置为 True 以进行屏蔽语言建模, Set it to True for masked language modeling, 104 @@ -535,7 +535,7 @@ The model is just making predictions 108 00:04:32,640 --> 00:04:35,460 -接下来是什么标记,所以你的标签 +接下来是什么词元,所以你的标签 for what token comes next, and so your labels 109 @@ -545,7 +545,7 @@ are more or less just a copy of your inputs, 110 00:04:37,800 --> 00:04:39,090 -整理人会处理 +整理器会处理你的输入 and the collator will handle that 111 @@ -560,7 +560,7 @@ When you set mlm to True though, 113 00:04:44,910 --> 00:04:46,786 -你会得到完全不同的行为, +你会得到完全不同的效果, you get quite different behavior, 114 @@ -575,7 +575,7 @@ and that's because setting mlm to True 116 00:04:51,660 --> 00:04:53,550 -表示掩码语言建模 +表示屏蔽语言建模 means masked language modeling 117 @@ -585,7 +585,7 @@ and that means the labels need to be, 118 00:04:55,680 --> 00:04:58,080 -你知道,输入需要被屏蔽。 +你懂的,输入需要被屏蔽。 you know, the inputs need to be masked. 119 @@ -605,7 +605,7 @@ the model is not predicting the next word, 122 00:05:06,570 --> 00:05:09,240 -相反,我们随机屏蔽掉一些标记 +相反,我们随机屏蔽掉一些词元 instead we randomly mask out some tokens 123 @@ -615,12 +615,12 @@ and the model predicts all of them at once. 124 00:05:11,130 --> 00:05:12,780 -所以,它试图填补空白 +所以,对于那些被屏蔽的词元 So, it tries to kinda fill in the blanks 125 00:05:12,780 --> 00:05:14,790 -对于那些被屏蔽的令牌。 +它试图填补空白。 for those masked tokens. 126 @@ -635,72 +635,72 @@ If we follow the protocol from the original BERT paper, 128 00:05:21,330 --> 00:05:23,970 -我们需要用掩码标记替换一些标记, +我们需要用掩码词元替换一些词元, we need to replace some tokens with a masked token, 129 00:05:23,970 --> 00:05:26,190 -一些其他带有随机令牌的令牌, +一些其他带有随机词元的词元, some other tokens with a random token, 130 00:05:26,190 --> 00:05:29,820 -然后保持第三组标记不变。 +然后保持第三组词元不变。 and then keep a third set of tokens unchanged. 131 00:05:29,820 --> 00:05:30,840 -是的,这不是讲座 +好吧,具体细节和我们这么做的原因 Yeah, this is not the lecture 132 00:05:30,840 --> 00:05:33,903 -进入细节或我们为什么这样做。 +就不在这里和大家详细说明了。 to go into the specifics of that or why we do it. 133 00:05:33,903 --> 00:05:36,660 -你可以随时查看原始的 BERT 论文 +如果你好奇的话,可以随时查看原始的 You can always check out the original BERT paper 134 00:05:36,660 --> 00:05:37,493 -如果你好奇的话。 +BERT 论文。 if you're curious. 135 00:05:37,493 --> 00:05:39,620 -写得很好。这很容易理解。 +它写得非常好。也很容易理解。 It's well written. It's easy to understand. 136 00:05:40,650 --> 00:05:44,190 -这里要知道的主要事情是它可能是一个真正的痛苦 +这里要知道的主要事情是自己实现起来 The main thing to know here is that it can be a real pain 137 00:05:44,190 --> 00:05:46,770 -并且自己实施起来非常复杂。 +是一件非常痛苦的事情,而且也非常地复杂。 and quite complex to implement that yourself. 138 00:05:46,770 --> 00:05:49,740 -但是 DataCollatorForLanguageModeling 会为你做 +但是当你将 mlm 设置为 True 时,DataCollatorForLanguageModeling But DataCollatorForLanguageModeling will do it for you 139 00:05:49,740 --> 00:05:51,750 -当你将 mlm 设置为 True 时。 +会为你完成。 when you set mlm to True. 140 00:05:51,750 --> 00:05:54,690 -这是一个更复杂的例子 +这是一些数据整理器所完成的 And that's an example of the more intricate 141 00:05:54,690 --> 00:05:57,870 -我们的一些数据整理人员所做的预处理。 +更加复杂的预处理操作。 preprocessing that some of our data collators do. 142 @@ -715,7 +715,7 @@ So, this covers the most commonly used data collators 144 00:06:01,920 --> 00:06:03,480 -以及它们用于执行的任务。 +以及它们所针对的具体任务。 and the tasks they're used for. 145 @@ -725,7 +725,7 @@ And hopefully, now you'll know when to use data collators 146 00:06:06,990 --> 00:06:10,833 -以及为你的特定任务选择哪一个。 +以及该为你的特定任务选择哪一个。 and which one to choose for your specific task. 147 diff --git a/subtitles/zh-CN/72_asking-for-help-on-the-forums.srt b/subtitles/zh-CN/72_asking-for-help-on-the-forums.srt index 3ccb3dda1..e4aa42002 100644 --- a/subtitles/zh-CN/72_asking-for-help-on-the-forums.srt +++ b/subtitles/zh-CN/72_asking-for-help-on-the-forums.srt @@ -20,17 +20,17 @@ 5 00:00:10,020 --> 00:00:11,640 -如果你有一般性问题 +如果你有一些问题 If you have a general question 6 00:00:11,640 --> 00:00:13,110 -或者正在调试你的代码, +或者正在调试你代码中的 bug, or are looking to debug your code, 7 00:00:13,110 --> 00:00:15,540 -论坛是提问的地方。 +那你可以去论坛提问。 the forums are the place to ask. 8 @@ -40,17 +40,17 @@ In this video we will teach you 9 00:00:16,710 --> 00:00:18,030 -如何写出一个好问题, +如何正确地在论坛中提问, how to write a good question, 10 00:00:18,030 --> 00:00:20,380 -以最大限度地提高你获得答案的机会。 +以使你尽快获得答案。 to maximize the chances you will get an answer. 11 00:00:21,570 --> 00:00:23,970 -首先,登录论坛, +首先,为了登录论坛, First things first, to login on the forums, 12 @@ -60,112 +60,112 @@ you need a Hugging Face account. 13 00:00:25,920 --> 00:00:27,750 -如果你还没有创建一个, +如果你还没有 Hugging Face 帐户, If you haven't created one already, 14 00:00:27,750 --> 00:00:31,080 -转到 hf.co 并单击注册。 +就访问 hf.co,然后单击注册。 go to hf.co and click sign up. 15 00:00:31,080 --> 00:00:32,780 -下面还有一个直接链接。 +也可以直接点击下面的链接。 There is also a direct link below. 16 00:00:33,750 --> 00:00:35,160 -填写你的邮箱和密码, +首先需要填写你的邮箱和密码, Fill your email and password, 17 00:00:35,160 --> 00:00:37,410 -然后继续选择你的用户名的步骤 +然后填写你的用户名 then continue the steps to pick your username 18 00:00:37,410 --> 00:00:38,860 -并更新头像。 +然后再更新一下头像。 and update a profile picture. 19 00:00:39,720 --> 00:00:43,200 -完成后,去 discuss.huggingface.co, +这些步骤完成后,访问 discuss.huggingface.co, Once this is done, go to discuss.huggingface.co, 20 00:00:43,200 --> 00:00:45,630 -下方链接,点击登录。 +或者点击下方的链接,然后点击登录(Log In)。 link below, and click log in. 21 00:00:45,630 --> 00:00:47,033 -使用与以下相同的登录信息 +使用前面注册的 Hugging Face 账户信息 Use the same login information as 22 00:00:47,033 --> 00:00:48,693 -Hugging Face 网站。 +登录。 for the Hugging Face website. 23 00:00:49,890 --> 00:00:51,300 -你可以通过单击搜索论坛 +你可以单击放大镜图标 You can search the forums by clicking 24 00:00:51,300 --> 00:00:52,800 -在放大镜上。 +来在论坛中搜索。 on the magnifying glass. 25 00:00:52,800 --> 00:00:55,710 -可能有人已经在某个主题中问过你的问题。 +因为可能有人已经在某个主题(Topic)中问过你想问的问题。 Someone may have already asked your question in a topic. 26 00:00:55,710 --> 00:00:58,260 -如果你发现你无法作为新用户发布新主题, +如果你发现你新注册的账户无法发布新主题, If you find you can't post a new topic as a new user, 27 00:00:58,260 --> 00:01:01,290 -这可能是因为反垃圾邮件过滤器。 +这可能是因为论坛的反垃圾信息过滤机制。 it may be because of the antispam filters. 28 00:01:01,290 --> 00:01:03,750 -确保你花一些时间阅读现有主题 +你需要花一段时间来阅读现有的主题, Make sure you spend some time reading existing topics 29 00:01:03,750 --> 00:01:05,370 -停用它。 +然后就可以发布新主题了。 to deactivate it. 30 00:01:05,370 --> 00:01:07,590 -当你确定你的问题还没有被问到时, +当你确定你的问题还没有人问过时, When you're sure your question hasn't been asked yet, 31 00:01:07,590 --> 00:01:09,660 -单击新主题按钮。 +单击新主题(New Topic)按钮。 click on the new topic button. 32 00:01:09,660 --> 00:01:12,600 -对于此示例,我们将使用以下代码, +在这里,我们将用 For this example, we'll use the following code, 33 00:01:12,600 --> 00:01:13,860 -产生错误, +「出现错误时该怎么办」视频中 that produces an error, 34 00:01:13,860 --> 00:01:16,660 -正如我们在 “遇到错误时该怎么办” 视频中看到的那样。 +报错的代码作为例子 as we saw in the "What to do when I get an error" video. 35 @@ -180,7 +180,7 @@ Since our error has to do with the Transformers library, 37 00:01:23,790 --> 00:01:24,903 -我们选择这个类别。 +所以我们选择这个类别。 we pick this category. 38 @@ -190,12 +190,12 @@ Next, choose a title that summarizes your error well. 39 00:01:29,880 --> 00:01:32,300 -不要太含糊,否则用户会遇到与你相同的错误 +标题不要太模糊,否则如果以后有用户遇到了与你相同的错误, Don't be too vague or users that get the same error you did 40 00:01:32,300 --> 00:01:34,773 -以后将无法找到你的主题。 +那么将无法搜索到你的主题。 in the future won't be able to find your topic. 41 @@ -205,47 +205,47 @@ Once you have finished typing your topic title, 42 00:01:38,370 --> 00:01:40,170 -确保问题没有得到回答 +确保你要问的问题在建议的相似主题中 make sure the question hasn't been answered 43 00:01:40,170 --> 00:01:42,690 -在 Discourse 主题中建议你。 +没有出现。 in the topics Discourse suggests you. 44 00:01:42,690 --> 00:01:44,190 -单击十字删除该窗口 +当你仔细检查之后, Click on the cross to remove that window 45 00:01:44,190 --> 00:01:46,230 -当你仔细检查时。 +就可以单击叉号关闭该窗口。 when you have double-checked. 46 00:01:46,230 --> 00:01:49,710 -这是发布错误时不应执行的操作的示例。 +这里展示的是一个不好的提问示例。 This is an example of what not to do when posting an error. 47 00:01:49,710 --> 00:01:51,120 -消息非常模糊, +这个示例中对问题的描述非常模糊, The message is very vague, 48 00:01:51,120 --> 00:01:53,370 -所以没有人能猜出哪里出了问题 +所以没有人能简单地判断出哪里出了问题 so no one else will be able to guess what went wrong 49 00:01:53,370 --> 00:01:55,623 -对你来说,它标记了太多人。 +另外,在这个问题中标记(@)了太多人。 for you, and it tags too many people. 50 00:01:56,490 --> 00:01:58,740 -标记人,尤其是版主, +标记别人,尤其是版主, Tagging people, especially moderators, 51 @@ -255,57 +255,57 @@ might have the opposite effect of what you want. 52 00:02:01,470 --> 00:02:04,380 -当你向他们发送通知时,他们会收到很多通知, +当你向他们发送通知的同时,他们也会收到很多别的通知, As you send them a notification, and they get plenty, 53 00:02:04,380 --> 00:02:06,300 -他们可能不会费心回复你, +所以他们可能不会费心回复你, they will probably not bother replying to you, 54 00:02:06,300 --> 00:02:09,300 -而你没有标记的用户可能会忽略这些问题, +而你没有标记的用户看到你标记了其他人, and users you didn't tag will probably ignore the questions, 55 00:02:09,300 --> 00:02:11,430 -因为他们看到被标记的用户。 +可能就会忽略这个问题。 since they see tagged users. 56 00:02:11,430 --> 00:02:13,697 -仅在你完全确定时才标记用户 +仅在你完全确定某个人特别适合回答这个问题时 Only tag a user when you are completely certain 57 00:02:13,697 --> 00:02:16,097 -他们是回答你问题的最佳场所。 +才去标记这个人。 they are the best place to answer your question. 58 00:02:17,730 --> 00:02:20,370 -文本要准确,如果出现错误 +文字描述要准确,如果你的某一段代码 Be precise in your text, and if you have an error coming 59 00:02:20,370 --> 00:02:22,710 -从一段特定的代码,包括该代码 +产生了错误,那么就要把这段代码 from a specific piece of code, include that code 60 00:02:22,710 --> 00:02:24,030 -在你的帖子中。 +包含在你的帖子中。 in your post. 61 00:02:24,030 --> 00:02:27,210 -为了确保你的帖子看起来不错,请提出你的问题 +为了确保你的帖子看起来美观,请把代码 To make sure your post looks good, place your question 62 00:02:27,210 --> 00:02:30,060 -在像这样的三个反引号之间。 +放在包含三个反引号(backticks)的两行之间。 between three backticks like this. 63 @@ -315,7 +315,7 @@ You can check on the right 64 00:02:30,990 --> 00:02:32,943 -发布后你的帖子将如何显示。 +帖子发布后的预览效果。 how your post will appear once posted. 65 @@ -325,47 +325,47 @@ If your question is about an error, 66 00:02:35,850 --> 00:02:38,640 -最好包括完整的回溯。 +最好包括完整的报错信息。 it's even better to include the full traceback. 67 00:02:38,640 --> 00:02:41,610 -正如 “遇到错误时该怎么办” 视频中所述, +正如「出现错误时该怎么办」视频中所述, As explained in the "What to do when I get an error" video, 68 00:02:41,610 --> 00:02:43,763 -如果你使用的是 Colab,请展开回溯。 +如果你使用的是 Colab,请展开回溯(traceback)。 expand the traceback if you're on Colab. 69 00:02:44,769 --> 00:02:45,990 -就像代码一样,把它 +和代码一样,把它们 Like for the code, put it 70 00:02:45,990 --> 00:02:48,300 -在包含三个反引号的两行之间 +放在包含三个反引号的两行之间 between two lines containing three backticks 71 00:02:48,300 --> 00:02:50,160 -正确格式化。 +看起来会比较美观。 for proper formatting. 72 00:02:50,160 --> 00:02:52,740 -我们最后的建议是记住要友善。 +我们最后的建议是语气要温和, Our last advice is to remember to be nice. 73 00:02:52,740 --> 00:02:54,540 -一句 “请” 和一句 “谢谢” 将大有帮助 +使用「请」和「谢谢」将让别人 A "Please," and a "Thank you" will go a long way 74 00:02:54,540 --> 00:02:56,490 -让别人帮助你。 +更乐意帮助你。 into getting others to help you. 75 diff --git a/subtitles/zh-CN/73_debugging-the-training-pipeline-(pytorch).srt b/subtitles/zh-CN/73_debugging-the-training-pipeline-(pytorch).srt index d12bee78e..c4b8ad020 100644 --- a/subtitles/zh-CN/73_debugging-the-training-pipeline-(pytorch).srt +++ b/subtitles/zh-CN/73_debugging-the-training-pipeline-(pytorch).srt @@ -5,18 +5,18 @@ 2 00:00:08,760 --> 00:00:11,896 -你在运行 Trainer.train 时遇到 +你在运行 Trainer.train 时你会遇到的 you encounter when running Trainer.train 3 00:00:11,896 --> 00:00:15,066 -例如,我们将使用这个微调脚本 +作为一个例子,我们将使用这个脚本微调 As an example, we will use this script that finetunes 4 00:00:15,066 --> 00:00:17,760 -GLUE MNLI 数据集上的 bert 模型。 -a bert model on the GLUE MNLI dataset. +一个 BERT 模型, 在 GLUE MNLI 数据集上。 +a BERT model on the GLUE MNLI dataset. 5 00:00:17,760 --> 00:00:19,470 @@ -25,7 +25,7 @@ Checkout the videos linked below 6 00:00:19,470 --> 00:00:21,840 -看看我们是如何得出这样一个脚本的。 +以看看我们是如何得出这样一个脚本的。 to see how we came to such a script. 7 @@ -40,7 +40,7 @@ Running the script gives us an error pretty quickly. 9 00:00:28,110 --> 00:00:29,040 -它发生在线上 +它发生在这一行 It happens at the line 10 @@ -55,12 +55,12 @@ according to the traceback. 12 00:00:32,850 --> 00:00:34,702 -这告诉我们那里有问题, +这告诉我们这里有问题, That tells us there is a problem there, 13 00:00:34,702 --> 00:00:37,881 -但问题可能来自许多不同的原因。 +但问题可能有许多不同的原因。 but the problem could come from many different causes. 14 @@ -70,7 +70,7 @@ To debug an error in a training, 15 00:00:39,330 --> 00:00:41,760 -你需要确保训练管道的每一步 +你需要确保训练 pipeline 的每一步 you need to make sure each step of the training pipeline 16 @@ -90,12 +90,12 @@ are correct, 19 00:00:47,040 --> 00:00:48,720 -你可以把它们批在一起, +你可以把它们分批在一起, you can batch them together, 20 00:00:48,720 --> 00:00:50,790 -通过模型喂养他们以获得损失, +把他们通过模型以获得损失, feed them through the model to get a loss, 21 @@ -115,7 +115,7 @@ So let's start by looking at the training dataset 24 00:00:57,810 --> 00:00:59,043 -这个培训师正在使用。 +这个 Trainer 正在使用。 this Trainer is using. 25 @@ -140,7 +140,7 @@ did not get input IDs 29 00:01:08,220 --> 00:01:11,100 -我们确实没有数据集中的那些。 +我们数据集中确实没有那些。 and we do not have those in the dataset indeed. 30 @@ -155,7 +155,7 @@ we can see we made a mistake 32 00:01:14,400 --> 00:01:17,400 -并将错误的数据集传递给培训师。 +并将错误的数据集传递给 Trainer 。 and passed the wrong datasets to the Trainer. 33 @@ -175,17 +175,17 @@ Inspecting the traceback 36 00:01:23,130 --> 00:01:25,860 -告诉我们当我们尝试创建批处理时会发生这种情况, +告诉我们当我们尝试创建批处理时会发生, tells us it happens when we try to create a batch, 37 00:01:25,860 --> 00:01:28,743 -专门用于对张量中的特征进行分组。 +特别对于对张量中的特征进行分组。 specifically to group the features in a tensor. 38 00:01:29,700 --> 00:01:32,610 -我们可以通过要求培训师给我们一批来确认这一点 +我们可以通过要求 Trainer 给我们一分批来确认这一点 We can confirm this by asking the Trainer to get us a batch 39 @@ -195,7 +195,7 @@ of the training data loader, 40 00:01:34,230 --> 00:01:35,913 -重现相同的错误。 +复现相同的错误。 which reproduces the same error. 41 @@ -210,12 +210,12 @@ we can then see they are not all of the same size. 43 00:01:42,870 --> 00:01:45,120 -这是因为我们还没有通过数据整理器 +这是因为我们还没有输入数据整理器 This is because we have not passed a data collator 44 00:01:45,120 --> 00:01:46,890 -对培训师进行填充 +对 Trainer 进行填充 to do the padding to the Trainer 45 @@ -230,7 +230,7 @@ Padding inside the Trainer is normally the default, 47 00:01:52,710 --> 00:01:55,380 -但前提是你将分词器提供给培训师, +但前提是你将 tokenizer 提供给 Trainer , but only if you provide your tokenizer to the Trainer, 48 @@ -265,7 +265,7 @@ so you have to restart your notebook from the beginning 54 00:02:13,260 --> 00:02:16,950 -第二,追溯对那些人来说完全没用。 +第二,追溯对那些来说完全没用。 and two, the traceback is completely useless for those. 55 @@ -345,7 +345,7 @@ But the GPU still hasn't finished 70 00:02:54,180 --> 00:02:55,710 -模型的前向传递 +模型的前向传播 the forward pass of the model 71 @@ -390,13 +390,13 @@ who raises the error at the wrong place. 79 00:03:16,350 --> 00:03:18,720 -所以要调试这个,我们需要执行接下来的步骤 -So to debug this, we will need to execute the next steps +所以要调试这个,我们需要执行 +So to debug this, we will need to execute 80 00:03:18,720 --> 00:03:21,211 -CPU 上的训练流水线。 -of the training pipeline on the CPU. +接下来 CPU 上的训练流水线的步骤。 +the next steps of the training pipeline on the CPU. 81 00:03:21,211 --> 00:03:22,380 @@ -420,7 +420,7 @@ the error actually happens during the forward pass 85 00:03:28,620 --> 00:03:29,453 -模型的, +对模型, of the model, 86 @@ -490,12 +490,12 @@ but here is how we would debug the next step 99 00:04:00,960 --> 00:04:02,944 -管道,梯度计算, + pipeline ,梯度计算, of the pipeline, gradient computation, 100 00:04:02,944 --> 00:04:05,850 -以及优化器步骤。 +以及优化器更新。 as well as the optimizer step. 101 diff --git a/subtitles/zh-CN/74_debugging-the-training-pipeline-(tensorflow).srt b/subtitles/zh-CN/74_debugging-the-training-pipeline-(tensorflow).srt index 0bf09b45a..176dbbe54 100644 --- a/subtitles/zh-CN/74_debugging-the-training-pipeline-(tensorflow).srt +++ b/subtitles/zh-CN/74_debugging-the-training-pipeline-(tensorflow).srt @@ -90,7 +90,7 @@ So, this is going to be a video about what to do 19 00:00:50,370 --> 00:00:52,410 -当你遇到那些噩梦中的一个错误时 +当你遇到那些噩梦般的一个错误时 when you run into one of those nightmare bugs 20 @@ -135,7 +135,7 @@ First, we do all our imports, we load a dataset, 28 00:01:16,410 --> 00:01:20,280 -我们创建了分词器并对数据集进行分词。 +我们创建了 tokenizer 并对数据集进行分词。 we create our tokenizer and we tokenize the dataset. 29 @@ -150,12 +150,12 @@ so that's tf.data.Dataset, 31 00:01:26,100 --> 00:01:28,500 -这样我们就可以适应它们, +这样我们就可以拟合它们, and that's so that we can run fit on them, 32 00:01:28,500 --> 00:01:31,170 -然后我们从预训练的检查点加载我们的模型, +然后我们从预训练的 checkpoint 加载我们的模型, and then we load our model from a pretrained checkpoint, 33 @@ -205,7 +205,7 @@ We tried to train on our data, but we got no gradient? 42 00:01:55,470 --> 00:01:59,130 -这很令人困惑,我的意思是,我们如何开始 +这很令人困惑,我的意思是,我们从何开始 It's pretty perplexing, I mean, how do we even begin 43 @@ -275,7 +275,7 @@ and that's because it's right at the end 56 00:02:31,560 --> 00:02:33,990 -的数据管道。 +的数据 pipeline 。 of the data pipeline. 57 @@ -285,7 +285,7 @@ And so that means that if those outputs are good, 58 00:02:36,990 --> 00:02:39,990 -你可以保证你的数据管道运行良好。 +你可以保证你的数据 pipeline 运行良好。 you're guaranteed that your data pipeline is working well. 59 @@ -300,7 +300,7 @@ for one iteration and then breaking, 61 00:02:44,790 --> 00:02:46,980 -这给了我们一个批次。 +这给了我们一个分批。 and that gives us a single batch. 62 @@ -360,7 +360,7 @@ the labels need to be passed in the input dictionary, 73 00:03:15,960 --> 00:03:17,940 -模型可以看到它们的地方。 +其模型可以看到它们。 where the model can see them. 74 @@ -430,7 +430,7 @@ or we keep using Keras losses 87 00:03:46,980 --> 00:03:50,520 -但我们将标签移动到 Keras 期望的位置。 +但我们将标签移动到 Keras 期望的地方。 but we move the labels to the place Keras expects them. 88 @@ -465,7 +465,7 @@ So we recompile with that, we call model.fit, what happens? 94 00:04:08,220 --> 00:04:13,050 -好吧,这次它运行了,但现在我们失去了 NaN。 +好吧,这次它运行了,但现在我们消失了 NaN。 Well, it runs this time but now we get a loss of NaN. 95 @@ -495,7 +495,7 @@ all the weights are NaN as well, as well as the loss. 100 00:04:27,600 --> 00:04:30,810 -所以一旦一个 NaN 悄悄进入你的计算, +所以一旦一个 NaN 悄悄爬进你的计算, So once a single NaN creeps into your computations, 101 @@ -515,17 +515,17 @@ it gets to the gradient, 104 00:04:37,530 --> 00:04:38,910 -然后一旦它处于渐变中 +然后一旦它处于梯度中 and then once it's in the gradient 105 00:04:38,910 --> 00:04:41,280 -它进入重量更新, +它进入权重更新, it enters the weight updates, 106 00:04:41,280 --> 00:04:43,980 -然后你所有的体重更新最终也都是 NaN 。 +然后你所有的权重更新最终也都是 NaN 。 and then all your weight updates end up as NaN as well. 107 @@ -630,8 +630,8 @@ because 0 is a label as well. 127 00:05:33,630 --> 00:05:35,070 -所以我们失去了 NaN -So we got a loss of NaN +所以我们得到大量 NaN +So we got a lots of NaN 128 00:05:35,070 --> 00:05:37,887 @@ -655,7 +655,7 @@ so we can set num_labels=3 132 00:05:45,870 --> 00:05:48,540 -当我们初始化模型但来自_pretrained 时, +当我们初始化模型用 from_pretrained 时, when we initialize the model but from_pretrained, 133 @@ -670,7 +670,7 @@ So, now we think our data is good and our model is good 135 00:05:54,660 --> 00:05:56,220 -所以培训应该有效 +所以训练应该有效 and so training should work 136 @@ -695,7 +695,7 @@ but it's not going down very quickly 140 00:06:06,090 --> 00:06:07,770 -如果我们一直用完这个, +如果我们一直运行这个, and if we keep running this out, 141 @@ -790,7 +790,7 @@ So this learning rate is way too high 159 00:06:50,550 --> 00:06:52,530 -用于训练变压器模型, +用于训练 transformer 模型, for training transformer models, 160 @@ -820,7 +820,7 @@ So if you recompile with that, 165 00:07:06,990 --> 00:07:09,840 -你最终会发现培训确实有效。 +你最终会发现训练确实有效。 you'll find that training actually works, at last. 166 @@ -860,7 +860,7 @@ and see what the errors look like 173 00:07:23,490 --> 00:07:25,380 -以及如何接近他们, +以及如何解决他们, and how you can approach them, 174 diff --git a/subtitles/zh-CN/75_writing-a-good-issue.srt b/subtitles/zh-CN/75_writing-a-good-issue.srt index 1afee7ceb..08a599f02 100644 --- a/subtitles/zh-CN/75_writing-a-good-issue.srt +++ b/subtitles/zh-CN/75_writing-a-good-issue.srt @@ -20,7 +20,7 @@ and you should always go there to report a bug 5 00:00:14,010 --> 00:00:16,020 -或要求新功能。 +或求新功能。 or ask for a new feature. 6 @@ -40,17 +40,17 @@ It's very important to write good issues 9 00:00:23,677 --> 00:00:27,232 -因为它将帮助你立即修复发现的错误。 +因为它将帮助你发现的错误被立即修复。 as it will help the bug you uncovered be fixed in no time. 10 00:00:27,232 --> 00:00:29,750 -对于这个视频,我们创建了一个版本的变形金刚 +对于这个视频,我们创建了一个版本的 Transformers For this video, we have created a version of Transformers 11 00:00:29,750 --> 00:00:31,066 -有一个错误。 +带有一个错误。 with a bug. 12 @@ -70,7 +70,7 @@ In this version, the following example fails. 15 00:00:41,016 --> 00:00:42,472 -错误相当神秘 +错误是相当神秘的 The error is rather cryptic 16 @@ -105,7 +105,7 @@ In our case, inspecting the traceback, 22 00:00:56,802 --> 00:00:59,645 -我们看到失败发生在管道函数内部 +我们看到失败发生在 pipeline 数内部 we see the failure happens inside the pipeline function 23 @@ -150,7 +150,7 @@ but it also will make it easier for the maintainers 31 00:01:20,610 --> 00:01:22,320 -解决你的问题。 +来解决你的问题。 to fix your problem. 32 @@ -160,7 +160,7 @@ Here we can play around a bit more with this code 33 00:01:24,030 --> 00:01:26,460 -并注意错误发生在不同的检查点 +并注意错误发生在不同的 checkpoints and notice the error happens for different checkpoints 34 @@ -175,7 +175,7 @@ and that it disappears when we use use_fast=False 36 00:01:31,262 --> 00:01:33,240 -在我们的分词器调用中。 +在我们的 tokenizer 调用中。 inside our tokenizer call. 37 @@ -190,7 +190,7 @@ that does not depend on any external files or data. 39 00:01:38,640 --> 00:01:40,350 -尝试用假值替换你的数据 +尝试用虚假的值替换你的数据 Try to replace your data by fake values 40 @@ -205,12 +205,12 @@ With all of this done, 42 00:01:43,620 --> 00:01:46,260 -我们准备开始写我们的问题。 +我们准备开始写我们的 issue 。 we are ready to start writing our issue. 43 00:01:46,260 --> 00:01:48,600 -单击错误报告旁边的按钮 +单击 Bug Report (错误报告) 旁边的按钮 Click on the button next to Bug Report 44 @@ -225,7 +225,7 @@ It will only take you a couple of minutes. 46 00:01:53,940 --> 00:01:56,460 -首先是正确命名你的问题。 +首先是正确命名你的 issue 。 The first thing is to properly name your issue. 47 @@ -280,7 +280,7 @@ There is a full list of usernames in the template. 57 00:02:23,010 --> 00:02:25,043 -由于我们的问题与分词器有关, +由于我们的问题与 tokenizer 有关, Since our issue has to do with tokenizers, 58 @@ -350,7 +350,7 @@ With all of this, you should expect an answer 71 00:02:57,030 --> 00:02:59,880 -非常快地解决你的问题,希望能快速解决。 +非常快地解决你的 issue ,希望能快速解决。 to your issue pretty fast and hopefully a quick fix. 72 diff --git a/utils/generate_subtitles.py b/utils/generate_subtitles.py index 31dccbe2c..7de26249e 100644 --- a/utils/generate_subtitles.py +++ b/utils/generate_subtitles.py @@ -1,31 +1,45 @@ import pandas as pd -from tqdm.auto import tqdm from youtube_transcript_api import YouTubeTranscriptApi from youtube_transcript_api.formatters import SRTFormatter from youtubesearchpython import Playlist from pathlib import Path import argparse -import sys +COURSE_VIDEOS_PLAYLIST = "https://youtube.com/playlist?list=PLo2EIpI_JMQvWfQndUesu0nPBAtZ9gP1o" +TASK_VIDEOS_PLAYLIST = "https://youtube.com/playlist?list=PLo2EIpI_JMQtyEr-sLJSy5_SnLCb4vtQf" +# These videos are not part of the course, but are part of the task playlist +TASK_VIDEOS_TO_SKIP = ["tjAIM7BOYhw", "WdAeKSOpxhw", "KWwzcmG98Ds", "TksaY_FDgnk", "leNG9fN9FQU", "dKE8SIt9C-w"] -def generate_subtitles(language: str, youtube_language_code: str = None): + +def generate_subtitles(language: str, youtube_language_code: str = None, is_task_playlist: bool = False): metadata = [] formatter = SRTFormatter() path = Path(f"subtitles/{language}") path.mkdir(parents=True, exist_ok=True) - playlist_videos = Playlist.getVideos("https://youtube.com/playlist?list=PLo2EIpI_JMQvWfQndUesu0nPBAtZ9gP1o") + if is_task_playlist: + playlist_videos = Playlist.getVideos(TASK_VIDEOS_PLAYLIST) + else: + playlist_videos = Playlist.getVideos(COURSE_VIDEOS_PLAYLIST) for idx, video in enumerate(playlist_videos["videos"]): video_id = video["id"] title = video["title"] title_formatted = title.lower().replace(" ", "-").replace(":", "").replace("?", "") id_str = f"{idx}".zfill(2) - srt_filename = f"subtitles/{language}/{id_str}_{title_formatted}.srt" + + if is_task_playlist: + srt_filename = f"{path}/tasks_{id_str}_{title_formatted}.srt" + else: + srt_filename = f"{path}/{id_str}_{title_formatted}.srt" # Skip course events if "Event Day" in title: continue + # Skip task videos that don't belong to the course + if video_id in TASK_VIDEOS_TO_SKIP: + continue + # Get transcript transcript_list = YouTubeTranscriptApi.list_transcripts(video_id) english_transcript = transcript_list.find_transcript(language_codes=["en", "en-US"]) @@ -51,10 +65,15 @@ def generate_subtitles(language: str, youtube_language_code: str = None): f.write("No transcript found for this video!") metadata.append({"id": video_id, "title": title, "link": video["link"], "srt_filename": srt_filename}) - break df = pd.DataFrame(metadata) - df.to_csv(f"subtitles/{language}/metadata.csv", index=False) + + if is_task_playlist: + df.to_csv(f"{path}/metadata_tasks.csv", index=False) + else: + df.to_csv(f"{path}/metadata.csv", index=False) + + if __name__ == "__main__": @@ -62,5 +81,6 @@ def generate_subtitles(language: str, youtube_language_code: str = None): parser.add_argument("--language", type=str, help="Language to generate subtitles for") parser.add_argument("--youtube_language_code", type=str, help="YouTube language code") args = parser.parse_args() - generate_subtitles(args.language, args.youtube_language_code) + generate_subtitles(args.language, args.youtube_language_code, is_task_playlist=False) + generate_subtitles(args.language, args.youtube_language_code, is_task_playlist=True) print(f"All done! Subtitles stored at subtitles/{args.language}")