Abliterated-модель — это обычная модель с открытыми весами, из которой поведение отказа удалили хирургически, правкой самих весов, — не дообучением и не хитрым промптом-обёрткой. Название склеено из ablate («удалить») и obliterate («стереть»), и механизм оно описывает точно: вы находите в пространстве активаций то единственное направление, которое несёт смысл «здесь стоит отказать», а затем делаете так, что сеть больше никогда не сможет это направление записать. Сам метод называют abliteration (абляция направления отказа).
Техника выросла из работ по интерпретируемости, показавших, что отказ в chat-tuned трансформерах опосредован в основном одним линейным направлением. Именно этот результат и делает всё предприятие дешёвым. Если бы отказ был размазан по тысячам взаимодействующих признаков, для его удаления понадобился бы градиентный спуск. Поскольку он сконцентрирован, достаточно линейной алгебры.
Отказ живёт в residual stream
Residual stream трансформера — это сквозной вектор, по одному на позицию токена, из которого читает и в который дописывает каждый блок. Attention-головы и MLP прибавляют к нему свои выходы; ничто его не перезаписывает. Благодаря этой аддитивной структуре направление в residual stream означает примерно одно и то же на слое 10 и на слое 40 — ровно то свойство, которое позволяет говорить о «направлении отказа» как об одном объекте, а не о сорока никак не связанных между собой.
Чтобы его найти, нужны два набора промптов: те, на которые модель стабильно отказывает, и те, на которые стабильно отвечает. Прогоните оба, снимите активации residual stream на фиксированном слое и позиции токена (хорошо подходят последние несколько токенов инструкции: к этому моменту модель уже дочитала запрос, но ещё не начала отвечать) и возьмите разность средних.
import torch
def refusal_direction(harmful: torch.Tensor, harmless: torch.Tensor) -> torch.Tensor:
"""Difference-in-means estimate of the refusal direction, unit-normalised.
Both tensors are [n_prompts, d_model] residual-stream activations captured at
the same layer and token position. Difference-in-means beats a trained probe
here: with a few hundred prompts a probe overfits, a mean shift does not.
"""
r = harmful.mean(dim=0) - harmless.mean(dim=0)
return r / r.norm()Так делают на каждом слое-кандидате и каждой позиции, получают десятки кандидатов на направление, а затем выбирают один. Отбор важнее извлечения: правильный критерий — провести абляцию по этому кандидату, а затем измерить и то, насколько упал уровень отказов на отложенных вредоносных промптах, и то, насколько сдвинулось распределение следующего токена на безобидных. Второе число — обычно KL-дивергенция относительно немодифицированной модели — и есть ваш измеритель ущерба. Кандидат, который убивает отказ, но сильно сдвигает распределение на безобидных промптах, хуже того, который убивает чуть меньше отказов ценой малой доли побочных потерь.
Как собирают abliterated-модель
Когда единичный вектор r у вас есть, использовать его можно двумя способами. Первый — хук во время инференса: на каждом слое вычитать из активации компоненту вдоль r, прежде чем передавать её дальше. Это работает, но стоит вам runtime-хука, так что модель перестаёт быть простым чекпоинтом.
Второй — собственно abliteration — зашивает правку в веса. Каждая матрица, которая пишет в residual stream, получает обновление ранга один, убирающее r из её выходного пространства: матрица эмбеддингов, все выходные проекции attention, все down-проекции MLP.
def orthogonalize_(weight: torch.Tensor, r: torch.Tensor) -> None:
"""Project r out of a matrix that writes into the residual stream, in place.
weight is [d_model, d_in]. After W <- (I - r rT) W, the product W @ x has zero
component along r for every possible x, so no amount of prompting can put the
direction back. Rank-one per matrix; no gradients, no optimiser, no dataset of
answers — only the few hundred prompts used to estimate r.
"""
weight -= torch.outer(r, r @ weight)Результат — обычный чекпоинт. Та же архитектура, тот же формат файла, та же стоимость инференса, без изменений загружается в vLLM или llama.cpp. На одном GPU вся процедура занимает минуты, и дорогая её часть — перебор кандидатов, а не сама правка.
Чем это отличается от файнтюнинга
Файнтюнинг ради удаления отказов — SFT или DPO на корпусе послушных ответов — принципиально другая операция. Он сдвигает каждый вес градиентным спуском, требует датасета ответов, а не только промптов, стоит GPU-часов и учит новому поведению вместо того, чтобы удалять существующее. Заодно он тащит за собой стиль, смещение по длине и фактические причуды обучающего корпуса и рискует катастрофическим забыванием не связанных с задачей способностей.
Abliteration трогает одно подпространство ранга один и ничего больше. Эта точность — её главное достоинство и одновременно источник её главного режима отказа: если вдоль r живёт что-то помимо отказа, оно будет уничтожено вместе с ним, и никакая аккуратность при переборе полностью этого не исключает.
Чем это отличается от джейлбрейк-промптов
Джейлбрейк не трогает веса, а вместо этого уводит активации в область, где схема отказа не срабатывает. Это хрупко — и хрупко ровно там, где это важно для автоматизации. Он сжигает токены контекста на каждом вызове. Он недетерминирован: один и тот же промпт может сработать на одной выборке и получить отказ на следующей. Он молча ломается, когда провайдер обновляет модель или закрывает конкретную формулировку. А поскольку механизм отказа никуда не делся, он может проявиться посреди генерации: три абзаца полезного текста, а следом — «впрочем, я не могу это продолжать».
У abliterated-модели проявляться нечему. Поведение стабильно на протяжении всей генерации и при любых формулировках промпта — именно это свойство делает её пригодной для batch-пайплайна.
Честно о компромиссах
Abliterated-модель — не лучшая модель. Это модель, которая перестала говорить «нет», и это утверждение куда уже, чем звучит.
- Дрейф способностей. Удаление направления из каждой пишущей матрицы имеет измеримую цену. Abliterated-чекпоинты обычно хуже следуют инструкциям в граничных случаях, чаще соглашаются с ложными посылками в промпте и показывают небольшие просадки на бенчмарках рассуждений.
- Отказ не идеально одномерен. Одно направление объясняет большую часть поведения, но не всё. Ожидайте, что часть отказов выживет, и ожидайте побочных потерь в том поведении, которое делит с отказом это направление.
- Никаких новых знаний. Abliteration ничего не добавляет. Там, где базовая модель отказала бы из незнания, замаскированного под осторожность, вы теперь получите уверенную выдумку — а для пайплайна оценки это может быть хуже отказа, потому что отказ хотя бы честный сигнал, который можно обнаружить.
- Наборы промптов определяют правку. Узкий набор вредоносных промптов даёт узкое направление. Две abliteration одной и той же базовой модели не взаимозаменяемы.
Многие опубликованные abliterated-модели потом проходят лёгкий восстановительный файнтюнинг, чтобы отыграть просевшие способности, — и это тихо возвращает ту самую цену, которой метод должен был избежать.
Где это полезно
Честный сценарий применения — работа, в которой отказ является ошибкой измерения, а не выигрышем в безопасности: red-teaming-харнессы, базовые уровни частоты отказов, оценка робастности и анализ безопасности, где сама тема задевает защитный фильтр, хотя задача защитная. unbleep отдаёт abliterated-модели через OpenAI-совместимый эндпоинт, так что вы можете направить на них существующий клиент и получить стабильный baseline. Что вы на этом построите — на вашей ответственности; в политике допустимого использования описаны границы, которые мы не переходим.
Получите API-ключ и измерьте разницу сами.