2025, Dec 07 03:02

Как починить eos_token в SFTTrainer (trl) с Qwen2: импортируйте unsloth раньше

Сбой «token not found» в SFTTrainer (trl) с Qwen2: eos_token <|im_end|> подменяется на <EOS_TOKEN> из‑за unsloth. Исправление — импортировать unsloth раньше trl.

При переносе пайплайна дообучения на более свежий стек trl может неожиданно всплыть сбой, связанный с обработкой EOS. После обновления trl и перехода с TrainingArguments на SFTConfig внутри SFTTrainer в конфигурации для Qwen2.5 заданный eos_token может тихо замениться на <EOS_TOKEN>, и тренер прерывается с ошибкой «токен не найден».

Постановка проблемы

Процесс дообучения опирается на SFTTrainer, Qwen2TokenizerFast, переданный через processing_class, и явный eos_token, совпадающий с конфигурацией токенизатора. Инициализация выглядит примерно так:

fine_tuner = SFTTrainer(
    model=base_model,
    processing_class=tok,
    train_dataset=train_split,
    eval_dataset=dev_split,
    data_collator=DataCollatorForSeq2Seq(tokenizer=tok),
    callbacks=[log_cb],
    args=SFTConfig(
        per_device_train_batch_size=batch_per_device,
        gradient_accumulation_steps=grad_accum_steps,
        warmup_steps=num_warmup,
        num_train_epochs=epochs_cap,
        max_steps=steps_cap,
        max_seq_length=seq_block,
        learning_rate=lr_value,
        fp16=not is_bfloat16_supported(),
        bf16=is_bfloat16_supported(),
        logging_steps=log_every,
        optim="paged_adamw_8bit",
        weight_decay=0.01,
        lr_scheduler_type="linear",
        seed=seed_value,
        eval_strategy="epoch" if dev_split is not None else "no",
        save_strategy="no",
        output_dir="models",
        save_steps=50,
        report_to="none",
        packing=False,
        dataset_text_field="text",
        eos_token="<|im_end|>",
    ),
)

Даже при явной установке eos_token в <|im_end|>, согласованной с Qwen2TokenizerFast, создание SFTTrainer может завершиться неудачей, потому что строка, попадающая на проверку, уже не та, которую вы передали.

Что именно ломается

Во время инициализации SFTTrainer берёт eos_token из args и проверяет его наличие в словаре токенизатора через processing_class.convert_tokens_to_ids. Если поиск возвращает None, тренер выбрасывает исключение. Неожиданность в том, что до проверки eos_token может дойти как <EOS_TOKEN> вместо <|im_end|>, из‑за чего поиск проваливается и инициализация останавливается с ошибкой формата «token not found».

Триггер этого поведения связан с unsloth. Пакет меняет что‑то «под капотом», и порядок импорта библиотек влияет на значение, которое SFTTrainer видит во время настройки.

Как исправить

Решение — импортировать unsloth раньше trl. В таком порядке тренер получает нужный eos_token и проходит проверку без сбоев.

from unsloth import FastLanguageModel
from trl import SFTTrainer, SFTConfig

fine_tuner = SFTTrainer(
    model=base_model,
    processing_class=tok,
    train_dataset=train_split,
    eval_dataset=dev_split,
    data_collator=DataCollatorForSeq2Seq(tokenizer=tok),
    callbacks=[log_cb],
    args=SFTConfig(
        per_device_train_batch_size=batch_per_device,
        gradient_accumulation_steps=grad_accum_steps,
        warmup_steps=num_warmup,
        num_train_epochs=epochs_cap,
        max_steps=steps_cap,
        max_seq_length=seq_block,
        learning_rate=lr_value,
        fp16=not is_bfloat16_supported(),
        bf16=is_bfloat16_supported(),
        logging_steps=log_every,
        optim="paged_adamw_8bit",
        weight_decay=0.01,
        lr_scheduler_type="linear",
        seed=seed_value,
        eval_strategy="epoch" if dev_split is not None else "no",
        save_strategy="no",
        output_dir="models",
        save_steps=50,
        report_to="none",
        packing=False,
        dataset_text_field="text",
        eos_token="<|im_end|>",
    ),
)

В рабочей конфигурации, на которую здесь ссылаются: trl==0.18.2 и unsloth==2025.6.2.

Почему это важно

В конвейерах дообучения малейшие изменения специальных токенов сказываются на колляции данных, маскировании функции потерь и критериях остановки. Если обработка EOS внезапно меняется, вы заметите не только сбои инициализации — повышается риск непоследовательного обучения. Осознание того, что порядок импорта влияет на интеграцию фреймворков, помогает удерживать такие настройки, как eos_token, в точном соответствии с заданными.

Итог

Если SFTTrainer сообщает, что ваш eos_token отсутствует в словаре, хотя вы уверены, что он есть, проверьте порядок импорта. Импортируйте unsloth перед trl, держите eos_token согласованным с конфигурацией Qwen2TokenizerFast и фиксируйте версии пакетов, с которыми всё работает. Эта небольшая деталь предотвращает тихую подмену <|im_end|> на <EOS_TOKEN> и делает цикл обучения предсказуемым.