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> и делает цикл обучения предсказуемым.