【隐语实现】XGB算法与SGB算法开发实践

隐语纵向树模型原理及工程实现介绍

讲师:邹沛成 (帅气小哥哥)

学习链接

一、纵向数据分割场景和树模型

1. 什么是纵向树模型

  • 常规的树模型是不区分横向和纵向概念的,纵向树模型就是基于纵向分割数据集训练的决策树模型

  • 纵向数据集

    一般来自于前置步骤特征隐私求交得到。

    ​ 特征来自于各个参与方,只有一方拥有标签。各个参与方不期望将特征以明文的形式直接传输给其他参与方,也不希望泄漏相关重要信息。

在这里插入图片描述

2. 纵向树模型的使用场景

  • 联合建模:不同特征维度、合作得到的联邦模型比单方训练的模型效果更优
  • 树模型:可解释性
    在这里插入图片描述

3. 隐语提供的纵向树模型算法

​ 根据安全程度不同,SF实现了以下两种:

  • 可证安全算法SS-XGB(SecretShared eXtremeGradientBoost)
    • 基于秘密分享
    • 实现了XGB的经典功能。
    • 采用MPC 进行密态计算,无信息泄漏,安全性保证基于数学证明。
    • 网络条件好时效率高。
  • 纵向联邦算法SGB(SecureGradientBoost)
    • 基于联邦学习和同态加密,即明密文混合计算
    • 吸收了部分来自XGB 和 lightGBM功能。
    • 采用同态加密来保护标签数据。
    • 非可证安全
    • 计算量大,算力高时效率高。但是在网络延迟较高时,性能优势明显。

4. XGBoost的一阶导数 g g g和二阶导数 h h h

​ 在XGBoost(eXtreme Gradient Boosting)中, g g g h h h分别表示一阶和二阶导数(即梯度和Hessian),用于加速模型的训练过程。这些值是损失函数相对于预测值的导数

  • 说明

    ​ 假设我们有一个损失函数 L ( y , y ^ ) L(y, \hat{y}) L(y,y^),其中 y y y是实际值, y ^ \hat{y} y^是预测值。

    • 梯度(Gradient, g g g: 梯度是损失函数对预测值的一阶导数,反映了当前预测值与实际值之间的差距。它用于调整模型的参数,使预测值逐渐接近实际值。

    g i = ∂ L ( y i , y i ^ ) ∂ y ^ i g_i = \frac{\partial L(y_i, \hat{y_i})}{\partial \hat{y}_i} gi=y^iL(yi,yi^)

    • Hessian( h h h: Hessian是损失函数对预测值的二阶导数,反映了梯度的变化情况。它用于调整学习率,从而使梯度下降更平滑、更稳定。

    h i = ∂ L 2 ( y i , y i ^ ) ∂ y ^ i 2 h_i=\frac{\partial L^2(y_i, \hat{y_i})}{\partial \hat{y}_i^2} hi=y^i2L2(yi,yi^)

  • 平方误差损失下的梯度计算
    L ( y , y ^ ) = 1 2 ( y − y ^ ) 2 g i = − ( y i − y ^ ) = y ^ − y i h i = 1 L(y, \hat{y}) = \frac{1}{2}(y - \hat{y})^2\\ g_i=-(y_i-\hat{y})=\hat{y}-y_i \\ h_i=1 L(y,y^)=21(yy^)2gi=(yiy^)=y^yihi=1

二、隐语纵向树模型

原生XGBoost使用教程:https://xgboost.readthedocs.io/en/stable/tutorials/model.html

1. 使用方法

​ 和传统机器学习一致,主要包括three steps:

  1. 准备:环境和数据集
  2. 训练:参数设置和执行
  3. 模型评估:计算指标和采取进一步决策

2. SS-XGB介绍

教程链接:https://www.secretflow.org.cn/zh-CN/docs/secretflow/v1.6.1b0/user_guide/mpc_ml/decision_tree

论文链接:https://arxiv.org/pdf/2005.08479

  • 单棵树分裂的主要过程如下:

    • 预计算:根据损失函数定义、样本标签、当前预测值,计算每个样本可以求得其一阶导 g i g_i gi和二阶导 h i h_i hi

    • 结点分裂:通过枚举所有分裂方案,选出带来最优增益值的方式执行分裂。分裂方案包含分裂特征和分裂阈值,可以将当前结点样本集合 I I I分裂为左子树样本集合 I L I_L IL 和右子树样本集合 I R I_R IR, 并由如下公式计算出此分裂方案的增益值:
      L s p l i t = 1 2 [ ( ∑ i ∈ I L g i ) 2 ∑ i ∈ I L h i + λ + ( ∑ i ∈ I R g i ) 2 ∑ i ∈ I R h i + λ − ( ∑ i ∈ I g i ) 2 ∑ i ∈ I h i + λ ] − γ L_{split} = \frac{1}{2}[\frac{(\sum_{i\in I_L}g_i)^2}{\sum_{i\in I_L}h_i+\lambda}+\frac{(\sum_{i\in I_R}g_i)^2}{\sum_{i\in I_R}h_i+\lambda}-\frac{(\sum_{i\in I}g_i)^2}{\sum_{i\in I}h_i+\lambda}]-\gamma Lsplit=21[iILhi+λ(iILgi)2+iIRhi+λ(iIRgi)2iIhi+λ(iIgi)2]γ
      其中, λ \lambda λ γ \gamma γ分别为叶节点数和叶节点权重的惩罚因子。

    • 权重计算:由落入该结点的样本计算得到
      w j ∗ = − ∑ i ∈ I j g i ∑ i ∈ I j h i + λ w_j^*=-\frac{\sum_{i\in I_j}g_i}{\sum_{i\in I_j}h_i+\lambda} wj=iIjhi+λiIjgi

  • 回归问题和分类问题的训练流程是相同的,除了:

    • 损失函数的选择(回归-MSE,分类-Logloss)。
    • 分类问题需要将预测值通过sigmoid函数转化为概率。
  • 使用秘密分享计算分裂增益值和叶权重。

    ​ 根据秘密分享协议提供的加/乘等操作来实现安全的多方联合计算

  • 特别需要关注的问题是:如何在计算分桶加和时,不泄漏任何样本分布相关的信息。

    对于每一个分桶,XGBoost会计算该分桶中所有样本的梯度和Hessian值的加和。这些统计量用于后续的增益计算。其中,梯度加和是指所有样本在该分桶内的梯度值之和,Hessian加和是指所有样本在该分桶内的Hessian值之和。

  • SF通过引入一个密态下的向量 S \mathbf{S} S来解决这个问题。

    ​ 例如,这里分裂点选择了Age的15。那么,在计算左子节点的梯度 G L G_L GL就需要拿到 g 1 g_1 g1 g 4 g_4 g4的值,右子节点 G R G_R GR要拿到 g 2 g_2 g2 g 3 g_3 g3 g 5 g_5 g5的梯度。

    ​ 如何不期望参与方得知哪些样本用于这次分裂,于是使用一个映射向量 S = [ 1 , 0 , 0 , 1 , 0 ] T \mathbf{S} = [1,0,0,1,0]^T S=[1,0,0,1,0]T,标记为1的样本是被选中的样本需要加和,0相反。因此,为了保证样本分布不泄漏,这个向量需要通过秘密分享协议保护。在秘密分享协议的保护下,计算向量 S \mathbf{S} S和梯度向量的内积,即可得到梯度在分桶内的累加和。

在这里插入图片描述

​ 向量𝑆中标记为1的样本是被选中的样本需要加和,0相反

  • 准备环境和数据

    • 加载纵向数据:https://www.secretflow.org.cn/zh-CN/docs/secretflow/v1.6.1b0/tutorial/FedDataFrameDataLoader
    • SPU配置:https://www.secretflow.org.cn/zh-CN/docs/secretflow/v1.6.1b0/tutorial/spu_basics
  • 设置训练参数:因为XGBoost的参数比较多,需要精心设置

    https://www.secretflow.org.cn/zh-CN/docs/secretflow/v1.6.1b0/source/secretflow.ml.boost.ml.boost.ss_xgb_v#secretflow.ml.boost.ss_xgb_v.Xgb.train

    ​ 点击上面链接,可以看到常用的参数说明,我这里截取了一些:

在这里插入图片描述

  • 模型评估

    • 不安全的评估方法:直接将预测结果和标签进行reveal,会泄漏标签。
    • 安全的评估方法:使用secretflow.stats.BiClassificationEval,只在拥有标签的参与方上进行,因为评估需要标签计算。

3. SGB介绍

SecureBoost

https://www.secretflow.org.cn/zh-CN/docs/secretflow/v1.6.1b0/tutorial/SecureBoost

​ HEU 是个虚拟设备,每个 HEU 实例由多个参与方组成。由于 HE 是一种非对称加密算法,组成 HEU 实例的参与方分为 sk_keeper 和 evaluator, sk_keeper 有且仅有一个参与方,其拥有私钥和公钥,俱备解密、运算能力,其余参与方皆为 evaluator,仅有公钥,俱备运算能力。

  • 准备环境和数据

    • 需要设置HEU

      heu_config = {
          'sk_keeper': {'party': 'alice'},
          'evaluators': [{'party': 'bob'}],
          'mode': 'PHEU',
          'he_parameters': {
              # ou is a fast encryption schema that is as secure as paillier.
              'schema': 'ou',
              'key_pair': {
                  'generate': {
                      # bit size should be 2048 to provide sufficient security.
                      'bit_size': 2048,
                  },
              },
          },
          'encoding': {
              'cleartext_type': 'DT_I32',
              'encoder': "IntegerEncoder",
              'encoder_args': {"scale": 1},
          },
      }
      
      heu = sf.HEU(heu_config, cluster_def['runtime_config']['field'])
      
      • 有标签的一方设置为sk_keeper,其他参与方对应evaluator
  • 设置具体参数

  • 模型评估和保存

    模型是分布式保存的,我们将保存到多个参与方,并从多个参与方中加载。

4. 对比

SS-XGBSGB
准备设备和数据基于MPC协议,主要使用SPU设备基于HE(OU/Paillier)和联邦学习,主要使用HEU设备(标签拥有方作为sk_keeper)
训练参数支持经典XGB功能,10个参数参数更为丰富,吸收了XGB和LightGBM的特点。共24个参数,训练参数有18个。
模型保存密态模型,各方持有分片,不联合,无法得到任何信息。联邦模型,各方持有部分明文参数:分裂点、叶权重
预测结果MPC密文(SPUObject)标签持有方可以看到预测的明文信息,

5. 隐语优势

  • 设备抽象

    底层提供的PYU让使用者无需感知MPC协议(ABY3、CHEETAH等),提供的HEU让使用者无需感知同态加密协议

  • 分层架构—算法,Python实现

  • 分层架构—原语,对瓶颈算子进行优化

  • 共享共建:众人拾柴火焰高

三、原理和实现-从理论到工程

1. SS-XGB从经典算法到MPC算法

Paper:(2021)https://arxiv.org/pdf/2005.08479,可证安全树算法

(1) 确定保护的数据部分

​ 确定哪些数据在秘密分享下进行,其中任何数据都被MPC保护

  • 数据源(标签和特征)
  • 数据处理中间特征的顺序
  • 数据样本在节点上的分布
(2) 准备安全原语

​ 设计一些用于MPC计算的算子,例如秘密分享、初始化、加法、乘法、Sigmoid/Argmax

(3) 改造数据结构和算法
原生XGBoost步骤SS-XGBoost(纵向)
各方进行特征分桶每个参与方在本地进行特征分桶,记录各个特征对应的分裂点和分箱结果
计算一阶导数 G G G/二阶导数 H H H在MPC下计算各个样本的 G G G H H H
计算每个分裂点的评分,选择最佳分裂点在MPC中计算
同步分裂点产生新节点各方计算(特征持有方获得真实分裂点,其他获得dummy信息)
计算下一层每个节点样本分布各方计算单方的已知信息,到MPC聚合
叶子节点权重计算在MPC中计算
预测一个样本在哪一个叶子节点各方计算单方的已知信息,到MPC聚合

2. SGB从经典算法到联邦算法

Paper:(2019) https://arxiv.org/pdf/2005.08479

  • SF的SGB结合了secureboost的训练部分和ss-xgb的预测部分
  • 没有精度损失,主要采用联邦学习和同态加密的方式保护隐私
(1) 确定保护的数据部分

​ 确定哪些数据在加密下进行,其中任何数据都被同态加密保护

  • 数据源:重点保护标签

  • 数据处理中间特征的顺序

    不保护样本在节点上的分布

(2) 准备安全原语

​ 设计一些用于同态加密的算子:例如选择半同态加密Paillier主要需要实现加法算子

(3) 改造数据结构和算法
原生XGBoost步骤SS-XGBoost(纵向)
各方进行特征分桶每个参与方在本地进行特征分桶,记录各个特征对应的分裂点和分箱结果
计算一阶导数 G G G/二阶导数 H H H在标签持有方计算
计算每个分裂点的评分,选择最佳分裂点在标签持有方计算
同步分裂点产生新节点各方计算(特征持有方获得真实分裂点,其他获得dummy信息)
计算下一层每个节点样本分布各方计算单方的已知信息,到标签持有方聚合
叶子节点权重计算在标签持有方计算
预测一个样本在哪一个叶子节点各方计算单方的已知信息,到标签持有方聚合

3. 开发实战

(1) 组件封装

如何在组件中封装算法

  • 定义组件主体,方便外部调用:确定组件封装的功能和信息
    • 名字
    • 版本(用于组件升级)

在这里插入图片描述

  • 定义参数和IO:根据算子已经实现的功能,定义组件参数和输入输出

    (新参数定义、旧参数更改)

    在这里插入图片描述

  • 定义调用方法:建立安全设备,调用引擎方法和存储结果

在这里插入图片描述

(2) 算法修改

如何增加训练参数、修改功能

  • 阅读代码:确定大的结构,熟悉主要的算法流程

    例如sgb的主要代码:https://github.com/secretflow/secretflow/blob/v1.6.1b0/secretflow/ml/boost/sgb_v/factory/factory.py#L157

    • 树的训练:(GlobalOrdermapBooster,https://github.com/secretflow/secretflow/blob/v1.6.1b0/secretflow/ml/boost/sgb_v/factory/booster/global_ordermap_booster.py#L135)

      关键步骤:

      1. 首先,遍历num_boost_round用于生成每棵树
      2. 之后拟合得到当前迭代的树
      3. 将该树插入到已经训练好的树中,这里的model_builder实际调用的是SgbModel_insert_distributed_tree(https://github.com/secretflow/secretflow/blob/v1.6.1b0/secretflow/ml/boost/sgb_v/model.py#L58C9-L58C33)来插入已经训练好的树。
      4. 最后返回得到的SgbModel

    在这里插入图片描述

  • 修改算法和对应的模块:明确要修改的算法模块,找到对应的代码模块

    • 例如希望实现goss功能

      GOSS(Gradient-based One-Side Sampling)的核心思想是在训练过程中对样本进行有策略的采样,以减少计算量,同时尽可能保留模型的准确性。具体来说,GOSS在每次迭代中选择一部分样本基于它们的梯度值,分为两个部分:一部分是梯度值较大的样本,另一部分是随机选择的样本。梯度值较大的样本通常对模型的损失贡献较大,因此优先选择这些样本可以加速收敛。

    • 步骤:

      1. 找到采样模块

        implement core functions in actors在actor中定义相关核心实现

        在这里插入图片描述

      2. Modify params in corresponding component修改对应的参数

      3. 在secretflow/ml/boost/sgb_v/core/params.py中添加参数

  • 修改单元测试,覆盖修改部分

(3) 模型导出
  • 阅读代码:确认是否提供了模型保存的方法,当前SF只为SGB提供了相关代码。

    • https://github.com/secretflow/secretflow/blob/v1.6.1b0/secretflow/ml/boost/sgb_v/model.py#L143
      • 保存通用信息
      • 保存叶子节点权重
      • 保存split tree
  • 按照接口存读模型

    https://www.secretflow.org.cn/zh-CN/docs/secretflow/v1.6.1b0/tutorial/SecureBoost

    注意,模型是分布式的,我们将保存到多个参与方,并从多个参与方中加载。

    • 模型保存

      # each participant party needs a location to store
      saving_path_dict = {
          # in production we may use remote oss, for example.
          device: "./" + device.party
          for device in v_data.partitions.keys()
      }
      
      r = model.save_model(saving_path_dict)
      wait(r)
      
    • 模型加载

      # alice is our label holder
      model_loaded = load_model(saving_path_dict, alice)
      fed_yhat_loaded = model_loaded.predict(v_data, alice)
      yhat_loaded = reveal(fed_yhat_loaded.partitions[alice])
      
      assert (
          yhat == yhat_loaded
      ).all(), "loaded model predictions should match original, yhat {} vs yhat_loaded {}".format(
          yhat, yhat_loaded
      )
      
  • 自定义模型序列化方法

    • 理解模型数据类型,区分SPUObject/PYUObject 和所属方

    • 自定义哪一方获取什么样的数据https://github.com/secretflow/secretflow/blob/v1.6.1b0/secretflow/ml/boost/sgb_v/core/distributed_tree/distributed_tree.py#L96

      def to_dict(self) -> Dict:
          """Serialize to a Dictionary. Note this dict contain PYUObjects, cannot dump to file at this level."""
          split_tree_dict = {
          device: device(lambda t: t.to_dict())(tree)
          for device, tree in self.split_tree_dict.items()
          }
          return {
          'split_tree_dict': split_tree_dict,
          'leaf_weight': self.label_holder(lambda arr: arr.tolist())(
          self.leaf_weight
          ),
          'label_holder': self.label_holder,
          'partition_column_counts': self.partition_column_counts,
          }
      

      序列化为一个字典,但是由于split_tree_dict中包含PYUObject,不能直接进行pickle.dump

四、实践

  • 数据集:

    银行营销数据集https://archive.ics.uci.edu/dataset/222/bank+marketing

    将原始数据集垂直切分,因为我们要进行纵向建模。

    • Bank_0_8.csv:id列+8个特征
    • Bank_8_16.csv:id列+8个特征
    • Bank_y.csv:id列+标签

    ​ 然后,将Bank_0_8.csv和Bank_y.csv放到参与方alice的数据目录下,将Bank_8_16.csv放到Bob的数据目录下。

1. 传统建模流程—single play

​ 利用本方已经拥有的数据进行建模,直接使用sklearn和原生XGBoost

  • 数据加载
# XGB alice single play
from sklearn.model_selection import train_test_split
import pandas as pd
import os
import xgboost as xgb
from sklearn.metrics import roc_auc_score

def load_pandas_data(path):
    return pd.read_csv(path, index_col=0)

current_dir = os.getcwd()

data = load_pandas_data(f"{current_dir}/bank_0_8.csv")
label = load_pandas_data(f"{current_dir}/bank_y.csv")

​ 这里的label中为yes的大概有5289,为no的样本大概有39922个,是一个不平衡数据集。

数据前五行差不多是这样的:

在这里插入图片描述

  • 特征工程

​ 可以看到有类别型特征,因此我们使用labelEncoder对数据进行处理。

from sklearn.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'])
X = data.to_numpy()
y = encoder.fit_transform(label)

​ 拆分一下数据集,这里设置stratify。我们把数据集拆分了三份:训练集、验证集和测试集。

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=94, stratify=y
)
X_train, X_val, y_train, y_val = train_test_split(
    X_train, y_train, test_size=0.2, random_state=94, stratify=y_train
)
  • 模型训练

    # Use "hist" for constructing the trees, with early stopping enabled.
    clf = xgb.XGBClassifier(
        tree_method="hist",
        n_estimators=20,
        max_depth=5,
        learning_rate=0.3,
        max_bin=10,
        early_stopping_rounds=5,
        base_score=0.5,
        eval_metric="auc",
        reg_lambda=0.1,
        min_child_weight=0,
    )
    clf.fit(X_train, y_train, eval_set=[(X_val, y_val)])
    
    ......
    [14]	validation_0-auc:0.67392
    [15]	validation_0-auc:0.67285
    fit time = 6.161916017532349s
    train set AUC score:  0.7141216984329877 test set AUC score:  0.6913379647330238 num_trees:  12
    
    • 速度很快,总共用时约 6s。
    • 在验证集上的auc约为0.69

2. 联合建模SecureBoost—double play

使用SF的1.6.1b0版本

  • 联合建模,特征维度增加,模型效果一般会有所提升。
(1) 环境配置
  • Ray cluster config
import secretflow as sf

cluster_config = {
    "parties": {
        "alice": {
            # replace with alice's real address
            "address": "alice方的rayfed通信地址",
            "listen_addr": "alice方的rayfed监听地址"

        },
        "bob": {
            "address": "bob方的rayfed通信地址",
            "listen_addr": "bob方的rayfed监听地址"
        },
    },
    'self_party': 'alice'
}

sf.shutdown()
sf.init(address="alice的ray head地址", cluster_config=cluster_config)

bob同理,不过要替换ray head地址

  • spu

    import spu
    
    cluster_conf = {
        "nodes": [
            {
                "party": "alice",
                "address": "alice的spu地址"
            },
            {
                "party": "bob",
                "address": "bob的spu地址"
            },
        ],
        "runtime_config": {
            "protocol": spu.spu_pb2.SEMI2K,
            "field": spu.spu_pb2.FM128,
            "sigmoid_mode": spu.spu_pb2.RuntimeConfig.SIGMOID_REAL,
        },
    }
    spu_obj = sf.SPU(
        cluster_def=cluster_conf,
        link_desc={
            "connect_retry_times": 60,
            "connect_retry_interval_ms": 1000
        },
    )
    
  • HEU配置

    • sk_keeper:设置为标签拥有方
    • ou:一种比较快的半同态加密协议
    import spu
    heu_config = {
        'sk_keeper': {'party': 'alice'},
        'evaluators': [{'party': 'bob'}],
        'mode': 'PHEU',
        'he_parameters': {
            # ou is a fast encryption schema that is as secure as paillier.
            'schema': 'ou',
            'key_pair': {
                'generate': {
                    # bit size should be 2048 to provide sufficient security.
                    'bit_size': 2048,
                },
            },
        },
        'encoding': {
            'cleartext_type': 'DT_I32',
            'encoder': "IntegerEncoder",
            'encoder_args': {"scale": 1},
        },
    }
    heu = sf.HEU(heu_config, spu.spu_pb2.FM128)
    
(2) 数据集加载
  • 加载特征
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()
current_dir
alice = sf.PYU("alice")
bob = sf.PYU("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"
)
data.shape
(45211, 16)

数据特征:

['age',
 'job',
 'marital',
 'education',
 'default',
 'balance',
 'housing',
 'loan',
 'contact',
 'day',
 'month',
 'duration',
 'campaign',
 'pdays',
 'previous',
 'poutcome']
  • 加载标签

    label只在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)})
    
(3) 特征工程

​ 主要对类别型特征进行编码,这里使用secretflow提供的LabelEncoder代码进行编码。

# 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)
    
(4) SecureBoost
  • 设置参数
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)

get_classic_XGB_params设置XGB参数模版

  • 开始训练
model = sgb.train(params, X_train_fed, y_train_fed)

​ 大概运行了1min多

(5) 模型评估

​ 评估在训练集和测试集上的auc。

from secretflow.device.driver import reveal
from sklearn.metrics import roc_auc_score

# we reveal and look at the evaluation score in cleartext, but there are safer alternatives

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

​ 输出:

train set AUC score:  0.9015009566774648 test set AUC score:  0.8932670395547175
  • 可以看到auc相比于单方训练有所提升,从0.79提升到0.89。

3. 联合建模SSXGB—double play

from secretflow.ml.boost.ss_xgb_v import Xgb, XgbModel
ss_xgb = Xgb(spu_obj)
model = ss_xgb.train(
    params={
        "num_boost_round": 14,
        "max_depth": 5,
        "learning_rate": 0.3,
        "objective": "logistic",
        "reg_lambda": 0.1,
        "sketch_eps": 0.1,
        "seed": 94,
    },
    dtrain=X_train_fed,
    label=y_train_fed
)

​ 不过,相比于SecureBoost,XGB的速度相对慢一些,同参数下,差不多用了5min多,速度慢了5倍。

  • How much gain does ss-XGB achieve
train set AUC score:  0.9184086164491229 test set AUC score:  0.9056833629118386

性能相对高一些哦,SGB的test auc为0.89,而SS-XGB的test auc为0.9

4. 如果一方有特征,一方有标签会怎么样

​ What about if Alice has 15 features while Bob has 1 feature? Can SGB and ss-XGB outperform alice’s single party XGB’s model

  • 如果特征仅在一方,那么本地计算会比纯SS要快得多。SGB的运行速度肯定会比SS-XGB快
(1) 数据准备
  • 我们把数据集合并,bank_0_8.csv和bank_8_16.csv合并,放在alice方

    >>> import pandas as pd
    >>> feat_df1 = pd.read_csv("bank_0_8.csv")
    >>> feat_df2 = pd.read_csv("bank_8_16.csv")
    >>> feat_all = pd.merge(feat_df1, feat_df2, on="id")
    
  • 数据集bank_y.csv放到bob方

(2) 运行上述代码

​ 假设模型参数一致,注意配置heu的时候把sk_keeper切换为bob,因为现在label在bob方。

SGBSS-XGB
fit time53s4min 54s
train-auc0.90186400.9184116
Test-arc0.89425160.9056858
  • 52
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值