Buckets:
| """Core implementation for the STE-quantization reproduction. | |
| Re-implements the quantizer, the macroscopic ODE (Theorem V.3 of | |
| Ichikawa et al., arXiv:2510.10693), and a batched STE simulation of the | |
| underlying linear-quantized regression problem. | |
| Reference paper (verbatim from arXiv abstract): | |
| "Quantized neural network training optimizes a discrete, non-differentiable | |
| objective. The straight-through estimator (STE) enables backpropagation | |
| through surrogate gradients... We theoretically show that in the | |
| high-dimensional limit, STE dynamics converge to a deterministic ordinary | |
| differential equation." | |
| Notation follows the paper: | |
| rho := ||w*||^2 / d (per-component 2nd moment) | |
| sigma2 := noise variance (Var[xi]) | |
| (b, omega, L, Delta, theta, v) describe the uniform quantizer psi. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| from dataclasses import dataclass | |
| import numpy as np | |
| from scipy.stats import norm | |
| # --------------------------------------------------------------------------- | |
| # 1. Quantizer definition | |
| # --------------------------------------------------------------------------- | |
| class Quantizer: | |
| """Uniform mid-tread quantizer as defined in Eq. (1) of the paper. | |
| Levels v_k = -omega + k*Delta, k = 0..L (L + 1 = 2^b - 1 levels) | |
| Step Delta = 2*omega / L | |
| Thresholds theta_k = -omega + (k - 1/2) * Delta, k = 1..L | |
| """ | |
| b: int | |
| omega: float | |
| def __post_init__(self): | |
| assert self.b >= 2, "bit width b>=2 required (paper Eq. defines L=2^b-2)" | |
| L = (1 << self.b) - 2 # L + 1 = 2^b - 1 | |
| Delta = 2.0 * self.omega / L | |
| levels = -self.omega + np.arange(L + 1) * Delta | |
| # thresholds theta_1..theta_L ; (theta_0 = -inf, theta_{L+1} = +inf) | |
| theta = -self.omega + (np.arange(1, L + 1) - 0.5) * Delta | |
| # expose as object attrs bypassing frozen | |
| object.__setattr__(self, "L", L) | |
| object.__setattr__(self, "Delta", Delta) | |
| object.__setattr__(self, "levels", levels) | |
| object.__setattr__(self, "theta", theta) | |
| # --- forward maps --------------------------------------------------- | |
| def psi(self, x: np.ndarray) -> np.ndarray: | |
| """Hard quantizer psi(x), Eq. (1).""" | |
| # psi(x) = -omega + Delta * sum_{k=1..L} Heaviside(x - theta_k) | |
| # = -omega + Delta * (number of thresholds <= x) | |
| out = np.full_like(x, -self.omega, dtype=float) | |
| for k in range(self.L): | |
| out = out + self.Delta * (x >= self.theta[k]).astype(float) | |
| return out | |
| def psi_t(self, x: np.ndarray, T: float) -> np.ndarray: | |
| """Differentiable relaxation psi_T(x), Eq. after (1). | |
| psi_T(x) = -omega + Delta * sum_{k=1..L} Phi((x - theta_k)/T) | |
| As T -> +0, psi_T -> psi (hard quantizer). | |
| For T = 0, falls back to psi(x). | |
| """ | |
| if T <= 0.0: | |
| return self.psi(x) | |
| out = np.full_like(x, -self.omega, dtype=float) | |
| for k in range(self.L): | |
| out = out + self.Delta * norm.cdf((x - self.theta[k]) / T) | |
| return out | |
| # --- moments -------------------------------------------------------- | |
| def sigma2_psi(self) -> float: | |
| """E[psi(X)^2], X ~ N(0, 1). Prop. II.1.""" | |
| L = self.L | |
| v = self.levels | |
| th = self.theta | |
| # boundaries: theta_0 = -inf, theta_{L+1} = +inf | |
| # p_k = Phi(theta_{k+1}) - Phi(theta_k), k = 0..L | |
| cdf = lambda x: 0.0 if x == -np.inf else (1.0 if x == np.inf else norm.cdf(x)) | |
| # use list of bounds | |
| bounds = [-np.inf] + list(th) + [np.inf] | |
| s = 0.0 | |
| for k in range(L + 1): | |
| p_k = cdf(bounds[k + 1]) - cdf(bounds[k]) | |
| s += v[k] ** 2 * p_k | |
| return float(s) | |
| def kappa_psi(self) -> float: | |
| """E[X * psi(X)], X ~ N(0, 1). Prop. II.1.""" | |
| L = self.L | |
| v = self.levels | |
| th = self.theta | |
| # kappa = sum_{k=1..L} (v_k - v_{k-1}) * phi(theta_k) | |
| s = 0.0 | |
| for k in range(L): | |
| s += (v[k + 1] - v[k]) * norm.pdf(th[k]) | |
| return float(s) | |
| def describe(self) -> dict: | |
| return { | |
| "b": self.b, | |
| "omega": self.omega, | |
| "L": self.L, | |
| "Delta": self.Delta, | |
| "levels": self.levels.tolist(), | |
| "theta": self.theta.tolist(), | |
| "sigma2_psi": self.sigma2_psi(), | |
| "kappa_psi": self.kappa_psi(), | |
| } | |
| def identity_quantizer_moments(): | |
| """For the unquantized baseline: psi(x) = x gives kappa=1, sigma2=1.""" | |
| return 1.0, 1.0 | |
| # --------------------------------------------------------------------------- | |
| # 2. Macroscopic state functions (Prop. V.2) | |
| # --------------------------------------------------------------------------- | |
| def macro_m_psi(m: float, s: float, qw: Quantizer, rho: float = 1.0) -> float: | |
| """m_psi(m, s) for w* with E[(w*)^2] = rho. For w* = 1_d (rho=1) the | |
| expectation E_{w*} is trivial: f(1).""" | |
| if rho != 1.0: | |
| raise NotImplementedError("Only w* = 1_d (rho=1) is implemented.") | |
| # m_psi = -omega + Delta * sum_{i=1..L} Phi((m/rho - theta_i)/s) | |
| if s <= 0: | |
| # degenerate: psi(m) for a constant value m | |
| return float(qw.psi(np.array([m]))[0]) | |
| z = (m - qw.theta) / s | |
| return float(-qw.omega + qw.Delta * np.sum(norm.cdf(z))) | |
| def macro_q_psi(m: float, s: float, qw: Quantizer, rho: float = 1.0) -> float: | |
| """q_psi(m, s).""" | |
| if rho != 1.0: | |
| raise NotImplementedError("Only w* = 1_d (rho=1) is implemented.") | |
| if s <= 0: | |
| v = float(qw.psi(np.array([m]))[0]) | |
| return v * v | |
| z = (m - qw.theta) / s | |
| v = qw.levels | |
| # q_psi = v_0^2 + sum_{i=1..L} (v_i^2 - v_{i-1}^2) Phi((m - theta_i)/s) | |
| diffs = np.diff(v ** 2) # length L | |
| return float(v[0] ** 2 + np.sum(diffs * norm.cdf(z))) | |
| def macro_r_psi(m: float, s: float, qw: Quantizer, rho: float = 1.0) -> float: | |
| """r_psi(m, s) = (m/rho) * m_psi + Delta * s * sum_i phi((m/rho - theta_i)/s).""" | |
| if rho != 1.0: | |
| raise NotImplementedError("Only w* = 1_d (rho=1) is implemented.") | |
| m_psi = macro_m_psi(m, s, qw, rho) | |
| if s <= 0: | |
| return float(m * m_psi) | |
| z = (m - qw.theta) / s | |
| return float((m / rho) * m_psi + qw.Delta * s * np.sum(norm.pdf(z))) | |
| def eps_g(m: float, s: float, qw: Quantizer, kappa_x: float, sigma2_x: float, | |
| rho: float = 1.0, sigma2: float = 0.0) -> float: | |
| """Generalization error eps_g = sigma2 + rho + sigma2_x * q_psi - 2 * kappa_x * m_psi.""" | |
| return float( | |
| sigma2 + rho | |
| + sigma2_x * macro_q_psi(m, s, qw, rho) | |
| - 2.0 * kappa_x * macro_m_psi(m, s, qw, rho) | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # 3. Macroscopic ODE (Theorem V.3) | |
| # --------------------------------------------------------------------------- | |
| def ode_rhs(tau, y, qw: Quantizer, kappa_x: float, sigma2_x: float, | |
| eta: float, lam: float, rho: float = 1.0, sigma2: float = 0.0): | |
| """Right-hand side of the macroscopic ODE system. | |
| State y = (m, q). s = sqrt(q - m^2 / rho). | |
| """ | |
| m, q = y | |
| # numerical guard: q >= m^2 / rho always (Cauchy-Schwarz); clip to keep s real. | |
| s2 = q - m * m / rho | |
| if s2 < 1e-12: | |
| s = 1e-6 | |
| else: | |
| s = math.sqrt(s2) | |
| m_p = macro_m_psi(m, s, qw, rho) | |
| r_p = macro_r_psi(m, s, qw, rho) | |
| e = eps_g(m, s, qw, kappa_x, sigma2_x, rho, sigma2) | |
| dmd = -eta * ((sigma2_x + lam) * m_p - kappa_x * rho) | |
| dqd = -2.0 * eta * ((sigma2_x + lam) * r_p - kappa_x * m) + eta * eta * sigma2_x * e | |
| return [dmd, dqd] | |
| # --------------------------------------------------------------------------- | |
| # 4. Input-only quantization: closed form fixed point (Prop. VI.1) | |
| # --------------------------------------------------------------------------- | |
| def input_only_fixed_point(kappa_x: float, sigma2_x: float, eta: float, | |
| lam: float, rho: float = 1.0, sigma2: float = 0.0): | |
| """Closed form (m*, q*) for input-only quantization (Prop. VI.1, Supp. V-A). | |
| m* = rho * kappa_x / (sigma2_x + lambda) | |
| q* = (2 kappa_x^2 + eta sigma2_x ((rho+sigma2)(sigma2_x+lambda) - 2 kappa_x^2)) | |
| / ((sigma2_x+lambda) (2(sigma2_x+lambda) - eta sigma2_x^2)) | |
| """ | |
| m_star = rho * kappa_x / (sigma2_x + lam) | |
| num = (2.0 * kappa_x ** 2 | |
| + eta * sigma2_x * ((rho + sigma2) * (sigma2_x + lam) - 2.0 * kappa_x ** 2)) | |
| den = (sigma2_x + lam) * (2.0 * (sigma2_x + lam) - eta * sigma2_x ** 2) | |
| q_star = num / den | |
| eps_star = rho + sigma2 + sigma2_x * q_star - 2.0 * kappa_x * m_star | |
| return float(m_star), float(q_star), float(eps_star) | |
| def input_only_stability_bound(sigma2_x: float, lam: float) -> float: | |
| """Stability boundary: 0 < eta < 2 (sigma2_x + lambda) / sigma2_x^2.""" | |
| return 2.0 * (sigma2_x + lam) / (sigma2_x ** 2) | |
| # --------------------------------------------------------------------------- | |
| # 5. Small-eta fixed-point prediction for joint weight+input quantization | |
| # (Prop. V.6, Theorem V.8) | |
| # --------------------------------------------------------------------------- | |
| def small_eta_fixed_point_prediction(qw: Quantizer, qx: Quantizer | None, | |
| lam: float, rho: float = 1.0, | |
| sigma2: float = 0.0): | |
| """Closed-form small-eta prediction of eps_g*. | |
| If qx is None, kappa_x = sigma2_x = 1 (unquantized inputs). | |
| Else uses input quantizer moments. | |
| """ | |
| if qx is None: | |
| kappa_x, sigma2_x = 1.0, 1.0 | |
| else: | |
| kappa_x = qx.kappa_psi() | |
| sigma2_x = qx.sigma2_psi() | |
| c = kappa_x * rho / (sigma2_x + lam) # Definition VI.2 | |
| # locate index i* in {0..L-1} such that v_{i*} <= c <= v_{i*+1} | |
| levels = qw.levels | |
| if c <= -qw.omega or c >= qw.omega: | |
| # |c| >= omega case | |
| eps_star = rho + sigma2 - 2.0 * kappa_x * qw.omega + sigma2_x * qw.omega ** 2 | |
| regime = "boundary" | |
| return { | |
| "c": c, | |
| "regime": regime, | |
| "m_star_pred": float(np.sign(c) * qw.omega), | |
| "eps_star_pred": float(eps_star), | |
| "delta2_p1p": 0.0, | |
| "p": None, | |
| "i_star": None, | |
| } | |
| # find i* | |
| i_star = int(np.searchsorted(levels, c, side="right") - 1) | |
| i_star = max(0, min(qw.L - 1, i_star)) | |
| p = (c - levels[i_star]) / qw.Delta | |
| if p <= 0.0 or p >= 1.0: | |
| # p in {0, 1}: logarithmic regime (rare boundary case) | |
| # eps_g* = eps_g0 + o(1/sqrt(log(1/eta))) -- just return eps_g0 | |
| eps0 = (rho + sigma2 - 2.0 * kappa_x * c | |
| + sigma2_x * qw.omega ** 2) # NOTE: actually c^2 if c is on a level | |
| # when p in {0,1}, c == v_{i*} exactly; treat as c^2: | |
| eps0 = rho + sigma2 - 2.0 * kappa_x * c + sigma2_x * (c ** 2) | |
| return { | |
| "c": c, | |
| "regime": "log", | |
| "m_star_pred": float(c), | |
| "eps_star_pred": float(eps0), | |
| "delta2_p1p": 0.0, | |
| "p": float(p), | |
| "i_star": int(i_star), | |
| } | |
| # p in (0, 1): eps_g* = eps_g^(0) + sigma2_x * Delta^2 * p * (1-p) + o(eta) | |
| eps0 = rho + sigma2 - 2.0 * kappa_x * c + sigma2_x * (c ** 2) | |
| correction = sigma2_x * qw.Delta ** 2 * p * (1.0 - p) | |
| return { | |
| "c": c, | |
| "regime": "interior", | |
| "m_star_pred": float(c), | |
| "eps_star_pred": float(eps0 + correction), | |
| "eps_g0_pred": float(eps0), | |
| "delta2_p1p": float(correction), | |
| "p": float(p), | |
| "i_star": int(i_star), | |
| } | |
Xet Storage Details
- Size:
- 11.6 kB
- Xet hash:
- fc82ba002c75fa924d4c78e4cf8f6d65a0042fc42e0131e8db622229812ae8b8
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.