Zero to Hero, лекция 4: активации, градиенты и BatchNorm (makemore, часть 3)
Подробный конспект лекции Карпатого: почему стартовый loss 27 вместо 3.3, чем опасен насыщенный tanh, откуда берётся Kaiming-инициализация, как устроена батч-нормализация и по каким четырём графикам видно, здорова ли глубокая сеть.
Коротко
Заголовок раздела «Коротко»- Перед рекуррентными сетями Карпатый задерживается на MLP из прошлой лекции. Нужно понять, как ведут себя активации на прямом проходе и градиенты на обратном. Именно из-за них глубокие и рекуррентные сети трудно обучать, и почти вся история архитектур — попытки это исправить.
- Две ошибки инициализации. Первая: стартовый loss 27 вместо ожидаемых — сеть «уверенно ошибается». Вторая: насыщенный tanh, который гасит градиент. Исправив обе, val улучшается с 2.17 до 2.10 без единого изменения архитектуры.
- Вместо подбора множителей на глаз — Kaiming-инициализацияинициализация Kaiming (He)EN Kaiming / He initializationVar(w) = 2 / n_in (He и соавт., MSRA, 2015). Двойка компенсирует то, что ReLU обнуляет половину входов. В PyTorch — nn.init.kaiming_normal_.: стандартное отклонение весов равно , для tanh gain равен .
- Батч-нормализацияпакетная нормализация (BatchNorm)EN batch normalizationНормирует каждый признак по примерам батча, хранит скользящие статистики для инференса (Ioffe, Szegedy, 2015). Стандарт CNN; в LLM не используется. (2015): раз хочется, чтобы входы tanh были примерно нормальными, их можно просто нормировать по батчу. Цена — примеры в батче связываются друг с другом. Это даёт побочную регуляризацию и бесконечные баги. Карпатый признаётся, что не раз «стрелял себе в ногу» этим слоем.
- Во второй части код переписывается в модули в стиле
torch.nn, и появляются четыре диагностических графика: активации, их градиенты, градиенты весов и главное — отношение обновления к весам. Ориентир — около .
Как смотреть вместе с курсом
Заголовок раздела «Как смотреть вместе с курсом»Главный урок курса по этой теме — 3.4.1 «Инициализация и нормализация»: затухание сигнала через 50 слоёв, Xavier и Kaiming, BatchNorm, LayerNorm и RMSNorm, Pre-Norm и Post-Norm. Лекция и урок дополняют друг друга. Урок шире (он ведёт к трансформерам и LLM), лекция подробнее показывает диагностику на живой сети. Графики «update / data» и «loss на старте» разобраны в уроке 3.3.3 «Как отлаживать обучение», а модули, параметры и буферы — в уроке 3.3.2.
Порядок такой: урок 4.1.4 (MLP-модель), потом первая часть лекции (до 1:18), потом урок 3.4.1, потом вторая часть лекции. Новичку можно бегло просмотреть разбор ResNet (1:04:55) и документации PyTorch. Главы про стартовый loss, насыщение tanh и четыре графика пропускать не стоит.
Код лекции — ноутбук makemore_part3_bn.ipynb (MIT). Статьи: He et al., Delving Deep into Rectifiers (2015) и Ioffe, Szegedy, Batch Normalization (2015). Китайская учебная версия с переведёнными пояснениями — 004_makemore_BatchNorm.
Карта лекции
Заголовок раздела «Карта лекции»Таймкоды — из глав в описании видео.
| Время | Глава в оригинале | О чём |
|---|---|---|
| 0:00 | intro | зачем ещё одна лекция про MLP |
| 1:25 | starter code | исходный код, torch.no_grad |
| 4:24 | fixing the initial loss | loss 27 вместо 3.3 |
| 13:04 | fixing the saturated tanh | насыщенный tanh и мёртвые нейроны |
| 27:53 | calculating the init scale: “Kaiming init” | масштаб весов по fan_in |
| 40:43 | batch normalization | батч-нормализация с нуля |
| 1:03:07 | batch normalization: summary | сводка по слою |
| 1:04:55 | real example: resnet50 walkthrough | ResNet и документация PyTorch |
| 1:14:14 | summary of the lecture | итоги первой части |
| 1:18:40 | just kidding: part2: PyTorch-ifying the code | свои Linear, BatchNorm1d, Tanh |
| 1:26:52 | viz #1: forward pass activations statistics | гистограммы активаций |
| 1:30:59 | viz #2: backward pass gradient statistics | гистограммы градиентов |
| 1:32:09 | the fully linear case of no non-linearities | сеть без нелинейностей |
| 1:36:20 | viz #3: parameter activation and gradient statistics | градиенты весов, grad/data |
| 1:39:55 | viz #4: update:data ratio over time | update/data во времени |
| 1:46:05 | bringing back batchnorm, looking at the visualizations | те же графики с BatchNorm |
| 1:51:34 | summary of the lecture for real this time | настоящие итоги |
1. Зачем ещё одна лекция про MLP
Заголовок раздела «1. Зачем ещё одна лекция про MLP»В планах — рекуррентные сети, GRU, LSTM. Карпатый объясняет, почему пока рано. Рекуррентные сети очень выразительны, в принципе они могут реализовать любой алгоритм. Но их плохо оптимизируют методы первого порядка, то есть градиентный спуск, — других у нас нет. Чтобы понять почему, нужна интуиция о том, как в сети ведут себя активации и градиенты. Многие варианты RNN появились как раз как попытки исправить эти проблемы.
Стартовый код — MLP из прошлой лекции, очищенный от «магических чисел»:
размер эмбеддинга n_embd = 10 и скрытого слоя n_hidden = 200 вынесены в переменные. Около 11 тысяч
параметров, 200 тысяч шагов, батч 32. Оценка вынесена в функцию split_loss(split) с декоратором
@torch.no_grad(). Внутри такой функции PyTorch не строит граф для обратного прохода, и всё работает быстрее.
Исходный результат: train около 2.12, val около 2.17.
2. Проблема 1: стартовый loss
Заголовок раздела «2. Проблема 1: стартовый loss»На первом шаге loss равен 27, а потом быстро падает до 2–3. Карпатый говорит, что почти всегда можно заранее прикинуть, каким должен быть loss при инициализации. На старте у сети нет причин предпочитать какой-то символ. Значит, распределение должно быть примерно равномерным, вероятность каждого символа — , а loss:
27 вместо 3.3 значит, что сеть «уверенно ошибается»: выдаёт очень острые распределения, и правильный символ почти всегда получает крошечную вероятность. Это видно на игрушечном примере из четырёх классов. При нулевых логитах softmax равномерный, и loss равен при любом правильном ответе. Если умножить случайные логиты на 10, правильный класс почти наверняка проиграет, и loss взлетит. На ещё более экстремальных логитах он доходит до бесконечности. Логиты не обязаны быть нулями — достаточно, чтобы они были равны. Но проще всего сделать их около нуля.
Логиты считаются как h @ W2 + b2. Значит, лечим так: смещение b2 умножаем на 0, веса W2 — на 0.01. Строго ноль
Карпатый ставить не любит. Для выходного слоя это, может, и безопасно, но маленькие ненулевые числа оставляют
немного случайности для нарушения симметрии. Стартовый loss становится 3.32.
Заодно исчезает «хоккейная клюшка» на графике loss: резкое падение в первые сотни шагов. В нём сеть ничему полезному не училась, а просто сжимала раздутые логиты. Теперь эти шаги тратятся на настоящее обучение, и итог лучше: val 2.17 → 2.13.
3. Проблема 2: насыщенный tanh
Заголовок раздела «3. Проблема 2: насыщенный tanh»Логиты в порядке, но внутри сети неладно. Гистограмма скрытых активаций h показывает, что большинство значений
равны или . Причина видна на гистограмме входов tanh (preactivation): они разбросаны от до , и
tanh почти всё сжимает к краям.
Почему это плохо, видно из обратного прохода tanh, который мы писали в micrograd:
Если , множитель равен нулю, и градиент через этот нейрон не проходит. Если , градиент проходит без изменений. Градиент через tanh может только уменьшаться, и тем сильнее, чем ближе нейрон к краю. Интуиция Карпатого: на плоском «хвосте» tanh изменение входа почти не меняет выход, значит, веса этого нейрона на loss не влияют.
Карпатый рисует картинку (примеры × нейроны): белое — где . Белого очень много. Если бы какой-то столбец был целиком белым, получился бы мёртвый нейрон: ни один пример не выводит его из насыщения, градиента нет, нейрон не учится никогда. Здесь таких столбцов нет, поэтому сеть всё-таки учится, но хуже, чем могла бы.
Та же беда у других нелинейностей:
- sigmoid — тоже «сжимающая» функция с плоскими хвостами;
- ReLU — при отрицательном входе ровно ноль, и градиент обнуляется полностью. Мёртвый ReLU-нейрон может появиться при инициализации или во время обучения. Слишком большой шаг «выбивает» нейрон с многообразия данных, и его больше ничто не активирует. Карпатый называет это «необратимым повреждением мозга» сети;
- у Leaky ReLU плоских участков нет, а у ELU они есть.
Лечение то же, что для логитов: входы tanh должны быть ближе к нулю. b1 умножаем на 0.01 (немного разнообразия
не помешает), W1 — сначала на 0.1, потом на 0.2. Белое на картинке исчезает, val 2.13 → 2.10. Сеть мелкая,
поэтому прощает плохую инициализацию. При 50 слоях ошибки накапливаются, и сеть может не обучиться совсем.
4. Kaiming-инициализация: откуда брать множитель
Заголовок раздела «4. Kaiming-инициализация: откуда брать множитель»Множитель 0.2 подобран на глаз. Для сети из многих слоёв так не годится. Карпатый показывает, откуда берётся правильный масштаб:
import torch
x = torch.randn(1000, 10) # 1000 примеров, 10 признаков, std = 1w = torch.randn(10, 200) # веса тоже из N(0, 1)print(x.std(), (x @ w).std()) # ≈ 1.0 и ≈ 3.16: разброс вырос в √10 разprint((x @ (w / 10**0.5)).std()) # ≈ 1.0: делим на √fan_in — разброс сохраняетсяКаждый выход — сумма десяти произведений, и дисперсии складываются. Чтобы стандартное отклонение осталось единичным, веса делят на , где fan_in — число входов слоя. Подробно, с выводом через дисперсию, это разбирает урок 3.4.1.
Систематически вопрос изучала статья Кайминга Хэ и соавторов «Delving Deep into Rectifiers» (2015), про свёрточные сети с ReLU. ReLU обнуляет половину распределения, и это нужно компенсировать множителем: стандартное отклонение весов . Статья проверяет и обратный проход. Если прямой проход нормирован правильно, обратный тоже нормирован с точностью до постоянного множителя, и на практике это почти не важно.
В PyTorch это torch.nn.init.kaiming_normal_. Аргумент mode чаще оставляют по умолчанию (fan_in), а
nonlinearity задаёт поправку gain:
| Нелинейность | gain | Почему |
|---|---|---|
| нет (линейный слой) | ничего не сжимается | |
| ReLU | обнуляет половину распределения | |
| tanh | сжимает хвосты, и это нужно компенсировать |
Итоговая формула: . У нас fan_in равен 30 (три символа по 10 чисел), поэтому — вместо подобранных на глаз 0.2. Результат тот же, val 2.10, но без магических чисел.
Важная оговорка Карпатого. Семь лет назад, когда вышла статья, к инициализации приходилось относиться очень бережно: всё было хрупким. С тех пор появились средства, которые делают обучение намного устойчивее. Это остаточные связи, слои нормализации (batch, layer, group) и оптимизаторы лучше обычного SGD, например RMSProp и особенно Adam. Сам он на практике просто делит веса на .
5. Батч-нормализация
Заголовок раздела «5. Батч-нормализация»BatchNorm предложили в Google в 2015 году. Статья была очень влиятельной: с этим слоем стало возможно надёжно обучать глубокие сети. Идея, которая когда-то, по словам Карпатого, «взорвала ему мозг»: мы хотим, чтобы входы tanh были примерно стандартными нормальными. Так давайте просто нормируем их. Вычитание среднего и деление на стандартное отклонение — дифференцируемые операции, через них можно делать backprop.
Для предактиваций hpreact формы среднее и стандартное отклонение считаются по батчу, то есть
по оси 0 с keepdim=True. Получаются строки , по числу на нейрон. После нормировки каждый нейрон
на этом батче имеет нулевое среднее и единичный разброс. Отсюда название.
Но жёстко держать распределение нормальным нельзя: сети нужно уметь делать нейроны острее или площе. Поэтому сверху добавляются обучаемые масштаб (gain, ) и сдвиг (bias, ). На старте это единицы и нули, а дальше backprop двигает их как обычные параметры:
Здесь и — среднее и дисперсия по батчу, (обычно ) защищает от деления на ноль. Код из ноутбука лекции (MIT), комментарии наши:
# параметры BatchNorm: учатся backprop'омbngain = torch.ones((1, n_hidden))bnbias = torch.zeros((1, n_hidden))# буферы: скользящие оценки для инференса, градиентом не учатсяbnmean_running = torch.zeros((1, n_hidden))bnstd_running = torch.ones((1, n_hidden))
# ...внутри цикла обучения:hpreact = embcat @ W1 # смещение b1 убрано: BatchNorm его всё равно вычтетbnmeani = hpreact.mean(0, keepdim=True) # среднее по батчу для каждого нейронаbnstdi = hpreact.std(0, keepdim=True) # разброс по батчуhpreact = bngain * (hpreact - bnmeani) / bnstdi + bnbiaswith torch.no_grad(): # обновление буферов не участвует в backprop bnmean_running = 0.999 * bnmean_running + 0.001 * bnmeani bnstd_running = 0.999 * bnstd_running + 0.001 * bnstdiВ сети с одним скрытым слоем BatchNorm почти ничего не даёт: val около 2.10. Масштаб весов здесь легко посчитать и руками. Но в глубокой сети с разными типами слоёв подобрать масштаб каждой матрицы невозможно, а вставить BatchNorm после каждого линейного или свёрточного слоя — легко. Поэтому слой и стал популярным.
Цена: примеры в батче связаны
Заголовок раздела «Цена: примеры в батче связаны»Раньше каждый пример обрабатывался независимо, а батч был лишь способом считать быстрее. С BatchNorm выход сети на примере зависит от того, какие ещё примеры случайно попали в батч: от них зависят среднее и разброс. Активации и логиты немного «дрожат». Казалось бы, это баг, но дрожание работает как регуляризация, что-то вроде аугментации данных: сети труднее подогнаться под конкретные примеры. Из-за этого побочного эффекта от BatchNorm трудно отказаться. Хотя никому не нравится, что примеры связаны, и поэтому придумали нормализации без этой связи: layer, instance, group normalization.
Инференс: скользящие средние
Заголовок раздела «Инференс: скользящие средние»После обучения сеть хочется применять к одному примеру, а для одного примера статистику батча не посчитать.
Статья предлагает после обучения один раз прогнать весь train и зафиксировать среднее и разброс. Это
«калибровка», и Карпатый её показывает. Но второй этап никто делать не хочет, поэтому оценки копят прямо
во время обучения скользящим средним, как в коде выше. В конце они почти совпадают с честно посчитанными, и
loss на валидации тот же. Так устроен и nn.BatchNorm1d в PyTorch.
Ещё две детали:
- смещение перед BatchNorm бесполезно. Что бы ни прибавил
b1, BatchNorm вычтет это вместе со средним, и градиентb1будет нулём. Поэтому у линейного слоя перед BatchNorm смещение отключают, а сдвиг берёт на себя ; - momentum. В лекции коэффициент обновления буферов 0.001, а в PyTorch по умолчанию 0.1. При большом батче статистики стабильны, и 0.1 подходит. При батче 32 они сильно скачут, и с 0.1 буферы могут не успокоиться.
6. Настоящий пример: ResNet и документация PyTorch
Заголовок раздела «6. Настоящий пример: ResNet и документация PyTorch»Карпатый открывает реализацию ResNet в PyTorch. Это остаточная сеть для классификации изображений, сама архитектура
будет позже. Её повторяющийся блок (bottleneck) устроен по тому же мотиву, что и наша сеть: слой с весами
(свёртка — по сути тот же , только на участках изображения), потом нормализация (BatchNorm2d), потом
нелинейность (ReLU). У свёрток там стоит bias=False — ровно по той причине, что выше.
Документация PyTorch подтверждает остальное:
nn.Linearпо умолчанию берёт веса из равномерного распределения на . Тот же масштаб , только с gain 1 и без нормального распределения;- у
nn.BatchNorm1dестьeps(по умолчанию ),momentum(0.1),affine(обучаемые и , почти всегда включено) иtrack_running_stats. Последний можно выключить, если статистику считаете отдельно после обучения.
Сводка улучшений из ноутбука:
| Шаг | train | val |
|---|---|---|
| исходная сеть | 2.125 | 2.168 |
| исправлен «уверенно неправый» softmax | 2.07 | 2.13 |
| исправлен насыщенный tanh | 2.036 | 2.103 |
| Kaiming вместо подбора на глаз | 2.038 | 2.107 |
| добавлен BatchNorm | 2.067 | 2.105 |
7. Часть 2: код в стиле torch.nn
Заголовок раздела «7. Часть 2: код в стиле torch.nn»«Шутка, лекция не закончилась» — и Карпатый переписывает сеть в модули с тем же интерфейсом, что в PyTorch:
Linear(fan_in, fan_out, bias=True)— веса из нормального распределения, делённые на , смещение нулями;BatchNorm1d(dim, eps=1e-5, momentum=0.1)— с флагомtraining. В режиме обучения слой берёт статистику батча и обновляет буферы, в режиме оценки — накопленныеrunning_meanиrunning_var. Здесь копится уже дисперсия, как в статье, а не стандартное отклонение. Параметры (gamma,beta) возвращаются вparameters(), буферы — нет: их обновляет экспоненциальное скользящее среднее подtorch.no_grad();Tanh— без параметров.
Каждый модуль сохраняет свой выход в self.out. В PyTorch такого нет, это нужно только для графиков. Если
приписать спереди nn., код будет работать с настоящими слоями PyTorch. Сеть из шести линейных слоёв по 100
нейронов — около 46–47 тысяч параметров. Курс разбирает настоящие nn.Module и nn.Sequential в уроке
3.3.2, а модульную версию этого кода — в
шестой лекции.
8. Четыре графика здоровья сети
Заголовок раздела «8. Четыре графика здоровья сети»Сеть сначала без BatchNorm: «бутерброд» из Linear и Tanh. Перед backward Карпатый вызывает
layer.out.retain_grad(), чтобы PyTorch сохранил градиенты промежуточных тензоров.
1. Активации прямого прохода. Гистограммы выходов каждого tanh-слоя, их среднее, стандартное отклонение и доля насыщения (сколько значений по модулю больше 0.97). С gain первый слой насыщен процентов на 20, дальше всё стабилизируется: разброс около 0.65, насыщение около 5%. С gain 1 активации от слоя к слою сжимаются к нулю, потому что tanh каждый раз немного сжимает распределение. С gain 3 — сильное насыщение. Карпатый честно говорит, что не знает, откуда в PyTorch взялось именно . Но эмпирически оно хорошо держит этот бутерброд.
2. Градиенты обратного прохода. Те же гистограммы для градиентов по выходам tanh. В здоровой сети они примерно одинаковы во всех слоях. При gain 0.5 активации гаснут, а градиенты расползаются. При gain 3 появляется асимметрия между слоями.
Отступление: сеть совсем без нелинейностей. Если убрать все tanh, правильный gain — ровно 1. При активации от слоя к слою разбухают, а градиенты сжимаются. Так выглядело обучение глубоких сетей до нормализаций, Adam и остаточных связей: «эквилибристика», где всё должно быть точно выверено. Зачем вообще tanh, если без него так просто? Стопка линейных слоёв схлопывается в один линейный слой и представляет только линейные функции. При этом динамика обучения у такой стопки другая, чем у одного слоя, и её изучают целые статьи. Нелинейности превращают бутерброд в сеть, которая может приблизить любую функцию.
3. Градиенты весов. Для каждой матрицы весов — среднее и разброс градиента и отношение разброса градиента к разбросу самих весов (grad:data). Почти везде градиент примерно в 1000 раз меньше весов. Выбивается последний слой: его градиенты примерно в 10 раз больше, потому что веса этого слоя искусственно уменьшили. При простом SGD последний слой на старте учится раз в 10 быстрее остальных. Со временем это выравнивается.
4. Отношение обновления к весам во времени. Важно не отношение градиента к весам, а то, насколько шаг реально меняет веса. Для каждой матрицы на каждом шаге считается , и строится график.
ud = [] # update:data для каждого параметра на каждом шаге# ...в цикле обучения, после шага:with torch.no_grad(): ud.append([((lr * p.grad).std() / p.data.std()).log10().item() for p in parameters])Ориентир Карпатого — около : за шаг веса меняются примерно на тысячную долю. Сильно ниже значит, что шаг слишком мал. С learning rate 0.001 обновления примерно в 10 000 раз меньше весов (около на графике), и сеть учится слишком медленно. Сильно выше значит, что веса меняются слишком резко. Если забыть деление на , все четыре графика сразу показывают беду: насыщение, разнобой градиентов, отношения около и . Курс применяет то же правило к обучению с Adam в уроке 3.3.3.
9. BatchNorm возвращается
Заголовок раздела «9. BatchNorm возвращается»Теперь BatchNorm ставится после каждого линейного слоя, перед tanh. Можно и после нелинейности, и после последнего линейного слоя перед loss. Тогда «уверенность» выхода на старте уменьшают через последнего BatchNorm (умножают на 0.1), а не через веса.
Активации, естественно, выглядят идеально: перед каждым tanh стоит нормировка. Разброс около 0.65, насыщение около 2%, все слои одинаковые. Главное — сеть стала намного менее чувствительна к gain. При gain 0.2 активации не меняются вовсе. Но меняется масштаб обновлений: обратный проход BatchNorm зависит от масштаба входа. Поэтому при сильной смене масштаба весов приходится перенастраивать learning rate. Можно даже вообще не делить веса на — сеть будет вести себя прилично. Только обновления окажутся заметно ниже , и learning rate нужно поднять раз в 10, до 1.0.
10. Итоги и честные оговорки
Заголовок раздела «10. Итоги и честные оговорки»Карпатый называет три цели второй части. Первая — познакомить с BatchNorm, одним из первых современных приёмов, стабилизирующих глубокие сети. Вторая — переписать код в модули, которые складываются как кубики лего. Третья — показать диагностику: смотреть не на один снимок, а на динамику активаций, градиентов и отношений update/data.
Чего он не добился и не пытался. BatchNorm не улучшил результат: Карпатый проверял, в том числе с поиском learning rate. Сеть упирается уже не в оптимизацию, а в длину контекста — три символа. Дальше нужны более мощные архитектуры: рекуррентные сети и трансформеры. Полного объяснения, почему смена gain требует другого learning rate, тоже нет: для этого нужно разбирать обратный проход каждого слоя. Его финальная мысль: инициализация и обратный проход до сих пор не «решены», это открытая область исследований, но инструменты, которые показывают, на верном ли мы пути, у нас уже есть.
Где курс объясняет то же самое и что есть сверх курса
Заголовок раздела «Где курс объясняет то же самое и что есть сверх курса»| Тема лекции | Где в курсе | Что в лекции сверх курса |
|---|---|---|
| Ожидаемый loss на старте () | 3.3.3 | как «уверенно неправый» выход рождает «хоккейную клюшку» |
| Насыщение tanh, мёртвые нейроны | 3.4.1, 3.3.3 | картинка насыщения «примеры × нейроны» |
| Xavier и Kaiming, gain | 3.4.1 | gain для tanh и его проверка на шестислойной сети |
| BatchNorm: формулы, режимы train / eval | 3.4.1 | калибровка после обучения против скользящих средних, momentum при маленьком батче, смещение перед BN |
| LayerNorm, RMSNorm, Pre-Norm | 3.4.1 | в лекции только упомянуты |
| Гистограммы, update / data ≈ | 3.3.3 | четыре графика подряд и как они ломаются при ошибках |
| Модули, параметры и буферы | 3.3.2 | свой BatchNorm1d с нуля |
| Остаточные связи | 3.4.2 | в лекции только анонс |
Курс идёт дальше лекции в главном: объясняет, почему в трансформерах вместо BatchNorm используют LayerNorm и RMSNorm. Карпатый к этому придёт в лекции 7.
Что сделать руками
Заголовок раздела «Что сделать руками»Упражнения из описания видео (копия с пояснениями на китайском — 004_makemore_BatchNorm/BatchNorm_exercises.ipynb
в репозитории karpathy-zero-to-hero-zh-transcripts):
- Инициализируйте все веса и смещения нулями и обучите сеть. Обычно ждут, что сеть либо учится, либо нет. На деле она учится, но лишь частично, и результат плохой. Посмотрите гистограммы активаций и градиентов и выясните, какие параметры учатся, а какие нет, и почему.
- Обучите небольшую трёхслойную MLP с BatchNorm, затем «вклейте» , и буферы в веса предыдущих линейных слоёв и уберите BatchNorm. Проверьте, что loss на инференсе не изменился (см. нашу проверку выше). В этом преимущество BatchNorm перед LayerNorm: после обучения слой можно выбросить.
И ещё:
- Повторите «журнал» улучшений из раздела 6 на своём компьютере. Совпадают ли числа до второго знака? Если нет, почему?
- Постройте четыре графика для сети с ReLU вместо tanh и gain . Найдите мёртвые нейроны после обучения с завышенным learning rate.
- Замените свой
BatchNorm1dнаnn.BatchNorm1d(n_hidden, momentum=0.001)и сравните буферы после обучения.
Словарик
Заголовок раздела «Словарик»| EN | RU |
|---|---|
| activations, pre-activations | активации, предактивации (входы нелинейности) |
| confidently wrong | уверенно неправ |
| hockey stick loss | «хоккейная клюшка» на графике loss |
| saturated tanh, flat tails | насыщенный tanh, плоские хвосты |
| dead neuron, dead ReLU | мёртвый нейрон, мёртвый ReLU |
| fan-in / fan-out | число входов / выходов слоя |
| gain | поправочный множитель (gain) |
| Kaiming (He) initialization | инициализация Kaiming (He) |
| batch normalization | батч-нормализация |
| scale and shift (gain, bias) | масштаб и сдвиг |
| running mean / variance, buffer | скользящее среднее / дисперсия, буфер |
| momentum (BatchNorm) | коэффициент скользящего среднего |
| calibration | калибровка |
| train / eval mode | режим обучения / режим оценки |
| grad:data ratio, update:data ratio | отношение градиента к весам, отношение обновления к весам |
| residual connection | остаточная связь |