LLM не думают на вашем языке. Они думают на языке чисел.
Но что, если мы можем перехватить этот внутренний диалог? Activation steering — это не просто модный термин, а реальный способ заглянуть в "голову" большой языковой модели и подтолкнуть её в нужную сторону. Я провел серию экспериментов с open-source моделями размером 7B (LLaMA-3.2-7B, Qwen2.5-7B) и готов рассказать, как искать те самые векторы, которые кодируют абстрактные концепты — от честности до креативности. Без магии, с кодом и парой граблей.
Проблема: мы видим только вход и выход. А внутри — dense layer и attention
Стандартный пайплайн: токенизация → forward pass → все. Мы не знаем, почему модель вдруг решила, что слово "справедливость" должно идти после "равенство". Даже механистическая интерпретируемость (отличная статья по теме) часто ограничивается анализом отдельных нейронов. Activation steering — это прослушка на уровне всего hidden state. Берем слой, вытаскиваем из него вектор для конкретного концепта и потом добавляем его на forward pass — и модель начинает вести себя иначе.
Но есть нюанс: "язык мыслей" LLM — это не английский и не русский. Это многомерное пространство, где "честность" соседствует с "длиной ответа" и "частотой слова 'the'".
1 Метод: вычитание средних, или как найти иголку в стоге тензоров
Классический подход — собрать два набора активаций: для положительных примеров концепта (например, "скажи правду") и для отрицательных ("солги"). Потом вычесть средний вектор отрицательного из среднего положительного. Это и есть наш концептуальный вектор. Звучит просто, но на практике:
- Сбор данных: для концепта "честность" я использовал 200 пар предложений из датасета TruthfulQA, прогонял через модель на одном и том же слое (обычно середине — слой 20 для 7B).
- Усреднение: брал mean по всем токенам последнего токена (или всех токенов — разница есть).
- Нормализация: L2-нормировка до единичной длины — стандарт, но... (об этом ниже).
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.2-7B")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-7B")
# Предположим, есть две батчи: pos_hidden_states, neg_hidden_states
# shape: (batch, seq_len, hidden_dim)
pos_mean = pos_hidden_states.mean(dim=0).mean(dim=0) # усреднение по позициям
neg_mean = neg_hidden_states.mean(dim=0).mean(dim=0)
concept_vector = pos_mean - neg_mean
concept_vector = concept_vector / concept_vector.norm() # L2
Но не делайте этой ошибки: я сначала брал mean по всем токенам последовательности, включая служебные. В итоге вектор "честности" оказался почти неотличим от вектора "длины ответа". Нужно брать активации только для последнего токена — именно он отвечает за генерацию следующего слова. Или, если концепт привязан к определенной позиции (например, слово "правда" в середине), то осреднять вокруг него.
2 Эксперимент: 7B, verify и странный артефакт
Я проверял концепт "verify" (подтверждение) на Qwen2.5-7B. Собрал предложения с глаголами "confirm", "verify" против "guess", "assume". Найдя вектор и добваляя его с коэффициентом 0.3 на layer 20, модель начинала чаще использовать слова "check", "validate", а в длинных ответах — переспрашивать. Казалось, мы нашли "язык мыслей" для режима проверки. Но когда я заменил коэффициент на -0.3 (вычитание вектора), модель стала хаотично утверждать даже то, в чем была неуверена — как будто вектор управлял не самим концептом, а степенью уверенности в высказывании. Вот тут я вспомнил статью про negation neglect: модель путает отрицание концепта с его противоположностью. Вектор "verify" на самом деле захватывал и "уверенность", и "формальность".
3 Что мы нашли: "язык мыслей" — это скорее диалект
Векторы, которые мы ищем, — не чистые концепты, а их смесь с контекстными признаками. Например, вектор "креативность" из датасета стихов vs прозы на 7B модели сильно коррелировал с длиной предложения. Чтобы отделить одно от другого, нужны контр-фактические пары: пары предложений с одинаковой длиной, но разной креативностью. Без этого мы управляем не "мыслью", а артефактом датасета. Это перекликается с выводом из статьи про подтасовку аналитики: распределение тем ≠ истинные паттерны.
С другой стороны, когда удается поймать "чистый" вектор (например, для тона — формальный vs неформальный — из эксперимента с регулятором креатива), эффект впечатляет: одна строка кода действительно меняет стиль ответов без ретрайна.
Подводные камни: почему не стоит доверять векторам на 100%
- Пересечение концептов: вектор "честность" частично совпадает с вектором "нейтральность". При усилении одного вы можете случайно приглушить эмоции — модель станет скучной и формальной. Как в исследовании про суицидальные мысли — модель видит проблему, но неправильно интерпретирует свой же вектор.
- Сателлитные эффекты: я добавил вектор "позитивный" и получил не только больше улыбок, но и странное увеличение количества артиклей 'the'. Оказалось, в моем датасете позитивные примеры были длиннее.
- Нестабильность по слоям: на ранних слоях (1-10) концептуальные векторы почти не управляют поведением; на поздних (30-32) — могут сломать генерацию. Оптимум — середина (12-24 для 7B).
Эти сложности заставляют вспомнить, что LLM — это инопланетные сущности, как пишут в статье про изучение ИИ как биологических организмов. Мы не можем быть уверены, что тот вектор, который мы считаем "честностью", на самом деле не кодирует "длину хвоста" в распределении токенов.
Практический выхлоп: как добавить креативности без fine-tuning
Несмотря на все оговорки, activation steering работает. Я повторил эксперимент из статьи про KEF и OpenAI o3 (сравнение фреймворков для reasoning), но вместо сложных методов использовал простой steering: добавил вектор "аналитическое мышление" (собранный на парах "explain step by step" vs "just answer") к forward pass. Модель без дополнительного обучения начала выдавать цепочки размышлений, похожие на Chain-of-Thought. Конечно, до уровня o3 далеко, но для 7B — прорыв.
Еще один забавный кейс: я нашел вектор "вопросительность" (собран на предложениях с '?' и без). Усиление этого вектора на входе заставляло модель чаще задавать уточняющие вопросы вместо того, чтобы сразу давать ответ. Это может быть полезно для диалоговых ассистентов, но есть риск, что модель зациклится, задавая вопросы вечно. Эксперимент со свободным общением между LLM как раз показал, что мета-внимание может порождать петли.
Прогноз: скоро мы будем управлять эмоциями LLM, как диммером
Уже есть работы, где steering-векторы используются для контроля "эмоционального тона" или "уровня агрессии". Это прямое продолжение того, что мы обсуждали в психоанализе для нейросетей. Но пока любой такой вектор нужно проверять на десятках контрольных примеров — потому что "язык мыслей" LLM все еще полон сюрпризов. Не доверяйте векторам, пока не увидите, что они ломают на негативных сценариях.