Python应用联邦学习怎么实现

wen python案例 3

本文目录导读:

Python应用联邦学习怎么实现

  1. 基础联邦学习框架示例
  2. 使用PySyft库实现联邦学习
  3. 使用Flower框架(推荐生产环境)
  4. 高级联邦学习功能
  5. 完整示例:FedAvg算法实现

我来详细介绍如何在Python中实现联邦学习,包括几种主流方法。

基础联邦学习框架示例

使用PyTorch实现的简单联邦学习

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
from copy import deepcopy
import random
# 简单的神经网络模型
class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = nn.Sequential(
            nn.Linear(784, 128),
            nn.ReLU(),
            nn.Linear(128, 10)
        )
    def forward(self, x):
        return self.fc(x.view(x.size(0), -1))
# 模拟客户端数据
def create_client_data(num_clients=5, samples_per_client=100):
    """创建模拟的联邦学习数据"""
    client_data = []
    for i in range(num_clients):
        # 生成随机数据,模拟非独立同分布(Non-IID)
        x = torch.randn(samples_per_client, 1, 28, 28)
        y = torch.randint(0, 10, (samples_per_client,))
        # 让每个客户端有不同的数据分布
        y = (y + i) % 10
        dataset = TensorDataset(x, y)
        dataloader = DataLoader(dataset, batch_size=10, shuffle=True)
        client_data.append(dataloader)
    return client_data
class FederatedLearning:
    def __init__(self, model, num_clients=5):
        self.global_model = model
        self.num_clients = num_clients
        self.client_models = [deepcopy(model) for _ in range(num_clients)]
    def client_update(self, client_model, dataloader, epochs=1):
        """客户端本地训练"""
        criterion = nn.CrossEntropyLoss()
        optimizer = optim.SGD(client_model.parameters(), lr=0.01)
        client_model.train()
        for epoch in range(epochs):
            for batch_x, batch_y in dataloader:
                optimizer.zero_grad()
                output = client_model(batch_x)
                loss = criterion(output, batch_y)
                loss.backward()
                optimizer.step()
        return client_model.state_dict()
    def federated_averaging(self, client_weights):
        """联邦平均算法(FedAvg)"""
        avg_weights = deepcopy(self.global_model.state_dict())
        for key in avg_weights.keys():
            avg_weights[key] = torch.mean(
                torch.stack([w[key].float() for w in client_weights]), 
                dim=0
            )
        return avg_weights
    def train_round(self, client_dataloaders, selected_clients=None):
        """执行一轮联邦学习"""
        if selected_clients is None:
            selected_clients = list(range(self.num_clients))
        client_weights = []
        # 每个客户端本地训练
        for client_id in selected_clients:
            client_model = deepcopy(self.global_model)
            weights = self.client_update(
                client_model, 
                client_dataloaders[client_id]
            )
            client_weights.append(weights)
        # 聚合更新全局模型
        global_weights = self.federated_averaging(client_weights)
        self.global_model.load_state_dict(global_weights)
        return self.global_model
# 使用示例
def main():
    # 创建数据和模型
    num_clients = 5
    client_data = create_client_data(num_clients)
    global_model = SimpleNet()
    # 初始化联邦学习
    fl_system = FederatedLearning(global_model, num_clients)
    # 训练多轮
    num_rounds = 10
    for round_idx in range(num_rounds):
        # 随机选择部分客户端(模拟客户端掉线)
        selected = random.sample(range(num_clients), 3)
        # 执行一轮训练
        fl_system.train_round(client_data, selected)
        if round_idx % 2 == 0:
            print(f"Round {round_idx}: Global model trained with {len(selected)} clients")
if __name__ == "__main__":
    main()

使用PySyft库实现联邦学习

# 安装: pip install syft[grid]
import syft as sy
import torch
import torch.nn as nn
import torch.optim as optim
from syft.frameworks.torch.fl import utils
# 创建虚拟工作节点
hook = sy.TorchHook(torch)
# 创建多个虚拟客户端
workers = []
for i in range(5):
    worker = sy.VirtualWorker(hook, id=f"client_{i}")
    workers.append(worker)
# 创建数据并分发到各个客户端
def distribute_data(workers):
    datasets = []
    for worker in workers:
        # 每个客户端有不同分布的数据
        x = torch.randn(100, 1, 28, 28).send(worker)
        y = torch.randint(0, 10, (100,)).send(worker)
        datasets.append((x, y))
    return datasets
# 联邦学习训练
def federated_training(model, datasets, workers, rounds=10):
    for round_idx in range(rounds):
        print(f"\nRound {round_idx + 1}")
        client_models = []
        for worker, (x, y) in zip(workers, datasets):
            # 发送模型到客户端
            model_ptr = model.copy().send(worker)
            # 本地训练
            criterion = nn.CrossEntropyLoss()
            optimizer = optim.SGD(model_ptr.parameters(), lr=0.01)
            for epoch in range(5):
                optimizer.zero_grad()
                output = model_ptr(x)
                loss = criterion(output, y.long())
                loss.backward()
                optimizer.step()
            # 获取更新后的模型
            model_ptr.get()
            client_models.append(model_ptr)
        # 聚合模型
        with torch.no_grad():
            # 平均所有客户端模型参数
            for param_idx, param in enumerate(model.parameters()):
                param.data = torch.mean(
                    torch.stack([m.parameters() for m in client_models])[param_idx],
                    dim=0
                )
        print(f"Global model updated with {len(client_models)} clients")
# 使用示例
model = nn.Sequential(
    nn.Linear(784, 128),
    nn.ReLU(),
    nn.Linear(128, 10)
)
datasets = distribute_data(workers)
federated_training(model, datasets, workers)

使用Flower框架(推荐生产环境)

# 安装: pip install flwr
import flwr as fl
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
# 定义模型
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.dropout1 = nn.Dropout2d(0.25)
        self.dropout2 = nn.Dropout2d(0.5)
        self.fc1 = nn.Linear(9216, 128)
        self.fc2 = nn.Linear(128, 10)
    def forward(self, x):
        x = self.conv1(x)
        x = nn.functional.relu(x)
        x = self.conv2(x)
        x = nn.functional.relu(x)
        x = nn.functional.max_pool2d(x, 2)
        x = self.dropout1(x)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = nn.functional.relu(x)
        x = self.dropout2(x)
        x = self.fc2(x)
        output = nn.functional.softmax(x, dim=1)
        return output
# 创建Flower客户端
class FlowerClient(fl.client.NumPyClient):
    def __init__(self, model, trainloader, testloader):
        self.model = model
        self.trainloader = trainloader
        self.testloader = testloader
    def get_parameters(self):
        return [val.cpu().numpy() for _, val in self.model.state_dict().items()]
    def set_parameters(self, parameters):
        params_dict = zip(self.model.state_dict().keys(), parameters)
        state_dict = {k: torch.tensor(v) for k, v in params_dict}
        self.model.load_state_dict(state_dict, strict=True)
    def fit(self, parameters, config):
        self.set_parameters(parameters)
        # 本地训练逻辑
        criterion = nn.CrossEntropyLoss()
        optimizer = optim.SGD(self.model.parameters(), lr=0.01)
        for epoch in range(5):
            for batch_x, batch_y in self.trainloader:
                optimizer.zero_grad()
                output = self.model(batch_x)
                loss = criterion(output, batch_y)
                loss.backward()
                optimizer.step()
        return self.get_parameters(), len(self.trainloader.dataset), {}
    def evaluate(self, parameters, config):
        self.set_parameters(parameters)
        # 评估逻辑
        self.model.eval()
        correct = 0
        total = 0
        with torch.no_grad():
            for batch_x, batch_y in self.testloader:
                output = self.model(batch_x)
                _, predicted = torch.max(output.data, 1)
                total += batch_y.size(0)
                correct += (predicted == batch_y).sum().item()
        accuracy = correct / total
        return float(accuracy), len(self.testloader.dataset), {"accuracy": accuracy}
# 启动联邦学习服务器
def start_server():
    # 服务器配置
    strategy = fl.server.strategy.FedAvg(
        fraction_fit=0.5,  # 每轮选择50%的客户端
        min_fit_clients=2,  # 最少需要2个客户端
        min_available_clients=2,
    )
    fl.server.start_server(
        server_address="[::]:8080",
        config=fl.server.ServerConfig(num_rounds=10),
        strategy=strategy,
    )
# 启动客户端
def start_client(client_id, model, trainloader, testloader):
    client = FlowerClient(model, trainloader, testloader)
    fl.client.start_numpy_client(
        server_address="[::]:8080",
        client=client,
    )
# 主程序
if __name__ == "__main__":
    # 创建模型和数据
    model = Net()
    # 创建模拟数据
    train_data = TensorDataset(torch.randn(100, 1, 28, 28), torch.randint(0, 10, (100,)))
    test_data = TensorDataset(torch.randn(20, 1, 28, 28), torch.randint(0, 10, (20,)))
    trainloader = DataLoader(train_data, batch_size=10)
    testloader = DataLoader(test_data, batch_size=10)
    # 启动客户端
    start_client("client_1", model, trainloader, testloader)

高级联邦学习功能

# 实现安全聚合(Secure Aggregation)
import hashlib
import pickle
class SecureAggregation:
    def __init__(self):
        self.secrets = {}
    def generate_masks(self, num_clients, model_size):
        """生成安全掩码"""
        masks = []
        for i in range(num_clients):
            mask = torch.randn(model_size)
            masks.append(mask)
        return masks
    def secure_aggregate(self, client_updates, masks):
        """安全聚合更新"""
        # 添加掩码
        masked_updates = []
        for update, mask in zip(client_updates, masks):
            masked_update = update + mask
            masked_updates.append(masked_update)
        # 聚合
        aggregated = torch.mean(torch.stack(masked_updates), dim=0)
        # 移除掩码
        mask_sum = torch.mean(torch.stack(masks), dim=0)
        final_result = aggregated - mask_sum
        return final_result
# 差分隐私联邦学习
class DPFederatedLearning(FederatedLearning):
    def __init__(self, model, num_clients, epsilon=1.0, delta=1e-5):
        super().__init__(model, num_clients)
        self.epsilon = epsilon  # 隐私预算
        self.delta = delta      # 失败概率
    def add_noise(self, gradients):
        """添加高斯噪声实现差分隐私"""
        sensitivity = 1.0  # 敏感度
        noise_scale = sensitivity * np.sqrt(2 * np.log(1.25 / self.delta)) / self.epsilon
        noisy_gradients = {}
        for key, grad in gradients.items():
            noise = torch.normal(0, noise_scale, size=grad.shape)
            noisy_gradients[key] = grad + noise
        return noisy_gradients
    def train_round(self, client_dataloaders, selected_clients=None):
        """带差分隐私的训练轮次"""
        client_weights = []
        for client_id in selected_clients:
            client_model = deepcopy(self.global_model)
            weights = self.client_update(client_model, client_dataloaders[client_id])
            # 添加差分隐私噪声
            noisy_weights = self.add_noise(weights)
            client_weights.append(noisy_weights)
        # 聚合
        global_weights = self.federated_averaging(client_weights)
        self.global_model.load_state_dict(global_weights)
        return self.global_model
# 异步联邦学习
class AsyncFederatedLearning:
    def __init__(self, model, staleness_threshold=5):
        self.global_model = model
        self.staleness_threshold = staleness_threshold
        self.model_version = 0
    def async_update(self, client_update, client_version):
        """异步处理客户端更新"""
        staleness = self.model_version - client_version
        if staleness > self.staleness_threshold:
            print(f"Client update too stale (staleness={staleness}), discarding")
            return
        # 根据时效性调整学习率
        learning_rate = 1.0 / (staleness + 1)
        # 应用更新
        with torch.no_grad():
            for global_param, client_param in zip(
                self.global_model.parameters(), 
                client_update.values()
            ):
                global_param.data += learning_rate * (client_param - global_param.data)
        self.model_version += 1

完整示例:FedAvg算法实现

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from collections import OrderedDict
import copy
class FedAvg:
    """联邦平均算法完整实现"""
    def __init__(self, global_model, num_clients, fraction=0.3):
        self.global_model = global_model
        self.num_clients = num_clients
        self.fraction = fraction  # 每轮参与训练的客户端比例
    def select_clients(self):
        """选择参与训练的客户端"""
        num_selected = max(1, int(self.num_clients * self.fraction))
        return np.random.choice(self.num_clients, num_selected, replace=False)
    def local_training(self, client_model, train_loader, epochs=10):
        """客户端本地训练"""
        criterion = nn.CrossEntropyLoss()
        optimizer = optim.SGD(client_model.parameters(), lr=0.01, momentum=0.9)
        client_model.train()
        for epoch in range(epochs):
            for data, target in train_loader:
                optimizer.zero_grad()
                output = client_model(data)
                loss = criterion(output, target)
                loss.backward()
                optimizer.step()
        return client_model.state_dict()
    def fed_avg_aggregation(self, client_weights, weights=None):
        """FedAvg聚合算法"""
        if weights is None:
            weights = [1.0 / len(client_weights)] * len(client_weights)
        # 初始化聚合权重
        avg_weights = OrderedDict()
        for key in client_weights[0].keys():
            avg_weights[key] = torch.zeros_like(client_weights[0][key])
            # 加权平均
            for w, client_weight in zip(weights, client_weights):
                avg_weights[key] += w * client_weight[key]
        return avg_weights
    def train(self, client_loaders, rounds=100):
        """执行联邦学习训练"""
        round_accuracies = []
        for round_idx in range(rounds):
            # 选择客户端
            selected_clients = self.select_clients()
            # 收集客户端更新
            client_weights = []
            for client_id in selected_clients:
                client_model = copy.deepcopy(self.global_model)
                # 本地训练
                weights = self.local_training(
                    client_model, 
                    client_loaders[client_id]
                )
                client_weights.append(weights)
            # 聚合更新
            global_weights = self.fed_avg_aggregation(client_weights)
            self.global_model.load_state_dict(global_weights)
            # 评估
            accuracy = self.evaluate()
            round_accuracies.append(accuracy)
            if round_idx % 10 == 0:
                print(f"Round {round_idx}: Accuracy = {accuracy:.4f}")
        return round_accuracies
    def evaluate(self):
        """评估全局模型"""
        self.global_model.eval()
        # 这里应该使用测试数据评估
        # 简化示例返回随机准确率
        return np.random.random()
# 使用示例
def main():
    # 创建模型
    model = nn.Sequential(
        nn.Linear(784, 128),
        nn.ReLU(),
        nn.Linear(128, 10)
    )
    # 初始化联邦学习系统
    fed_avg = FedAvg(model, num_clients=10, fraction=0.3)
    # 创建模拟客户端数据加载器
    client_loaders = []
    for i in range(10):
        # 每个客户端100个样本
        data = torch.randn(100, 784)
        labels = torch.randint(0, 10, (100,))
        dataset = torch.utils.data.TensorDataset(data, labels)
        loader = torch.utils.data.DataLoader(dataset, batch_size=10, shuffle=True)
        client_loaders.append(loader)
    # 开始训练
    accuracies = fed_avg.train(client_loaders, rounds=100)
if __name__ == "__main__":
    main()

实现联邦学习的关键点:

  1. 通信机制:客户端和服务器之间的参数传递
  2. 聚合算法:FedAvg是最基础的方法
  3. 隐私保护:差分隐私、安全多方计算等
  4. 异步处理:处理不同客户端的速度差异
  5. 容错机制:处理客户端离线等问题

推荐在实际项目中使用成熟的框架如 FlowerPySyft,它们提供了更多的功能和更好的性能优化。

抱歉,评论功能暂时关闭!