Designing Machine Learning ApplicationsDesigning Machine Learning Applications
Home
Discus
Home
Discus
  • Contents
  • Preface

    • About the Author
    • About DMLA
  • Mathematical Foundations

    • Linear Algebra

      • Vector Basics
      • Matrix Basics
    • Calculus

      • Limits, Derivatives, and Differentials
      • Multivariate Functions and Composite Function Differentiation
    • Statistics and Probability

      • Probability Basics
      • Statistical Inference
  • Classical Statistical Learning

    • Linear Models

      • Linear Regression
      • Logistic Regression
      • Regularization and Generalized Linear Models
    • Bayesian Methods

      • Naive Bayes
      • Bayesian Network
      • EM Algorithm
    • Support Vector Machines

      • Support Vector Machine
      • Kernel Trick
    • Decision Trees and Ensembles

      • Decision Trees
      • Random Forest
      • Boosting
    • Unsupervised Learning

      • Clustering
      • Dimensionality Reduction
  • Neural Networks and Deep Learning

    • Neural Network Architectures

      • Fundamentals of Neural Networks
      • Linear Perceptron
      • Multi-Layer Perceptron
      • Forward Propagation
      • Backpropagation
      • Activation Functions and Loss Functions
    • Optimization

      • Gradient Descent
      • Adaptive Optimizers
    • Deep Network Stability

      • Weight Initialization
      • Dropout Regularization
      • Batch Normalization
    • Convolutional Neural Networks

      • CNN Basics
      • AlexNet and the CNN Revival
      • VGG and GoogLeNet
      • ResNet Residual Network
      • Lab: AlexNet Image Classification
    • Generative Models

      • Variational Autoencoder
      • Generative Adversarial Network
      • Lab: DCGAN Image Generation
    • Sequence Models

      • Word Embedding and Representation Learning
      • RNN Fundamentals
      • LSTM and GRU Gating Mechanisms
      • Seq2Seq Sequence Mapping
      • Lab: LSTM Poetry Generation
  • The Language Model Singularity

    • Transformer Architecture

      • Transformer Fundamentals
      • Transformer Evolution and Variants
      • Language Models and Tokenization
      • Lab: Transformer Model Training
    • Pretraining and Fine-Tuning

      • Pretraining Data Engineering
      • Scaling Laws
      • Distributed Training Infrastructure
      • Supervised Fine-Tuning
      • Lab: SFT Model Conversation
    • Alignment Training

      • Reinforcement Learning from Human Feedback
      • Evolution of Alignment Methods
      • Lab: DPO Alignment Training
    • Reasoning Capabilities

      • Chain of Thought and Reasoning Models
      • Test-Time Compute Scaling
      • Inference Efficiency Optimization
      • Lab: LLM Inference Optimization
    • Multimodal Fusion and Safety

      • Multimodal Large Language Models
      • Model Evaluation and Safety
      • Lab: VLM Training
  • AI Infrastructure and Engineering

    • Model Serving

      • Inference Service Architecture
      • Request Scheduling and Batching
      • GPU Resource Management
      • Lab: Deploying LLM Inference Service
    • MLOps Practices

      • Data Versioning
      • Experiment Tracking and Model Registry
      • Hyperparameter Optimization
      • Model Performance Monitoring
      • Drift Detection
  • Agentic Application Systems

    • Vector Retrieval and RAG

      • Embedding and Vector Retrieval
      • Retrieval Quality Evaluation and Optimization
      • Retrieval-Augmented Generation
      • Lab: Building a Knowledge Base Q&A System
    • Building Agent Applications

      • From LLM to Agent
      • Tool Use
      • Planning and Reasoning
      • Memory Systems
      • Agent Collaboration and Communication
      • Orchestration and Fault Tolerance
      • Lab: Research Agent Collaboration System
  • Appendix

    • Building the Sandbox Environment
    • NumPy Practice

      • Data Processing Practice
      • Calculus Computation Practice
      • Probability and Statistics Practice

Adaptive Optimizers

The idea of adaptive learning rates stems from a simple observation: different parameters play different roles during training. Some parameters are updated frequently with large and stable gradients; others are updated sparsely with small gradients, occasionally even zero. If all parameters are given the same step size, the frequently updated parameters may take steps that are too large, causing oscillation and hindering convergence, while the sparsely updated parameters may take steps that are too small, making progress slow. It is like a principal assigning the same homework load to all students in the school — senior students find it boring, while junior students find it overwhelming.

Adaptive Optimizers are designed precisely to solve this "one-size-fits-all" problem. They automatically adjust the learning rate based on the historical gradients of each parameter, assigning an independent step size to every parameter. This "teach according to aptitude" strategy was first proposed by John Duchi in 2011, whose AdaGrad algorithm pioneered adaptive learning rates. Subsequently, Hinton proposed RMSprop in his 2012 Coursera course, addressing the issue of premature learning rate decay in AdaGrad. In 2015, Diederik Kingma and Jimmy Ba published the landmark paper "Adam: A Method for Stochastic Optimization", effectively combining momentum with adaptive learning rates, making Adam the most popular optimizer in deep learning. In 2019, Ilya Loshchilov and Frank Hutter discovered a theoretical flaw in Adam's weight decay implementation and proposed AdamW, further improving generalization. In 2024, Keller Jordan proposed Muon, which departs from the per-parameter adaptive learning rate approach and instead leverages the geometric structure of parameter matrices, using Newton-Schulz iteration to orthogonalize momentum updates, setting records in the NanoGPT and CIFAR-10 speed benchmarks. This chapter introduces these four adaptive optimizers along with the new-direction Muon, analyzing their design principles, advantages, disadvantages, and suitable application scenarios.

AdaGrad

The momentum method and NAG introduced in the previous chapter solved the oscillation problem of gradient descent, but they still assign the same learning rate to all parameters. This is limiting in many scenarios. For example, in tasks such as natural language processing, the parameter updates in the word embedding layer are highly uneven: common words appear frequently with large gradients, while rare words appear occasionally with small or even zero gradients. With a uniform learning rate, the embedding vectors of common words update too quickly and tend to oscillate, while those of rare words update too slowly to learn effective representations. This motivated neural network researchers to find ways for each parameter to have its own learning rate.

AdaGrad (Adaptive Gradient Algorithm), proposed by John Duchi in 2011, is one of the earliest adaptive optimizers. The design philosophy of AdaGrad is straightforward: parameters that have been updated frequently have already learned a lot of information, so they should slow down; conversely, parameters updated sparsely still have a lot to learn, so they should take larger steps. This "teach according to aptitude" strategy allows each parameter to learn at its own optimal pace, avoiding the efficiency waste of a uniform learning rate.

The key to implementing this idea lies in measuring the magnitude of parameter updates. The metric AdaGrad chooses is the accumulation of squared gradients. There are two reasons for choosing squared gradients rather than gradients themselves: first, the squaring operation converts negative gradients to positive numbers, preventing positive and negative values from canceling out and distorting the accumulation (a parameter may frequently update in alternating positive and negative directions, but the accumulator should faithfully reflect the actual update activity); second, squaring amplifies large gradients and shrinks small ones, causing the accumulator for active parameters to grow faster and the learning rate to decay more significantly, achieving the effect of "the more active, the more cautious." AdaGrad chooses accumulation rather than moving average because its original design goal was to handle sparse gradients. Sparse parameters only receive gradients occasionally, so their accumulator grows slowly, preserving a large learning rate for a long time; frequent parameters, on the other hand, accumulate quickly, causing the learning rate to decay rapidly and preventing oscillation. This design of cumulative growth and decreasing learning rate essentially uses historical gradients as a "credit score" to endorse future updates — the more active the gradient, the larger the accumulation, the smaller the learning rate, indicating that the parameter has already learned enough and subsequent updates should be more cautious.

Let Gt\mathbf{G}_tGt​ be the accumulation of historical squared gradients, recording how many times and by how much the parameter has been updated. (∇Lt)2(\nabla L_t)^2(∇Lt​)2 is the square of the current gradient, converting gradient values to non-negative numbers (negative gradients also need to be learned). The following formula expresses the gradient accumulation process:

Gt=Gt−1+(∇Lt)2\mathbf{G}_t = \mathbf{G}_{t-1} + (\nabla L_t)^2Gt​=Gt−1​+(∇Lt​)2
(1)

AdaGrad uses the learning rate hyperparameter divided by the square root of the accumulated gradients to achieve the effect that larger accumulation yields a smaller effective learning rate. To prevent division by zero at the beginning when there is no accumulated gradient yet, a small constant ϵ\epsilonϵ (typically 10−810^{-8}10−8) is added to the accumulation in practice. The effective learning rate of AdaGrad is expressed as ηGt+ϵ\frac{\eta}{\sqrt{\mathbf{G}_t + \epsilon}}Gt​+ϵ​η​, where this denotes element-wise operations, meaning the learning rate for each component of the gradient is ηGt,i+ϵ\frac{\eta}{\sqrt{G_{t,i} + \epsilon}}Gt,i​+ϵ​η​. This implies that within the same batch of parameters, some may have a large learning rate while others have a small one, depending entirely on their respective gradient histories. In summary, the weight update process is:

Wt+1=Wt−ηGt+ϵ⋅∇Lt\mathbf{W}_{t+1} = \mathbf{W}_t - \frac{\eta}{\sqrt{\mathbf{G}_t + \epsilon}} \cdot \nabla L_tWt+1​=Wt​−Gt​+ϵ​η​⋅∇Lt​
(2)

This adaptive adjustment of AdaGrad is particularly suitable for sparse gradient problems (such as word embeddings in natural language processing), where sparsely updated parameters receive larger learning rates to accelerate learning. However, AdaGrad's seemingly perfect strategy of accumulating historical gradients harbors a serious flaw. Since Gt\mathbf{G}_tGt​ is the accumulation of historical squared gradients, it only increases during training (squared gradients are non-negative), and Gt\sqrt{\mathbf{G}_t}Gt​​ also monotonically increases. This means the effective learning rate ηGt\frac{\eta}{\sqrt{\mathbf{G}_t}}Gt​​η​ only ever decreases — AdaGrad merely regulates how fast the learning rate shrinks. With each parameter update, the learning rate becomes a little smaller. In the later stages of training, the learning rate can become extremely small (e.g., 10−610^{-6}10−6), causing parameters to almost stop updating and training to stagnate. This flaw puts AdaGrad in a dilemma: it is well-suited for short-term training or sparse gradient problems (frequently updated parameters get a small learning rate, sparse parameters get a large learning rate), but for long-term training, the learning rate decays too early, causing stagnation. To address this flaw, the improved version of AdaGrad, RMSprop, was introduced.

RMSprop

RMSprop (Root Mean Square Propagation) was proposed by Geoffrey Hinton in his 2012 Coursera course. Interestingly, Hinton never formally published a paper on RMSprop — it was disseminated only as course notes, but its simplicity and effectiveness led to rapid adoption by the community. RMSprop's improvement over AdaGrad is to replace AdaGrad's accumulation with an exponentially weighted moving average. A moving average is a weighted average where new data has more weight and old data gradually decays, much like a window sliding along the data stream, allowing historical information to flow in and out. As a result, RMSprop retains gradient information from only the most recent approximately 11−γ\frac{1}{1-\gamma}1−γ1​ steps.

Let Et\mathbf{E}_tEt​ be the exponentially weighted moving average of squared gradients, and γ\gammaγ be the decay coefficient (typically 0.90.90.9), used as the weight for the historical accumulated squared gradients, controlling how much historical information is retained. The remaining (1−γ)(1-\gamma)(1−γ) serves as the weight for the new squared gradients, ensuring the weights sum to 1. RMSprop's gradient accumulation formula is (compare with formula (1)):

Et=γEt−1+(1−γ)(∇Lt)2\mathbf{E}_t = \gamma \mathbf{E}_{t-1} + (1 - \gamma)(\nabla L_t)^2Et​=γEt−1​+(1−γ)(∇Lt​)2
(3)

If γ\gammaγ is set to 0.90.90.9, it retains 90% of historical gradient information and incorporates 10% of the current gradient. Old history gradually fades out, preventing numerical inflation. The effective window is approximately 11−0.9=10\frac{1}{1-0.9} = 101−0.91​=10 steps, retaining only the gradient information from the most recent 10 steps. Apart from this, RMSprop's weight update is identical to AdaGrad (see (2)):

Wt+1=Wt−ηEt+ϵ⋅∇Lt\mathbf{W}_{t+1} = \mathbf{W}_t - \frac{\eta}{\sqrt{\mathbf{E}_t + \epsilon}} \cdot \nabla L_tWt+1​=Wt​−Et​+ϵ​η​⋅∇Lt​

RMSprop's use of an exponentially weighted moving average brings two benefits. First, the window effect: retaining only the gradient information from roughly the last 11−γ\frac{1}{1-\gamma}1−γ1​ steps prevents the accumulator from only absorbing without discarding, so the learning rate does not monotonically decrease but instead exhibits mild fluctuations. Second, RMSprop can adapt more quickly to gradient changes — when gradients change sharply, Et\mathbf{E}_tEt​ adjusts accordingly, making the learning rate more responsive. However, RMSprop also has a drawback (one that AdaGrad shares as well, and is not caused by RMSprop's improvement): it only uses squared gradients to adjust the learning rate without accumulating historical gradient directions, effectively forgoing the smoothing effect of Momentum. Is it possible to have both the stability of momentum and the flexibility of adaptive learning rates? This leads us to the widely used adaptive optimizer Adam.

Adam

Adam (Adaptive Moment Estimation) was proposed by Diederik Kingma and Jimmy Ba at the International Conference on Learning Representations (ICLR) in 2015. The name Adam stems from its core mechanism: simultaneously estimating the first moment (the mean) and the second moment (the uncentered variance) of gradients, combining momentum with adaptive learning rates. This dual-pronged strategy gives parameter updates both smooth directional stability and individualized step sizes, making Adam the most popular optimizer in deep learning today.

The key to this fusion is that Adam maintains two state variables simultaneously. The first moment mt\mathbf{m}_tmt​ accumulates directional information from historical gradients, analogous to the velocity variable in momentum methods, smoothing the update path and suppressing oscillations. The second moment vt\mathbf{v}_tvt​ accumulates the squares of historical gradients, similar to the moving average in RMSprop, adjusting the learning rate for each parameter. The two operate independently without interfering with each other: the first moment handles "which direction to go," while the second moment handles "how far to go." This division of labor makes Adam perform robustly across a wide range of tasks, with low sensitivity to hyperparameter choices. The default parameters (β1=0.9,β2=0.999,η=0.001\beta_1=0.9, \beta_2=0.999, \eta=0.001β1​=0.9,β2​=0.999,η=0.001) work well for most scenarios, which is an important reason for its widespread popularity.

Let mt\mathbf{m}_tmt​ be the first moment estimate of the gradient (momentum), β1\beta_1β1​ be the decay coefficient for the first moment (default 0.90.90.9), and ∇Lt\nabla L_t∇Lt​ be the current gradient direction. The following formula expresses Adam's momentum accumulation process (consistent with the principle of Momentum):

mt=β1mt−1+(1−β1)∇Lt\mathbf{m}_t = \beta_1 \mathbf{m}_{t-1} + (1 - \beta_1) \nabla L_tmt​=β1​mt−1​+(1−β1​)∇Lt​
(4)

Let vt\mathbf{v}_tvt​ be the second moment estimate of the gradient (accumulated gradients), β2\beta_2β2​ be the decay coefficient for the second moment (default 0.9990.9990.999), and (∇Lt)2(\nabla L_t)^2(∇Lt​)2 be the square of the current gradient. The following formula expresses Adam's moving average process of squared gradients (identical to RMSprop's gradient accumulation formula (3)):

vt=β2vt−1+(1−β2)(∇Lt)2\mathbf{v}_t = \beta_2 \mathbf{v}_{t-1} + (1 - \beta_2) (\nabla L_t)^2vt​=β2​vt−1​+(1−β2​)(∇Lt​)2

Adam initializes m0=0,v0=0\mathbf{m}_0 = 0, \mathbf{v}_0 = 0m0​=0,v0​=0. This seemingly natural choice, however, introduces a cold-start problem. Expanding the first moment formula (4), mt\mathbf{m}_tmt​ is essentially a weighted sum of historical gradients: mt=(1−β1)[∇Lt+β1∇Lt−1+β12∇Lt−2+...]\mathbf{m}_t = (1-\beta_1)[\nabla L_t + \beta_1 \nabla L_{t-1} + \beta_1^2 \nabla L_{t-2} + ...]mt​=(1−β1​)[∇Lt​+β1​∇Lt−1​+β12​∇Lt−2​+...]. The sum of the weights should ideally be 1 to correctly reflect the weighted average of gradients. However, due to the initialization m0=0\mathbf{m}_0 = 0m0​=0, the actual sum of weights is the geometric series 1−β1t1 - \beta_1^t1−β1t​. In early training, ttt is small and β1t\beta_1^tβ1t​ is close to 1 (e.g., with β1=0.9\beta_1=0.9β1​=0.9, at t=1t=1t=1, β1t=0.9\beta_1^t=0.9β1t​=0.9), so the weight sum 1−β1t1-\beta_1^t1−β1t​ is far less than 1. The missing weight is occupied by the zero initialization, causing the estimate to be biased toward zero. The same flaw exists in the second moment formula. This is Adam's cold-start problem. Therefore, intermediate variables m^t\hat{\mathbf{m}}_tm^t​ and v^t\hat{\mathbf{v}}_tv^t​ are defined to correct the bias by dividing mt\mathbf{m}_tmt​ and vt\mathbf{v}_tvt​ by the weight sum to compensate for the missing portion:

m^t=mt1−β1t,v^t=vt1−β2t\hat{\mathbf{m}}_t = \frac{\mathbf{m}_t}{1 - \beta_1^t}, \quad \hat{\mathbf{v}}_t = \frac{\mathbf{v}_t}{1 - \beta_2^t}m^t​=1−β1t​mt​​,v^t​=1−β2t​vt​​

Observing Adam's iteration process, in early training when ttt is small, βt\beta^tβt is close to 1, and the correction factor 11−βt\frac{1}{1 - \beta^t}1−βt1​ reaches its maximum, amplifying the estimate to counteract the zero-initialization bias. In later training when ttt is large, βt\beta^tβt approaches 0, the correction factor approaches 1, and the correction effect disappears. To illustrate with concrete numbers, let β1=0.9\beta_1 = 0.9β1​=0.9, t=1t = 1t=1, gradient ∇L1=10\nabla L_1 = 10∇L1​=10. The uncorrected m1=0.9×0+0.1×10=1\mathbf{m}_1 = 0.9 \times 0 + 0.1 \times 10 = 1m1​=0.9×0+0.1×10=1. The corrected m^1=11−0.91=10\hat{\mathbf{m}}_1 = \frac{1}{1 - 0.9^1} = 10m^1​=1−0.911​=10, which exactly equals the actual gradient. Combining all four variables, Adam's weight update formula is:

Wt+1=Wt−ηv^t+ϵ⋅m^t\mathbf{W}_{t+1} = \mathbf{W}_t - \frac{\eta}{\sqrt{\hat{\mathbf{v}}_t} + \epsilon} \cdot \hat{\mathbf{m}}_tWt+1​=Wt​−v^t​​+ϵη​⋅m^t​
(5)

This formula can be understood as using the bias-corrected momentum m^t\hat{\mathbf{m}}_tm^t​ to indicate the direction of progress (smoothed gradient), and the adaptive learning rate ηv^t\frac{\eta}{\sqrt{\hat{\mathbf{v}}_t}}v^t​​η​ to control the step size (individually adjusted per parameter). ϵ\epsilonϵ (typically 10−810^{-8}10−8) prevents division by zero, and η\etaη is the global learning rate (typically 0.0010.0010.001, smaller than SGD's default because the adaptive mechanism can make the effective step size for some parameters too large). Adam combines the advantages of momentum and adaptive learning rates and is widely used in computer vision, natural language processing, recommendation systems, and other fields, making it the default optimizer in deep learning research.

Adam provides four hyperparameters, but apart from the global learning rate, the other three can usually be left at their default values. In practice, if the loss oscillates, reduce the learning rate; if convergence is slow, increase the learning rate:

HyperparameterDefaultRole
η\etaη0.0010.0010.001Global learning rate, typically in the range [10−4,10−2][10^{-4}, 10^{-2}][10−4,10−2]
β1\beta_1β1​0.90.90.9First moment decay coefficient, controls momentum smoothing
β2\beta_2β2​0.9990.9990.999Second moment decay coefficient, controls learning rate adaptivity
ϵ\epsilonϵ10−810^{-8}10−8Numerical stability constant, prevents division by zero

AdamW

Adam appears to have gathered all the advantages of momentum and adaptive learning rates, becoming the default optimizer in deep learning. However, a 2019 paper revealed a hidden theoretical flaw: the implementation of weight decay (L2 regularization) in Adam conflicts with adaptive learning rates, making the regularization effect unstable. This issue led to Adam's latest corrected version, AdamW.

Weight decay is a common technique for preventing overfitting. In SGD, it is essentially equivalent to L2 regularization. A penalty term is added to the loss function to force parameters to remain small: Ltotal=Ldata+λ∥W∥2L_{total} = L_{data} + \lambda \|\mathbf{W}\|^2Ltotal​=Ldata​+λ∥W∥2. Correspondingly, the gradient becomes ∇Ltotal=∇Ldata+2λW\nabla L_{total} = \nabla L_{data} + 2\lambda \mathbf{W}∇Ltotal​=∇Ldata​+2λW. In SGD, the implementation of weight decay is simple and intuitive:

Wt+1=Wt−η∇Ldata−2ηλWt=Wt(1−2ηλ)−η∇Ldata\mathbf{W}_{t+1} = \mathbf{W}_t - \eta \nabla L_{data} - 2\eta \lambda \mathbf{W}_t = \mathbf{W}_t(1 - 2\eta \lambda) - \eta \nabla L_{data}Wt+1​=Wt​−η∇Ldata​−2ηλWt​=Wt​(1−2ηλ)−η∇Ldata​

Each step multiplies the weights by (1−2ηλ)(1 - 2\eta \lambda)(1−2ηλ), gradually decaying them, treating all parameters equally. But Adam's implementation differs. The gradient ∇Ltotal=∇Ldata+2λW\nabla L_{total} = \nabla L_{data} + 2\lambda \mathbf{W}∇Ltotal​=∇Ldata​+2λW is accumulated into the first moment mt\mathbf{m}_tmt​ and second moment vt\mathbf{v}_tvt​, so the weight decay term 2λW2\lambda \mathbf{W}2λW is also scaled by the adaptive learning rate:

ΔWreg=−ηv^t+ϵ⋅2λW\Delta \mathbf{W}_{reg} = -\frac{\eta}{\sqrt{\hat{\mathbf{v}}_t} + \epsilon} \cdot 2\lambda \mathbf{W}ΔWreg​=−v^t​​+ϵη​⋅2λW

This leads to a problem. When v^t\hat{\mathbf{v}}_tv^t​ is large (the parameter's gradient history is active), the adaptive learning rate ηv^t\frac{\eta}{\sqrt{\hat{\mathbf{v}}_t}}v^t​​η​ is small, causing the weight decay term to be shrunk and the regularization effect to weaken. When v^t\hat{\mathbf{v}}_tv^t​ is small (the parameter's gradient history is sparse), the adaptive learning rate is large, causing the weight decay term to be amplified. This clearly contradicts the original design intent of L2 regularization, which is to uniformly decay all weights. Recall Adam's mechanism: gradient updates are scaled by the adaptive learning rate so that frequently updated parameters slow down and sparsely updated parameters accelerate. However, the purpose of weight decay is to uniformly constrain all parameters, preventing any single parameter from becoming too large and causing overfitting — a goal entirely different from adaptive adjustment. Mixing the two means that regularization, which should treat all parameters equally, is also subjected to "teaching according to aptitude," which is logically contradictory.

AdamW (Adam with Decoupled Weight Decay) was proposed by Ilya Loshchilov in the 2019 paper "Decoupled Weight Decay Regularization". The paper reveals Adam's weight decay problem and provides a concise solution: separate weight decay from gradient updates. Let the gradient update be responsible for learning data patterns, and weight decay be responsible for controlling model complexity — the two should operate independently without interference. Compared to Adam (see (5)), AdamW's update rule differs by the addition of a weight decay term:

Wt+1=Wt−ηv^t+ϵ⋅m^t−ηλWt\mathbf{W}_{t+1} = \mathbf{W}_t - \frac{\eta}{\sqrt{\hat{\mathbf{v}}_t} + \epsilon} \cdot \hat{\mathbf{m}}_t - \eta \lambda \mathbf{W}_tWt+1​=Wt​−v^t​​+ϵη​⋅m^t​−ηλWt​

Here, the term −ηλWt-\eta \lambda \mathbf{W}_t−ηλWt​ is the weight decay term, applied directly to the parameters without being scaled by the adaptive learning rate. The term −ηv^t+ϵ⋅m^t-\frac{\eta}{\sqrt{\hat{\mathbf{v}}_t} + \epsilon} \cdot \hat{\mathbf{m}}_t−v^t​​+ϵη​⋅m^t​ is the gradient update term, scaled by the adaptive learning rate. The overall formula can be understood as weight decay executing independently and gradient updates executing adaptively — the two are decoupled and do not interfere. In practice, AdamW can now replace Adam in almost all scenarios. Especially on tasks requiring strong regularization, such as large-scale language model training, AdamW has become the default optimizer for many Transformer-based models, such as BERT and GPT.

Muon

A common feature of the optimizers introduced so far is that they assign an independent learning rate to each parameter, adjusting the step size based on gradient history. This per-parameter adaptive strategy has been highly successful, but neural network parameters are not truly independent scalars — there are correlations between parameters. The weights of a neural network's hidden layer are naturally two-dimensional matrices, where rows correspond to transformations of input features and columns correspond to combinations of output neurons. Flattening the matrix into a vector and adjusting the learning rate element by element effectively discards the geometric information of the matrix. In December 2024, Keller Jordan proposed the Muon optimizer in a blog post, taking a different path from the traditional per-parameter adaptive learning rate approach by leveraging the matrix's geometric structure through orthogonalization of the momentum update.

The name Muon is derived from the somewhat mouthful phrase "Momentum Orthogonalized by Newton-Schulz" (Momentum Orthogonalized by Newton-Schulz), yet its concrete steps are surprisingly simple, consisting of just the following two:

  • Step one: momentum accumulation. Like momentum methods, Muon maintains a weighted average of historical gradients (a momentum buffer). When a new gradient arrives, it retains most of the old direction while incorporating a small portion of the new direction, thereby smoothing the update path and suppressing oscillations.

  • Step two: orthogonalization. This is where Muon differs from all the previously discussed optimizers. Think of a matrix as a combination of multiple independent directions and their strengths. For example, a weight matrix might push very aggressively in some directions (large strength) while only lightly touching in others (small strength). Orthogonalization does exactly this: it retains only the directions themselves while leveling all their strengths to the same magnitude. It is like a team with loud members and quiet members — orthogonalization essentially gives everyone a microphone at the same volume, so all voices can be heard equally.

The reason orthogonalization is needed is that Jordan discovered, through experimentation, that the update matrices produced by SGD momentum and Adam for model hidden layers are often dominated by a few strong directions, while the magnitudes of the many remaining "rare directions" are negligible. These rare directions, though inconspicuous, may carry information crucial for learning. Orthogonalization amplifies these weak directions to the same intensity as the dominant ones, allowing them to participate equally in parameter updates, preventing the network from developing blind spots during learning. The standard approach to orthogonalization is to first perform singular value decomposition on the matrix, separating directions from strengths, then discard the strengths and keep only the directions. However, the computational cost of singular value decomposition is too high to run at every training step. Muon uses Newton-Schulz iteration to bypass singular value decomposition — this is the origin of the "N" in its name.

Newton-Schulz iteration exploits a mathematical fact: if a carefully constructed polynomial function is repeatedly applied to a matrix, the function acts on the "strength" part of the matrix rather than the "direction" part, gradually pushing all strengths toward 1, ultimately approximating orthogonalization. The entire process requires only matrix multiplication, without explicit matrix decomposition. Jordan's team chose a quintic polynomial and tuned its three coefficients through extensive experiments, achieving stable orthogonalization in just five iterations under BF16 precision. Before the iteration begins, the matrix is first scaled to unit length, ensuring all strengths lie between 0 and 1. Then, with each iteration, the polynomial is applied to the matrix once: initially small strengths are rapidly pulled up, gradually approaching 1. After five iterations, the matrix is already very close to the orthogonalized result. Compared to directly performing singular value decomposition (too slow) and another method called coupled Newton iteration (requires FP32 precision, inefficient on GPUs), Newton-Schulz iteration can run in native BF16 precision on modern GPUs, offering both speed and numerical stability — this is the key to its ability to be embedded in the training loop.

After completing these two steps, Muon uses the orthogonalized momentum as the final update, multiplied by the global learning rate and applied to the weights. The entire process can be summarized as: momentum accumulation provides a smooth direction, orthogonalization levels all direction strengths, and the learning rate controls the overall step size. However, since Muon leverages the two-dimensional geometric relationship of matrices, it is only applicable to the two-dimensional weight matrices of neural network hidden layers. For scalar parameters (such as biases), vector parameters (such as scaling factors in layer normalization), and input/output embedding layers, standard optimizers like AdamW are still needed. This hybrid optimizer strategy (Muon for hidden layers, AdamW for the rest) is the mainstream approach for language models in 2026.

Muon offers tremendous advantages in both resources and performance. In terms of performance, Muon excels across multiple benchmarks. It compresses the training time to reach 94% accuracy on CIFAR-10 by approximately 21%. In the NanoGPT speed benchmark (FineWeb dataset), Muon improves training speed by 1.35 times. When training a 1.5B parameter Transformer to GPT-2 XL level performance with 8 H100 GPUs, Muon takes only 10 hours, whereas AdamW requires 13.3 hours. Subsequently, large-scale projects such as the Moonlight 16B mixture-of-experts model (5.7T tokens of training data) have further validated Muon's feasibility in industrial-grade scenarios. In terms of resources, both the computational and memory overhead of Muon are very low. In typical language model training (e.g., Llama 405B, model dimension 16384, batch size 16M tokens), the additional floating-point operations from the five Newton-Schulz iterations account for less than 1% of the total. In terms of memory consumption, Muon maintains only one momentum buffer, saving approximately half the optimizer state memory compared to AdamW, significantly lowering the resource barrier for training environments.

Muon represents a new direction in optimizer design — moving from independently adjusting learning rates for each scalar parameter to directly leveraging the matrix structure of parameters and improving the update direction through geometric transformations. Of course, this matrix-native optimization is still in its early stages, covering only linear layers and not yet extended to more complex components such as attention mechanisms. However, it has already proven its value in speed benchmarks and has inspired a subsequent series of optimizer research based on matrix structure.

Optimizer Selection Guide

At this point, we have studied eight optimizers: SGD, Momentum, NAG, AdaGrad, RMSprop, Adam, AdamW, and Muon. Each optimizer has its own characteristics. This section provides a straightforward "two-step" selection guide from the perspectives of both optimizer features and task types.

  • Step one: first understand the characteristics of each optimizer:

    OptimizerCore MechanismAdvantageDisadvantageSuitable Scenario
    SGDBasic gradient descentSimple, stableOscillation, slowSimple tasks, fine-tuning
    MomentumMomentum smoothingAcceleration, suppresses oscillationRequires learning rate tuningGeneral purpose
    NAGGradient at predicted positionAnticipates inflection pointsSlightly more complexPursuing precision
    AdaGradAccumulates squared gradientsSparse gradient friendlyLearning rate decaysSparse data, short-term training
    RMSpropMoving average of squared gradientsStable learning rateNo momentumLong-term training, RNN
    AdamMomentum + adaptiveRobust, fastWeight decay issueGeneral purpose, default choice
    AdamWAdam + decoupled weight decayStable regularizationAdditional second-moment memory overhead vs SGDDefault choice
    MuonMomentum + Newton-Schulz orthogonalizationLeverages matrix structure, memory efficientOnly applies to 2D matrix parametersTransformer hidden layers, large-scale training
  • Step two: select the optimizer based on task characteristics:

    Task TypeRecommended OptimizerReason
    General deep learningAdamWStrong robustness, default choice
    Computer visionSGD + MomentumExperiments show better generalization
    Natural language processingAdamWSparse gradients, clear adaptive advantage
    RNN/LSTMRMSprop / AdamWAdapts to gradient scale differences
    Fine-tuning pretrained modelsSGD + MomentumSmall learning rate fine-tuning, prevents damaging pretrained features
    Sparse data (recommendation systems)AdamWSparse parameters receive large learning rates
    Large-scale language model trainingMuon + AdamWMuon for hidden layers to accelerate, AdamW for the rest

Adaptive Optimizer in Practice

Theoretical analysis has revealed the design principles of each optimizer. Next, we compare the convergence behavior of SGD, Momentum, NAG, AdaGrad, RMSprop, Adam, AdamW, and Muon on a quadratic loss function through code experiments. The experiment uses a long elliptical loss function (with large gradient differences across directions), starting from the same initial point, and observes the parameter path, loss curve, and effective learning rate changes of each optimizer. The code implements the complete update logic for all eight optimizers, including momentum accumulation, gradient squared accumulation, moving average, bias correction, gradient computation at the predicted position, weight decay decoupling, and Newton-Schulz orthogonalization. Note that Muon is designed specifically for two-dimensional matrix parameters; in this experiment it is applied to a two-dimensional vector (treated as a 2×12 \times 12×1 matrix), where orthogonalization degenerates to direction normalization. Its matrix structure advantages can only be fully realized in actual neural network training.

import numpy as np
import matplotlib.pyplot as plt

# Define loss function and gradient - 10x gradient difference to demonstrate SGD oscillation
def loss_function(W):
    """Quadratic loss function L = 0.5 * W^T A W"""
    A = np.array([[1, 0], [0, 10]])  # 10x gradient difference
    return 0.5 * np.dot(W, A @ W)

def gradient(W):
    """Gradient ∇L = A W"""
    A = np.array([[1, 0], [0, 10]])
    return A @ W

# Optimizer implementations
class SGD:
    def __init__(self, lr=0.15):  # learning rate > 0.1 causes oscillation (lr > 1/gradient difference)
        self.lr = lr
        self.path = []

    def step(self, W, grad):
        W_new = W - self.lr * grad
        self.path.append(W_new.copy())
        return W_new

class Momentum:
    def __init__(self, lr=0.05, momentum=0.9):  # smaller learning rate to avoid excessive oscillation
        self.lr = lr
        self.momentum = momentum
        self.v = np.zeros(2)
        self.path = []

    def step(self, W, grad):
        self.v = self.momentum * self.v + self.lr * grad
        W_new = W - self.v
        self.path.append(W_new.copy())
        return W_new

class NAG:
    """Nesterov Accelerated Gradient - compute gradient at lookahead position"""
    def __init__(self, lr=0.05, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = np.zeros(2)
        self.path = []

    def step(self, W, grad_func):
        # NAG core: compute gradient at lookahead position, respond to inflection points early
        W_lookahead = W - self.momentum * self.v
        grad_lookahead = grad_func(W_lookahead)
        self.v = self.momentum * self.v + self.lr * grad_lookahead
        W_new = W - self.v
        self.path.append(W_new.copy())
        return W_new

class AdaGrad:
    def __init__(self, lr=1.0, eps=1e-8):  # large learning rate, fast initial convergence
        self.lr = lr
        self.eps = eps
        self.G = np.zeros(2)
        self.path = []

    def step(self, W, grad):
        self.G += grad ** 2  # accumulate squared gradients
        lr_adaptive = self.lr / np.sqrt(self.G + self.eps)  # decreasing learning rate
        W_new = W - lr_adaptive * grad
        self.path.append(W_new.copy())
        return W_new

class RMSprop:
    def __init__(self, lr=0.3, gamma=0.9, eps=1e-8):  # moderate learning rate
        self.lr = lr
        self.gamma = gamma
        self.eps = eps
        self.E = np.zeros(2)
        self.path = []

    def step(self, W, grad):
        self.E = self.gamma * self.E + (1 - self.gamma) * (grad ** 2)  # moving average
        lr_adaptive = self.lr / np.sqrt(self.E + self.eps)  # stable learning rate
        W_new = W - lr_adaptive * grad
        self.path.append(W_new.copy())
        return W_new

class Adam:
    def __init__(self, lr=0.3, beta1=0.9, beta2=0.999, eps=1e-8):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        self.m = np.zeros(2)  # first moment (momentum)
        self.v = np.zeros(2)  # second moment (squared gradients)
        self.t = 0
        self.path = []

    def step(self, W, grad):
        self.t += 1
        self.m = self.beta1 * self.m + (1 - self.beta1) * grad  # momentum accumulation
        self.v = self.beta2 * self.v + (1 - self.beta2) * (grad ** 2)  # accumulate squared gradients

        # bias correction
        m_hat = self.m / (1 - self.beta1 ** self.t)
        v_hat = self.v / (1 - self.beta2 ** self.t)

        W_new = W - self.lr / (np.sqrt(v_hat) + self.eps) * m_hat
        self.path.append(W_new.copy())
        return W_new

class AdamW:
    def __init__(self, lr=0.3, beta1=0.9, beta2=0.999, eps=1e-8, weight_decay=0.01):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        self.weight_decay = weight_decay
        self.m = np.zeros(2)
        self.v = np.zeros(2)
        self.t = 0
        self.path = []

    def step(self, W, grad):
        self.t += 1
        self.m = self.beta1 * self.m + (1 - self.beta1) * grad
        self.v = self.beta2 * self.v + (1 - self.beta2) * (grad ** 2)

        m_hat = self.m / (1 - self.beta1 ** self.t)
        v_hat = self.v / (1 - self.beta2 ** self.t)

        # Decoupled weight decay: applied directly to parameters, not scaled by adaptive learning rate
        W_new = W - self.lr * self.weight_decay * W
        W_new = W_new - self.lr / (np.sqrt(v_hat) + self.eps) * m_hat
        self.path.append(W_new.copy())
        return W_new

class Muon:
    """Muon optimizer - accumulates momentum then orthogonalizes the update matrix via Newton-Schulz iteration"""
    def __init__(self, lr=0.3, momentum=0.9, ns_steps=5):
        self.lr = lr
        self.momentum = momentum
        self.ns_steps = ns_steps
        self.B = np.zeros((2, 1))  # momentum buffer (matrix form)
        self.path = []

    def _newtonschulz(self, G):
        """Newton-Schulz iteration for approximate orthogonalization (G ≈ U S V^T → U V^T)"""
        a, b, c = (3.4445, -4.7750, 2.0315)  # optimized coefficients for quintic polynomial
        X = G / (np.linalg.norm(G) + 1e-7)
        if G.shape[0] > G.shape[1]:
            X = X.T
        for _ in range(self.ns_steps):
            A = X @ X.T
            B_mat = b * A + c * A @ A
            X = a * X + B_mat @ X
        if G.shape[0] > G.shape[1]:
            X = X.T
        return X

    def step(self, W, grad):
        grad_mat = grad.reshape(-1, 1)
        self.B = self.momentum * self.B + grad_mat  # momentum accumulation
        O = self._newtonschulz(self.B)  # Newton-Schulz orthogonalization
        W_new = W - self.lr * O.flatten()
        self.path.append(W_new.copy())
        return W_new

# Run experiment
W_init = np.array([5.0, 5.0])  # starting point
n_iterations = 50

optimizers = {
    'SGD': SGD(lr=0.15),
    'Momentum': Momentum(lr=0.05, momentum=0.9),
    'NAG': NAG(lr=0.05, momentum=0.9),
    'AdaGrad': AdaGrad(lr=1.0),
    'RMSprop': RMSprop(lr=0.3, gamma=0.9),
    'Adam': Adam(lr=0.3),
    'AdamW': AdamW(lr=0.3, weight_decay=0.01),
    'Muon': Muon(lr=0.3, momentum=0.9)
}

results = {}
for name, opt in optimizers.items():
    W = W_init.copy()
    losses = []

    for t in range(n_iterations):
        loss = loss_function(W)
        losses.append(loss)
        grad = gradient(W)

        # NAG requires the gradient function, other optimizers receive gradient values
        if name == 'NAG':
            W = opt.step(W, gradient)
        else:
            W = opt.step(W, grad)

    results[name] = {
        'path': np.array(opt.path),
        'losses': losses,
        'final_W': W,
        'final_loss': loss_function(W)
    }

    print(f"{name:10s}: final position ({W[0]:.4f}, {W[1]:.4f}), final loss {loss_function(W):.6f}")

print()

# Visualization
fig, axes = plt.subplots(2, 2, figsize=(14, 12))

colors = {'SGD': '#e74c3c', 'Momentum': '#3498db', 'NAG': '#e67e22',
          'AdaGrad': '#f39c12', 'RMSprop': '#9b59b6', 'Adam': '#2ecc71', 'AdamW': '#1abc9c',
          'Muon': '#e91e63'}

# Plot 1: parameter paths
ax1 = axes[0, 0]
W1_range = np.linspace(-6, 6, 100)
W2_range = np.linspace(-6, 6, 100)
W1_grid, W2_grid = np.meshgrid(W1_range, W2_range)
L_grid = 0.5 * (W1_grid**2 + 10 * W2_grid**2)

ax1.contour(W1_grid, W2_grid, L_grid, levels=[1, 5, 10, 25, 50, 100],
           colors='gray', alpha=0.5, linewidths=0.5)
ax1.contourf(W1_grid, W2_grid, L_grid, levels=[0, 1, 5, 10, 25, 50, 100, 200],
             cmap='Blues', alpha=0.3)

for name, result in results.items():
    path = result['path']
    ax1.plot(path[:, 0], path[:, 1], 'o-', color=colors[name],
             linewidth=2, markersize=3, alpha=0.7, label=name)

ax1.plot(W_init[0], W_init[1], 'ko', markersize=10, label='Start')
ax1.plot(0, 0, 'k*', markersize=15, label='Minimum')
ax1.set_xlabel('W1', fontsize=11)
ax1.set_ylabel('W2', fontsize=11)
ax1.set_title('Parameter Path Comparison', fontsize=12)
ax1.legend(loc='upper right')
ax1.grid(True, alpha=0.3)
ax1.set_xlim(-6, 6)
ax1.set_ylim(-6, 6)

# Plot 2: loss curves
ax2 = axes[0, 1]
for name, result in results.items():
    ax2.plot(result['losses'], color=colors[name], linewidth=2, label=name)

ax2.set_xlabel('Iteration', fontsize=11)
ax2.set_ylabel('Loss', fontsize=11)
ax2.set_title('Loss Curve', fontsize=12)
ax2.legend()
ax2.grid(True, alpha=0.3)
ax2.set_yscale('log')

# Plot 3: effective learning rate (W1 direction) - adaptive optimizers only
ax3 = axes[1, 0]
adaptive_optimizers = ['AdaGrad', 'RMSprop', 'Adam', 'AdamW']

for name, result in results.items():
    if name in adaptive_optimizers:
        path = result['path']
        lr_eff = []
        for i in range(len(path) - 1):
            W1_change = path[i+1, 0] - path[i, 0]
            W1_grad = path[i, 0]  # grad_W1 ≈ W1
            lr_eff.append(np.abs(W1_change) / np.abs(W1_grad + 1e-8))
        ax3.plot(lr_eff[:min(30, len(lr_eff))], color=colors[name], linewidth=2, label=name, alpha=0.7)

ax3.set_xlabel('Iteration', fontsize=11)
ax3.set_ylabel('Effective Learning Rate (W1)', fontsize=11)
ax3.set_title('Adaptive Learning Rate', fontsize=12)
ax3.legend()
ax3.grid(True, alpha=0.3)

# Plot 4: convergence speed comparison (loss decrease rate)
ax4 = axes[1, 1]

for name, result in results.items():
    losses = result['losses']
    loss_decrease = [losses[i] - losses[i+1] for i in range(len(losses)-1)]
    ax4.plot(loss_decrease[:min(30, len(loss_decrease))], color=colors[name], linewidth=2, label=name, alpha=0.7)

ax4.set_xlabel('Iteration', fontsize=11)
ax4.set_ylabel('Loss Decrease Per Step', fontsize=11)
ax4.set_title('Convergence Speed Comparison', fontsize=12)
ax4.legend()
ax4.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()
plt.close()
Click Run to execute code. Click the code area to edit.

Summary

This chapter introduced the principles and applications of adaptive optimizers, demonstrating how the idea of assigning different learning rates to each parameter improves optimization efficiency, and presented Muon, a new optimizer that leverages matrix geometric structure. At this point, the content on neural network optimization is complete. We have mastered the principles and applications of gradient descent, momentum, NAG, adaptive optimizers, and Muon, and understand how to select and tune optimization algorithms. The next chapter moves into neural network stability, covering weight initialization, Dropout, batch normalization, and gradient problem diagnosis to address training stability issues.

Exercises

  1. Analyze why AdaGrad's learning rate decreases monotonically. Assume the gradient is constant at ggg, derive the effective learning rate ηeff=ηt⋅g2\eta_{eff} = \frac{\eta}{\sqrt{t \cdot g^2}}ηeff​=t⋅g2​η​ after ttt steps. What is the impact of this property?

    Reference Answer

    AdaGrad Learning Rate Decay Derivation:

    AdaGrad accumulates squared gradients:

    Gt=∑i=1tgi2G_t = \sum_{i=1}^{t} g_i^2Gt​=i=1∑t​gi2​

    Assuming a constant gradient ggg (gi=gg_i = ggi​=g):

    Gt=∑i=1tg2=t⋅g2G_t = \sum_{i=1}^{t} g^2 = t \cdot g^2Gt​=i=1∑t​g2=t⋅g2

    Effective learning rate:

    ηeff=ηGt+ϵ≈ηt⋅g2=ηgt\eta_{eff} = \frac{\eta}{\sqrt{G_t + \epsilon}} \approx \frac{\eta}{\sqrt{t \cdot g^2}} = \frac{\eta}{g \sqrt{t}}ηeff​=Gt​+ϵ​η​≈t⋅g2​η​=gt​η​

    The effective learning rate is inversely proportional to t\sqrt{t}t​, monotonically decreasing with the number of iterations.

    Impact Analysis:

    1. Fast initial convergence: Early in training, ttt is small, ηeff\eta_{eff}ηeff​ is large, parameters update quickly.

    2. Slow later convergence: Later in training, ttt is large, ηeff\eta_{eff}ηeff​ is small, parameter updates become extremely slow.

    3. Long-term training stagnation: When ttt is very large (e.g., t=106t = 10^6t=106), ηeff≈ηg⋅1000\eta_{eff} \approx \frac{\eta}{g \cdot 1000}ηeff​≈g⋅1000η​, the learning rate is minuscule, and training nearly stops.

    Numerical Example: Let η=0.1\eta = 0.1η=0.1, g=1g = 1g=1:

    tttηeff\eta_{eff}ηeff​Parameter Update Magnitude
    10.1Large
    1000.01Medium
    100000.001Small
    10610^61060.0001Very small

    Conclusion:

    AdaGrad's monotonically decreasing learning rate leads to:

    • Suitable for short-term training or sparse gradient problems (frequently updated parameters receive small learning rates, sparse parameters receive large learning rates)
    • Unsuitable for long-term training: learning rate decays too early, training stagnates in later stages
    • RMSprop solves this problem through a moving average

    Improvement Direction:

    RMSprop uses an exponentially weighted moving average:

    Et=γEt−1+(1−γ)g2E_t = \gamma E_{t-1} + (1-\gamma) g^2Et​=γEt−1​+(1−γ)g2

    With a constant gradient ggg:

    Et=(1−γ)g2∑i=0t−1γi=(1−γ)g21−γt1−γ≈g2E_t = (1-\gamma) g^2 \sum_{i=0}^{t-1} \gamma^i = (1-\gamma) g^2 \frac{1-\gamma^t}{1-\gamma} \approx g^2Et​=(1−γ)g2i=0∑t−1​γi=(1−γ)g21−γ1−γt​≈g2

    EtE_tEt​ converges to g2g^2g2 (a stable value) rather than growing indefinitely. The effective learning rate ηeff=ηg2=ηg\eta_{eff} = \frac{\eta}{\sqrt{g^2}} = \frac{\eta}{g}ηeff​=g2​η​=gη​ remains stable.

    Summary: AdaGrad's monotonically decreasing learning rate stems from the cumulative growth of squared gradient accumulation. This property makes AdaGrad suitable for short-term training and sparse gradients, but long-term training will stagnate. RMSprop uses a moving average to avoid accumulation, keeping the learning rate stable and suitable for long-term training.

  2. Explain the necessity of Adam's bias correction. Let m0=0\mathbf{m}_0 = 0m0​=0, β1=0.9\beta_1 = 0.9β1​=0.9, gradient ∇L1=10\nabla L_1 = 10∇L1​=10. Compute the uncorrected m1\mathbf{m}_1m1​ and the corrected m^1\hat{\mathbf{m}}_1m^1​, and analyze the difference.

    Reference Answer

    Bias Correction Calculation:

    Adam first moment estimate:

    mt=β1mt−1+(1−β1)∇Ltm_t = \beta_1 m_{t-1} + (1-\beta_1) \nabla L_tmt​=β1​mt−1​+(1−β1​)∇Lt​

    Let β1=0.9\beta_1 = 0.9β1​=0.9, m0=0m_0 = 0m0​=0, ∇L1=10\nabla L_1 = 10∇L1​=10:

    Uncorrected:

    m1=0.9⋅0+0.1⋅10=1m_1 = 0.9 \cdot 0 + 0.1 \cdot 10 = 1m1​=0.9⋅0+0.1⋅10=1

    Corrected:

    m^1=m11−β11=11−0.9=10.1=10\hat{m}_1 = \frac{m_1}{1 - \beta_1^1} = \frac{1}{1 - 0.9} = \frac{1}{0.1} = 10m^1​=1−β11​m1​​=1−0.91​=0.11​=10

    Difference Analysis:

    • Uncorrected m1=1m_1 = 1m1​=1 (biased toward zero)
    • Corrected m^1=10\hat{m}_1 = 10m^1​=10 (equal to the actual gradient)
    • Correction factor 11−β1t=10\frac{1}{1-\beta_1^t} = 101−β1t​1​=10 amplifies m1m_1m1​

    Cause of Bias:

    Adam initializes m0=0m_0 = 0m0​=0, and the first moment estimate is a weighted average of historical gradients:

    mt=(1−β1)∑i=1tβ1t−i∇Lim_t = (1-\beta_1) \sum_{i=1}^{t} \beta_1^{t-i} \nabla L_imt​=(1−β1​)i=1∑t​β1t−i​∇Li​

    Sum of weights:

    ∑i=1t(1−β1)β1t−i=(1−β1)1−β1t1−β1=1−β1t\sum_{i=1}^{t} (1-\beta_1) \beta_1^{t-i} = (1-\beta_1) \frac{1-\beta_1^t}{1-\beta_1} = 1 - \beta_1^ti=1∑t​(1−β1​)β1t−i​=(1−β1​)1−β1​1−β1t​​=1−β1t​

    When ttt is small (early training), the weight sum 1−β1t<11 - \beta_1^t < 11−β1t​<1:

    • t=1t=1t=1: weight sum =0.1= 0.1=0.1
    • t=2t=2t=2: weight sum =0.19= 0.19=0.19
    • t=10t=10t=10: weight sum =0.65= 0.65=0.65

    The weight sum is less than 1, so the estimate is biased toward zero (because the initialization m0=0m_0 = 0m0​=0 occupies the missing weight).

    Correction Principle:

    Bias correction counteracts the initialization bias:

    m^t=mt1−β1t\hat{m}_t = \frac{m_t}{1 - \beta_1^t}m^t​=1−β1t​mt​​

    Multiplying the estimate by 11−β1t\frac{1}{1-\beta_1^t}1−β1t​1​ compensates for the missing weight.

    When ttt is large (late training), β1t→0\beta_1^t \to 0β1t​→0:

    • t=100t=100t=100: β1100≈0\beta_1^{100} \approx 0β1100​≈0
    • Correction factor 11−β1100≈1\frac{1}{1-\beta_1^{100}} \approx 11−β1100​1​≈1
    • m^100≈m100\hat{m}_{100} \approx m_{100}m^100​≈m100​

    The correction effect disappears.

    Numerical Example: Assuming a constant gradient of 10:

    tttmtm_tmt​ (uncorrected)m^t\hat{m}_tm^t​ (corrected)Ratio
    111010x
    54.16.91.7x
    106.5101.5x
    1009.99101x

    Conclusion:

    The necessity of Adam's bias correction:

    1. Early training (small ttt): initialization m0=0m_0=0m0​=0 biases the estimate toward zero; correction amplifies the estimate, offsetting the bias.
    2. Late training (large ttt): weight sum approaches 1, bias disappears, correction effect diminishes.
    3. Bias correction ensures accurate gradient estimation in early training, avoiding the cold-start problem.

    Practical Significance:

    Without bias correction, Adam's learning rate in early training could be too small (because mtm_tmt​ is biased toward zero), slowing parameter updates. Bias correction normalizes the early learning rate, accelerating convergence.

    This is why Adam's bias correction is a critical design — it solves the cold-start problem caused by initialization bias.

  3. Explain why AdamW's weight decay effect is more stable than Adam's. Analyze the problem of L2 regularization gradients being scaled by the adaptive learning rate in Adam.

    Reference Answer

    Weight Decay Implementation Problem in Adam:

    L2 regularization adds a penalty term to the loss function:

    Ltotal=Ldata+λ∣∣W∣∣2L_{total} = L_{data} + \lambda ||\mathbf{W}||^2Ltotal​=Ldata​+λ∣∣W∣∣2

    The gradient becomes:

    ∇Ltotal=∇Ldata+2λW\nabla L_{total} = \nabla L_{data} + 2\lambda \mathbf{W}∇Ltotal​=∇Ldata​+2λW

    Adam accumulates the gradient into the first moment mtm_tmt​ and second moment vtv_tvt​:

    mt=β1mt−1+(1−β1)(∇Ldata+2λW)m_t = \beta_1 m_{t-1} + (1-\beta_1)(\nabla L_{data} + 2\lambda \mathbf{W})mt​=β1​mt−1​+(1−β1​)(∇Ldata​+2λW)
    vt=β2vt−1+(1−β2)(∇Ldata+2λW)2v_t = \beta_2 v_{t-1} + (1-\beta_2)(\nabla L_{data} + 2\lambda \mathbf{W})^2vt​=β2​vt−1​+(1−β2​)(∇Ldata​+2λW)2

    Parameter update:

    ΔW=−ηv^t+ϵ⋅m^t\Delta \mathbf{W} = -\frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \cdot \hat{m}_tΔW=−v^t​​+ϵη​⋅m^t​

    The weight decay term 2λW2\lambda \mathbf{W}2λW is included in m^t\hat{m}_tm^t​ and is scaled by the adaptive learning rate ηv^t+ϵ\frac{\eta}{\sqrt{\hat{v}_t} + \epsilon}v^t​​+ϵη​.

    Problem Analysis:

    When a parameter has an active gradient history (v^t\hat{v}_tv^t​ is large):

    • Adaptive learning rate ηv^t+ϵ\frac{\eta}{\sqrt{\hat{v}_t} + \epsilon}v^t​​+ϵη​ is small
    • Weight decay term 2λW2\lambda \mathbf{W}2λW is shrunk, regularization effect weakens

    When a parameter has a sparse gradient history (v^t\hat{v}_tv^t​ is small):

    • Adaptive learning rate ηv^t+ϵ\frac{\eta}{\sqrt{\hat{v}_t} + \epsilon}v^t​​+ϵη​ is large
    • Weight decay term 2λW2\lambda \mathbf{W}2λW is amplified, regularization effect strengthens

    This contradicts the original design intent of L2 regularization to "uniformly decay all weights." L2 regularization should decay all weights equally (multiplying each step by 1−ηλ1 - \eta \lambda1−ηλ), but Adam makes the weight decay effect depend on the parameter's gradient history.

    Numerical Example:

    Let η=0.001\eta = 0.001η=0.001, λ=0.01\lambda = 0.01λ=0.01, ϵ=10−8\epsilon = 10^{-8}ϵ=10−8:

    Parameterv^t\hat{v}_tv^t​Adaptive Learning RateWeight Decay Magnitude
    Active parameter1000.00110=10−4\frac{0.001}{10} = 10^{-4}100.001​=10−410−4⋅0.02≈010^{-4} \cdot 0.02 \approx 010−4⋅0.02≈0
    Sparse parameter10.0011=10−3\frac{0.001}{1} = 10^{-3}10.001​=10−310−3⋅0.02=2×10−510^{-3} \cdot 0.02 = 2 \times 10^{-5}10−3⋅0.02=2×10−5

    Weight decay for active parameters is nearly zero, while it is larger for sparse parameters. The regularization effect is uneven.

    AdamW's Decoupled Design:

    AdamW separates weight decay from gradient updates:

    Wt+1=Wt−ηλWt−ηv^t+ϵ⋅m^t\mathbf{W}_{t+1} = \mathbf{W}_t - \eta \lambda \mathbf{W}_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \cdot \hat{m}_tWt+1​=Wt​−ηλWt​−v^t​​+ϵη​⋅m^t​

    The weight decay term −ηλWt-\eta \lambda \mathbf{W}_t−ηλWt​ is applied directly to the parameters, not scaled by the adaptive learning rate.

    Each step multiplies the weights by (1−ηλ)(1 - \eta \lambda)(1−ηλ):

    Wt+1=Wt(1−ηλ)−ηv^t+ϵ⋅m^t\mathbf{W}_{t+1} = \mathbf{W}_t(1 - \eta \lambda) - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \cdot \hat{m}_tWt+1​=Wt​(1−ηλ)−v^t​​+ϵη​⋅m^t​

    All weights decay equally, consistent with SGD's behavior.

    AdamW vs Adam Weight Decay Comparison:

    PropertyAdamAdamW
    Weight decay implementationAdds L2 term to gradientDirectly decays weights
    Decay effectAffected by v^t\hat{v}_tv^t​ (uneven)Stable and uniform
    Hyperparameter couplingη\etaη and λ\lambdaλ coupledDecoupled and independent

    Conclusion:

    Adam's weight decay problem arises from L2 regularization gradients being scaled by the adaptive learning rate. The decay effect weakens for active parameters and strengthens for sparse parameters, making regularization uneven.

    AdamW decouples weight decay, applying it directly to parameters, resulting in a stable and uniform decay effect. Experiments show that AdamW generalizes better than Adam, especially on tasks requiring strong regularization.

    Recommendation: Prefer AdamW over Adam for more stable and reliable weight decay.

Words: 6,958
Updated 2026-07-28
Last Updated:
Contributors: icyfenix, Claude
Prev
Gradient Descent
Next
Weight Initialization