【阅读笔记】联邦学习实战——用FATE从零实现横向逻辑回归

前言

FATE是微众银行开发的联邦学习平台是全球首个工业级的联邦学习开源框架在github上拥有近4000stars可谓是相当有名气的该平台为联邦学习提供了完整的生态和社区支持为联邦学习初学者提供了很好的环境否则利用python从零开发那将会是一件非常痛苦的事情。本篇博客内容涉及《联邦学习实战》第四章和第五章内容使用的fate版本为1.6.0下面就让我们开始吧。


1. FATE平台

1.1 FATE平台架构概述

在这里插入图片描述
FATE的官方文档提供了FATE的架构图可以看到FATE的架构自上而下分为了四层最上面一层是FATE提供的服务包括FATE云服务FATE面板FATE任务调度管理生命周期管理等往下一层是FATE的机器学习核心组件包括横向联邦学习、纵向联邦学习等再往下一层是应用在FATE中的安全协议隐私保护算法包括同态加密FedAvgRSA等最后一层是底层框架又可以分为平行的三层包括计算框架TensorFlowPytorchSpark消息队列协议以及存储框架。
FATE的主要功能如下

  • 提供了一种基于数据隐私保护的分布式安全计算框架
  • 为机器学习、深度学习等常用算法提供高性能的安全计算支持
  • 支持同态加密、秘密共享等多种多方安全计算协议确保数据和模型的安全
  • 提供友好的跨域交互信息管理方案和开发文档极大方便开发人员使用。

2. Docker镜像安装FATE

FATE支持Linux和Mac系统支持单机部署、集群部署和KubeFATE部署三种方式具体的部署安装请查看FATE的官方文档这里只提供利用Docker镜像配置FATE单机部署。

2.1 Docker的安装

Docker的安装见我的另一篇博客里面安装步骤很详细官方文档推荐的是18.09版本但是在Ubuntu20.04中只提供了19以上的版本所以我下载的是19版本但是不影响FATE的使用。

2.2 FATE的安装

2.2.1 拉取镜像

设置部署的环境变量

$ export version={本次部署的FATE版本号, 如1.7.0}

通过镜像包

$ wget https://webank-ai-1251170195.cos.ap-guangzhou.myqcloud.com/fate/${version}/release/standalone_fate_docker_image_${version}_release.tar;
$ docker load < standalone_fate_docker_image_${version}_release.tar;
$ docker images | grep federatedai/standalone_fate

能看到对应的版本则镜像下载成功。

通过公共镜像拉取

$ docker pull federatedai/standalone_fate:${version}

2.2.2 启动

$ docker run -d --name standalone_fate -p 8080:8080 federatedai/standalone_fate:${version};
$ docker ps -a | grep standalone_fate

端口映射到8080启动的容器名称为standalone_fate 后面是镜像名仓库+镜像名。后面的指令输入后能看到对应的版本容器启动则成功。
ps在启动的时候我遇到过端口占用的情况用指令杀死占用端口的进程还要注意删除容器再重复run不然会显示容器重复如果杀死进程还是有端口占用建议重启。

2.2.3 测试

进入容器

$ docker exec -it $(docker ps -aqf "name=standalone_fate") bash

如果报错参数不匹配就把$中的内容换成容器的ID即可后面的bash表示用shell界面操作容器。
Toy测试

$ flow test toy -gid 10000 -hid 10000

如果成功屏幕会显示

$ success to calculate secure_sum, it is 2000.0

单元测试

$ fate_test unittest federatedml --yes

如果成功会显示下方语句

$ there are 0 failed test

有些用例算法在examples文件夹下可以自行使用。

3. FATE编程范式

FATE构建联邦学习模型有两种不同编程范式。

  • 组件化配置模型训练拆分为不同的任务每个任务以组建的形式通过有向无环图相连联邦学习所需的配置参数在配置文件中定义。该模式中用户只需自定义和提交配置文件就可以直接执行联邦训练。
  • 脚本编程FATE提供API接口用户通过脚本编程的方式实现联邦模型类似直接使用Python编程。

由于脚本编程不够稳定这里只使用组件化配置阐述。组件化配置需要提供两个配置文件。

  • dsl配置文件FATE内置的一套自定义领域特定语言在dsl中常见的机器学习任务划分为不同的模块如数据读写、模型训练、模型评估可以通过一个有向无环图连接。
  • conf配置文件设置dsl中组件模块的参数。

利用FATE组件化配置的优点

  • 提供多种安全策略机制
  • 部署简单方便
  • 提供可视化界面
  • 支持常用的机器学习算法。

4. 用FATE从零实现横向逻辑回归

4.1 数据集

本章实验使用的数据集是威斯康星州临床科学中心开源的乳腺癌肿瘤数据集该数据集内置在sklearn库中可以直接加载查看。

from sklearn.datasets import load_breast_cancer
import pandas as pd

breast_dataset = load_breast_cancer()
breast = pd.DataFrame(breast_dataset.data, columns=breast_dataset.feature_names)
breast['y'] = breast_dataset.target
# 查看前五组数据
breast.head()

在这里插入图片描述可以看到数据总共有30个特征10个属性分别以均值、标准差、最大差值出现三次,569个样本中恶性肿瘤样本212个良性有357个。

4.2 逻辑回归

这是一个典型的二分类模型的训练数据集并不大使用简单的逻辑回归作为实验模型。
传统的线性回归定义

y = W T X + b y=W^{T}X+b y=WTX+b
但是对于二分类模型来说简单的函数线性映射是不能够分类的还需要一个非线性函数映射到离散标签

y = f ( W T X + b ) y=f(W^{T}X+b) y=f(WTX+b)

在逻辑回归中使用logistic函数进行非线性映射logistic表示为

f ( z ) = 1 1 + e − z f(z)=\frac{1}{1+e^{-z} } f(z)=1+ez1

图像表示为
在这里插入图片描述
可以看出 z = W T X + b ≥ 0 z=W^{T}X+b\ge 0 z=WTX+b0则判断为正例否则判断为反例。

4.3 横向数据切分

假设当前有两方参与横向联邦学习训练取乳腺癌数据集前469条数据作为训练集后100条作为测试集数据切分的策略如下

  • 训练数据切分前200条作为公司A的本地数据存为breast_1_train.csv剩余269条作为公司B的本地数据存为breast_2_train.csv
  • 测试数据集不切分由双方共用存为breast_eval.csv

横向数据切分代码

from sklearn.datasets import load_breast_cancer
import pandas as pd

# 导入并查看数据
breast_dataset = load_breast_cancer()
breast = pd.DataFrame(breast_dataset.data, columns=breast_dataset.feature_names)
breast.head()

# z-score标准化
breast = (breast-breast.mean()) / (breast.std())
# 获取列名
col_names = breast.columns.values.tolist()

# 更换列名
columns = {}
for idx, n in enumerate(col_names):
    columns[n] = "x%d"%idx
breast = breast.rename(columns=columns)

# 插入每行序列和y
breast['y'] = breast_dataset.target
idx = range(breast.shape[0])
breast.insert(0, 'idx', idx)

# 打乱数据并生成csv
breast = breast.sample(frac=1)
train = breast.iloc[:469]
eval =  breast.iloc[469:]
breast_1_train = breast.iloc[:200]
breast_2_train = breast.iloc[200:]
breast_1_train.to_csv('breast_1_train.csv', index=False, header=True)
breast_2_train.to_csv('breast_2_train.csv', index=False, header=True)
eval.to_csv('breast_eval.csv', index=False, header=True)

4.4 横向联邦模型训练

FATE构建联邦学习模型工作

  • 数据输入将文件转换为支持的DTable格式。DTable是一个分布式数据集合FATE所有运算都基于DTable格式进行。
  • 模型训练FATE为模型训练构建流水线。
  • 模型评估将训练好的模型在测试集上评估。

4.4.1 数据输入

首先确定基目录在运行FATE后可以输入pwd查看我的基目录为

fate_dir=/data/projects/fate/

根据数据切分的结果把三组数据集(.csv)上传到$fate_dir/examples/data/中。
由于文件需要上传到docker容器中所以要特殊的文件上传工具这里使用rz如果docker中没有则在docker中输入

$ sudo apt-get install lrzsz

如果是在ubuntu本机上运行的建议更换设备用xshell远程连接否则在本机上输入

$ rz -be

会出现一堆乱码等半天也不跳出文件框而在xshell中瞬间弹出如果出现传输失败那就要检查要传输的文件是否被使用。
在这里插入图片描述
用ls查看左边一列即是上传的文件。

4.4.2 上传配置文件

由于FATE所有运算都基于DTable格式进行所以要把上传的文件转换为DTable。首先上传配置文件配置文件的实例文件有两种对应v1和v2两个版本。

  • example/dsl/v1
    upload_data.jsonupload_host.jsonupload_guest.json结构如下
{
    "file": "examples/data/breast_hetero_guest.csv",	// 数据文件路径相对于当前所在路径
    "head": 1,	// 指定数据文件是否包含表头1: 是0: 否
    "partition": 16,	// 指定用于存储数据的分区数
    "work_mode": 0, 	 // 指定工作模式0: 单机版1: 集群版
    "table_name": "breast_hetero_guest",	// 需要转换为DTable格式的表名相当于后续需要使用的表
    "namespace": "experiment"	// DTable格式的表名对应的命名空间
}
  • example/dsl/v2/upload
{
	"file": "/data/projects/fate/examples/data/breast_hetero_guest.csv",	// 数据文件路径相对于当前所在路径
	"table_name": "breast_hetero_guest",	// 需要转换为DTable格式的表名
	"namespace": "experiment",// DTable格式的表名对应的命名空间
	"head": 1,	// 指定数据文件是否包含表头1: 是0: 否
	"partition": 8,	// 指定用于存储数据的分区数
	"work_mode": 0,	 // 指定工作模式0: 单机版1: 集群版
	"backend": 0	// 指定后端,0:EggRoll, 1: Spark _ RabbitMQ, 2: Spark + Pulsar
}

在FATE-1.6.0版本中fate_flow的目录存放在fateflow/python/中所以执行upload的过程和书中不一样为

python /fate/python/fate_flow/fate_flow_client.py -f upload -c upload_data.json

从FATE-1.5开始推荐使用FATE-Flow-Client Command Line执行FATE-Flow任务上传命令格式为

$ flow data upload -c example/dsl/v2/upload/upload_conf.json

本文使用v1版本的上传数据操作如果有想使用v2的朋友可以访问参考链接的第一个链接。

配置文件如下

  • 上传数据至公司A。
{
    "file": "/fate/example/data/breast_1_train.csv",
    "head": 1,
    "partition": 8,
    "work_mode": 0,
    "table_name": "homo_breast_1_train",
    "namespace": "homo_host_breast_train"
}

  • 上传数据至公司B。
{
    "file": "/fate/example/data/breast_2_train.csv",
    "head": 1,
    "partition": 8,
    "work_mode": 0,
    "table_name": "homo_breast_2_train",
    "namespace": "homo_guest_breast_train"
}

  • 上传测试数据至公司A 。
{
    "file": "/fate/example/data/breast_eval.csv",
    "head": 1,
    "partition": 8,
    "work_mode": 0,
    "table_name": "homo_breast_1_eval",
    "namespace": "homo_host_breast_eval"
}

  • 上传测试数据至公司B 。

在根目录下输入python /fate/python/fate_flow/fate_flow_client.py -f upload -c upload_data.json 视每个人的环境而定。返回如下代码则成功

{
    "data": {
        "board_url": "http://127.0.0.1:8080/index.html#/dashboard?job_id=202203110325129735931&role=local&party_id=0",
        "job_dsl_path": "/fate/jobs/202203110325129735931/job_dsl.json",
        "job_id": "202203110325129735931",
        "job_runtime_conf_on_party_path": "/fate/jobs/202203110325129735931/local/job_runtime_on_party_conf.json",
        "job_runtime_conf_path": "/fate/jobs/202203110325129735931/job_runtime_conf.json",
        "logs_directory": "/fate/logs/202203110325129735931",
        "model_info": {
            "model_id": "local-0#model",
            "model_version": "202203110325129735931"
        },
        "namespace": "homo_host_breast_train",
        "pipeline_dsl_path": "/fate/jobs/202203110325129735931/pipeline_dsl.json",
        "table_name": "homo_breast_1_train",
        "train_runtime_conf_path": "/fate/jobs/202203110325129735931/train_runtime_conf.json"
    },
    "jobId": "202203110325129735931",
    "retcode": 0,
    "retmsg": "success"
}

还可以在FATEboard上查看可视化结果。
在这里插入图片描述

4.4.3 模型训练

FATE支持常用的机器学习模型如

  • 线性模型横向、纵向的线性回归、logistic回归等线性模型。
  • 树模型基于纵向的GBDT实现。
  • 神经网络支持横向的深度神经网络模型DNN。

在这里插入图片描述
可以看到在fate dsl_conf的V2版本模型种类又比书中所描述的详细不少。
V2版本在预设任务配置上有一些改变和提升最直观的一点是不会自动为训练任务生成预测dsl需要用户自定义也可以使用flow命令自动配置预测dsl。
为了在V2中使用命令行客户端即flow命令需要进行一系列配置。参考官方文档FATE Client包含了FATE项目多个客户端Pipeline, FATE Flow ClientFATE Test
获取所有命令分类和子命令

    [IN]
    flow

    [OUT]
    Usage: flow COMMAND [OPTIONS]

      Fate Flow Client

    Options
      -h, --help  Show this message and exit.

    Commands
      component   Component Operations
      data        Data Operations
      init        Flow CLI Init Command
      job         Job Operations
      model       Model Operations
      queue       Queue Operations
      table       Table Operations
      task        Task Operations

安装FATE CLient

$ pip install fate-client
# 或者
$ pip install fate-client==${version}

集群上安装请移步官方文档。
初始化

# 指定fateflow的IP地址和端口进行初始化
$ flow init --ip 192.168.0.1 --port 9380

获得如下返回视为初始化成功

{
    "retcode": 0,
    "retmsg": "Fate Flow CLI has been initialized successfully."
}

验证
查询任务情况

$ flow job query

返回以下即可

{
    "data": [],
    "retcode": 0,
    "retmsg": "no job could be found"
}

本章使用逻辑回归模型进入$fate_dir/examples/dsl/v1/homo_logistic_regression目录该目录下已经有很多预设的dsl文件和conf文件。修改homo_lr_train_dsl.jsonhomo_lr_train_conf.json。修改推荐用vim如果没有用yumapt-get进行安装

$ yum install -y vim
# 如果yum没有
$ apt-get install -y vim
  • test_homolr_train_dsl.json描述任务模块将任务模块以有向无环图形式组合。包括dataio_0homo_lr_0evaluation_0这些组件分别用作数据格式转换、自带横向逻辑回归组件、模型评估。
    在这里插入图片描述

  • test_homolr_train_conf.json设置各个组建的参数。找到role字段修改三个参数train_data下的namenamespace以及表示标签列对应的属性名label_name在这里插入图片描述
    接着是algorithm_parameters字段它是用来设置训练的超参数信息包括学习率优化函数迭代次数等可以根据实际需要自行修改。在这里插入图片描述

文件配置结束在当前位置输入

$ python /fate/python/fate_flow/fate_flow_client.py -f submit_job -d test_homolr_train_job_dsl.json -c test_homolr_train_job_conf.json

在FATEboard上查看任务运行情况。

在这里插入图片描述在这里插入图片描述

4.4.4 模型评估

常用的模型评估方法包括留出法和交叉验证法。

  • 留出法将数据按照一定比例切分预留一部分数据作为评估模型数据。
  • 交叉验证法 将数据集D切分为k份D1D2…Dk这样可以获得k组不同的训练数据集和评估数据集得到k个评估的结果取其平均值作为最终模型评估结果。

由于之前已经有了额外的数据集作为评估数据集这里用留出法。为了将留出的数据用于模型评估需要修改dsl组建配置。具体来说在test_homolr_train_job_dsl文件中在components组件下添加一个新的数据输入组件dataio_1用来读取测试数据如下所示。

{
    "components" : {
        "dataio_0": {
            "module": "DataIO",
            "input": {
                "data": {
                    "data": ["args.train_data"]
                }
            },
            "output": {
                "data": ["train"],
                "model": ["dataio"]
            }
         },
        "dataio_1": {
            "module": "DataIO",
            "input": {
                "data": {
                    "data": ["args.eval_data"] # 表示测试数据采用conf文件中的args.eval_data设置的文件
                },
                "model": ["dataio_0.dataio"] # 使用数据训练模块"dataio_0.dataio"的输出作为"dataio_1"的模型输入
            },
            "output": {
                "data": ["eval_data"] # 设置输出的data名称可任意设定
            }
        },
        "feature_scale_0": {
            "module": "FeatureScale",
            "input": {
                "data": {
                    "data": ["dataio_0.train"]
                }
            },
            "output": {
                "data": ["train"],
                "model": ["feature_scale"]
            }
        },
        "feature_scale_1": {
            "module": "FeatureScale",
            "input": {
                "data": {
                    "data": ["dataio_1.eval_data"]
                }
            },
            "output": {
                "data": ["eval_data"],
                "model": ["feature_scale"]
            }
        },        
        "homo_lr_0": {
            "module": "HomoLR",
            "input": {
                "data": {
                    "train_data": ["feature_scale_0.train"]
                }
            },
            "output": {
                "data": ["train"],
                "model": ["homolr"]
            }
        },
        "homo_lr_1": {
            "module": "HomoLR",
            "input": {
                "data": {
                    "eval_data": ["feature_scale_1.eval_data"]	# 指定训练数据
                },
                "model": ["homo_lr_0.homolr"]
            },
            "output": {
                "data": ["eval_data"],
                "model": ["homolr"]
            }
        },        
        "evaluation_0": {
            "module": "Evaluation",
            "input": {
                "data": {
                    "data": ["homo_lr_0.train"]
                }
            }
        },
        "evaluation_1": {
            "module": "Evaluation",
            "input": {
                "data": {
                    "data": ["homo_lr_1.eval_data"]
                }
            }
        }        
    }
}

然后修改conf文件在role_parameters字段中为guest和host添加测试数据的DTable表名。

{
    "initiator": {
        "role": "guest",
        "party_id": 10000
    },
    "job_parameters": {
        "work_mode": 0
    },
    "role": {
        "guest": [10000],
        "host": [10000],
        "arbiter": [10000]
    },
    "role_parameters": {
        "guest": {
            "args": {
                "data": {
                    "train_data": [
                        {
                            "name": "homo_breast_2_train",
                            "namespace": "homo_guest_breast_train"
                        }
                    ],
                    "eval_data": [
                        {
                            "name": "homo_breast_2_eval",
                            "namespace": "homo_guest_breast_eval"
                        }
                    ]
                }
            },
            "dataio_0": {
                "with_label": [true],
                "label_name": ["y"],
                "label_type": ["int"],
                "output_format": ["dense"]
            }
        },
        "host": {
            "args": {
                "data": {
                    "train_data": [
                        {
                            "name": "homo_breast_1_train",
                            "namespace": "homo_host_breast_train"
                        }
                    ],
                    "eval_data": [
                        {
                            "name": "homo_breast_1_eval",
                            "namespace": "homo_host_breast_eval"
                        }
                    ]
                }
            },
            "dataio_0": {
                "with_label": [true],
                "label_name": ["y"],
                "label_type": ["int"],
                "output_format": ["dense"]
            },
            "evaluation_0": {
                "need_run": [false]
            },
            "evaluation_1": {
                "need_run": [false]
            }
        }
    },
    "algorithm_parameters": {
        "homo_lr_0": {
            "penalty": "L2",
            "optimizer": "sgd",
            "tol": 1e-05,
            "alpha": 0.01,
            "max_iter": 20,
            "early_stop": "diff",
            "batch_size": 320,
            "learning_rate": 0.05,
            "validation_freqs": 1,
            "init_param": {
                "init_method": "zeros"
            },
            "encrypt_param": {
                "method": null
            },
            "cv_param": {
                "n_splits": 4,
                "shuffle": true,
                "random_seed": 33,
                "need_cv": false
            }
        },
        "evaluation_0": {
            "eval_type": "binary"
        }
    }
}

执行submit_job命令。

$ python /fate/python/fate_flow/fate_flow_client.py -f submit_job -d test_homolr_train_job_dsl.json -c test_homolr_train_job_conf.json

可以查看带有模型评估算法模块的有向无环图该图由训练模块和评估模块两部分构成。
在这里插入图片描述

4.4.5 多参与方环境配置

对于多个客户端参与的场景需要在配置文件中修改部分参数值。

  • 首先在conf文件中找到role字段在默认情况下只有两方多客户端就是在role下host子字段方添加新的客户端ID即可各个客户端ID逗号分隔。
  • role_parameters字段中host子字段添加对应新的客户端DTable表名和命名空间。

阅读总结

本来这篇博客因该早在半个月前就能写完但是编写到一半代码跑不出来了。。。当时用的是FATE1.7.0单元测试都没问题但是只要运行甚至是上传数据都会报错报错如下
在这里插入图片描述
不知道访问了多少论坛在FATE的各个交流群都问了个遍至今杳无音讯只能作罢其中重装fate重装docker换fate的旧版本就差重装系统了。终于测试fate1.6.0版本时没有这个问题了虽然有其他小问题但不影响结果。这半个多月来对我的心智是一个很大的考验我甚至一度摆烂觉得永远不可能跑通了好在功夫不负有心人最后我想对环境有问题的小伙伴们提个建议如果怎么都没办法跑通沉淀一下自己歇几天做别的事情没准就能转换思路了。最后的最后如果有朋友知道上图问题如何解决请务必告诉我万分感谢

参考链接

https://blog.csdn.net/Sisyphus_98/article/details/122933110
https://blog.csdn.net/qq_41841524/article/details/117662143?spm=1001.2014.3001.5502

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6