联邦学习之隐私增强

Posted by kalos Aner on December 5, 2024

联邦学习系列

联邦学习之基础用法

联邦学习之分布训练

联邦学习之自定义化

联邦学习之隐私增强

联邦学习之带宽需求

引言

虽然联邦学习可以避免数据上传到服务器而导致隐私泄露,但是恶意攻击者可以通过攻击服务器对模型进行重构攻击还原用户的隐私,并且拥有很高的还原质量。差分隐私(DP)是在数据分析期间为个人增强隐私的一种突出解决方法。它通过向查询结果添加校准噪声来模糊个人数据,确保任何单个数据点的存在或不存在都不会对分析结果产生显著影响。差分隐私出现在模型训练、模型更新的聚合以及客户端与服务端之间的通信。本节将极少两种DP:集中式 DP 和本地 DP。

DP 有两个重要的主题:一是裁剪,它限制敏感度并减轻异常值的影响。敏感度代表当从数据集中添加或删除单个数据点时输出可以改变的最大量。二是加噪,它添加添加校准噪声以使输出在统计上无法区分。

在集中式 DP 中,中央服务器负责向全局聚合的参数添加噪声。总体方法是裁剪客户端发送的模型更新,然后向聚合模型添加一定量的噪声。

在本地 DP 中,每个客户端负责执行 DP。本地 DP 避免了对完全可信聚合器的需求。每个客户端负责在将更新后的模型发送到服务器之前在本地进行裁剪和加噪。

本小节所用到的资源都放在这里

数据准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from flwr.client.mod import adaptiveclipping_mod
from flwr.server.strategy import (
    DifferentialPrivacyClientSideAdaptiveClipping,
    FedAvg,
)

from utils4 import *
# 将数据分成 10 份。
def load_data(partition_id):
    fds = FederatedDataset(dataset="mnist", partitioners={"train": 10})
    partition = fds.load_partition(partition_id)

    traintest = partition.train_test_split(test_size=0.2, seed=42)
    traintest = traintest.with_transform(normalize)
    trainset, testset = traintest["train"], traintest["test"]

    trainloader = DataLoader(trainset, batch_size=64, shuffle=True)
    testloader = DataLoader(testset, batch_size=64)
    return trainloader, testloader

用户端和服务端配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class FlowerClient(NumPyClient):
    def __init__(self, net, trainloader, testloader):
        self.net = net
        self.trainloader = trainloader
        self.testloader = testloader

    def fit(self, parameters, config):
        set_weights(self.net, parameters)
        train_model(self.net, self.trainloader)
        return get_weights(self.net), len(self.trainloader), {}

    def evaluate(self, parameters, config):
        set_weights(self.net, parameters)
        loss, accuracy = evaluate_model(self.net, self.testloader)
        return loss, len(self.testloader), {"accuracy": accuracy}


def client_fn(context: Context) -> Client:
    net = SimpleModel()
    partition_id = int(context.node_config["partition-id"])
    trainloader, testloader = load_data(partition_id=partition_id)
    return FlowerClient(net, trainloader, testloader).to_client()
# mods 允许用户在客户端应用程序中处理任务之前和之后执行操作,adaptiveclipping_mod 是内置 mod,作用是自适应裁剪,该模块在将模型更新发送回服务端之前执行模型更新的自适应裁剪。
client = ClientApp(
    client_fn,
    mods=[adaptiveclipping_mod],  # modifiers
)

net = SimpleModel()
params = ndarrays_to_parameters(get_weights(net))
# 差分隐私
def server_fn(context: Context):
    fedavg_without_dp = FedAvg(
        fraction_fit=0.6,
        fraction_evaluate=1.0,
        initial_parameters=params,
    )
    # 将 FedAvg 策略包裹在 DP 策略中。
    fedavg_with_dp = DifferentialPrivacyClientSideAdaptiveClipping(
        fedavg_without_dp,  # <- wrap the FedAvg strategy
        noise_multiplier=0.3, # 噪声乘数
        num_sampled_clients=6,# 抽样客户端数量
    )
    
    # Adjust to 50 rounds to ensure DP guarantees hold
    # with respect to the desired privacy budget
    config = ServerConfig(num_rounds=5)
    
    return ServerAppComponents(
        strategy=fedavg_with_dp,
        config=config,
    )
server = ServerApp(server_fn=server_fn)

模型训练

1
2
3
4
5
run_simulation(server_app=server,
               client_app=client,
               num_supernodes=10,
               backend_config=backend_setup
               )

由于裁剪和添加噪声,差分隐私通常会导致收敛速度变慢。使用越小的噪声乘数对性能的影响越小,但是隐私性也会越小。