Python を使用して複数のクライアントをローカルでシミュレートすると、サーバーはフェデレーテッド ラーニングのためにそれらを均一に管理します。クライアントは独自のデータを使用してモデルをローカルでトレーニングします。サーバーはトレーニング結果を集約し、モデルを更新してクライアントに配布し、クライアントはトレーニングを続けています。
目次
2. フェデレーション ラーニングにおけるプライバシーの問題
5. フェデレーションラーニングと集中トレーニングの効果の比較
I.はじめに
1. フェデレーテッド ラーニングとは何ですか?
フェデレーテッド ラーニングは機械学習の概念です。その概念は、サーバーが最初に統合モデルをクライアントに配布し、次にクライアントがトレーニングにローカル データを使用し、その後モデルを更新してサーバーに送り返すというものです。すべての関係者からの新しいモデルを受け取ると、対応する計算が実行されてグローバル モデルが更新され、その後、更新されたモデルが分割され、一定の回数に達するか収束に達するまでトレーニングが継続され、最後にモデルが共同でトレーニングされます。複数のパーティが得られます。フェデレーテッド ラーニングの中核となる概念は、「モデルはデータの動きを理解しておらず、データは利用可能ではあるが目に見えない」というものです。
(ps: ここでは簡単な紹介のみを行います。フェデレーション ラーニングの詳細については、次の記事を参照してください)
2. フェデレーション ラーニングにおけるプライバシーの問題
Federated Learning の本質は依然として機械学習ですが、従来のデータの集中学習は、各クライアントが独自に学習するように変更され、パラメーターのみが交換されるため、データのセキュリティとプライバシーを確保できますが、これはあくまでも最大限の効果にすぎません。さまざまな問題を考慮すると、フェデレーション ラーニングに対するこの種の攻撃は、学習プロセスのセキュリティを確保するために他のテクノロジーと組み合わせる必要もあります。
(追記: ここでは、フェデレーテッド ラーニングにおけるデータ セキュリティの問題についてはあまり分析しません。興味がある場合は、次の論文を参照してください。非常によく書かれており、理解しやすいです)
http://www.jos.org.cn/jos/article/abstract/6446 http://www.jos.org.cn/jos/article/abstract/6446
2. 環境整備
この実験は Python で実装され、機械学習ライブラリ PyTorch を使用します。
- アナコンダ、Python、PyTorch
- Pycharmを使用したコンパイラ
- データセット: cifar10
- モデル: ResNet-18
基本的なプロセス:
- サーバーは設定ファイルに従って初期化モデルを生成し、クライアントは自身のIDに従ってデータセットを重複せずに水平方向に切り出します。
- サーバーはグローバル モデルをクライアントに送信します
- クライアントはグローバル モデルを (サーバーから) 受信し、複数回のローカル反復を通じてローカル パラメーターの差を計算し、それをサーバーに返します。
- サーバーは各クライアント間の差異を集約してモデルを更新し、現在のモデルのパフォーマンスを評価します。
- 性能が基準を満たしていない場合は手順 2 を繰り返し、基準を満たしていない場合は終了します。
3. 具体的な実装
1.設定ファイルの書き込み
プロジェクトフォルダー直下にutilsフォルダーを作成し、その中に設定ファイルconf.jsonを作成します(その中のデータは必要に応じて変更できます)。(json ファイルではコメントが許可されていないため、各値は 2 回割り当てられ、1 回目はコメントとして使用され、2 回目は実際の値になります)
{
"model_name" : "模型名称",
"model_name" : "resnet18",
"no_models" : "客户端总数量",
"no_models" : 5,
"type" : "数据集信息",
"type" : "cifar",
"global_epochs" : "全局迭代次数",
"global_epochs" : 5,
"local_epochs" : "本地迭代次数",
"local_epochs" : 2,
"k" : "每一轮选用k个客户端参与训练",
"k" : 3,
"batch_size" : "本地训练每一轮的样本数",
"batch_size" : 32,
"notes" : "本地训练的超参数设置",
"lr" : 0.001,
"momentum" : 0.0001,
"lambda" : 0.1
}
2. トレーニングデータセットを取得する
プロジェクト フォルダーの下に datasets.py ファイルを作成します。
from torchvision import datasets, transforms
# 获取数据集
def get_dataset(dir, name):
if name == 'mnist':
# root: 数据路径
# train参数表示是否是训练集或者测试集
# download=true表示从互联网上下载数据集并把数据集放在root路径中
# transform:图像类型的转换
train_dataset = datasets.MNIST(dir, train=True, download=True, transform=transforms.ToTensor())
eval_dataset = datasets.MNIST(dir, train=False, transform=transforms.ToTensor())
elif name == 'cifar':
# 设置两个转换格式
# transforms.Compose 是将多个transform组合起来使用(由transform构成的列表)
transform_train = transforms.Compose([
# transforms.RandomCrop: 切割中心点的位置随机选取
transforms.RandomCrop(32, padding=4),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
# transforms.Normalize: 给定均值:(R,G,B) 方差:(R,G,B),将会把Tensor正则化
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
])
transform_test = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
])
train_dataset = datasets.CIFAR10(dir, train=True, download=True, transform=transform_train)
eval_dataset = datasets.CIFAR10(dir, train=False, transform=transform_test)
return train_dataset, eval_dataset
3. サーバー側のコードを作成する
プロジェクト フォルダーに server.py ファイルを作成します。サーバーの主な機能は、コンストラクター、集計関数 (FedAvg アルゴリズムを使用)、評価関数などのモデルを集計、評価、および配布することです。
import models
import torch
# 服务器类
class Server(object):
# 定义构造函数
def __init__(self, conf, eval_dataset):
# 导入配置文件
self.conf = conf
# 根据配置文件获取模型
self.global_model = models.get_model(self.conf["model_name"])
# 生成测试集合加载器
self.eval_loader = torch.utils.data.DataLoader(
eval_dataset,
# 根据配置文件设置单个批次大小(32)
batch_size=self.conf["batch_size"],
# 打乱数据集
shuffle=True
)
# 模型聚合函数
# weight_accumulator 存储了每个客户端上传参数的变化值
def model_aggregate(self, weight_accumulator):
# 遍历服务器的全局模型
for name, data in self.global_model.state_dict().items():
# 更新每一次乘以配置文件中的学习率
update_per_layer = weight_accumulator[name] * self.conf["lambda"]
# 累加
if data.type() != update_per_layer.type():
# 因为update_per_layer的type是floatTensor,所以将其转换为模型的LongTensor(损失精度)
data.add_(update_per_layer.to(torch.int64))
else:
data.add_(update_per_layer)
# 模型评估函数
def model_eval(self):
# 开启模型评估模式
self.global_model.eval()
total_loss = 0.0
correct = 0
dataset_size = 0
# 遍历评估数据集合
for batch_id, batch in enumerate(self.eval_loader):
data, target = batch
# 获取所有样本总量大小
dataset_size += data.size()[0]
# 如果可以的话存储到gpu
if torch.cuda.is_available():
data = data.cuda()
target = target.cuda()
# 加载到模型中训练
output = self.global_model(data)
# 聚合所有损失 cross_entropy 交叉熵函数计算损失
total_loss += torch.nn.functional.cross_entropy(
output,
target,
reduction='sum'
).item()
# 获取最大的对数概率的索引值,即在所有预测结果中选择可能性最大的作为最终结果
pred = output.data.max(1)[1]
# 统计预测结果与真实标签的匹配个数
correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()
# 计算准确率
acc = 100.0 * (float(correct) / float(dataset_size))
# 计算损失值
total_l = total_loss / dataset_size
return acc, total_l
4. クライアントコードを書く
プロジェクト フォルダーに client.py ファイルを作成します。クライアントの主な機能は、サーバーからグローバル モデルを受け取り、ローカル データを使用してモデルをトレーニングし、コンストラクターとローカル トレーニング関数を含む差分を返すことです。
import models
import torch
# 客户端类
class Client(object):
#构造函数
def __init__(self, conf, model, train_dataset, id=-1):
# 读取配置文件
self.conf = conf
# 根据配置文件获取客户端本地模型(一般由服务器传输)
self.local_model = models.get_model(self.conf["model_name"])
# 客户端ID
self.client_id = id
# 客户端本地数据集
self.train_dataset = train_dataset
# 按ID对数据集集合进行拆分
all_range = list(range(len(self.train_dataset)))
data_len = int(len(self.train_dataset) / self.conf['no_models'])
train_indices = all_range[id * data_len: (id + 1) * data_len]
# 生成数据加载器
self.train_loader = torch.utils.data.DataLoader(
# 指定父集合
self.train_dataset,
# 每个batch加载多少样本
batch_size=conf["batch_size"],
# 指定子集合
# sampler定义从数据集中提取样本的策略
sampler=torch.utils.data.sampler.SubsetRandomSampler(train_indices)
)
# 模型本地训练函数
def local_train(self, model):
# 客户端获取服务器的模型,然后通过部分本地数据集进行训练
for name, param in model.state_dict().items():
# 用服务器下发的全局模型覆盖本地模型
self.local_model.state_dict()[name].copy_(param.clone())
# 定义最优化函数器用户本地模型训练
optimizer = torch.optim.SGD(
self.local_model.parameters(),
lr=self.conf['lr'],
momentum=self.conf['momentum']
)
# 本地训练模型
# 设置开启模型训练
self.local_model.train()
# 开始训练模型
for e in range(self.conf["local_epochs"]):
for batch_id, batch in enumerate(self.train_loader):
data, target = batch
# 如果可以的话加载到gpu
if torch.cuda.is_available():
data = data.cuda()
target = target.cuda()
# 梯度初始化为0
optimizer.zero_grad()
# 训练预测
output = self.local_model(data)
# 计算损失函数cross_entropy交叉熵误差
loss = torch.nn.functional.cross_entropy(output, target)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
print("Epoch %d done." % e)
# 创建差值字典(结构与模型参数同规格),用于记录差值
diff = dict()
for name, data in self.local_model.state_dict().items():
# 计算训练后与训练前的差值
diff[name] = (data - model.state_dict()[name])
print("Client %d local train done" % self.client_id)
# 客户端返回差值
return diff
5.メイン関数を書く
コードを統合するには、プロジェクト フォルダーに main.py ファイルを作成します。
import argparse
import json
import random
import datasets
from client import *
from server import *
if __name__ == '__main__':
# 设置命令行程序
parser = argparse.ArgumentParser(description='Federated Learning')
parser.add_argument('-c', '--conf', dest='conf')
# 获取所有参数
args = parser.parse_args()
# 读取配置文件,指定编码格式为utf-8
with open(args.conf, 'r', encoding='utf-8') as f:
conf = json.load(f)
# 获取数据集,加载描述信息
train_datasets, eval_datasets = datasets.get_dataset("./data/", conf["type"])
# 启动服务器
server = Server(conf, eval_datasets)
# 定义客户端列表
clients = []
# 创建10个客户端到列表中
for c in range(conf["no_models"]):
clients.append(Client(conf, server.global_model, train_datasets, c))
print("\n\n")
# 全局模型训练
for e in range(conf["global_epochs"]):
print("Global Epoch %d" % e)
# 每次训练从clients列表中随机抽取k个进行训练
candidates = random.sample(clients, conf["k"])
print("select clients is: ")
for c in candidates:
print(c.client_id)
# 累计权重
weight_accumulator = {}
# 初始化空模型参数weight_accumulator
for name, params in server.global_model.state_dict().items():
# 生成一个和参数矩阵大小相同的0矩阵
weight_accumulator[name] = torch.zeros_like(params)
# 遍历选中的客户端,每个客户端本地进行训练
for c in candidates:
diff = c.local_train(server.global_model)
# 根据客户端返回的参数差值字典更新总体权重
for name, params in server.global_model.state_dict().items():
weight_accumulator[name].add_(diff[name])
# 模型参数聚合
server.model_aggregate(weight_accumulator)
# 模型评估
acc, loss = server.model_eval()
print("Epoch %d, acc: %f, loss: %f\n" % (e, acc, loss))
6. モデルファイルの書き込み
プロジェクト フォルダーに models.py ファイルを作成して、使用するさまざまな機械学習モデルを定義します。
import torch
from torchvision import models
# 各种机器学习模型
def get_model(name="vgg16", pretrained=True):
if name == "resnet18":
model = models.resnet18(pretrained=pretrained)
elif name == "resnet50":
model = models.resnet50(pretrained=pretrained)
elif name == "densenet121":
model = models.densenet121(pretrained=pretrained)
elif name == "alexnet":
model = models.alexnet(pretrained=pretrained)
elif name == "vgg16":
model = models.vgg16(pretrained=pretrained)
elif name == "vgg19":
model = models.vgg19(pretrained=pretrained)
elif name == "inception_v3":
model = models.inception_v3(pretrained=pretrained)
elif name == "googlenet":
model = models.googlenet(pretrained=pretrained)
if torch.cuda.is_available():
return model.cuda()
else:
return model
4. テストの実施
プロジェクト全体の構造:
プロジェクト ディレクトリを入力し、コマンド ラインを使用して次のコマンドを実行します。
python main.py -c ./utils/conf.json
まず、データ セットがダウンロードされ、使用するデータ セットが含まれるデータ フォルダーがプロジェクト ディレクトリに表示されます。次に、トレーニング対象のクライアントがランダムに選択され、指定された数のトレーニングが完了すると停止します。トレーニングのラウンド。
5. フェデレーションラーニングと集中トレーニングの効果の比較
フェデレーション トレーニング: 合計 10 台のクライアント デバイスがあり、各ラウンドのトレーニングに参加するために 5 台が選択されます。各ローカル トレーニングの反復は 3 回、グローバルの反復数は 20 回です。
集中トレーニング: クライアント デバイスの数を 1 に変更し、各ラウンドでトレーニング用に 1 つのデバイスを選択します。集中トレーニングの効果は、構成を変更することで実現できます。
図の単一点トレーニングは、特定のクライアントの下でローカル データを使用したモデル トレーニングの結果にすぎません。
-
シングルポイント トレーニング (青いバー) のモデル効果はフェデレーション トレーニング (緑色のバーと赤色のバー) の効果よりも大幅に低いことがわかります。これは、単一のクライアントのデータだけではデータを適切に学習できないことも示しています。グローバル分布特性があり、モデルの汎化能力が低い。
-
また、各ラウンドのフェデレーション トレーニングに参加するクライアントの数 (k 値) によってもパフォーマンスが異なり、k 値が大きいほど、各ラウンドのトレーニングに参加するクライアントの数が多くなり、パフォーマンスが向上します。良いですが、各ラウンドを完了するには比較的長い時間がかかります。
6. まとめ
現在、フェデレーテッド ラーニングは広く使用されており、さまざまな大企業が独自のフェデレーテッド ラーニング フレームワーク (WeBank の FATE、Google の TensorFlow、OpenMind の PySyft、Baidu の PaddleFL、Byte の FedLearner など) を開発しています。非常に優れたフレームワークです。この記事はシミュレーションのみです。クライアントとサーバーをローカルでトレーニングします。複数のマシンでトレーニングするには追加の学習が必要です。