Noise Injection¶
Opake's noise mechanisms live next to the training paradigm they support:
opake.dpsgd.noise for independent Gaussian-family noise, and
opake.dpftrl.noise for matrix-factorization (correlated) noise. The base
NoiseState type that both build on lives in opake.types.
Overview¶
After clipping gradients, DP-SGD requires adding noise proportional to the clip norm and noise multiplier. The noise obscures individual contributions, providing the actual privacy guarantee.
Opake provides several noise mechanisms:
Independent Noise (DP-SGD)¶
gaussian_noise()— Gaussian noise.
Correlated Noise (DP-FTRL / Matrix Factorization)¶
mf_gaussian_noise()— Unified correlated noise dispatcher. Takes a strategy object and creates the corresponding noise mechanism.
Strategy factories (passed to mf_gaussian_noise()):
band_mf_strategy()— BandMF banded Toeplitz correlated noiseblt_strategy()— Buffered Linear Toeplitz (BLT) correlated noiselambda_cgd_strategy()— DP-λCGD correlated noise (PRNG replay, zero extra memory)bisr_strategy()— BISR (Banded Inverse Square Root) correlated noiseidentity_strategy()— Identity (DP-SGD via MF API, easy to swap)
All noise functions return (noise_fn, state), where
noise_fn(grads, state) -> (noisy_grads, new_state).
When private second moments are enabled, the noisy value is a
SecondMomentNoiseOutput carrying both noisy_grads and
noisy_squared_grads.
State Classes¶
NoiseState— Abstract base class for all noise state types. Defines_step_counterand_rng_key.SecondMomentNoiseOutput— Paired first/squared-gradient output for private second moments.GaussianNoiseState— State forgaussian_noise(). Holds step counter and RNG key.MFNoiseState— State formf_gaussian_noise(). Holds internal correlation state, step counter, and RNG key.SecondMomentMFNoiseState— Paired-stream MF state used bymf_gaussian_noise(..., second_moment_strategy=...).
Distributed Sync Helpers¶
Use sync() from opake.distributed to validate noise state consistency
across ranks. It auto-dispatches based on type:
sync(GaussianNoiseState)— Validate RNG key and step counter match across ranks.sync(MFNoiseState)— Validate MF noise state matches across ranks.
See also: Noise Addition User Guide
Paired second-moment release¶
When clipped_grad(..., second_moment=True) produces a
SecondMomentClippingOutput, both gaussian_noise (DP-SGD) and
mf_gaussian_noise(..., second_moment_strategy=...) (DP-FTRL) consume it and
emit a SecondMomentNoiseOutput with paired noise on both streams.
The runtime σ allocation is sensitivity-proportional and works
identically for scalar max_norm and per-group PerGroup max_norm. For
each group \(g\) (single-group K=1 for scalar clipping), let
\(\Delta^{(1)}_g\) be the first-stream per-record bound (C_g / n for
averaged clipping, or ζ · ‖C₁‖ strategy-amplified for DP-FTRL) and
\(\Delta^{(2)}_g\) the second-stream per-record bound (C_g² / n derived
from the same clip; ζ² · ‖C₂‖ for DP-FTRL). Then with noise multiplier
\(\text{nm}\), set
This satisfies the joint Mahalanobis budget with equality:
So the paired release has the same PLD as a single sensitivity-1
Gaussian release at the joint effective multiplier passed to
paired_noise_stddevs (see its docstring: no-op for DP-SGD; divide by
‖C₁‖ for DP-FTRL). Accounting is plain gaussian(nm) for DP-SGD and the
underlying mf_gaussian(nm, …) for DP-FTRL; there is no separate
transformation wrapper and no ρ knob.
mf_gaussian_noise accepts scalar or PerGroup max_norm on ClippedPytree
inputs. Single-stream IID stddevs for PerGroup bounds match the
MSE-optimal allocation from ClippedPytree.noise_stddev_for (the same
Mahalanobis allocation as gaussian_noise). Leaf→group keys are optree
ParamPath tuples (build them with per_group(params, …)); nested
parameter trees are supported. Trainer/examples keep flat
named_parameters by choice.
Gaussian¶
gaussian_noise(*, noise_multiplier, key, compute_dtype=torch.float32)
¶
Create a Gaussian noise function with immutable state.
Returns (noise_fn, state) where noise_fn adds calibrated Gaussian
noise to :class:opake.types.ClippedPytree inputs and returns updated
state. The realized standard deviation is
noise_multiplier * clipped.max_norm. The output is a
:class:opake.types.NoisedPytree carrying that realized
noise_stddev metadata.
The noise function uses exactly the key you provide — no auto-detection
of distributed state. For synchronized noise in DDP, pass the same key on
every rank. For independent noise, derive a per-rank key::
from opake.random import key, fold_in
my_key = fold_in(key(42), rank) # different noise per rank
noise_fn, state = gaussian_noise(noise_multiplier=1.1, key=my_key)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
noise_multiplier
|
float
|
Gaussian noise multiplier. The realized standard
deviation is |
required |
key
|
RngKey
|
Explicit RNG key for deterministic, functional randomness.
Same key on all ranks → same noise (synchronized).
|
required |
compute_dtype
|
dtype
|
One of |
float32
|
Returns:
| Type | Description |
|---|---|
GaussianNoiseFn
|
A tuple |
GaussianNoiseState
|
|
tuple[GaussianNoiseFn, GaussianNoiseState]
|
|
Example
import torch from opake.types import clipped from opake.dpsgd.noise import gaussian_noise from opake.random import key
noise_fn, state = gaussian_noise(noise_multiplier=1.1, key=key(42)) grads = torch.zeros(10) noisy_grads, state = noise_fn(clipped(grads, max_norm=1.0), state)
Source code in packages/opake-dpsgd/src/opake/api/dpsgd/noise/_gaussian.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | |
Matrix Factorization Noise¶
Dispatcher¶
mf_gaussian_noise(grad_template, strategy, *, n_steps, min_sep=1, max_participations=None, noise_multiplier, key, compute_dtype=torch.float32, second_moment_strategy=None)
¶
Create a correlated noise mechanism for the given MF strategy.
The polymorphic :meth:MfStrategy.streaming_matrix query selects the
appropriate streaming primitive at the supplied amplification
context; :class:LambdaCgdStrategy uses a PRNG-replay path (no
streaming matrix is materialized).
The returned noise_fn dispatches on its input type:
ClippedPytree→NoisedPytree(single-stream noise).SecondMomentClippingOutput→SecondMomentNoiseOutput(paired-stream noise; only whensecond_moment_strategywas supplied at construction).
The paired-stream release uses the sensitivity-proportional joint
Mahalanobis allocation
(:func:~opake.api.engine.noise_allocation.paired_noise_stddevs)
with the MF translation nm / ‖C₁‖ as the joint effective
multiplier so the joint PLD matches the single-stream MF Gaussian
accountant at (noise_multiplier, ‖C₁‖). PerGroup
max_norm is supported on both streams.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grad_template
|
Any
|
Pytree with same structure/shapes as gradients. |
required |
strategy
|
MfStrategy
|
MF strategy recipe from one of the factory functions. |
required |
n_steps
|
int
|
Total training steps (the amplifier's horizon). |
required |
min_sep
|
int
|
Minimum separation between participations (default 1). |
1
|
max_participations
|
int | None
|
Max participations per example
( |
None
|
noise_multiplier
|
float
|
Gaussian noise multiplier. The clipped-gradient
|
required |
key
|
RngKey
|
Explicit RNG key for deterministic randomness. |
required |
compute_dtype
|
dtype
|
Dtype used for the underlying |
float32
|
second_moment_strategy
|
MfStrategy | None
|
Optional explicit strategy recipe for the squared-gradient stream. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[Callable[..., tuple[Any, MFNoiseState | SecondMomentMFNoiseState]], MFNoiseState | SecondMomentMFNoiseState]
|
A tuple |
Source code in packages/opake-dpftrl/src/opake/api/dpftrl/noise/_mf_gaussian_noise.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | |
Strategies¶
band_mf_strategy(*, bands, momentum=1.0, lr_schedule=None)
¶
Create a BandMF strategy recipe.
The factory only validates static recipe knobs. The Toeplitz
optimization happens lazily when a strategy method is first called
with an amplifier-supplied n_steps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bands
|
int
|
Number of bands in the Toeplitz matrix (>= 1). |
required |
momentum
|
float
|
Polyak momentum coefficient (default 1.0 = prefix-sum). |
1.0
|
lr_schedule
|
Schedule | None
|
Optional :data: |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
BandMfStrategy
|
class: |
Source code in packages/opake-dpftrl/src/opake/api/dpftrl/noise/_band_mf.py
blt_strategy(*, max_buffers=10, momentum=1.0, lr_schedule=None)
¶
Create a BLT (Buffered Linear Toeplitz) strategy recipe.
The L-BFGS optimization runs lazily inside the strategy methods
when an amplifier supplies the participation context
(n_steps, min_sep, max_participations) — once per recipe per
context, via a module-level cache shared with the noise side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_buffers
|
int
|
Maximum BLT buffer count for the optimizer (default 10). |
10
|
momentum
|
float
|
Polyak momentum (default 1.0 = prefix-sum workload). |
1.0
|
lr_schedule
|
Schedule | None
|
Optional :data: |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
BltStrategy
|
class: |
Source code in packages/opake-dpftrl/src/opake/api/dpftrl/noise/_blt.py
lambda_cgd_strategy(*, lambda_, normalized=True, lr_schedule=None)
¶
Create a DP-lambda-CGD strategy recipe (bandwidth=2, PRNG-replay noise).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lambda_
|
float
|
Correlation coefficient in [0, 1). |
required |
normalized
|
bool
|
Use column-normalized matrix (default True). |
True
|
lr_schedule
|
Schedule | None
|
Deprecated compatibility argument. Only |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
LambdaCgdStrategy
|
class: |
Source code in packages/opake-dpftrl/src/opake/api/dpftrl/noise/_lambda_cgd.py
bisr_strategy(*, bandwidth, normalized=True, momentum=0.0, lr_schedule=None, inv_coefficients=None)
¶
Create a BISR (Banded Inverse Square Root) strategy recipe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bandwidth
|
int
|
BISR bandwidth p (>= 2). |
required |
normalized
|
bool
|
Use column-normalized matrix (default True). |
True
|
momentum
|
float
|
The paper's beta in the alpha=1 specialization. Must be in
|
0.0
|
lr_schedule
|
Schedule | None
|
Deprecated compatibility argument. Only |
None
|
inv_coefficients
|
Sequence[float] | None
|
Explicit :math: |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
BisrStrategy
|
class: |
Source code in packages/opake-dpftrl/src/opake/api/dpftrl/noise/_bisr.py
identity_strategy()
¶
State Classes¶
NoiseState
¶
Bases: ABC
Base class for noise state.
All noise functions (Gaussian and MF) return a state object that inherits from this class, providing a unified interface for step tracking and RNG key management.
Attributes:
| Name | Type | Description |
|---|---|---|
_step_counter |
int
|
Number of |
_rng_key |
RngKey
|
Immutable RNG key for deterministic per-step derivation. |
Source code in packages/opake-engine/src/opake/api/engine/types.py
SecondMomentNoiseOutput
¶
Bases: NamedTuple
Noise output with private first and second moment streams.
Attributes:
| Name | Type | Description |
|---|---|---|
noisy_grads |
NoisedPytree
|
Noised clipped gradients for the optimizer's first moment / update direction. |
noisy_squared_grads |
NoisedPytree
|
Noised element-wise squared clipped gradients for optimizers with a second moment accumulator. |
Source code in packages/opake-engine/src/opake/api/engine/types.py
GaussianNoiseState
dataclass
¶
Bases: NoiseState
Immutable state for Gaussian noise generation.
Holds an immutable RNG key for deterministic per-step derivation.
Noise for step t is generated from
fold_in(_rng_key, GAUSSIAN_STREAM_FOLD, t).
Attributes:
| Name | Type | Description |
|---|---|---|
_step_counter |
int
|
Number of noise_fn calls made. |
_rng_key |
RngKey
|
Immutable RNG key for deterministic derivation. |
Source code in packages/opake-dpsgd/src/opake/api/dpsgd/noise/_gaussian.py
MFNoiseState
dataclass
¶
Bases: NoiseState
State for matrix factorization noise.
Restoration is template-driven. A saved state must be loaded into a template built with the same strategy recipe, horizon, compute dtype, and parameter-tree structure. The trainer checkpoint records and checks that execution provenance before restoring this state; standalone callers are responsible for preserving the same construction inputs.
Attributes:
| Name | Type | Description |
|---|---|---|
_inner_state |
Any
|
Internal state (streaming matrix state or step counter). |
_step_counter |
int
|
Number of noise_fn calls made. |
_rng_key |
RngKey
|
Immutable RNG key for deterministic per-step derivation. |
_first_max_norm |
float | PerGroup | None
|
|
_first_max_norm_sync_fingerprint |
int | None
|
Deterministic 64-bit fingerprint
(computed at latch time) for cheap cross-rank checks in
:func: |
Source code in packages/opake-dpftrl/src/opake/api/dpftrl/noise/_engine.py
SecondMomentMFNoiseState
dataclass
¶
Bases: NoiseState
Internal state for MF noise with private second moments.
Source code in packages/opake-dpftrl/src/opake/api/dpftrl/noise/_second_moment.py
Distributed Synchronization¶
Use opake.distributed.sync(state) — it auto-dispatches on the state's
type to the right sync function. GaussianNoiseState and MFNoiseState
both register handlers at import time, so no named sync call is required.