隐语课程学习笔记7- SGB、SS-XGB算法原理及编程实战

        本周开启隐语课程的第七节,关于纵向多方协同的集成树模型XGB算法的学习,隐语提供了基于两种不同加密协议实现的算法,一种是基于MPC协议实现SS-XGB, 一种是基于半同态加密算法实现的SecureBoost。本笔记分为两个部分来阐述,第一部分为理论篇,第二部分为实战篇。

一、隐语理论篇学习

         XGBoost是日常建模工作中特别常见的一类树模型算法,属于梯度提升树(Gradient Boosting Tree)的一种实现。它通过集成多个弱学习器(通常是决策树)来构建一个强大的预测模型。该算法具有一些突出的优点,包括:(1)高性能:具有较快的训练速度和较低的内存消耗。可以使用并行计算和近似算法等技术,提高模型的训练效率。(2)准确性:通过梯度提升的方式,它能够逐步改善模型的预测能力,获得较高的准确性。(3)鲁棒性:使用了正则化技术来控制模型的复杂度,从而减少过拟合的风险。(4)灵活性:适用于回归问题和分类问题。(4)可解释性:能够计算每个特征对模型预测的重要性。通过分析特征重要性,可以了解哪些特征对于模型的决策起到关键作用。通过分析SHAP值,可以了解每个特征如何影响模型的预测结果,进而得出对模型预测的解释。

        在介绍具体的SS-XGB和SGB之前,先回顾一下明文XGBoost的算法逻辑,可以帮助理解后续对于SS-XGB和SGB的算法原理。

以下是XGBoost算法的主要原理:

1. 梯度提升框架

        XGBoost基于梯度提升框架,该框架通过逐步构建一系列决策树来提高模型性能。每一

        棵新树都是在前面所有树的基础上,针对当前模型的残差(即预测误差)进行训练的。

2. 目标函数

        XGBoost优化的是一个目标函数,包含两个部分:

                损失函数:度量模型预测值与真实值之间的差异。
                正则化项:控制模型复杂度,防止过拟合。       

        目标函数的形式如下:

        其中,L是损失函数,是前 t−1棵树的预测值,ft是第t棵树,Ω是正则化项。

3. 二阶泰勒展开

        为了方便优化目标函数,XGBoost使用了二阶泰勒展开,将目标函数近似为一个二次函

        数。这使得优化过程更高效:

        其中,gi和 hi分别是损失函数的一阶和二阶导数(梯度和Hessian矩阵)。

4. 树的结构

        XGBoost通过贪心算法来构建决策树。它在每一步都选择能够最大化增益的分裂点。增

        益的计算方式考虑了损失函数的梯度和Hessian,以及正则化项:

        其中, GL和 HL是左子节点的梯度和Hessian,GR 和 HR是右子节点的梯度和

        Hessian,λ 和 γ是正则化参数。

5. 学习率和采样
        学习率:在每次迭代后对树的输出乘以一个小于1的因子,以降低每棵树的影响,从而

                      提高模型的鲁棒性。
        子样本采样:在每次迭代时,随机选择部分样本来构建树,类似于随机森林中的样本采

                      样,能减少过拟合。

6. 特征列抽样

        除了对样本进行采样外,XGBoost还对特征进行采样。每次分裂时随机选择部分特征进

        行分裂,这进一步减少了过拟合并提高了计算效率。

7. 并行化
        XGBoost通过并行化提高了计算效率。在构建树的过程中,可以并行计算每个分裂点的

        增益,并选择最佳分裂点。

8. 缺失值处理
        XGBoost能够自动处理数据中的缺失值。对于每个分裂点,XGBoost会分别计算缺失值

        分配到左子节点和右子节点的增益,并选择最优分配方式。

        SS-XGB实现了XGB的经典功能,面向纵向数据切分场景,采用MPC协议进行密态计算,无信息泄露,可证安全。由于依赖于MPC,因此适用于网络条件相对较好的场景。

        SS-XGB使用秘密分享计算分裂增益值和叶权重。使用秘密分享协议提供的加/乘等操作来实现安全的多方联合计算。特别需要关注的问题是:在计算分桶加和时,不泄漏任何样本分布相关的信息。可以看到,SS方式实现的XGB,一阶、二阶梯度信息以及数据分布信息都实现了安全保护。向量𝑆中标记为1的样本是被选中的样本需要加和,0相反。为了保证样本分布不泄漏,这个向量也是通过秘密分享协议保护的。在秘密分享协议的保护下,计算向量𝑆和梯度向量的内积,即可得到梯度在分桶内的累加和。参考:Large-Scale Secure XGB for Vertical Federated Learning

        而SecureBoost(SGB)是来自微众团队研发的算法,吸收了XGB和Lightgbm的一些优势。采用同态加密算法来保护标签数据。在网络条件受限的情况下,具备一定的优势,对算力资源有一定的要求。在隐语中,使用HEU设备进行密态计算支持,另外支持了一些高级训练功能,包括lightGBM风格(叶子优先和GOSS)训练,仅使用标签持有者的数据训练第一棵树等。参考:SecureBoost: A Lossless Federated Learning Framework

另外,针对secureboost,需要提一下其后续的迭代版本secureboost+,引入了更多的性能提升项,值得参考SecureBoost+ : A High Performance Gradient Boosting Tree Framework forLarge Scale Vertical Federated Learning

在这篇文章中,引入了梯度打包机制、混合树构建等新的训练优化机制,大幅提升了计算性能。

        对于SS-XGB和SGB等多方协同树模型的使用,与明文模型的使用,基本是一致的,包括数据的准备、训练参数设置及执行,以及对训练完成的模型进行指标评估和预测使用。

        SS-XGB,需要准备SPU设备,这个例子中设置的是三个参与方。参数设置与明文模型基本一致。模型评估需要先将预测结果进行reveal成明文后再进行计算。

        SGB则是在两方下执行,包括公私钥生成对象sk_keeper,以及使用公钥计算方evaluators。

        两种实现在设备初始化存在差异,需要注意。SS-XGB基于SPU设备,采用MPC协议。SGB基于HEU设备,隐语提供了OU、Paillier等实现,由标签持有方决定密钥持有方。

        SS-XGB与SGB目前在参数设置上存在一定的差异,SGB由于吸收了XGB和lightGBM的更多功能点,引入了更多的训练参数设置。

        SS-XGB预测结果为MPC碎片形态,以各方持有分片的形式保存模型,因此单独的任意一方都无法知晓模型的任何信息。而SGB是标签持有方以明文形式获得预测结果,各方持有部分明文模型参数(分裂点、分裂阈值信息),叶子权重一般是存在标签方。

        另外,隐语也给出了将经典明文算法改造成MPC算法的要点。主要是各方对自身特征进行处理分桶记录分桶结果信息,然后采用MPC计算分桶对应的一阶梯度和以及二阶梯度和。最佳分裂点的计算也采用MPC进行。特征持有方获得真实分裂点。各方计算单方已知信息到MPC整合。叶子节点权重计算也采用MPC除法算子等。预测结果也是各方先计算一个中间结果,再进行MPC整合。实现全流程的安全保护。

        对于明文模型到联邦模型SGB的改造,涉及同态加密。以标签方为保护中心,在标签方计算一阶梯度、二阶梯度,参与方持有特征的分桶求和在参与方以同态密文计算,分裂点计算在标签方计算。节点的样本分布、预测结果整合都在标签方进行。从实现上来看,标签方的权力和信息会更多,因此相对于SS-XGB来说,安全性较弱一些。高密场景建议使用SS-XGB。

        针对SS-XGB和SGB组件的封装和调用,也给出了一些示例,整理复杂度还好,找到对应模块,修改对应模块的代码功能,再进行单元测试覆盖。

        得益于隐语对设备抽象(包括PYU\SPU\HEU),采用分层架构,对密码原语进行针对性算子优化,将明文模型改造成联邦或者基于mpc协议的密文模型,整体实现起来对于开发者还是比较友好的。

二、隐语SGB以及SS-XGB实战篇

执行代码如下:

1. 首先初始化集群

import secretflow as sf
import spu
import os

network_conf = {  # 定义网络配置字典,包含参与方的地址信息
    "parties": {
        "alice": {
            "address": "alice:8000",  # Alice的地址
        },
        "bob": {
            "address": "bob:8000",  # Bob的地址
        },
    },
}

party = os.getenv("SELF_PARTY", "alice")
sf.shutdown()
sf.init(  # 初始化secretflow集群
    address="127.0.0.1:6379",
    cluster_config={**network_conf, "self_party": party},  # 集群配置,包括网络配置和当前参与方
    log_to_driver=True,
)

这一步可能因为网络的原因会出现失败,多试几次。

2. 配置HEU设备信息以及SPU设备信息,为后续计算做准备

alice, bob = sf.PYU("alice"), sf.PYU("bob")  # 创建alice和bob的PYU对象

# 配置SPU设备信息
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,  # 使用semi2k协议
        "field": spu.spu_pb2.FM128,  # 采用128bit环
        "sigmoid_mode": spu.spu_pb2.RuntimeConfig.SIGMOID_REAL,
    },
}

# 初始化HEU设备信息
heu_config = {
    'sk_keeper': {'party': 'alice'},  # 保密保管者为Alice
    'evaluators': [{'party': 'bob'}],  # 评估者为Bob
    'mode': 'PHEU',
    'he_parameters': {
        # ou是一个快速加密方案,安全性与paillier相当。
        'schema': 'ou',  # 加密模式为ou
        'key_pair': {
            'generate': {
                # 位数应为2048,提供足够的安全性。
                'bit_size': 2048,  # 密钥位数
            },
        },
    },
    'encoding': {
        'cleartext_type': 'DT_I32',  # 明文类型为32位整数
        'encoder': "IntegerEncoder",  # 编码器类型为整数编码器
        'encoder_args': {"scale": 1},  # 编码器参数设置
    },
}

heu = sf.HEU(heu_config, spu_conf['runtime_config']['field'])  # 使用heu_config和字段类型来初始化HEU对象

3. 加载数据

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()

# 将Alice和Bob的数据加载为垂直数据结构
data = v_read_csv(
    {alice: f"{current_dir}/bank_0_8.csv", bob: f"{current_dir}/bank_8_16.csv"},
    keys="id",
    drop_keys="id",
)

# 加载Alice的标签数据
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)})

4. 数据预处理,对数据做label encoder,处理成数值编码,适合后续的heu以及spu的模型学习。

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)  # 对标签数据进行编码处理

5. 对数据进行切分处理,包括特征和标签

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)

6. 基于heu进行xgb模型初始化、参数配置及模型训练

from secretflow.ml.boost.sgb_v import (
    get_classic_XGB_params,
    Sgb,  # 从sgb_v模块中导入Sgb类
)

sgb = Sgb(heu)  # 使用heu对象初始化Sgb类

params = get_classic_XGB_params()  # 调用获取经典XGBoost参数的函数,初始化参数字典
params['num_boost_round'] = 14  # 设定boosting轮数
params['max_depth'] = 5  # 设定每棵树的最大深度
params['base_score'] = 0.5  # 基础得分
params['reg_lambda'] = 0.1  # L2正则化项权重
params['learning_rate'] = 0.3  # 学习率
params['sketch_eps'] = 1 / 10  # sketch_eps参数,控制最大分箱数为10
params['enable_early_stop'] = True  # 启用早停策略
params['enable_monitor'] = True  # 启用监控模式
params['validation_fraction'] = 0.2  # 验证集比例
params['stopping_rounds'] = 5  # 早停轮数
params['stopping_tolerance'] = 0.001  # 早停容忍度
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)  # 使用参数训练模型,得到训练后的模型对象

这里我设置了早停容忍度为0.001,避免出现早停,也是为了后续可以和SS-XGB进行对比。

模型迭代

迭代14轮停止

7. 模型评估

from secretflow.device.driver import reveal  # 从device.driver模块中导入reveal函数,用于揭示数据
from sklearn.metrics import roc_auc_score  # 导入sklearn中的ROC AUC评估指标

# 我们在明文中揭示并查看评估分数,但有更安全的替代方案

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))),
)

8.接下来,用SS-XGB设置同样的参数进行运行,跑同样的流程

from secretflow.ml.boost.ss_xgb_v import Xgb

 
spu = sf.SPU(spu_conf)
 
# 基于SPU初始化Xgb对象
ss_xgb = Xgb(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 = ss_xgb.train(params, X_train_fed, y_train_fed)

用SS-XGB跑,明显性能要差很多,运行了8分44s。而同样参数的sgb,只需要30s,效率差距很明显。

9. SS-XGB模型评估

从实际评估结果看,SGB和SS-XGB的准确率基本上持平,SS-XGB略微好一点点,test-auc可以到0.9066.

  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值