您现在的位置是:主页 > 币圈资讯 >

联邦学习 FATE 框架的使用与训练效果的评估

2021-10-30 14:22币圈资讯 人已围观

简介 由于安全问题、竞争关系等因素,数据在各个行业甚至企业内部常以「数据孤岛」的形式存在,但数据的流通和共享...

由于安全问题、竞争关系等因素,数据在各个行业甚至企业内部常以「数据孤岛」的形式存在,但数据的流通和共享对于多方创新协作及价值创造极为重要;因此,在保护隐私安全的前提下实现数据可信共享的需求越来越突出。

边界智能团队自主研发的 IRITA BEAN 是专注于大数据隐私保护和边缘分析的安全可控的企业级联盟链产品,集成了主流开源隐私计算框架,实现了大数据多层次授权共享、多方隐私保护查询、多方联盟学习等能力。近期,IRITA BEAN 完成了对联邦学习 FATE 框架的集成,能够更好地支撑以保护数据隐私为前提的数据共享,进而推进多方可信协作。

本文围绕联邦学习 FATE 框架的使用及其训练效果展开,由边界研究院娄辰飞编纂,首发于边界智能知乎专栏「边界研究」,旨在与开源技术社区共同探讨研究。

此外,团队成员蒲军和娄辰飞还连续成为 8 月和 9 月「FATE 社区月度活跃建设者」,积极参与并持续为开源技术社区建设做出贡献。

A97xAyUHhDc1BjzocSbOzodISLwLE3ZWHm41MjCZ.png

作者:边界研究院 娄辰飞

1. 什么是联邦学习?

在愈来愈注重隐私保护的时代,用户的数据变成了公司的秘密,不同公司之间的数据变得互相孤立。这为很多机器学习的应用带来了阻碍,毕竟对机器学习模型效果影响最大的因素便是数据的丰富性和多样性。若能将不同公司的数据结合在一起去训练一个模型,一定能够比每个公司单独训练自己的模型取得更好的效果。在这个背景下,联邦学习的概念诞生了。联邦学习是一种能够保护数据隐私的分布式机器学习技术。

学术界一般将联邦学习分为横向联邦学习、纵向联邦学习和联邦迁移学习[1] 。

横向联邦学习指的是:不同的公司拥有不同的用户群体,但是拥有同样的数据特征。以北京协和医院和上海瑞金医院为例,一方以北京病人群体为主,另一方为上海的病人群体,但两家医院都有着病人的病史、血压血型等特征数据,因此双方的数据可共同组成更多的样本数量,以提升模型的训练效果。横向联邦学习最经典的算法是联邦平均,即各自算出梯度,然后交由第三方或者使用分布式平均算法求出平均梯度,再发还给每一方。

纵向联邦学习指的是:不同的公司拥有同样的用户群体,但是拥有不同的数据特征。例如,上海浦发银行和上海瑞金医院,双方都具有大量上海居民的数据,但是一方拥有的是金融数据,另一方拥有的是医疗数据。纵向联邦学习在技术上实现难度高于横向联邦学习,模型的实现方式也更为多样,纵向的逻辑回归、梯度提升树、神经网络都是单独设计的模型。

2. 联邦学习的框架:FATE

目前,较为流行的框架是 FATE。

联邦学习及其框架本身尚未完全成熟,关于联邦学习的训练效果还有较多地方需要验证。本篇文章主要验证的是:联邦学习是否可以达到与传统机器学习算法一样的效果?

在 FATE 框架中,横向的场景被称为 Homo,而纵向的则被称为 Hetero,比如纵向安全提升树模型称为 Hetero Secure Boost。

在这里,我们用「default of credit card clients dataset」数据集测试了 FATE 框架下的 Hetero Secure Boost、Hetero Logistic Regression 和 Hetero Neural Network 三个模型,并在单机模式下运行了 xgboost、logistic regression 作为对照。随后,我们还使用了文献「The Comparison of Data Mining...」[2] 中提到的方法,对我们的联邦学习模型进行了评估,最终得到的结论是:在纵向联邦学习场景下,FATE 可以成功地训练模型并达到较好效果。

3. 训练的结果

3.1. 梯度提升树

  • 联邦学习 Hetero Secure Boost(单机部署下的训练用时:约 20min):

  • 图片

  • 单机 xgboost(训练用时:约 10s):

  • 图片

    比较上文两个表格中可得结论:联邦学习和单机机器学习可以达到类似的效果;并且模型对 0 与 1 的分类效果都较好。

    3.2. 逻辑回归

  • 联邦学习 Hetero Logistic Regression(单机部署下的训练用时:约 20min):

  • 图片

  • 单机 Logistic Regression(训练用时:约 10s):

  • 图片

    比较上文两个表格中可得结论:联邦学习和单机机器学习可以达到类似的效果;模型的分类效果稍次于梯度提升树。

    3.3. 神经网络(单机部署下的训练用时:约 5h):

    图片

    *注:神经网络模型在 docker-compose 部署和 k8s 部署环境下均出现了 Bug,虽然已经反映给 FATE 社区,但在撰文时这些问题尚未得到妥善解决。因此,上述实验是在 standalone 部署下完成的。

    从上文数据可以看出,模型的分类效果较不理想,并且由于纵向神经网络的结构和单机的神经网络有本质的差异,因此没有使用单机的神经网络进行对比。

    此外,我们还采用了论文[2]中所提出的评估方法对我们的模型进行了评估。该论文同样是一篇处理 default credit 数据集的文章,且比较了六种不同的分类器在该数据集上的分类效果。

    文章使用的两种评估方式分别是 Area Ratio 和 Predictive Accuracy,具体含义介绍为:

    1. Area Ratio 是在模型的 gain chart 图中,Part 1/ (Part 1+Part 2) 的值。

    图片

    2. Predictive Accuracy 是「真实概率」与「预测概率」的线性拟合相关度 R²,其中:

  • 「预测概率」是模型的直接输出(即没有经过阈值分类操作的原始输出)

  • 「真实概率」是根据以下经验公式计算得出的:

    图片

    其中 Y₂ 表示按照「预测概率」排序的真实标签值。

  • 我们计算了 Hetero Secure Boost 的这两个指标,并与论文中的结果进行了对比,总结如下:

    图片

    由此可见,我们的模型效果是非常理想的。

    4. 部署的相关技术细节和代码

    作为例子,以下展示 secure boost 训练的过程:

    【提示:根据 FATE 官网给出的要求,设备需要拥有至少 8 核、16G 内存、500G 硬盘。操作系统为:CentOS Linux release 7】

    1. 下载数据集

    我们所使用的数据集直接来自于 kaggle,是未经处理的原始业务数据。它叫做 default of credit card clients dataset,是统计台湾信用卡用户下个月是否违约的数据集。

    该数据集包括:23 个 features,1 个 label,共 30000 条用户数据

    我们通过命令获取了数据集 csv 表格。

    2. 对数据集进行拆分:

    OFy5awNOrjma7b4b896T1UTaI1Sj5aTXdefZdK5V.png

    使用 python 进行拆分时所用到的代码在附录 5.3 中

    3. 编写用来定义任务的 json 文件

    首先,对于每一份数据文件我们都需要一个单独的 json 文件来定义上传任务,并通过以下的命令执行上传任务:

    flow data upload -c upload.json

    关于 upload.json 的具体内容请查看附录 5.4

    其次,对于一次训练,我们需要写一份用于定义任务流程的 dsl.json 文件和用于定义任务参数的 conf.json 文件。然后,通过以下的命令执行这次任务:

    flow job submit -c conf.json -d dsl.json

    随后,我们便可以通过 fateboard 来查看模型的进度和各个模块的输出。

    关于 conf.json 和 dsl.json 的具体内容请查看附录 5.5 和 5.6

    5. 代码附录

    5.1:

    import pandas as pd import numpy as np   ''' for training gain '''   graph_detail = pd.read_csv( '/fate/bianjie_credit/gain_details/sb_train_unsampled.csv' ) graph_detail_np = graph_detail.values   coordinates = graph_detail_np[:, [1, 2]]   area = 0 for i in range( coordinates.shape[0] - 1 ):     height = ( coordinates[i][1] + coordinates[i+1][1] ) / 2 - ( coordinates[i][0] + coordinates[i+1][0] ) / 2     width = coordinates[i+1][0] - coordinates[i][0]     area = area + width * height   print( "area between training gain and baseline = ", area )   print( "area between optimal gain and baseline = ", 0.78/2 ) print( "area ratio for training gain = ", area / (0.78/2) )   ''' for validating gain '''   graph_detail = pd.read_csv( '/fate/bianjie_credit/gain_details/sb_validate_unsampled.csv' ) graph_detail_np = graph_detail.values   coordinates = graph_detail_np[:, [1, 2]]   area = 0 for i in range( coordinates.shape[0] - 1 ):     height = ( coordinates[i][1] + coordinates[i+1][1] ) / 2 - ( coordinates[i][0] + coordinates[i+1][0] ) / 2     width = coordinates[i+1][0] - coordinates[i][0]     area = area + width * height   print( "area between training gain and baseline = ", area )   print( "area between optimal gain and baseline = ", 0.78/2 ) print( "area ratio for training gain = ", area / (0.78/2) )

    5.2:

    from numpy.lib.type_check import real import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.metrics import r2_score   predict_detail = pd.read_csv( '/fate/bianjie_credit/predict_details/sb_unsampled.csv', index_col=False ) predict_detail_np = predict_detail.values num_for_smoothing = 50   ''' sort the numpy array along the 'predict_score' column '''   predict_detail_sorted = predict_detail_np[ predict_detail_np[:,3].argsort() ]   ''' calculate one real probability for an index '''   def real_prob( index ):     sum = 0     temp_list = [i + index - num_for_smoothing for i in range( num_for_smoothing * 2 + 1 )]     for temp_index in temp_list:         sum = sum + predict_detail_sorted[temp_index][1]     prob = sum / ( num_for_smoothing * 2 + 1 )     return prob   ''' create a new 'real_probability array whose 2 columns are     predict_score, calculated_probability, respectively '''   real_probability = np.empty( (0, 2) ) count = 0 for predict_item in predict_detail_sorted:     count = count + 1     if count <= num_for_smoothing or count > predict_detail_sorted.shape[0] - num_for_smoothing:         continue     real_probability = np.append( real_probability, np.array( [[ predict_item[3], real_prob( count - 1 ) ]] ), axis=0 )   real_label = np.empty( (0, 2) ) for predict_item in predict_detail_sorted:     real_label = np.append( real_label, np.array( [[ predict_item[3], predict_item[1] ]] ), axis=0 )   ''' plot the scatter plot the linear regression fit line calculate the R^2 '''   plt.plot( real_label[:,0], real_label[:,1] ) plt.savefig('real_label.png') plt.clf()   m, b = np.polyfit( real_probability[:,0], real_probability[:,1], 1) plt.plot( real_probability[:,0], real_probability[:,1] ) plt.plot( real_probability[:,0], m * real_probability[:,0] + b ) plt.annotate("r-squared = {:.3f}".format(r2_score(m * real_probability[:,0] + b, real_probability[:,1])), xy=(0, 1), xytext=(0.2, 0.8) ) plt.savefig('real_probabilities.png') plt.clf() print( r2_score(m * real_probability[:,0] + b, real_probability[:,1]) )

    5.3:

    import pandas as pd import random import numpy as np row_num = 30000 col_num = 23 training_row_num = 20000 validating_row_num = 5000 evaluating_row_num = row_num - training_row_num - validating_row_num host_col_num = 15 guest_col_num = col_num - host_col_num training_host_row_num = 16000 training_guest_row_num = 15000 validating_host_row_num = 3000 validating_guest_row_num = 3500 dir_name = "./data" ''' get the raw data and store them as DataFrame format ''' # read the default credit dataset from excel file to DataFrame credit_dataset = pd.read_csv( '/fate/bianjie_credit/default_of_credit_card_clients.csv' ) ''' 30000 items in total,     training set: first 20000         host: 16000 random iterm and first 15 features         guest: 15000 random iterm and second 8 features and the label     validating set: second 10000         host: 70000 random iterm and first 15 features         guest: 8000 random iterm and second 8 features and the label     all sets include the id ''' # determine the row indices training_host_rows = random.sample( range(training_row_num), training_host_row_num )  training_guest_rows = random.sample( range(training_row_num), training_guest_row_num )  validating_host_rows = random.sample( [i + training_row_num for i in range(validating_row_num)], validating_host_row_num ) validating_guest_rows = random.sample( [i + training_row_num for i in range(validating_row_num)], validating_guest_row_num ) evaluating_host_rows = [i + training_row_num + validating_row_num for i in range(evaluating_row_num)] evaluating_guest_rows = evaluating_host_rows # determine the column indices host_cols = range(host_col_num + 1) guest_cols = [i + host_col_num + 1 for i in range(guest_col_num)] guest_cols.insert( 0, 0 ) ''' create and modify the numpy representation for the dataframe ''' credit_data_np = credit_dataset.values credit_target = credit_dataset['default payment next month'].values credit_feature_names_list = [] for i in range( col_num + 1 ): credit_feature_names_list.append(credit_dataset.axes[1][i]) credit_feature_names = np.array( credit_feature_names_list ) # create numpy arrays for datasets training_host_dataset = credit_data_np[training_host_rows] training_host_dataset = training_host_dataset[:, host_cols] training_guest_dataset = credit_data_np[training_guest_rows] training_guest_dataset = training_guest_dataset[:, guest_cols] validating_host_dataset = credit_data_np[validating_host_rows] validating_host_dataset = validating_host_dataset[:, host_cols] validating_guest_dataset = credit_data_np[validating_guest_rows] validating_guest_dataset = validating_guest_dataset[:, guest_cols] evaluating_host_dataset = credit_data_np[evaluating_host_rows] evaluating_host_dataset = evaluating_host_dataset[:, host_cols] evaluating_guest_dataset = credit_data_np[evaluating_guest_rows] evaluating_guest_dataset = evaluating_guest_dataset[:, guest_cols] ''' create dataframes for datasets ''' training_host_frame = pd.DataFrame( training_host_dataset, columns=credit_feature_names[host_cols] ) training_guest_frame = pd.DataFrame( training_guest_dataset, columns=credit_feature_names[guest_cols] ) training_guest_frame['default payment next month'] = credit_target[training_guest_rows] validating_host_frame = pd.DataFrame( validating_host_dataset, columns=credit_feature_names[host_cols] ) validating_guest_frame = pd.DataFrame( validating_guest_dataset, columns=credit_feature_names[guest_cols] ) validating_guest_frame['default payment next month'] = credit_target[validating_guest_rows] evaluating_host_frame = pd.DataFrame( evaluating_host_dataset, columns=credit_feature_names[host_cols] ) evaluating_guest_frame = pd.DataFrame( evaluating_guest_dataset, columns=credit_feature_names[guest_cols] ) evaluating_guest_frame['default payment next month'] = credit_target[evaluating_guest_rows] ''' create files and store the data in csv files ''' # create csv variables training_host_csv = training_host_frame.to_csv( index=False )  training_guest_csv = training_guest_frame.to_csv( index=False )  validating_host_csv = validating_host_frame.to_csv( index=False )  validating_guest_csv = validating_guest_frame.to_csv( index=False )  evaluating_host_csv = evaluating_host_frame.to_csv( index=False )  evaluating_guest_csv = evaluating_guest_frame.to_csv( index=False ) # write the csv variables in corresponding files  fhand = open( dir_name + "/training_host.csv", "w" ) fhand.write( training_host_csv ) fhand.close() fhand = open( dir_name + "/training_guest.csv", "w" ) fhand.write( training_guest_csv ) fhand.close() fhand = open( dir_name + "/validating_host.csv", "w" ) fhand.write( validating_host_csv ) fhand.close() fhand = open( dir_name + "/validating_guest.csv", "w" ) fhand.write( validating_guest_csv ) fhand.close() fhand = open( dir_name + "/evaluating_host.csv", "w" ) fhand.write( evaluating_host_csv ) fhand.close() fhand = open( dir_name + "/evaluating_guest.csv", "w" ) fhand.write( evaluating_guest_csv ) fhand.close()

    5.4:

    {     "file": "/data/projects/fate/examples/chenfei_test_guest/guest_data/training_guest.csv",     "head": 1,     "partition": 16,     "work_mode": 1,     "table_name": "credit_training_guest",     "namespace": "module_selection"}

    5.5:

    {   "components": {     "reader_train": {       "module": "Reader",       "output": {         "data": ["data_train"]       }     },     "dataio_train": {       "module": "DataIO",       "input": {         "data": {           "data": ["reader_train.data_train"]         }       },       "output": {         "data": ["data_train"],         "model": ["trained_model"]       }     },     "intersection_train": {       "module": "Intersection",       "input": {         "data": {           "data": ["dataio_train.data_train"]         }       },       "output": {         "data": ["data_train_intersection"]       }     },     "scale_train": {       "module": "FeatureScale",       "input": {         "data": {           "data": ["intersection_train.data_train_intersection"]         }       },       "output": {         "data": ["data_train_scale"],         "model": ["model_scale"]       }     },     "sample_weight_train": {       "module": "SampleWeight",       "input": {         "data": {           "data": ["scale_train.data_train_scale"]         }       },       "output": {         "data": ["data_sample_weight"]       }     },       "reader_validate": {       "module": "Reader",       "output": {         "data": ["data_validate"]       }     },     "dataio_validate": {       "module": "DataIO",       "input": {         "data": {           "data": ["reader_validate.data_validate"]         },         "model": ["dataio_train.trained_model"]       },       "output": {         "data": ["data_validate"],         "model": ["validated_model"]       }     },     "intersection_validate": {       "module": "Intersection",       "input": {         "data": {           "data": ["dataio_validate.data_validate"]         }       },       "output": {         "data": ["data_validate_intersection"]       }     },     "scale_validate": {       "module": "FeatureScale",       "input": {         "data": {           "data": ["intersection_validate.data_validate_intersection"]         }       },       "output": {         "data": ["data_validate_scale"],         "model": ["model_scale"]       }     },       "reader_evaluate": {       "module": "Reader",       "output": {         "data": ["data_evaluate"]       }     },     "dataio_evaluate": {       "module": "DataIO",       "input": {         "data": {           "data": ["reader_evaluate.data_evaluate"]         }       },       "output": {         "data": ["data_evaluate"]       }     },     "intersection_evaluate": {       "module": "Intersection",       "input": {         "data": {           "data": ["dataio_evaluate.data_evaluate"]         }       },       "output": {         "data": ["data_evaluate_intersection"]       }     },     "scale_evaluate": {       "module": "FeatureScale",       "input": {         "data": {           "data": ["intersection_evaluate.data_evaluate_intersection"]         }       },       "output": {         "data": ["data_evaluate_scale"],         "model": ["model_scale"]       }     },       "hetero_secure_boost": {       "module": "HeteroSecureBoost",       "input": {         "data": {           "train_data": ["sample_weight_train.data_sample_weight"],           "validate_data": ["scale_validate.data_validate_scale"]         }       },       "output": {         "data": ["data_train"],         "model": ["trained_model"]       }     },     "hetero_secure_boost_test": {       "module": "HeteroSecureBoost",       "input": {         "data": {           "test_data": ["scale_evaluate.data_evaluate_scale"]         },         "model": ["hetero_secure_boost.trained_model"]       },       "output": {         "data": ["tested_data"],         "model": ["tested_model"]       }     },       "evaluation_train": {       "module": "Evaluation",       "input": {         "data": {           "data": ["hetero_secure_boost.data_train"]         }       },       "output": {         "data": ["evaluated_data"]       }     },     "evaluation_test": {       "module": "Evaluation",       "input": {         "data": {           "data": ["hetero_secure_boost_test.tested_data"]         }       },       "output": {         "data": ["evaluated_data"]       }     }   } }

    5.6:

    {   "dsl_version": 2,   "initiator": {     "role": "guest",     "party_id": 9999   },   "role": {     "guest": [9999],     "host": [10000],     "arbiter": [10000]   },   "job_parameters": {     "common": {       "job_type": "train",       "backend": 0,       "work_mode": 0     }   },   "component_parameters": {     "common": {       "dataio_train": {         "output_format": "dense"       },       "dataio_validate": {         "output_format": "dense"       },       "sample_train": {         "mode": "stratified",         "method": "downsample",         "fractions": [           [0, 1.0],           [1, 1.0]         ]       },       "sample_validate": {         "mode": "stratified",         "method": "downsample",         "fractions": [           [0, 1.0],           [1, 1.0]         ]       },       "sample_evaluate": {         "mode": "random",         "method": "downsample",         "fractions": 1       },       "sample_weight_train": {         "need_run": true,         "class_weight": { "0": 1, "1": 3.3 }       },       "hetero_secure_boost": {         "task_type": "classification",         "learning_rate": 0.3,         "objective_param": {           "objective": "cross_entropy"         },         "num_trees": 30,         "validation_freqs": 2,         "early_stopping_rounds": 4,         "use_first_metric_only": true,         "tree_param": {           "criterion_params": {             "l1": 0,             "l2": 0.1           },           "max_depth": 4         }       },       "evaluation_train": {         "eval_type": "binary"       },       "evaluation_test": {         "eval_type": "binary"       }     },     "role": {       "guest": {         "0": {           "reader_train": {             "table": {               "name": "credit_training_guest",               "namespace": "module_selection"             }           },           "dataio_train": {             "with_label": true,             "label_name": "default payment next month"           },             "reader_validate": {             "table": {               "name": "credit_testing_guest",               "namespace": "module_selection"             }           },           "dataio_validate": {             "with_label": true,             "label_name": "default payment next month"           },             "reader_evaluate": {             "table": {               "name": "credit_evaluating_guest",               "namespace": "module_selection"             }           },           "dataio_evaluate": {             "with_label": true,             "label_name": "default payment next month"           }         }       },       "host": {         "0": {           "reader_train": {             "table": {               "name": "credit_training_host",               "namespace": "module_selection"             }           },           "dataio_train": {             "with_label": false           },             "reader_validate": {             "table": {               "name": "credit_testing_host",               "namespace": "module_selection"             }           },           "dataio_validate": {             "with_label": false           },             "reader_evaluate": {             "table": {               "name": "credit_evaluating_host",               "namespace": "module_selection"             }           },           "dataio_evaluate": {             "with_label": false           }         }       }     }   } }

    6. 参考资料

    [1] 1POUYANFAR S, SADIQ S, YAN Y, et al. A survey on deep learning: algorithms, techniques, and applications [J]. ACM Computing Surveys, 2019, 51(5): 1-36. 

    [2] a b Yeh, I. C., & Lien, C. H. (2009). The comparisons of data mining techniques for the predictive accuracy of probability of default of credit card clients. Expert Systems with Applications, 36(2), 2473-2480.

    关于边界研究院

    边界研究院基于边界智能在自主可控区块链底层技术、跨链技术以及结合大数据和隐私计算方面的长久技术积累,致力于热点事件分析、前沿技术分享、行业观点阐述、行业标准制定、国内外开源社区交流等研究工作,为各行业贡献以区块链技术为核心的专业研究成果。

    现阶段重点研究方向包括跨链技术探索(服务、NFT、智能合约的跨链交互)、产业应用(智慧政务、碳中和、文创与版权、全球跨境贸易)、前沿技术融合(区块链+隐私计算、大数据分析)等领域。

    Tags:

    标签云

    站点信息