Когда модель сходит с ума: проблема, которую все игнорируют
Вы запускаете тот же промпт. На той же модели. С теми же параметрами. А ответ другой. Иногда чуть-чуть другой - синоним поменялся. Иногда кардинально - вместо "да" получаете "нет". Иногда модель просто начинает генерировать бред, которого раньше не было.
Это не баг. Это дрейф. И он убивает воспроизводимость экспериментов, ломает продакшен-системы и заставляет исследователей тратить недели на поиск несуществующих ошибок в коде.
Самый опасный дрейф - тот, который вы не замечаете. Модель медленно деградирует, качество падает на 2-3% в месяц, а вы списываете это на "статистическую погрешность" или "особенности датасета". Через полгода система работает вполсилы, но никто не понимает почему.
Почему существующие метрики не работают
BLEU, ROUGE, точность, F1-мера - все эти метрики измеряют что угодно, только не дрейф. Они сравнивают текст с эталоном, но не отвечают на главный вопрос: почему ответ изменился?
Может, это случайная вариация из-за температуры? Может, контекст переполнился и модель "забыла" инструкцию? Может, в новой версии модели действительно что-то сломали?
Без ответа на этот "почему" вы просто фиксируете факт изменения. Как врач, который говорит "у вас температура", но не проверяет, грипп это или солнечный удар.
Таксономия дрейфа: четыре оси изменений
Новая таксономия (опубликована на Zenodo) предлагает смотреть на дрейф по четырем независимым осям. Не "есть дрейф/нет дрейфа", а "какой именно дрейф и насколько он опасен".
| Тип дрейфа | Что меняется | Как обнаружить | Опасность |
|---|---|---|---|
| Контекстная вариация | Формулировка, стиль, синонимы | Семантическое сходство > 0.9 | Низкая |
| Поведенческий сдвиг | Логика, решение, вывод | Семантическое сходство < 0.7 | Высокая |
| Систематический дрейф | Качество по всем промптам | Статистика по бенчмарку | Критическая |
| Стохастическая нестабильность | Непредсказуемые изменения | Высокий std между запусками | Средняя |
1 Диагностика: что у вас на самом деле
Прежде чем паниковать, нужно понять тип дрейфа. Возьмите 50-100 промптов из вашего рабочего набора. Запустите их на "старой" версии системы (или на контрольной модели). Запустите на "новой". Не сравнивайте тексты - сравнивайте семантические ядра.
# Минимальный пример диагностики
from sentence_transformers import SentenceTransformer
import numpy as np
model = SentenceTransformer('all-MiniLM-L6-v2')
# Ответы от двух версий модели
answers_v1 = ["Да, это правильное решение.", "Нет, это ошибка.", ...]
answers_v2 = ["Да, это верное решение.", "Нет, это неверно.", ...]
# Эмбеддинги
embeddings_v1 = model.encode(answers_v1)
embeddings_v2 = model.encode(answers_v2)
# Косинусное сходство для каждой пары
similarities = []
for emb1, emb2 in zip(embeddings_v1, embeddings_v2):
sim = np.dot(emb1, emb2) / (np.linalg.norm(emb1) * np.linalg.norm(emb2))
similarities.append(sim)
# Анализ распределения
mean_sim = np.mean(similarities)
std_sim = np.std(similarities)
print(f"Среднее сходство: {mean_sim:.3f}")
print(f"Стандартное отклонение: {std_sim:.3f}")
if mean_sim > 0.9 and std_sim < 0.1:
print("Вероятно, контекстная вариация")
elif mean_sim < 0.7:
print("Вероятно, поведенческий сдвиг")
elif std_sim > 0.2:
print("Вероятно, стохастическая нестабильность")
Это базовая диагностика. Она не идеальна, но отделяет реальные проблемы от статистического шума.
Не используйте BLEU/ROUGE! Они ломаются на синонимах и перефразированиях. Модель может дать идеально правильный ответ другими словами, а метрика покажет "низкое качество".
2 Глубокая проверка: ищем системные изменения
Если диагностика показала поведенческий сдвиг или системный дрейф - нужно копать глубже. Потому что разница может быть в:
- Контекстном окне - модель "теряет" информацию в середине длинного контекста (помните Lost in the Middle?)
- Температуре и seed - банальная случайность, которую забыли зафиксировать
- Форматировании промпта - лишний пробел, другой шаблон
- Реальном изменении весов - кто-то обновил модель и сломал логику
Создайте контрольные группы промптов:
- Короткие фактологические вопросы (проверка базовых знаний)
- Логические задачи с однозначным ответом
- Творческие задания с субъективной оценкой
- Промпты с длинным контекстом (10к+ токенов)
Запустите каждую группу отдельно. Если дрейф только в длинных контекстах - проблема в attention механизме. Если во всех группах - что-то сломано фундаментально.
Особый случай: дрейф в RAG-системах
В RAG дрейф может быть в трех местах одновременно: в модели, в эмбеддерах, в поиске. И самое мерзкое - они маскируют друг друга.
Модель начинает генерировать чуть более развернутые ответы → эмбеддер ищет другие фрагменты → качество падает на 15% → вы думаете, что сломался векторный поиск → начинаете его оптимизировать → на самом деле проблема была в температуре генерации.
Протокол проверки для RAG:
# Изолируем компоненты RAG
def test_retriever_only(query, documents):
"""Тестируем только поиск, без генерации"""
# Фиксируем эмбеддинги запроса и документов
# Сравниваем топ-N результатов между версиями
return retrieval_similarity
def test_generator_only(context, query):
"""Тестируем только генерацию, с фиксированным контекстом"""
# Даем модели одинаковый контекст
# Сравниваем ответы
return generation_similarity
def test_full_rag(query, documents):
"""Тестируем полный цикл"""
# Полный RAG pipeline
return answer_similarity
# Если падает только full_rag - проблема во взаимодействии
# Если падает generator_only - дрейф в LLM
# Если падает retriever_only - дрейф в эмбеддерах
Практические ловушки: где ошибаются даже опытные
Я видел команды, которые месяц искали "дрейф модели", а проблема была в кэше GPU. Или в том, что кто-то поменял версию CUDA. Или в том, что модель начали запускать на другом типе GPU (A100 vs H100 - разная числовая точность).
Чеклист перед тем, как диагностировать дрейф LLM:
- Точная версия модели (хеш весов, не просто название)
- Точная версия фреймворка (transformers, vLLM, etc)
- Настройки квантования (если есть)
- Тип GPU и драйверы
- Параметры генерации (temperature, top_p, seed)
- Шаблон чата (точно такой же формат)
- Размер контекста и способ его заполнения
Пропустите один пункт - и будете искать несуществующий дрейф.
Самый частый источник "фантомного дрейфа" - разный seed. В продакшене seed часто не фиксируют (для разнообразия ответов). При тестировании используют seed=42. Модель с temperature=0.7 и разным seed дает разные ответы. Это не дрейф, это стохастичность.
Когда дрейф - это фича, а не баг
Иногда изменение поведения - это хорошо. Модель научилась лучше понимать контекст. Или стала менее токсичной. Или начала давать более развернутые объяснения.
Как отличить улучшение от деградации? Нужны человеческие оценщики и предметные метрики.
Пример: в финансовой аналитике (как у квантов) модель может начать добавлять disclaimer о рисках. По семантическому сходству ответы будут отличаться сильно. Но по качеству - улучшаться.
Решение: трехступенчатая оценка:
- Автоматическая проверка (семантическое сходство, точность)
- Предметная проверка (задача-специфичные метрики)
- Человеческая оценка (качество, полезность, безопасность)
Только если все три уровня покажут негативные изменения - это реальный дрейф, который нужно исправлять.
Инструменты и автоматизация
Вручную проверять дрейф на сотнях промптов - ад. Нужна автоматизация. Но готовых инструментов почти нет.
Базовый пайплайн мониторинга:
# Псевдокод системы мониторинга дрейфа
class DriftMonitor:
def __init__(self, reference_model, test_model, prompts):
self.reference = reference_model
self.test = test_model
self.prompts = prompts
self.baseline = self._create_baseline()
def _create_baseline(self):
"""Запускаем все промпты на референсной модели"""
baseline_results = {}
for prompt in self.prompts:
answer = self.reference.generate(prompt)
embedding = self._get_embedding(answer)
baseline_results[prompt] = {
'answer': answer,
'embedding': embedding
}
return baseline_results
def check_drift(self, threshold=0.8):
"""Проверяем дрейф относительно baseline"""
drift_report = {
'contextual_variation': [],
'behavioral_shift': [],
'systematic_drift': False
}
similarities = []
for prompt in self.prompts:
test_answer = self.test.generate(prompt)
test_embedding = self._get_embedding(test_answer)
baseline_embedding = self.baseline[prompt]['embedding']
similarity = cosine_similarity(test_embedding, baseline_embedding)
similarities.append(similarity)
if similarity < threshold:
# Анализируем тип расхождения
if self._is_behavioral_shift(prompt, test_answer):
drift_report['behavioral_shift'].append(prompt)
else:
drift_report['contextual_variation'].append(prompt)
# Проверяем системный дрейф
avg_similarity = np.mean(similarities)
if avg_similarity < 0.7:
drift_report['systematic_drift'] = True
return drift_report
Такую систему нужно запускать регулярно: при каждом обновлении модели, изменении инфраструктуры, раз в неделю для мониторинга.
Что делать, если дрейф найден
Алгоритм действий:
- Не паниковать. 80% "дрейфов" оказываются контекстными вариациями или проблемами инфраструктуры.
- Изолировать проблему. Модель? Инфраструктура? Данные? Параметры?
- Воспроизвести на минимальном примере. Убрать все сложности, оставить один промпт.
- Сравнить с контрольной версией. Если есть старая версия модели/кода.
- Зафиксировать все параметры. Сделать снимок системы, который можно воспроизвести.
- Эскалировать правильно. Не "модель сломалась", а "на промптах типа X наблюдается поведенческий сдвиг с similarity 0.65, пример прилагаю".
Если дрейф системный и подтвержденный - нужно откатывать модель или исправлять промпты. Иногда помогает RLM-подход - заставить модель самой управлять контекстом. Иногда - переобучение на проблемных примерах.
Прогноз: дрейф станет главной проблемой 2025
Сейчас все увлечены размером контекста, мультимодальностью, скоростью inference. Через год осознают: самая дорогая проблема - нестабильность.
Представьте: вы построили сложную агентскую систему на 10 моделях. Каждая обновляется раз в месяц. Каждая дрейфует по-своему. Система становится непредсказуемой. Отлаживать невозможно - ошибки плавающие.
Решение будет в:
- Стандартизированных бенчмарках дрейфа (как GLUE для качества)
- Версионировании не только моделей, но и их поведения
- Автоматических систем мониторинга в реальном времени
- Детерминированных режимах даже ценой креативности
Пока этого нет - используйте таксономию из этой статьи. Хотя бы понимайте, с каким типом дрейфа вы столкнулись. Это уже на 50% решает проблему.
И да, сохраняйте seed. Всегда.