diff --git a/mindarmour/diff_privacy/__init__.py b/mindarmour/diff_privacy/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9c45184da6c8d16b9af6a340508957b1ecbc5ad6 --- /dev/null +++ b/mindarmour/diff_privacy/__init__.py @@ -0,0 +1,10 @@ +""" +This module provide Differential Privacy feature to protect user privacy. +""" +from .mechanisms.mechanisms import GaussianRandom +from .mechanisms.mechanisms import AdaGaussianRandom +from .mechanisms.mechanisms import MechanismsFactory + +__all__ = ['GaussianRandom', + 'AdaGaussianRandom', + 'MechanismsFactory'] diff --git a/mindarmour/diff_privacy/mechanisms/__init__.py b/mindarmour/diff_privacy/mechanisms/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/mindarmour/diff_privacy/mechanisms/mechanisms.py b/mindarmour/diff_privacy/mechanisms/mechanisms.py new file mode 100644 index 0000000000000000000000000000000000000000..3e82a5fe2119b0529b05445d609ff84a11a74310 --- /dev/null +++ b/mindarmour/diff_privacy/mechanisms/mechanisms.py @@ -0,0 +1,208 @@ +# Copyright 2019 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Noise Mechanisms. +""" +import numpy as np + +from mindspore import Tensor +from mindspore.nn import Cell +from mindspore.ops import operations as P +from mindspore.common.parameter import Parameter +from mindspore.common import dtype as mstype + +from mindarmour.utils._check_param import check_param_type +from mindarmour.utils._check_param import check_value_positive + + +class MechanismsFactory: + """ Factory class of mechanisms""" + + def __init__(self): + pass + + @staticmethod + def create(policy, *args, **kwargs): + """ + Args: + policy(str): Noise generated strategy, could be 'Gaussian' or + 'AdaGaussian'. Default: 'AdaGaussian'. + args(Union[float, str]): Parameters used for creating noise + mechanisms. + kwargs(Union[float, str]): Parameters used for creating noise + mechanisms. + + Raises: + NameError: `policy` must be in ['Gaussian', 'AdaGaussian']. + Returns: + Mechanisms, class of noise generated Mechanism. + """ + if policy == 'Gaussian': + return GaussianRandom(*args, **kwargs) + if policy == 'AdaGaussian': + return AdaGaussianRandom(*args, **kwargs) + raise NameError("The {} is not implement, please choose " + "['Gaussian', 'AdaGaussian']".format(policy)) + + +class Mechanisms(Cell): + """ + Basic class of noise generated mechanism. + """ + + def __init__(self): + pass + + def construct(self, shape): + """ + Construct function. + """ + + +class GaussianRandom(Mechanisms): + """ + Gaussian noise generated mechanism. + + Args: + norm_bound(float): Clipping bound for the l2 norm of the gradients. + Default: 1.5. + initial_noise_multiplier(float): Ratio of the standard deviation of + Gaussian noise divided by the norm_bound, which will be used to + calculate privacy spent. Default: 5.0. + + Returns: + Tensor, generated noise. + + Examples: + >>> shape = (3, 2, 4) + >>> norm_bound = 1.5 + >>> initial_noise_multiplier = 0.1 + >>> net = GaussianRandom(shape, norm_bound, initial_noise_multiplier) + >>> res = net(shape) + >>> print(res) + """ + + def __init__(self, norm_bound=1.5, initial_noise_multiplier=5.0): + super(GaussianRandom, self).__init__() + self._norm_bound = check_value_positive('norm_bound', norm_bound) + self._initial_noise_multiplier = check_value_positive('initial_noise_multiplier', + initial_noise_multiplier,) + stddev = self._norm_bound*self._initial_noise_multiplier + self._stddev = stddev + self._mean = 0 + + def construct(self, shape): + """ + Generated Gaussian noise. + + Args: + shape(tuple): The shape of gradients. + + Returns: + numpy.ndarray, generated noise. + """ + shape = check_param_type('shape', shape, tuple) + noise = np.random.normal(self._mean, self._stddev, shape) + return Tensor(noise, mstype.float32) + + +class AdaGaussianRandom(Mechanisms): + """ + Adaptive Gaussian noise generated mechanism. + + Args: + norm_bound(float): Clipping bound for the l2 norm of the gradients. + Default: 1.5. + initial_noise_multiplier(float): Ratio of the standard deviation of + Gaussian noise divided by the norm_bound, which will be used to + calculate privacy spent. Default: 5.0. + alpha(float): Hyperparameter for controlling the noise decay. + Default: 6e-4. + decay_policy(str): Noise decay strategy include 'Step' and 'Time'. + Default: 'Time'. + + Returns: + Tensor, generated noise. + + Examples: + >>> shape = (3, 2, 4) + >>> norm_bound = 1.0 + >>> initial_noise_multiplier = 0.1 + >>> alpha = 0.5 + >>> decay_policy = "Step" + >>> net = AdaGaussianRandom(norm_bound, initial_noise_multiplier, + >>> alpha, decay_policy) + >>> res = net(shape) + >>> print(res) + """ + + def __init__(self, norm_bound=1.5, initial_noise_multiplier=5.0, + alpha=6e-4, decay_policy='Step'): + super(AdaGaussianRandom, self).__init__() + initial_noise_multiplier = check_value_positive('initial_noise_multiplier', + initial_noise_multiplier) + initial_noise_multiplier = Tensor(np.array(initial_noise_multiplier, np.float32)) + self._initial_noise_multiplier = Parameter(initial_noise_multiplier, + name='initial_noise_multiplier') + self._noise_multiplier = Parameter(initial_noise_multiplier, + name='noise_multiplier') + norm_bound = check_value_positive('norm_bound', norm_bound) + self._norm_bound = Tensor(np.array(norm_bound, np.float32)) + + alpha = check_param_type('alpha', alpha, float) + self._alpha = Tensor(np.array(alpha, np.float32)) + + self._decay_policy = check_param_type('decay_policy', decay_policy, str) + self._mean = 0.0 + self._sub = P.Sub() + self._mul = P.Mul() + self._add = P.TensorAdd() + self._div = P.Div() + self._stddev = self._update_stddev() + self._dtype = mstype.float32 + + def _update_multiplier(self): + """ Update multiplier. """ + if self._decay_policy == 'Time': + temp = self._div(self._initial_noise_multiplier, + self._noise_multiplier) + temp = self._add(temp, self._alpha) + temp = self._div(self._initial_noise_multiplier, temp) + self._noise_multiplier = Parameter(temp, name='noise_multiplier') + else: + one = Tensor(1, self._dtype) + temp = self._sub(one, self._alpha) + temp = self._mul(temp, self._noise_multiplier) + self._noise_multiplier = Parameter(temp, name='noise_multiplier') + + def _update_stddev(self): + self._stddev = self._mul(self._noise_multiplier, self._norm_bound) + return self._stddev + + def construct(self, shape): + """ + Generate adaptive Gaussian noise. + + Args: + shape(tuple): The shape of gradients. + + Returns: + numpy.ndarray, generated noise. + """ + shape = check_param_type('shape', shape, tuple) + noise = np.random.normal(self._mean, self._stddev.asnumpy(), + shape) + self._update_multiplier() + self._update_stddev() + return Tensor(noise, mstype.float32) diff --git a/tests/ut/python/diff_privacy/mechanisms/test_mechanisms.py b/tests/ut/python/diff_privacy/mechanisms/test_mechanisms.py new file mode 100644 index 0000000000000000000000000000000000000000..6b009523bd5b1f968b21a3e5f34539d29991c109 --- /dev/null +++ b/tests/ut/python/diff_privacy/mechanisms/test_mechanisms.py @@ -0,0 +1,81 @@ +# Copyright 2019 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +different Privacy test. +""" +import pytest + +from mindspore import context +from mindarmour.diff_privacy import GaussianRandom +from mindarmour.diff_privacy import AdaGaussianRandom +from mindarmour.diff_privacy import MechanismsFactory + + +@pytest.mark.level0 +@pytest.mark.platform_x86_ascend_training +@pytest.mark.env_onecard +@pytest.mark.component_mindarmour +def test_gaussian(): + context.set_context(mode=context.PYNATIVE_MODE, device_target="Ascend") + shape = (3, 2, 4) + norm_bound = 1.0 + initial_noise_multiplier = 0.1 + net = GaussianRandom(norm_bound, initial_noise_multiplier) + res = net(shape) + print(res) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_ascend_training +@pytest.mark.env_onecard +@pytest.mark.component_mindarmour +def test_ada_gaussian(): + context.set_context(mode=context.PYNATIVE_MODE, device_target="Ascend") + shape = (3, 2, 4) + norm_bound = 1.0 + initial_noise_multiplier = 0.1 + alpha = 0.5 + decay_policy = "Step" + net = AdaGaussianRandom(norm_bound, initial_noise_multiplier, + alpha, decay_policy) + res = net(shape) + print(res) + + +def test_factory(): + context.set_context(mode=context.PYNATIVE_MODE, device_target="Ascend") + shape = (3, 2, 4) + norm_bound = 1.0 + initial_noise_multiplier = 0.1 + alpha = 0.5 + decay_policy = "Step" + noise_mechanism = MechanismsFactory() + noise_construct = noise_mechanism.create('Gaussian', + norm_bound, + initial_noise_multiplier) + noise = noise_construct(shape) + print('Gaussian noise: ', noise) + ada_mechanism = MechanismsFactory() + ada_noise_construct = ada_mechanism.create('AdaGaussian', + norm_bound, + initial_noise_multiplier, + alpha, + decay_policy) + ada_noise = ada_noise_construct(shape) + print('ada noise: ', ada_noise) + + +if __name__ == '__main__': + # device_target can be "CPU", "GPU" or "Ascend" + context.set_context(mode=context.PYNATIVE_MODE, device_target="Ascend")