Importance sampling for the stochastic Frank-Wolfe variants - #112
Open
GeoffNN wants to merge 3 commits into
Open
Importance sampling for the stochastic Frank-Wolfe variants#112GeoffNN wants to merge 3 commits into
GeoffNN wants to merge 3 commits into
Conversation
The rate of the SAG-style estimator in minimize_sfw is governed by the constant
Psi(q) = sum_m max_j d_j (1-q_j)^m, where d_j = max_{u,v in C} |a_j^T(u-v)| is
how far datapoint j's prediction can move across the constraint set. Uniform
sampling gives Psi = (n-1) max_j d_j; sampling proportionally to d_j reduces it
to roughly sum_j d_j, a gain of max_j d_j / mean_j d_j. That is 1 on a
homogeneous design and large on a heavy-tailed one.
minimize_sfw now takes sampling_probs, and sfw_importance_probs computes the
weights for an l1 ball. Restricted to the SAG and SAGA variants at batch_size=1,
since the batch sampler draws without replacement and the analysis assumes unit
batches; probabilities must be strictly positive, as a datapoint that is never
resampled keeps a stale gradient forever. On a heavy-tailed design SAG improves
on all 12 seeds tried, SAGA on 8 of 12 -- the guarantee describes the biased
SAG-style estimator, so only the former is asserted in the tests.
Also documents that the existing 'DR' step size builds its certificate from the
stochastic gap, which is not a lower bound on the true directional derivative,
and so is a heuristic rather than a sufficient-decrease guarantee.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every run on the repo currently fails in ~3s at "Set up Python" with "Version 3.9 with arch x64 not found": ubuntu-latest is now ubuntu-24.04, whose image ships no Python below 3.10, and setup-python@v1 only consumes pre-installed interpreters rather than downloading one. This is unrelated to any PR -- master is equally red. - checkout@v1 -> v4, setup-python@v1 -> v5 (both were Node 12 actions). - Matrix 3.8/3.9/3.10 -> 3.10/3.11/3.12. 3.8 and 3.9 are both EOL and neither is obtainable on ubuntu-24.04. - pipconflictchecker -> pip check. pip-conflict-checker was last released in 2016 and imports pkg_resources, which setuptools 81 deprecated and setuptools 84 removed, so it now dies on import. pip check does the same job natively. - flake8 and pytest are now installed explicitly. They were only reaching the runner as transitive dependencies of pip-conflict-checker and pytest-parallel, so dropping those would otherwise break the Lint step. - Drop pytest-parallel. It is unmaintained and nothing passes --workers. Verified locally on 3.12: pip check clean, flake8 clean, 208 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SAGA's update rescales the change in the sampled dual variable by a constant
n_samples - 1. That constant is 1/q_j - 1 evaluated at the uniform q_j = 1/n:
the estimator is grad_agg_prev + (1/(n q_j)) a_j (f'_j(fresh) - f'_j(stale)),
and with the 1/n already carried by dual_var the factor is 1/q_j, of which
grad_agg has already supplied 1. Left as a constant, SAGA's gradient estimate
is biased as soon as sampling_probs is not uniform.
Computing the factor from the sampled probability restores unbiasedness and is
exactly equivalent for uniform sampling, so nothing changes without
sampling_probs -- SAG is untouched either way, since it reads grad_agg directly
and never forms grad_est.
The effect on the heavy-tailed design in the tests is large. Mean suboptimality
against a projected-gradient reference over 12 seeds, importance sampling vs
uniform:
epoch 30 epoch 100 epoch 300
before 1.4x 1.1x 1.6x (8/12, 5/12, 12/12 seeds)
after 13.7x 34.0x 85.1x (12/12 at all three)
So the improvement can now be asserted for both memory-based variants rather
than SAG alone, and the test is parametrized over the two. The docstring no
longer claims 'SAGA' is uncovered; it notes instead that Psi(q) is derived for
the SAG-style estimator, so d_j-proportional weights are principled for SAG and
merely effective for SAGA, not known to be variance-optimal there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GeoffNN
marked this pull request as ready for review
September 7, 2026 05:52
Member
|
hmm something's weird. its trying to duplicate everything into a build/ directory and comitting that it seems |
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.
Importance sampling for the stochastic Frank-Wolfe variants
minimize_sfw's SAG-style estimator keeps one stale scalar per datapoint and refreshes auniformly sampled one each step. Its error constant is
where
q_jis the probability of sampling datapointjandd_jis how far thatdatapoint's prediction can move across the constraint set. Uniform sampling gives exactly
Psi = (n-1) max_j d_j. Takingq_jproportional tod_jreduces it to aboutsum_j d_j(up to a log factor) — a gain of
max_j d_j / mean_j d_j, which is 1 on a homogeneousdesign and large on a heavy-tailed one. For the l1 ball of radius
alpha,d_j = 2 * alpha * ||a_j||_inf.What this adds
sfw_importance_probs(A, alpha, ord=1)— computes the weights for an l1 ball.sampling_probs=Noneonminimize_sfw— pass the weights to use them.Scope, and why
Restricted to the
SAGandSAGAvariants atbatch_size=1. Both restrictions are realrather than conservative: the batch sampler draws without replacement, which non-uniform
probabilities do not describe, and the analysis assumes unit batches. Probabilities must be
strictly positive — a datapoint that is never resampled keeps a stale gradient forever.
The improvement is asserted for both
SAGandSAGA, but only after fixing a second thing —see below.
Psi(q)is derived for the SAG-style estimator, whose per-datapoint error decays atrate
q_j, so these weights are principled forSAGand merely effective forSAGA; theyare not claimed to be variance-optimal for the latter.
SAGA's correction had to be made unbiased first
SAGArescaled the change in the sampled dual variable by the constantn_samples - 1. Thatconstant is
1/q_j - 1evaluated at the uniformq_j = 1/n: the estimator isgrad_agg_prev + (1/(n q_j)) a_j (f'_j(fresh) - f'_j(stale)), and sincedual_varalreadycarries the
1/n, the factor is1/q_j— of whichgrad_agghas supplied 1. Left constant,SAGA's gradient estimate is biased the moment sampling stops being uniform.Computing the factor from the sampled probability restores unbiasedness and is exactly
equivalent under uniform sampling, so nothing changes when
sampling_probsis not passed.SAGis untouched either way — it readsgrad_aggdirectly and never formsgrad_est.The effect is large. Mean suboptimality against a projected-gradient reference over 12 seeds,
importance sampling vs uniform:
Also documented, not fixed
step_size='DR'builds its certificate from the stochastic gap<-grad_agg, update_direction>, which is not a lower bound on the true directionalderivative of the objective. It is therefore a heuristic step size rather than one backed by
a sufficient-decrease guarantee. This matters if anyone tries to build a backtracking line
search on it: when the stochastic gap overestimates the true gap, no Lipschitz estimate
satisfies the sufficient-decrease test and the loop diverges — observed reaching ~1e17
within a few thousand iterations. Only a docstring note here; no behaviour change.
Follow-up deliberately left out
A backtracking line search using the exact Frank-Wolfe gap. For this problem class the
exact gap costs
O(n)rather thanO(nd)— maintainingtheta = A @ xmakes both theobjective and the gap cheap, since the vertex is sparse. But evaluating the objective from
the linear predictions needs a value counterpart to
partial_deriv, which the loss classesdo not have. Adding one to every loss class is an API decision that belongs to the
maintainer, so it is not in this PR. Happy to follow up if you want it.
Tests
pytest tests/test_stochastic_fw.py— 67 passed (60 pre-existing, 7 new).A second, separable commit: the CI workflow
CI on this repo was red before this PR —
masterincluded. Every job died after~3s at Set up Python with
Version 3.9 with arch x64 not found, becauseubuntu-latestis now ubuntu-24.04 (no Python below 3.10 in the image) andsetup-python@v1only consumes pre-installed interpreters instead of downloadingone. So there was no way to show this PR green without touching the workflow.
The second commit does the minimum for that:
checkout@v1→v4,setup-python@v1→v5, matrix3.8/3.9/3.10→3.10/3.11/3.12, andpipconflictchecker→pip check(pip-conflict-checker was last released in 2016and imports
pkg_resources, which setuptools 84 removed, so it now dies onimport).
flake8andpytestbecome explicit installs since they were onlyarriving as transitive dependencies of the packages being dropped.
Result: 208 passed on each of 3.10, 3.11 and 3.12. It is a separate commit on
purpose — drop or rework it freely if you would rather fix CI on its own terms.
Attribution
The estimator and its analysis are from Négiar, Dresdner, Tsai, El Ghaoui, Locatello,
Freund and Pedregosa, Stochastic Frank-Wolfe for Constrained Finite-Sum Minimization,
ICML 2020 (arXiv:2002.11860). The importance-sampling
refinement is work in preparation by the same first author.
🤖 Generated with Claude Code