电商网站建设需要多少钱一年图书馆网站建设目标
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.算法步骤
-
初始化预测值
- 目标:设定初始预测值(通常为常数,如样本标签的均值);
- 公式: 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=1∑nL(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)=n1∑i=1nyi。
-
迭代构建树(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=∂Ft−1(xi)∂L(yi,Ft−1(xi)),hi=∂Ft−1(xi)2∂2L(yi,Ft−1(xi))
- g i g_i gi:这是损失函数对当前预测值 F t − 1 ( x i ) F_{t−1}(x_i) Ft−1(xi) 的一阶导数,也被称为梯度。在给定第 i i i 个样本的真实标签 y i y_i yi 和前一棵树(或之前所有树)的预测结果 F t − 1 ( x i ) F_{t−1}(x_i) Ft−1(xi) 的情况下,它衡量了损失函数随预测值变化的变化率。一阶导数帮助我们了解如何调整预测以减少误差;
- h i h_i hi:这是损失函数对当前预测值 F t − 1 ( x i ) F_{t−1}(x_i) Ft−1(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[∑i∈Lhi+λ(∑i∈Lgi)2+∑i∈Rhi+λ(∑i∈Rgi)2−∑ihi+λ(∑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 ∑i∈Lgi 和 ∑ i ∈ R g i \sum_{i \in R} g_i ∑i∈Rgi:分别是左子树和右子树中所有样本的一阶导数之和,反映了两个子树各自预测误差的方向和大小;
- ∑ i ∈ L h i \sum_{i \in L} h_i ∑i∈Lhi 和 ∑ i ∈ R h i \sum_{i \in R} h_i ∑i∈Rhi:分别是左子树和右子树中所有样本的二阶导数之和,用于衡量这两个子集中样本的预测误差的变化率或曲率;
- ( ∑ 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} ∑i∈Lhi+λ(∑i∈Lgi)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} ∑i∈Rhi+λ(∑i∈Rgi)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 是一个缩放因子,不影响分裂的选择,仅用于简化后续的计算;
- 最后减去的 γ γ γ 是叶子节点的复杂度代价,也是一个正则化项,用来惩罚过于复杂的模型(即过多的叶子节点)。这有助于控制模型的复杂性,避免过拟合并提高泛化能力。
- 计算每个分裂点的 增益(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[∑i∈Lhi+λ(∑i∈Lgi)2+∑i∈Rhi+λ(∑i∈Rgi)2−∑ihi+λ(∑igi)2]−γ
- 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=−∑i∈Ijhi+λ∑i∈Ijgi
- 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 ∑i∈Ijgi:表示分配到第 j j j 个叶子节点的所有样本的一阶梯度之和。一阶梯度衡量了损失函数相对于当前预测的变化率,反映了模型在这些样本点上的误差方向和大小;
- ∑ i ∈ I j h i \sum_{i \in I_j} h_i ∑i∈Ijhi:表示分配到第 j j j 个叶子节点的所有样本的二阶梯度之和。二阶导数提供了关于损失函数形状的信息,特别是其曲率,这有助于确定调整预测值的方向和步长;
- λ λ λ :这是一个正则化参数,用于控制模型的复杂度,防止过拟合。通过在分母中添加 λ λ λ,可以避免模型过于依赖训练数据中的噪声或异常值,从而提高模型的泛化能力。
- 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=∂Ft−1(xi)∂L(yi,Ft−1(xi)),hi=∂Ft−1(xi)2∂2L(yi,Ft−1(xi))
-
正则化
- 目标:防止过拟合,提升泛化能力;
- 策略:
- 权重收缩(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)=Ft−1(x)+η⋅ft(x)
- F t ( x ) F_t(x) Ft(x):这是在第 t t t 轮迭代后的模型输出。随着每次迭代,模型会逐步学习到数据中的模式,并试图减少预测误差;
- F t − 1 ( x ) F_{t-1}(x) Ft−1(x):这是前一轮迭代结束时的模型输出。也就是说,这是在没有加上本轮新学到的信息之前的模型状态;
- η η η :这是学习率(也称为收缩率或步长),通常是一个介于 0 和 1 之间的数。学习率控制了每棵树对最终结果的影响程度。较小的学习率意味着需要更多的树来拟合训练数据,但通常可以带来更好的泛化能力和更平滑的决策边界;
- f t ( x ) f_t(x) ft(x) :这是在第 t t t 轮迭代中训练出的新树的预测结果。每一次迭代都致力于纠正之前所有树的预测错误,通过聚焦于那些先前模型预测不准确的数据点,从而逐步改进模型的整体性能。
- 列采样(Column Subsampling):随机选择部分特征进行分裂;
- 剪枝(Pruning):根据增益阈值 γ γ γ 剪除低增益的分支。
- 权重收缩(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)=Ft−1(x)+η⋅ft(x)
-
更新预测值
- 将新生成的树 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)=Ft−1(x)+η⋅ft(x)
-
停止条件
- 预设轮次:达到最大迭代次数(如 100 轮);
- 早停(Early Stopping):验证集误差连续 k k k 轮不再下降;
- 性能阈值:损失函数降至目标值以下。
2.参数选择
参数类型 | 核心参数 | 调优优先级 | 作用方向 |
---|---|---|---|
基础控制 | η (学习率) | 中 | 控制模型收敛速度与稳定性 |
复杂度控制 | max_depth (树复杂度参数) | 高 | 防止过拟合/欠拟合 |
min_child_weight (最小样本权重和) | 高 | 控制叶子节点最小样本权重 | |
正则化 | λ (L2 正则化) | 中 | 约束权重,抑制过拟合 |
γ (分裂增益阈值) | 中 | 剪枝,简化模型结构 | |
随机化 | subsample (行采样) | 低 | 增强多样性,防过拟合 |
colsample_bytree (列采样) | 低 | 类似随机森林的特征抽样 |
-
学习率 η
- 作用:控制每棵树的权重,值越小模型越保守,需更多树;
- 经验范围:
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.1~0.3):快速收敛,适合数据量小或树数少(
- 案例:
若η=0.3
时测试集MSE=5.2,η=0.1
时MSE=4.8,但需 3 倍树数量,需权衡时间与性能。
-
树复杂度参数
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} i∈node∑hi≥min_child_weight
- h i h_i hi 是样本 ii 的二阶导数(Hessian),与损失函数相关;
- 左式表示当前节点的所有样本的 Hessian 之和;
- 分裂条件:只有当分裂后的 左子节点和右子节点 的 Hessian 之和均满足此阈值时,才允许分裂。
-
正则化参数
λ
(L2正则化系数):- 作用:惩罚叶子权重的平方,值越大模型越简单。
- 经验范围:
0.1~10
- 调优:若模型过拟合( 训 练 误 差 < < 测 试 误 差 训练误差 << 测试误差 训练误差<<测试误差),增大
λ
。
γ
(最小分裂增益阈值):- 作用:分裂增益需超过该值才允许分裂,越大模型越简单。
- 经验范围:
0~5
- 公式: Gain ≥ γ 才 分 裂 \text{Gain}≥γ 才分裂 Gain≥γ才分裂
-
随机化参数
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 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 > gaingain = gain_alt;default_dir = true; % 缺失值归左elsedefault_dir = false; % 缺失值归右end% 更新最佳分裂if gain > best_gainbest_gain = gain;best_feature = f_global;best_threshold = threshold;best_default = default_dir;endendend% ========== 修改后的终止条件判断 ==========% 当所有特征都无法产生有效分裂时if best_gain <= params.gamma || best_feature == 0w = -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) == 0w = -sum(g)/(sum(h) + params.lambda);tree = struct('is_leaf', true, 'value', w, 'best_gain', best_gain, 'col_idx', col_idx);return;end% ========== 带保护的递归调用 ==========tryleft_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 ...);
endfunction 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 trueif node.is_leafpred(i) = node.value;break;end% 获取特征值(注意特征索引对齐)f_global = node.feature;val = X(i, f_global);% 处理缺失值if isnan(val)go_left = node.default; % 使用训练时确定的方向elsego_left = val <= node.threshold;end% 移动到子节点if go_leftnode = node.left;elsenode = node.right;endendend
endfunction [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