Attention sinks в трансформерах: что это и как исправить без overhead | AiManual
AiManual Logo Ai / Manual.
14 Янв 2026 Гайд

Артефакты внимания в ViT и LLM: тихий убийца качества, который вы не видите

Проблема attention sinks в ViT и LLM - высокие нормы регистров, которые портят DINOv2 и Qwen3-Next. Практическое руководство по исправлению.

Внимание разбегается по углам: как трансформеры теряют информацию

Откройте визуализацию внимания в любом современном ViT (Vision Transformer) - например, в DINOv2. Смотрите на эти красивые карты, где модель якобы "фокусируется" на объектах? Забудьте. Половина этого внимания - артефакт архитектуры, а не реальное понимание.

Когда вы видите, что LLM генерирует наукообразный бред или ViT странно сегментирует изображение - это может быть не недостаток данных, а фундаментальный изъян в механизме внимания. Вайб-физика иногда рождается именно здесь.

Что такое attention sinks на самом деле?

Представьте трансформерный слой. У вас есть запросы (Q), ключи (K) и значения (V). Softmax распределяет внимание. В теории - всё идеально. На практике - появляются "регистры-губки", которые поглощают внимание просто потому, что имеют аномально высокую норму.

Эти регистры становятся attention sinks - точками, куда стекается внимание независимо от семантической релевантности. Они работают как гравитационные колодцы в пространстве внимания: всё падает туда, даже если должно лететь мимо.

Тип артефакта Где встречается Симптомы
High-norm spikes DINOv2, ViT-Large Внимание к пустым патчам
Позиционные sinks Qwen3-Next, Llama 3 Предпочтение первых/последних токенов
Семантические аттракторы Все LLM с длинным контекстом Interpretation drift и нестабильность

Почему это происходит? Механика катастрофы

Всё начинается с инициализации. Некоторые регистры получают случайно высокие нормы. Softmax их замечает - ведь он экспоненциально усиливает различия. Дальше - петля положительной обратной связи:

  1. Регистр с высокой нормой притягивает больше внимания
  2. Градиенты обновляют его, делая норму ещё выше
  3. На следующей эпохе он доминирует ещё сильнее
  4. Остальные регистры фактически отключаются

Это похоже на проблему, описанную в эффекте Манделы в ИИ, но на уровне архитектуры. Модель не "галлюцинирует" - она просто следует математическому императиву, заложенному в softmax.

Как обнаружить sinks в своей модели?

Для ViT (DINOv2 и подобные):

  • Визуализируйте нормы регистров по слоям - ищите выбросы
  • Проверьте attention maps для пустых патчей (например, чёрный квадрат)
  • Если внимание распределено не по объектам, а по каким-то фиксированным позициям - это sinks

Для LLM (Qwen3-Next и другие):

  • Анализируйте attention weights для разных позиций в контексте
  • Первые 10 токенов получают непропорционально много внимания? Это sink
  • Сравните качество генерации при разной длине контекста - если падает, виноваты sinks
💡
Простой тест: загрузите в LLM длинный текст, затем спросите о деталях из середины. Если модель "забывает" середину, но помнит начало и конец - у вас классические позиционные sinks. Это связано с тем, что LLM понимают цель, но игнорируют её из-за архитектурных ограничений.

Исправляем без переобучения: 3 практических метода

Исследование на NeurIPS 2025 показало - можно исправить sinks без дополнительного обучения. Zero overhead. Вот как.

1 Нормализация регистров онлайн

Вместо того чтобы бороться с последствиями, атакуйте причину - высокие нормы. Добавьте слой нормализации прямо перед softmax:

# Вместо стандартного внимания:
# attention = softmax(Q @ K.T / sqrt(d_k))

# Делаем так:
def safe_attention(Q, K, V):
    # Нормализуем ключи по последнему измерению
    K_norm = K / (torch.norm(K, dim=-1, keepdim=True) + 1e-8)
    
    # Масштабируем запросы, но сохраняем их оригинальные нормы
    scale = torch.norm(Q, dim=-1, keepdim=True)
    Q_scaled = Q / (scale + 1e-8)
    
    # Внимание с нормализованными ключами
    scores = Q_scaled @ K_norm.T / math.sqrt(Q.size(-1))
    attention = F.softmax(scores, dim=-1)
    
    # Восстанавливаем масштаб для output
    return attention @ V * scale

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

2 Temperature annealing для softmax

Проблема в экспоненте softmax. Он превращает небольшие различия в огромные. Решение - динамическая температура:

def adaptive_softmax(scores, current_layer, total_layers):
    """
    Температура зависит от глубины слоя.
    В ранних слоях - более высокая температура (менее "пиковое" распределение)
    В поздних слоях - более низкая (более уверенные предсказания)
    """
    # Базовый коэффициент
    base_temp = 1.0
    
    # Ранние слои получают bonus к температуре
    if current_layer < total_layers // 3:
        temperature = base_temp * 2.0  # Более равномерное внимание
    elif current_layer < 2 * total_layers // 3:
        temperature = base_temp * 1.5
    else:
        temperature = base_temp  # Оригинальная температура
    
    return F.softmax(scores / temperature, dim=-1)

Почему это работает? Ранние слои формируют представления. Если там появится sink - он испортит всё дальнейшее обучение. Высокая температура в начале предотвращает это.

Этот подход особенно эффективен для моделей с tuneable attention, где механизм внимания уже подвергается модификациям. Можно комбинировать методы для синергетического эффекта.

3 Патч для существующих моделей (DINOv2, Qwen3-Next)

Если у вас уже есть предобученная модель с sinks, не нужно её переучивать. Добавьте post-processing слой:

class SinkCorrector(nn.Module):
    def __init__(self, hidden_dim, num_heads):
        super().__init__()
        self.hidden_dim = hidden_dim
        self.num_heads = num_heads
        
        # Детектор sinks - учится находить аномальные паттерны
        self.sink_detector = nn.Sequential(
            nn.Linear(hidden_dim, hidden_dim // 4),
            nn.ReLU(),
            nn.Linear(hidden_dim // 4, num_heads)
        )
        
    def forward(self, attention_weights, layer_output):
        """
        attention_weights: [batch, heads, seq_len, seq_len]
        layer_output: [batch, seq_len, hidden_dim]
        """
        batch_size, seq_len, _ = layer_output.shape
        
        # Детектируем sinks по статистике внимания
        attention_entropy = -torch.sum(
            attention_weights * torch.log(attention_weights + 1e-8),
            dim=-1
        )  # [batch, heads, seq_len]
        
        # Низкая энтропия = пиковое распределение = возможный sink
        sink_mask = (attention_entropy < 0.5).float()
        
        # Пересчитываем attention без sinks
        corrected_attention = attention_weights * (1 - sink_mask.unsqueeze(-1))
        
        # Перенормируем
        corrected_attention = corrected_attention / (
            corrected_attention.sum(dim=-1, keepdim=True) + 1e-8
        )
        
        return corrected_attention

Этот корректор можно вставить после каждого трансформерного слоя. Он работает онлайн, почти не добавляя вычислительной сложности.

Что будет, если проигнорировать проблему?

Краткий список катастроф, которые уже происходят в продакшене:

  • DINOv2 для сегментации: Модель "видит" границы там, где их нет. Потому что attention застрял в каком-то sink-регистре, который активируется на текстурах, похожих на границы.
  • Qwen3-Next для длинных документов: Качество анализа падает после 4К токенов. Не из-за ограничений контекста, а из-за того, что sinks поглощают внимание, нужное для середины документа.
  • Нестабильность fine-tuning: Вы делаете LoRA-адаптацию, а модель начинает вести себя непредсказуемо. Потому что sinks взаимодействуют с новыми весами непредвиденным образом. Датасет для LoRA может быть идеальным, но sinks всё испортят.

Самое опасное - sinks создают иллюзию работы. Модель проходит метрики на стандартных датасетах, но в реальных задачах даёт сбой. Это тот случай, когда нейросети понимают вашу боль, но всё равно дают опасный совет - потому что механизм принятия решений сломан на фундаментальном уровне.

FAQ: Ответы на неочевидные вопросы

Вопрос: Attention sinks - это баг или фича?

И то, и другое. В некоторых задачах sinks могут работать как "память" или "буфер". Но в 95% случаев это баг, который нужно фиксить. Разработчики часто не знают о их существовании, поэтому не документируют.

Вопрос: Почему sinks не обнаруживают при тестировании?

Потому что стандартные бенчмарки используют короткие последовательности. Sinks проявляются на длинных контекстах или специфических паттернах. Нужно специальное тестирование - например, тест (c/t)^n для семантического заземления.

Вопрос: Можно ли использовать sinks для ускорения?

Теоретически - да. Если знать, какие регистры становятся sinks, можно предвычислять их вклад. Но на практике это слишком нестабильно. Лучше исправить sinks, а для ускорения использовать другие методы.

Вопрос: Как sinks связаны с эпистемической асимметрией?

Прямо. Sinks создают "предвзятость внимания" - модель обращает внимание не на то, что важно, а на то, что математически удобно. Это форма внутренней асимметрии: у модели есть доступ ко всей информации, но механизм выбора сломан.

Что делать прямо сейчас?

  1. Проверьте свои ViT/LLM модели на наличие sinks (код выше)
  2. Если используете DINOv2 или Qwen3-Next - обязательно добавьте нормализацию регистров
  3. Для новых проектов закладывайте защиту от sinks в архитектуру с самого начала
  4. Делитесь находками с комьюнити - это системная проблема, а не особенность конкретной модели

И последнее: не верьте красивым визуализациям внимания. Они часто показывают артефакты, а не понимание. Настоящее внимание скрыто в динамике градиентов, в нормах регистров, в статистике, которую обычно не показывают в статьях.

Проверьте свою модель сегодня. Скорее всего, вы найдёте sinks. И когда исправите их - качество вырастет на те самые проценты, которых вам не хватало для production.