Gemma 4 апскейл: настройка layer_scalar при дублировании слоёв (RYS) | AiManual
AiManual Logo Ai / Manual.
04 Июл 2026 Гайд

Как правильно апскейлить Gemma 4: настройка layer_scalar при дублировании слоёв

Пошаговое руководство по корректировке layer_scalar для стабильного апскейла Gemma 4. Формула s^(1/N), примеры ошибок и фиксы.

Откуда растут ноги у layer_scalar

Когда Google выпустила Gemma 4, все бросились копировать слои, чтобы натянуть модель на большее количество параметров. Энтузиасты RYS (Recycle Your Scale) радостно дублировали трансформер-блоки, ожидая прироста интеллекта. Но через пару итераций модель начинала нести чушь, а loss улетал в стратосферу. Проблема не в самом дублировании — проблема в том, что каждый слой содержит layer_scalar, и его нужно пересчитывать. Без этого вы просто ломаете внутреннюю нормализацию.

В Gemma 4 layer_scalar — это learnable параметр, который масштабирует вывод каждого слоя перед skip-connection. Он инициализируется как layer_scalar = 1 / sqrt(depth), где depth — количество слоёв. Когда мы увеличиваем глубину, исходные коэффициенты перестают соответствовать новой архитектуре — отсюда взрывной рост активаций.

Зачем вообще нужен этот скаляр? Дело в residual stream. В глубоких сетях градиенты затухают или взрываются, если не контролировать вклад каждого блока. Gemma 4 использует Pre-LayerNorm и scaling, чтобы стабилизировать поток. Когда вы берёте оригинальный слой и просто копируете его N раз, все скаляры остаются старыми — допустим, 0.07 для 200-слойной модели. Но если вы удвоили глубину, скаляр каждого нового слоя должен быть меньше, чтобы суммарный вклад не перекосило.

Типичная ошибка: копировать слои вслепую

Берём Gemma 4 12B (28 слоёв). Хотим апскейл 2х — получаем 56 слоёв. Самый частый сценарий: скопировать чекпоинт, продублировать блоки, запустить обучение. Loss на старте — 8.5 (ваш друг LLM вас не понимает). Некоторые пробуют заморозить старые слои и дообучать новые — работает чуть лучше, но стабильность всё равно хромает. Почему? layer_scalar старых слоёв рассчитывался на 28 блоков, а новых — тоже старый (обычно единица или 1/sqrt(28) ≈ 0.19). Но правильное значение для 56 слоёв — 1/sqrt(56) ≈ 0.134. Разница в 1.4 раза — это прямой путь к разбалансировке.

Помните историю с лоботомическими слоями в Llama 3.1 и Qwen 2.5? Та же петрушка: если не согласовать масштабирование, часть слоёв превращается в «зоны убийства». В Gemma 4 это проявляется ещё сильнее из-за более чувствительной нормализации.

Формула спасения: s^(1/N)

Вместо того чтобы гадать на кофейной гуще, используем мультипликативную корректировку. Пусть s — множитель увеличения глубины (например, 2 для удвоения). Тогда каждый новый слой получает layer_scalar, домноженный на s^(1/N), где N — общее количество новых слоёв после всех дублирований. На практике это означает:

  • Старый слой: original_scalar = 1/sqrt(L_old)
  • Новый скаляр для всех слоёв (и старых, и новых) становится: scalar_new = original_scalar * (s)^(1/N_new)

Но есть нюанс: мы не меняем параметры весов, только scalar. Поскольку layer_scalar — learnable, после такой инициализации модель быстро адаптируется, но стартовый loss падает в разы. Проверено на Gemma 4 2B → 4B эксперименте в гибриде Gemma и DeepSeek — там тот же принцип используется для смешивания слоёв разного масштаба.

💡
Вывод формулы: хотим, чтобы норма активаций оставалась O(1) при увеличении глубины. Если каждый слой вносит вклад ~layer_scalar, то суммарный вклад N слоёв = N * layer_scalar. Для сохранения нормы нужно layer_scalar ~ 1/N. Но в Gemma 4 используется 1/sqrt(L). Поэтому при апскейле в s раз: scalar_new = 1/sqrt(L*s) = original_scalar / sqrt(s). Однако лучше пересчитать относительно всех слоёв: scalar_new = original_scalar * (1/s)^(1/2). Форма s^(1/N) — более общая для неравномерного дублирования.

Пошаговый план: от чекпоинта до апскейла 2x

1 Получить архитектуру и значения layer_scalar

import torch
from transformers import Gemma4ForCausalLM

model = Gemma4ForCausalLM.from_pretrained("google/gemma-4-12b")
# Найти все layer_scalar параметры
scalar_params = {name: param for name, param in model.named_parameters() if "layer_scalar" in name}
L_old = len(scalar_params)  # например, 28
original_scalar_value = scalar_params[list(scalar_params.keys())[0]].item()
print(f"Старых слоёв: {L_old}, scalar: {original_scalar_value:.4f}")

2 Дублировать слои (стандартный RYS)

# Условно — копируем блоки
# Нужно расширить список слоёв и вставить копии
# Получаем новую модель с N_new = N_old * s
s = 2
N_new = L_old * s  # 56

3 Пересчитать layer_scalar

import math

# Правильный новый скаляр для всех слоёв
new_scalar = original_scalar_value * (s ** (1.0 / N_new))
print(f"Новый layer_scalar: {new_scalar:.4f}")

# Присвоить всем layer_scalar новое значение
for name, param in model.named_parameters():
    if "layer_scalar" in name:
        param.data.fill_(new_scalar)

4 Проверить loss на старте

# Оценить loss на batch данных
with torch.no_grad():
    outputs = model(input_ids=torch.randint(0, 256, (1, 128)))
    loss = outputs.loss
print(f"Initial loss: {loss.item()}")  # Должно быть около 2-4, не 8+

Если loss сразу < 5 — можно начинать дообучение. Если > 7 — проверьте корректность расчёта и то, что layer_scalar действительно применился ко всем слоям (старым и новым).

Грабли и подводные камни

  • Не замораживайте старые скаляры. Оставьте их учиться вместе с новыми. Инициализация — это лишь стартовая точка, но layer_scalar должен подстроиться под конкретную задачу.
  • Проверьте, что layer_scalar не привязан к позиции. В Gemma 4 все скаляры независимы — их можно безопасно перезаписать. В других архитектурах может быть shared scalar — тогда формула меняется (делить на корень из числа слоёв один раз).
  • Апскейл в 4x или 8x. Формула работает для любого s, но при s > 4 рекомендую добавить warmup по layer_scalar: начинать с меньшего значения (например, original_scalar * s^(1/(2N))) и постепенно увеличивать до расчётного.
  • Если используете LoRA поверх апскейла. Сжатие MLP и компрессия могут ещё сильнее дестабилизировать модель, если layer_scalar не сбалансирован. Применяйте корректировку до внедрения LoRA.
  • Помните про внимание. Дублирование слоёв часто нарушает распределение attention. Скаляр влияет на норму ключей/значений косвенно. После апскейла полезно сделать короткий тест на когерентность текста.

Однажды я видел эксперимент, где парень увеличил Gemma 4 2B до 6.5B, скопировав каждый слой трижды и не тронув layer_scalar. Модель на первом же токене выдала температуру 98. Я не шучу — loss был 97.8. После применения формулы loss упал до 4.1. Так что не повторяйте его ошибок.

Альтернативный подход: инициализация от последнего слоя

Некоторые вместо формулы используют копирование layer_scalar из последнего слоя для всех новых. Это работает, если модель уже обучена, но в Gemma 4 последний слой часто имеет самый маленький scalar (из-за sqrt(L)). Если копировать его, новые слои стартуют с заниженным значением — градиенты будут слабыми, обучение замедлится. Формула s^(1/N) даёт более высокий старт для молодых слоёв, ускоряя сходимость. Если у вас есть доступ к GPU с большим объёмом памяти, можете попробовать оба варианта — разница в скорости достижения baseline составляет ~30%.

Вместо точки — прогноз

Google не будет официально поддерживать RYS, но комьюнити уже адаптирует Gemma 4 под свои задачи. Думаю, через полгода появятся инструменты вроде mergekit, которые автоматически пересчитывают layer_scalar при апскейле. А пока — держите формулу в тулбоксе и не забывайте проверять loss. Если статья была полезна, поделитесь с коллегой, который всё ещё тыкает copy-paste бездумно. И да, если вы запускаете на Lambda Labs — берите как минимум A100 80GB, на 3090 апскейл 2B→4B ещё влезет, но стоить будет вечно.

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