LBFGS
OptimizerLBFGS(params: Iterable[Parameter] | Iterable[dict[str, object]], lr: float = 1.0, max_iter: int = 20, max_eval: int | None = None, tolerance_grad: float = 1e-07, tolerance_change: float = 1e-09, history_size: int = 100, line_search_fn: str | None = None)Limited-memory Broyden–Fletcher–Goldfarb–Shanno (L-BFGS) optimizer.
L-BFGS is a quasi-Newton method that approximates the inverse Hessian
using a limited history of gradient and parameter difference vectors.
One call to step runs up to max_iter iterations. Each
iteration computes a search direction via the two-loop
recursion:
where is the L-BFGS inverse-Hessian approximation built from
the last history_size curvature pairs
:
with the initial diagonal scaling
A pair is stored only when . The parameters
then move to . With
line_search_fn=None the step is fixed,
(on the very first iteration ). With "strong_wolfe",
is found by cubic interpolation so that
with and .
Parameters
paramsiterable of Parameter or iterable of dictlrfloat= 1.01.0). The fixed step without a line search,
and the first trial step of the strong-Wolfe search.max_iterint= 20step call
(default: 20).max_evalint or None= Nonestep call
(default: None, meaning max_iter * 5 // 4).tolerance_gradfloat= 1e-07step stops once
tolerance_grad (default: 1e-7).tolerance_changefloat= 1e-09step stops once the directional derivative, the parameter
change or the change of the loss
falls below this (default: 1e-9).history_sizeint= 100100).line_search_fnstr or None= None"strong_wolfe" or None for a fixed step (default: None).Attributes
param_groupslist of dictdefaultsdictRaises
ValueErrorparams holds more than one parameter group, or an option is
out of range (lr < 0, history_size < 1, an unknown
line_search_fn).Notes
Unlike first-order optimizers, L-BFGS requires a closure argument
in step that clears gradients, computes the loss, and calls
loss.backward(). Without a closure the method raises
ValueError.
The state (the last direction d and step t, the curvature pairs
old_dirs / old_stps with their ro = 1 / yᵀs, H_diag,
prev_flat_grad, prev_loss, n_iter and func_evals) carries
over from one step to the next and round-trips through
state_dict.
L-BFGS is best suited for full-batch or large-batch training where the curvature information is reliable. It is not recommended for stochastic mini-batch training because noisy gradients corrupt the Hessian approximation.
Examples
>>> import lucid
>>> import lucid.nn as nn
>>> import lucid.optim as optim
>>> model = nn.Linear(4, 2)
>>> x = lucid.randn(1, 4)
>>> y = lucid.zeros((1, 2))
>>> criterion = nn.MSELoss()
>>> optimizer = optim.LBFGS(model.parameters(), lr=1.0, max_iter=20)
>>> def closure():
... optimizer.zero_grad()
... loss = criterion(model(x), y)
... loss.backward()
... return loss
>>> loss = optimizer.step(closure) # step returns the closure's first lossUsed by 2
Constructors
1__init__
→None__init__(params: Iterable[Parameter] | Iterable[dict[str, object]], lr: float = 1.0, max_iter: int = 20, max_eval: int | None = None, tolerance_grad: float = 1e-07, tolerance_change: float = 1e-09, history_size: int = 100, line_search_fn: str | None = None)Initialise the LBFGS. See the class docstring for parameter semantics.
Instance methods
3Refuse a second group: L-BFGS optimises one flat vector of parameters.
Raises
ValueErrorRun up to max_iter L-BFGS iterations.
Each iteration takes the two-loop-recursion direction, moves along it
by the fixed step lr or by a strong-Wolfe line search, and stores
the new curvature pair. The loop ends after max_iter iterations,
after max_eval closure evaluations, or when a tolerance_grad /
tolerance_change rule is met.
Parameters
closurecallable= None- Calls
optimizer.zero_grad()to clear stale gradients. - Runs the forward pass and computes the scalar loss.
- Calls
loss.backward()to populate gradients. - Returns the loss tensor.
None raises
ValueError.Returns
TensorThe closure's loss at the parameters step started from
(its first evaluation), as the reference framework returns it.
Raises
ValueErrorclosure is None.Notes
The closure is called up to max_eval times per step
call, always with gradients enabled. Ensure that any side effects
(e.g. batch norm running stats) are handled appropriately if this
matters for your use-case.
Examples
>>> import lucid
>>> import lucid.nn as nn
>>> import lucid.optim as optim
>>> model = nn.Linear(4, 2)
>>> x = lucid.randn(1, 4)
>>> y = lucid.zeros((1, 2))
>>> criterion = nn.MSELoss()
>>> optimizer = optim.LBFGS(model.parameters(), lr=1.0, max_iter=20)
>>> def closure():
... optimizer.zero_grad()
... output = model(x)
... loss = criterion(output, y)
... loss.backward()
... return loss
>>> loss = optimizer.step(closure) # step returns the closure's first lossSet gradients of all parameters to None.
L-BFGS always sets gradients to None regardless of the
set_to_none argument, because the closure passed to
step is responsible for zeroing and recomputing gradients
on each function evaluation.
Parameters
set_to_nonebool= TrueOptimizer
(default: True).Examples
>>> import lucid
>>> import lucid.nn as nn
>>> import lucid.optim as optim
>>> model = nn.Linear(4, 2)
>>> x = lucid.randn(1, 4)
>>> y = lucid.zeros((1, 2))
>>> criterion = nn.MSELoss()
>>> optimizer = optim.LBFGS(model.parameters(), lr=1.0, max_iter=20)
>>> def closure():
... optimizer.zero_grad()
... loss = criterion(model(x), y)
... loss.backward()
... return loss
>>> loss = optimizer.step(closure) # step returns the closure's first loss