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

Стратегии генерации

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

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

Базовые методы декодирования

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

Жадный поиск

Жадный поиск — это стратегия декодирования по умолчанию. На каждом шаге он выбирает наиболее вероятный следующий токен. Если в GenerationConfig не указано иное, эта стратегия генерирует не более 20 новых токенов.

Жадный поиск хорошо работает для задач с относительно короткими выводами, где креативность не является приоритетом. Однако он дает сбой при генерации более длинных последовательностей, поскольку начинает повторяться.

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from accelerate import Accelerator

device = Accelerator().device

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to(device)

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", dtype=torch.float16).to(device)
# explicitly set to default length because Llama2 generation length is 4096
outputs = model.generate(**inputs, max_new_tokens=20)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
'Hugging Face is an open-source company that provides a suite of tools and services for building, deploying, and maintaining natural language processing'

Выборка

Сэмплинг, или мультиномиальное сэмплирование, случайно выбирает токен на основе распределения вероятностей по всему словарю модели (в отличие от наиболее вероятного токена, как при жадном поиске). Это означает, что каждый токен с ненулевой вероятностью имеет шанс быть выбранным. Стратегии сэмплирования снижают повторения и позволяют генерировать более креативные и разнообразные результаты.

Включите выборку по мультиномиальному распределению с do_sample=True и num_beams=1.

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from accelerate import Accelerator

device = Accelerator().device

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to(device)

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", dtype=torch.float16).to(device)
# explicitly set to 100 because Llama2 generation length is 4096
outputs = model.generate(**inputs, max_new_tokens=50, do_sample=True, num_beams=1)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
'Hugging Face is an open-source company 🤗\nWe are open-source and believe that open-source is the best way to build technology. Our mission is to make AI accessible to everyone, and we believe that open-source is the best way to achieve that.'

Поиск по лучам

Beam search отслеживает несколько сгенерированных последовательностей (лучей) на каждом шаге. После определённого числа шагов он выбирает последовательность с наибольшей общей вероятностью. В отличие от жадного поиска, эта стратегия может «заглядывать вперёд» и выбирать последовательность с более высокой общей вероятностью, даже если начальные токены имеют меньшую вероятность. Она лучше всего подходит для задач, привязанных к входным данным, таких как описание изображения или распознавание речи. Вы также можете использовать do_sample=True вместе с beam search, чтобы выполнять выборку на каждом шаге, однако beam search всё равно будет жадно отсекать последовательности с низкой вероятностью между шагами.

Ознакомьтесь с визуализатором beam search, чтобы увидеть, как работает beam search.

Включите beam search с помощью параметра num_beams (значение должно быть больше 1, иначе поиск эквивалентен жадному).

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from accelerate import Accelerator

device = Accelerator().device

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
inputs = tokenizer("Hugging Face is an open-source company", return_tensors="pt").to(device)

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf", dtype=torch.float16).to(device)
# explicitly set to 100 because Llama2 generation length is 4096
outputs = model.generate(**inputs, max_new_tokens=50, num_beams=2)
tokenizer.batch_decode(outputs, skip_special_tokens=True)
"['Hugging Face is an open-source company that develops and maintains the Hugging Face platform, which is a collection of tools and libraries for building and deploying natural language processing (NLP) models. Hugging Face was founded in 2018 by Thomas Wolf']"

Пользовательские методы генерации

Пользовательские методы генерации обеспечивают специализированное поведение, такое как:

  • позволить модели продолжать размышлять, если она не уверена;
  • откатить генерацию, если модель застряла;
  • обрабатывать специальные токены с помощью пользовательской логики;
  • использовать специализированные кэши KV;

Мы поддерживаем пользовательские методы генерации через репозитории моделей, предполагая наличие определённого тега модели и структуры файлов (см. подраздел ниже). Эта функция является расширением пользовательского кода моделирования и, как и он, требует установки trust_remote_code=True.

Если репозиторий модели содержит собственный метод генерации, самый простой способ его проверить — загрузить модель и выполнить генерацию:

from transformers import AutoModelForCausalLM, AutoTokenizer

# `transformers-community/custom_generate_example` holds a copy of `Qwen/Qwen2.5-0.5B-Instruct`, but
# with custom generation code -> calling `generate` uses the custom generation method!
tokenizer = AutoTokenizer.from_pretrained("transformers-community/custom_generate_example")
model = AutoModelForCausalLM.from_pretrained(
    "transformers-community/custom_generate_example", device_map="auto", trust_remote_code=True
)

inputs = tokenizer(["The quick brown"], return_tensors="pt").to(model.device)
# The custom generation method is a minimal greedy decoding implementation. It also prints a custom message at run time.
gen_out = model.generate(**inputs)
# you should now see its custom message, "✨ using a custom generation method ✨"
print(tokenizer.batch_decode(gen_out, skip_special_tokens=True))
'The quick brown fox jumps over a lazy dog, and the dog is a type of animal. Is'

Репозитории моделей с кастомными методами генерации обладают особым свойством: их метод генерации можно загрузить из любой модели через аргумент custom_generate функции generate(). Это означает, что любой пользователь может создать и поделиться своим кастомным методом генерации, который потенциально будет работать с любой моделью Transformers, без необходимости устанавливать дополнительные пакеты Python.

from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct")
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct", device_map="auto")

inputs = tokenizer(["The quick brown"], return_tensors="pt").to(model.device)
# `custom_generate` replaces the original `generate` by the custom generation method defined in
# `transformers-community/custom_generate_example`
gen_out = model.generate(**inputs, custom_generate="transformers-community/custom_generate_example", trust_remote_code=True)
print(tokenizer.batch_decode(gen_out, skip_special_tokens=True)[0])
'The quick brown fox jumps over a lazy dog, and the dog is a type of animal. Is'

Вам следует прочитать файл README.md репозитория, содержащего пользовательскую стратегию генерации, чтобы узнать, есть ли новые аргументы и отличия в типе возвращаемого значения. В противном случае можно считать, что она работает как базовый метод generate().

Все пользовательские методы генерации можно найти, выполнив поиск по их пользовательскому тегу, custom_generate.

В качестве примера рассмотрим репозиторий на Hub transformers-community/custom_generate_example. В файле README.md указано, что он принимает дополнительный аргумент left_padding, который добавляет некоторое количество токенов отсечения перед промптом.

gen_out = model.generate(
    **inputs, custom_generate="transformers-community/custom_generate_example", trust_remote_code=True, left_padding=5
)
print(tokenizer.batch_decode(gen_out)[0])
'<|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|>The quick brown fox jumps over the lazy dog.\n\nThe sentence "The quick'

Если у пользовательского метода зафиксированы требования к Python, которые не выполняются в вашей среде, вы получите исключение о недостающих зависимостях. Например, в transformers-community/custom_generate_bad_requirements в файле custom_generate/requirements.txt задан невыполнимый набор требований, и при попытке его запуска вы увидите приведённое ниже сообщение об ошибке.

ImportError: Missing requirements in your local environment for `transformers-community/custom_generate_bad_requirements`:
foo (installed: None)
bar==0.0.0 (installed: None)
torch>=99.0 (installed: 2.6.0)

Обновление требований к Python в соответствии с этим устранит данное сообщение об ошибке.

Создание собственного метода генерации

Чтобы создать новый метод генерации, вам нужно создать новый репозиторий Model и загрузить в него несколько файлов.

  1. Модель, с которой вы разработали метод генерации.
  2. custom_generate/generate.py, в котором содержится вся логика вашего метода кастомной генерации.
  3. custom_generate/requirements.txt, используется для необязательного добавления новых требований Python и/или фиксации конкретных версий для корректного использования вашего метода.
  4. README.md, где следует добавить тег custom_generate и описать здесь любые новые аргументы или отличия в типе вывода вашего пользовательского метода.

После того как вы добавите все необходимые файлы, ваш репозиторий должен выглядеть так

your_repo/
├── README.md          # include the 'custom_generate' tag
├── config.json
├── ...
└── custom_generate/
    ├── generate.py
    └── requirements.txt

Добавление базовой модели

Точкой отсчёта для вашего собственного метода генерации служит репозиторий модели, как и в любом другом случае. Модель, которую вы добавляете в этот репозиторий, должна быть той, с которой вы разрабатывали свой метод, и она предназначена для использования в составе рабочей самодостаточной пары «модель — генерация». При загрузке модели из этого репозитория ваш собственный метод генерации переопределяет generate. Не переживайте — ваш метод генерации по-прежнему можно загрузить с любой другой моделью Transformers, как описано в разделе выше.

Если вы просто хотите скопировать существующую модель, вы можете

from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("source/model_repo")
model = AutoModelForCausalLM.from_pretrained("source/model_repo")
tokenizer.save_pretrained("your/generation_method", push_to_hub=True)
model.save_pretrained("your/generation_method", push_to_hub=True)

generate.py

Это ядро вашего метода генерации. Он должен содержать метод с именем generate, и этот метод должен принимать model в качестве первого аргумента. model — это экземпляр модели, что означает, что у вас есть доступ ко всем атрибутам и методам модели, включая те, что определены в GenerationMixin (например, базовый метод generate).

generate.py должен находиться в папке с именем custom_generate, а не в корне репозитория. Пути к файлам для этой функции заданы жёстко.

Под капотом, когда базовый метод generate() вызывается с аргументом custom_generate, он сначала проверяет свои требования к Python (если они есть), затем находит пользовательский метод generate в файле generate.py и, наконец, вызывает пользовательский generate. Все полученные аргументы и model передаются в ваш пользовательский метод generate, за исключением аргументов, используемых для запуска пользовательской генерации (trust_remote_code и custom_generate).

Это означает, что ваш generate может принимать как исходные, так и пользовательские аргументы (а также возвращать другой тип данных), как показано ниже.

import torch

def generate(model, input_ids, generation_config=None, left_padding=None, **kwargs):
    generation_config = generation_config or model.generation_config  # default to the model generation config
    cur_length = input_ids.shape[1]
    max_length = generation_config.max_length or cur_length + generation_config.max_new_tokens

    # Example of custom argument: add `left_padding` (integer) pad tokens before the prompt
    if left_padding is not None:
        if not isinstance(left_padding, int) or left_padding < 0:
            raise ValueError(f"left_padding must be an integer larger than 0, but is {left_padding}")

        pad_token = kwargs.pop("pad_token", None) or generation_config.pad_token_id or model.config.pad_token_id
        if pad_token is None:
            raise ValueError("pad_token is not defined")
        batch_size = input_ids.shape[0]
        pad_tensor = torch.full(size=(batch_size, left_padding), fill_value=pad_token).to(input_ids.device)
        input_ids = torch.cat((pad_tensor, input_ids), dim=1)
        cur_length = input_ids.shape[1]

    # Simple greedy decoding loop
    while cur_length < max_length:
        logits = model(input_ids).logits
        next_token_logits = logits[:, -1, :]
        next_tokens = torch.argmax(next_token_logits, dim=-1)
        input_ids = torch.cat((input_ids, next_tokens[:, None]), dim=-1)
        cur_length += 1

    return input_ids

Следуйте приведённым ниже рекомендациям, чтобы убедиться, что ваш собственный метод генерации работает как ожидается.

  • Вы можете свободно переиспользовать логику валидации и подготовки входных данных из исходного generate().
  • Закрепите версию transformers в requirements, если вы используете какой-либо приватный метод/атрибут в model.
  • Подумайте о добавлении проверки модели, проверки входных данных или даже отдельного файла тестов, чтобы помочь пользователям проверить работоспособность вашего кода в их окружении.

Ваш собственный метод generate может использовать относительный импорт кода из папки custom_generate. Например, если у вас есть файл utils.py, вы можете импортировать его следующим образом:

from .utils import some_function

Поддерживаются только относительные импорты из папки custom_generate того же уровня. Импорты из родительских или соседних папок недопустимы. Аргумент custom_generate также работает локально с любым каталогом, содержащим структуру custom_generate, что является рекомендуемым рабочим процессом для разработки вашего собственного метода генерации.

gen_out = model.generate(**inputs, custom_generate="path/to/local/dir", trust_remote_code=True)

Загрузка локального каталога по-прежнему выполняет его custom_generate/generate.py, поэтому, как и для репозитория на Hub, требуется trust_remote_code=True. Передавайте trust_remote_code=True только для кода, который вы написали или проверили.

requirements.txt

При необходимости вы можете указать дополнительные требования к Python в файле requirements.txt внутри папки custom_generate. Они проверяются во время выполнения, и если каких-то пакетов не хватает, будет выброшено исключение, чтобы пользователь мог обновить окружение.

README.md

Файл README.md в корне репозитория модели обычно описывает саму модель. Однако, поскольку основной акцент в репозитории сделан на кастомном методе генерации, мы настоятельно рекомендуем сместить фокус описания на этот метод. Помимо описания метода, мы рекомендуем задокументировать любые отличия во входных и/или выходных данных по сравнению с оригинальным generate(). Так пользователи смогут сосредоточиться на новом функционале, а для общих деталей реализации обращаться к документации Transformers.

Для лучшей видимости настоятельно рекомендуем добавить тег custom_generate в ваш репозиторий. Для этого начало вашего файла README.md должно выглядеть как на примере ниже. После того как вы отправите файл, тег должен появиться в вашем репозитории!

---
library_name: transformers
tags:
  - custom_generate
---

(your markdown content here)

Рекомендуемые практики:

  • Опишите различия между входом и выходом в generate().
  • Добавьте самодостаточные примеры для быстрого экспериментирования.
  • Опишите мягкие требования, например, если метод хорошо работает только с определённым семейством моделей.

Повторное использование подготовки входных данных для generate

Если вы добавляете новый цикл декодирования, возможно, вам потребуется сохранить подготовку входных данных, присутствующую в generate (расширение батча, маски внимания, процессоры логитов, критерии остановки и т. д.). Вы также можете передать callable в custom_generate, чтобы переиспользовать полный конвейер подготовки из generate(), переопределяя только цикл декодирования.

def custom_loop(model, input_ids, attention_mask, logits_processor, stopping_criteria, generation_config, **model_kwargs):
    next_tokens = input_ids
    while input_ids.shape[1] < stopping_criteria[0].max_length:
        logits = model(next_tokens, attention_mask=attention_mask, **model_kwargs).logits
        next_token_logits = logits_processor(input_ids, logits[:, -1, :])
        next_tokens = torch.argmax(next_token_logits, dim=-1)[:, None]
        input_ids = torch.cat((input_ids, next_tokens), dim=-1)
        attention_mask = torch.cat((attention_mask, torch.ones_like(next_tokens)), dim=-1)
    return input_ids

output = model.generate(
    **inputs,
    custom_generate=custom_loop,
    max_new_tokens=10,
)

Если вы публикуете репозиторий custom_generate, ваша реализация generate может сама определить вызываемый объект и передать его в model.generate(). Это позволяет настраивать цикл декодирования, сохраняя при этом преимущества встроенной логики подготовки входных данных в Transformers.

Поиск пользовательских методов генерации

Все пользовательские методы генерации можно найти, выполнив поиск по их пользовательскому тегу, custom_generate. Помимо тега, мы поддерживаем две коллекции методов custom_generate:

Ресурсы

Read the How to generate text: using different decoding methods for language generation with Transformers blog post for an explanation of how common decoding strategies work.

Update on GitHub