关于pytorch:联邦学习Federated-Learning详解以及示例代码

85次阅读

共计 9851 个字符,预计需要花费 25 分钟才能阅读完成。

联邦学习也称为协同学习,它能够在产生数据的设施上进行大规模的训练,并且这些敏感数据保留在数据的所有者那里,本地收集、本地训练。在本地训练后,地方的训练协调器通过获取散布模型的更新取得每个节点的训练奉献,然而不拜访理论的敏感数据。

联邦学习自身并不能保障隐衷(稍后咱们将探讨联邦学习零碎中的隐衷毁坏和修复),但它的确使隐衷成为可能。

联邦学习的用例:

  • 手机输入法的下一个词预测(e.g. McMahan et al. 2017, Hard et al. 2019)
  • 衰弱钻研(e.g. Kaissis et al. 2020, Sadilek et al. 2021)
  • 汽车主动驾驶(e.g. Zeng et al. 2021, OpenMined 的文章)
  • “智能家居”零碎(e.g. Matchi et al. 2019, Wu et al. 2020)

因为隐衷的问题所以对于集体来说,人们宁愿放弃他们的集体数据,也不会将数据提供给平台(平台有时候也想着白嫖😉),所以联邦学习简直涵盖了所有以集体为单位进行预测的所有场景。

随着公众和政策制定者越来越意识到隐衷的重要性,数据实际中对爱护隐衷的机器学习的需要也正在回升,对于数据的拜访受到越来越多的审查,对联邦学习等尊重隐衷的工具的钻研也越来越沉闷。在现实状况下,联邦学习能够在爱护集体和机构的隐衷的前提下,使数据利益相关者之间的单干成为可能,因为以前商业秘密、私人衰弱信息或数据泄露危险的通常使这种单干变得艰难甚至无奈进行。

欧盟《通用数据保护条例》或《加利福尼亚消费者隐衷法》等政府法规使联邦学习等隐衷爱护策略成为心愿放弃非法经营的企业的有用工具。与此同时,在放弃模型性能和效率的同时取得所需的隐衷和平安水平,这自身就带来了大量技术挑战。

从集体数据生产者(咱们都是其中的一员)的日常角度来看,至多在实践上是能够在私人衰弱和财务数据之间搁置一些货色来屏蔽那种跟踪你在网上行为设置裸露你的个人隐私的所谓的大杂烩生态系统。

如果这些问题中的任何一个引起你的共鸣,请持续浏览以理解更多对于联邦学习的复杂性以及它能够为应用敏感数据的机器学习做了哪些工作。

联邦学习简介

联邦学习的目标是训练来自多个数据源的单个模型,其约束条件是数据停留在数据源上,而不是由数据源 (也称为节点、客户端) 替换,也不是由地方服务器进行编排训练(如果存在的话)。

在典型的联邦学习计划中,地方服务器将模型参数发送到各节点(也称为客户端、终端或工作器)。节点针对本地数据的一些训练初始模型,并将新训练的权重发送回地方服务器,地方服务器对新模型参数求平均值(通常与在每个节点上执行的训练量无关)。在这种状况下,地方服务器或其余节点永远不会间接看到任何一个其余节点上的数据,并应用平安聚合等附加技术进一步加强隐衷。

该框架内有许多变体。例如,在本文中次要关注由地方服务器治理的联邦学习计划,该计划在多个雷同类型的设施上编排训练,节点上每次训练都应用本人的本地数据并将后果上传到地方服务器,这是在 2017 年由 McMahan 等人形容的根本计划。然而某些状况下可能须要勾销训练的集中控制,当单个节点调配地方管理器的角色时,它就变成了去中心化的联邦学习,这是针对非凡的医疗数据训练模型的一种无效的解决方案。

典型的联邦学习场景可能波及大量的设施(例如手机),所有手机的计算能力大抵类似,训练雷同的模型架构。但有一些计划,例如 Diao 等人 2021 年提出的 HeteroFL 容许在具备微小差别的通信和计算能力的各种设施上训练一个繁多的推理模型,甚至能够训练具备不同架构和参数数量的部分模型,而后将训练的参数汇集到一个全局推理模型中。

联邦学习还有一个劣势是数据保留在产生数据的设施上,训练数据集通常比模型要大得多,因而发送后者而不是前者能够节俭带宽。

但在这些劣势中最重要的还是隐衷爱护,尽管有可能仅通过模型参数更新就推断出对于公有数据集内容的某些内容。McMahan 等人在 2017 年应用了一个简略的例子来解释该破绽,即应用一个“词袋”输出向量训练的语言模型,其中每个输出向量具体对应于一个大词汇表中的一个单词。对于相应单词的每个非零梯度更新将为窃听者提供一个对于该单词在公有数据集中存在 (反之亦然) 的线索,还有更简单的攻打也被证实了。为了解决这个问题,能够将多种隐衷加强技术整合到联邦学习中,从平安的更新聚合到应用齐全同态加密进行训练。上面咱们将简要介绍联邦学习中对隐衷的最突出的威逼及其缓解措施。

国家对数据隐衷的监管是一个新兴的政策畛域,然而要比基于集体数据收集和剖析的倒退要晚 10 到 20 年。2016 年颁布的《欧洲个别数据保护条例》(European General data Protection regulation,简称 GDPR)是最重要的对于公众集体数据的法规,这可能会有些奇怪, 因为相似的爱护限度企业监测和数据收集的措施施尚处于起步阶段甚至是没有。

两年后的 2018 年,加州消费者隐衷法案紧随欧盟的 GDPR 成为法律。作为一项州法律,与 GDPR 相比,CCPA 在天文范畴上显著受到限制,该法案对个人信息的定义更窄。

联邦学习的名字是由 McMahan 等人在 2017 年的一篇论文中引入的,用来形容扩散数据模型的训练。作者依据 2012 年白宫对于消费者数据隐衷的报告为他们的零碎制订了设计策略。他们提出了联邦学习的两个次要用例: 图像分类和用于语音辨认或下一个单词 / 短语预测的语言模型。

不久以后与分布式训练相干的潜在攻打就相继的呈现了。Phong et al. 2017 和 Bhowmick et al. 2018 等人的工作表明,即便只拜访从联邦学习客户端返回到服务器的梯度更新或局部训练的模型,也能够推断出一些形容私人数据的细节。在 inphero 的文章中,你能够看到对于隐衷问题的总结和解决办法。

在联邦学习中,隐衷、有效性和效率之间的均衡波及宽泛的畛域。服务器和客户机之间的通信 (或者仅仅是去中心化客户机之间的通信) 能够在传输时进行加密,但还有一个更强壮的选项即在训练期间数据和模型也放弃加密。同态加密可用于对加密的数据执行计算,因而 (在现实状况下) 输入只能由持有密钥的涉众解密。OpenMined 的 PySyft、Microsoft 的 SEAL 或 TensorFlow Encrypted 等库为加密的深度学习提供了工具,这些工具能够利用到联邦学习零碎中。

对于联邦学习的介绍到此为止,接下来咱们将在教程局部中设置一个简略的联邦学习演示。

联邦学习代码实现

既然咱们曾经晓得在何处以及为什么要应用联邦学习,那么让咱们入手看看咱们如何这样做,在这里咱们应用鸢尾花数据集进行联邦学习。

有许多联邦学习库可供选择,从在 GitHub 上领有超过 1700 颗星的更支流的 Tensorflow Federated 到风行且重视隐衷的 PySyft,再到面向钻研的 FedJAX。上面表中蕴含风行的联邦学习存储库的参考列表。

在咱们的演示中将应用 Flower 库。咱们抉择这个库的局部起因是它以一种可拜访的形式举例说明了根本的联邦学习概念并且它与框架无关,Flower 能够整合任何构建模型的深度学习工具包(他们在文档中有 TensorFlow、PyTorch、MXNet 和 SciKit-Learn 的示例)所以咱们将应用 SciKit-Learn 中蕴含的“iris”数据集和 Pytorch 来验证它所说的与框架无关的这个个性。从高层的角度来看咱们须要设置一个服务器和一个客户端,对于客户端咱们应用不同的训练数据集。首先就是设置地方协调器。

设置协调器的第一步就是定义一个评估策略并将其传递给 Flower 中的默认配置服务器。但首先让咱们确保设置了一个虚拟环境,其中蕴含须要的所有依赖项。在 Unix 命令行上:

virtualenv flower_env python==python3
source flower_env/bin/activate
pip install flwr==0.17.0# I'm running this example on a laptop (no gpu)
# so I am installing the cpu only version of PyTorch
# follow the instructions at https://pytorch.org/get-started/locally/
# if you want the gpu optionpip install torch==1.9.1+cpu torchvision==0.10.1+cpu \
    -f https://download.pytorch.org/whl/torch_stable.htmlpip install scikit-learn==0.24.0

随着咱们的虚拟环境启动并运行,咱们能够编写一个模块来启动 Flower 服务器来解决联邦学习。在上面的代码中,咱们蕴含了 argparse,以便在从命令行调用服务器模块时更容易地试验不同数量的训练轮次。咱们还定义了一个生成评估函数的函数,这是咱们增加到 Flower 服务器默认配置应用的策略中的惟一其余内容。

以下咱们的服务器模块文件的内容:

import argparse
import flwr as fl
import torch
from pt_client import get_data, PTMLPClient

def get_eval_fn(model):
    # This `evaluate` function will be called after every round
    def evaluate(parameters: fl.common.Weights):
        loss, _, accuracy_dict = model.evaluate(parameters)
        return loss, accuracy_dict
return evaluate

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-r", "--rounds", type=int, default=3,\
            help="number of rounds to train")
    args = parser.parse_args()
    torch.random.manual_seed(42)
    model = PTMLPClient(split="val")
    strategy = fl.server.strategy.FedAvg( \
        eval_fn=get_eval_fn(model),\
        )
    fl.server.start_server("[::]:8080", strategy=strategy, \
            config={"num_rounds": args.rounds})

留神下面代码中调用的 PTMLPClient。这个是 server 模块用来定义评估函数的,这个类也是用于训练的模型类并兼作联邦学习客户端。接下来咱们将定义 PTMLPClient,并继承 Flower 的 NumPyClient 类和 torch.nn.Module 类,如果您应用 PyTorch,你必定就相熟它们。

NumPyClient 类解决与服务器的通信,咱们须要实现 4 个形象函数 set_parameters、get_parameters、fit 和 evaluate。torch.nn.Module 类为咱们提供了 PyTorch 模型,还有就是应用 PyTorch Adam 优化器进行训练的能力。咱们的 PTMLPClient 类只有 100 多行代码,所以咱们将从 init 开始顺次介绍每个类的函数。

请留神,咱们从两个类继承。从 nn.Module 继承意味着咱们必须确保应用 super 命令从 nn.Module 调用 init,然而如果您遗记这样做,Python 会立刻告诉你。除此之外,咱们将三个线性层初始化为矩阵(torch.tensor 数据类型),并将一些对于训练宰割和模型维度的信息存储为类变量。

class PTMLPClient(fl.client.NumPyClient, nn.Module):
    def __init__(self, dim_in=4, dim_h=32, \
            num_classes=3, lr=3e-4, split="alice"):
        super(PTMLPClient, self).__init__()
        self.dim_in = dim_in
        self.dim_h = dim_h
        self.num_classes = num_classes
        self.split = split
        
        self.w_xh = nn.Parameter(torch.tensor(\
            torch.randn(self.dim_in, self.dim_h) \
            / np.sqrt(self.dim_in * self.dim_h))\
            )
        self.w_hh = nn.Parameter(torch.tensor(\
            torch.randn(self.dim_h, self.dim_h) \
            / np.sqrt(self.dim_h * self.dim_h))\
            )
        self.w_hy = nn.Parameter(torch.tensor(\
            torch.randn(self.dim_h, self.num_classes) \
            / np.sqrt(self.dim_h * self.num_classes))\
            )
        self.lr = lr

接下来咱们将定义 PTMLPClient 类的 get_parameters 和 set_parameters 函数。这些函数将所有模型参数连贯为一个扁平的 numpy 数组,这是 Flower 的 NumPyClient 类预期返回和接管的数据类型。这合乎联邦学习计划,因为服务器将向每个客户端发送初始参数(应用 set_parameters)并冀望返回一组局部训练的权重(来自 get_parameters)。这种模式在训练的每轮呈现一次。咱们还在 set_parameters 中初始化优化器和损失函数。

def get_parameters(self):
        my_parameters = np.append(\
            self.w_xh.reshape(-1).detach().numpy(), \
            self.w_hh.reshape(-1).detach().numpy() \
            )
        my_parameters = np.append(\
        my_parameters, \
            self.w_hy.reshape(-1).detach().numpy() \
            )
       return my_parameters

def set_parameters(self, parameters):
        parameters = np.array(parameters)
        total_params = reduce(lambda a,b: a*b,\
            np.array(parameters).shape)
        expected_params = self.dim_in * self.dim_h \
            + self.dim_h**2 \
            + self.dim_h * self.num_classes
        
        start = 0
        stop = self.dim_in * self.dim_h
        self.w_xh = nn.Parameter(torch.tensor(\
                parameters[start:stop])\
                .reshape(self.dim_in, self.dim_h).float() \)
                
        start = stop
        stop += self.dim_h**2
        self.w_hh = nn.Parameter(torch.tensor(\
                parameters[start:stop])\
                .reshape(self.dim_h, self.dim_h).float() \)
        start = stop
        stop += self.dim_h * self.num_classes
        self.w_hy = nn.Parameter(torch.tensor(\
                parameters[start:stop])\
                .reshape(self.dim_h, self.num_classes).float()\)
        self.act = torch.relu
        
        self.optimizer = torch.optim.Adam(self.parameters())
        self.loss_fn = nn.CrossEntropyLoss()

接下来,咱们将定义咱们的前向传递和一个用于获取损失标量的函数。

def forward(self, x):
        x = self.act(torch.matmul(x, self.w_xh))
        x = self.act(torch.matmul(x, self.w_hh))
        x = torch.matmul(x, self.w_hy)
        return x
        
def get_loss(self, x, y):
        prediction = self.forward(x)
        loss = self.loss_fn(prediction, y)
        return loss

咱们客户端还须要的最初几个函数是 fit 和 evaluate。对于每一轮,每个客户端在进行几个阶段的训练之前应用提供给 fit 办法的参数初始化它的参数(在本例中默认为 10)。evaluate 办法在计算训练数据验证的损失和准确性之前设置参数。

def fit(self, parameters, config=None, epochs=10):
        self.set_parameters(parameters)
        x, y = get_data(split=self.split)
        x, y = torch.tensor(x).float(), torch.tensor(y).long()
        self.train()
        for ii in range(epochs):
            self.optimizer.zero_grad()
            loss = self.get_loss(x, y)
            loss.backward()
            self.optimizer.step()
        
        loss, _, accuracy_dict = self.evaluate(self.get_parameters())
        return self.get_parameters(), len(y), \
                {"loss": loss, "accuracy": \
                accuracy_dict["accuracy"]}

    def evaluate(self, parameters, config=None):
        self.set_parameters(parameters)
        val_x, val_y = get_data(split="val")
        val_x = torch.tensor(val_x).float()
        val_y = torch.tensor(val_y).long()
        
        self.eval()
        prediction = self.forward(val_x)
        
        loss = self.loss_fn(prediction, val_y).detach().numpy()
        
        prediction_class = np.argmax(\
            prediction.detach().numpy(), axis=-1)
        
        accuracy = sklearn.metrics.accuracy_score(\
            val_y.numpy(), prediction_class)
        
        return float(loss), len(val_y), \
            {"accuracy":float(accuracy)}

咱们的客户端类中的 fit 和 evaluate 都调用了一个函数 get_data,它只是 SciKit-Learn iris 数据集的包装器。它还将数据拆分为训练集和验证集,并进一步拆分训练数据集(咱们称为“alice”和“bob”)以模仿联邦学习,因为联邦学习的客户端都有本人的数据。

def get_data(split="all"):
x, y = sklearn.datasets.load_iris(return_X_y=True)
    np.random.seed(42); np.random.shuffle(x)
    np.random.seed(42); np.random.shuffle(y)
    val_split = int(0.2 * x.shape[0])
    train_split = (x.shape[0] - val_split) // 2
    eval_x, eval_y = x[:val_split], y[:val_split]    
    
    alice_x, alice_y = x[val_split:val_split + train_split], y[val_split:val_split + train_split]
    
    bob_x, bob_y = x[val_split + train_split:], y[val_split + train_split:]
    
    train_x, train_y = x[val_split:], y[val_split:]
    
    if split == "all":
        return train_x, train_y
    elif split == "alice":
        return alice_x, alice_y
    elif split == "bob":
        return bob_x, bob_y
    elif split == "val":
        return eval_x, eval_y
    else:
        print("error: split not recognized.")
        return None

当初咱们只须要在文件底部填充一个 if name == “main”: 办法,以便咱们能够从命令即将咱们的客户端代码作为模块运行。

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-s", "--split", type=str, default="alice",\
    help="The training split to use, options are'alice','bob', or'all'")
    
    args = parser.parse_args()
    torch.random.manual_seed(42)
    
    fl.client.start_numpy_client("localhost:8080", client=PTMLPClient(split=args.split))

最初,确保在客户端模块的顶部导入所需的所有内容。

import argparse
import numpy as np
import sklearn
import sklearn.datasets
import sklearn.metrics
import torch
import torch.nn as nn
from functools import reduce
import flwr as fl

这就是咱们应用 Flower 运行联邦训练演示所需实现的全副代码!

要开始运行联邦训练,首先在其命令行终端中启动服务器。咱们将咱们的服务器保留为 pt_server.py,咱们的客户端模块保留为 pt_client.py,两者都在咱们正在工作的目录的根目录中,所以为了启动一个服务器并通知它进行 40 轮联邦学习,咱们应用以下命令。

python -m pt_server -r 40

接下来关上一个新的终端,用“alice”训练分组启动你的第一个客户端:

python -m pt_client -s alice

启动“bob”训练分组的第二个客户端。

python -m pt_client -s bob

如果一切正常,在运行服务器过程的终端中看到训练启动和信息滚动。

这个演示在 20 轮训练中达到了 96% 以上的准确率。训练运行的损失和准确度曲线如下所示:

联邦学习的将来

人们可能会置信“再也没有隐衷这种货色了”。这些申明次要是针对互联网的(这样的申明至多从 1999 年就开始了),但随着智能家居设施和爱管闲事的家用机器人的迅速遍及,你可能感觉这些舆论是正确的。

然而请留神是谁在做这些申明,你会发现他们中的许多人在你的数据被窃取的过程中是可能取得既得利益的。这种“没有隐衷”的失败主义态度不仅是谬误的,而且是危险的: 失去隐衷会使集体和个人以他们可能不会留神到或抵赖的形式被奇妙地操纵。

联邦学习是随同着不断扩大的数据量而生的,数据无处不在,联邦学习的劣势因而取得了政府、企业等各界的关注。联邦学习可能无效解决数据孤岛和数据隐衷爱护的两难问题。这将会为将来人工智能合作,从而实现跨越式倒退奠定良好基础,在多行业、多畛域都有宽泛的利用前景。

作者:James Montantes

正文完
 0