Skip to content

deepbox/optim

Optimizers

SGD, Adam and its variants, RMSprop, Adagrad, LBFGS, LARS, LAMB, Lion and other optimizers, with parameter groups and state dicts.

  • Optimizers

Overview

  • Build an optimizer with new Adam(model.parameters(), { lr }), or pass groups, [{ params, lr }], for per-group options. Options are camelCase and flat. Adam takes beta1 and beta2 rather than a betas tuple, plus weightDecay, amsgrad and maximize.
  • Defaults: SGD uses lr: 0.01 with no momentum, Adam and AdamW use lr: 0.001, and AdamW uses weightDecay: 0.01.
  • step() reads the gradients that backward() left behind, and gradients accumulate, so call optimizer.zeroGrad() before each backward(). Frozen parameters and parameters without a gradient are skipped at step(), as in PyTorch. In 1.0.0 the second case threw NotFittedError. A failed step, for example one with a non-finite gradient, changes neither the parameters nor the optimizer state.
  • stateDict() returns a copy that does not alias live state, and loadStateDict() applies it atomically. LBFGS takes a closure, step(closure), that re-evaluates the model and returns the loss. It supports lineSearchFn: "strong_wolfe".
  • When the parameters live on a device, the optimizers keep their state there. LBFGS and SparseAdam are the exceptions and throw DeviceError for device parameters.

Types

1

ParamGroup

type

Represents a group of parameters with optional per-group hyperparameters.

Options left out of a group fall back to the optimizer defaults.

type ParamGroup<Options extends Record<string, unknown>> = {    readonly params: Iterable<GradTensor>;} & Partial<Options>;

Classes

17

Optimizer

extends Record<string, unknown>, State extends Record<string, unknown>>

Abstract base class for all optimizers.

Concrete optimizers (SGD, Adam, ...) extend this class and implement step(). The base class owns the parameter groups, the per-parameter state map, gradient zeroing and checkpointing through stateDict() / loadStateDict().

Parameters are split into groups, and every group carries its own options (for example a different learning rate per layer). Options a group does not set are taken from the optimizer defaults, and every group is range checked when it is added and again at each step().

A parameter with requiresGrad = false (for example one frozen with Module.freezeParameters()) stays in its group but is skipped by step(). A trainable parameter whose gradient is null (it took no part in the loss, or no backward() has run yet) is skipped as well, as in PyTorch, and gets no optimizer state. A failed step() (a non-finite gradient, an invalid option) changes neither the parameters nor the optimizer state.

Example
import { SGD } from 'deepbox/optim'; const optimizer = new SGD(model.parameters(), { lr: 0.01 }); // Training loopfor (let epoch = 0; epoch < 100; epoch++) {  optimizer.zeroGrad();  const loss = computeLoss();  loss.backward();  optimizer.step();  console.log(`epoch ${epoch}: loss ${loss.item()}`);}
Example
// Different learning rates per groupconst optimizer = new SGD([  { params: model.layer1.parameters(), lr: 0.01 },  { params: model.layer2.parameters(), lr: 0.001 }], { lr: 0.01 });
Members (11)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<Options>>, defaults: Readonly<Options>)

    Create a new optimizer.

  • paramGroups: Array<{ params: GradTensor[]; options: Options; }>

    Groups of parameters with their associated hyperparameters.

  • lr: number

    Learning rate of the first parameter group, or 0 when the optimizer has no groups or the first group has no numeric lr option.

  • stepCount: number

    Total number of optimization steps performed, including the steps recorded in a state dictionary that was loaded.

  • getLearningRate(groupIdx?: number): number

    Get the learning rate of a parameter group.

  • setLearningRate(lr: number): void

    Set the learning rate of every parameter group.

  • abstract step(closure?: () => number): number | undefined

    Perform a single optimization step (parameter update).

  • zeroGrad(): void

    Reset the gradients of all optimized parameters to zero.

  • addParamGroup(paramGroup: ParamGroup<Options>): void

    Add a parameter group to the optimizer.

  • stateDict(): { stepCount: number; state: { paramId: number; param: GradTensor; state: State; }[]; paramGroups: { params: GradTensor[]; paramIds: number[]; options: Options; }[]; }

    Get a snapshot of the optimizer state for checkpointing.

  • loadStateDict(stateDict: Record<string, unknown>): void

    Load optimizer state from a state dictionary.

AdaDelta

extends Optimizer<AdaDeltaOptions, AdaDeltaState>

AdaDelta optimizer (Zeiler, 2012).

AdaDelta is an extension of Adagrad that avoids its monotonically shrinking learning rate. It keeps exponential moving averages of the squared gradients and of the squared parameter updates, and scales each step by the ratio of their root-mean-squares. The update follows torch.optim.Adadelta:

v = rho * v + (1 - rho) * g^2delta = sqrt(acc + eps) / sqrt(v + eps) * gacc = rho * acc + (1 - rho) * delta^2theta -= lr * delta

When weightDecay is non-zero, weightDecay * theta is added to the gradient (L2 penalty) before the update.

Example
import { AdaDelta } from 'deepbox/optim'; const optimizer = new AdaDelta(model.parameters(), {  lr: 1.0,  rho: 0.9,  eps: 1e-6}); // Training loopfor (let epoch = 0; epoch < numEpochs; epoch++) {  optimizer.zeroGrad();  // ...  optimizer.step();}
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<AdaDeltaOptions>>, options?: { readonly lr?: number; readonly rho?: number; readonly eps?: number; readonly weightDecay?: number; readonly maximize?: boolean; })

    Create a new AdaDelta optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

Adagrad

extends Optimizer<AdagradOptions, AdagradState>

Adagrad (Adaptive Gradient Algorithm) optimizer.

Adagrad adapts the learning rate for each parameter based on the historical sum of squared gradients. Parameters with larger gradients receive smaller effective learning rates, while parameters with smaller gradients receive larger effective learning rates. The update follows torch.optim.Adagrad:

clr = lr / (1 + (t - 1) * lrDecay)sum += g^2theta -= clr * g / (sqrt(sum) + eps)

When weightDecay is non-zero, weightDecay * theta is added to the gradient (L2 penalty) before the update.

Example
import { Adagrad } from 'deepbox/optim'; const optimizer = new Adagrad(model.parameters(), {  lr: 0.01,  eps: 1e-10}); // Training loopfor (let epoch = 0; epoch < numEpochs; epoch++) {  optimizer.zeroGrad();  // ...  optimizer.step();}
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<AdagradOptions>>, options?: { readonly lr?: number; readonly eps?: number; readonly weightDecay?: number; readonly lrDecay?: number; readonly initialAccumulatorValue?: number; readonly maximize?: boolean; })

    Create a new Adagrad optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

Adam

extends Optimizer<AdamOptions, AdamState>

Adam (Adaptive Moment Estimation) optimizer (Kingma and Ba, 2015).

Computes adaptive learning rates for each parameter by maintaining running averages of both the gradients and their squared values. The update follows torch.optim.Adam:

m = beta1 * m + (1 - beta1) * gv = beta2 * v + (1 - beta2) * g^2theta -= (lr / (1 - beta1^t)) * m / (sqrt(v / (1 - beta2^t)) + eps)

When weightDecay is non-zero, weightDecay * theta is added to the gradient (classic L2 penalty); use AdamW for decoupled weight decay. With amsgrad, v is replaced by its running maximum in the denominator.

Example
import { Adam } from 'deepbox/optim'; const optimizer = new Adam(model.parameters(), {  lr: 0.001,  beta1: 0.9,  beta2: 0.999});
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<AdamOptions>>, options?: { readonly lr?: number; readonly beta1?: number; readonly beta2?: number; readonly eps?: number; readonly weightDecay?: number; readonly amsgrad?: boolean; readonly maximize?: boolean; })

    Create a new Adam optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

Adamax

extends Optimizer<AdamaxOptions, AdamaxState>

Adamax optimizer, a variant of Adam based on the infinity norm (Kingma and Ba, 2015).

Often used for embeddings and sparse gradients. The update follows torch.optim.Adamax:

m = beta1 * m + (1 - beta1) * gu = max(beta2 * u, |g| + eps)theta -= (lr / (1 - beta1^t)) * m / u

When weightDecay is non-zero, weightDecay * theta is added to the gradient (L2 penalty) before the update.

Example
import { Adamax } from 'deepbox/optim'; const optimizer = new Adamax(model.parameters(), { lr: 0.002 });
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<AdamaxOptions>>, options?: { readonly lr?: number; readonly beta1?: number; readonly beta2?: number; readonly eps?: number; readonly weightDecay?: number; readonly maximize?: boolean; })

    Create a new Adamax optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

AdamW

extends Optimizer<AdamWOptions, AdamWState>

AdamW (Adam with decoupled weight decay) optimizer (Loshchilov and Hutter, 2019).

AdamW applies weight decay directly to the parameters instead of adding it to the gradient, so the decay is not rescaled by the adaptive denominator. The update follows torch.optim.AdamW:

theta *= 1 - lr * weightDecaym = beta1 * m + (1 - beta1) * gv = beta2 * v + (1 - beta2) * g^2theta -= (lr / (1 - beta1^t)) * m / (sqrt(v / (1 - beta2^t)) + eps)
Example
import { AdamW } from 'deepbox/optim'; const optimizer = new AdamW(model.parameters(), {  lr: 0.001,  weightDecay: 0.01,  // Typical value for AdamW  beta1: 0.9,  beta2: 0.999}); // Training loopfor (let epoch = 0; epoch < numEpochs; epoch++) {  optimizer.zeroGrad();  // ...  optimizer.step();}
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<AdamWOptions>>, options?: { readonly lr?: number; readonly beta1?: number; readonly beta2?: number; readonly eps?: number; readonly weightDecay?: number; readonly amsgrad?: boolean; readonly maximize?: boolean; })

    Create a new AdamW optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

ASGD

extends Optimizer<ASGDOptions, ASGDState>

Averaged Stochastic Gradient Descent (ASGD) optimizer (Polyak and Juditsky, 1992).

Runs SGD with a decaying step size and keeps a running (Polyak-Ruppert) average of the iterates, which often generalizes better than the last iterate. The average starts after t0 steps and is available through ASGD.averagedParameters. The update follows torch.optim.ASGD:

theta = theta * (1 - lambda * eta) - eta * gax += mu * (theta - ax)eta = lr / (1 + lambda * lr * t)^alphamu = 1 / max(1, t - t0)

When weightDecay is non-zero, weightDecay * theta is added to the gradient (L2 penalty) before the update. With maximize, the gradient is negated first.

Example
import { ASGD } from 'deepbox/optim'; const optimizer = new ASGD(model.parameters(), {  lr: 0.01,  t0: 1e6,});
Members (3)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<ASGDOptions>>, options?: { readonly lr?: number; readonly lambda?: number; readonly alpha?: number; readonly t0?: number; readonly weightDecay?: number; readonly maximize?: boolean; })

    Create a new ASGD optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

  • averagedParameters(): Tensor[]

    Get the running average of each parameter (Polyak-Ruppert averaging).

LAMB

extends Optimizer<LAMBOptions, LAMBState>

LAMB (Layer-wise Adaptive Moments optimizer for Batch training) optimizer.

LAMB extends Adam with layer-wise adaptive learning rates, enabling training with very large batch sizes (up to 64K for BERT). It combines the benefits of Adam's per-element adaptivity with LARS-style layer-wise trust ratios. Each parameter tensor is treated as one layer:

r = m_hat / (sqrt(v_hat) + eps) + weightDecay * thetatrust = ||theta|| / ||r||        (1 when either norm is zero)theta -= lr * trust * r

where m_hat and v_hat are the bias-corrected Adam moment estimates.

Reference: "Large Batch Optimization for Deep Learning: Training BERT in 76 Minutes" (You et al., 2019)

Example
import { LAMB } from 'deepbox/optim'; const optimizer = new LAMB(model.parameters(), {  lr: 0.001,  beta1: 0.9,  beta2: 0.999,  weightDecay: 0.01,}); for (let epoch = 0; epoch < numEpochs; epoch++) {  optimizer.zeroGrad();  // ...  optimizer.step();}
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<LAMBOptions>>, options?: { readonly lr?: number; readonly beta1?: number; readonly beta2?: number; readonly eps?: number; readonly weightDecay?: number; })

    Create a new LAMB optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

LARS

extends Optimizer<LARSOptions, LARSState>

LARS (Layer-wise Adaptive Rate Scaling) optimizer.

LARS scales the learning rate per layer based on the ratio of the parameter norm to the gradient norm. This allows training with very large batch sizes (up to 32K) without divergence. Each parameter tensor is treated as one layer:

localLr = eta * ||theta|| / (||g|| + weightDecay * ||theta|| + eps)   (1 when either norm is zero)m = momentum * m + lr * localLr * (g + weightDecay * theta)theta -= m

Reference: "Large Batch Training of Convolutional Networks" (You et al., 2017)

Example
import { LARS } from 'deepbox/optim'; const optimizer = new LARS(model.parameters(), {  lr: 0.1,  momentum: 0.9,  weightDecay: 1e-4,  eta: 0.001,}); for (let epoch = 0; epoch < numEpochs; epoch++) {  optimizer.zeroGrad();  // ...  optimizer.step();}
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<LARSOptions>>, options?: { readonly lr?: number; readonly momentum?: number; readonly weightDecay?: number; readonly eta?: number; readonly eps?: number; })

    Create a new LARS optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

LBFGS

extends Optimizer<LBFGSOptions, LBFGSState>

L-BFGS (Limited-memory Broyden–Fletcher–Goldfarb–Shanno) optimizer.

A quasi-Newton method that approximates the inverse Hessian using a limited history of past updates. Unlike first-order methods (Adam, SGD), LBFGS can converge much faster for smooth, deterministic objectives.

Important: LBFGS requires a closure that reevaluates the model and returns the loss. This is because LBFGS may evaluate the function multiple times per step. All parameters are treated as one flat vector, so it uses a single set of hyperparameters: parameter groups with differing options are rejected.

Each step(closure) runs up to maxIter L-BFGS iterations. The step length is chosen by one of two line searches: - lineSearchFn: null (default): the initial step lr is halved (at most 10 times) until the loss decreases. The first step is scaled by min(1, 1 / ||g||_1). maxEval is checked between iterations, so one line search can exceed it by up to nine evaluations. - lineSearchFn: "strong_wolfe": a line search satisfying the strong Wolfe conditions, as in torch.optim.LBFGS. This is the better choice for non-quadratic objectives.

LBFGS only supports float32/float64 parameters on the host; device tensors are rejected with a DeviceError.

Example
import { LBFGS } from 'deepbox/optim'; const optimizer = new LBFGS(model.parameters(), { lr: 1 }); for (let epoch = 0; epoch < numEpochs; epoch++) {  const closure = () => {    optimizer.zeroGrad();    const output = model.forward(input);    const loss = criterion(output, target);    loss.backward();    return loss.item();  };  optimizer.step(closure);}
Members (4)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<LBFGSOptions>>, options?: { readonly lr?: number; readonly maxIter?: number; readonly maxEval?: number; readonly toleranceGrad?: number; readonly toleranceChange?: number; readonly historySize?: number; readonly lineSearchFn?: "strong_wolfe" | null; })

    Create a new LBFGS optimizer.

  • stateDict(): { lbfgs: { stepCount: number; sHistory: Float64Array<ArrayBuffer>[]; yHistory: Float64Array<ArrayBuffer>[]; rhoHistory: number[]; prevFlatGrad: Float64Array<ArrayBuffer> | null; prevFlatParams: Float64Array<ArrayBuffer> | null; }; stepCount: number; state: { paramId: number; param: GradTensor; state: LBFG…

    Snapshot of the optimizer, including the L-BFGS curvature history so that a resumed run continues with the same Hessian approximation.

  • loadStateDict(stateDict: Record<string, unknown>): void

    Restore optimizer state saved by LBFGS.stateDict.

  • step(closure?: () => number): number | undefined

    Run up to maxIter L-BFGS iterations.

Lion

extends Optimizer<LionOptions, LionState>

Lion (EvoLved Sign Momentum) optimizer.

Introduced by Chen et al. (2023) in "Symbolic Discovery of Optimization Algorithms". Lion is a simpler, more memory-efficient alternative to Adam that keeps a single momentum buffer and applies sign-based updates, so every element moves by the same magnitude lr regardless of its gradient scale.

Update rule (per parameter):

u = sign(beta1 * m + (1 - beta1) * g)theta -= lr * (u + weightDecay * theta)m = beta2 * m + (1 - beta2) * g

Weight decay is decoupled, as in AdamW. Lion typically needs a 3-10x smaller learning rate than Adam (suggested: 1e-4 to 3e-4, versus Adam's 1e-3) and a correspondingly larger weight decay.

Example
import { Lion } from 'deepbox/optim'; const optimizer = new Lion(model.parameters(), {  lr: 3e-4,  beta1: 0.9,  beta2: 0.99});
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<LionOptions>>, options?: { readonly lr?: number; readonly beta1?: number; readonly beta2?: number; readonly weightDecay?: number; readonly maximize?: boolean; })

    Create a new Lion optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

Nadam

extends Optimizer<NadamOptions, NadamState>

Nadam (Nesterov-accelerated Adam) optimizer (Dozat, 2016).

Combines Adam's adaptive learning rates with Nesterov momentum: the first moment estimate is replaced by a look-ahead blend of the next momentum estimate and the current gradient. The momentum coefficient follows the schedule mu_t = beta1 * (1 - 0.5 * 0.96^(t * momentumDecay)) used by torch.optim.NAdam.

By default weightDecay is an L2 penalty added to the gradient. With decoupledWeightDecay: true the parameters are instead multiplied by 1 - lr * weightDecay before the update (NAdamW).

Example
import { Nadam } from 'deepbox/optim'; const optimizer = new Nadam(model.parameters(), {  lr: 0.002,  beta1: 0.9,  beta2: 0.999}); // Training loopfor (let epoch = 0; epoch < numEpochs; epoch++) {  optimizer.zeroGrad();  // ...  optimizer.step();}
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<NadamOptions>>, options?: { readonly lr?: number; readonly beta1?: number; readonly beta2?: number; readonly eps?: number; readonly weightDecay?: number; readonly momentumDecay?: number; readonly decoupledWeightDecay?: boolean; readonly maximize?: boo…

    Create a new Nadam optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

RAdam

extends Optimizer<RAdamOptions, RAdamState>

RAdam (Rectified Adam) optimizer.

Fixes Adam's early training variance issue by computing a variance rectification term. When the variance is tractable (high enough SMA), uses the adaptive learning rate; otherwise falls back to SGD with momentum. The update follows PyTorch's torch.optim.RAdam:

rhoInf = 2 / (1 - beta2) - 1rhoT   = rhoInf - 2 t beta2^t / (1 - beta2^t)rhoT > 5:  param -= lr * rect * sqrt(1 - beta2^t) * mHat / (sqrt(v) + eps)otherwise: param -= lr * mHat                      (mHat = m / (1 - beta1^t))

With the default beta2 = 0.999 the first five steps use the SGD-with-momentum branch.

Example
import { RAdam } from 'deepbox/optim'; const optimizer = new RAdam(model.parameters(), { lr: 0.001 });
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<RAdamOptions>>, options?: { readonly lr?: number; readonly beta1?: number; readonly beta2?: number; readonly eps?: number; readonly weightDecay?: number; readonly decoupledWeightDecay?: boolean; readonly maximize?: boolean; })

    Create a new RAdam optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

RMSprop

extends Optimizer<RMSpropOptions, RMSpropState>

RMSprop (Root Mean Square Propagation) optimizer.

RMSprop adapts the learning rate for each parameter by dividing by a running average of recent gradient magnitudes. This helps with non-stationary objectives and is particularly effective for RNNs.

The update follows PyTorch's torch.optim.RMSprop:

v = alpha * v + (1 - alpha) * g^2avg = centered ? sqrt(v - gAvg^2) + eps : sqrt(v) + eps      (gAvg = EMA of g)buf = momentum * buf + g / avg                               (momentum > 0)param = param - lr * (momentum > 0 ? buf : g / avg)
Example
import { RMSprop } from 'deepbox/optim'; const optimizer = new RMSprop(model.parameters(), {  lr: 0.01,  alpha: 0.99,  momentum: 0.9,  centered: true}); // Training loopfor (let epoch = 0; epoch < numEpochs; epoch++) {  optimizer.zeroGrad();  // ...  optimizer.step();}
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<RMSpropOptions>>, options?: { readonly lr?: number; readonly alpha?: number; readonly eps?: number; readonly weightDecay?: number; readonly momentum?: number; readonly centered?: boolean; readonly maximize?: boolean; })

    Create a new RMSprop optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

Rprop

extends Optimizer<RpropOptions, RpropState>

Rprop (Resilient Backpropagation) optimizer.

Only uses the sign of the gradient, not its magnitude. Step sizes adapt individually per parameter based on sign changes. The update follows PyTorch's torch.optim.Rprop (Rprop- with weight reset on a sign change): for every element, with prev the previous gradient,

g * prev > 0:  step = min(step * etaPlus,  stepMax);  param -= sign(g) * stepg * prev < 0:  step = max(step * etaMinus, stepMin);  param unchanged, prev = 0otherwise:     param -= sign(g) * step

The step sizes start at lr (clamped to [stepMin, stepMax]). Because the learning rate only seeds the step sizes, changing it later (for example with an LR scheduler) does not affect parameters that already have state.

Example
import { Rprop } from 'deepbox/optim'; const optimizer = new Rprop(model.parameters(), {  lr: 0.01,  etaMinus: 0.5,  etaPlus: 1.2,});
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<RpropOptions>>, options?: { readonly lr?: number; readonly etaMinus?: number; readonly etaPlus?: number; readonly stepMin?: number; readonly stepMax?: number; readonly maximize?: boolean; })

    Create a new Rprop optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

SGD

extends Optimizer<SGDOptions, SGDState>

Stochastic Gradient Descent (SGD) optimizer.

Implements vanilla SGD with optional momentum, weight decay, and Nesterov acceleration. The update follows PyTorch's torch.optim.SGD:

d = grad + weightDecay * parambuf = momentum * buf + (1 - dampening) * d      (buf = d on the first step)d = nesterov ? d + momentum * buf : bufparam = param - lr * d

With maximize: true the gradient sign is flipped and the objective is maximized.

Example
import { SGD } from 'deepbox/optim';import { Module } from 'deepbox/nn'; const model: Module = ...;const optimizer = new SGD(model.parameters(), {  lr: 0.01,  momentum: 0.9,  weightDecay: 5e-4,  nesterov: true}); // Training loopfor (let epoch = 0; epoch < numEpochs; epoch++) {  for (const [inputs, targets] of dataLoader) {    optimizer.zeroGrad();    const outputs = model.forward(inputs);    const loss = criterion(outputs, targets);    loss.backward();    optimizer.step();    console.log(`epoch ${epoch}: loss ${loss.item()}`);  }}
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<SGDOptions>>, options?: { readonly lr?: number; readonly momentum?: number; readonly dampening?: number; readonly weightDecay?: number; readonly nesterov?: boolean; readonly maximize?: boolean; })

    Create a new SGD optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

SparseAdam

extends Optimizer<SparseAdamOptions, SparseAdamState>

SparseAdam optimizer, a variant of Adam designed for sparse gradients.

Only updates the moment estimates for gradient indices that are non-zero, making it suited to parameters with sparse gradient updates such as embedding layers. Gradients are dense tensors here, and an exact zero marks an index that was not touched. The update follows PyTorch's torch.optim.SparseAdam. Weight decay is not supported.

Algorithm: For each parameter element where gradient ≠ 0:

m_t = β₁ * m_{t-1} + (1 - β₁) * g_tv_t = β₂ * v_{t-1} + (1 - β₂) * g_t²θ_t = θ_{t-1} - lr * √(1 - β₂^t) / (1 - β₁^t) * m_t / (√v_t + ε)

For indices where gradient = 0, moment estimates and parameters are unchanged. The step counter t advances on every call to step().

Example
import { SparseAdam } from 'deepbox/optim';import { Embedding } from 'deepbox/nn'; const embedding = new Embedding(10000, 128);const optimizer = new SparseAdam(embedding.parameters(), { lr: 0.001 });
Members (2)
  • constructor(params: Iterable<GradTensor> | ReadonlyArray<ParamGroup<SparseAdamOptions>>, options?: { readonly lr?: number; readonly beta1?: number; readonly beta2?: number; readonly eps?: number; readonly maximize?: boolean; })

    Create a new SparseAdam optimizer.

  • step(closure?: () => number): number | undefined

    Perform a single optimization step.

optim-optimizers.ts
import { tensor } from "deepbox/ndarray";import { Linear, mseLoss } from "deepbox/nn";import { AdamW, SGD } from "deepbox/optim"; const layer = new Linear(2, 1);const x = tensor([[1, 2], [3, 4], [5, 6]]);const y = tensor([[5], [11], [17]]); const optimizer = new AdamW(layer.parameters(), { lr: 0.05, weightDecay: 0.01 });for (let step = 0; step < 100; step++) {  optimizer.zeroGrad(); // gradients accumulate otherwise  const loss = mseLoss(layer.forward(x), y);  loss.backward();  optimizer.step();  if (step % 25 === 0) console.log(step, loss.item());} // Parameter groups with their own learning rates.const [weight, bias] = [...layer.parameters()];if (weight && bias) {  const grouped = new SGD([{ params: [weight], lr: 0.1 }, { params: [bias], lr: 0.01 }], { lr: 0.05 });  console.log(grouped.getLearningRate(0), grouped.getLearningRate(1));}