【监督学习】XGBoost 步骤及matlab实现

XGBoost

    • XGBoost(以下仅为个人笔记,如有错误还请指出)
      • 1.算法步骤
      • 2.参数选择
      • 3.MATLAB 实现
      • 参考资料

XGBoost(以下仅为个人笔记,如有错误还请指出)

XGBoost(eXtreme Gradient Boosting)实现了机器学习中的梯度提升决策树算法(Gradient Boosting Decision Trees, GBDT),并以其出色的性能和效率在数据科学界获得了广泛的认可。XGBoost是一个强大而高效的机器学习工具,特别适合需要高准确率和处理大规模数据集的任务。无论是学术研究还是工业应用,XGBoost都是一个值得考虑的选择。以下是关于XGBoost的一些关键点:

  • 算法基础:XGBoost 基于梯度提升框架,通过迭代地添加决策树来构建模型。每棵新树都试图纠正之前所有树的预测错误,从而逐步改进模型;
  • 优化与扩展
    • XGBoost引入了正则化项以控制模型复杂性,减少过拟合的风险;
    • 支持自定义损失函数,只要损失函数是可微的;
    • 采用二阶导数信息(Hessian 矩阵)来更精确地找到最佳分裂点;
    • 包含处理缺失值的功能,自动学习缺失值的最佳方向;
    • 实现了并行与分布式计算,支持大规模数据集的训练。
  • 应用场景:XGBoost因其强大的表现力和灵活性,被广泛应用于分类、回归和排序等问题中。它在众多数据科学竞赛中表现出色;
  • 易用性:XGBoost提供了对多种编程语言的支持,包括Python、MATLAB、R、Java、Scala等,并且可以轻松集成到现有的数据流水线中。它还提供了一个直观的接口,使得用户能够快速上手并进行实验。

多棵树预测原理

1.算法步骤

开始
初始化预测值
迭代构建树
是否满足停止条件?
输出最终模型
计算一阶梯度 gi 和二阶梯度 hi
生成候选特征分裂点
选择最佳分裂点--增益最大化
生成子树并更新权重
正则化--剪枝与权重收缩
更新预测值
  1. 初始化预测值

    • 目标:设定初始预测值(通常为常数,如样本标签的均值);
    • 公式 F 0 ( x ) = arg ⁡ min ⁡ γ ∑ i = 1 n L ( y i , γ ) F_0(x) = \arg \min_{\gamma} \sum_{i=1}^{n} L(y_i, \gamma) F0(x)=argγmini=1nL(yi,γ)
      • F 0 ( x ) F_0(x) F0(x):这是模型的初始预测值。对于所有的样本点,它是一个常数值,因为这是在考虑任何特征或进行任何提升迭代之前的初步猜测;
      • arg ⁡ min ⁡ γ ​ \arg \min_{\gamma}​ argminγ:这个操作是在寻找一个能最小化后面损失函数的参数 γ γ γ 的值。换句话说,它是要找到那个使得损失函数达到最小值的 γ γ γ
      • ∑ i = 1 n L ( y i , γ ) \sum_{i=1}^{n} L(y_i, \gamma) i=1nL(yi,γ):这部分表示对所有训练样本(从 i = 1 i=1 i=1 i = n i=n i=n )计算的损失函数 L ( y i ​ , γ ) L(y_i​,γ) L(yi,γ) 的总和。这里的 y i ​ y_i​ yi 是第 i i i 个样本的真实标签,而 γ γ γ 是我们试图优化的参数,即初始预测值;
      • L ( y i ​ , γ ) L(y_i​,γ) L(yi,γ):损失函数衡量了真实值 y i y_i yi​ 和预测值 γ γ γ 之间的差异。不同的问题可能使用不同的损失函数,比如回归问题中常用的平方误差损失或者分类问题中的对数损失等,例如,对于平方损失函数,初始预测值为所有样本标签的均值: F 0 ( x ) = 1 n ∑ i = 1 n y i F_0(x) = \frac{1}{n} \sum_{i=1}^{n} y_i F0(x)=n1i=1nyi
  2. 迭代构建树(Boosting)

    • 核心思想:通过加法模型(Additive Model)逐步添加决策树,修正前一轮的残差;
    • 第 t 轮迭代步骤
      • 2.1 计算梯度:对每个样本计算损失函数的一阶梯度( g i ​ g_i​ gi )和二阶梯度( h i h_i hi ​): g i = ∂ L ( y i , F t − 1 ( x i ) ) ∂ F t − 1 ( x i ) , h i = ∂ 2 L ( y i , F t − 1 ( x i ) ) ∂ F t − 1 ( x i ) 2 g_i = \frac{\partial L(y_i, F_{t-1}(x_i))}{\partial F_{t-1}(x_i)}, \quad h_i = \frac{\partial^2 L(y_i, F_{t-1}(x_i))}{\partial F_{t-1}(x_i)^2} gi=Ft1(xi)L(yi,Ft1(xi)),hi=Ft1(xi)22L(yi,Ft1(xi))
        • g i ​ g_i​ gi:这是损失函数对当前预测值 F t − 1 ( x i ) F_{t−1}(x_i) Ft1(xi) 的一阶导数,也被称为梯度。在给定第 i i i 个样本的真实标签 y i y_i yi ​和前一棵树(或之前所有树)的预测结果 F t − 1 ( x i ) F_{t−1}(x_i) Ft1(xi) 的情况下,它衡量了损失函数随预测值变化的变化率。一阶导数帮助我们了解如何调整预测以减少误差;
        • h i h_i hi​:这是损失函数对当前预测值 F t − 1 ( x i ) F_{t−1}(x_i) Ft1(xi) 的二阶导数,可以视为曲率的一种度量。二阶导数提供了关于损失函数形状的信息,特别是它的弯曲程度,这有助于确定下一步应该移动多远才能更接近最小值,在XGBoost中,使用二阶导数来更好地近似损失函数,并进行更加精确的更新。
      • 2.2 生成候选分裂点
        • 对每个特征按分位数(如三分位数)划分候选分裂点;
        • 优化:XGBoost 采用加权分位数法,减少候选点数量,提升效率。
      • 2.3 选择最佳分裂点
        • 计算每个分裂点的 增益(Gain),选择增益最大的分裂点: Gain = 1 2 [ ( ∑ i ∈ L g i ) 2 ∑ i ∈ L h i + λ + ( ∑ i ∈ R g i ) 2 ∑ i ∈ R h i + λ − ( ∑ i g i ) 2 ∑ i h i + λ ] − γ \text{Gain} = \frac{1}{2} \left[ \frac{\left( \sum_{i \in L} g_i \right)^2}{\sum_{i \in L} h_i + \lambda} + \frac{\left( \sum_{i \in R} g_i \right)^2}{\sum_{i \in R} h_i + \lambda} - \frac{\left( \sum_i g_i \right)^2}{\sum_i h_i + \lambda} \right] - \gamma Gain=21[iLhi+λ(iLgi)2+iRhi+λ(iRgi)2ihi+λ(igi)2]γ
          • Gain \text{Gain} Gain:代表了如果按照当前考虑的特征和阈值分裂节点,模型损失函数的减少量。增益越高,说明这个分裂越有价值;
          • L L L R R R 分别代表分裂后生成的左子树和右子树中的样本集合。对于每个子集,我们计算其梯度 g i g_i gi 和二阶梯度 h i h_i hi ​的总和;
          • ∑ i ∈ L g i \sum_{i \in L} g_i iLgi ∑ i ∈ R g i \sum_{i \in R} g_i iRgi:分别是左子树和右子树中所有样本的一阶导数之和,反映了两个子树各自预测误差的方向和大小;
          • ∑ i ∈ L h i \sum_{i \in L} h_i iLhi ∑ i ∈ R h i \sum_{i \in R} h_i iRhi:分别是左子树和右子树中所有样本的二阶导数之和,用于衡量这两个子集中样本的预测误差的变化率或曲率;
          • ( ∑ i ∈ L g i ) 2 ∑ i ∈ L h i + λ \frac{\left( \sum_{i \in L} g_i \right)^2}{\sum_{i \in L} h_i + \lambda} iLhi+λ(iLgi)2 ( ∑ i ∈ R g i ) 2 ∑ i ∈ R h i + λ \frac{\left( \sum_{i \in R} g_i \right)^2}{\sum_{i \in R} h_i + \lambda} iRhi+λ(iRgi)2:这两个项分别计算了分裂后左右子树的损失减少量。这里分母加上了一个正则化参数 λ λ λ,目的是为了防止过拟合,增加分裂的质量要求,避免因为数据的小波动而做出不必要的分裂;
          • ( ∑ i g i ) 2 ∑ i h i + λ \frac{\left( \sum_i g_i \right)^2}{\sum_i h_i + \lambda} ihi+λ(igi)2 :这是分裂前整个节点的损失情况,作为对比基准;
          • 括号前的 1 2 \frac{1}{2} 21 是一个缩放因子,不影响分裂的选择,仅用于简化后续的计算;
          • 最后减去的 γ γ γ 是叶子节点的复杂度代价,也是一个正则化项,用来惩罚过于复杂的模型(即过多的叶子节点)。这有助于控制模型的复杂性,避免过拟合并提高泛化能力。
      • 2.4 生成子树
        • 根据分裂点递归生成树结构,直至达到最大深度或无法继续分裂;
        • 叶子节点权重计算 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
          • I j ​ I_j​ Ij:属于叶子节点 j j j 的样本集合。
          • w j w_j wj:这是第 j j j 个叶子节点上的预测值(权重)。对于回归问题,它可以被看作是该叶子节点对最终预测结果的贡献;对于分类问题,它代表了该叶子节点上样本属于某一类别的得分;
          • ∑ i ∈ I j g i \sum_{i \in I_j} g_i iIjgi:表示分配到第 j j j 个叶子节点的所有样本的一阶梯度之和。一阶梯度衡量了损失函数相对于当前预测的变化率,反映了模型在这些样本点上的误差方向和大小;
          • ∑ i ∈ I j h i \sum_{i \in I_j} h_i iIjhi:表示分配到第 j j j 个叶子节点的所有样本的二阶梯度之和。二阶导数提供了关于损失函数形状的信息,特别是其曲率,这有助于确定调整预测值的方向和步长;
          • λ λ λ :这是一个正则化参数,用于控制模型的复杂度,防止过拟合。通过在分母中添加 λ λ λ,可以避免模型过于依赖训练数据中的噪声或异常值,从而提高模型的泛化能力。
  3. 正则化

    • 目标:防止过拟合,提升泛化能力;
    • 策略
      • 权重收缩(Shrinkage):每棵树的贡献乘学习率 η η η(如 0.1 ): F t ( x ) = F t − 1 ( x ) + η ⋅ f t ( x ) F_t(x) = F_{t-1}(x) + \eta \cdot f_t(x) Ft(x)=Ft1(x)+ηft(x)
        • F t ( x ) F_t(x) Ft(x):这是在第 t t t 轮迭代后的模型输出。随着每次迭代,模型会逐步学习到数据中的模式,并试图减少预测误差;
        • F t − 1 ( x ) F_{t-1}(x) Ft1(x):这是前一轮迭代结束时的模型输出。也就是说,这是在没有加上本轮新学到的信息之前的模型状态;
        • η η η :这是学习率(也称为收缩率或步长),通常是一个介于 0 和 1 之间的数。学习率控制了每棵树对最终结果的影响程度。较小的学习率意味着需要更多的树来拟合训练数据,但通常可以带来更好的泛化能力和更平滑的决策边界;
        • f t ( x ) f_t(x) ft(x) :这是在第 t t t 轮迭代中训练出的新树的预测结果。每一次迭代都致力于纠正之前所有树的预测错误,通过聚焦于那些先前模型预测不准确的数据点,从而逐步改进模型的整体性能。
      • 列采样(Column Subsampling):随机选择部分特征进行分裂;
      • 剪枝(Pruning):根据增益阈值 γ γ γ 剪除低增益的分支。
  4. 更新预测值

    • 将新生成的树 f t ​ ( x ) f_t​(x) ft(x) 加入模型: F t ( x ) = F t − 1 ( x ) + η ⋅ f t ( x ) F_t(x) = F_{t-1}(x) + \eta \cdot f_t(x) Ft(x)=Ft1(x)+ηft(x)
  5. 停止条件

    • 预设轮次:达到最大迭代次数(如 100 轮);
    • 早停(Early Stopping):验证集误差连续 k k k 轮不再下降;
    • 性能阈值:损失函数降至目标值以下。

2.参数选择

参数类型核心参数调优优先级作用方向
基础控制η (学习率)控制模型收敛速度与稳定性
复杂度控制max_depth(树复杂度参数)防止过拟合/欠拟合
min_child_weight(最小样本权重和)控制叶子节点最小样本权重
正则化λ (L2 正则化)约束权重,抑制过拟合
γ (分裂增益阈值)剪枝,简化模型结构
随机化subsample (行采样)增强多样性,防过拟合
colsample_bytree (列采样)类似随机森林的特征抽样
  1. 学习率 η

    • 作用:控制每棵树的权重,值越小模型越保守,需更多树;
    • 经验范围0.01 ~ 0.3
    • 调优策略
      • 高学习率(0.1~0.3):快速收敛,适合数据量小或树数少(n_estimators=50~100);
      • 低学习率(0.01~0.1):需更多树(n_estimators=200~1000),配合早停法(early_stopping_rounds=10)。
    • 案例
      η=0.3 时测试集MSE=5.2,η=0.1 时MSE=4.8,但需 3 倍树数量,需权衡时间与性能。
  2. 树复杂度参数

    • max_depth (树最大深度)
      • 作用:控制树的生长,值越大模型越复杂。
      • 经验范围3~10
        • 分类任务:3-6
        • 回归任务:5-10
      • 调优:从 3 开始逐步增加,观察验证集性能。
    • min_child_weight (最小样本权重和)
      • 作用:基于二阶导数(Hessian)的样本权重和,限制叶子节点样本量。
      • 经验范围1~20
        • 小数据集:1-5
        • 大数据集:10-20
      • 公式 ∑ i ∈ node h i ≥ min_child_weight \sum_{i \in \text{node}} h_i \geq \text{min\_child\_weight} inodehimin_child_weight
        • h i h_i hi 是样本 ii 的二阶导数(Hessian),与损失函数相关;
        • 左式表示当前节点的所有样本的 Hessian 之和;
        • 分裂条件:只有当分裂后的 左子节点和右子节点 的 Hessian 之和均满足此阈值时,才允许分裂。
  3. 正则化参数

    • λ (L2正则化系数)
      • 作用:惩罚叶子权重的平方,值越大模型越简单。
      • 经验范围0.1~10
      • 调优:若模型过拟合( 训 练 误 差 < < 测 试 误 差 训练误差 << 测试误差 <<),增大 λ
    • γ (最小分裂增益阈值)
      • 作用:分裂增益需超过该值才允许分裂,越大模型越简单。
      • 经验范围0~5
      • 公式 Gain ≥ γ 才 分 裂 \text{Gain}≥γ 才分裂 Gainγ
  4. 随机化参数

    • subsample (行采样比例)
      • 作用:每棵树训练时采样样本的比例,增强多样性。
      • 经验范围0.5~1.0
      • 调优:数据量大时用 0.8~1.0,小数据用 0.5~0.8
    • colsample_bytree (列采样比例)
      • 作用:每棵树训练时采样特征的比例。
      • 经验范围0.5~1.0
      • 调优:特征数多时用 0.5~0.8,特征少时用 0.8~1.0

3.MATLAB 实现

XGBoost手动实现结果①
XGBoost手动实现结果②

%% XGBoost matlab实现
clc; clear; close all;

%% XGBoost核心参数设置
params = struct(...
    'eta', 0.05, ...           % 学习率 (同learning_rate), 控制每棵树的贡献
    'gamma', 0, ...           % 最小分裂增益阈值,防止过拟合
    'lambda', 1, ...          % L2正则化系数,约束叶子权重
    'max_depth', 3, ...       % 树最大深度,控制模型复杂度
    'subsample', 1, ...     % 行采样比例,增强多样性
    'colsample_bytree', 1, ... % 列采样比例,类似随机森林
    'min_child_weight', 1 ... % 叶子节点最小样本权重和(基于Hessian)
);

%% 生成模拟数据(含10%缺失值)
rng(42); % 固定随机种子
n_samples = 200;  % 样本数量
n_features = 5;   % 特征维度
X = rand(n_samples, n_features) * 10; % 特征矩阵范围[0,10]
Y = 3*X(:,1) + 2*X(:,2) + 1.5*X(:,3) + randn(n_samples,1)*2; % 目标变量(线性关系+噪声)

% 添加10%缺失值(NaN表示缺失)
X(randperm(numel(X), round(0.1*numel(X)))) = NaN;

% 划分训练集和测试集
cv = cvpartition(n_samples, 'HoldOut', 0.3);
X_train = X(cv.training,:);
Y_train = Y(cv.training,:);
X_test = X(cv.test,:);
Y_test = Y(cv.test,:);

%% 初始化预测值(基学习器为均值)
base_score = mean(Y_train);  % 初始预测值
F = base_score * ones(size(Y_train)); % 训练集预测值初始化
F_test = base_score * ones(size(Y_test)); % 测试集预测值初始化

%% XGBoost训练主循环
n_estimators = 70;  % 树的数量
trees = cell(n_estimators, 1); % 存储所有树结构

for t = 1:n_estimators
    % 步骤1: 数据采样(行采样 + 列采样)
    [sub_X, sub_Y, sub_idx, col_idx] = subsample(X_train, Y_train, ...
        params.subsample, params.colsample_bytree);
    
    % 步骤2: 计算一阶梯度(g)和二阶导数(h) —— 均方误差损失
    g = -(sub_Y - F(sub_idx));  % 一阶导数:负残差
    h = ones(size(g));          % 二阶导数:平方损失下恒为1
    
    % 步骤3: 训练XGBoost树(处理缺失值)
    tree = xgb_train_tree(sub_X, g, h, params, 0, 1:size(col_idx,2));
    
    % 步骤4: 更新训练集预测值(带学习率)
    leaf_pred = xgb_predict_tree(tree, X_train(:, col_idx)); % 仅使用采样特征
    F = F + params.eta * leaf_pred;
    
    % 存储树结构和使用的特征索引
    trees{t} = struct('tree', tree, 'col_idx', col_idx);
    
    % 打印训练进度
    fprintf('Tree %d 训练完成 | 最佳增益: %.2f\n', t, tree.best_gain);
end

%% 测试集预测
for t = 1:n_estimators
    % 获取当前树的特征子集
    col_idx = trees{t}.col_idx;
    % 预测并累加结果
    leaf_pred = xgb_predict_tree(trees{t}.tree, X_test(:, col_idx));
    F_test = F_test + params.eta * leaf_pred;
end

% 计算评估指标
mse = mean((Y_test - F_test).^2);
fprintf('\n测试集MSE: %.2f\n', mse);

plot(1:size(Y_test,1),Y_test,1:size(F_test,1),F_test)
%% ========== 核心函数定义 ==========

function tree = xgb_train_tree(X, g, h, params, depth, col_idx)
    % XGBoost树训练函数(递归实现)
    % 输入:
    %   X - 特征矩阵(可能含NaN)
    %   g - 一阶梯度
    %   h - 二阶导数
    %   params - 超参数
    %   depth - 当前深度
    %   col_idx - 全局特征索引(用于预测时对齐)
    % 输出:
    %   tree - 树结构体
    
    % 终止条件1: 达到最大深度
    % 终止条件2: 节点样本权重和不足(min_child_weight)
    if depth >= params.max_depth || sum(h) < params.min_child_weight
        % 计算叶子节点权重(带L2正则化)
        w = -sum(g) / (sum(h) + params.lambda);
        tree = struct('is_leaf', true, 'value', w, 'best_gain', 0, 'col_idx', col_idx);
        return;
    end
    
    % 初始化最佳分裂参数
    best_gain = -inf;
    best_feature = 0;
    best_threshold = 0;
    best_default = 0; % 缺失值默认方向(0=左子树,1=右子树)
    
    % 遍历所有采样特征(此处使用全局特征索引)
    for f_global = col_idx
        % 获取当前特征数据(可能含NaN)
        feature_data = X(:, f_global);
        
        % 步骤1: 处理缺失值,获取有效数据点
        valid_mask = ~isnan(feature_data);
        sorted_data = sort(feature_data(valid_mask));
        
        % 步骤2: 生成候选分裂点(简化版分位数)
        n_bins = min(10, length(sorted_data)); % 分箱数量
        candidates = linspace(min(sorted_data), max(sorted_data), n_bins+1);
        candidates = candidates(2:end-1); % 去除首尾
        
        % 步骤3: 评估每个候选点
        for threshold = candidates
            % 分裂掩码(处理缺失值为默认方向)
            left_mask = feature_data <= threshold;
            left_mask(isnan(feature_data)) = false; % 初始假设缺失值归右
            
            % 计算增益(假设缺失值在右)
            G_left = sum(g(left_mask));
            H_left = sum(h(left_mask));
            G_right = sum(g) - G_left;
            H_right = sum(h) - H_left;
            gain = (G_left^2)/(H_left + params.lambda) + ...
                   (G_right^2)/(H_right + params.lambda) - ...
                   (G_left + G_right)^2/(H_left + H_right + params.lambda);
            gain = gain/2 - params.gamma; % XGBoost官方增益公式
            
            % 检查是否更优增益(考虑缺失值在左的情况)
            G_left_alt = sum(g(left_mask | isnan(feature_data)));
            H_left_alt = sum(h(left_mask | isnan(feature_data)));
            G_right_alt = sum(g) - G_left_alt;
            H_right_alt = sum(h) - H_left_alt;
            gain_alt = (G_left_alt^2)/(H_left_alt + params.lambda) + ...
                       (G_right_alt^2)/(H_right_alt + params.lambda) - ...
                       (G_left_alt + G_right_alt)^2/(H_left_alt + H_right_alt + params.lambda);
            gain_alt = gain_alt/2 - params.gamma;
            
            % 选择更优的缺失值分配方向
            if gain_alt > gain
                gain = gain_alt;
                default_dir = true; % 缺失值归左
            else
                default_dir = false; % 缺失值归右
            end
            
            % 更新最佳分裂
            if gain > best_gain
                best_gain = gain;
                best_feature = f_global;
                best_threshold = threshold;
                best_default = default_dir;
            end
        end
    end
    
    % ========== 修改后的终止条件判断 ==========
    % 当所有特征都无法产生有效分裂时
    if best_gain <= params.gamma || best_feature == 0
        w = -sum(g)/(sum(h) + params.lambda);
        tree = struct('is_leaf', true, 'value', w, 'best_gain', best_gain, 'col_idx', col_idx);
        return;
    end
    
    
%     % 终止条件3: 无有效分裂(增益不足)
%     if best_gain <= 0
%         w = -sum(g)/(sum(h) + params.lambda);
%         tree = struct('is_leaf', true, 'value', w, 'best_gain', best_gain, 'col_idx', col_idx);
%         return;
%     end
    
    % 步骤4: 执行分裂
    % 生成分裂掩码(处理缺失值)
    feature_data = X(:, best_feature);
    left_mask = feature_data <= best_threshold;
    left_mask(isnan(feature_data)) = best_default; % 根据最佳方向分配缺失值
    
    % 检查左右子节点是否为空
    if sum(left_mask) == 0 || sum(~left_mask) == 0
        w = -sum(g)/(sum(h) + params.lambda);
        tree = struct('is_leaf', true, 'value', w, 'best_gain', best_gain, 'col_idx', col_idx);
        return;
    end
    
    % ========== 带保护的递归调用 ==========
    try
        left_tree = xgb_train_tree(X(left_mask, :), g(left_mask), h(left_mask), ...
                   params, depth+1, col_idx);
        right_tree = xgb_train_tree(X(~left_mask, :), g(~left_mask), h(~left_mask), ...
                    params, depth+1, col_idx);
    catch ME
        % 递归错误处理
        warning('深度 %d 递归失败: %s', depth, ME.message);
        w = -sum(g)/(sum(h) + params.lambda);
        tree = struct('is_leaf', true, 'value', w, 'best_gain', best_gain, 'col_idx', col_idx);
        return;
    end
    
    
%     % 递归构建子树
%     left_tree = xgb_train_tree(X(left_mask, :), g(left_mask), h(left_mask), ...
%                 params, depth+1, col_idx);
%     right_tree = xgb_train_tree(X(~left_mask, :), g(~left_mask), h(~left_mask), ...
%                  params, depth+1, col_idx);
    
    % 返回非叶子节点结构
    tree = struct(...
        'is_leaf', false, ...
        'feature', best_feature, ...
        'threshold', best_threshold, ...
        'default', best_default, ... % 缺失值方向
        'left', left_tree, ...
        'right', right_tree, ...
        'best_gain', best_gain, ...
        'col_idx', col_idx ...
    );
end

function pred = xgb_predict_tree(tree, X)
    % XGBoost树预测函数
    % 输入:
    %   tree - 训练好的树结构
    %   X - 特征矩阵(必须包含tree.col_idx指定的特征)
    % 输出:
    %   pred - 预测值
    
    pred = zeros(size(X,1), 1);
    for i = 1:size(X,1)
        node = tree;
        while true
            if node.is_leaf
                pred(i) = node.value;
                break;
            end
            
            % 获取特征值(注意特征索引对齐)
            f_global = node.feature;
            val = X(i, f_global);
            
            % 处理缺失值
            if isnan(val)
                go_left = node.default; % 使用训练时确定的方向
            else
                go_left = val <= node.threshold;
            end
            
            % 移动到子节点
            if go_left
                node = node.left;
            else
                node = node.right;
            end
        end
    end
end

function [sub_X, sub_Y, sub_idx, col_idx] = subsample(X, Y, subsample_ratio, colsample_ratio)
    % 数据采样函数(行+列采样)
    % 输入:
    %   X, Y - 原始数据和标签
    %   subsample_ratio - 行采样比例
    %   colsample_ratio - 列采样比例
    % 输出:
    %   sub_X, sub_Y - 采样后的数据和标签
    %   sub_idx - 行采样索引(相对于原始数据)
    %   col_idx - 列采样索引(全局特征索引)
    
    % 行采样
    n = size(X,1);
    sub_idx = randperm(n, round(n * subsample_ratio));
    sub_X = X(sub_idx, :);
    sub_Y = Y(sub_idx);
    
    % 列采样
    n_features = size(X,2);
    col_idx = sort(randperm(n_features, round(n_features * colsample_ratio)));
    sub_X = sub_X(:, col_idx);
end

参考资料

[1] XGBoost详解_哔哩哔哩_bilibili
[2] 机器学习第二阶段:机器学习经典算法(4)——Xgboost_哔哩哔哩_bilibili
[3] 如何向5岁小孩解释模型:XGBoost_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值