Gradient Clipping¶
Per-example clipping APIs for DP training: fixed norm, AUTO-S scaling, and
(on the DP-SGD side) adaptive clipping. DP-FTRL users import fixed and
AUTO-S helpers from opake.dpftrl.clipping; DP-SGD users use
opake.dpsgd.clipping for all three.
Overview¶
Per-sample gradient clipping bounds the influence that any single training example can have on the model, enabling differential privacy.
Clipping functions¶
clipped_grad()— High-level API: differentiates, clips, and sums gradients.clipped_fun()— Clip and sum arbitrary function outputs (PyTrees).auto_clipped_grad()— AUTO-S (Bu et al. NeurIPS 2023) automatic per-example gradient scaling. Constant per-record sensitivity ⇒ composes with both DP-SGD's Gaussian mechanism and DP-FTRL's matrix-factorization mechanisms.auto_clipped_fun()— AUTO-S for arbitrary function outputs.adaptive_clipped_grad()— Adaptive clipping (Andrew et al. 2021) with automatic threshold tuning; DP-SGD-only.clip_pytree()— Low-level: clip an existing PyTree of gradients.auto_scale_pytree()— Low-level: AUTO-S scale an existing PyTree.
State types¶
ClipState— Base class for clipping state.FixedClipState— Marker state for fixedclipped_grad/clipped_fun.AdaptiveClipState— Internal execution state foradaptive_clipped_grad.AutoClipState— Marker state forauto_clipped_grad/auto_clipped_fun.
Auxiliary output types¶
ClippedGradAux— Per-exampleloss_values,grad_norms,clipped_grad_norms,loss_aux, plus aggregateclipping_rateandbatch_size(fromclipped_grad).ClippedFunAux— Per-examplevalues,norms,clipped_norms,value_aux, plus aggregateclipping_rateandbatch_size(fromclipped_fun).AdaptiveClippedGradAux— Inherits all fields fromClippedGradAux(fromadaptive_clipped_grad).AutoClippedGradAux— InheritsClippedGradAuxfields with AUTO-S semantics:grad_norms(pre-scale),clipped_grad_norms(post-scale, bounded by R),clipping_rate(fraction with ‖g‖ > R),loss_values,loss_aux,group_norms,batch_size.AutoClippedFunAux— InheritsClippedFunAuxfields with AUTO-S semantics:norms(pre-scale),clipped_norms(post-scale, bounded by R),clipping_rate(fraction with ‖v‖ > R),values,value_aux,group_norms,batch_size.
PyTree wrappers¶
ClippedPytree— Wraps a pytree of clipped gradients with calibration metadata (max_norm). Carries amax_normwhich is either a scalarfloator aPerGroupfor per-parameter-group clipping.
noise_stddev_for(*, noise_multiplier, allocation="optimal") -> float | PerGroup
Returns the noise standard deviation that gaussian_noise(noise_multiplier) would apply to this
clipped pytree.
- For scalar
max_norm: returnsnoise_multiplier * max_norm. - For
PerGroupmax_normwithallocation="optimal"(default): returnsPerGroupof per-group standard deviationsσᵢ = noise_multiplier · √(Cᵢ · ΣⱼCⱼ)(MSE-optimal Mahalanobis allocation). - For
PerGroupwithallocation="isotropic": returns scalarnoise_multiplier * max_norm.effective.
Privacy accounting is gaussian(noise_multiplier) regardless of allocation — per-group allocation
costs nothing in the accountant because the Mahalanobis constraint is satisfied with equality.
grads, state = grad_fn(params, x, state=clip_state)
stddev = grads.noise_stddev_for(noise_multiplier=0.8)
noise_fn, noise_state = gaussian_noise(noise_multiplier=0.8, ...)
NoisedPytree— Subclass ofClippedPytreethat also carries anoise_stddevfield (scalar orPerGroup). Returned by all noise functions.
Distributed sync helpers¶
Use sync() from opake.distributed to synchronize any clipping state or aux
object. It auto-dispatches to the right function based on type:
sync(FixedClipState | AutoClipState)→ marker-state passthrough.sync(AdaptiveClipState)→ sums local clipped counts and batch sizes across ranks, uses the centered statistic with the public expected batch size, and updates the internal next adaptive threshold identically on every rank.sync(ClippedFunAux | ClippedGradAux | AutoClippedFunAux | AutoClippedGradAux | AdaptiveClippedGradAux)→ gathers aux across ranks.
Dispatch resolves by exact type and then by __mro__, so any subclass of a
registered clipping state or aux type reaches its base handler; an unregistered
type raises InputTypeError rather than passing through unsynchronized.
See also: Per-Sample Gradient Clipping User Guide
API Documentation¶
clipping
¶
DP-SGD clipping façade — fixed, AUTO-S, per-group, and adaptive thresholding.
Headline factories:
- :func:
clipped_grad— fixed-threshold per-example clipping. - :func:
auto_clipped_grad— AUTO-S (Bu et al.,Automatic Clipping <https://arxiv.org/abs/2206.07136>_, NeurIPS 2023). - :func:
per_group— build :class:~opake.types.PerGroupgroupings. - :func:
adaptive_clipped_grad— adaptive clipping (Andrew et al.,Differentially Private Learning with Adaptive Clipping <https://arxiv.org/abs/1905.03871>_, 2021).
Adaptive clipping is DP-SGD-only: its threshold drifts across steps, which violates the constant-sensitivity assumption matrix-factorization proofs require.
State and auxiliary dataclasses live in :mod:opake.dpsgd.clipping.types.
AUTO-S function-level helpers live in :mod:opake.dpsgd.clipping.fun.
adaptive_clipped_grad(loss_fn, argnums=0, has_aux=False, *, initial_clipping_norm=0.1, target_quantile=0.5, learning_rate=0.2, clipping_norm_min=0.01, clipping_norm_max=100.0, fraction_noise_std=_DEFAULT_FRACTION_NOISE_STD, expected_batch_size=None, key, return_aux=False, return_stats=False, pre_clipping_transform=lambda x: x, **clipped_grad_kwargs)
¶
Create function for adaptive gradient clipping with explicit state-passing.
Returns (clipped_grad_fn, initial_state). The returned function
takes state as an explicit parameter and returns
(grad, new_state) or ((grad, aux), new_state).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
loss_fn
|
Callable
|
Loss function (scalar output). If |
required |
argnums
|
int | tuple[int, ...]
|
Which argument(s) to differentiate w.r.t. |
0
|
has_aux
|
bool
|
If True, |
False
|
initial_clipping_norm
|
float | PerGroup
|
Initial clipping threshold C_0. When
|
0.1
|
target_quantile
|
float
|
Target fraction of clipped gradients. |
0.5
|
learning_rate
|
float
|
Step size for geometric adaptation. |
0.2
|
clipping_norm_min
|
float
|
Minimum allowed clipping threshold. |
0.01
|
clipping_norm_max
|
float
|
Maximum allowed clipping threshold. |
100.0
|
fraction_noise_std
|
float
|
Std of Gaussian noise added to the centered clipping indicator (default 0.05). |
_DEFAULT_FRACTION_NOISE_STD
|
expected_batch_size
|
float | None
|
Public, fixed denominator for the clipping
indicator. Defaults to an explicitly supplied |
None
|
key
|
RngKey
|
RNG key for quantile noise generation. |
required |
return_aux
|
bool
|
If True, return per-example aux with loss values, gradient norms, and clipping rate. |
False
|
return_stats
|
bool
|
If True, return aggregate clipping statistics alongside
the gradient. Cannot be combined with |
False
|
pre_clipping_transform
|
Callable
|
An optional function to apply to the per-example gradients before clipping. The function should consume the gradient pytree for a single example and return a new pytree (possibly with different structure). Can be used to e.g., scale the leaves of the pytree to accommodate preconditioner clipping. Does not affect the sensitivity guarantee. Default is identity function. |
lambda x: x
|
**clipped_grad_kwargs
|
Any
|
Passed to |
{}
|
Note
Empty-batch parity (second_moment): the empty-batch
short-circuit mirrors :func:~opake.dpsgd.clipping.clipped_grad and
:func:~opake.dpsgd.clipping.auto_clipped_grad — when second_moment=True
it returns a :class:~opake.types.SecondMomentClippingOutput of zeros
(squared-stream sensitivity C²/normalize_by), so paired-stream
noise and optimizer dispatch are stable across empty and non-empty
steps under Poisson sampling.
Returns:
| Type | Description |
|---|---|
tuple[Callable, AdaptiveClipState]
|
A tuple of (clipped_grad_fn, initial_state) where:
- clipped_grad_fn: Function with signature
(args, state, kwargs) -> (grad, new_state) or
(args, state, **kwargs) -> ((grad, aux), new_state)
or |
Example (single device):
>>> import torch
>>> from opake.dpsgd.clipping import adaptive_clipped_grad
>>> from opake.dpsgd.noise import gaussian_noise
>>> from opake.random import key
>>> import torchopt
>>>
>>> def loss_fn(params, x, y):
... pred = x @ params
... return ((pred - y) ** 2).mean()
>>>
>>> # Create an adaptive clipping function with explicit local state.
>>> # Distributed callers must synchronize state explicitly.
>>> grad_fn, clip_state = adaptive_clipped_grad(
... loss_fn,
... initial_clipping_norm=0.1,
... target_quantile=0.5,
... key=key(0),
... batch_argnums=(1, 2),
... normalize_by=4,
... )
>>>
>>> # Training loop with explicit state-passing
>>> params = torch.randn(10, requires_grad=False)
>>> optimizer = torchopt.adamw(lr=1e-3)
>>> opt_state = optimizer.init(params)
>>>
>>> noise_fn, noise_state = gaussian_noise(noise_multiplier=1.1, key=key(1))
>>> # dataloader yields (x, y) batches (e.g. a torch DataLoader).
>>> dataloader = [(torch.randn(4, 10), torch.randn(4)) for _ in range(3)]
>>> for batch_x, batch_y in dataloader:
... # Compute clipped gradients - state passed explicitly
... grad, clip_state = grad_fn(params, batch_x, batch_y, state=clip_state)
...
... # Add DP noise scaled to current threshold
... noisy_grad, noise_state = noise_fn(grad, noise_state)
...
... # Optimizer step
... updates, opt_state = optimizer.update(noisy_grad, opt_state, params=params)
... params = torchopt.apply_updates(params, updates)
...
... # Monitor adaptation
... # The current DP max_norm is attached to the clipped output.
... current_bound = grad.max_norm
Example with distributed training (DDP with Poisson sampling): >>> import torch.distributed as dist >>> from torch.utils.data import DataLoader >>> from opake.dpsgd.clipping import adaptive_clipped_grad >>> from opake.dpsgd.noise import gaussian_noise >>> from opake.dpsgd.sampling import PoissonSampler >>> from opake.distributed import local_shard, sum_gradients, sync >>> from opake.random import fold_in, key >>> >>> # Initialize distributed first (typically via torchrun). >>> dist.init_process_group(backend='nccl') >>> rank, world_size = dist.get_rank(), dist.get_world_size() >>> >>> # Create adaptive clipping with the global public expected batch size. >>> expected_batch_size = 0.01 * len(dataset) >>> grad_fn, clip_state = adaptive_clipped_grad( ... loss_fn, ... key=key(0), ... batch_argnums=(1, 2), ... normalize_by=expected_batch_size, ... ) >>> noise_fn, noise_state = gaussian_noise(noise_multiplier=1.1, key=key(2)) >>> >>> # Poisson sampling: shard per rank, derive a per-rank sampling key. >>> shard = local_shard(dataset, rank=rank, world_size=world_size) >>> sampler = PoissonSampler( ... shard, sample_rate=0.01, key=fold_in(key(42), rank) ... ) >>> loader = DataLoader(shard, batch_sampler=sampler) >>> >>> for batch_x, batch_y in loader: ... # Each device: compute clipped gradients and local adaptive state ... grad, clip_state = grad_fn(params, batch_x, batch_y, state=clip_state) ... # Explicit sync step for adaptive clipping state. ... clip_state = sync(clip_state) ... ... # Sum clipped gradients across devices ... grad = sum_gradients(grad) ... ... # Add noise and update ... noisy_grad, noise_state = noise_fn(grad, noise_state) ... # ... optimizer step
References
Andrew et al., "Differentially Private Learning with Adaptive Clipping", NeurIPS 2021. https://arxiv.org/abs/1905.03871
Source code in packages/opake-dpsgd/src/opake/api/dpsgd/clipping/_adaptive.py
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 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 | |
auto_clipped_grad(loss_fn, argnums=0, has_aux=False, *, R=1.0, gamma=_DEFAULT_GAMMA, normalize_by=1.0, batch_argnums=1, return_aux=False, return_stats=False, pre_clipping_transform=lambda x: x, microbatch_size=None, dtype=None, second_moment=False, _chunk_compiler=None)
¶
Create a function that computes the sum of AUTO-S scaled per-example gradients.
AUTO-S (Bu et al., 2023) replaces the standard clipping threshold with a per-example scaling
.. math::
\tilde g_i = R \cdot g_i / (\lVert g_i \rVert + \gamma)
so every per-example gradient has L2 norm at most R by
construction. There is no threshold to tune; the effective step size
is absorbed into the optimizer learning rate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
loss_fn
|
Callable
|
Scalar loss function. If |
required |
argnums
|
int | tuple[int, ...]
|
Which argument(s) to differentiate w.r.t. |
0
|
has_aux
|
bool
|
If True, |
False
|
R
|
float | PerGroup
|
Sensitivity max_norm (default 1.0). When |
1.0
|
gamma
|
float
|
Denominator stabilizer :math: |
_DEFAULT_GAMMA
|
normalize_by
|
float
|
Divisor applied to the summed gradients (set to expected batch size for averaged gradients). |
1.0
|
batch_argnums
|
int | tuple[int, ...]
|
Which arguments have a batch dimension. |
1
|
return_aux
|
bool
|
If True, returns per-example diagnostics as
:class: |
False
|
pre_clipping_transform
|
Callable
|
An optional function to apply to the per-example gradients before AUTO-S scaling. The function should consume the gradient pytree for a single example and return a new pytree (possibly with different structure). Can be used to e.g., scale the leaves of the pytree to accommodate preconditioner clipping. Does not affect the sensitivity guarantee. Default is identity function. |
lambda x: x
|
microbatch_size
|
int | None
|
Process the batch in chunks of this size. |
None
|
dtype
|
Any
|
Optional accumulation dtype for the summed gradient. |
None
|
second_moment
|
bool
|
If True, also accumulate the per-example sum of
element-wise squared scaled gradients and return a
:class: |
False
|
return_stats
|
bool
|
If True, return :class: |
False
|
Returns:
| Type | Description |
|---|---|
Callable
|
|
AutoClipState
|
|
tuple[Callable, AutoClipState]
|
|
tuple[Callable, AutoClipState]
|
|
tuple[Callable, AutoClipState]
|
|
Formal guarantee
Under add/remove or zero-out DP, the L2 sensitivity of the
summed gradients is the returned ClippedPytree.max_norm metadata —
a constant independent of the input data. Per-step Gaussian noise
(gaussian_noise, …) reads that value
each step; PLD composition does not require max_norm to be
identical across steps when the accountant models step-varying
sensitivity (for example adaptive clipping with adaclip).
mf_gaussian_noise's matrix-factorization correlated noise does require
max_norm to stay fixed for the whole run — the dispatcher latches
the first-call bound — and AUTO-S satisfies that because R and
normalize_by do not drift. AUTO-S scaling is per-example and adds
no privacy cost beyond gaussian(noise_multiplier) for the
gradient release.
Example
import torch from opake.api.engine.clipping import auto_clipped_grad def loss_fn(params, x, y): ... return ((x @ params - y) ** 2).mean() grad_fn, state = auto_clipped_grad( ... loss_fn, argnums=0, batch_argnums=(1, 2), ... R=1.0, normalize_by=32, ... ) params = torch.randn(10) batch_x = torch.randn(32, 10) batch_y = torch.randn(32) grads, state = grad_fn(params, batch_x, batch_y, state=state)
The returned ClippedPytree carries max_norm = R / normalize_by
(constant across steps), so it composes with mf_gaussian_noise (DP-FTRL)
and gaussian_noise (DP-SGD) the same way clipped_grad does at
the same bound.
References
Bu, Wang, Zha, Karypis. "Automatic Clipping: Differentially Private Deep Learning Made Easier and Stronger." NeurIPS 2023. https://arxiv.org/abs/2206.07136
Source code in packages/opake-engine/src/opake/api/engine/clipping/_auto.py
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 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | |
clipped_grad(loss_fn, argnums=0, has_aux=False, *, clipping_norm, normalize_by=1.0, batch_argnums=1, return_aux=False, return_stats=False, second_moment=False, pre_clipping_transform=lambda x: x, microbatch_size=None, dtype=None, compute_dtype=None, _scale_fn=None, _builtin_scale=None, _chunk_compiler=None)
¶
Create a function to compute the sum of clipped gradients of loss_fn.
This function acts as a transformation similar to torch.func.grad, but with added
functionality for gradient clipping applied on a per-example (or per-group)
basis before summation. It computes the gradient of loss_fn with respect to
argnums, calculates the L2 norm of the gradient for each example slice
along the first axis of the batch_argnums args, clips each per-example
gradient to have a norm of at most clipping_norm, and finally sums these
clipped gradients.
Non-grad outputs of the returned function (aux) may optionally be returned
by setting return_aux=True. These outputs are per-example, and hence have
a batch axis. It is up to the caller to handle these as necessary.
Example Usage
import torch from opake.dpsgd.clipping import clipped_grad f = lambda param, data: 0.5 * ((data - param) ** 2).mean() g, clip_state = clipped_grad(f, clipping_norm=float('inf')) result, clip_state = g(torch.tensor(3.0), torch.tensor([0.0, 7.0, -2.0]), state=clip_state) result tensor(1.3333)
Example Usage (with Auxiliary Output): >>> g, clip_state = clipped_grad( ... f, clipping_norm=float('inf'), return_aux=True ... ) >>> (_, aux), clip_state = g(torch.tensor(3.0), torch.tensor([0.0, 7.0, -2.0]), state=clip_state) >>> aux.loss_values tensor([4.5000, 8.0000, 12.5000]) >>> aux.grad_norms tensor([3., 4., 5.])
Formal Guarantees
For the gradient output:
The L2 sensitivity of the returned function with respect to the batch
arguments (specified by batch_argnums) under add/remove or zero-out
differential privacy definitions is guaranteed to be clipping_norm.
Under replace-one DP, the sensitivity is doubled (2 * clipping_norm).
All auxiliary outputs (loss_values, grad_norms) are per-example. This
function guarantees that per-example outputs only depend on the data for the
same example. This allows maximum flexibility for the caller to aggregate
these as desired (possibly with a DP mean, median, quantile, or histogram
mechanism).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
loss_fn
|
Callable
|
The loss function to be differentiated, which should return a scalar
value. If |
required |
argnums
|
int | tuple[int, ...]
|
Specifies which argument(s) of |
0
|
has_aux
|
bool
|
If True, |
False
|
clipping_norm
|
float | PerGroup
|
The maximum L2 norm for each per-example gradient. Gradients with a norm larger than this value will be scaled down. |
required |
normalize_by
|
float
|
Divide the clipped sum by this constant before returning. Set to expected batch size to produce averaged gradients with sensitivity = clipping_norm / normalize_by. |
1.0
|
batch_argnums
|
int | tuple[int, ...]
|
Specifies which argument(s) of |
1
|
return_aux
|
bool
|
If True, the transformed function will also return a per-example aux dataclass containing loss values, gradient norms, and loss aux. |
False
|
return_stats
|
bool
|
If True, return aggregate clipping statistics including
the number and fraction of clipped examples and the batch size.
Cannot be combined with |
False
|
pre_clipping_transform
|
Callable
|
An optional function to apply to the per-example gradients before clipping. The function should consume the gradient pytree for a single example and return a new pytree (possibly with different structure). Can be used to e.g., scale the leaves of the pytree to accommodate preconditioner clipping. Account separately for any data-dependent control flow or telemetry. Default is identity function. |
lambda x: x
|
microbatch_size
|
int | None
|
If set, the batch is split up into microbatches of this size for memory-efficient processing. Processes each microbatch separately and accumulates results without materializing the full batch of gradients. Set this to reduce peak memory usage at the cost of slightly slower computation. |
None
|
dtype
|
dtype | None
|
Optional dtype for the returned gradient. If None, the dtype will be the same as the dtypes of the gradient function. Can be useful to avoid overflow issues when using low-precision dtypes as the returned function computes a sum over a potentially large batch. |
None
|
compute_dtype
|
dtype | None
|
Internal reduction and accumulation dtype. |
None
|
second_moment
|
bool
|
Whether to accumulate the clipped-gradient second moment required by DP-FTRL noise mechanisms. |
False
|
Returns:
Tuple of (:class:ClippedGradFn, clip_state) where:
- clipped_grad_fn: A function that computes the sum of clipped per-example gradients.
Call signature: clipped_grads, new_state = clipped_grad_fn(..., state=clip_state)
If auxiliary outputs are requested, returns: (clipped_grads, grad_aux), new_state
If return_stats=True, returns (clipped_grads, stats), new_state.
- clip_state: Initial FixedClipState containing sensitivity information
The grad_aux output (when requested) is a ClippedGradAux dataclass with fields:
- loss_values: Per-example function values (if return_aux=True)
- grad_norms: Per-example gradient norms (if return_aux=True)
- loss_aux: Per-example auxiliary data (if has_aux=True)
Source code in packages/opake-engine/src/opake/api/engine/clipping/_clipped_grad.py
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 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 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | |
per_group(params, /, patterns=None, *, fallback=None, allow_unused_patterns=False, **kwargs)
¶
Construct PerGroup from parameter leaf paths and substring patterns.
Each pattern is a substring matched against the dotted display form of
each leaf :data:~opake.pytree.ParamPath (see
:func:~opake.pytree.param_path_display). Leaves whose display path
contains the substring are assigned to that group. Every leaf must
match exactly one pattern (error on 0 or 2+).
params may be any tensor pytree: flat named_parameters dicts
(paths are one-segment (name,)), nested dicts, lists, or tuples.
The returned :class:~opake.types.PerGroup is compiled against those
optree paths so clip / noise / optimizers look up the same identity.
The patterns dict arg merges with **kwargs to support keys
containing dots (which cannot be used as keyword arguments)::
per_group(params, **{"layers.0": 0.5, "layers.1": 1.0})
# equivalent to:
per_group(params, patterns={"layers.0": 0.5, "layers.1": 1.0})
The fallback parameter assigns a value to any parameter that does
not match an explicit pattern. Without fallback, unmatched
parameters raise ValueError.
By default, every explicit pattern must match at least one parameter.
Set allow_unused_patterns=True when intentionally reusing a
configuration across models with different parameter names.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
Any
|
Parameter pytree (flat or nested). Leaf paths are matched against patterns via their dotted display form. |
required |
patterns
|
dict[str, float] | None
|
Optional dict of |
None
|
fallback
|
float | None
|
Optional value for parameters that don't match any
explicit pattern. Unmatched params are assigned to a group
named |
None
|
allow_unused_patterns
|
bool
|
Whether to allow explicit patterns that do
not match any parameter. Defaults to |
False
|
**kwargs
|
float
|
Pattern-value pairs where the kwarg name is the substring pattern and the value is the per-group value (e.g. clipping norm). |
{}
|
Returns:
| Type | Description |
|---|---|
PerGroup
|
class: |
PerGroup
|
path-to-group assignments. |
See also
:mod:opake.serialization — persist or restore a PerGroup with
state_dict / from_state_dict. Call :func:per_group again
when parameter keys or grouping patterns change.
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If no patterns are provided, if a parameter matches zero
patterns (and no |
Examples:
>>> per_group(params, self_attn=1.0, mlp=2.0)
>>> per_group(params, self_attn=1.0, fallback=0.5) # catch-all
>>> per_group(params, q_proj=0.5, k_proj=0.5, v_proj=0.5, o_proj=0.8,
... gate_proj=1.0, up_proj=1.0, down_proj=1.0)
>>> per_group(params, **{f'layers.{i}': norms[i] for i in range(32)})
Source code in packages/opake-engine/src/opake/api/engine/clipping/_per_group.py
44 45 46 47 48 49 50 51 52 53 54 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 | |
fun
¶
DP-SGD clipping power-user helpers.
- :func:
clipped_fun/ :func:auto_clipped_fun— clip + sum any per-example function output (PyTree). - :func:
clip_pytree/ :func:auto_scale_pytree— clip / AUTO-S an already-batched pytree.
auto_clipped_fun(fun, has_aux=False, *, batch_argnums=0, R=1.0, gamma=_DEFAULT_GAMMA, normalize_by=1.0, return_aux=False, return_stats=False, microbatch_size=None, dtype=None, _chunk_compiler=None)
¶
Transform a function so each per-example output is scaled to clipped norm via AUTO-S.
Mirrors :func:clipped_fun but replaces threshold-based clipping with
AUTO-S automatic scaling R \cdot v / (\|v\| + \gamma). The output of
the returned function is the sum of the scaled per-example outputs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fun
|
Callable
|
The per-example function to be transformed. |
required |
has_aux
|
bool
|
If True, |
False
|
batch_argnums
|
int | tuple[int, ...]
|
Which arguments have a batch dimension. |
0
|
R
|
float | PerGroup
|
Sensitivity max_norm for the scaled output. When |
1.0
|
gamma
|
float
|
Denominator stabilizer :math: |
_DEFAULT_GAMMA
|
normalize_by
|
float
|
Divisor applied to the scaled sum. |
1.0
|
return_aux
|
bool
|
If True, the returned callable returns an
:class: |
False
|
return_stats
|
bool
|
If True, return :class: |
False
|
microbatch_size
|
int | None
|
Process the batch in chunks of this size. |
None
|
dtype
|
Any
|
Optional accumulation dtype for the sum. |
None
|
Returns:
| Type | Description |
|---|---|
Callable
|
|
AutoClipState
|
|
tuple[Callable, AutoClipState]
|
|
tuple[Callable, AutoClipState]
|
|
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If the batch is empty. |
Formal guarantee
Under add/remove or zero-out DP, the L2 sensitivity of the first
output with respect to the batch arguments is the returned
ClippedPytree.max_norm metadata. The bound is constant and
data-independent, so per-step Gaussian calibration is correct.
mf_gaussian_noise additionally requires that max_norm not drift across
steps; AUTO-S satisfies that latch because R and normalize_by
are fixed.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_auto.py
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 204 205 206 207 208 | |
auto_scale_pytree(pytree, R=1.0, gamma=0.01, *, compute_dtype=None)
¶
AUTO-S automatic scaling of a PyTree (Bu et al., NeurIPS 2023).
Scales the PyTree by R / (\|pytree\| + gamma) so the output L2
norm is clipped by R for any input. Unlike :func:clip_pytree, there
is no threshold to tune — every example contributes an approximately
unit-length update, with the effective step size absorbed into the
learning rate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pytree
|
Any
|
Tensor pytree to scale (flat or nested). |
required |
R
|
float | PerGroup
|
Output sensitivity bound (non-negative). When |
1.0
|
gamma
|
float
|
Small positive denominator stabilizer :math: |
0.01
|
compute_dtype
|
dtype | None
|
Internal dtype for the L2-norm reduction and leaf
scaling. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
Tuple of (scaled_pytree, aux) where |
ClipPytreeAux
|
norm and |
tuple[Any, ClipPytreeAux]
|
mode. |
Formal guarantee
For every input, the output has L2 norm at most R (scalar case)
or :math:\sqrt{\sum_k R_k^2} (per-group case). The bound holds on
the values as stored: each leaf is scaled in compute_dtype (never
below its storage precision) and cast once to its output dtype. The
scale is shrunk by the final cast's rounding margin so the result cannot
exceed R. Unlike :func:clip_pytree there is no min(1, ...), so
the shrink applies to every input — roughly 0.4% of the output magnitude
at bfloat16, 0.05% at float16, and a few ULPs at float32.
NaN/Inf values are sanitized to zero before scaling, matching the
behavior of :func:clip_pytree.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_pytree.py
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | |
clip_pytree(pytree, clipping_norm, return_zero=False, *, compute_dtype=None)
¶
Clip a PyTree of tensors to a maximum L2 norm.
NaN and Inf values in the input are replaced with zeros before clipping.
This is vmap-compatible and DP-safe: norm(output) <= clipping_norm holds
on the values as stored, not just in exact arithmetic. The norm reduction,
the multiplication run in compute_dtype (never below the leaf's storage
precision), and the result is cast once to the leaf dtype. That cast can
round up, so each leaf's scale is shrunk by its final-rounding margin. The
shrink is applied before the min(1, ...), so an input well inside the
threshold is returned unchanged; one whose norm sits within a few ULPs of
clipping_norm is scaled by that much. For clipped inputs the cost is
roughly 0.4% of the output magnitude at bfloat16, 0.05% at float16, and a
few ULPs at float32.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pytree
|
Any
|
Tensor pytree to clip (flat or nested). |
required |
clipping_norm
|
float | PerGroup
|
Maximum L2 norm (non-negative, or inf for no clipping).
When |
required |
return_zero
|
bool
|
If True, the output PyTree is guaranteed to be zero no matter what the inputs are. Does not influence the formal guarantees but useful for privacy amplification via padding (see https://arxiv.org/pdf/2411.04205). |
False
|
compute_dtype
|
dtype | None
|
Internal dtype for the L2-norm reduction and leaf
scaling. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[Any, ClipPytreeAux]
|
Tuple of (clipped_pytree, aux) where aux contains: - norm: The L2 norm of the original (unclipped) pytree |
Edge cases
- clipping_norm=0: Returns zeros
- clipping_norm=inf: No clipping (passthrough)
- pytree_norm=0: Returns unchanged
- NaN/Inf values: Replaced with zeros before clipping
- return_zero=True: Returns zeros regardless of other parameters
Source code in packages/opake-engine/src/opake/api/engine/clipping/_pytree.py
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 | |
clipped_fun(fun, has_aux=False, *, batch_argnums=0, clipping_norm=1.0, normalize_by=1.0, return_aux=False, return_stats=False, second_moment=False, microbatch_size=None, dtype=None, compute_dtype=None, _scale_fn=None, _builtin_scale=None, _chunk_compiler=None)
¶
Transform a function to clip its output and sum across a batch.
This is the primary API for per-example clipping in DP-SGD. It wraps a function to clip each per-example output to a maximum L2 norm, then sums the clipped outputs.
The returned pytree is wrapped as :class:ClippedPytree (single-stream)
or :class:SecondMomentClippingOutput (paired-stream when
second_moment=True), carrying the post-normalization
max_norm for downstream noise calibration. The bound is part of
the contract: consumers (gaussian_noise, mf_gaussian_noise) read it
directly without the caller threading a separate sensitivity
argument. Unwrap to a raw pytree via .pytree if you need the
summed values without metadata.
Example Usage
from opake.api.engine.clipping._clipped_fun import clipped_fun data = torch.tensor([0.0, 1.0, 2.0, 3.0, 4.0, 5.0]) clipped_mean, clip_state = clipped_fun(torch.mean, clipping_norm=1.0) result, clip_state = clipped_mean(data, state=clip_state) result.pytree tensor(5.) result.max_norm 1.0
Formal Guarantees
For the first function output:
The L2 sensitivity of the returned function with respect to the batch
arguments (specified by batch_argnums) under add/remove or zero-out
differential privacy definitions is guaranteed to be clipping_norm.
Under replace-one DP, the sensitivity is doubled (2 * clipping_norm).
Extra auxiliary outputs (aux, norms) are per-example. This function
guarantees that per-example outputs only depend on the data for the same
example. This allows maximum flexibility for the caller to aggregate
these as desired (possibly with a DP mean, median, quantile, or histogram
mechanism).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fun
|
Callable[..., Any]
|
The function to be clipped. |
required |
has_aux
|
bool
|
If True, |
False
|
batch_argnums
|
int | tuple[int, ...]
|
Specifies which argument(s) of |
0
|
clipping_norm
|
float | PerGroup
|
The maximum L2 norm allowed. |
1.0
|
normalize_by
|
float
|
Divide the clipped output by this value before returning. |
1.0
|
return_aux
|
bool
|
If True, the returned Callable will return a per-example aux
dataclass containing the original per-example values, per-example norms
before clipping, and any auxiliary data returned by |
False
|
return_stats
|
bool
|
If True, return :class: |
False
|
second_moment
|
bool
|
If True, also accumulate the element-wise sum of
per-example squared clipped values, i.e. |
False
|
microbatch_size
|
int | None
|
If set, the batch is split up into microbatches of this
size for memory-efficient processing. Processes each microbatch separately
and accumulates results without materializing the full batch of gradients.
Set this to reduce peak memory usage at the cost of slightly slower computation.
The running sum is held in the wider of |
None
|
dtype
|
dtype | None
|
Optional dtype for the clipped+aggregated pytree. If None, the dtype will be the same as the dtypes of the function output. |
None
|
compute_dtype
|
dtype | None
|
Internal reduction and accumulation dtype. |
None
|
Returns:
A tuple (clip_fn, FixedClipState) where clip_fn(*args, state=...)
clips the output of fun and sums across the batch. The exact
return shape depends on second_moment and return_aux:
| ``second_moment`` | ``return_aux`` | ``clip_fn`` returns |
| :---------------- | :------------- | :------------------------------------------------- |
| False | False | ``(ClippedPytree, state)`` |
| False | True | ``((ClippedPytree, ClippedFunAux), state)`` |
| True | False | ``(SecondMomentClippingOutput, state)`` |
| True | True | ``((SecondMomentClippingOutput, ClippedFunAux), state)`` |
With ``return_stats=True`` and ``return_aux=False``, the clipped value
is paired with :class:`ClippingStats`.
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If the batch is empty or a chunk compiler is used without a finite microbatch size. |
Source code in packages/opake-engine/src/opake/api/engine/clipping/_clipped_fun.py
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 | |
types
¶
DP-SGD clipping types façade.
Re-exports adaptive (DP-SGD) and AUTO-S clipping state / aux dataclasses for type annotations from the impl trees.
ClippedGradResult = ClippedGradValue | tuple[ClippedGradValue, 'ClippedGradAux'] | tuple[ClippedGradValue, 'ClippingStats']
module-attribute
¶
Value returned by :class:ClippedGradFn before its updated state.
AdaptiveClippedGradAux
dataclass
¶
Bases: ClippedGradAux
Diagnostic outputs from adaptive_clipped_grad.
All fields are diagnostic — they reflect pre-noise, pre-aggregation
values and must not be fed back into private computation. Use the
returned ClippedPytree.max_norm metadata for noise calibration.
Inherits all fields from :class:ClippedGradAux.
Source code in packages/opake-dpsgd/src/opake/api/dpsgd/clipping/_adaptive.py
AdaptiveClipState
dataclass
¶
Bases: ClipState
Immutable state for adaptive gradient clipping.
This state is passed explicitly to the clipping function and returned as part of the output, enabling pure functional composition.
The fields are internal implementation details. Adaptive clipping must carry the current threshold, next-step threshold, step counter, RNG/config, and local clipping counts so distributed synchronization can aggregate counts and recompute the global next threshold.
Source code in packages/opake-dpsgd/src/opake/api/dpsgd/clipping/_adaptive.py
__post_init__()
¶
Validate state values.
Source code in packages/opake-dpsgd/src/opake/api/dpsgd/clipping/_adaptive.py
AutoClippedFunAux
dataclass
¶
Bases: ClippedFunAux
Diagnostic outputs from auto_clipped_fun.
Inherits all fields from :class:ClippedFunAux with AUTO-S
semantics:
norms: per-example L2 norms before scaling (i.e.||v||).clipped_norms: per-example L2 norms after AUTO-S scaling (R * ||v|| / (||v|| + gamma)), clipped byR.clipping_rate: fraction of examples with||v|| > R(approximately the fraction where the scale factor was < 1).values,value_aux,batch_size,group_norms: unchanged from :class:ClippedFunAux.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_auto.py
AutoClippedGradAux
dataclass
¶
Bases: ClippedGradAux
Diagnostic outputs from auto_clipped_grad.
Inherits all fields from :class:ClippedGradAux with AUTO-S
semantics:
grad_norms: per-example gradient L2 norms before scaling.clipped_grad_norms: per-example gradient L2 norms after AUTO-S scaling (R * ||g|| / (||g|| + gamma)), clipped byR.clipping_rate: fraction of examples with||g|| > R(approximately the fraction where the scale factor was < 1).loss_values,loss_aux,batch_size,group_norms: unchanged from :class:ClippedGradAux.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_auto.py
AutoClipState
dataclass
¶
ClippedGradFn
¶
Bases: Protocol
Callable returned by :func:opake.dpsgd.clipping.clipped_grad.
The positional arguments mirror the wrapped loss function. state is
passed by keyword and every invocation returns a new immutable
:class:FixedClipState with the clipped gradient result.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_types.py
ClippingStats
dataclass
¶
Aggregated clipping statistics without per-example materialization.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_clipped_fun.py
FixedClipState
dataclass
¶
Bases: ClipState
Marker state for fixed (non-adaptive) clipping.
Returned by :func:clipped_fun and :func:opake.api.engine.clipping.clipped_grad.
Carries no fields; the configured clipping threshold flows through
the ClippedPytree.max_norm metadata, not through the state.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_clipped_fun.py
clipping
¶
DP-FTRL clipping façade — fixed threshold, AUTO-S, and per-group norms.
Adaptive thresholding is only available under :mod:opake.dpsgd.clipping
(DP-SGD). State and aux types live in :mod:opake.dpftrl.clipping.types.
auto_clipped_grad(loss_fn, argnums=0, has_aux=False, *, R=1.0, gamma=_DEFAULT_GAMMA, normalize_by=1.0, batch_argnums=1, return_aux=False, return_stats=False, pre_clipping_transform=lambda x: x, microbatch_size=None, dtype=None, second_moment=False, _chunk_compiler=None)
¶
Create a function that computes the sum of AUTO-S scaled per-example gradients.
AUTO-S (Bu et al., 2023) replaces the standard clipping threshold with a per-example scaling
.. math::
\tilde g_i = R \cdot g_i / (\lVert g_i \rVert + \gamma)
so every per-example gradient has L2 norm at most R by
construction. There is no threshold to tune; the effective step size
is absorbed into the optimizer learning rate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
loss_fn
|
Callable
|
Scalar loss function. If |
required |
argnums
|
int | tuple[int, ...]
|
Which argument(s) to differentiate w.r.t. |
0
|
has_aux
|
bool
|
If True, |
False
|
R
|
float | PerGroup
|
Sensitivity max_norm (default 1.0). When |
1.0
|
gamma
|
float
|
Denominator stabilizer :math: |
_DEFAULT_GAMMA
|
normalize_by
|
float
|
Divisor applied to the summed gradients (set to expected batch size for averaged gradients). |
1.0
|
batch_argnums
|
int | tuple[int, ...]
|
Which arguments have a batch dimension. |
1
|
return_aux
|
bool
|
If True, returns per-example diagnostics as
:class: |
False
|
pre_clipping_transform
|
Callable
|
An optional function to apply to the per-example gradients before AUTO-S scaling. The function should consume the gradient pytree for a single example and return a new pytree (possibly with different structure). Can be used to e.g., scale the leaves of the pytree to accommodate preconditioner clipping. Does not affect the sensitivity guarantee. Default is identity function. |
lambda x: x
|
microbatch_size
|
int | None
|
Process the batch in chunks of this size. |
None
|
dtype
|
Any
|
Optional accumulation dtype for the summed gradient. |
None
|
second_moment
|
bool
|
If True, also accumulate the per-example sum of
element-wise squared scaled gradients and return a
:class: |
False
|
return_stats
|
bool
|
If True, return :class: |
False
|
Returns:
| Type | Description |
|---|---|
Callable
|
|
AutoClipState
|
|
tuple[Callable, AutoClipState]
|
|
tuple[Callable, AutoClipState]
|
|
tuple[Callable, AutoClipState]
|
|
Formal guarantee
Under add/remove or zero-out DP, the L2 sensitivity of the
summed gradients is the returned ClippedPytree.max_norm metadata —
a constant independent of the input data. Per-step Gaussian noise
(gaussian_noise, …) reads that value
each step; PLD composition does not require max_norm to be
identical across steps when the accountant models step-varying
sensitivity (for example adaptive clipping with adaclip).
mf_gaussian_noise's matrix-factorization correlated noise does require
max_norm to stay fixed for the whole run — the dispatcher latches
the first-call bound — and AUTO-S satisfies that because R and
normalize_by do not drift. AUTO-S scaling is per-example and adds
no privacy cost beyond gaussian(noise_multiplier) for the
gradient release.
Example
import torch from opake.api.engine.clipping import auto_clipped_grad def loss_fn(params, x, y): ... return ((x @ params - y) ** 2).mean() grad_fn, state = auto_clipped_grad( ... loss_fn, argnums=0, batch_argnums=(1, 2), ... R=1.0, normalize_by=32, ... ) params = torch.randn(10) batch_x = torch.randn(32, 10) batch_y = torch.randn(32) grads, state = grad_fn(params, batch_x, batch_y, state=state)
The returned ClippedPytree carries max_norm = R / normalize_by
(constant across steps), so it composes with mf_gaussian_noise (DP-FTRL)
and gaussian_noise (DP-SGD) the same way clipped_grad does at
the same bound.
References
Bu, Wang, Zha, Karypis. "Automatic Clipping: Differentially Private Deep Learning Made Easier and Stronger." NeurIPS 2023. https://arxiv.org/abs/2206.07136
Source code in packages/opake-engine/src/opake/api/engine/clipping/_auto.py
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 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | |
clipped_grad(loss_fn, argnums=0, has_aux=False, *, clipping_norm, normalize_by=1.0, batch_argnums=1, return_aux=False, return_stats=False, second_moment=False, pre_clipping_transform=lambda x: x, microbatch_size=None, dtype=None, compute_dtype=None, _scale_fn=None, _builtin_scale=None, _chunk_compiler=None)
¶
Create a function to compute the sum of clipped gradients of loss_fn.
This function acts as a transformation similar to torch.func.grad, but with added
functionality for gradient clipping applied on a per-example (or per-group)
basis before summation. It computes the gradient of loss_fn with respect to
argnums, calculates the L2 norm of the gradient for each example slice
along the first axis of the batch_argnums args, clips each per-example
gradient to have a norm of at most clipping_norm, and finally sums these
clipped gradients.
Non-grad outputs of the returned function (aux) may optionally be returned
by setting return_aux=True. These outputs are per-example, and hence have
a batch axis. It is up to the caller to handle these as necessary.
Example Usage
import torch from opake.dpsgd.clipping import clipped_grad f = lambda param, data: 0.5 * ((data - param) ** 2).mean() g, clip_state = clipped_grad(f, clipping_norm=float('inf')) result, clip_state = g(torch.tensor(3.0), torch.tensor([0.0, 7.0, -2.0]), state=clip_state) result tensor(1.3333)
Example Usage (with Auxiliary Output): >>> g, clip_state = clipped_grad( ... f, clipping_norm=float('inf'), return_aux=True ... ) >>> (_, aux), clip_state = g(torch.tensor(3.0), torch.tensor([0.0, 7.0, -2.0]), state=clip_state) >>> aux.loss_values tensor([4.5000, 8.0000, 12.5000]) >>> aux.grad_norms tensor([3., 4., 5.])
Formal Guarantees
For the gradient output:
The L2 sensitivity of the returned function with respect to the batch
arguments (specified by batch_argnums) under add/remove or zero-out
differential privacy definitions is guaranteed to be clipping_norm.
Under replace-one DP, the sensitivity is doubled (2 * clipping_norm).
All auxiliary outputs (loss_values, grad_norms) are per-example. This
function guarantees that per-example outputs only depend on the data for the
same example. This allows maximum flexibility for the caller to aggregate
these as desired (possibly with a DP mean, median, quantile, or histogram
mechanism).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
loss_fn
|
Callable
|
The loss function to be differentiated, which should return a scalar
value. If |
required |
argnums
|
int | tuple[int, ...]
|
Specifies which argument(s) of |
0
|
has_aux
|
bool
|
If True, |
False
|
clipping_norm
|
float | PerGroup
|
The maximum L2 norm for each per-example gradient. Gradients with a norm larger than this value will be scaled down. |
required |
normalize_by
|
float
|
Divide the clipped sum by this constant before returning. Set to expected batch size to produce averaged gradients with sensitivity = clipping_norm / normalize_by. |
1.0
|
batch_argnums
|
int | tuple[int, ...]
|
Specifies which argument(s) of |
1
|
return_aux
|
bool
|
If True, the transformed function will also return a per-example aux dataclass containing loss values, gradient norms, and loss aux. |
False
|
return_stats
|
bool
|
If True, return aggregate clipping statistics including
the number and fraction of clipped examples and the batch size.
Cannot be combined with |
False
|
pre_clipping_transform
|
Callable
|
An optional function to apply to the per-example gradients before clipping. The function should consume the gradient pytree for a single example and return a new pytree (possibly with different structure). Can be used to e.g., scale the leaves of the pytree to accommodate preconditioner clipping. Account separately for any data-dependent control flow or telemetry. Default is identity function. |
lambda x: x
|
microbatch_size
|
int | None
|
If set, the batch is split up into microbatches of this size for memory-efficient processing. Processes each microbatch separately and accumulates results without materializing the full batch of gradients. Set this to reduce peak memory usage at the cost of slightly slower computation. |
None
|
dtype
|
dtype | None
|
Optional dtype for the returned gradient. If None, the dtype will be the same as the dtypes of the gradient function. Can be useful to avoid overflow issues when using low-precision dtypes as the returned function computes a sum over a potentially large batch. |
None
|
compute_dtype
|
dtype | None
|
Internal reduction and accumulation dtype. |
None
|
second_moment
|
bool
|
Whether to accumulate the clipped-gradient second moment required by DP-FTRL noise mechanisms. |
False
|
Returns:
Tuple of (:class:ClippedGradFn, clip_state) where:
- clipped_grad_fn: A function that computes the sum of clipped per-example gradients.
Call signature: clipped_grads, new_state = clipped_grad_fn(..., state=clip_state)
If auxiliary outputs are requested, returns: (clipped_grads, grad_aux), new_state
If return_stats=True, returns (clipped_grads, stats), new_state.
- clip_state: Initial FixedClipState containing sensitivity information
The grad_aux output (when requested) is a ClippedGradAux dataclass with fields:
- loss_values: Per-example function values (if return_aux=True)
- grad_norms: Per-example gradient norms (if return_aux=True)
- loss_aux: Per-example auxiliary data (if has_aux=True)
Source code in packages/opake-engine/src/opake/api/engine/clipping/_clipped_grad.py
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 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 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | |
per_group(params, /, patterns=None, *, fallback=None, allow_unused_patterns=False, **kwargs)
¶
Construct PerGroup from parameter leaf paths and substring patterns.
Each pattern is a substring matched against the dotted display form of
each leaf :data:~opake.pytree.ParamPath (see
:func:~opake.pytree.param_path_display). Leaves whose display path
contains the substring are assigned to that group. Every leaf must
match exactly one pattern (error on 0 or 2+).
params may be any tensor pytree: flat named_parameters dicts
(paths are one-segment (name,)), nested dicts, lists, or tuples.
The returned :class:~opake.types.PerGroup is compiled against those
optree paths so clip / noise / optimizers look up the same identity.
The patterns dict arg merges with **kwargs to support keys
containing dots (which cannot be used as keyword arguments)::
per_group(params, **{"layers.0": 0.5, "layers.1": 1.0})
# equivalent to:
per_group(params, patterns={"layers.0": 0.5, "layers.1": 1.0})
The fallback parameter assigns a value to any parameter that does
not match an explicit pattern. Without fallback, unmatched
parameters raise ValueError.
By default, every explicit pattern must match at least one parameter.
Set allow_unused_patterns=True when intentionally reusing a
configuration across models with different parameter names.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
Any
|
Parameter pytree (flat or nested). Leaf paths are matched against patterns via their dotted display form. |
required |
patterns
|
dict[str, float] | None
|
Optional dict of |
None
|
fallback
|
float | None
|
Optional value for parameters that don't match any
explicit pattern. Unmatched params are assigned to a group
named |
None
|
allow_unused_patterns
|
bool
|
Whether to allow explicit patterns that do
not match any parameter. Defaults to |
False
|
**kwargs
|
float
|
Pattern-value pairs where the kwarg name is the substring pattern and the value is the per-group value (e.g. clipping norm). |
{}
|
Returns:
| Type | Description |
|---|---|
PerGroup
|
class: |
PerGroup
|
path-to-group assignments. |
See also
:mod:opake.serialization — persist or restore a PerGroup with
state_dict / from_state_dict. Call :func:per_group again
when parameter keys or grouping patterns change.
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If no patterns are provided, if a parameter matches zero
patterns (and no |
Examples:
>>> per_group(params, self_attn=1.0, mlp=2.0)
>>> per_group(params, self_attn=1.0, fallback=0.5) # catch-all
>>> per_group(params, q_proj=0.5, k_proj=0.5, v_proj=0.5, o_proj=0.8,
... gate_proj=1.0, up_proj=1.0, down_proj=1.0)
>>> per_group(params, **{f'layers.{i}': norms[i] for i in range(32)})
Source code in packages/opake-engine/src/opake/api/engine/clipping/_per_group.py
44 45 46 47 48 49 50 51 52 53 54 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 | |
fun
¶
DP-FTRL clipping power-user helpers.
- :func:
clipped_fun/ :func:auto_clipped_fun— clip + sum any per-example function output (PyTree). - :func:
clip_pytree/ :func:auto_scale_pytree— clip / AUTO-S an already-batched pytree.
auto_clipped_fun(fun, has_aux=False, *, batch_argnums=0, R=1.0, gamma=_DEFAULT_GAMMA, normalize_by=1.0, return_aux=False, return_stats=False, microbatch_size=None, dtype=None, _chunk_compiler=None)
¶
Transform a function so each per-example output is scaled to clipped norm via AUTO-S.
Mirrors :func:clipped_fun but replaces threshold-based clipping with
AUTO-S automatic scaling R \cdot v / (\|v\| + \gamma). The output of
the returned function is the sum of the scaled per-example outputs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fun
|
Callable
|
The per-example function to be transformed. |
required |
has_aux
|
bool
|
If True, |
False
|
batch_argnums
|
int | tuple[int, ...]
|
Which arguments have a batch dimension. |
0
|
R
|
float | PerGroup
|
Sensitivity max_norm for the scaled output. When |
1.0
|
gamma
|
float
|
Denominator stabilizer :math: |
_DEFAULT_GAMMA
|
normalize_by
|
float
|
Divisor applied to the scaled sum. |
1.0
|
return_aux
|
bool
|
If True, the returned callable returns an
:class: |
False
|
return_stats
|
bool
|
If True, return :class: |
False
|
microbatch_size
|
int | None
|
Process the batch in chunks of this size. |
None
|
dtype
|
Any
|
Optional accumulation dtype for the sum. |
None
|
Returns:
| Type | Description |
|---|---|
Callable
|
|
AutoClipState
|
|
tuple[Callable, AutoClipState]
|
|
tuple[Callable, AutoClipState]
|
|
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If the batch is empty. |
Formal guarantee
Under add/remove or zero-out DP, the L2 sensitivity of the first
output with respect to the batch arguments is the returned
ClippedPytree.max_norm metadata. The bound is constant and
data-independent, so per-step Gaussian calibration is correct.
mf_gaussian_noise additionally requires that max_norm not drift across
steps; AUTO-S satisfies that latch because R and normalize_by
are fixed.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_auto.py
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 204 205 206 207 208 | |
auto_scale_pytree(pytree, R=1.0, gamma=0.01, *, compute_dtype=None)
¶
AUTO-S automatic scaling of a PyTree (Bu et al., NeurIPS 2023).
Scales the PyTree by R / (\|pytree\| + gamma) so the output L2
norm is clipped by R for any input. Unlike :func:clip_pytree, there
is no threshold to tune — every example contributes an approximately
unit-length update, with the effective step size absorbed into the
learning rate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pytree
|
Any
|
Tensor pytree to scale (flat or nested). |
required |
R
|
float | PerGroup
|
Output sensitivity bound (non-negative). When |
1.0
|
gamma
|
float
|
Small positive denominator stabilizer :math: |
0.01
|
compute_dtype
|
dtype | None
|
Internal dtype for the L2-norm reduction and leaf
scaling. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
Tuple of (scaled_pytree, aux) where |
ClipPytreeAux
|
norm and |
tuple[Any, ClipPytreeAux]
|
mode. |
Formal guarantee
For every input, the output has L2 norm at most R (scalar case)
or :math:\sqrt{\sum_k R_k^2} (per-group case). The bound holds on
the values as stored: each leaf is scaled in compute_dtype (never
below its storage precision) and cast once to its output dtype. The
scale is shrunk by the final cast's rounding margin so the result cannot
exceed R. Unlike :func:clip_pytree there is no min(1, ...), so
the shrink applies to every input — roughly 0.4% of the output magnitude
at bfloat16, 0.05% at float16, and a few ULPs at float32.
NaN/Inf values are sanitized to zero before scaling, matching the
behavior of :func:clip_pytree.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_pytree.py
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | |
clip_pytree(pytree, clipping_norm, return_zero=False, *, compute_dtype=None)
¶
Clip a PyTree of tensors to a maximum L2 norm.
NaN and Inf values in the input are replaced with zeros before clipping.
This is vmap-compatible and DP-safe: norm(output) <= clipping_norm holds
on the values as stored, not just in exact arithmetic. The norm reduction,
the multiplication run in compute_dtype (never below the leaf's storage
precision), and the result is cast once to the leaf dtype. That cast can
round up, so each leaf's scale is shrunk by its final-rounding margin. The
shrink is applied before the min(1, ...), so an input well inside the
threshold is returned unchanged; one whose norm sits within a few ULPs of
clipping_norm is scaled by that much. For clipped inputs the cost is
roughly 0.4% of the output magnitude at bfloat16, 0.05% at float16, and a
few ULPs at float32.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pytree
|
Any
|
Tensor pytree to clip (flat or nested). |
required |
clipping_norm
|
float | PerGroup
|
Maximum L2 norm (non-negative, or inf for no clipping).
When |
required |
return_zero
|
bool
|
If True, the output PyTree is guaranteed to be zero no matter what the inputs are. Does not influence the formal guarantees but useful for privacy amplification via padding (see https://arxiv.org/pdf/2411.04205). |
False
|
compute_dtype
|
dtype | None
|
Internal dtype for the L2-norm reduction and leaf
scaling. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[Any, ClipPytreeAux]
|
Tuple of (clipped_pytree, aux) where aux contains: - norm: The L2 norm of the original (unclipped) pytree |
Edge cases
- clipping_norm=0: Returns zeros
- clipping_norm=inf: No clipping (passthrough)
- pytree_norm=0: Returns unchanged
- NaN/Inf values: Replaced with zeros before clipping
- return_zero=True: Returns zeros regardless of other parameters
Source code in packages/opake-engine/src/opake/api/engine/clipping/_pytree.py
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 | |
clipped_fun(fun, has_aux=False, *, batch_argnums=0, clipping_norm=1.0, normalize_by=1.0, return_aux=False, return_stats=False, second_moment=False, microbatch_size=None, dtype=None, compute_dtype=None, _scale_fn=None, _builtin_scale=None, _chunk_compiler=None)
¶
Transform a function to clip its output and sum across a batch.
This is the primary API for per-example clipping in DP-SGD. It wraps a function to clip each per-example output to a maximum L2 norm, then sums the clipped outputs.
The returned pytree is wrapped as :class:ClippedPytree (single-stream)
or :class:SecondMomentClippingOutput (paired-stream when
second_moment=True), carrying the post-normalization
max_norm for downstream noise calibration. The bound is part of
the contract: consumers (gaussian_noise, mf_gaussian_noise) read it
directly without the caller threading a separate sensitivity
argument. Unwrap to a raw pytree via .pytree if you need the
summed values without metadata.
Example Usage
from opake.api.engine.clipping._clipped_fun import clipped_fun data = torch.tensor([0.0, 1.0, 2.0, 3.0, 4.0, 5.0]) clipped_mean, clip_state = clipped_fun(torch.mean, clipping_norm=1.0) result, clip_state = clipped_mean(data, state=clip_state) result.pytree tensor(5.) result.max_norm 1.0
Formal Guarantees
For the first function output:
The L2 sensitivity of the returned function with respect to the batch
arguments (specified by batch_argnums) under add/remove or zero-out
differential privacy definitions is guaranteed to be clipping_norm.
Under replace-one DP, the sensitivity is doubled (2 * clipping_norm).
Extra auxiliary outputs (aux, norms) are per-example. This function
guarantees that per-example outputs only depend on the data for the same
example. This allows maximum flexibility for the caller to aggregate
these as desired (possibly with a DP mean, median, quantile, or histogram
mechanism).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fun
|
Callable[..., Any]
|
The function to be clipped. |
required |
has_aux
|
bool
|
If True, |
False
|
batch_argnums
|
int | tuple[int, ...]
|
Specifies which argument(s) of |
0
|
clipping_norm
|
float | PerGroup
|
The maximum L2 norm allowed. |
1.0
|
normalize_by
|
float
|
Divide the clipped output by this value before returning. |
1.0
|
return_aux
|
bool
|
If True, the returned Callable will return a per-example aux
dataclass containing the original per-example values, per-example norms
before clipping, and any auxiliary data returned by |
False
|
return_stats
|
bool
|
If True, return :class: |
False
|
second_moment
|
bool
|
If True, also accumulate the element-wise sum of
per-example squared clipped values, i.e. |
False
|
microbatch_size
|
int | None
|
If set, the batch is split up into microbatches of this
size for memory-efficient processing. Processes each microbatch separately
and accumulates results without materializing the full batch of gradients.
Set this to reduce peak memory usage at the cost of slightly slower computation.
The running sum is held in the wider of |
None
|
dtype
|
dtype | None
|
Optional dtype for the clipped+aggregated pytree. If None, the dtype will be the same as the dtypes of the function output. |
None
|
compute_dtype
|
dtype | None
|
Internal reduction and accumulation dtype. |
None
|
Returns:
A tuple (clip_fn, FixedClipState) where clip_fn(*args, state=...)
clips the output of fun and sums across the batch. The exact
return shape depends on second_moment and return_aux:
| ``second_moment`` | ``return_aux`` | ``clip_fn`` returns |
| :---------------- | :------------- | :------------------------------------------------- |
| False | False | ``(ClippedPytree, state)`` |
| False | True | ``((ClippedPytree, ClippedFunAux), state)`` |
| True | False | ``(SecondMomentClippingOutput, state)`` |
| True | True | ``((SecondMomentClippingOutput, ClippedFunAux), state)`` |
With ``return_stats=True`` and ``return_aux=False``, the clipped value
is paired with :class:`ClippingStats`.
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
If the batch is empty or a chunk compiler is used without a finite microbatch size. |
Source code in packages/opake-engine/src/opake/api/engine/clipping/_clipped_fun.py
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 | |
types
¶
DP-FTRL clipping state and aux types for type annotations.
ClippedGradResult = ClippedGradValue | tuple[ClippedGradValue, 'ClippedGradAux'] | tuple[ClippedGradValue, 'ClippingStats']
module-attribute
¶
Value returned by :class:ClippedGradFn before its updated state.
ClipPytreeAux = namedtuple('ClipPytreeAux', ['norm', 'group_norms'])
module-attribute
¶
Auxiliary outputs from clip_pytree.
Fields
norm: The L2 norm of the original (unclipped) pytree. group_norms: Per-group L2 norms before clipping (dict[str, Tensor]), or None when global clipping is used.
AutoClippedFunAux
dataclass
¶
Bases: ClippedFunAux
Diagnostic outputs from auto_clipped_fun.
Inherits all fields from :class:ClippedFunAux with AUTO-S
semantics:
norms: per-example L2 norms before scaling (i.e.||v||).clipped_norms: per-example L2 norms after AUTO-S scaling (R * ||v|| / (||v|| + gamma)), clipped byR.clipping_rate: fraction of examples with||v|| > R(approximately the fraction where the scale factor was < 1).values,value_aux,batch_size,group_norms: unchanged from :class:ClippedFunAux.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_auto.py
AutoClippedGradAux
dataclass
¶
Bases: ClippedGradAux
Diagnostic outputs from auto_clipped_grad.
Inherits all fields from :class:ClippedGradAux with AUTO-S
semantics:
grad_norms: per-example gradient L2 norms before scaling.clipped_grad_norms: per-example gradient L2 norms after AUTO-S scaling (R * ||g|| / (||g|| + gamma)), clipped byR.clipping_rate: fraction of examples with||g|| > R(approximately the fraction where the scale factor was < 1).loss_values,loss_aux,batch_size,group_norms: unchanged from :class:ClippedGradAux.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_auto.py
AutoClipState
dataclass
¶
ClippedFunAux
dataclass
¶
Diagnostic outputs from clipped_fun.
All fields are diagnostic — they reflect pre-noise, pre-aggregation
values and must not be fed back into private computation. Use the
returned ClippedPytree.max_norm metadata for noise calibration.
Fields
values: Per-example function values before clipping.
norms: Per-example L2 norms before clipping.
clipped_norms: Per-example L2 norms after clipping.
value_aux: Per-example auxiliary payload returned by the wrapped function.
clipping_rate: Fraction of per-example outputs whose norm exceeded the
clipping threshold. Equal to num_clipped / batch_size.
batch_size: Number of examples in the batch.
group_norms: Per-group per-example L2 norms before clipping
(dict[str, Tensor] with shape [batch_size] per group), or None
when global clipping is used.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_clipped_fun.py
ClippedGradAux
dataclass
¶
Diagnostic outputs from clipped_grad.
All fields are diagnostic — they reflect pre-noise, pre-aggregation
values and must not be fed back into private computation. Use the
returned ClippedPytree.max_norm metadata for noise calibration.
Fields
loss_values: Per-example loss values before clipping. grad_norms: Per-example gradient L2 norms before clipping. clipped_grad_norms: Per-example gradient L2 norms after clipping. loss_aux: Per-example auxiliary payload returned by the loss function. clipping_rate: Fraction of per-example gradients whose norm exceeded the clipping threshold. batch_size: Number of examples in the batch. group_norms: Per-group per-example gradient L2 norms before clipping (dict[str, Tensor] with shape [batch_size] per group), or None when global clipping is used.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_clipped_grad.py
ClippedGradFn
¶
Bases: Protocol
Callable returned by :func:opake.dpsgd.clipping.clipped_grad.
The positional arguments mirror the wrapped loss function. state is
passed by keyword and every invocation returns a new immutable
:class:FixedClipState with the clipped gradient result.
Source code in packages/opake-engine/src/opake/api/engine/clipping/_types.py
FixedClipState
dataclass
¶
Bases: ClipState
Marker state for fixed (non-adaptive) clipping.
Returned by :func:clipped_fun and :func:opake.api.engine.clipping.clipped_grad.
Carries no fields; the configured clipping threshold flows through
the ClippedPytree.max_norm metadata, not through the state.