|
联邦学习算法介绍-FedAvg详细案例-Python代码获取一、联邦学习系统框架二、联邦平均算法(FedAvg)三、联邦随梯度下降算法(FedSGD)四、差分隐私随联邦梯度下降算法(DP-FedSGD)五、差分隐私联邦平均算法(DP-FedAVG)六、FedAVG案例附代码1)案例背景2)参数设置3)结果展示4)代码详解七、完整项目代码获取方式一、联邦学习系统框架图1中心化联邦学习框架 由服务端收集各客户端的梯度信息,通过聚合计算后再分发给各客户端,从而实现多个客户端联合训练模型,且“原始数据不出岛”,从而保护了客户端数据隐私。图2去中心化联邦学习框架 假设中心方是好奇的,那么客户端通过某种规则向其他客户端广播梯度信息,收到梯度信息的客户端聚合参数并训练,将新的梯度信息广播。二、联邦平均算法(FedAvg)图3联邦平均算法框架输入:全局模型参数初始值ω0\omega^0ω0,参与方个数nnn,批样本大小BBB,训练轮数EEE,参与方比例CCC,局部模型学习率η\etaη,各参与方的样本个数mkm_kmk。输出:最后一次迭代的全局模型参数ωt+1\omega_{t+1}ωt+11.中央服务器初始化全局模型参数ω0\omega^0ω0,并传输给所有参与方。2.对t=0,1,2,⋯t=0,1,2,\cdotst=0,1,2,⋯,迭代以下步骤直到全局模型参数ωt+1\omega_{t+1}ωt+1收敛。 (1)中央服务器根据参与方比例C∈(0,1]C\in(0,1]C∈(0,1],计算参与第ttt轮迭代的参与方个数:M←max(C×n,1)M\leftarrow\max(C\timesn,1)M←max(C×n,1) (2)中央服务器随机选取MMM个参与方,构成参与方集合StS_tSt (3)对∀k∈St\forallk\inS_t∀k∈St,通过以下步骤更新局部模型参数: [1]使用接收到的模型参数ωt\omega_tωt进行模型初始化ωt+1k⟵ωt\omega_{t+1}^k\longleftarrow\omega_tωt+1k⟵ωt。 [2]将数据索引集PkP_kPk按照批样本大小BBB分为若干个批次,记由这些批次构成的集合为BkB_kBk。对每次训练j=1,⋯ ,Ej=1,\cdots,Ej=1,⋯,E,使用∀b∈Bk\forallb\inB_k∀b∈Bk,更新局部模型参数:ωt+1k⟵ωt+1k−η∇Fk(ω;b)\omega_{t+1}^k\longleftarrow\omega_{t+1}^k-\eta\nablaF_k(\omega;b)ωt+1k⟵ωt+1k−η∇Fk(ω;b)将更新好的局部模型参数ωl+1k\omega_{l+1}^kωl+1k传输给中央服务器。 (4)中央服务器聚合所有参数,并传输回所有参与方ωt+1=∑k=1Mmkmωt+1k\omega_{t+1}=\sum_{k=1}^M\frac{m_k}{m}\omega_{t+1}^kωt+1=k=1∑Mmmkωt+1k mkm\frac{m_k}{m}mmk为t+1轮聚合中客户端k占参与训练的M个客户端总样本的比例。三、联邦随梯度下降算法(FedSGD)当训练轮数E=1E=1E=1,且批样本大小BBB是对应的参与方的总样本个数时,FedAvg算法退化为FedSGD算法。使用各自的所有样本,对参数ωl+1k\omega_{l+1}^kωl+1k进行一次梯度下降,计算参数ωt+1k\omega_{t+1}^kωt+1k的梯度:gk=∇Fk(ωt+1k)g_k=\nablaF_k\left(\omega_{t+1}^k\right)gk=∇Fk(ωt+1k)或者计算参数更新值ωt+1k⟵ωt+1k−ηgk\omega_{t+1}^k\longleftarrow\omega_{t+1}^k-\etag_kωt+1k⟵ωt+1k−ηgk其中η\etaη是学习率。四、差分隐私随联邦梯度下降算法(DP-FedSGD)图4DP-FedSGD算法框架1.参数更新量的裁剪方式ClipFn裁剪方式分2种,即水平裁剪和分层裁剪。(1)水平裁剪。记参数更新量矢量为Δ\DeltaΔ,将其2-范数的上界设为S∈RS\inRS∈R,即:π(Δ,S)= def Δ⋅min(1,S∥Δ∥)\pi(\boldsymbol{\Delta},S)\stackrel{\text{def}}{=}\boldsymbol{\Delta}\cdot\min\left(1,\frac{S}{\|\Delta\|}\right)\quadπ(Δ,S)= def Δ⋅min(1,∥Δ∥S)(2)分层裁剪。面向神经网络模型,假设网络总共有ccc层,每一层的参数更新量矢量分别为Δ(1),⋯ ,Δ(c)\Delta(1),\cdots,\Delta(c)Δ(1),⋯,Δ(c),对应的2-范数上界分别为S1,⋯S_1,\cdotsS1,⋯,ScS_cSc,通过水平裁剪的方法,分别对每一层的矢量进行裁剪:Δ′(j)=π(Δ(j),Sj)\boldsymbol{\Delta}^{\prime}(j)=\pi\left(\boldsymbol{\Delta}(j),S_j\right)Δ′(j)=π(Δ(j),Sj)总体的参数更新量裁剪上界定义为S=∑j=1cSj2S=\sqrt{\sum_{j=1}^cS_j^2}S=j=1∑cSj22.中央服务器通过以下步骤对局部模型参数更新量进行聚合1)计算聚合结果关于加权聚合方式f(C)=∑k∈CdkΔk∑k∈Cdkf(C)=\frac{\sum_{k\inC}d_k\Delta^k}{\sum_{k\inC}d_k}f(C)=∑k∈Cdk∑k∈CdkΔk(其中Δk\Delta^kΔk是参与方kkk的参数更新量,CCC为一轮中参与迭代的参与方集合)的有界灵敏度(Bounded-sensitivity)的估计量,分为以下2种: [1]f~f(C)=∑k∈CdkΔkqD\tilde{f}_{\mathrm{f}}(C)=\frac{\sum_{k\inC}d_k\Delta^k}{qD}f~f(C)=qD∑k∈CdkΔk其中dk=min(mkm^,1)d_k=\min\left(\frac{m_k}{\hat{m}},1\right)dk=min(m^mk,1),是每个参与方的权重;D=∑k=1ndkD=\sum_{k=1}^nd_kD=∑k=1ndk,qqq为每轮通信中参与方的选择概率。 [2]f~c(C)=∑k∈CdkΔkmax(qDmin ,∑k∈Cdk)\tilde{f}_{\mathrm{c}}(C)=\frac{\sum_{k\inC}d_k\Delta^k}{\max\left(qD_{\text{min}},\sum_{k\inC}d_k\right)}f~c(C)=max(qDmin ,∑k∈Cdk)∑k∈CdkΔk其中Dmin D_{\text{min}}Dmin 是预先设置的关于权重和的超参数。(2)令S←\leftarrow←裁剪方式ClipFn中的裁剪上界,根据选定的有界灵敏度估计量和噪声规模z,设置高斯噪声的方差:σ←{zSqD for f~f or 2zSqDmin for f~c}\sigma\leftarrow\left\{\frac{zS}{qD}\text{for}\tilde{f}_{\mathrm{f}}\text{or}\frac{2zS}{qD_{\min}}\text{for}\tilde{f}_{\mathrm{c}}\right\}σ←{qDzS for f~f or qDmin2zS for f~c}(3)聚合全局模型的参数为ωt+1←ωt+Δt+1+N(0,Iσ2)\omega_{t+1}\leftarrow\omega_t+\Delta_{t+1}+N\left(0,I\sigma^2\right)ωt+1←ωt+Δt+1+N(0,Iσ2)其中,N(0,Iσ2)N\left(0,I\sigma^2\right)N(0,Iσ2)是均值为0、方差为σ2\sigma^2σ2的高斯分布;III是单位方阵,行数和列数都是参数的个数。(4)根据zzz和M\mathcal{M}M计算隐私损失值并输出。五、差分隐私联邦平均算法(DP-FedAVG) 在DP-FedSGD中,被选中的参与方使用全局模型参数对局部模型进行初始化,通过批梯度下降法进行多轮梯度下降,计算梯度更新量。而在DP-FedAVG中,是利用一个批次的数据进行一次梯度下降,计算梯度更新量。六、FedAVG案例附代码1)案例背景 收集2012年某10个城市每天每小时的电力数据。用前24时刻的电力负荷值以及该时刻的4个相关气象数据,来预测该时刻的电力负荷值。 构造四层的深度网络:z1=Iw1,h1=σ(z1)z2=h1w2,h2=σ(z2)z3=h2w3,h3=σ(z3)z4=h3w4,O=σ(z4) loss =12(O−y)2z1=Iw1,h1=σ(z1)z2=h1w2,h2=σ(z2)z3=h2w3,h3=σ(z3)z4=h3w4,O=σ(z4) loss =12(O−y)2z1=Iw1,h1=σ(z1)z2=h1w2,h2=σ(z2)z3=h2w3,h3=σ(z3)z4=h3w4,O=σ(z4) loss =12(O−y)2z1=Iw1,h1=σ(z1)z2=h1w2,h2=σ(z2)z3=h2w3,h3=σ(z3)z4=h3w4,O=σ(z4) loss =21(O−y)2 σ\sigmaσ为sigmoid激活函数。2)参数设置表1FedAVG参数参数值聚合轮数5本地训练次数20客户端总数10学习率0.08本地批量样本大小50优化器adam3)结果展示 设置每轮随机抽取参与训练的客户端数量为2、5、8、10。图5FedAVGmae图6FedAVGrmse向梯度中添加随机噪声图7FedAVG+noisemae图8FedAVG+noisermse4)代码详解数据结构,在本地文件夹中,有10个csv文件,这10个文件各自代表一个客户端。在每个csv文件中,均有7个指标,6577条样本,其中第一列表示服务端id。第一步,加载数据。首先需要划分每个客户端的训练集和测试集,本文设置了每个客户端数据结构与样本数量一致(也可以不一致,通过样本对齐方法即可)。#-*-coding:utf-8-*-"""@File:bp_nn.py"""importcopyimportsysimportnumpyasnpimportpandasaspdfromtorchimportnnfromtqdmimporttqdmsys.path.append('../')fromsklearn.metricsimportmean_absolute_error,mean_squared_errorfromitertoolsimportchainfrommodelsimportBP##自定义importosos.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"#避免jupyter崩溃clients_wind=['Task1_W_Zone'+str(i)foriinrange(1,11)]fromargsimportargs_parser##自定义参数defload_data(file_name):#读取某一个文件---横向联邦学习df=pd.read_csv(os.path.dirname(os.getcwd())+'/data/Wind_new/Task1/Task1_W_Zone1_10/'+file_name+'.csv',encoding='gbk')columns=df.columnsdf.fillna(df.mean(),inplace=True)foriinrange(3,7):#3,4,5,6MAX=np.max(df[columns[i]])MIN=np.min(df[columns[i]])df[columns[i]]=(df[columns[i]]-MIN)/(MAX-MIN)#将3,4,5,6列的值,标准化returndf#0-6列,后4列已经标准化defnn_seq_wind(file_name,B):#B实则为本地批量大小print('dataprocessing...')dataset=load_data(file_name)#splittrain=dataset[:int(len(dataset)*0.6)]#前60%为训练集val=dataset[int(len(dataset)*0.6):int(len(dataset)*0.8)]#中间20%为验证集test=dataset[int(len(dataset)*0.8):len(dataset)]#最后20%为测试集defprocess(data):#将特征与标签分开columns=data.columnswind=data[columns[2]]wind=wind.tolist()#转换成列表https://vimsky.com/examples/usage/python-pandas-series-tolist.htmldata=data.values.tolist()X,Y=[],[]foriinrange(len(data)-30):train_seq=[]train_label=[]forjinrange(i,i+24):#24小时train_seq.append(wind[j])forcinrange(3,7):train_seq.append(data[i+24][c])train_label.append(wind[i+24])X.append(train_seq)Y.append(train_label)X,Y=np.array(X),np.array(Y)length=int(len(X)/B)*BX,Y=X[:length],Y[:length]returnX,Ytrain_x,train_y=process(train)val_x,val_y=process(val)test_x,test_y=process(test)return[train_x,train_y],[val_x,val_y],[test_x,test_y]defget_val_loss(args,model,val_x,val_y):#验证集,计算损失,model即为nnbatch_size=args.Bbatch=int(len(val_x)/batch_size)#计算循环次数val_loss=[]foriinrange(batch):start=i*batch_sizeend=start+batch_sizemodel.forward_prop(val_x[start:end],val_y[start:end])model.backward_prop(val_y[start:end])val_loss.append(np.mean(model.loss))returnnp.mean(val_loss)deftrain(args,nn):print('training...')tr,val,te=nn_seq_wind(nn.file_name,args.B)train_x,train_y=tr[0],tr[1]val_x,val_y=val[0],val[1]nn.len=len(train_x)#nn.len训练集的长度batch_size=args.B#每批次大小epochs=args.E#迭代次数batch=int(len(train_x)/batch_size)#每一迭代,需要训练多少次#trainingmin_epochs=10best_model=Nonemin_val_loss=5forepochintqdm(range(epochs)):train_loss=[]foriinrange(batch):start=i*batch_sizeend=start+batch_sizenn.forward_prop(train_x[start:end],train_y[start:end])nn.backward_prop(train_y[start:end])train_loss.append(np.mean(nn.loss))#validationval_loss=get_val_loss(args,nn,val_x,val_y)ifepoch+1>=min_epochsandval_loss
|
|