MoE-модели: зацикливание генерации и оптимизация DPO в FP8 | AiManual
AiManual Logo Ai / Manual.
24 Мар 2026 Гайд

Проблемы разработки MoE-моделей: как GigaChat победил зацикливание и оптимизировал DPO в FP8

Разбор реальных проблем разработки MoE-моделей: зацикливание генераций, баг в SGLang, и решения от команды GigaChat: метрика на BPE-сжатии, переход на нативный

Когда MoE-модели начинают повторяться: проблема зацикливания генераций

Вы запускаете inference на своей красивом MoE-архитектуре, а модель вместо осмысленного ответа выдает одно и то же слово или фразу снова и снова. Знакомо? Команда GigaChat столкнулась с этим в 2025 году при разработке своей последней MoE-модели. Зацикливание - не просто баг, это системная проблема в архитектуре экспертов.

Зацикливание часто возникает из-за того, что определенный эксперт в MoE-архитектуре начинает доминировать и постоянно активироваться, создавая петлю обратной связи в генерации токенов.

В классических dense-моделях такие проблемы решаются через штрафы за повторение, но в MoE все сложнее. Здесь каждый токен может обрабатываться разными экспертами, и если один эксперт "застревает" в определенном паттерне, стандартные методы не работают.

Метрика на BPE-сжатии: как измерить зацикливание

Команда GigaChat разработала новую метрику для обнаружения зацикливания на ранних этапах. Вместо подсчета повторяющихся n-грамм, они используют BPE-сжатие сгенерированного текста.

💡
Идея проста: если текст зациклен, его BPE-представление будет иметь высокую степень сжатия. Нормальный текст сжимается хуже из-за разнообразия токенов.

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

Баг в SGLang: почему ускорение inference иногда ломает генерацию

При оптимизации serving с помощью SGLang, команда обнаружила, что зацикливание усиливается. Оказалось, что в SGLang версии 1.3.2 (актуальной на начало 2025) был баг в кэшировании состояний экспертов для MoE-моделей.

Баг проявлялся только при определенных конфигурациях экспертов и длинных контекстах. SGLang неправильно кэшировал активации экспертов, что приводило к тому, что один эксперт мог быть использован для нескольких токенов, даже если это было неоптимально.

Решение: перейти на исправленную версию SGLang 1.4.0, где этот баг был исправлен. Также они настроили параметры кэширования вручную, чтобы избежать подобных проблем. Если вы используете SGLang для serving MoE-моделей, обязательно обновитесь до последней версии. Более подробно об оптимизации serving massive MoE моделей можно прочитать в нашем benchmark Qwen3.5-397B на 8x H20 с SGLang.

DPO в FP8: когда оптимизация памяти убивает качество

Direct Preference Optimization (DPO) - стандартный метод тонкой настройки LLM по предпочтениям. Но при работе с MoE-моделями, где память критична, переход на FP8 для DPO казался логичным шагом. Однако первоначальная реализация DPO в FP8 приводила к значительной потере качества.

Проблема в том, что градиенты в DPO очень чувствительны к точности вычислений. При использовании стандартного FP8 (через амплификацию типов) накапливались ошибки округления, которые искажали сигнал предпочтений.

Переход на нативный FP8: как сохранить качество

Вместо использования оберток для автоматического преобразования в FP8, команда GigaChat переписала критические части DPO-алгоритма для нативной работы с FP8. Это включало:

  1. Пересчет функций потерь с учетом динамического диапазона FP8
  2. Кастомные оптимизаторы, которые работают с масштабированием градиентов
  3. Использование mixed precision только для определенных операций, а не для всего графа

Результат: потребление памяти уменьшилось на 60%, а качество модели (оцененное по человеческим предпочтениям) упало всего на 0.5% по сравнению с FP16. Для сравнения, стандартный FP8 приводил к падению на 3-5%.

💡
Нативный FP8 требует глубокого понимания алгоритма DPO. Нельзя просто включить amp и надеяться на лучшее. Нужно вручную настроить масштабирование для каждой группы параметров.

Практический план: как внедрить эти решения в ваш пайплайн

1 Добавьте метрику BPE-сжатия в ваши тесты

Реализуйте функцию, которая вычисляет коэффициент сжатия для сгенерированного текста. Пороговое значение 0.85 - хорошая отправная точка, но его нужно подбирать под вашу модель и задачу.

import zipfile
import io

def bpe_compression_ratio(text: str) -> float:
    """Вычисляет коэффициент сжатия текста с помощью BPE-подобного алгоритма."""
    # Упрощенная реализация: используем фактическое сжатие zip
    buffer = io.BytesIO()
    with zipfile.ZipFile(buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
        zip_file.writestr("text.txt", text.encode('utf-8'))
    compressed_size = buffer.tell()
    original_size = len(text.encode('utf-8'))
    return compressed_size / original_size if original_size > 0 else 1.0

# Использование
text = "повторение повторение повторение повторение"
ratio = bpe_compression_ratio(text)
print(f"Коэффициент сжатия: {ratio:.3f}")
if ratio > 0.85:
    print("Вероятно зацикливание!")

2 Обновите SGLang и настройте кэширование

Убедитесь, что используете SGLang версии 1.4.0 или выше. Для MoE-моделей настройте параметры кэширования экспертов:

# Пример конфигурации для SGLang с MoE
config = {
    "cache_config": {
        "expert_cache_size": 512,  # Количество экспертов, которые кэшируются
        "enable_expert_aware_caching": True,  # Критически важно для MoE
        "max_context_length": 8192
    }
}

3 Реализуйте нативный FP8 для DPO

Это сложнее. Вот ключевые изменения в функции потерь DPO:

import torch
import torch.nn.functional as F

def dpo_loss_fp8(policy_chosen_logps, policy_rejected_logps,
                 reference_chosen_logps, reference_rejected_logps,
                 beta=0.1):
    """DPO loss с нативной поддержкой FP8."""
    # Конвертируем логиты в FP8 для вычислений
    # Важно: масштабируем перед softmax
    scale = 128.0  # Масштабный коэффициент для FP8
    
    # Вычисляем разницы в логарифмических вероятностях
    logits = (policy_chosen_logps - policy_rejected_logps) - \
             (reference_chosen_logps - reference_rejected_logps)
    
    # Масштабируем для FP8
    logits_fp8 = (logits * scale).to(torch.float8_e4m3fn)
    logits_scaled = logits_fp8.float() / scale
    
    # Вычисляем потери
    losses = -F.logsigmoid(beta * logits_scaled)
    return losses.mean()

Это упрощенный пример. В реальности нужно также адаптировать оптимизатор и обратное распространение.

Ошибки, которых стоит избегать

  • Не доверяйте автоматическому mixed precision для DPO. Он не учитывает специфику алгоритма и может испортить градиенты.
  • Не используйте старые версии SGLang для MoE. Баг с кэшированием экспертов может привести к необъяснимым зацикливаниям.
  • Не полагайтесь только на метрики perplexity для обнаружения зацикливания. BPE-сжатие более эффективно для этой конкретной проблемы.
  • Не квантуйте все слои модели в FP8. Некоторые эксперты могут требовать более высокой точности. Подробнее о тонкостях квантования MoE-моделей читайте в нашем опыте с Qwen3-Coder-Next.

Что дальше? Будущее MoE-оптимизаций

Опыт GigaChat показывает, что MoE-модели требуют кастомных решений. Стандартные оптимизации часто ломают что-то в архитектуре экспертов. К 2026 году мы видим тенденцию к специализированным фреймворкам для MoE, которые учитывают эти нюансы из коробки.

Совет: если вы разрабатываете MoE-модель, инвестируйте время в создание собственных инструментов мониторинга и оптимизации. Не полагайтесь на общие решения. И следите за развитием таких проектов, как SGLang и новых библиотек для квантования, которые добавляют поддержку MoE.

Для тех, кто хочет глубже погрузиться в тему оптимизации MoE-моделей на потребительском железе, рекомендую наш гайд MoE на RTX 4090: Как выжать максимум из 24 ГБ VRAM.

Подписаться на канал