差分隐私与在联邦学习的使用

差分隐私与在联邦学习的使用

一、简单了解差分隐私

差分隐私是一种针对差分攻击的隐私保护框架

示例

要查询一个大型医疗数据库,如果我们知道A的信息在数据库中,就可以利用一种叫差分攻击的方法得到个体的信息。可以查询有多少人患糖尿病,假如有100个人,然后条件查询,有多少不叫A的人患糖尿病,假如有99个人,这样经过差分,就得到了A患糖尿病。

那么利用差分隐私来使得查100个人还是查99个人结果都一样,那么可以在查询结果里加入随机噪声,从而令攻击者无法根据返回的带噪声的结果推断出原始数据

二、差分隐私的公式理解

对于一个随机算法 M M M,如果对于任意一对相邻数据集 D D D D ′ D' D,算法M能满足
P r [ M ( D ) ∈ O ] ⩽ e ϵ P r [ M ( D ′ ) ∈ O ] + δ Pr[M(D)\in O] \leqslant e^{\epsilon}Pr[M(D')\in O] + \delta Pr[M(D)O]eϵPr[M(D)O]+δ
可以简单理解为 M M M就是我们需要用的各种分布,比如拉普拉斯、高斯等等。 P r Pr Pr就是指的分布取 D D D的时候映射出的概率 D D D不是一个点,是一个集合,为了方便理解可以简单理解为一个区间,然后这个概率就可以表示为对这个分布 M M M在这个区间 D D D内的积分,可以先这么理解。然后就这个理解看这个式子就是说这两个概率之间的差距不能超过一定值,而要满足这个范围就必须要满足这个公式,其中的 e e e就是高中常见的那个自然常数 ϵ \epsilon ϵ δ \delta δ便是相关的常数

能满足这个式子的 M M M便被称为满足** ( ϵ , δ ) (\epsilon,\delta) (ϵ,δ)-差分隐私**,如果 δ \delta δ为0就说明满足** ( ϵ , 0 ) (\epsilon,0) (ϵ,0)-差分隐私**

二、差分隐私相关参数

敏感度

当数据被改变后,查询的结果会相应改变,而这个改变程度可以被解释为查询结果对该数据的依赖程度,一般使用敏感度 Δ f \Delta f Δf来衡量,如果敏感度较大则噪声幅度也该更大,确保更好的隐私保证。

示例

集合D包括很多人的数据,C代指人数,那么假如D中加入一个人,那么C最多会改变1,此时,敏感度为1。

三、差分隐私机制

关键点在于加入噪声,而加入噪声的机制常用的是两种,一种是高斯机制,一种是拉普拉斯机制

高斯机制

高斯分布(其实就是正态分布)
f ( x ) = 1 2 π σ e x p ( − ( x − μ ) 2 2 σ 2 ) f(x)=\frac{1}{\sqrt{2\pi\sigma}}exp(-\frac{(x-\mu)^2}{2\sigma^2}) f(x)=2πσ 1exp(2σ2(xμ)2)
若要满足**( ϵ , δ \epsilon,\delta ϵ,δ)-差分隐私**,则需要使噪声参数 σ ⩾ c Δ f ϵ \sigma \geqslant \frac{c \Delta f}{\epsilon} σϵcΔf ,并且在满足 ϵ ∈ ( 0 , 1 ) \epsilon \in (0,1) ϵ(0,1) c ⩾ 2 ln ⁡ ( 1.25 / δ ) c \geqslant \sqrt{2\ln(1.25/\delta)} c2ln(1.25/δ)

总结来说高斯分布需要三个参数

  • 标准差 σ \sigma σ 决定噪声的尺度
  • ϵ \epsilon ϵ 表示隐私预算与噪声负相关
  • δ \delta δ 表示松弛项,如果设置成 10 − 5 10^{-5} 105 则说明只允许 10 − 5 10^{-5} 105的概率违反严格差分隐私

拉普拉斯机制

拉普拉斯分布
f ( x ∣ μ , b ) = 1 2 b e − ∣ x − μ ∣ b f(x|\mu,b)=\frac{1}{2b}e^{-{}\frac{|x-\mu|}{b}} f(xμ,b)=2b1ebxμ
可以参考刚刚的正态分布,这个跟那个也是一个意思,拉普拉斯分布有以下特征。

  • 一般来说 μ \mu μ取0。

  • x x x取0时得到最大值。

那么现在决定这个函数值的只有 x x x b b b了,我们取 b = Δ f ϵ b=\frac{\Delta f}{\epsilon} b=ϵΔf

则能满足

使用敏感度 Δ f \Delta f Δf来衡量,如果敏感度较大则噪声幅度也该更大

如此满足**( ϵ \epsilon ϵ ,0)-差分隐私**,相关证明这里略过。

五、简单了解联邦学习

联邦学习是由谷歌团队提出的一种深度学习方法,主要用于解决隐私保护,数据分散性与非均衡性,通信效率,大规模分布式计算的问题。

说白了就是把各个在不调用各个客户端数据集的情况下服务器端获取客户端训练好的参数,来进行聚合和优化。

那么差分隐私就可以用在用户训练参数以及服务端聚合过程中,进一步保证隐私安全。

(关于联邦学习,我之后还会再出内容)

六、联邦学习·差分隐私

客户端方面

在联邦学习客户端中,在相对应的本地训练过程中,在每个batch结束后,进行差分隐私。

def train(net, trainloader, epochs, device, lr, criterian, optimizer, mu=0):
    global_params = copy.deepcopy(net).parameters()
    global_model = copy.deepcopy(net)

    for name,param in net.state_dict().items():
        global_model.state_dict()[name].copy_(param.clone())

    net.to(device) 
    task_logger.info(f"Training on device: {device}")
    optimizer = optimizer(net.parameters(), lr=lr)
    criterian = criterian().to(device)
    net.train()
    running_loss = 0.0
    total_samples = 0
    for _ in range(epochs):
        for batch in trainloader:
            images = batch["img"]
            labels = batch["fine_label"]
            total_samples += len(images)
            optimizer.zero_grad()
            proximal_term = 0
            if mu != 0:
                for local_weights, global_weights in zip(net.parameters(), global_params):
                    proximal_term += (local_weights - global_weights).norm(2)
            loss = criterian(net(images.to(device)), labels.to(device)) + (mu / 2) * proximal_term
            loss.backward()
            optimizer.step()
            running_loss += loss.item() * len(images)
            
            # 之后进行差分隐私
            if privacy == "laplace":
                # 使用laplace机制
                privacy = laplace.MyLaplace()
                privacy.client_dp(net,global_model)
            elif privacy == "guassain":
                # 使用guassain机制
                privacy = guassian.MyGuassian()
                privacy.client_dp(net,global_model)

    avg_trainloss = running_loss / total_samples
    return avg_trainloss

服务端方面

我们在聚合完成过后,使用对应机制加入噪声。

def aggregate_fit(
    self,
    server_round: int,
    results: list[tuple[fl.server.client_proxy.ClientProxy, fl.common.FitRes]],
    failures: list[Union[tuple[ClientProxy, FitRes], BaseException]],
) -> tuple[Optional[Parameters], dict[str, Scalar]]:
    """Aggregate model weights using weighted average and store checkpoint"""

    # Call aggregate_fit from base class (FedAvg) to aggregate parameters and metrics
    aggregated_parameters, aggregated_metrics = super().aggregate_fit(
        server_round, results, failures
    )

    if aggregated_parameters is not None:
        if server_round % 5 == 1:
            # Save the model every 5 rounds
            log(INFO, f"Saving model at round {server_round}")
            # Convert `Parameters` to `list[np.ndarray]`
            aggregated_ndarrays: list[np.ndarray] = parameters_to_ndarrays(aggregated_parameters)
            model_name = self.user_config["server-config"]["model-name"]
            num_classes = self.user_config["server-config"]["num-classes"]
            net = get_model(model_name, num_classes)
            app_id = self.user_config["app-id"]
            self.cur_version = self.save_model(model_name, num_classes, server_round, aggregated_ndarrays, app_id,self.cur_version)
            round_data = {
                "round": server_round,
                "mode": "fit",
                "metrics": aggregated_metrics,
            }
            self.metric_fit.append(round_data)

            if self.user_config["privacy-config"] == "laplace":
                #使用laplace机制
                num_clients = len(results)
                current_state_dict = net.state_dict()
                privacy = laplace.MyLaplace()
                privacy.server_dp(num_clients,current_state_dict,net)

            elif self.user_config["privacy-config"] == "guassain":
                #使用guassain机制
                num_clients = len(results)
                current_state_dict = net.state_dict()
                privacy = guassian.MyGuassian()
                privacy.server_dp(num_clients, current_state_dict, net)
            
        total_round = int(self.user_config["client-config"]["model-config"]["total-rounds"]) 
        if server_round == total_round:
            # Save the training data
            self.metric_fit = self.save_training_data(
                cur_version=self.cur_version,
                app_id=app_id,
                metric=self.metric_fit,
            )

    return aggregated_parameters, aggregated_metrics

拉普拉斯机制类相关代码

首先是拉普拉斯机制,参数如下定义,max-grad-norm梯度裁剪参数(有的地方写的是C,私以为两者差不多,有问题可以再留言),然后是 ϵ \epsilon ϵ epsilon,敏感度我们会在服务端使用,将会被计算出来。

{
  "privacy-strategy": "laplace",
  "epsilon": 3.0,
  "max-grad-norm": 2.0
}

这里设立拉普拉斯类(具体调用代码略),调用里面的客户端函数,传入本批次训练完的本地模型全局模型

客户端函数

计算全局模型和本地模型差异的L1范数,然后经过计算得到scale,scale来对客户端本地模型和全局模型的参数差异进行动态缩放,确保参数差异的范数不超过预设的阈值,也就是max-grad-norm

然后我们遍历本地模型的参数,进行计算裁剪后的差异clipped_diff

再让参数加上裁剪差异,这样就能满足本地模型参数和全局模型参数的差异不会超过一定的值,实现约束作用。

def client_dp(self,net,global_model):
    # 计算模型参数差异的L1范数
    model_norm = sum(torch.abs(param.data - global_param).sum()
                     for param, global_param in zip(net.state_dict().items(), global_model.state_dict().items()))
    scale = min(1.0, self.max_grad_norm / model_norm)

    for name, param in net.state_dict().items():
        # 裁剪后的参数进行更新
        clipped_diff = scale * (param.data - global_model.state_dict()[name])
        # 更新参数:参数 + 裁剪差异
        param.data.copy_(global_model.state_dict()[name].detach() + clipped_diff)
服务端函数

然后我们在服务端,传入选取的客户端的数量,现在的模型参数,以及当前客户对应的模型,然后计算 Δ f ϵ \frac{\Delta f}{\epsilon} ϵΔf,也就是b,目的是为了使用拉普拉斯函数产生噪声,说白了就是根据拉普拉斯分布函数随机产生一个值,这个函数总归要被定义,所以需要算出b来,然后再随机选取值作为噪声加入到对应参数里。

def server_dp(self,num_clients,current_state_dict,net):
    noisy_state_dict = {}
    for name, param in current_state_dict.items():
        sensitivity = self.max_grad_norm / num_clients
        scale = sensitivity / self.epsilon

        noise = torch.distributions.Laplace(0, scale).sample(param.shape).to(param.device)
        noise = noise.to(dtype=param.dtype)

        noisy_state_dict[name] = param + noise
    net.load_state_dict(noisy_state_dict)
    log(INFO, "end")

高斯机制相关代码

高斯机制的参数比起拉普拉斯多一个 δ \delta δ因为,满足的是** ( ϵ , δ ) (\epsilon,\delta) (ϵ,δ)-差分隐私**,其他的值基本差不多。

{
  "privacy-strategy": "guassian",
  "epsilon": 0.5,
  "max-grad-norm": 2.0,
  "delta": 1e-5
}
客户端方面

由于高斯机制对应的是L2范数,所以我们计算的是L2范数去计算对应的scale

def client_dp(self, net, global_model):
    # 计算模型参数差异的L2范数
    model_norm = torch.sqrt(sum(torch.sum((param.data - global_param.data) ** 2)
        for param, global_param in zip(net.parameters(), global_model.parameters())).item())

    # 动态缩放差异
    scale = min(1.0, self.max_grad_norm / (model_norm + 1e-10))

    for name, param in net.named_parameters():
    # 裁剪差异
        clipped_diff = scale * (param.data - global_model.state_dict()[name])
    # 更新参数:全局参数 + 裁剪差异
    param.data.copy_(global_model.state_dict()[name].detach() + clipped_diff)
服务端方面
def server_dp(self, num_clients, current_state_dict, net):
    noisy_state_dict = {}
    for name, param in current_state_dict.items():
        # 计算敏感度(FedAvg平均后的L2敏感度)
        sensitivity = self.max_grad_norm / num_clients

        # 高斯噪声标准差
        sigma = sensitivity * np.sqrt(2 * np.log(1.25 / self.delta)) / self.epsilon

        # 生成高斯噪声(匹配参数设备和数据类型)
        noise = torch.randn_like(param) * sigma
        noise = noise.to(device=param.device, dtype=param.dtype)

        noisy_state_dict[name] = param + noise

    # 加载带噪声的参数
    net.load_state_dict(noisy_state_dict)
    log(INFO, f"Added Gaussian noise (σ={sigma:.4f}) to global model")

总结

总得来说,粗略实现了在联邦学习中使用差分隐私机制,至于准确率上的影响,小组成员实验过后发现似乎并没有太大差异,但是本人感觉结合的还是差点意思,之后也会继续完善。

文中也许有错误之处,或更能完善之处,欢迎评论指正给出意见。

参考

差分隐私(Differential Privacy)-优快云博客

Practicing-Federated-Learning/chapter15_Differential_Privacy at main · FederatedAI/Practicing-Federated-Learning

差分隐私 - Flower Framework

### 关于差分隐私联邦学习的实现方法 #### 什么是差分隐私差分隐私是一种用于保护个人数据隐私的技术框架,其核心思想是在查询结果中加入随机噪声,使得攻击者无法通过观察查询结果推断出某个特定个体的信息[^1]。 #### 联邦学习的基础概念 联邦学习(Federated Learning, FL)是一种分布式机器学习技术,在该技术下,多个客户端设备协作训练模型而无需共享原始数据。这种方式能够有效减少敏感数据泄露的风险[^2]。 #### 差分隐私联邦学习的结合 为了进一步增强联邦学习中的隐私保护能力,可以通过在本地模型更新阶段引入差分隐私机制来防止潜在的数据泄漏风险。这种方法通常称为 **差分隐私联邦学习 (Differentially Private Federated Learning)**。具体而言,当各节点上传梯度或其他参数时,会先向这些参数添加一定量的高斯白噪声或拉普拉斯噪声后再发送给服务器端进行聚合处理。 #### 核心算法原理及其操作步骤 以下是基于差分隐私联邦学习的核心流程描述: 1. 初始化全局模型并广播至各个参者; 2. 各个参方利用自身的私有数据集对该初始版本执行一轮或多轮次的小批量SGD优化过程得到局部权重变化Δw_i; 3. 对每一个计算出来的差异项施加适当大小范围内的随机扰动ε~Lap(λ),形成新的带噪版δW'_i= Δwi + ε ; 4. 将经过上述变换后的数值提交回中央控制器处汇总成最终改进过的整体结构P_new=P_old+(Σ δWi')/(n*k). 此外,《横向联邦学习中PCA差分隐私数据发布算法》提供了一种具体的方案设计思路——即采用主成分分析降维配合DP机制完成跨机构间联合建模任务的同时保障用户信息安全[^3]. 下面给出一段简单的Python伪代码展示如何模拟这一过程: ```python import numpy as np def add_noise_to_updates(local_update, sensitivity, epsilon): """Add Laplacian noise to local model updates.""" scale = sensitivity / epsilon noisy_update = local_update + np.random.laplace(loc=0.0, scale=scale, size=local_update.shape) return noisy_update # Example usage within a federated learning loop global_model = initialize_global_model() for round_num in range(num_rounds): selected_clients = select_clients(global_model) client_updates = [] for client in selected_clients: local_update = train_on_client_data(client.data, global_model) private_local_update = add_noise_to_updates(local_update, sensitivity=sensitivity_value, epsilon=epsilon_value) client_updates.append(private_local_update) aggregate_and_update_global_model(global_model, client_updates) ``` 此段脚本展示了如何在一个典型的FL循环内部应用差分隐私技术以确保每次迭代期间所传输信息的安全性.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值