文章目录
差分隐私与在联邦学习的使用
一、简单了解差分隐私
差分隐私是一种针对差分攻击的隐私保护框架
示例
要查询一个大型医疗数据库,如果我们知道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)}
c⩾2ln(1.25/δ)
总结来说高斯分布需要三个参数
- 标准差 σ \sigma σ 决定噪声的尺度
- ϵ \epsilon ϵ 表示隐私预算与噪声负相关
- δ \delta δ 表示松弛项,如果设置成 10 − 5 10^{-5} 10−5 则说明只允许 10 − 5 10^{-5} 10−5的概率违反严格差分隐私
拉普拉斯机制
拉普拉斯分布
f
(
x
∣
μ
,
b
)
=
1
2
b
e
−
∣
x
−
μ
∣
b
f(x|\mu,b)=\frac{1}{2b}e^{-{}\frac{|x-\mu|}{b}}
f(x∣μ,b)=2b1e−b∣x−μ∣
可以参考刚刚的正态分布,这个跟那个也是一个意思,拉普拉斯分布有以下特征。
-
一般来说 μ \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")
总结
总得来说,粗略实现了在联邦学习中使用差分隐私机制,至于准确率上的影响,小组成员实验过后发现似乎并没有太大差异,但是本人感觉结合的还是差点意思,之后也会继续完善。
文中也许有错误之处,或更能完善之处,欢迎评论指正给出意见。