Да го накараме да се обучава — и да обобщава
Шестслойна мрежа, чиято загуба стои на ln 2, поправена измерване по измерване. После double descent: 5 000 параметъра върху 40 точки.
На тази страница
Мрежата от Глава 5 работи. Има девет параметъра, научава XOR и градиентите ѝ съвпадат с PyTorch до шестнадесетия знак след десетичната запетая.
Направете я шест слоя дълбока и тя спира да учи напълно. Не бавно — напълно. Ето шестслойна мрежа върху класификационен проблем с две спирали, обучавана 5000 стъпки:
step 1: loss 0.693147
step 5000: loss 0.693147
accuracy: 50.0 %Това число не е произволно. е бинарната кръстосана ентропия на модел, който връща вероятност за всичко, а 50 % е хвърляне на монета върху балансиран набор от данни. След пет хиляди стъпки мрежата не е помръднала нито с една цифра. Нищо не се е сринало, нищо не е предупредило, а градиентите все още са точно правилни.
Тази глава е за пропастта между мрежа, която се изпълнява, и мрежа, която работи. Тя има две половини, които изглеждат като различни теми, но са една и съща задача: да накарате загубата да тръгне надолу и да я накарате да тръгне надолу върху данни, които моделът никога не е виждал.
Защо шестслойната мрежа е мъртва
Връзка към раздела: Защо шестслойната мрежа е мъртваЗапочнете с гледане, вместо с гадаене. Прекарайте batch от входове през мрежата и отпечатайте стандартното отклонение на активациите във всеки слой, а след това стандартното отклонение на градиентите на теглата:
def profile(model, x):
h = x
for layer in model:
h = layer(h)
if isinstance(layer, (nn.Tanh, nn.ReLU)):
print(f"activation std: {h.std().item():.4f}")
model(x).sum().backward()
for p in model.parameters():
if p.dim() == 2:
print(f"gradient std: {p.grad.std().item():.2e}")Три инициализации, една и съща архитектура, шест слоя от :
| инициализация | std на активациите, слоеве 1→6 |
|---|---|
| normal, std | 0.0145 · 0.0016 · 0.0002 · 0.0000 · 0.0000 · 0.0000 |
| normal, std | 0.6573 · 0.9296 · 0.9585 · 0.9634 · 0.9637 · 0.9625 |
| Xavier | 0.1579 · 0.1493 · 0.1353 · 0.1333 · 0.1325 · 0.1403 |
| инициализация | std на градиента, първи слой → последен |
|---|---|
| normal, std | 3.20e-06 · 4.97e-07 · … · 6.40e-06 |
| normal, std | 1.94e+03 · 2.28e+02 · 1.22e+02 · 4.43e+01 · 1.85e+01 · 7.30e+00 |
| Xavier | 2.31e+00 · 4.50e-01 · 4.26e-01 · 3.89e-01 · 4.39e-01 · 4.73e-01 |
Първият ред е мрежата по-горе и тя не учи бавно — в нея не е останал сигнал. До четвъртия слой стандартното отклонение на активациите е паднало до нула при четири знака след десетичната запетая. Всеки вход произвежда един и същ изход, изходът е константа, а градиентът на константа е нищо. Теглата са били инициализирани малки „за всеки случай“, а малкото се е оказало фатално.
Вторият ред е противоположният отказ и си струва да бъде разбран, защото е контраинтуитивен. Активациите изглеждат здрави — около 0.96 — но това е наситена, закована близо до границата си, точно режимът, който Глава 5 измери като губещ почти десет хиляди пъти от градиента. И все пак градиентите са огромни: 1940 в първия слой. И двете неща са верни едновременно. Всяка обратна стъпка умножава по , а при 128 входа с единична дисперсия този фактор има усилване около , което надделява над свиването от наситената . Градиентите растат геометрично на връщане назад. Това е exploding gradient и в реално обучение води до стойности на загубата nan в рамките на няколко стъпки.
Третият ред е това, което искате: активации с приблизително постоянен мащаб през дълбочината, градиенти с приблизително постоянен мащаб през дълбочината. Нищо не умира, нищо не експлодира.
Нормализация и коя оцеля
Връзка към раздела: Нормализация и коя оцеляДобрата инициализация оправя мащаба в стъпка нула. Тя не го държи фиксиран: теглата се движат, а до стъпка пет хиляди внимателният аргумент с дисперсията вече не важи.
Нормализационните слоеве налагат мащаба непрекъснато. При даден вектор от активации изваждат средна стойност, делят на стандартно отклонение, после прилагат научен мащаб и отместване , така че слоят да може да отмени нормализацията, ако се окаже, че това иска:
Единственият истински въпрос е върху какво осреднявате. Batch normalisation3 взема и по batch измерението, по една статистика за всяка характеристика. Layer normalisation4 ги взема по характеристиките, по една статистика за всеки пример.
Този избор изглежда дребен и решава почти всичко надолу по веригата:
BatchNorm прави изхода на всеки пример зависим от другите примери, които случайно са попаднали в неговия batch. По време на обучение това е лек регуляризатор. По време на inference няма batch, затова трябва да пази текуща средна стойност на статистиките, събрани при обучение — което означава, че слоят се държи различно в training и evaluation режим, а забравянето да смените режимите е една от най-честите грешки в практиката. Освен това се влошава при малки batch-ове и е неудобен при последователности с променлива дължина, защото „средната стойност по batch-а на позиция 40“ се изчислява от толкова последователности, колкото случайно са толкова дълги.
LayerNorm нормализира всеки пример самостоятелно. Няма batch зависимост, няма текущи статистики, поведението е идентично при обучение и inference, безразличен е към размера на batch-а, безразличен е към дължината на последователността. Всяко от тези свойства е изискване, а не удобство, щом генерирате по един token наведнъж за един потребител — а дотам стига Глава 13.
Затова LayerNorm е този, който ще срещнете отново в Глава 9 непроменен: transformer блокът го използва и го използва по причините в дясната колона, не защото е по-добър абстрактно.
Да поправяте по едно нещо наведнъж — истинският skill
Връзка към раздела: Да поправяте по едно нещо наведнъж — истинският skillЧетири кандидат-поправки за мъртвата мрежа: Xavier initialisation, LayerNorm, residual connections и Adam вместо SGD. Изкушението е да приложите и четирите и да продължите. Направите ли го, никога няма да разберете коя е имала значение, а следващия път, когато се случи, няма да имате метод — само ритуал.
Затова ги прилагайте една по една. Същият seed, същите данни, същата архитектура, 800 стъпки:
| какво е добавено | крайна загуба | точност |
|---|---|---|
| нищо | 0.6931 | 50.0 % |
| Xavier initialisation | 0.5692 | 60.4 % |
| LayerNorm | 0.6230 | 61.5 % |
| residual connections | 0.6651 | 56.6 % |
| Adam | 0.6787 | 58.7 % |
| и четирите | 0.0000 | 100.0 % |
Прочетете тази таблица така, както бихте я чели в 2 сутринта, и изводът е: нищо не работи само, всичко работи заедно, следователно deep learning е алхимия. Този извод е грешен и да разберете защо е най-полезното нещо в тази глава.
Дайте на всеки run шест пъти повече бюджет — 5000 стъпки вместо 800 — и картината се променя напълно:
| какво е добавено | крайна загуба @ 5000 | точност |
|---|---|---|
| нищо | 0.6931 | 50.0 % |
| Xavier initialisation | 0.0007 | 100.0 % |
| LayerNorm | 0.0002 | 100.0 % |
| residual connections | 0.6653 | 56.7 % |
| Adam | 0.6908 | 53.4 % |
| Xavier + Adam | 0.0000 | 100.0 % |
| Xavier + LayerNorm | 0.0001 | 100.0 % |
Сега картината е остра и това е диагноза, не ритуал.
Инициализацията сама го поправя. Нормализацията сама го поправя. Всяка от тях адресира истинската болест — forward сигналът се срива до нула — и всяка е достатъчна. При 800 стъпки просто изглеждаха като частичен успех, защото вече бяха решили проблема и все още изкачваха изхода от него.
Residual connections и Adam не го поправят при никакъв бюджет. Не защото са лоши, а защото лекуват друга болест. Residual connection дава на градиента път около блокиращ слой; това струва много, когато проблемът е градиентът, и не струва нищо, когато forward сигналът вече е нула, защото shortcut около мъртъв слой пак носи мъртва стойност. Adam преоразмерява стъпката на всеки параметър според собствената му история на градиента; това помага, когато градиентите имат силно различни величини, и не може да възкреси мрежа, чийто изход не зависи от входа.
А „нищо“ все още е точно 0.6931 след пет хиляди стъпки. Не 0.6929. Не е бавно; мъртво е, и тази разлика вече се вижда по начин, по който преди не се виждаше, защото имате ред, който казва, че дадена поправка работи, за сравнение.
Да заслужим PyTorch
Връзка към раздела: Да заслужим PyTorchОттук нататък този курс използва PyTorch. Това трябва да бъде заслужено, не просто обявено, затова ето точно какво прави той, което вече знаете как да правите.
Оптимизаторът е правило за превръщане на градиенти в обновления на параметрите. Обикновеният gradient descent използва градиента. Momentum използва текуща средна стойност от него, която изглажда шума и набира скорост по направления, които остават последователни:
v = beta * v + p.grad
p -= lr * v Adam5 пази две текущи средни стойности — на градиента и на градиента на квадрат — и дели едната на квадратния корен от другата, така че всеки параметър получава стъпка, мащабирана спрямо собствената му скорошна величина на градиента:
m = b1 * m + (1 - b1) * g # mean of the gradient
v = b2 * v + (1 - b2) * g * g # mean of the squared gradient
m_hat = m / (1 - b1 ** t) # bias correction: both averages start at zero
v_hat = v / (1 - b2 ** t)
p -= lr * m_hat / (v_hat.sqrt() + eps) Десет реда. Пуснете и двата срещу torch.optim върху същия проблем за 50 стъпки:
SGD+momentum by hand [2.7781870365142822, -1.0304985046386719]
torch [2.7781870365142822, -1.0304983854293823] max |diff| = 1.19e-07
Adam by hand [0.4893140196800232, -0.46317872405052185]
torch [0.48931416869163513, -0.46317875385284424] max |diff| = 1.49e-07Идентични до точността на float32. torch.optim.Adam са тези пет реда, плюс десетилетия грижа за гранични случаи и C++ kernel. Това е размяната, която правите оттук нататък: не магия срещу разбиране, а скорост срещу редове, които вече сте написали.
Защо съществува Adam: кривина
Връзка към раздела: Защо съществува Adam: кривинаОбичайното обяснение на Adam е „adaptive per-parameter learning rates“, което е описание, а не причина. Причината е геометрия и тя може да се измери.
Вземете загуба, чиято кривина се различава между направленията: стръмна в едно, плитка в друго. SGD има един глобален learning rate, затова трябва да избере стойност, достатъчно малка, за да е стабилна в най-стръмното направление — и тази стойност после е прекалено малка за плиткото, където напредъкът пълзи. Това причинява класическата картина на gradient descent, който зигзагообразно се спуска по тясна долина.
Две съотношения на кривината, три оптимизатора, 300 стъпки, и на всеки оптимизатор е даден най-добрият learning rate от sweep, така че никой да не е ощетен:
| съотношение на кривината | SGD | SGD + momentum | Adam |
|---|---|---|---|
| 10 : 1 | грешка 0.000002 | грешка 0.000000 | грешка 0.000000 |
| 1000 : 1 | грешка 1.925485 | грешка 0.001432 | грешка 0.000000 |
| diverged при (1000:1) | 4 от 8 rates | 4 от 8 rates | 0 от 6 rates |
При съотношение десет всичко работи и няма какво да се обсъжда. При хиляда обикновеният SGD не може да стигне до отговора при нито един пробван learning rate — най-добрият му резултат все още е грешка 1.93 — и направо diverge-ва при половината rates. Adam каца точно върху целта и не diverge-ва при нито един.
Последната колона е практическата причина Adam да е default. Не че Adam намира по-добри решения; при добре обусловени проблеми настроеният SGD често го достига или бие. А че Adam е много по-малко чувствителен към избрания learning rate, а реалните мрежи имат съотношения на кривината много по-лоши от хиляда сред милионите си параметри.
Тук принадлежат още две части и и двете са по един ред. Gradient clipping преоразмерява вектора на градиента, когато norm-ът му надхвърли праг, което превръща реда „загубата внезапно скача до огромна стойност“ от диагностичната таблица в несъбитие. И learning rate schedules: кратък warmup от почти нула през първите няколкостотин стъпки, защото оценките на дисперсията в Adam са боклук, докато не са видели някакви градиенти, а стъпка с пълен размер върху боклук може да съсипе инициализация; после cosine decay към нула, защото да завършите run със същия размер на стъпката, с който сте започнали, означава да трептите около минимума, вместо да се установите в него.
Втората половина: моделът, който пасва идеално и не предсказва нищо
Връзка към раздела: Втората половина: моделът, който пасва идеално и не предсказва нищоВсичко дотук беше за това да свалим загубата. Сега по-трудната половина, защото падането на загубата не е целта — то е proxy за целта, а proxy-то се проваля по конкретен и известен начин.
Дванадесет точки от гладка функция с малко шум. Напаснете полиноми с нарастваща степен:
| степен | train RMSE | test RMSE |
|---|---|---|
| 1 | 0.764499 | 0.6985 |
| 3 | 0.252605 | 0.3031 |
| 5 | 0.164437 | 0.1568 |
| 9 | 0.088960 | 0.2347 |
| 11 | 0.000000 | 1.2094 |
Степен 11 през 12 точки минава през всяка една от тях точно — train грешка нула до шест знака след десетичната запетая — и е осем пъти по-лоша от степен 5 върху данни, които не е виждала. Попитайте степен 3 и степен 11 да предскажат при , малко извън training диапазона:
degree 3: predicts -1.053 (truth -0.012)
degree 11: predicts +61.224 (truth -0.012)Шестдесет и едно, когато отговорът е приблизително нула. Моделът не е научил функцията; научил е дванадесетте точки, а между тях прави каквото аритметиката изисква.
Това е overfitting, а противоположното — степен 1, която изобщо не може да представи кривата и е лоша навсякъде — е underfitting. Класическото обяснение разделя очакваната грешка на модела на три части: bias, грешката от това, че моделът е твърде ригиден, за да представи истината; variance, грешката от това, че моделът е толкова гъвкав, че преследва шума в точно тази извадка; и неизбежен шум, който нищо не поправя. Простите модели са biased, гъвкавите модели са high-variance, а класическата рецепта е да намерите златната среда — степен 5 в таблицата по-горе.
Стандартните инструменти атакуват variance термина:
- L2 регуляризация (weight decay) добавя към загубата, придърпвайки теглата към нула и правейки функцията по-гладка. В таблицата по-горе най-големият коефициент на степен 11 нанася щетата; наказването на размера го обезврежда.
- L1 добавя вместо това. Разликата не е козметична: градиентът на L2 е пропорционален на теглото и затова се свива заедно с него, приближавайки нула, без да пристига, докато градиентът на L1 е константа , която продължава да бута докрай. Затова L1 произвежда тегла, които са точно нула — тя избира характеристики. L2 произвежда малки тегла. Използвайте L2, когато искате гладкост, L1 — когато искате sparse решение.
- Dropout7 занулява случаен поднабор от активации при всяка training стъпка, така че никоя единица да не може да разчита, че конкретна друга единица присъства.
- Early stopping следи validation загубата и спира, когато тя тръгне нагоре.
- Data augmentation произвежда още training примери от тези, които имате, което атакува проблема в източника му: overfitting е недостиг на данни толкова, колкото и излишък на параметри.
- Cross-validation разделя данните по начина и обучава пъти, което купува надеждна оценка на test грешката, когато имате твърде малко данни, за да отделите held-out set.
Double descent, или защо предишният раздел не е цялата история
Връзка към раздела: Double descent, или защо предишният раздел не е цялата историяСега фактът, който чупи картината.
Историята за bias-variance казва, че след златната среда повече параметри означават по-лошо обобщаване. Съвременните езикови модели имат много повече параметри, отколкото класическите правила позволяват за данните, които виждат, и обобщават превъзходно. И двете твърдения са верни, а съгласуването им е най-полезното нещо в тази глава.
Четиридесет training точки, двадесетизмерни входове, случайни ReLU характеристики и брой характеристики , sweep-нат от 2 до 5000 — с избрано minimum-norm решение винаги когато има много, които пасват:
| train RMSE | test RMSE | |||
|---|---|---|---|---|
| 10 | 0.25 | 0.8822 | 1.2520 | 1.89 |
| 20 | 0.50 | 0.5962 | 1.1634 | 2.59 |
| 30 | 0.75 | 0.3896 | 1.5323 | 4.15 |
| 38 | 0.95 | 0.1769 | 3.7163 | 10.25 |
| 40 | 1.00 | 0.0000 | 5.8140 | 14.83 |
| 42 | 1.05 | 0.0000 | 3.1623 | 9.35 |
| 60 | 1.50 | 0.0000 | 1.1058 | 2.78 |
| 200 | 5.00 | 0.0000 | 0.6638 | 0.98 |
| 1500 | 37.50 | 0.0000 | 0.5859 | 0.33 |
| 5000 | 125.00 | 0.0000 | 0.5664 | 0.18 |
Прочетете я в три части. До класическата история важи точно: грешката пада, после започва да расте. При — interpolation threshold, където моделът има точно достатъчно параметри, за да мине през всяка training точка — test грешката достига пик, 5.81, пет пъти по-лошо от малкия модел. Този пик е класическото предупреждение и е реален.
После тя пада отново. И продължава да пада, след , след , чак до , където test грешката 0.5664 е по-добра от най-доброто, което някога е постигал under-parameterised моделът. Модел с 5000 параметъра, напаснат към 40 точки, е най-добрият модел в таблицата.
Това е double descent,89 и механизмът се вижда в последната колона. Щом има безкрайно много настройки на параметрите, които пасват точно на training данните, а коя ще получите зависи от това как избирате. Minimum-norm решението избира най-малката, а показва какво означава това: достига пик 14.83 точно на threshold-а — където има точно едно интерполиращо решение и сте stuck с него, колкото и extreme да е — и после пада монотонно, докато расте, защото повече параметри означава повече интерполиращи решения, от които да избирате, което означава, че най-малкото налично става по-малко. При norm-ът е 0.18, осемдесет пъти по-малък от този при threshold-а.
Значи допълнителните параметри не добавят сложност. Те добавят избор, а правилото за избор харчи този избор за простота. Регуляризацията не е във функцията на загубата; тя е в алгоритъма. Gradient descent от малка инициализация има документиран bias към small-norm решения, затова това поведение се появява в реални мрежи, обучени по обичайния начин, а не само в линейната алгебра по-горе.
Практическата последица, от която зависи Глава 10: „моделът има повече параметри, отколкото данни, значи ще overfit-не“ не е валиден аргумент. Това беше добро правило, когато моделите живееха вляво от threshold-а. Всичко интересно сега живее далеч вдясно от него, където правилото се обръща.
Накъде продължаваме
Връзка към раздела: Накъде продължавамеИнструментите в тази глава са достатъчни, за да обучите мрежа, която работи върху данни, които можете да сложите в таблица: редове от числа, колона с етикети.
Езикът не е това. Преди моделът да може да предскаже следващата дума, нещо трябва да реши какво изобщо е „дума“ — а отговорът не е нито букви, нито думи, а речник, който моделът научава от суровите байтове на training данните. Това решение, взето веднъж преди началото на обучението, определя колко неща може да каже моделът, колко струва една заявка и защо модели, които могат да издържат изпит по право, не могат надеждно да преброят буквите в strawberry.
Глава 7 изгражда tokenizer.
Източници и метод
Връзка към раздела: Източници и методЗа residual connections, използвани по-горе, He et al., Deep Residual Learning for Image Recognition (arXiv:1512.03385). Building makemore Part 3: Activations & Gradients, BatchNorm на Andrej Karpathy минава през диагностиката с хистограми на активациите върху реален модел и е най-доброто практическо разглеждане на първата половина от тази глава. Лекциите 8 и 11–13 от Learning From Data на Yaser Abu-Mostafa дават правилно класическата теория на обобщаването, включително частите, които тази глава сви до един абзац.
Препратки
Връзка към раздела: Препратки-
Glorot, X. and Bengio, Y. Understanding the difficulty of training deep feedforward neural networks. AISTATS (2010). Аргументът за запазване на дисперсията е възпроизведен в карето по-горе. ↩
-
He, K., Zhang, X., Ren, S. and Sun, J. Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification. arXiv:1502.01852 (2015). ↩
-
Ioffe, S. and Szegedy, C. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift. arXiv:1502.03167 (2015). Имайте предвид, че обяснението „internal covariate shift“ в заглавието оттогава е сериозно оспорено; слоят работи, но първоначалното обяснение защо е спорно. ↩
-
Ba, J. L., Kiros, J. R. and Hinton, G. E. Layer Normalization. arXiv:1607.06450 (2016). ↩
-
Kingma, D. P. and Ba, J. Adam: A Method for Stochastic Optimization. arXiv:1412.6980 (2014). ↩
-
Loshchilov, I. and Hutter, F. Decoupled Weight Decay Regularization. arXiv:1711.05101 (2017). ↩
-
Srivastava, N., Hinton, G., Krizhevsky, A., Sutskever, I. and Salakhutdinov, R. Dropout: A Simple Way to Prevent Neural Networks from Overfitting. JMLR 15, pp. 1929–1958 (2014). ↩
-
Belkin, M., Hsu, D., Ma, S. and Mandal, S. Reconciling modern machine-learning practice and the classical bias–variance trade-off. PNAS 116(32), pp. 15849–15854 (2019). Статията, която дава име на явлението. ↩
-
Nakkiran, P., Kaplun, G., Bansal, Y., Yang, T., Barak, B. and Sutskever, I. Deep Double Descent: Where Bigger Models and More Data Hurt. arXiv:1912.02292 (2019). Показва ефекта в реални дълбоки мрежи и по оста време за обучение, както и по оста размер на модела. ↩