一、纵向树
纵向树模型是联邦学习环境中的一个重要应用,特别是在数据隐私保护方面。在纵向树模型中,多个参与方(机构或组织)各自持有同一数据集的不同特征子集,这种数据分布称为“纵向数据分布”。纵向树模型的核心在于允许这些参与方在不共享原始数据的前提下,共同构建一个机器学习模型。
基本概念
在纵向树模型中,数据是按照特征(列)而非样本(行)进行分割的。例如,一个金融机构可能持有客户的财务信息,而医疗机构持有同一客户的健康信息。尽管这些机构对相同客户的数据进行了不同方面的收集,但他们可以通过纵向树模型合作,以提高模型的预测能力而不泄露各自的敏感数据。
工作原理
- 数据准备:
- 各参与方对自己的数据预处理,如标准化、处理缺失值等。
- 确保各方数据的行索引对齐,即不同机构的数据集应该按照相同的客户ID或其他唯一标识进行排序。
- 模型训练:
- 在训练过程中,各方不交换原始数据。相反,它们会交换必要的统计信息或中间计算结果,如梯度信息、特征分裂的候选点评分等。
- 通常通过安全多方计算(SMC)技术、同态加密(HE)或差分隐私(DP)等方法保护交换的信息,确保数据的隐私性。
- 模型迭代:
- 基于决策树的学习算法将按照特征重要性或预先设定的规则,在参与方之间轮换选取最优分裂特征。
- 各方根据收到的信息更新本地模型,逐步完善全局模型的构建。
安全性与隐私
纵向树模型在数据安全和隐私保护常用的技术包括:
- 安全多方计算(SMC):一种密码学方法,允许多个方共同计算函数而不暴露各自的输入。
- 同态加密(HE):允许在加密数据上直接进行计算的技术,计算结果仍然保持加密状态。
- 差分隐私(DP):通过添加噪声来保护数据的隐私技术,确保在输出结果中无法准确推断出任何个体信息。
应用场景
纵向树模型特别适合于需要跨域合作但又需严格遵守数据隐私和安全法规的领域,如金融、医疗和政府部门等。
二、SS-XGB
SS-XGB,相比于XGB,结合了秘密分享技术,这种结合使得XGBoost能够在保证数据隐私的前提下,跨多个参与方进行训练。秘密分享是一种加密技术,通过将敏感数据分割成多个份额,并分发给多个参与方,使得任何单一方无法独立重构原始数据。
工作原理
SS-XGB的核心在于使用秘密分享技术来保护训练数据和模型的中间计算结果,从而使得模型的训练可以在多个数据持有者之间合作完成,而无需暴露各自持有的原始数据。下面是其基本的工作流程:
- 数据预处理和秘密分享:
- 各参与方首先对自己持有的数据进行必要的预处理操作(如编码、归一化等)。
- 然后,使用秘密分享技术将每个数据项分割成多个份额,并将这些份额分发给协作的参与方。
- 分布式模型训练:
- 在训练阶段,所有参与方将基于各自持有的数据份额进行局部计算,如梯度的计算和模型参数的更新。
- 通过秘密分享技术,各方可以共享必要的聚合信息(如梯度和更新后的模型参数)而不泄露具体的数据内容。
- 安全聚合和模型更新:
- 通过安全的多方计算协议,各参与方将加密的局部计算结果进行安全聚合,以此来更新全局模型。
- 这一过程确保了模型更新的准确性和隐私性。
安全性和隐私保护
SS-XGB特别适用于数据敏感或受到严格隐私法规制约的应用场景。其使用的秘密分享技术可以确保即便在参与方之中存在恶意行为者,也不会导致数据泄露。此外,秘密分享还可以保护模型训练过程中生成的所有中间数据,如梯度和权重更新,从而保护整个训练过程的隐私。
实现挑战
- 计算和通信开销:秘密分享和安全聚合的操作增加了额外的计算和通信负担,可能会影响模型训练的效率。
- 复杂的协同工作:需要精心设计协议和算法来确保多方参与的训练过程既安全又高效。
应用领域
SS-XGB适合于金融、医疗、政府等对数据隐私有严格要求的领域。在这些领域,多个机构可能需要合作训练机器学习模型以提高服务质量或决策效率,同时又必须严格遵守数据保护法规。
三、SGB
SGB相比于XGB,结合了联邦学习和同态加密,主要用于纵向联邦学习场景,允许多个机构在不共享原始数据的情况下,共同构建和训练梯度增强决策树模型。
工作原理
SGB的核心是结合联邦学习和同态加密来处理数据的分布式特性,同时保护数据的隐私性。以下是SGB的基本工作流程:
- 数据准备与分布:
- 在纵向联邦学习中,每个参与方持有相同样本的不同特征集。例如,银行可能持有客户的财务信息,而医院持有相同客户的健康信息。
- 数据保持在各自的参与方手中,不进行跨机构传输。
- 联邦梯度计算:
- 在模型训练过程中,每个参与方根据自己持有的数据局部计算梯度信息。
- 通过联邦机制,这些梯度信息被安全地聚合以更新全局模型,此过程可以通过同态加密来加强安全性。
- 同态加密保护:
- 同态加密允许参与方在加密数据上进行计算,如梯度更新和模型优化,计算结果保持加密状态。
- 这确保了即使在传输过程中数据被拦截,也无法被解析,从而保护了数据隐私。
安全性和隐私保护
SGB的设计充分考虑了数据隐私和安全性,使其在敏感数据的机器学习应用中尤为有用:
- 数据隐私:通过联邦学习和同态加密,SGB确保了数据的隐私性和安全性。数据在本地处理,只有必要的加密信息被用于全局模型更新。
- 防止信息泄露:同态加密技术确保了即便是在数据聚合或梯度分享过程中,原始数据和中间计算结果也不会泄露。
实现挑战
- 性能和效率:同态加密操作通常计算量大、速度慢,这可能会增加模型训练的整体时间和计算成本。
- 技术复杂性:实现一个高效且安全的SGB系统需要高级的技术支持,包括优化同态加密操作和联邦学习策略。
应用领域
SGB适用于多个垂直行业合作的场景,如金融、医疗保健、保险等。
四、SS-XGB和SGB的不同点
SS-XGB和SGB虽然都能够在分布式数据环境下提供隐私保护,但是保护的方式有些区别:
SS-XGB:
- 使用秘密分享(Secret Sharing)技术,该技术通过将敏感数据分割成多个份额,并在多个参与方之间分发这些份额来保护数据隐私。每个份额单独无法提供任何有用信息,只有合并特定数量的份额才能重构原始数据。
SGB:
- 结合了联邦学习和同态加密技术。联邦学习允许多个参与方合作训练共同的模型,而无需直接交换数据。同态加密允许在加密数据上进行计算,计算结果仍然保持加密状态,从而保护数据在传输和计算过程中的隐私。
五、实践
先给出SGB的完整代码:
import secretflow as sf
import spu
import os
#配置参与方、网络配置
network_conf = {
"parties":{
"alice":{
"address": "alice:8000",
},
"bob":{
"address": "bob:8000",
},
},
}
party = os.getenv("SELF_PARTY", "alice")
sf.shutdown()
sf.init(
address="127.0.0.1:6379",
cluster_config={**network_conf,"self_party":party},
log_to_driver=True,
)
sf.__version__
alice, bob = sf.PYU("alice"), sf.PYU("bob")
spu_conf = {
"nodes":[
{
"party":"alice",
"address":"alice:8001",
"listen_addr":"alice:8001",
},
{
"party":"bob",
"address":"bob:8001",
"listen_addr":"bob:8001",
},
],
"runtime_config":{
"protocol": spu.spu_pb2.SEMI2K,
"field":spu.spu_pb2.FM128,
"sigmoid_mode":spu.spu_pb2.RuntimeConfig.SIGMOID_REAL,
},
}
heu_config = {
'sk_keeper': {'party':'alice'},
'evaluators':[{'party':'bob'}],
'mode': 'PHEU',
'he_parameters':{
'schema':'ou',
'key_pair':{
'generate':{
'bit_size':2048,
},
},
},
'encoding':{
'cleartext_type':'DT_I32',
'encoder':"IntegerEncoder",
'encoder_args':{"scale":1},
},
}
heu = sf.HEU(heu_config, spu_conf['runtime_config']['field'])
#加载Alice和Bob的数据集,两者分别拥有8种特征,并且Alice拥有标签,数据集连接https://www.kaggle.com/datasets/janiobachmann/bank-marketing-dataset,手动分一下,分别上传Alice和Bob
import pandas as pd
import os
from secretflow.data.vertical import read_csv as v_read_csv, VDataFrame
from secretflow.data.core import partition
current_dir = os.getcwd()
data = v_read_csv(
{alice:f"{current_dir}/bank_0-7.csv",bob:f"{current_dir}/bank_8-16.csv"},
keys="id",
drop_keys="id",
)
alice_y_pyu_object = alice(lambda path: pd.read_csv(path, index_col = 0))(f"{current_dir}/bank_y.csv")
label = VDataFrame(partitions={alice:partition(alice_y_pyu_object)})
data.columns
label.columns
#使用LabelEncode对分类特征进行下编码转换,使其能够被模型处理
# from data descryptions we know we need to encode data
from secretflow.preprocessing import LabelEncoder
encoder =LabelEncoder()
data['job']= encoder.fit_transform(data['job'])
data['marital']=encoder.fit_transform(data['marital'])
data['education']= encoder.fit_transform(data['education'])
data['default']=encoder.fit_transform(data['default'])
data['housing']= encoder.fit_transform(data['housing'])
data['loan']=encoder.fit_transform(data['loan'])
data['contact']= encoder.fit_transform(data['contact'])
data['poutcome']= encoder.fit_transform(data['poutcome'])
data['month']=encoder.fit_transform(data['month'])
label =encoder.fit_transform(label)
from secretflow.data.split import train_test_split as train_test_split_fed
#划分训练集和测试集
X_train_fed, X_test_fed = train_test_split_fed(data, test_size=0.2, random_state=94)
y_train_fed, y_test_fed = train_test_split_fed(label, test_size=0.2, random_state=94)
from secretflow.ml.boost.sgb_v import (
get_classic_XGB_params,
Sgb,
)
#加载模型,开始训练
sgb = Sgb(heu)
params =get_classic_XGB_params()
params['num_boost_round']=14
params['max_depth']= 5
params['base_score']= 0.5
params['reg_lambda']= 0.1
params['learning_rate']= 0.3
params['sketch_eps']=1/10 #1/10 means maximum number of bins = 10
# only effective for sf 1.4 and above, but keeping it here is ok
params['enable_early_stop']= True
params['enable_monitor']= True
params['validation_fraction']=0.2
params['stopping_rounds']=5
params['stopping_tolerance']= 0.01
params['seed']= 94
params['first_tree_with_label_holder_feature']= True
params['save_best_model']= True
model = sgb.train(params, X_train_fed, y_train_fed)
from secretflow.device.driver import reveal
from sklearn.metrics import roc_auc_score
print(
"train set AUC score:",
roc_auc_score(reveal(y_train_fed.partitions[alice].data), reveal(model.predict(X_train_fed))),
"test set AUC score:",
roc_auc_score(reveal(y_test_fed.partitions[alice].data), reveal(model.predict(X_test_fed))),
)
仿照SGB,写出SS-XGB
import secretflow as sf
import spu
import os
from secretflow.ml.boost.ss_xgb_v import Xgb
# 网络配置
network_conf = {
"parties": {
"alice": {
"address": "alice:8000",
},
"bob": {
"address": "bob:8000",
},
},
}
party = os.getenv("SELF_PARTY", "alice")
sf.shutdown()
sf.init(
address="127.0.0.1:6379",
cluster_config={**network_conf, "self_party": party},
log_to_driver=True,
)
alice, bob = sf.PYU("alice"), sf.PYU("bob")
# 秘密分享配置
spu_conf = {
"nodes": [
{
"party": "alice",
"address": "alice:8001",
"listen_addr": "alice:8001",
},
{
"party": "bob",
"address": "bob:8001",
"listen_addr": "bob:8001",
},
],
"runtime_config": {
"protocol": spu.spu_pb2.SEMI2K,
"field": spu.spu_pb2.FM128,
"sigmoid_mode": spu.spu_pb2.RuntimeConfig.SIGMOID_REAL,
},
}
spu = sf.SPU(spu_conf)
import pandas as pd
import os
from secretflow.data.vertical import read_csv as v_read_csv, VDataFrame
from secretflow.data.core import partition
current_dir = os.getcwd()
data = v_read_csv(
{alice:f"{current_dir}/bank_0-7.csv",bob:f"{current_dir}/bank_8-16.csv"},
keys="id",
drop_keys="id",
)
alice_y_pyu_object = alice(lambda path: pd.read_csv(path, index_col = 0))(f"{current_dir}/bank_y.csv")
label = VDataFrame(partitions={alice:partition(alice_y_pyu_object)})
# from data descryptions we know we need to encode data
from secretflow.preprocessing import LabelEncoder
encoder =LabelEncoder()
data['job']= encoder.fit_transform(data['job'])
data['marital']=encoder.fit_transform(data['marital'])
data['education']= encoder.fit_transform(data['education'])
data['default']=encoder.fit_transform(data['default'])
data['housing']= encoder.fit_transform(data['housing'])
data['loan']=encoder.fit_transform(data['loan'])
data['contact']= encoder.fit_transform(data['contact'])
data['poutcome']= encoder.fit_transform(data['poutcome'])
data['month']=encoder.fit_transform(data['month'])
label =encoder.fit_transform(label)
from secretflow.data.split import train_test_split as train_test_split_fed
X_train_fed, X_test_fed = train_test_split_fed(data, test_size=0.2, random_state=94)
y_train_fed, y_test_fed = train_test_split_fed(label, test_size=0.2, random_state=94)
xgb = Xgb(spu)
# 使用SPU进行训练的伪代码
params = {
'num_boost_round': 14,
'max_depth': 5,
'learning_rate': 0.3,
'sketch_eps': 0.1,
'objective': 'logistic',
'reg_lambda': 0.1,
'subsample': 1,
'colsample_by_tree': 1,
'base_score': 0.5,
}
model = xgb.train(params, X_train_fed, y_train_fed)
from secretflow.device.driver import reveal
from sklearn.metrics import roc_auc_score
print(
"train set AUC score:",
roc_auc_score(reveal(y_train_fed.partitions[alice].data), reveal(model.predict(X_train_fed))),
"test set AUC score:",
roc_auc_score(reveal(y_test_fed.partitions[alice].data), reveal(model.predict(X_test_fed))),
)
相比于SGB,SS-XGB精度了提高了些,但提升有限。
如果是Alice拥有15个特征和标签,而Bob只拥有1个特征呢?
SGB-15个特征:
SS-XGB(15个特征):
当Alice和Bob的特征分布较为均衡时(各8个特征),两种模型的性能相近,但SS-XGB在测试集上略优于SGB。
当特征在Alice处高度集中时(Alice 15个,Bob 1个),SGB的性能在训练集和测试集上都有显著提升,而SS-XGB的提升不明显。这表明SGB可能更能有效利用不均匀分布的特征进行学习。