Transformers documentation

Моделирование языка с причинно-следственной связью

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Моделирование языка с причинно-следственной связью

Существует два типа языкового моделирования: каузальное и маскированное. В этом руководстве рассматривается каузальное языковое моделирование. Каузальные языковые модели часто используются для генерации текста. Вы можете использовать эти модели для творческих приложений, таких как текстовые квесты с выбором вариантов или интеллектуальные помощники по написанию кода, например Copilot или CodeParrot.

Моделирование языка с причинно-следственной связью (causal language modeling) предсказывает следующий токен в последовательности токенов, при этом модель может обращать внимание только на токены, расположенные слева. Это означает, что модель не видит будущие токены. Примером модели с причинно-следственным моделированием языка является GPT-2.

В этом руководстве показано, как:

  1. Дообучите DistilGPT2 на подмножестве r/askscience набора данных ELI5.
  2. Используйте вашу дообученную модель для инференса.

Чтобы увидеть все архитектуры и контрольные точки, совместимые с этой задачей, рекомендуем обратиться к странице задачи

Прежде чем начать, убедитесь, что у вас установлены все необходимые библиотеки:

pip install transformers datasets evaluate

Мы рекомендуем вам войти в свой аккаунт Hugging Face, чтобы вы могли загрузить свою модель и поделиться ею с сообществом. При появлении запроса введите токен для входа:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

Загрузить набор данных ELI5

Начните с загрузки первых 5000 примеров из набора данных ELI5-Category с помощью библиотеки 🤗 Datasets. Это позволит вам провести эксперименты и убедиться, что всё работает, прежде чем тратить время на обучение на полном наборе данных.

>>> from datasets import load_dataset

>>> eli5 = load_dataset("dany0407/eli5_category", split="train[:5000]")

Разбейте train-подмножество датасета на обучающую и тестовую выборки с помощью метода train_test_split:

>>> eli5 = eli5.train_test_split(test_size=0.2)

Теперь рассмотрим пример:

>>> eli5["train"][0]
{'q_id': '7h191n',
 'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
 'selftext': '',
 'category': 'Economics',
 'subreddit': 'explainlikeimfive',
 'answers': {'a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
  'text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
   'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
   'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
   'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
  'score': [21, 19, 5, 3],
  'text_urls': [[],
   [],
   [],
   ['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']]},
 'title_urls': ['url'],
 'selftext_urls': ['url']}

Хотя это может показаться большим объёмом, на самом деле вас интересует только поле text. В задачах языкового моделирования не нужны метки (это также называется задачей без учителя), потому что следующее слово и есть метка.

Предобработка

Следующий шаг — загрузить токенизатор DistilGPT2 для обработки подполя text:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilgpt2")

Как видно из примера выше, поле text вложенно внутри answers. Это означает, что вам нужно будет извлечь подполе text из его вложенной структуры с помощью метода flatten:

>>> eli5 = eli5.flatten()
>>> eli5["train"][0]
{'q_id': '7h191n',
 'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
 'selftext': '',
 'category': 'Economics',
 'subreddit': 'explainlikeimfive',
 'answers.a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
 'answers.text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
  'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
  'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
  'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
 'answers.score': [21, 19, 5, 3],
 'answers.text_urls': [[],
  [],
  [],
  ['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']],
 'title_urls': ['url'],
 'selftext_urls': ['url']}

Каждое подполе теперь представляет собой отдельный столбец, что обозначено префиксом answers, а поле text теперь является списком. Вместо токенизации каждого предложения по отдельности преобразуйте список в строку, чтобы токенизировать их совместно.

Вот первая функция предобработки, которая объединяет список строк для каждого примера и токенизирует результат:

>>> def preprocess_function(examples):
...     return tokenizer([" ".join(x) for x in examples["answers.text"]])

Чтобы применить эту функцию предобработки ко всему набору данных, используйте метод map библиотеки 🤗 Datasets. Вы можете ускорить работу функции map, установив batched=True для обработки нескольких элементов набора данных за один раз, а также увеличив количество процессов с помощью num_proc. Удалите все столбцы, которые вам не нужны:

>>> tokenized_eli5 = eli5.map(
...     preprocess_function,
...     batched=True,
...     num_proc=4,
...     remove_columns=eli5["train"].column_names,
... )

Этот набор данных содержит последовательности токенов, однако некоторые из них длиннее максимальной длины входных данных для модели.

Теперь вы можете использовать вторую функцию предобработки, чтобы

  • объединить все последовательности
  • разделить объединённые последовательности на более короткие фрагменты, определяемые параметром block_size, которые должны быть короче максимальной длины входных данных и достаточно короткими для вашей GPU RAM.
>>> block_size = 128


>>> def group_texts(examples):
...     # Concatenate all texts.
...     concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
...     total_length = len(concatenated_examples[list(examples.keys())[0]])
...     # We drop the small remainder, we could add padding if the model supported it instead of this drop, you can
...     # customize this part to your needs.
...     if total_length >= block_size:
...         total_length = (total_length // block_size) * block_size
...     # Split by chunks of block_size.
...     result = {
...         k: [t[i : i + block_size] for i in range(0, total_length, block_size)]
...         for k, t in concatenated_examples.items()
...     }
...     result["labels"] = result["input_ids"].copy()
...     return result

Примените функцию group_texts ко всему набору данных:

>>> lm_dataset = tokenized_eli5.map(group_texts, batched=True, num_proc=4)

Теперь создайте пакет примеров с помощью DataCollatorForLanguageModeling. При сборке пакета эффективнее динамически дополнять предложения до максимальной длины в пакете, чем дополнять весь датасет до максимальной длины.

Используйте токен конца последовательности в качестве токена отступов и установите mlm=False. В этом случае входные данные будут использоваться в качестве меток, сдвинутых вправо на один элемент:

>>> from transformers import DataCollatorForLanguageModeling

>>> tokenizer.pad_token = tokenizer.eos_token
>>> data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)

Обучение

Если вы не знакомы с дообучением модели с помощью Trainer, загляните в базовое руководство!

Теперь вы готовы приступить к обучению модели! Загрузите DistilGPT2 с помощью AutoModelForCausalLM:

>>> from transformers import AutoModelForCausalLM, TrainingArguments, Trainer

>>> model = AutoModelForCausalLM.from_pretrained("distilbert/distilgpt2")

На этом этапе осталось выполнить только три шага:

  1. Определите гиперпараметры обучения в TrainingArguments. Единственный обязательный параметр — output_dir, который указывает, куда сохранять модель. Вы загрузите эту модель на Hub, установив push_to_hub=True (для загрузки модели необходимо войти в аккаунт Hugging Face).
  2. Передайте аргументы обучения в Trainer вместе с моделью, наборами данных и коллаборатором данных.
  3. Вызовите train(), чтобы дообучить модель.
>>> training_args = TrainingArguments(
...     output_dir="my_awesome_eli5_clm-model",
...     eval_strategy="epoch",
...     learning_rate=2e-5,
...     weight_decay=0.01,
...     push_to_hub=True,
... )

>>> trainer = Trainer(
...     model=model,
...     args=training_args,
...     train_dataset=lm_dataset["train"],
...     eval_dataset=lm_dataset["test"],
...     data_collator=data_collator,
...     processing_class=tokenizer,
... )

>>> trainer.train()

После завершения обучения используйте метод evaluate(), чтобы оценить модель и получить её perplexity:

>>> import math

>>> eval_results = trainer.evaluate()
>>> print(f"Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
Perplexity: 49.61

Затем опубликуйте свою модель на Hub с помощью метода push_to_hub(), чтобы все могли ею пользоваться:

>>> trainer.push_to_hub()

Более подробный пример дообучения модели для каузального языкового моделирования можно найти в соответствующем ноутбуке PyTorch.

Вывод

Отлично, теперь, когда вы дообучили модель, вы можете использовать её для инференса!

Придумайте промпт, по которому вы хотите сгенерировать текст:

>>> prompt = "Somatic hypermutation allows the immune system to"

Самый простой способ проверить вашу дообученную модель для инференса — использовать её в pipeline(). Создайте экземпляр pipeline для генерации текста с вашей моделью и передайте в него текст:

>>> from transformers import pipeline

>>> generator = pipeline("text-generation", model="username/my_awesome_eli5_clm-model")
>>> generator(prompt)
[{'generated_text': "Somatic hypermutation allows the immune system to be able to effectively reverse the damage caused by an infection.\n\n\nThe damage caused by an infection is caused by the immune system's ability to perform its own self-correcting tasks."}]

Токенизируйте текст и верните input_ids в виде тензоров PyTorch:

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("username/my_awesome_eli5_clm-model")
>>> inputs = tokenizer(prompt, return_tensors="pt").input_ids

Используйте метод generate() для генерации текста. Подробнее о различных стратегиях генерации текста и параметрах управления процессом генерации см. на странице Стратегии генерации текста.

>>> from transformers import AutoModelForCausalLM

>>> model = AutoModelForCausalLM.from_pretrained("username/my_awesome_eli5_clm-model")
>>> outputs = model.generate(inputs, max_new_tokens=100, do_sample=True, top_k=50, top_p=0.95)

Декодировать сгенерированные идентификаторы токенов обратно в текст:

>>> tokenizer.batch_decode(outputs, skip_special_tokens=True)
["Somatic hypermutation allows the immune system to react to drugs with the ability to adapt to a different environmental situation. In other words, a system of 'hypermutation' can help the immune system to adapt to a different environmental situation or in some cases even a single life. In contrast, researchers at the University of Massachusetts-Boston have found that 'hypermutation' is much stronger in mice than in humans but can be found in humans, and that it's not completely unknown to the immune system. A study on how the immune system"]
Update on GitHub