Към съдържанието
6/30Глава 6 от 30

Да го накараме да се обучава — и да обобщава

Шестслойна мрежа, чиято загуба стои на ln 2, поправена измерване по измерване. После double descent: 5 000 параметъра върху 40 точки.

На тази страница

Мрежата от Глава 5 работи. Има девет параметъра, научава XOR и градиентите ѝ съвпадат с PyTorch до шестнадесетия знак след десетичната запетая.

Направете я шест слоя дълбока и тя спира да учи напълно. Не бавно — напълно. Ето шестслойна мрежа върху класификационен проблем с две спирали, обучавана 5000 стъпки:

TEXT
step    1: loss 0.693147
step 5000: loss 0.693147
accuracy: 50.0 %

Това число не е произволно. ln2=0.693147\ln 2 = 0.693147 е бинарната кръстосана ентропия на модел, който връща вероятност 0.50.5 за всичко, а 50 % е хвърляне на монета върху балансиран набор от данни. След пет хиляди стъпки мрежата не е помръднала нито с една цифра. Нищо не се е сринало, нищо не е предупредило, а градиентите все още са точно правилни.

Тази глава е за пропастта между мрежа, която се изпълнява, и мрежа, която работи. Тя има две половини, които изглеждат като различни теми, но са една и съща задача: да накарате загубата да тръгне надолу и да я накарате да тръгне надолу върху данни, които моделът никога не е виждал.

Започнете с гледане, вместо с гадаене. Прекарайте batch от входове през мрежата и отпечатайте стандартното отклонение на активациите във всеки слой, а след това стандартното отклонение на градиентите на теглата:

profile.pyPYTHON
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}")

Три инициализации, една и съща архитектура, шест слоя от tanh\tanh:

инициализацияstd на активациите, слоеве 1→6
normal, std 0.010.010.0145 · 0.0016 · 0.0002 · 0.0000 · 0.0000 · 0.0000
normal, std 110.6573 · 0.9296 · 0.9585 · 0.9634 · 0.9637 · 0.9625
Xavier0.1579 · 0.1493 · 0.1353 · 0.1333 · 0.1325 · 0.1403
инициализацияstd на градиента, първи слой → последен
normal, std 0.010.013.20e-06 · 4.97e-07 · … · 6.40e-06
normal, std 111.94e+03 · 2.28e+02 · 1.22e+02 · 4.43e+01 · 1.85e+01 · 7.30e+00
Xavier2.31e+00 · 4.50e-01 · 4.26e-01 · 3.89e-01 · 4.39e-01 · 4.73e-01

Първият ред е мрежата по-горе и тя не учи бавно — в нея не е останал сигнал. До четвъртия слой стандартното отклонение на активациите е паднало до нула при четири знака след десетичната запетая. Всеки вход произвежда един и същ изход, изходът е константа, а градиентът на константа е нищо. Теглата са били инициализирани малки „за всеки случай“, а малкото се е оказало фатално.

Вторият ред е противоположният отказ и си струва да бъде разбран, защото е контраинтуитивен. Активациите изглеждат здрави — около 0.96 — но това е tanh\tanh наситена, закована близо до границата си, точно режимът, който Глава 5 измери като губещ почти десет хиляди пъти от градиента. И все пак градиентите са огромни: 1940 в първия слой. И двете неща са верни едновременно. Всяка обратна стъпка умножава по WW^\top, а при 128 входа с единична дисперсия този фактор има усилване около 12811\sqrt{128} \approx 11, което надделява над свиването от наситената tanh\tanh. Градиентите растат геометрично на връщане назад. Това е exploding gradient и в реално обучение води до стойности на загубата nan в рамките на няколко стъпки.

Третият ред е това, което искате: активации с приблизително постоянен мащаб през дълбочината, градиенти с приблизително постоянен мащаб през дълбочината. Нищо не умира, нищо не експлодира.

Добрата инициализация оправя мащаба в стъпка нула. Тя не го държи фиксиран: теглата се движат, а до стъпка пет хиляди внимателният аргумент с дисперсията вече не важи.

Нормализационните слоеве налагат мащаба непрекъснато. При даден вектор от активации изваждат средна стойност, делят на стандартно отклонение, после прилагат научен мащаб γ\gamma и отместване β\beta, така че слоят да може да отмени нормализацията, ако се окаже, че това иска:

h^=hμσ2+ϵ,y=γh^+β\hat{h} = \frac{h - \mu}{\sqrt{\sigma^2 + \epsilon}}, \qquad y = \gamma\hat{h} + \beta

Единственият истински въпрос е върху какво осреднявате. Batch normalisation3 взема μ\mu и σ\sigma по 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.693150.0 %
Xavier initialisation0.569260.4 %
LayerNorm0.623061.5 %
residual connections0.665156.6 %
Adam0.678758.7 %
и четирите0.0000100.0 %

Прочетете тази таблица така, както бихте я чели в 2 сутринта, и изводът е: нищо не работи само, всичко работи заедно, следователно deep learning е алхимия. Този извод е грешен и да разберете защо е най-полезното нещо в тази глава.

Дайте на всеки run шест пъти повече бюджет — 5000 стъпки вместо 800 — и картината се променя напълно:

какво е добавенокрайна загуба @ 5000точност
нищо0.693150.0 %
Xavier initialisation0.0007100.0 %
LayerNorm0.0002100.0 %
residual connections0.665356.7 %
Adam0.690853.4 %
Xavier + Adam0.0000100.0 %
Xavier + LayerNorm0.0001100.0 %

Сега картината е остра и това е диагноза, не ритуал.

Инициализацията сама го поправя. Нормализацията сама го поправя. Всяка от тях адресира истинската болест — forward сигналът се срива до нула — и всяка е достатъчна. При 800 стъпки просто изглеждаха като частичен успех, защото вече бяха решили проблема и все още изкачваха изхода от него.

Residual connections и Adam не го поправят при никакъв бюджет. Не защото са лоши, а защото лекуват друга болест. Residual connection дава на градиента път около блокиращ слой; това струва много, когато проблемът е градиентът, и не струва нищо, когато forward сигналът вече е нула, защото shortcut около мъртъв слой пак носи мъртва стойност. Adam преоразмерява стъпката на всеки параметър според собствената му история на градиента; това помага, когато градиентите имат силно различни величини, и не може да възкреси мрежа, чийто изход не зависи от входа.

А „нищо“ все още е точно 0.6931 след пет хиляди стъпки. Не 0.6929. Не е бавно; мъртво е, и тази разлика вече се вижда по начин, по който преди не се виждаше, защото имате ред, който казва, че дадена поправка работи, за сравнение.

Оттук нататък този курс използва PyTorch. Това трябва да бъде заслужено, не просто обявено, затова ето точно какво прави той, което вече знаете как да правите.

Оптимизаторът е правило за превръщане на градиенти в обновления на параметрите. Обикновеният gradient descent използва градиента. Momentum използва текуща средна стойност от него, която изглажда шума и набира скорост по направления, които остават последователни:

optim_by_hand.pyPYTHON
v = beta * v + p.grad          
p -= lr * v                    

Adam5 пази две текущи средни стойности — на градиента и на градиента на квадрат — и дели едната на квадратния корен от другата, така че всеки параметър получава стъпка, мащабирана спрямо собствената му скорошна величина на градиента:

optim_by_hand.pyPYTHON
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 стъпки:

TEXT
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 е „adaptive per-parameter learning rates“, което е описание, а не причина. Причината е геометрия и тя може да се измери.

Вземете загуба, чиято кривина се различава между направленията: стръмна в едно, плитка в друго. SGD има един глобален learning rate, затова трябва да избере стойност, достатъчно малка, за да е стабилна в най-стръмното направление — и тази стойност после е прекалено малка за плиткото, където напредъкът пълзи. Това причинява класическата картина на gradient descent, който зигзагообразно се спуска по тясна долина.

Две съотношения на кривината, три оптимизатора, 300 стъпки, и на всеки оптимизатор е даден най-добрият learning rate от sweep, така че никой да не е ощетен:

съотношение на кривинатаSGDSGD + momentumAdam
10 : 1грешка 0.000002грешка 0.000000грешка 0.000000
1000 : 1грешка 1.925485грешка 0.001432грешка 0.000000
diverged при (1000:1)4 от 8 rates4 от 8 rates0 от 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 RMSEtest RMSE
10.7644990.6985
30.2526050.3031
50.1644370.1568
90.0889600.2347
110.0000001.2094

Степен 11 през 12 точки минава през всяка една от тях точно — train грешка нула до шест знака след десетичната запетая — и е осем пъти по-лоша от степен 5 върху данни, които не е виждала. Попитайте степен 3 и степен 11 да предскажат при x=3.25x = 3.25, малко извън training диапазона:

TEXT
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) добавя λw2\lambda \lVert w \rVert^2 към загубата, придърпвайки теглата към нула и правейки функцията по-гладка. В таблицата по-горе най-големият коефициент на степен 11 нанася щетата; наказването на размера го обезврежда.
  • L1 добавя λwi\lambda \sum |w_i| вместо това. Разликата не е козметична: градиентът на L2 е пропорционален на теглото и затова се свива заедно с него, приближавайки нула, без да пристига, докато градиентът на L1 е константа ±λ\pm\lambda, която продължава да бута докрай. Затова L1 произвежда тегла, които са точно нула — тя избира характеристики. L2 произвежда малки тегла. Използвайте L2, когато искате гладкост, L1 — когато искате sparse решение.
  • Dropout7 занулява случаен поднабор от активации при всяка training стъпка, така че никоя единица да не може да разчита, че конкретна друга единица присъства.
  • Early stopping следи validation загубата и спира, когато тя тръгне нагоре.
  • Data augmentation произвежда още training примери от тези, които имате, което атакува проблема в източника му: overfitting е недостиг на данни толкова, колкото и излишък на параметри.
  • Cross-validation разделя данните по kk начина и обучава kk пъти, което купува надеждна оценка на test грешката, когато имате твърде малко данни, за да отделите held-out set.

Double descent, или защо предишният раздел не е цялата история

Връзка към раздела: Double descent, или защо предишният раздел не е цялата история

Сега фактът, който чупи картината.

Историята за bias-variance казва, че след златната среда повече параметри означават по-лошо обобщаване. Съвременните езикови модели имат много повече параметри, отколкото класическите правила позволяват за данните, които виждат, и обобщават превъзходно. И двете твърдения са верни, а съгласуването им е най-полезното нещо в тази глава.

Четиридесет training точки, двадесетизмерни входове, случайни ReLU характеристики и брой характеристики PP, sweep-нат от 2 до 5000 — с избрано minimum-norm решение винаги когато има много, които пасват:

PPP/nP/ntrain RMSEtest RMSEw\lVert w \rVert
100.250.88221.25201.89
200.500.59621.16342.59
300.750.38961.53234.15
380.950.17693.716310.25
401.000.00005.814014.83
421.050.00003.16239.35
601.500.00001.10582.78
2005.000.00000.66380.98
150037.500.00000.58590.33
5000125.000.00000.56640.18

Прочетете я в три части. До P/n=0.5P/n = 0.5 класическата история важи точно: грешката пада, после започва да расте. При P=n=40P = n = 40interpolation threshold, където моделът има точно достатъчно параметри, за да мине през всяка training точка — test грешката достига пик, 5.81, пет пъти по-лошо от малкия модел. Този пик е класическото предупреждение и е реален.

После тя пада отново. И продължава да пада, след P=5nP = 5n, след P=37nP = 37n, чак до P=125nP = 125n, където test грешката 0.5664 е по-добра от най-доброто, което някога е постигал under-parameterised моделът. Модел с 5000 параметъра, напаснат към 40 точки, е най-добрият модел в таблицата.

Това е double descent,89 и механизмът се вижда в последната колона. Щом P>nP > n има безкрайно много настройки на параметрите, които пасват точно на training данните, а коя ще получите зависи от това как избирате. Minimum-norm решението избира най-малката, а w\lVert w \rVert показва какво означава това: достига пик 14.83 точно на threshold-а — където има точно едно интерполиращо решение и сте stuck с него, колкото и extreme да е — и после пада монотонно, докато PP расте, защото повече параметри означава повече интерполиращи решения, от които да избирате, което означава, че най-малкото налично става по-малко. При P=5000P = 5000 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 дават правилно класическата теория на обобщаването, включително частите, които тази глава сви до един абзац.

  1. Glorot, X. and Bengio, Y. Understanding the difficulty of training deep feedforward neural networks. AISTATS (2010). Аргументът за запазване на дисперсията е възпроизведен в карето по-горе.

  2. He, K., Zhang, X., Ren, S. and Sun, J. Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification. arXiv:1502.01852 (2015).

  3. Ioffe, S. and Szegedy, C. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift. arXiv:1502.03167 (2015). Имайте предвид, че обяснението „internal covariate shift“ в заглавието оттогава е сериозно оспорено; слоят работи, но първоначалното обяснение защо е спорно.

  4. Ba, J. L., Kiros, J. R. and Hinton, G. E. Layer Normalization. arXiv:1607.06450 (2016).

  5. Kingma, D. P. and Ba, J. Adam: A Method for Stochastic Optimization. arXiv:1412.6980 (2014).

  6. Loshchilov, I. and Hutter, F. Decoupled Weight Decay Regularization. arXiv:1711.05101 (2017).

  7. 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).

  8. 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). Статията, която дава име на явлението.

  9. 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). Показва ефекта в реални дълбоки мрежи и по оста време за обучение, както и по оста размер на модела.


Създадено от

David Vicente Campos

Основател на NeuraLIA Labs и съосновател на MyRealFood

Компютърен инженер съм, завършил Университета в Леон. Съосновах MyRealFood, където като CTO създадох приложението, което милиони хора са използвали, за да се хранят по-здравословно, и основах NeuraLIA Labs, където изграждам AI продукти. Тук пиша за това, което трябваше да разбера по пътя, така, както ми се иска някой да ми го беше обяснил.

Още за автора

Публикувано от NeuraLIA Labs.

Получавайте нови публикации във входящата си поща

Новини за AI, ръководства и продуктови обновления — кратък имейл, когато публикуваме нещо, което си заслужава.

Индекс на курса

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev12 мин четене

AI моделът Jev е създаден за решения, не за проза

Jev на TypeSafe AI привлича внимание, защото разглежда софтуерната интелигентност като проблем на вероятностите: изберете правилния клон, добавете увереност и не плащайте на LLM да пише текст, когато кодът има нужда от решение.

Abstract legal research workspace with documents, search nodes and governance controls.
openai11 мин четене

Astra for Law на OpenAI е правна AI система, не нов модел

Правният старт на OpenAI е не толкова за нов базов модел, колкото за системата около него: домейн извличане, надеждни инструменти, права, бенчмаркове и пътища за преглед.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering12 мин четене

Инженеринг на контекста за AI агенти с дълъг хоризонт

Дълго работещите агенти не се провалят само защото прозорецът е малък. Те се провалят, когато файлове, изходи от инструменти и остаряла история изтласкат задачата, която агентът е трябвало да завърши.

Готови ли сте LIA да избира вместо вас?

Създавайте с всички AI модели на едно място — започнете безплатно още днес.