WwZzz / easyFL

An experimental platform for federated learning.
Apache License 2.0
519 stars 88 forks source link

DiversityPartitioner中缺少lmbalance项 #41

Open Lyy838354973 opened 1 year ago

Lyy838354973 commented 1 year ago

class DiversityPartitioner(BasicPartitioner): """`Partition the indices of samples in the original dataset according to numbers of types of a particular attribute (e.g. label) . This way of partition is widely used by existing works in federated learning.

Args:
    num_clients (int, optional): the number of clients
    diversity (float, optional): the ratio of locally owned types of the attributes (i.e. the actual number=diversity * total_num_of_types)
    imbalance (float, optional): the degree of imbalance of the amounts of different local_movielens_recommendation data (0<=imbalance<=1)
    index_func (int, optional): the index of the distribution-dependent (i.e. label) attribute in each sample.
"""
def __init__(self, num_clients=100, diversity=1.0, index_func=lambda X:[xi[-1] for xi in X]):
    self.num_clients = num_clients
    self.diversity = diversity
    self.index_func = index_func

貌似imbalance项没有在调用里

WwZzz commented 1 year ago

class DiversityPartitioner(BasicPartitioner): """`Partition the indices of samples in the original dataset according to numbers of types of a particular attribute (e.g. label) . This way of partition is widely used by existing works in federated learning.

Args:
    num_clients (int, optional): the number of clients
    diversity (float, optional): the ratio of locally owned types of the attributes (i.e. the actual number=diversity * total_num_of_types)
    imbalance (float, optional): the degree of imbalance of the amounts of different local_movielens_recommendation data (0<=imbalance<=1)
    index_func (int, optional): the index of the distribution-dependent (i.e. label) attribute in each sample.
"""
def __init__(self, num_clients=100, diversity=1.0, index_func=lambda X:[xi[-1] for xi in X]):
    self.num_clients = num_clients
    self.diversity = diversity
    self.index_func = index_func

貌似imbalance项没有在调用里

你好,找到一组数据分布同时满足diversity比例、用户数据量分布、数据集属性分布是个比较复杂的问题,即假设用户持有数据数量构成一个矩阵,其中每个元素是某用户持有某类数据的数量,整理一下可以发现找这个分布相当于优化一个非线性整数规划问题,需要调用求解器进行求解。我将在后续的更新中找到一个合适的方式将imbalance插入进去。

Lyy838354973 commented 12 months ago

类 DiversityPartitioner(BasicPartitioner):“”“'根据特定属性(例如标签)的类型数量对原始数据集中的样本索引进行分区。这种划分方式被联邦学习中的现有工作广泛使用。

Args:
    num_clients (int, optional): the number of clients
    diversity (float, optional): the ratio of locally owned types of the attributes (i.e. the actual number=diversity * total_num_of_types)
    imbalance (float, optional): the degree of imbalance of the amounts of different local_movielens_recommendation data (0<=imbalance<=1)
    index_func (int, optional): the index of the distribution-dependent (i.e. label) attribute in each sample.
"""
def __init__(self, num_clients=100, diversity=1.0, index_func=lambda X:[xi[-1] for xi in X]):
    self.num_clients = num_clients
    self.diversity = diversity
    self.index_func = index_func

貌似不平衡项没有在调用里

你好,找到一组数据分布同时满足diversity比例、用户数据量分布、数据集属性分布是个比较复杂的问题,即假设用户持有数据数量构成一个矩阵,其中每个元素是某用户持有某类数据的数量,整理一下可以发现找这个分布相当于优化一个非线性整数规划问题,需要调用求解器进行求解。我将在后续的更新中找到一个合适的方式将不平衡插入进去。

还想请问一下,现在的库能实现波动客户端集吗,就是初始化定义一开始的客户端,然后在需要新的客户端加入时,再进行数据的分配,并将其加入训练过程,如果目前不能实现的话,我是否能够改动实现这一效果

WwZzz commented 12 months ago

定义一开始的客户端,然后在需要新的客户端加入时,再进行数据的分配,并将其加入训练过程,如果目前不能实现的话,我是否能

你好,1)目前数据集的分配不能够动态地进行,每个task生成后用户的数据分布就是固定的了,这种设置也比较贴合实际情形;2)另一方面我不明确你所描述的波动的客户端的具体含义;如果是指部分用户对于服务器在训练早期不可见,而后期训练过程中动态加入的话,可以在Simulator中通过设置用户的活跃性实现,比如分别设置每个用户活跃的起点轮数,即从某一轮开始后变成始终活跃;这样服务器通过available_clients属性可以访问每一轮活跃的用户,从而无法在早期接触到没加入的用户;如果指的是服务器主动增添新的客户端的话,我认为可以不对用户做修改,通过设置服务器的行为实现相同的效果,比如一开始只与self.clients中的前10个用户交互,后面逐步扩充交互用户的规模

Lyy838354973 commented 12 months ago

定义一开始的客户端,然后在需要新的客户端加入时,再进行数据的分配,并将其加入训练过程,如果目前不能实现的话,我是否能

你好,1)目前数据集的分配不能够动态地进行,每个task生成后用户的数据分布就是固定的了,这种设置也比较贴合实际情形;2)另一方面我不明确你所描述的波动的客户端的具体含义;如果是指部分用户对于服务器在训练早期不可见,而后期训练过程中动态加入的话,可以在Simulator中通过设置用户的活跃性实现,比如分别设置每个用户活跃的起点轮数,即从某一轮开始后变成始终活跃;这样服务器通过available_clients属性可以访问每一轮活跃的用户,从而无法在早期接触到没加入的用户;如果指的是服务器主动增添新的客户端的话,我认为可以不对用户做修改,通过设置服务器的行为实现相同的效果,比如一开始只与self.clients中的前10个用户交互,后面逐步扩充交互用户的规模

集合波动性就是可用的客户可能在不同的时间发生变化,并且可能有新的客户加入培训。我所设想的理想情况是能够在初始化是先设定一批客户端,在随后的训练过程需要有新的客户端集加入时,独立的进行数据分配并将其加入系统中,如果现在没有办法解决,我会考虑您所说的解决方案,您也可以考虑一下这方面的代码,我认为以后的联邦学习可能也会从波动性入手

WwZzz commented 12 months ago

定义一开始的客户端,然后在需要新的客户端加入时,再进行数据的分配,并将其加入训练过程,如果目前不能实现的话,我是否能

你好,1)目前数据集的分配不能够动态地进行,每个task生成后用户的数据分布就是固定的了,这种设置也比较贴合实际情形;2)另一方面我不明确你所描述的波动的客户端的具体含义;如果是指部分用户对于服务器在训练早期不可见,而后期训练过程中动态加入的话,可以在Simulator中通过设置用户的活跃性实现,比如分别设置每个用户活跃的起点轮数,即从某一轮开始后变成始终活跃;这样服务器通过available_clients属性可以访问每一轮活跃的用户,从而无法在早期接触到没加入的用户;如果指的是服务器主动增添新的客户端的话,我认为可以不对用户做修改,通过设置服务器的行为实现相同的效果,比如一开始只与self.clients中的前10个用户交互,后面逐步扩充交互用户的规模

集合波动性就是可用的客户可能在不同的时间发生变化,并且可能有新的客户加入培训。我所设想的理想情况是能够在初始化是先设定一批客户端,在随后的训练过程需要有新的客户端集加入时,独立的进行数据分配并将其加入系统中,如果现在没有办法解决,我会考虑您所说的解决方案,您也可以考虑一下这方面的代码,我认为以后的联邦学习可能也会从波动性入手

你好,可用的用户可能在不同的时间发生变化这个概念在FL里现有工作一般称作Intermittent Client Availability,具体论文包括MIFA、F3AST等。有新的用户加入训练有一篇忘了名字的ICML论文称作Flexiable Participation。如果是实现Client Availability的话,我想这个tutorials可以帮到你https://flgo-xmu.github.io/Tutorials/4_Simulator_Customization/4.1_Client_Availability/ ,此外我觉得从Client Availability角度去模拟Flexiable Participation也是可行的。

Lyy838354973 commented 12 months ago

1695124692243

请问我设置了self.roundwise_fixed_availability = True,为什么每轮可用设备还随着被选择客户端数量减少

WwZzz commented 12 months ago

轮可用设备还随着被选择客户端数量减少

你好,这个变量的含义是:在同一个聚合轮次内,用户的availability是否会随着时间变化。我贴了一个例子来解释这件事:这里simulator1是roundwise_fixed_availability=True, simulator2是False(后面简称该变量为rfa)。然后我稍微修改了下fedavg算法,让服务器在每个iterate里通过self.gv.clock.step(1)让时间强制流逝1个时间单位。此时若rfa为True,则用户的availability在下次模型聚合之前,不会随着时间变化而变化;若rfa为False,则用户的availability会随着时间变化而变化,与聚合轮次无关。

import flgo
import flgo.algorithm.fedavg as fedavg
import flgo.simulator.base as fsb
import random

class MySimulator1(fsb.BasicSimulator):
    def update_client_availability(self):
        if self.gv.clock.current_time==0:
            self.set_variable(self.all_clients, 'prob_available', [1 for _ in self.clients])
            self.set_variable(self.all_clients, 'prob_unavailable', [int(random.random() >= 0.5) for _ in self.clients])
            return
        pa = [0.1 for _ in self.clients]
        pua = [0.1 for _ in self.clients]
        self.set_variable(self.all_clients, 'prob_available', pa)
        self.set_variable(self.all_clients, 'prob_unavailable', pua)
        self.roundwise_fixed_availability = True

class MySimulator2(fsb.BasicSimulator):
    def update_client_availability(self):
        if self.gv.clock.current_time==0:
            self.set_variable(self.all_clients, 'prob_available', [1 for _ in self.clients])
            self.set_variable(self.all_clients, 'prob_unavailable', [int(random.random() >= 0.5) for _ in self.clients])
            return
        pa = [0.1 for _ in self.clients]
        pua = [0.1 for _ in self.clients]
        self.set_variable(self.all_clients, 'prob_available', pa)
        self.set_variable(self.all_clients, 'prob_unavailable', pua)
        self.roundwise_fixed_availability = False

class Server(fedavg.Server):
    def iterate(self):
        print("The number of currently available clients: {}".format(len(self.available_clients)))
        print("The availability of clients being selected at last round: {}".format([(cid in self.available_clients) for cid in self.selected_clients]))
        self.gv.clock.step(1) # 等待1个时间单位
        print('After a time unit...')
        print("The number of currently available clients: {}".format(len(self.available_clients)))
        print("The availability of clients being selected at last round after a second: {}".format([(cid in self.available_clients) for cid in self.selected_clients]))
        self.selected_clients = self.sample()
        models = self.communicate(self.selected_clients)['model']
        self.model = self.aggregate(models)
        return True

class MyFedavg:
    Server = Server
    Client = fedavg.Client

if __name__=='__main__':
    task = 'my_task'
    flgo.init(task, MyFedavg, option={'gpu':0, 'num_steps':1, 'sample':'uniform_available', 'num_rounds':5}, Simulator=MySimulator1).run()
    flgo.init(task, MyFedavg, option={'gpu': 0, 'num_steps': 1, 'sample': 'uniform_available', 'num_rounds': 5}, Simulator=MySimulator2).run()

运行这段代码后,可以看到屏幕上的输出为:

# Simulator1-rfa=True的情形
2023-09-19 11:00:46,006 fedbase.py run [line:253] INFO Eval Time Cost:               1.4580s
The number of currently available clients: 34
The availability of clients being selected at last round: [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False]
After a time unit...
The number of currently available clients: 34
The availability of clients being selected at last round after a second: [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False

可以看到在一个时间单位后用户的活跃分布不发生改变。

# Simulator2-rfa=False的情形
The number of currently available clients: 33
The availability of clients being selected at last round: [False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False]
After a time unit...
The number of currently available clients: 35
The availability of clients being selected at last round after a second: [False, False, False, False, False, False, False, True, False, False, False, True, False, False, True, True, False, False, False, False]

可以看到用户的活跃分布在1个时间单位后刷新了。 至于为什么选择用户后活跃用户会变少,我不明确你具体运行的代码;但是被选择的用户的状态将从被选择那一刻开始由idle转为working,working用户在训练完之前不被认作是可以被再次采样的用户(即非“活跃”用户)。

Lyy838354973 commented 11 months ago

请问,在cifar10数据集中,我使用DiversityPartitioner将客户端只塞入了一种标签的数据,设置了100个客户端,每轮选择10个客户端,测试精度出现多个0.1000是什么原因 下面是我的设置: option = {'proportion':0.1,'num_rounds':50, 'gpu':0,'learning_rate':0.01, 'batch_size':40, 'num_epochs':3}

1697511388808

WwZzz commented 11 months ago

请问,在cifar10数据集中,我使用DiversityPartitioner将客户端只塞入了一种标签的数据,设置了100个客户端,每轮选择10个客户端,测试精度出现多个0.1000是什么原因 下面是我的设置: option = {'proportion':0.1,'num_rounds':50, 'gpu':0,'learning_rate':0.01, 'batch_size':40, 'num_epochs':3}

1697511388808

你好,这种情况是因为noniid程度太极端,同时local epoch过大,导致模型收敛极慢甚至无法收敛;此时需要调小local epoch(或直接设置num_steps替代num_epochs)或是调小learning_rate,才能观察到数十个round内损失稳定下降;或是用使用一些针对niid问题进行了优化的算法替代fedavg;