Source code for snn.optimizers.lion

import numpy as np
from .base import Optimizer


[docs] class Lion(Optimizer): """ Lion — EvoLved Sign Momentum. Uses only the *sign* of the update vector, making every parameter receive an update of exactly ±learning_rate per step. This gives Lion 3–10× lower memory usage than Adam (one momentum buffer instead of two) and competitive or better performance on image and language tasks. Update rule:: c_t = β₁ · m_{t-1} + (1 − β₁) · g_t θ_t = θ_{t-1} − lr · (sign(c_t) + λ · θ_{t-1}) m_t = β₂ · m_{t-1} + (1 − β₂) · g_t Note: Lion typically needs a **3–10× smaller learning rate** and **3–10× larger weight decay** than Adam. A good starting point is ``lr=1e-4, weight_decay=1e-2``. Reference: Chen et al. (2023) "Symbolic Discovery of Optimization Algorithms". https://arxiv.org/abs/2302.06675 Parameters ---------- learning_rate : float Step size (default 1e-4). beta_1 : float Coefficient for computing the update direction (default 0.9). beta_2 : float Coefficient for maintaining the momentum buffer (default 0.99). weight_decay : float Decoupled weight decay (default 0.0). """ def __init__(self, learning_rate=1e-4, beta_1=0.9, beta_2=0.99, weight_decay=0.0): super().__init__(learning_rate) self.beta_1 = beta_1 self.beta_2 = beta_2 self.weight_decay = weight_decay self._m = {}
[docs] def apply_gradients(self, params, grads): self._iterations += 1 updates = {} for key, grad in grads.items(): param = params[key] m = self._m.get(key, np.zeros_like(param)) # Interpolated direction c = self.beta_1 * m + (1.0 - self.beta_1) * grad # Weight update: sign of c + weight decay delta = np.sign(c) + self.weight_decay * param new_param = param - self.learning_rate * delta # Update momentum with β₂ (separate from β₁ used above) self._m[key] = self.beta_2 * m + (1.0 - self.beta_2) * grad updates[key] = new_param return updates
[docs] def get_config(self): cfg = super().get_config() cfg.update({ "beta_1": self.beta_1, "beta_2": self.beta_2, "weight_decay": self.weight_decay, }) return cfg