【联邦学习】联邦学习量化——non-iid数据集下的仿真

改进项目背景

在前面的项目中,虽然对联邦学习中,各个ue训练出来的模型上传的参数进行了量化,并仿真的相关结果。但是仍有一些俺不是非常符合场景的情况,需要改进的方向如下:

  1. 量化函数需要重写,将前面的只对小数点后进行0~1的量化改成自适应的在一段范围之内的数组量化。
  2. 信道仿真函数,在真实的通信环境中,一个信道的速率模拟可以由一个基于正态分布的初始速率,每隔一定时间加减均匀分布的变化值。
  3. 根据量化的程度不同,模型分为基础值和增量值,先传数据量较少的基础值,如果通信条件好的画
  4. 需要模仿接收方BS的接收规则:首先应该计算传输过程中的耗时,如果耗时超过了一个等待的门限,那么这个模型就不会被纳入聚合的model们里。如果接收了基础值之后,BS还会等待一段时间,如果增量值没到达,那只能用基础值去参与聚合了。这样对于BS来说就陷入一个博弈:是采用更精确的量化模型去提高自己模型的准确度呢,还是采用更少的量化程度的模型来保证在信道上能够正确传输。而我正是要仿真这样一个场景。
  5. 联邦学习框架的使用:PySyft在自己一个电脑上的仿真完全没用!完全可以抛弃框架自己写代码去模拟模型聚合与信道上的传输过程。
  6. 急需一个能够仿真出non-iid数据集的库,方便后续的仿真代码编写。

量化函数的改进

对原始数组进行线性变化,映射在一定范围内:
V q = Q × ( V x − min ⁡ ( V x ) ) V_q=Q\times(V_x-\min(V_x)) Vq=Q×(Vxmin(Vx))
V x ′ = V q / Q + min ⁡ ( V x ) V_x'=V_q/Q+\min(V_x) Vx=Vq/Q+min(Vx)
Q = S / R , R = max ⁡ ( V x ) + m i n ( V x ) , S = 1 < < b i t s − 1 Q=S/R,R=\max(V_x)+min(V_x),S=1<<bits-1 Q=S/R,R=max(Vx)+min(Vx),S=1<<bits1
其中 V x V_x Vx表示原浮点数, V q V_q Vq表示量化后的定点数值, V x ′ V_x' Vx表示根据量化参数还原出的浮点数,bits为量化比特位数。
传输的时候只需要传输低比特矩阵 V q V_q Vq和参数 Q , S , R Q,S,R Q,S,R等,在接收端即可还原成浮点数。

总而言之,举例:一个正弦函数值数组,经过4bit量化后呈现如下效果:
在这里插入图片描述
而神经网络中同一个层的tensor,数值分布恰好在同一个数量范围内,适合这样的数组量化:我们选择这个tensor中的最大值和最小值,以此为范围进行量化。

#4bit量化前:
tensor([ 0.0201,  0.0059,  0.0153, -0.0319, -0.0419,  0.0025, -0.0467, -0.0022,
         0.0106,  0.0512, -0.0321, -0.0190, -0.0409,  0.0128,  0.0191,  0.0479,
        -0.0289, -0.0515, -0.0237, -0.0473, -0.0420, -0.0156, -0.0371,  0.0184,
         0.0014,  0.0103, -0.0436, -0.0375,  0.0042, -0.0070,  0.0027,  0.0168])
#4bit量化后:
tensor([ 0.0215,  0.0072,  0.0143, -0.0287, -0.0430,  0.0000, -0.0502,  0.0000,
         0.0072,  0.0502, -0.0287, -0.0215, -0.0430,  0.0143,  0.0215,  0.0502,
        -0.0287, -0.0502, -0.0215, -0.0502, -0.0430, -0.0143, -0.0359,  0.0215,
         0.0000,  0.0072, -0.0430, -0.0359,  0.0072, -0.0072,  0.0000,  0.0143])

具体的函数如下:

def Quant(Vx, Q, RQM):
    return round(Q * Vx) - RQM


def QuantRevert(VxQuant, Q, RQM):
    return (VxQuant + RQM) / Q


def ListQuant(data_list, quant_bits):
    # 数组范围估计
    data_min = min(data_list)
    data_max = max(data_list)

    # 量化参数估计
    Q = ((1 << quant_bits) - 1) * 1.0 / (data_max - data_min)
    RQM = (int)(np.round(Q*data_min))

    # 产生量化后的数组
    quant_data_list = []
    for x in data_list:
        quant_data = Quant(x, Q, RQM)
        quant_data_list.append(quant_data)
    quant_data_list = np.array(quant_data_list)
    return (Q, RQM, quant_data_list)


def ListQuantRevert(quant_data_list, Q, RQM):
    quant_revert_data_list = []
    for quant_data in quant_data_list:
        # 量化数据还原为原始浮点数据
        revert_quant_data = QuantRevert(quant_data, Q, RQM)
        quant_revert_data_list.append(revert_quant_data)
    quant_revert_data_list = np.array(quant_revert_data_list)
    return quant_revert_data_list

non-iid数据集的设置

信道变化->BS接收到ue的模型数量变化->聚合时用于平均的model数量变化

⬆️只有数据集是non-iid的时候,model数量变化才能明显表现出对性能的影响。
如果各个ue在相同数据集上训练相同batch,再进行聚合平均,聚合的model数量对性能影响不大。
我寻找到了一个由本校学长参与开发的一个联邦学习函数库Fedlab,除了数据集的处理,库还提供了别的很多在联邦学习中非常有用的函数,如BS和客户机的交流通信函数等,在这里把Github上的repo贴一下:
https://github.com/SMILELab-FL

点开才发现,这个repo居然是同校计算机学院的一位博士学长创建和维护的,后来还在飞书上联系到了他。各位如果有兴趣的话,非常建议在repo的issue上提出问题,他们都会即使解答的。
另外,如果不想下载Fedlab这个库,或者对Dirichlet划分是数学原理感兴趣的,可以参考下面这个:
https://zhuanlan.zhihu.com/p/468992765
按Dirichlet分布划分Non-IID数据集
在这里插入图片描述
由于Dataloader在每次加载时数据的索引不变,因此在多轮测试的时候,每个ue上的数据分布不会变(区别于完全随机)。

Fedlab划分数据集的踩雷

一开始,我是直接按照这个文档来的:https://zhuanlan.zhihu.com/p/411308268,刚好我也需要采用CIFAR10数据集,但是在其中有这样一句导入包:from fedlab.utils.dataset.sampler import SubsetSampler,是错误的,检查源码也发现dataset里面根本就没有sampler,这让我十分抓狂。后面询问之后才知道,原来sampler的效率太低了,他们已经在新版本放弃不用了,新的划分方案直接看github中的tutorial文件夹部分。于是我找到了如下:
partitioned_cifar10的用法

class PartitionedCIFAR10(root, path, dataname, num_clients, download=True, preprocess=False, balance=True, partition=‘iid’, unbalance_sgm=0, num_shards=None, dir_alpha=None, verbose=True, seed=None, transform=None, target_transform=None)

我们就需要先实例化这一个类,然后利用这个类提供的几个函数来实现数据集的划分与加载。这个类有如此多的参数,那么具体每个参数什么含义,我们在使用的时候又该如何设置呢?

  • root (str) – Path to download raw dataset. 和pytorch的datasets一样,填'/cifar10'

  • path (str) – Path to save partitioned subdataset.预训练好的.pkl文件名,我填'/cifar10_hetero_dir.pkl'

  • dataname (str) – “cifar10” or “cifar100”填‘/cifar10’

  • num_clients (int) – Number of clients.要分成几份,对应ue的个数

  • download (bool) – Whether to download the raw dataset.同pytorch里的datasets

  • preprocess (bool) – Whether to preprocess the dataset.是否预划分,这个第一次必须填true,后面就可以填false了

  • balance (bool, optional) – Balanced partition over all clients or not. Default as True.false

  • partition (str, optional) – Partition type, only “iid”, shards, “dirichlet” are supported. Default as “iid”.填'dirichlet'

  • unbalance_sgm (float, optional) – Log-normal distribution variance for unbalanced data partition over clients. Default as 0 for balanced partition.可选项,没填

  • num_shards (int, optional) – Number of shards in non-iid “shards” partition. Only works if partition=“shards”. Default as None.可选项,没填

  • dir_alpha (float, optional) – Dirichlet distribution parameter for non-iid partition. Only works if partition=“dirichlet”. Default as None.0.3

  • verbose (bool, optional) – Whether to print partition process. Default as True.可选项,没填

  • seed (int, optional) – Random seed. Default as None.2022

  • transform (callable, optional) – A function/transform that takes in an PIL image and returns a transformed version.同pytorch里的datasets,对图像进行预处理,然后转化为tensor

  • target_transform (callable, optional) – A function/transform that takes in the target and transforms it.可选项,没填

其中一个非常容易错的点。PartitionedCIFAR10这个类中的preprocess实际上就是按照所选的划分模式,对整个数据集贴标签形成一个字典,标注每条数据属于哪个ue,保存在一个.pkl文件中。后面在训练加载数据的时候,就按照这个字典从数据集中取数据,就完成了non-iid的划分啦。
同时transform里面和pytorch的写法一样的,可以对图片进行大小的更改,进行normalize等操作,当然一定别忘记了必须Totensor()将图片转化为tensor,然而我发现忘记totensor,改了之后,发现还是报错!仔细以看才知道,原来是改了transform,但是忘记了重新preprocess一下,导致还是按照旧的方式去加载,自然错啦。

后面的使用,PartitionedCIFAR10提供了两个比较有用的函数;
在这里插入图片描述

他们的返回值就是pytorch中的datasetdataloader了。用法也和pytorch中的一样:

hetero = PartitionedCIFAR10(
    root='/cifar10',
    path='/cifar10_hetero_dir.pkl',
    dataname="cifar10",
    num_clients=train_args['num_clients'],
    download=False,
    preprocess=False,
    balance=False,
    partition="dirichlet",
    seed=2022,
    dir_alpha=0.3,
    transform=transforms.Compose([
        transforms.Resize((32, 32)),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.4750, 0.4750, 0.4750], std=[0.2008, 0.2008, 0.2008])]
    ),
    target_transform=transforms.ToTensor()
)

for id, ue in enumerate(UE_list):
    train_loader = hetero.get_dataloader(
        id, batch_size=train_args['batch_size'])
    for batch_idx, (data, target) in enumerate(train_loader):
        # if batch_idx > 100:
        #     break
        ue_data = data.to(device)
        ue_target = target.to(device)
        loss = ue.train(ue_data, ue_target)

后面的就是按照正常方式去训练啦,最后也是成功得到了一个不同bit压缩的联邦学习训练效果对比图:
在这里插入图片描述

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
<项目介绍> 簇联邦学习改进python实现源码+项目说明+代码注释(提高精度+缓解用户孤立问题).zip 该资源内项目源码是个人的毕设,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到94.5分,放心下载使用! 该资源适合计算机相关专业(如人工智能、通信工程、自动化、软件工程等)的在校学生、老师或者企业员工下载,适合小白学习或者实际项目借鉴参考! 当然也可作为毕业设计、课程设计、课程作业、项目初期立项演示等。如果基础还行,可以在此代码基础之上做改动以实现更多功能。 本人硕士一年级在读,专注于边缘计算方向,目前主要关注联邦学习的内容。在解决数据的non-IID过程中,有一个想法,并且用代码做了一个小实验。 想法:现实世界中,non-IID非常普遍,但是也不是完全非独立同分布的,因为物以类聚人以群分。那么我们能不能用聚类的方法进行学习?将每一个设备上训练的参数上传后,首先进行聚类,将相似的设备分为一类。划分一个个簇以后,每一个簇内进行联邦学习。 当然,利用聚类的方法解决non-IID问题的论文也有,如:An Efficient Framework for Clustered Federated Learning.(NIPS2020);ON THE BYZANTINE ROBUSTNESS OF CLUSTERED FEDERATED LEARNING。但是存在问题:1.高昂的计算开销和通信开销,每次传输所有的簇模型、通过计算在本地数据的损失确定簇,2.簇个数已知,但是现实世界往往很难确定簇个数,3.簇内相对孤立,无法考虑其他簇模型对自己的影响等。为了解决上述问题,我们提出动态簇连邦学习,通过动态确定簇个数,同时学习其他簇知识,提高在non-IID上得性能。 2.文件结构 cache : 存放产生的模型文件 clients_and_server init.py clients.py :产生用户需要用到 cluster.py : 聚类算法 server.py : 产生云服务 data : 存放下载相应的数据集 data_and_model init.py datasets.py : 产生相应的数据集 models.py : 产生模型 result : 存放实验结果 main:主程序 plot: 画图 3.详细描述文件 3.1 cache文件夹 这个文件夹内存放模型训练的结果,包括用户的模型和簇模型 3.2 clients_and_server文件夹 这个文件夹包含三个文件,clients、cluster和server文件。 3.2.1 clients文件 这个文件用来定义一个用户,一个用户的信息:自身标号、簇编号、模型、训练测试数据、学习率、优化器、训练次数等。包含6个函数:get_cluster_modal、get_model、local_train、pre_train、test_model和updata_clu get_cluster_modal:用来获得簇模型。在整体每一轮循环中,簇内部训练L轮会簇内部聚合,簇内部聚合的模型送到簇内的每一个用户。 get_model:获得全局模型。全局模型训练完成后,将所有的簇模型再平均一下,得到全局模型(这样,不会因为某一类的用户数量过少,导致聚合的时候“话语权”较小),将全局模型送到每一个用户手里,然后开始下一轮训练。 local_train:模型在本地训练 pre_train:模型聚类之前,首先需要预训练,获得一个描述本身数据的模型 test_model:测试模型,返回accuracy updata_clu:模型聚类之后,更新所在簇的编号。 3.2.2 cluster文件 本文件是对用户进行聚类,一共使用三种聚类方式:k均值、层次聚类和密度聚类。另外2个函数load_clients和distance函数,第一个函数是加载所有用户模型,将字典类型变成list类型,第二个函数是计算两个模型之间的距离。 K_means_cluster,Hierarchical_clustering分别是k均值和层次聚类。Density_clustering表示密度聚类,并没有给出代码。在实验的过程中,我们发现k均值是完全可以满足我们的要求的,而层次聚类不能满足。 3.2.3 server文件 本文件包含一个云服务类,用于定义云服务端,以及簇模型。 aggregate_model:用于将所有簇模型聚合 gain_acc:测试每个簇的精度 aggregate_cluster:簇内部模型聚合 3.3 data文件夹 本文件夹用于存放数据集,本实验可以采用MNIST、CIFAR10、EMNIST和FMNIST数据集

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值