Bases: OptiModule
Abstract noise-addition module for DP purposes.
This module uses either fast numpy pseudo-random number generation,
or slower cryptographically secure pseudo-random numbers (CSPRN).
Source code in declearn/optimizer/modules/_noise.py
38
39
40
41
42
43
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 | class NoiseModule(OptiModule, metaclass=ABCMeta, register=False):
"""Abstract noise-addition module for DP purposes.
This module uses either fast numpy pseudo-random number generation,
or slower cryptographically secure pseudo-random numbers (CSPRN).
"""
name: ClassVar[str] = "abstract-noise"
def __init__(
self,
safe_mode: bool = True,
seed: Optional[int] = None,
) -> None:
"""Instantiate the noise module.
Parameters
----------
safe_mode: bool, default=True
Whether to use cryptographically-secure pseudo-random numbers
(CSPRN) rather than the default numpy generator.
For experimental purposes, set flag to False, as generating CSPRN
is significantly slower.
seed: int or None, default=None
Seed used for initiliazing the non-secure random number generator.
If `safe_mode=True`, seed is ignored.
"""
rng = SystemRandom if safe_mode else np.random.default_rng
self._rng = rng(seed)
self.seed = seed
@property
def safe_mode(self) -> bool:
"""Whether this module uses CSPRN rather than base numpy RNG."""
return isinstance(self._rng, SystemRandom)
def get_config(
self,
) -> Dict[str, Any]:
return {"safe_mode": self.safe_mode, "seed": self.seed}
def run(
self,
gradients: Vector,
) -> Vector:
if not NumpyVector in gradients.compatible_vector_types:
raise TypeError( # pragma: no cover
f"{self.__class__.__name__} requires input gradients to "
"be compatible with NumpyVector, which is not the case "
f"of {type(gradients).__name__}."
)
# Gather gradients' specs.
shapes = gradients.shapes()
dtypes = gradients.dtypes()
# Conduct noise sampling for each and every gradient coordinate.
noise = {
key: self._sample_noise(shapes[key], dtypes[key])
for key in gradients.coefs
}
# Add the sampled noise to the gradients and return them.
# Silence warnings about sparse gradients getting densified.
with warnings.catch_warnings():
warnings.filterwarnings("ignore", ".*densifying.*", RuntimeWarning)
return gradients + NumpyVector(noise)
@abstractmethod
def _sample_noise(
self,
shape: Tuple[int, ...],
dtype: str,
) -> np.ndarray:
"""Sample a noise tensor from a module-specific distribution."""
|
safe_mode: bool
property
Whether this module uses CSPRN rather than base numpy RNG.
__init__(safe_mode=True, seed=None)
Instantiate the noise module.
Parameters:
Name |
Type |
Description |
Default |
safe_mode |
bool
|
Whether to use cryptographically-secure pseudo-random numbers
(CSPRN) rather than the default numpy generator.
For experimental purposes, set flag to False, as generating CSPRN
is significantly slower. |
True
|
seed |
Optional[int]
|
Seed used for initiliazing the non-secure random number generator.
If safe_mode=True , seed is ignored. |
None
|
Source code in declearn/optimizer/modules/_noise.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 | def __init__(
self,
safe_mode: bool = True,
seed: Optional[int] = None,
) -> None:
"""Instantiate the noise module.
Parameters
----------
safe_mode: bool, default=True
Whether to use cryptographically-secure pseudo-random numbers
(CSPRN) rather than the default numpy generator.
For experimental purposes, set flag to False, as generating CSPRN
is significantly slower.
seed: int or None, default=None
Seed used for initiliazing the non-secure random number generator.
If `safe_mode=True`, seed is ignored.
"""
rng = SystemRandom if safe_mode else np.random.default_rng
self._rng = rng(seed)
self.seed = seed
|