Add FTRL-Proximal as a fused TBE optimizer (CUDA + CPU) - #6295
Open
tiankongdeguiji wants to merge 1 commit into
Open
Add FTRL-Proximal as a fused TBE optimizer (CUDA + CPU)#6295tiankongdeguiji wants to merge 1 commit into
tiankongdeguiji wants to merge 1 commit into
Conversation
Adds `OptimType.FTRL`, implementing FTRL-Proximal exactly as TensorFlow's
`ApplyFtrlV2` (with l2_shrinkage = 0) and Alibaba x-deeplearning's
`FtrlUpdater` do, so hyperparameters and checkpoints carry over from either.
Per element, with state accum (sum of squared gradients) and linear:
new_accum = accum + g^2
sigma_new = new_accum ^ (-learning_rate_power)
sigma_old = accum ^ (-learning_rate_power)
linear += g - (sigma_new - sigma_old) / learning_rate * weight
quadratic = sigma_new / learning_rate + 2 * l2_reg
weight = |linear| > l1_reg ? (l1_reg * sgn(linear) - linear) / quadratic : 0
accum = new_accum
learning_rate_power == -0.5 (the default) is special-cased to sqrt, which is
faster and matches x-deeplearning.
State layout mirrors ADAM: momentum1 = accum, momentum2 = linear, both
elementwise (D floats per row). get_optimizer_state() reports them under
TensorFlow's slot names, "accum" and "linear".
Three new TBE constructor knobs, all defaulted so they are no-ops for every
other optimizer: ftrl_learning_rate_power (-0.5), ftrl_l1_reg (0.0) and
ftrl_l2_reg (0.0). They carry the ftrl_ prefix because OptimizerArgs is a flat
namespace shared by all optimizers. weight_decay is deliberately not reused for
the L2 term: FBGEMM's weight_decay is half of a (weight_decay,
weight_decay_mode) pair with six modes and enters as a gradient term, whereas
FTRL's L2 enters the denominator as 2*l2 and honors none of those modes.
Two properties inherent to the algorithm, both documented in the code: FTRL
recomputes the weight from (accum, linear) instead of incrementing it, so a
row's initial value is discarded the first time it is touched; and the update
divides by learning_rate, so the constructor asserts learning_rate > 0 rather
than letting it produce inf/NaN weights.
Support: CUDA, CPU and VBE. Global weight decay is not supported (FTRL's
proximal L2 is not a gradient-space decay, so the two do not compose) and
neither is SSD offloading, though the EmbOptimType metadata arms are filled in
so enabling it later is mechanical.
Tests: test_backward_optimizers_ftrl runs the shared randomized harness over
variable T/D/B/E/L, mixed dims, weighted/unweighted, VBE and all pooling modes
on CPU and CUDA, with a new reference block that recomputes accum, linear and
the post-step weights from the formula above. A deterministic FtrlTest class
covers exact-zero L1 thresholding, the generic powf path agreeing with the
reference, the 2*l2 denominator term, multi-step state accumulation, zero-grad
idempotence and CPU/CUDA parity.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPQnjDECjd8ZEvGF5Hpzpn
Contributor
Author
|
The TorchRec side that consumes this is meta-pytorch/torchrec#4710 — it adds a dense |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
OptimType.FTRL(FTRL-Proximal) as a fused TBE optimizer, with both CUDA and CPU backends.FTRL-Proximal is the standard optimizer for the sparse/wide part of CTR models, and is what Alibaba's x-deeplearning uses for its embedding parameter server. There was no equivalent in FBGEMM, so recsys workloads that rely on it have nothing to migrate onto.
Algorithm
Per element, with state
accum(running sum of squared gradients) andlinear:This is bit-for-bit the same formulation as TensorFlow's
ApplyFtrlV2(withl2_shrinkage = 0) and x-deeplearning'sFtrlUpdater, so checkpoints and hyperparameters carry over from either.learning_rate_power == -0.5(the default) is special-cased tosqrtf/std::sqrt, which is both faster and what x-deeplearning does.Two properties worth calling out, both inherent to the algorithm rather than artifacts of this implementation:
(accum, linear)instead of incrementing it, so a row's initial random value is discarded the first time that row is touched.learning_rate, solearning_rate > 0is required. The TBE constructor asserts this for FTRL rather than letting it produce inf/NaN weights.State layout
momentum1=accum,momentum2=linear, both elementwise (Dfloats per row), the same footprint asADAM.get_optimizer_state()exposes them under TensorFlow's slot names,accumandlinear.Knobs
Three new TBE constructor arguments, all defaulted so they are no-ops for every other optimizer:
ftrl_learning_rate_power-0.5accum; -0.5 gives the classic 1/sqrt scheduleftrl_l1_reg0.0|linear|never exceeds this stays exactly0, which is what makes FTRL sparsify embedding rowsftrl_l2_reg0.0They are
ftrl_-prefixed becauseOptimizerArgsis a flat namespace shared by every optimizer.weight_decayis deliberately not reused forftrl_l2_reg: FBGEMM'sweight_decayis half of a(weight_decay, weight_decay_mode)pair with six modes and enters as a gradient term, whereas FTRL's L2 enters the denominator as2 * l2and honors none of those modes. Overloading it would makeweight_decay_modea silent no-op on FTRL tables.initial_accumulator_valueis intentionally not implemented — no FBGEMM optimizer has a state-init argument today (Adagrad included), so an FTRL-only one would be inconsistent. A genericoptimizer_state_init_valuesTBE argument would be the right way to add it later.Support matrix
EmbOptimTypemetadata arms are filled in, so enabling it later is mechanical)Tests
fbgemm_gpu/test/tbe/training/backward_optimizers_test.py:test_backward_optimizers_ftrl— the shared randomized harness over variable T/D/B/E/L, mixed dims, weighted/unweighted, VBE and all pooling modes, on CPU and CUDA. The new reference block recomputesaccum,linearand the post-step weights from the formula above and compares againstsplit_optimizer_states()/split_embedding_weights().FtrlTest— a deterministic class covering what the randomized harness cannot check cheaply:ftrl_l1_regproducing exactly0.0, the genericpowfpath agreeing with the reference (and differing from the sqrt fast path), the2 * l2_regdenominator term, multi-stepaccum/linearaccumulation, zero-gradient idempotence, and CPU/CUDA parity.🤖 Generated with Claude Code
https://claude.ai/code/session_01FPQnjDECjd8ZEvGF5Hpzpn