在创建好联邦任务后进行联邦训练时,需要用到init函数,其中的参数如下:
def init(task: str, algorithm, option = {}, model=None, Logger: flgo.experiment.logger.BasicLogger = flgo.experiment.logger.simple_logger.SimpleLogger, Simulator: BasicSimulator=flgo.system_simulator.DefaultSimulator, scene='horizontal'):
对于task参数和option参数,前面已经进行过一些简单的介绍,本节将介绍其中的algorithm参数和model参数
algorithm参数
在教程中,是直接将fedavg算法变为scaffold算法来演示如何在同一个联邦任务中运行不同的算法。
简单来说,这两个算法在框架中都已经实现,因此只需要改变algorithmn参数进行两次联邦训练即可
完整代码如下:
import flgo
import flgo.algorithm.fedavg as fedavg
import flgo.algorithm.fedprox as fedprox
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import flgo.experiment.analyzer as al
import matplotlib.pyplot as plt
#生成联邦任务 \ Generate Federated Task
# 生成Synthetic(0.5,0.5)-30个用户的数据集
task = './test_synthetic'
config = {'benchmark':{'name':'flgo.benchmark.synthetic_regression', 'para':{'alpha':0.5, 'beta':0.5, 'num_clients':30}}}
if not os.path.exists(task):
flgo.gen_task(config, task_path = task)
# 结果分析 \ Result Analysis
analysis_plan = {
'Selector':{
'task': task,
'header':['fedavg', 'fedprox']
},
'Painter':{
'Curve':[
{'args':{'x': 'communication_round', 'y':'val_loss'}, 'fig_option':{'title':'valid loss on Synthetic'}},
{'args':{'x': 'communication_round', 'y':'val_accuracy'}, 'fig_option':{'title':'valid accuracy on Synthetic'}},
]
}
}
if __name__ == '__main__':
# 运行联邦算法 \ Running Federated Algorithms
option = {'num_rounds': 30, 'num_epochs': 1, 'batch_size': 8, 'learning_rate': 0.1}
fedavg_runner = flgo.init(task, fedavg, option=option)
fedprox_runner = flgo.init(task, fedprox, option=option)
fedavg_runner.run()
fedprox_runner.run()
flgo.experiment.analyzer.show(analysis_plan)
按照教程中代码运行时会出现显示valid_loss和valid_accuracy关键字错误,只需将analysis_plan中的valid_loss和valid_accuracy改成val_loss\val_accuracy即可
model参数
在这一部分主要介绍在联邦任务中切换不同的模型。
直接在init函数中切换
在函数中通过model参数切换到在框架中已经实现的模型
cnn_runner = flgo.init(task, fedavg, option={'num_rounds':5, 'num_epochs':1, 'gpu':0}, model=cnn)
mlp_runner = flgo.init(task, fedavg, option={'num_rounds':5, 'num_epochs':1, 'gpu':0}, model=mlp)
cnn_runner.run()
mlp_runner.run()
切换到自定义模型
(这一块还不太懂)
在FLGo中,我们将模型视作benchmark所包含的一部分。这是因为对于不同的数据集来说,模型的架构往往不一致,且同一个数据集也可以使用不同的模型。因此,每个benchmark都必须具有benchmark_name.model子模块,且子模块中需要包含适用于该benchmark的模型(例如mnist_classification.model.cnn)。
以mnist_classification.model.cnn为例,讲解FLGo中模型是被定义和初始化的
完整代码
# -*- coding: utf-8 -*-
# @Author : guan
# @Time : 2024/2/28 20:48
from torch import nn
import torch.nn.functional as F
from flgo.utils.fmodule import FModule
import flgo
import flgo.algorithm.fedavg as fedavg
import flgo.algorithm.fedprox as fedprox
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import flgo.experiment.analyzer as al
class Model(FModule):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=5, padding=2)
self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2)
self.fc1 = nn.Linear(3136, 512)
self.fc2 = nn.Linear(512, 10)
def forward(self, x):
x = self.get_embedding(x)
x = self.fc2(x)
return x
def get_embedding(self, x):
x = x.view((x.shape[0],28,28))
x = x.unsqueeze(1)
x = F.max_pool2d(F.relu(self.conv1(x)), 2)
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, x.shape[1]*x.shape[2]*x.shape[3])
x = F.relu(self.fc1(x))
return x
def init_local_module(object):
pass
def init_global_module(object):
if 'Server' in object.__class__.__name__:
object.model = Model().to(object.device)
class MyCNN:
init_local_module = init_local_module
init_global_module = init_global_module
task = './test_mnist'
config = {'benchmark':{'name':'flgo.benchmark.mnist_classification'},'partitioner':{'name': 'IIDPartitioner','para':{'num_clients':100}}}
analysis_plan = {
'Selector': {'task': task,'header': ['fedavg'],'filter': {'M': ['MyCNN']},'legend_with': ['M']},
'Painter': {'Curve': [{'args': {'x': 'communication_round', 'y': 'val_loss'},'fig_option': {'title': 'valid loss on MNIST'}},
{'args': {'x': 'communication_round', 'y': 'val_accuracy'},'fig_option': {'title': 'valid accuracy on MNIST'}},]}
}
if not os.path.exists(task): flgo.gen_task(config, task_path = task)
if __name__ == '__main__':
mycnn_runner = flgo.init(task, fedavg, option={'num_rounds': 5, 'num_epochs': 1}, model=MyCNN)
mycnn_runner.run()
import flgo.experiment.analyzer
flgo.experiment.analyzer.show(analysis_plan)
运行结果
在这一小节个人的理解是自己定义在服务器端和客户端进行训练的模型,上述例子是通过重写model.init_local_module和model.init_global_module接口实现自己定义模型。其中:
- init_local_module是本地模型,客户端拥有
- init_global_module是全局模型,服务器端拥有
在这个例子中在这个例子中,init_local_module 函数是一个空函数,即没有为本地模型做特定的分配操作。可以根据具体需求在这个函数中添加逻辑,用于个性化地为每个实体分配本地模型。init_global_module 函数是用于为实体分配用于共享的全局模型。在这个例子中,只有类别为 Server 的实例才会设置一个全局模型,而其他实体不持有模型。这是因为在经典横向联邦学习中,全局模型通常由服务器维护,而客户端只负责本地模型的训练。因此,对于 Server 类型的实例,全局模型被初始化为 Model 类的实例。
MyCNN 类是一个包含初始化本地模块和初始化全局模块的示例类。它定义了 init_local_module 和 init_global_module 方法,分别调用了 init_local_module 和 init_global_module 函数。在这里,init_local_module 函数为空,而 init_global_module 函数为 Server 实例设置了一个全局模型。
几点疑问:
- 这里的算法和模型有什么区别?算法是进行联邦学习时的算法,包括数据如何广播,服务器端如何聚合;而模型是指服务器上和每个客户端上要训练的模型