Niet-lineair optimaliseren: de bergwandeling waarmee neurale netwerken leren

· 15 min lezen

In Hoe werken LLM’s? stond één zin over hoe een taalmodel leert: na elke reeks voorspellingen worden alle gewichten een klein beetje bijgesteld, in de richting die de fouten verkleint. Achter die zin zit een heel vakgebied. Vrijwel elk neuraal netwerk leert op deze manier, van een beeldherkenner tot de taalmodellen waarop juridische AI draait.

Dit stuk legt uit hoe dat bijstellen werkt. Het beeld dat we gebruiken, is een bergwandeling: een wandelaar die in dichte mist het dal probeert te bereiken. We bespreken vijf manieren van lopen, van de eenvoudigste tot een methode van eind 2024. Waar ze in verschillen, laten we zien met één rekenvoorbeeld: een klein Python-script, bergwandeling.py, dat de methoden echt over een dal laat lopen. Bij elke methode staat het stukje code dat haar uitvoert, en alle figuren en getallen over de paden komen uit dat script. U kunt het onderaan downloaden en zelf draaien. Waar het stuk iets beweert over hoe deze methoden werken, verwijst het naar het onderzoek waar dat vandaan komt.

Leren is het laagste punt zoeken

Een netwerk heeft parameters: getallen die bepalen wat het met zijn invoer doet. GPT-3 heeft er 175 miljard[1]. Voor elke instelling van die getallen kun je meten hoe slecht het netwerk het doet op de voorbeelden waarvan het moet leren. Die maat heet het verlies: hoe hoger, hoe meer fouten. Leren betekent een instelling zoeken waarbij het verlies laag is.

Stel u elke instelling van de parameters voor als een plek in een landschap, en het verlies als de hoogte op die plek. Dan is leren het zoeken naar een dal. Met twee parameters is dat een gewoon berglandschap, met noord-zuid en oost-west. Met 175 miljard parameters heeft het landschap 175 miljard richtingen. Dat kan niemand zich voorstellen, maar het idee blijft hetzelfde.

Waarom niet gewoon uitrekenen?

Bij sommige problemen hoef je niet te zoeken. Is alles lineair, dan wordt het beste punt, als het bestaat, altijd ook op een hoekpunt van het toegestane gebied bereikt[2]. Een vaste rekenprocedure, de simplexmethode, loopt die hoekpunten systematisch af[3].

Een neuraal netwerk is niet lineair. Tussen de lagen zitten functies die het signaal buigen, en dat is ook de bedoeling: zonder die bochten zou het hele netwerk samenvallen tot één lineaire bewerking, en zou het niets ingewikkelds kunnen leren. Het gevolg is een landschap vol bulten, ruggen en dalen, waarvoor geen formule bestaat die het laagste punt aanwijst[4]. Er zit niets anders op dan lopen.

Afdalen in de mist

Stel u een wandelaar voor op een berg in dichte mist. Het dal ziet ze niet, het pad ook niet. Wat ze wel kan, is voelen in welke richting de grond onder haar voeten het steilst daalt. Dat is precies wat een netwerk kan uitrekenen: de helling van het verlies voor alle parameters tegelijk, de gradiënt, met een methode die backpropagation heet[5]. Hoe die werkt, staat in Deep learning uitgelegd.

De eenvoudigste strategie: zet een stap in de steilst dalende richting, voel opnieuw, en herhaal. Dat heet gradiëntdaling, en het idee is oud. De Franse wiskundige Augustin-Louis Cauchy beschreef het in 1847, als algemene methode om stelsels van vergelijkingen op te lossen[6]. In woorden is het één regel: nieuwe plek = oude plek − staplengte × helling. In het script is dat de wandeling zelf:

uit bergwandeling.py
def wandel(methode, eta, hoek):
    """Zet STAPPEN stappen bergaf; geeft het hele pad terug, met het startpunt."""
    stap = methode(eta)
    ruis = [draai(r, hoek) for r in ruisreeks()]  # de ruis draait mee met het dal
    p = draai(START, hoek)
    pad = [p]
    for t in range(1, STAPPEN + 1):
        g = helling(p, hoek)
        g = (g[0] + ruis[t - 1][0], g[1] + ruis[t - 1][1])  # de helling, geschat
        dx, dy = stap(g, t)
        p = (p[0] - dx, p[1] - dy)  # nieuwe plek = oude plek - stap
        pad.append(p)
    return pad

Die staplengte, in vaktaal de learning rate, is de belangrijkste knop. Met te kleine stappen duurt de wandeling eindeloos. Met te grote stappen stapt de wandelaar over het dal heen, de helling aan de overkant op, en komt ze bij elke stap hoger uit.

SGD: de helling schatten met een handvol voorbeelden

Om de helling precies te kennen, moet het netwerk naar al zijn trainingsvoorbeelden kijken. Bij een taalmodel zijn dat honderden miljarden stukjes tekst[1], en voor één stap is dat veel te duur. Daarom schat het netwerk de helling op een kleine, willekeurige greep voorbeelden, een minibatch, en zet het op basis daarvan een stap. Dat is stochastische gradiëntdaling: SGD. Het idee gaat terug op werk van Robbins en Monro uit 1951 over het vinden van een oplossing met alleen ruizige metingen[7]. In het script is de ruis de afwijking die bij elke stap bij de helling wordt opgeteld; SGD zelf is daarna één regel:

uit bergwandeling.py
def sgd(eta):
    def stap(g, t):
        return tuple(eta * gi for gi in g)

    return stap

De wandelaar krijgt zo een onbetrouwbaar kompas: elke meting wijst ongeveer bergaf, niet precies. Dat blijkt minder erg dan het klinkt. Bij grote hoeveelheden gegevens brengen veel goedkope stappen in ongeveer de goede richting je sneller vooruit dan weinig dure, precieze stappen[8]. De ruis kan zelfs helpen: er zijn aanwijzingen dat training met kleine minibatches uitkomt in brede, vlakke dalen, waarin het netwerk beter presteert op nieuwe gegevens dan in smalle, scherpe kuilen[9].

Het smalle dal

Het lastigste terrein voor deze wandelaar is geen steile berg, maar een smal dal met steile wanden en een bodem die maar flauw afloopt. De steilste richting wijst dan vooral naar de overkant, nauwelijks langs de bodem naar beneden. De wandelaar stapt heen en weer tussen de wanden en schiet langs de bodem weinig op. Een grotere stap helpt niet: die smijt haar tegen de wand aan de overkant omhoog. Hoe snel ze over de bodem kan, wordt dus begrensd door de steilheid van de wanden[8].

Neurale netwerken zitten vol met zulke dalen. Daarom laten we de methoden allemaal op hetzelfde dal los. In het script is dat dal twee functies: de hoogte op een plek, en de helling daar.

uit bergwandeling.py
def hoogte(punt, hoek):
    """Het verlies op deze plek, in een dal dat `hoek` graden gedraaid ligt."""
    u, w = draai(punt, -hoek)
    return 0.5 * (u * u + STEILHEID * w * w)
uit bergwandeling.py
def helling(punt, hoek):
    """De gradient: de richting waarin het hier het steilst omhoog gaat."""
    u, w = draai(punt, -hoek)
    return draai((u, STEILHEID * w), hoek)

De wanden zijn 50 keer zo steil gekromd als de bodem. Elke wandelaar begint links, een eind tegen de wand op, en zet 60 stappen. Alle vier krijgen ze dezelfde ruis op de helling. En om de vergelijking eerlijk te houden, probeert het script voor elke methode dezelfde vaste reeks staplengtes en houdt het de staplengte die het laagst eindigt:

uit bergwandeling.py
def beste_wandeling(naam, hoek):
    """Probeert elke staplengte en houdt de wandeling die het laagst eindigt.

    Laagst eindigen = de gemiddelde hoogte over de laatste tien stappen.
    """
    beste = None
    for eta in STAPLENGTES:
        pad = wandel(METHODEN[naam], eta, hoek)
        hoogtes = [hoogte(p, hoek) for p in pad]
        eind = sum(hoogtes[-10:]) / 10
        if math.isfinite(eind) and (beste is None or eind < beste["eindhoogte"]):
            onder_een = next((i for i, h in enumerate(hoogtes) if h < 1), None)
            beste = {
                "methode": naam,
                "staplengte": eta,
                "eindhoogte": eind,
                "onder_een": onder_een,
                "pad": pad,
            }
    return beste

SGD

staplengte 0,032 · hoogte na 60 stappen 1,25

SGD, dal langs de as: na 60 stappen gemiddelde hoogte 1,25

Momentum

staplengte 0,0042 · hoogte na 60 stappen 0,07

Momentum, dal langs de as: na 60 stappen gemiddelde hoogte 0,07

AdaGrad

staplengte 1,8 · hoogte na 60 stappen 0,03

AdaGrad, dal langs de as: na 60 stappen gemiddelde hoogte 0,03

Adam

staplengte 1 · hoogte na 60 stappen 0,06

Adam, dal langs de as: na 60 stappen gemiddelde hoogte 0,06
Vier wandelaars in hetzelfde smalle dal, op een wandelkaart met hoogtelijnen. Elk zet 60 stappen vanaf het open rondje; het dichte rondje is waar ze eindigen, het kruisje het laagste punt. De hoogte is het gemiddelde over de laatste tien stappen. De paden komen uit bergwandeling.py, met ruis van standaardafwijking 1 per richting.

De SGD-wandelaar doet wat we voorspelden. Ze zigzagt tussen de wanden en staat na 60 stappen nog op de helling, op een gemiddelde hoogte van 1,25. Een grotere staplengte dan 0,032 kon ze niet nemen: daar zorgen de steile wanden voor.

Momentum: vaart houden

De eerste verbetering vervangt de wandelaar door een slee. Een slee die naar beneden glijdt, houdt vaart. Een duw opzij, de ene keer naar links en de volgende keer naar rechts, middelt uit; een duw die steeds dezelfde kant op wijst, stapelt zich op. Momentum doet dat met de stappen: het telt de nieuwe helling op bij een groot deel van de vorige stap. Boris Polyak beschreef deze methode, die bekendstaat als de ‘zware bal’, in 1964[10], en Yurii Nesterov gaf in 1983 een variant die eerst vooruitkijkt waar de vaart hem brengt[11]. Voor diepe netwerken bleek momentum geen detail, maar een voorwaarde om goed te kunnen trainen[12].

uit bergwandeling.py
def momentum(eta, beta=0.9):
    v = (0.0, 0.0)

    def stap(g, t):
        nonlocal v
        v = tuple(beta * vi + gi for vi, gi in zip(v, g))  # vaart houden
        return tuple(eta * vi for vi in v)

    return stap

In de figuur dooft de zigzag uit, en de slee staat na 60 stappen op een hoogte van 0,07, tegen 1,25 voor SGD. Vaart heeft wel een prijs: een slee die te hard gaat, schiet voorbij het dal. Daarom komt ze, uit dezelfde reeks, het best uit met een veel kleinere staplengte dan SGD: 0,0042 tegen 0,032.

AdaGrad: per richting een eigen pas

Een andere oplossing is niet harder lopen, maar per richting een andere pas nemen: kleine stapjes waar het terrein steil is, grote stappen waar het vlak is. AdaGrad, van Duchi, Hazan en Singer, doet dat voor elke parameter afzonderlijk. Het deelt de stap in een richting door de wortel uit de som van alle kwadraten van de hellingen die het in die richting tot nu toe tegenkwam[13]. Een richting die steeds steil was, krijgt zo kleine stappen, een richting waarin weinig gebeurde grote. Dat werkt vooral goed bij zeldzame kenmerken, zoals een woord dat maar af en toe voorkomt en juist een flinke stap verdient als het eens langskomt.

uit bergwandeling.py
def adagrad(eta, eps=1e-8):
    som = (0.0, 0.0)

    def stap(g, t):
        nonlocal som
        som = tuple(si + gi * gi for si, gi in zip(som, g))  # groeit alleen
        return tuple(eta * gi / (math.sqrt(si) + eps) for gi, si in zip(g, som))

    return stap

In de figuur loopt AdaGrad vrijwel recht naar het dal, en is het als eerste onder hoogte 1: na 15 stappen. Het nadeel zit in die som: die groeit alleen maar, zodat de stappen steeds kleiner worden, ook als het terrein daar niet om vraagt[4]. RMSProp, dat Geoffrey Hinton in een online college presenteerde en daar toeschreef aan Tijmen Tieleman, vervangt de som daarom door een lopend gemiddelde, zodat oude hellingen langzaam worden vergeten[14].

Adam: vaart én een eigen pas

Adam, van Kingma en Ba, combineert de twee ideeën. Het houdt vaart zoals momentum, en deelt per richting door de gebruikelijke grootte van de helling, zoals RMSProp. Een correctie zorgt dat de schattingen in de eerste stappen, als er nog weinig te middelen valt, niet te klein uitvallen[15]. Adam is het werkpaard van het vakgebied geworden. Ook GPT-3 werd met Adam getraind[1], en de variant AdamW, die het afremmen van grote gewichten los van de rest regelt[16], is de gangbare keuze voor het trainen van grote taalmodellen[17].

uit bergwandeling.py
def adam(eta, beta1=0.9, beta2=0.999, eps=1e-8):
    m = (0.0, 0.0)
    v = (0.0, 0.0)

    def stap(g, t):
        nonlocal m, v
        m = tuple(beta1 * mi + (1 - beta1) * gi for mi, gi in zip(m, g))
        v = tuple(beta2 * vi + (1 - beta2) * gi * gi for vi, gi in zip(v, g))
        # Biascorrectie: m en v beginnen op nul en zijn eerst te klein.
        m_hat = tuple(mi / (1 - beta1**t) for mi in m)
        v_hat = tuple(vi / (1 - beta2**t) for vi in v)
        return tuple(eta * mh / (math.sqrt(vh) + eps) for mh, vh in zip(m_hat, v_hat))

    return stap

Perfect is Adam niet. Reddi en collega’s gaven een eenvoudig voorbeeld waarin Adam niet bij het optimum uitkomt[18]. In de figuur zie je de vaart terug: Adam schiet voorbij het dal, voordat het op een hoogte van 0,06 tot rust komt.

Als het dal scheef ligt

AdaGrad en Adam hadden het in de eerste figuur makkelijk, en dat is niet helemaal eerlijk. Ze kiezen een pas per richting, maar ‘richting’ betekent voor hen: per parameter, langs de assen van de kaart. Het dal lag precies langs een as. Draai je hetzelfde dal een achtste slag, met de ruis meegedraaid, dan verandert er voor SGD en Momentum niets, want die volgen alleen de helling zelf: ze lopen precies hetzelfde pad, gedraaid. Voor AdaGrad wel: dat komt in 60 stappen niet meer onder hoogte 1. Ook Adam doet er langer over: het komt pas na 42 stappen onder hoogte 1, tegen 28 langs de as.

SGD

staplengte 0,032 · hoogte na 60 stappen 1,25

SGD, dal scheef: na 60 stappen gemiddelde hoogte 1,25

Momentum

staplengte 0,0042 · hoogte na 60 stappen 0,07

Momentum, dal scheef: na 60 stappen gemiddelde hoogte 0,07

AdaGrad

staplengte 2,4 · hoogte na 60 stappen 2,42

AdaGrad, dal scheef: na 60 stappen gemiddelde hoogte 2,42

Adam

staplengte 0,75 · hoogte na 60 stappen 0,09

Adam, dal scheef: na 60 stappen gemiddelde hoogte 0,09
Hetzelfde dal, 45 graden gedraaid, met de ruis meegedraaid en dezelfde reeks staplengtes. SGD en Momentum lopen precies hetzelfde pad als hierboven, alleen gedraaid; AdaGrad en Adam niet.
MethodeHoogte, asHoogte, scheefOnder 1, asOnder 1, scheef
SGD1,251,255757
Momentum0,070,074242
AdaGrad0,032,4215niet
Adam0,060,092842
Dezelfde uitkomsten als in de figuren. Hoogte: gemiddeld over de laatste tien van 60 stappen; het dal ligt op 0. Onder 1: de eerste stap waarop de wandelaar onder hoogte 1 komt.

In echte netwerken liggen de dalen zelden netjes langs de assen. AdaGrad bestaat ook in een versie die met een scheef dal overweg kan, met een volledige matrix in plaats van één getal per richting, maar die is voor miljarden parameters veel te duur[13]. Methoden als Shampoo benaderen haar daarom per gewichtsmatrix[19]. Het idee om naar de matrix als geheel te kijken in plaats van naar losse getallen, leidt naar de jongste methode in dit stuk.

Muon: de hele matrix tegelijk

Keller Jordan en collega’s publiceerden Muon eind 2024, niet in een tijdschrift maar in een blog, nadat ze er snelheidsrecords mee hadden gevestigd bij het trainen van kleine modellen[20]. De gewichten van een neuraal netwerk zitten grotendeels in matrices, en de stap voor zo’n matrix is zelf ook een matrix. Jordan merkte op dat die stappen in transformers vaak worden beheerst door een paar sterke richtingen, terwijl de overige richtingen nauwelijks iets krijgen, ook als ze ertoe doen.

Muon neemt daarom de momentum-stap voor een matrix en maakt alle richtingen ongeveer even sterk. In wiskundige termen trekt het alle singuliere waarden naar één. Dat gebeurt niet met een dure, exacte berekening, maar met een paar rondes van een eenvoudige rekenregel, de Newton-Schulz-iteratie, die alleen matrixvermenigvuldigingen nodig heeft[20]. Het tweede script, newton_schulz.py, doet die rekenregel op een kleine matrix, met de coëfficiënten van Jordan:

uit newton_schulz.py
def newton_schulz(g, rondes=RONDES):
    """X <- A*X + (B*XX' + C*(XX')^2)*X, na delen door de Frobeniusnorm.

    Geeft alle tussenstanden terug, te beginnen bij de genormaliseerde stap.
    """
    norm = math.sqrt(sum(x * x for rij in g for x in rij))
    x = [[e / norm for e in rij] for rij in g]
    standen = [x]
    for _ in range(rondes):
        a = maal(x, getransponeerd(x))
        b = optellen((B, a), (C, maal(a, a)))
        x = optellen((A, x), (1, maal(b, x)))
        standen.append(x)
    return standen

De stap die erin gaat, heeft één richting van sterkte 10 en één van sterkte 0,1. Na 5 rondes zijn dat 0,70 en 0,70: de verhouding gaat van 100 op 1 naar vrijwel 1 op 1. Precies 1 worden ze niet: de rekenregel is afgesteld op snelheid, niet op exactheid, en volgens Jordan schaadt een afwijking tot ongeveer 0,3 het trainen niet[20].

Bernstein en Newhouse lieten zien dat zo’n stap de steilste afdaling is als je de grootte van een stap meet met de spectrale norm, een maat die naar de matrix als geheel kijkt in plaats van naar losse getallen[21]. Muon kijkt dus niet langs de assen van de kaart, zoals AdaGrad en Adam, maar naar de richtingen van de matrix zelf. Het is alleen bedoeld voor de matrices binnen in het netwerk. De overige parameters, zoals de woordvectoren aan het begin en de laatste laag, trainen ook bij Muon gewoon met AdamW[20].

Of Muon ook bij grote modellen werkt, was lang de vraag. Onderzoekers van Moonshot AI rapporteerden in 2025 dat Muon, met een paar aanpassingen, bij het trainen van grote taalmodellen ongeveer twee keer zo rekenefficiënt is als AdamW[17]. Later trainden ze met een variant, MuonClip, een model van een biljoen parameters[22]. Die schaalresultaten zijn nog niet door vakgenoten getoetst, en AdamW is nog altijd de standaard. Over de methode zelf verschijnt inmiddels wel getoetst onderzoek, met onder meer een eerste bewijs dat en hoe snel Muon convergeert[23].

Bergen met meer dan één dal

Tot nu toe hadden we één dal. Een echt verlieslandschap heeft er ontelbaar veel, en daarnaast zadels: plekken die in de ene richting een dal zijn en in de andere een bergrug. Lang was de vrees dat de wandelaar vast komt te zitten in een ondiep dal, ver boven het echte laagste punt. Onderzoek wijst op een ander beeld. Dauphin en collega’s betogen dat in landschappen met heel veel richtingen zadels het grote obstakel zijn, niet slechte dalen: dat het in alle richtingen tegelijk omhooggaat, is daar zeldzaam[24]. In een sterk vereenvoudigd model van een groot netwerk liggen de dalen die je vindt bovendien op vergelijkbare, lage hoogte[25]. Ook de bouw van het netwerk telt: verbindingen die lagen overslaan, zoals de residual stream uit het LLM-stuk, maken het landschap zichtbaar gladder[26].

Een garantie is het niet. Voor zulke landschappen is niet bewezen dat de wandeling op het laagste punt uitkomt. Het werkt in de praktijk, en het vak begrijpt steeds beter waarom.

Zelf proberen

Beide scripts gebruiken alleen de standaardbibliotheek van Python 3; er hoeft niets geïnstalleerd te worden. Download bergwandeling.py en newton_schulz.py, en draai ze in een terminal met python3 bergwandeling.py. Dit is wat u dan te zien krijgt:

uitvoer van bergwandeling.py
Dal langs de as (0 graden), 60 stappen
  methode    staplengte   hoogte  onder 1
  SGD            0.0316     1.25       57
  Momentum       0.0042     0.07       42
  AdaGrad        1.7783     0.03       15
  Adam           1.0000     0.06       28

Dal scheef (45 graden), 60 stappen
  methode    staplengte   hoogte  onder 1
  SGD            0.0316     1.25       57
  Momentum       0.0042     0.07       42
  AdaGrad        2.3714     2.42     niet
  Adam           0.7499     0.09       42
uitvoer van newton_schulz.py
stap zelf          10.0000  0.1000
genormaliseerd      1.0000  0.0100
na ronde 1          0.7010  0.0344
na ronde 2          1.1136  0.1184
na ronde 3          0.7207  0.4000
na ronde 4          1.0931  1.0900
na ronde 5          0.6989  0.6965

Met --json erachter geeft het eerste script alle paden, zoals de figuren ze tekenen. Verander gerust iets: de steilheid van het dal, de ruis of het startpunt. Een staplengte die bij de ene methode het best werkt, blijkt bij een andere vaak veel te groot of te klein, en wat in het ene dal wint, verliest in het andere.

Wat dit betekent

Voor wie met AI werkt, volgen hier een paar dingen uit. Een taalmodel is het eindpunt van zo’n wandeling. Niemand heeft zijn gewichten gekozen; ze zijn de plek waar de wandelaar na de training stond. Een tweede training, met andere ruis, eindigt doorgaans op een andere plek, met een model dat net anders antwoordt. En het verlies dat tijdens de training daalt, meet hoe goed het model de trainingstekst voorspelt, niet of zijn juridische antwoorden kloppen. Daarom meten wij de kwaliteit van de antwoorden apart, en laat Fermat 360 bij elke bewering de bron zien.

Wie het liever ziet dan leest: Grant Sanderson (3Blue1Brown) legt gradiëntdaling uit in Gradient descent, how neural networks learn, het tweede deel van de reeks waarop ook Hoe werken LLM’s? is gebaseerd.

Bronnen

  1. Brown, T. B., e.a. (2020). Language Models are Few-Shot Learners. Advances in Neural Information Processing Systems 33 (NeurIPS 2020). arxiv.org/abs/2005.14165
  2. Dantzig, G. B. (1963). Linear Programming and Extensions. Santa Monica: RAND Corporation. www.rand.org/pubs/reports/R366.html
  3. Dantzig, G. B. (1951). Maximization of a linear function of variables subject to linear inequalities. In T. C. Koopmans (red.), Activity Analysis of Production and Allocation, 339–347. New York: Wiley. cowles.yale.edu/sites/default/files/2022-09/m13-all.pdf
  4. Goodfellow, I., Bengio, Y. & Courville, A. (2016). Deep Learning, hoofdstuk 8: Optimization for Training Deep Models. Cambridge (MA): MIT Press. www.deeplearningbook.org/contents/optimization.html
  5. Rumelhart, D. E., Hinton, G. E. & Williams, R. J. (1986). Learning representations by back-propagating errors. Nature, 323, 533–536. doi.org/10.1038/323533a0
  6. Cauchy, A.-L. (1847). Méthode générale pour la résolution des systèmes d'équations simultanées. Comptes rendus hebdomadaires des séances de l'Académie des sciences, 25(16), 536–538. archive.org/details/comptes-rendus-hebdomadaires-academie-des-sciences_1847-10-18_25_16
  7. Robbins, H. & Monro, S. (1951). A Stochastic Approximation Method. The Annals of Mathematical Statistics, 22(3), 400–407. doi.org/10.1214/aoms/1177729586
  8. Bottou, L., Curtis, F. E. & Nocedal, J. (2018). Optimization Methods for Large-Scale Machine Learning. SIAM Review, 60(2), 223–311. doi.org/10.1137/16M1080173
  9. Keskar, N. S., e.a. (2017). On Large-Batch Training for Deep Learning: Generalization Gap and Sharp Minima. International Conference on Learning Representations (ICLR 2017). arxiv.org/abs/1609.04836
  10. Polyak, B. T. (1964). Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics, 4(5), 1–17. doi.org/10.1016/0041-5553(64)90137-5
  11. Nesterov, Y. (1983). A method of solving a convex programming problem with convergence rate O(1/k²). Soviet Mathematics Doklady, 27, 372–376. zbmath.org/?q=an:0535.90071
  12. Sutskever, I., Martens, J., Dahl, G. & Hinton, G. (2013). On the importance of initialization and momentum in deep learning. Proceedings of the 30th International Conference on Machine Learning, PMLR 28(3), 1139–1147. proceedings.mlr.press/v28/sutskever13.html
  13. Duchi, J., Hazan, E. & Singer, Y. (2011). Adaptive Subgradient Methods for Online Learning and Stochastic Optimization. Journal of Machine Learning Research, 12, 2121–2159. jmlr.org/papers/v12/duchi11a.html
  14. Hinton, G., Srivastava, N. & Swersky, K. (2012). Lecture 6e — rmsprop: Divide the gradient by a running average of its recent magnitude. Collegeslides, Coursera: Neural Networks for Machine Learning. www.cs.toronto.edu/~hinton/coursera/lecture6/lec6.pdf
  15. Kingma, D. P. & Ba, J. (2015). Adam: A Method for Stochastic Optimization. International Conference on Learning Representations (ICLR 2015). arxiv.org/abs/1412.6980
  16. Loshchilov, I. & Hutter, F. (2019). Decoupled Weight Decay Regularization. International Conference on Learning Representations (ICLR 2019). arxiv.org/abs/1711.05101
  17. Liu, J., e.a. (2025). Muon is Scalable for LLM Training. arXiv:2502.16982. arxiv.org/abs/2502.16982
  18. Reddi, S. J., Kale, S. & Kumar, S. (2018). On the Convergence of Adam and Beyond. International Conference on Learning Representations (ICLR 2018). openreview.net/forum?id=ryQu7f-RZ
  19. Gupta, V., Koren, T. & Singer, Y. (2018). Shampoo: Preconditioned Stochastic Tensor Optimization. Proceedings of the 35th International Conference on Machine Learning, PMLR 80, 1842–1850. proceedings.mlr.press/v80/gupta18a.html
  20. Jordan, K., e.a. (2024). Muon: An optimizer for hidden layers in neural networks. Blog. kellerjordan.github.io/posts/muon/
  21. Bernstein, J. & Newhouse, L. (2024). Old Optimizer, New Norm: An Anthology. OPT 2024: Optimization for Machine Learning (workshop); arXiv:2409.20325. arxiv.org/abs/2409.20325
  22. Kimi Team (2025). Kimi K2: Open Agentic Intelligence. arXiv:2507.20534. arxiv.org/abs/2507.20534
  23. Pethick, T., e.a. (2025). Training Deep Learning Models with Norm-Constrained LMOs. Proceedings of the 42nd International Conference on Machine Learning, PMLR 267, 49069–49104. proceedings.mlr.press/v267/pethick25a.html
  24. Dauphin, Y. N., e.a. (2014). Identifying and attacking the saddle point problem in high-dimensional non-convex optimization. Advances in Neural Information Processing Systems 27 (NeurIPS 2014). arxiv.org/abs/1406.2572
  25. Choromanska, A., e.a. (2015). The Loss Surfaces of Multilayer Networks. Proceedings of the 18th International Conference on Artificial Intelligence and Statistics, PMLR 38, 192–204. proceedings.mlr.press/v38/choromanska15.html
  26. Li, H., e.a. (2018). Visualizing the Loss Landscape of Neural Nets. Advances in Neural Information Processing Systems 31 (NeurIPS 2018). arxiv.org/abs/1712.09913