// Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved. // // 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. #include #include #include #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/kernels/funcs/eigen/common.h" namespace phi { template void DpsgdOpKernel(const Context &dev_ctx, const DenseTensor ¶m_in, const DenseTensor &grad_in, const DenseTensor &learning_rate_in, float clip_in, float batch_size_in, float sigma_in, int seed_in, DenseTensor *param_out) { const auto *learning_rate = &learning_rate_in; const auto *param = ¶m_in; const auto *grad = &grad_in; auto sz = param_out->numel(); PADDLE_ENFORCE_EQ(param->numel(), sz, common::errors::InvalidArgument( "Input parameter's number of elements is error, " "expected %lld, but received %lld.", sz, param->numel())); PADDLE_ENFORCE_EQ(grad->numel(), sz, common::errors::InvalidArgument( "Input gradient's number of elements is error, " "expected %lld, but received %lld.", sz, grad->numel())); const T *lr = learning_rate->data(); const T *param_data = param->data(); const T *grad_data = grad->data(); T *out_data = dev_ctx.template Alloc(param_out); T clip = static_cast(clip_in); T batch_size = static_cast(batch_size_in); T sigma = static_cast(sigma_in); // compute clipping float l2_norm = 0.0; for (int64_t i = 0; i < grad->numel(); ++i) { l2_norm = l2_norm + grad_data[i] * grad_data[i]; } l2_norm = std::sqrt(l2_norm); float scale = 1.0; if (l2_norm > clip) { scale = l2_norm / clip; } // generate gaussian noise. // [https://en.wikipedia.org/wiki/Box-Muller_transform] float V1, V2, S; float X; float mu = 0.0; float U1, U2; unsigned seed = static_cast(seed_in); if (seed == 0) { seed = (unsigned)(time(NULL)); } std::minstd_rand engine; engine.seed(seed); std::uniform_real_distribution dist(0.0, 1.0); do { U1 = dist(engine); U2 = dist(engine); V1 = 2 * U1 - 1; V2 = 2 * U2 - 1; S = V1 * V1 + V2 * V2; } while (S >= 1 || S == 0); X = V1 * sqrt(-2 * log(S) / S); float gaussian_noise = mu + X * sigma; // update parameters for (int64_t i = 0; i < grad->numel(); ++i) { out_data[i] = param_data[i] - lr[0] * (grad_data[i] / scale + gaussian_noise / batch_size); } // CCS16 - Deep Learning with Differential Privacy. // [https://arxiv.org/abs/1607.00133] } // Compute } // namespace phi PD_REGISTER_KERNEL(dpsgd, CPU, ALL_LAYOUT, phi::DpsgdOpKernel, float, double) {}