Когда AI начинает заикаться: откуда берутся эти чертовы повторы
Вы только что дообучили Qwen2.5-VL-3B на своих данных. Параметры вроде бы стандартные: r=32, lr=5e-5, batch_size=4. Модель запускаете - и она начинает выдавать что-то вроде: "The cat is walking. The cat is walking. The cat is walking. The cat is..."
Знакомо? Добро пожаловать в клуб.
Петли повторений после LoRA-дообучения - это не баг, а фича. Фича того, что вы неправильно поняли, как работает мультимодальная модель. Особенно когда речь о video reasoning.
Важно: если вы видите повторяющиеся фразы в выводе модели - это уже поздняя стадия. Проблема началась гораздо раньше.
Почему LoRA ломает Video Reasoning
Qwen2.5-VL-3B - не просто языковая модель. Это гибрид, который должен понимать временную последовательность в видео. И вот что происходит при LoRA-дообучении:
- Смещение распределения вероятностей: LoRA-адаптеры изменяют веса так, что модель начинает предпочитать «безопасные» токены
- Потеря временного контекста: Видео - это последовательность кадров. LoRA может нарушить понимание этой последовательности
- Переобучение на коротких последовательностях: Если в датасете много коротких описаний - модель научится генерировать только их
Вот типичная ошибка, которую все делают (и я в том числе):
# Как НЕ надо делать
lora_config = LoraConfig(
r=32,
lora_alpha=64,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM"
)
Проблема в том, что для мультимодальных моделей target_modules должны быть другими. И task_type тоже.
Диагностика: где искать корень проблемы
Прежде чем лечить, нужно понять, что болит. Запустите этот код для диагностики:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
# Загружаем вашу дообученную модель
model = AutoModelForCausalLM.from_pretrained("./your-lora-model")
tokenizer = AutoTokenizer.from_pretrained("./your-lora-model")
# Тестовый промпт для видео
test_prompt = "Describe the video: A person is opening a door. "
inputs = tokenizer(test_prompt, return_tensors="pt")
# Смотрим распределение вероятностей для первых 10 токенов
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits
# Вычисляем энтропию распределения
probs = torch.softmax(logits[0, -1], dim=-1)
entropy = -torch.sum(probs * torch.log(probs + 1e-10))
print(f"Entropy of next token distribution: {entropy.item()}")
# Если энтропия ниже 2.0 - у вас проблемы
Низкая энтропия = модель уверена в своем выборе. Слишком уверена. Настолько, что будет повторять один и тот же токен вечно.
Пять практических решений (от простого к сложному)
1 Регулировка температуры и repetition_penalty
Самое простое - и самое часто игнорируемое. При инференсе нужно выставить правильные параметры:
# Правильные параметры для video reasoning
generation_config = {
"temperature": 0.7, # Не 0.1, как все ставят!
"repetition_penalty": 1.2, # Наказываем повторы
"top_p": 0.9, # Nucleus sampling
"top_k": 50, # Ограничиваем выбор
"max_new_tokens": 200,
"do_sample": True, # Обязательно True!
"num_beams": 1 # Beam search ломает видео-описания
}
Temperature=0.7 дает достаточно случайности, чтобы избежать петель. Но это костыль, а не решение.
2 Правильная конфигурация LoRA для мультимодальных моделей
Для Qwen2.5-VL нужно адаптировать не только q_proj и v_proj:
# Правильная конфигурация для Qwen2.5-VL
lora_config = LoraConfig(
r=16, # Меньше rank для видео!
lora_alpha=32,
target_modules=[
"q_proj", "v_proj", "k_proj", "o_proj",
"gate_proj", "up_proj", "down_proj" # Для MLP слоев
],
lora_dropout=0.05, # Меньше dropout
bias="lora_only",
task_type="SEQ_2_SEQ_LM", # Не CAUSAL_LM!
modules_to_save=["lm_head", "embed_tokens"] # Важно!
)
r=16 вместо 32 - это ключевое изменение. Видео-модели более чувствительны к изменениям весов.
3 Data augmentation для временных последовательностей
Если ваш датасет состоит из коротких описаний - модель научится только им. Нужно искусственно создавать длинные последовательности:
# Пример аугментации для видео-датасета
def augment_video_description(description, min_length=50):
"""Добавляем временные маркеры в описание"""
sentences = description.split(". ")
if len(sentences) < 3:
# Добавляем временные отношения
augmented = []
for i, sent in enumerate(sentences):
if i == 0:
augmented.append(f"First, {sent}")
elif i == 1:
augmented.append(f"Then, {sent}")
else:
augmented.append(f"After that, {sent}")
return ". ".join(augmented)
return description
Это учит модель понимать последовательность событий. Без этого она будет описывать каждый кадр отдельно - и зацикливаться.
4 Scheduled learning rate с warmup
lr=5e-5 - это слишком агрессивно для финальных слоев мультимодальной модели. Нужен schedule:
from transformers import get_cosine_schedule_with_warmup
# Конфигурация обучения
training_args = TrainingArguments(
learning_rate=3e-5, # Меньше!
warmup_steps=100, # Обязательно warmup
weight_decay=0.01,
num_train_epochs=3,
logging_steps=10,
save_steps=500,
fp16=True,
gradient_accumulation_steps=2,
optim="adamw_8bit"
)
# scheduler
scheduler = get_cosine_schedule_with_warmup(
optimizer,
num_warmup_steps=100,
num_training_steps=len(train_dataloader) * 3
)
Warmup позволяет модели адаптироваться к новым весам постепенно. Без этого первые же шаги градиента ломают временные зависимости.
5 Контрастive learning для избежания коллапса
Самый продвинутый метод - добавить контрастную loss функцию:
class ContrastiveLORATrainer(Trainer):
def compute_loss(self, model, inputs, return_outputs=False):
# Стандартная loss
outputs = model(**inputs)
loss = outputs.loss
# Добавляем контрастную loss
logits = outputs.logits
batch_size = logits.shape[0]
# Вычисляем similarity между последовательными токенами
# Наказываем за слишком высокую similarity
contrastive_loss = self.compute_contrastive_loss(logits)
# Комбинируем
total_loss = loss + 0.1 * contrastive_loss
return (total_loss, outputs) if return_outputs else total_loss
def compute_contrastive_loss(self, logits):
# Упрощенная реализация
# На практике нужно использовать proper contrastive learning
return torch.tensor(0.0) # Заглушка
Это предотвращает коллапс модели в одно состояние - причину петель повторений.
Чего точно не делать
| Ошибка | Почему это плохо | Что делать вместо этого |
|---|---|---|
| Использовать только q_proj, v_proj | Не затрагивает MLP слои, отвечающие за временные зависимости | Добавлять gate_proj, up_proj, down_proj |
| lr=5e-5 без warmup | Резкие изменения весов ломают attention patterns | lr=3e-5 с warmup_steps=100 |
| temperature=0.1 | Детерминированный выбор токенов ведет к петлям | temperature=0.7, do_sample=True |
| Обучать на коротких описаниях | Модель не учится генерировать длинные последовательности | Аугментировать данные с временными маркерами |
Проверка работоспособности
После применения всех исправлений, проверьте модель на тестовом видео. Идеальный результат должен выглядеть так:
Input: Describe the video: A person enters a kitchen, opens refrigerator, takes out milk.
Good output: First, a person walks into the kitchen. Then, they approach the refrigerator and open it. After that, they reach inside and take out a carton of milk.
Bad output: The person opens the refrigerator. The person opens the refrigerator. The person opens the refrigerator.
Разница очевидна. Хороший вывод содержит временные маркеры и разнообразную лексику. Плохой - зациклен.
Совет: если после всех исправлений петли остаются - уменьшите rank LoRA до 8. Иногда меньше параметров = лучше результаты для видео.
FAQ: частые вопросы от коллег
Петли появляются только на длинных последовательностях. Почему?
Потому что модель не обучена поддерживать контекст. В коротких последовательностях она еще может "помнить" начало. В длинных - забывает и начинает повторять последнее состояние. Решение - учить на длинных примерах с самого начала.
Можно ли использовать repetition_penalty больше 1.5?
Можно, но осторожно. Слишком высокий repetition_penalty (больше 1.8) заставляет модель избегать любых повторов - даже логических. В видео-описаниях иногда нужно повторить субъект: "The person... the person...". С penalty=2.0 модель будет искать синонимы там, где они не нужны.
Какой датасет лучше для video reasoning?
Не существует идеального датасета. Но есть правило: минимум 30% примеров должны быть длиннее 50 токенов. И в каждом примере должны быть временные маркеры (first, then, after, meanwhile). Без этого модель не научится reasoning.
Что делать, если ничего не помогает
Бывает. Иногда петли повторений - симптом более глубокой проблемы: модель просто не способна к video reasoning в ее текущем виде. В этом случае:
- Попробуйте другую архитектуру. Qwen2.5-VL-7B может работать лучше
- Используйте двухэтапное обучение: сначала language-only, потом multimodal
- Добавьте reinforcement learning с reward за разнообразие вывода
И помните: если вы столкнулись с этой проблемой - вы не одиноки. Петли повторений после LoRA - это обряд посвящения в мир мультимодальных моделей. Пройдя его, вы поймете их внутреннее устройство лучше, чем 90% «специалистов».
А если хотите глубже разобраться в создании датасетов для LoRA, посмотрите мой гайд Как создать датасет для LoRA, который действительно учит модель. Там те же принципы, но для текстовых моделей.