Skip to content

Commit 3f67dfa

Browse files
vrouletOptaxDev
authored andcommitted
revert rollback of seed key v3 PR #1240
PiperOrigin-RevId: 748434720
1 parent 9015713 commit 3f67dfa

14 files changed

Lines changed: 118 additions & 44 deletions

optax/_src/alias.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@
1717
from collections.abc import Callable
1818
import functools
1919
from typing import Any, Optional, Union
20+
import warnings
2021

22+
import chex
23+
import jax
2124
import jax.numpy as jnp
2225
from optax._src import base
2326
from optax._src import clipping
2427
from optax._src import combine
2528
from optax._src import factorized
2629
from optax._src import linesearch as _linesearch
2730
from optax._src import transform
31+
from optax._src import utils
2832
from optax._src import wrappers
2933

3034

@@ -1280,7 +1284,7 @@ def noisy_sgd(
12801284
learning_rate: base.ScalarOrSchedule,
12811285
eta: float = 0.01,
12821286
gamma: float = 0.55,
1283-
seed: int = 0,
1287+
key: chex.PRNGKey | int | None = None
12841288
) -> base.GradientTransformationExtraArgs:
12851289
r"""A variant of SGD with added noise.
12861290
@@ -1311,7 +1315,7 @@ def noisy_sgd(
13111315
eta: Initial variance for the Gaussian noise added to gradients.
13121316
gamma: A parameter controlling the annealing of noise over time ``t``, the
13131317
variance decays according to ``(1+t)**(-gamma)``.
1314-
seed: Seed for the pseudo-random generation process.
1318+
key: a PRNG key used as the random key.
13151319
13161320
Returns:
13171321
The corresponding :class:`optax.GradientTransformationExtraArgs`.
@@ -1321,7 +1325,10 @@ def noisy_sgd(
13211325
>>> import jax
13221326
>>> import jax.numpy as jnp
13231327
>>> def f(x): return jnp.sum(x ** 2) # simple quadratic function
1324-
>>> solver = optax.noisy_sgd(learning_rate=0.003)
1328+
>>> solver = optax.noisy_sgd(
1329+
... learning_rate=0.003,
1330+
... key=jax.random.key(0)
1331+
... )
13251332
>>> params = jnp.array([1., 2., 3.])
13261333
>>> print('Objective function: ', f(params))
13271334
Objective function: 14.0
@@ -1341,8 +1348,13 @@ def noisy_sgd(
13411348
Neelakantan et al, `Adding Gradient Noise Improves Learning for Very Deep
13421349
Networks <https://arxiv.org/abs/1511.06807>`_, 2015
13431350
"""
1351+
if key is None:
1352+
warnings.warn(
1353+
'Specifying a key for optax.noisy_sgd will be required in optax 0.3.0.'
1354+
)
1355+
key = jax.random.key(0)
13441356
return combine.chain(
1345-
transform.add_noise(eta, gamma, seed),
1357+
transform.add_noise(eta, gamma, key=utils.to_random_key(key)),
13461358
transform.scale_by_learning_rate(learning_rate),
13471359
)
13481360

optax/_src/alias_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
{'opt_name': 'nadamw', 'opt_kwargs': {'learning_rate': 1e-2}},
7272
{
7373
'opt_name': 'noisy_sgd',
74-
'opt_kwargs': {'learning_rate': 1e-3, 'eta': 1e-4},
74+
'opt_kwargs': {'learning_rate': 1e-3, 'key': 0, 'eta': 1e-4},
7575
},
7676
{'opt_name': 'novograd', 'opt_kwargs': {'learning_rate': 1e-3}},
7777
{'opt_name': 'optimistic_adam', 'opt_kwargs': {'learning_rate': 2e-3}},

optax/_src/float64_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@
4848
{'step_size_fn': lambda x: x * 0.1},
4949
),
5050
('scale_by_trust_ratio', transform.scale_by_trust_ratio, {}),
51-
('add_noise', transform.add_noise, {'eta': 1.0, 'gamma': 0.1, 'seed': 42}),
51+
('add_noise', transform.add_noise, {'key': 42, 'eta': 1.0, 'gamma': 0.1}),
5252
('apply_every_k', transform.apply_every, {}),
5353
('adagrad', alias.adagrad, {'learning_rate': 0.1}),
5454
('adam', alias.adam, {'learning_rate': 0.1}),
5555
('adamw', alias.adamw, {'learning_rate': 0.1}),
5656
('fromage', alias.fromage, {'learning_rate': 0.1}),
5757
('lamb', alias.lamb, {'learning_rate': 0.1}),
58-
('noisy_sgd', alias.noisy_sgd, {'learning_rate': 0.1}),
58+
('noisy_sgd', alias.noisy_sgd, {'learning_rate': 0.1, 'key': 0}),
5959
('rmsprop', alias.rmsprop, {'learning_rate': 0.1}),
6060
('sgd', alias.sgd, {'learning_rate': 0.1}),
6161
('sign_sgd', alias.sgd, {'learning_rate': 0.1}),

optax/_src/utils.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ def canonicalize_dtype(
4646
return dtype
4747

4848

49+
def to_random_key(key_or_seed: chex.PRNGKey | int) -> chex.PRNGKey:
50+
"""Canonicalize a random key or an int representing a seed to a random key."""
51+
if (isinstance(key_or_seed, jax.Array) and jnp.issubdtype(
52+
key_or_seed.dtype, jax.dtypes.prng_key
53+
)):
54+
return key_or_seed
55+
return jax.random.key(key_or_seed)
56+
57+
4958
@functools.partial(
5059
chex.warn_deprecated_function, replacement='optax.tree_utils.tree_cast'
5160
)
@@ -106,10 +115,10 @@ def __init__(self, loc: chex.Array, log_scale: chex.Array):
106115
self._mean.shape, self._scale.shape
107116
)
108117

109-
def sample(self, shape: Sequence[int], seed: chex.PRNGKey) -> chex.Array:
118+
def sample(self, shape: Sequence[int], key: chex.PRNGKey) -> chex.Array:
110119
sample_shape = tuple(shape) + self._param_shape
111120
return (
112-
jax.random.normal(seed, shape=sample_shape) * self._scale + self._mean
121+
jax.random.normal(key, shape=sample_shape) * self._scale + self._mean
113122
)
114123

115124
def log_prob(self, x: chex.Array) -> chex.Array:

optax/contrib/_common_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
{'opt_name': 'lion', 'opt_kwargs': {'learning_rate': 1.0, 'b1': 0.99}},
106106
{
107107
'opt_name': 'noisy_sgd',
108-
'opt_kwargs': {'learning_rate': 1.0, 'eta': 1e-4},
108+
'opt_kwargs': {'learning_rate': 1.0, 'key': 0, 'eta': 1e-4},
109109
},
110110
{'opt_name': 'novograd', 'opt_kwargs': {'learning_rate': 1.0}},
111111
{

optax/contrib/_privacy.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
from typing import Any, NamedTuple, Optional
1818

19+
import chex
1920
import jax
2021
from optax._src import base
2122
from optax._src import clipping
2223
from optax._src import combine
2324
from optax._src import transform
25+
from optax._src import utils
2426

2527

2628
class DifferentiallyPrivateAggregateState(NamedTuple):
@@ -33,14 +35,16 @@ class DifferentiallyPrivateAggregateState(NamedTuple):
3335

3436

3537
def differentially_private_aggregate(
36-
l2_norm_clip: float, noise_multiplier: float, seed: int
38+
l2_norm_clip: float,
39+
noise_multiplier: float,
40+
key: chex.PRNGKey | int
3741
) -> base.GradientTransformation:
3842
"""Aggregates gradients based on the DPSGD algorithm.
3943
4044
Args:
4145
l2_norm_clip: maximum L2 norm of the per-example gradients.
4246
noise_multiplier: ratio of standard deviation to the clipping norm.
43-
seed: initial seed used for the jax.random.PRNGKey
47+
key: a PRNG key used as the random key.
4448
4549
Returns:
4650
A :class:`optax.GradientTransformation`.
@@ -63,10 +67,11 @@ def differentially_private_aggregate(
6367
specific way.
6468
"""
6569
noise_std = l2_norm_clip * noise_multiplier
70+
key = utils.to_random_key(key)
6671

6772
def init_fn(params):
6873
del params
69-
return DifferentiallyPrivateAggregateState(rng_key=jax.random.PRNGKey(seed))
74+
return DifferentiallyPrivateAggregateState(rng_key=utils.to_random_key(key))
7075

7176
def update_fn(updates, state, params=None):
7277
del params
@@ -91,7 +96,7 @@ def dpsgd(
9196
learning_rate: base.ScalarOrSchedule,
9297
l2_norm_clip: float,
9398
noise_multiplier: float,
94-
seed: int,
99+
key: chex.PRNGKey | int,
95100
momentum: Optional[float] = None,
96101
nesterov: bool = False,
97102
) -> base.GradientTransformation:
@@ -106,7 +111,7 @@ def dpsgd(
106111
learning_rate: A fixed global scaling factor.
107112
l2_norm_clip: Maximum L2 norm of the per-example gradients.
108113
noise_multiplier: Ratio of standard deviation to the clipping norm.
109-
seed: Initial seed used for the jax.random.PRNGKey
114+
key: a PRNG key used as the random key.
110115
momentum: Decay rate used by the momentum term, when it is set to `None`,
111116
then momentum is not used at all.
112117
nesterov: Whether Nesterov momentum is used.
@@ -133,7 +138,7 @@ def dpsgd(
133138
differentially_private_aggregate(
134139
l2_norm_clip=l2_norm_clip,
135140
noise_multiplier=noise_multiplier,
136-
seed=seed,
141+
key=utils.to_random_key(key),
137142
),
138143
(
139144
transform.trace(decay=momentum, nesterov=nesterov)

optax/contrib/_privacy_test.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ def setUp(self):
4545
def test_no_privacy(self):
4646
"""l2_norm_clip=MAX_FLOAT32 and noise_multiplier=0 should recover SGD."""
4747
dp_agg = _privacy.differentially_private_aggregate(
48-
l2_norm_clip=jnp.finfo(jnp.float32).max, noise_multiplier=0.0, seed=0
48+
l2_norm_clip=jnp.finfo(jnp.float32).max,
49+
noise_multiplier=0.0,
50+
key=jax.random.key(0)
4951
)
5052
state = dp_agg.init(self.params)
5153
update_fn = self.variant(dp_agg.update)
@@ -59,7 +61,9 @@ def test_no_privacy(self):
5961
@parameterized.parameters(0.5, 10.0, 20.0, 40.0, 80.0)
6062
def test_clipping_norm(self, l2_norm_clip):
6163
dp_agg = _privacy.differentially_private_aggregate(
62-
l2_norm_clip=l2_norm_clip, noise_multiplier=0.0, seed=42
64+
l2_norm_clip=l2_norm_clip,
65+
noise_multiplier=0.0,
66+
key=jax.random.key(42)
6367
)
6468
state = dp_agg.init(self.params)
6569
update_fn = self.variant(dp_agg.update)
@@ -87,7 +91,9 @@ def test_clipping_norm(self, l2_norm_clip):
8791
def test_noise_multiplier(self, l2_norm_clip, noise_multiplier):
8892
"""Standard dev. of noise should be l2_norm_clip * noise_multiplier."""
8993
dp_agg = _privacy.differentially_private_aggregate(
90-
l2_norm_clip=l2_norm_clip, noise_multiplier=noise_multiplier, seed=1337
94+
l2_norm_clip=l2_norm_clip,
95+
noise_multiplier=noise_multiplier,
96+
key=jax.random.key(1337)
9197
)
9298
state = dp_agg.init(self.params)
9399
update_fn = self.variant(dp_agg.update)
@@ -103,7 +109,9 @@ def test_noise_multiplier(self, l2_norm_clip, noise_multiplier):
103109
def test_aggregated_updates_as_input_fails(self):
104110
"""Expect per-example gradients as input to this transform."""
105111
dp_agg = _privacy.differentially_private_aggregate(
106-
l2_norm_clip=0.1, noise_multiplier=1.1, seed=2021
112+
l2_norm_clip=0.1,
113+
noise_multiplier=1.1,
114+
key=jax.random.key(2021)
107115
)
108116
state = dp_agg.init(self.params)
109117
mean_grads = jax.tree.map(lambda g: g.mean(0), self.per_eg_grads)

optax/monte_carlo/control_variates.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def control_variates_jacobians(
310310
# gradient.
311311
# The rng has to be the same as passed to the grad_estimator above so that we
312312
# obtain the same samples.
313-
samples = dist_builder(*params).sample((num_samples,), seed=rng)
313+
samples = dist_builder(*params).sample((num_samples,), key=rng)
314314
# If the CV has state, update it.
315315
control_variate_state = update_state_cv(
316316
params, samples, control_variate_state

optax/monte_carlo/stochastic_gradient_estimators.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def score_function_jacobians(
8585
def surrogate(params):
8686
dist = dist_builder(*params)
8787
one_sample_surrogate_fn = lambda x: function(x) * dist.log_prob(x)
88-
samples = jax.lax.stop_gradient(dist.sample((num_samples,), seed=rng))
88+
samples = jax.lax.stop_gradient(dist.sample((num_samples,), key=rng))
8989
# We vmap the function application over samples - this ensures that the
9090
# function we use does not have to be vectorized itself.
9191
return jax.vmap(one_sample_surrogate_fn)(samples)
@@ -141,7 +141,7 @@ def surrogate(params):
141141
# We vmap the function application over samples - this ensures that the
142142
# function we use does not have to be vectorized itself.
143143
dist = dist_builder(*params)
144-
return jax.vmap(function)(dist.sample((num_samples,), seed=rng))
144+
return jax.vmap(function)(dist.sample((num_samples,), key=rng))
145145

146146
return jax.jacfwd(surrogate)(params)
147147

@@ -239,7 +239,7 @@ def measure_valued_estimation_mean(
239239
mean, log_std = dist.params
240240
std = jnp.exp(log_std)
241241

242-
dist_samples = dist.sample((num_samples,), seed=rng)
242+
dist_samples = dist.sample((num_samples,), key=rng)
243243

244244
pos_rng, neg_rng = jax.random.split(rng)
245245
pos_sample = jax.random.weibull_min(
@@ -312,7 +312,7 @@ def measure_valued_estimation_std(
312312
mean, log_std = dist.params
313313
std = jnp.exp(log_std)
314314

315-
dist_samples = dist.sample((num_samples,), seed=rng)
315+
dist_samples = dist.sample((num_samples,), key=rng)
316316

317317
pos_rng, neg_rng = jax.random.split(rng)
318318

optax/perturbations/_make_pert.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import jax.numpy as jnp
2626
from optax import tree_utils as otu
2727
from optax._src import base
28+
from optax._src import utils
2829

2930

3031
Shape = base.Shape
@@ -35,11 +36,11 @@ class Normal:
3536

3637
def sample(
3738
self,
38-
seed: chex.PRNGKey,
39+
key: chex.PRNGKey | int,
3940
sample_shape: Shape,
4041
dtype: chex.ArrayDType = float,
4142
) -> jax.Array:
42-
return jax.random.normal(seed, sample_shape, dtype)
43+
return jax.random.normal(utils.to_random_key(key), sample_shape, dtype)
4344

4445
def log_prob(self, inputs: jax.Array) -> jax.Array:
4546
return -0.5 * inputs**2
@@ -50,11 +51,11 @@ class Gumbel:
5051

5152
def sample(
5253
self,
53-
seed: chex.PRNGKey,
54+
key: chex.PRNGKey | int,
5455
sample_shape: Shape,
5556
dtype: chex.ArrayDType = float,
5657
) -> jax.Array:
57-
return jax.random.gumbel(seed, sample_shape, dtype)
58+
return jax.random.gumbel(utils.to_random_key(key), sample_shape, dtype)
5859

5960
def log_prob(self, inputs: jax.Array) -> jax.Array:
6061
return -inputs - jnp.exp(-inputs)

0 commit comments

Comments
 (0)