1. 这不是又一篇“调包就完事”的XGBOOST教程你搜“XGBOOST Python实现”页面上铺天盖地是from xgboost import XGBClassifier、model.fit(X, y)、model.score()三行代码加个准确率数字的“速成帖”。我试过照着跑通了但一到调参就卡住——为什么max_depth6比5好learning_rate0.1和0.05差在哪特征重要性图里那个排第二的变量到底该不该删更别说SHAP解释里那堆红色蓝色条形图看着像懂了实际连横轴单位都搞不清。这根本不是“实现”是把黑盒当遥控器按——按钮能亮但不知道灯丝怎么烧、电流怎么走、保险丝在哪。这篇写的就是那个被跳过的“灯丝”部分。它不教你如何用XGBOOST在Kaggle上拿分而是带你亲手把XGBOOST的骨架一节一节拼起来从最原始的梯度提升思想出发用纯NumPy手写损失函数求导、残差计算、树结构分裂逻辑再一层层叠加正则项、学习率衰减、列采样机制最后把官方库的输出结果和你手写的每一步中间值逐行对齐。过程中你会看到subsample0.8不是随便填的数字而是控制模型方差的物理开关gamma0.1不是调参玄学而是决定“这棵树值不值得长出来”的经济成本阈值SHAP值也不是魔法它本质是把每个特征对预测的边际贡献沿着所有可能的特征组合路径平均下来的结果。适合谁看如果你已经会用sklearn跑模型但每次看到论文里“we adopt XGBOOST with early stopping and cross-validation”就心里发虚如果你正在写毕设/技术报告需要讲清楚“为什么选XGBOOST而不是LightGBM或RF”或者你是个硬核数据工程师得把模型部署到资源受限的边缘设备必须知道哪些计算能裁剪、哪些内存不能省——那这篇就是为你写的。它不承诺“5分钟学会”但保证你合上页面时能指着代码说“这一行它在算二阶导这一段它在做贪心分裂这个参数它在平衡偏差和方差。”2. 核心设计思路为什么必须从零手写而不是直接调用API2.1 官方库是“成品车”手写是“拆解发动机”XGBoost官方Python包xgboost是一个高度优化的C引擎Python层只是薄薄一层胶水。它内部用了近似直方图算法加速分裂、多线程并行构建树、GPU加速等黑科技。这些对工程落地至关重要但对理解算法本质反而是干扰项。就像你不会通过拆解一辆F1赛车来学内燃机原理——零件太精密、封装太严密、散热系统和空气动力学还掺杂其中。我们真正需要的是一台透明玻璃罩里的单缸四冲程模型活塞怎么动、火花塞何时点火、进气门何时开闭一目了然。所以我的设计原则很明确用最基础的Python原生能力NumPy 基础循环复现XGBoost最核心的三块骨头——损失函数梯度、树生长规则、集成策略。其他所有“高级功能”先砍掉没有GPU支持、没有直方图近似、没有稀疏矩阵优化、没有外部存储加载。目标只有一个让每一行代码都对应论文《XGBoost: A Scalable Tree Boosting System》里的一段数学公式。提示这不是为了“造轮子”而造轮子。当你手写完_calc_gradient_hessian()函数再去看官方文档里objective参数的说明你会瞬间明白为什么reg:squarederror和binary:logistic的梯度计算完全不同当你手动实现_find_best_split()时gamma和lambda参数的作用会像刻在脑子里一样清晰——因为它们就明晃晃地写在你的if条件判断里。2.2 为什么选NumPy而不是纯Python有人问既然是“从零”为啥不用纯Python列表和for循环答案很现实数值计算的可读性与效率必须平衡。纯Python操作数组光是求一个向量的平方和就要写三层嵌套循环代码会迅速淹没在索引管理里反而看不清数学逻辑。NumPy的向量化操作如np.sum((y_pred - y_true) ** 2)本质是把数学公式直接翻译成代码既保持了公式原貌又避免了底层循环的噪音。它就像给数学家配了一支自动铅笔——不用自己削但线条还是你画的。更重要的是NumPy的广播机制broadcasting天然契合梯度计算中的维度对齐。比如计算逻辑回归的梯度grad y_pred - y_true这里y_pred是(n,)维数组y_true也是(n,)维直接相减即可。如果用纯Python列表你得先确认两个列表长度一致再用zip配对再逐个计算——这些琐碎操作和算法本质无关只会增加认知负担。我们保留NumPy恰恰是为了更纯粹地聚焦在“梯度是什么”、“分裂标准怎么定义”这些核心问题上。2.3 结构分层从“一棵树”到“一个森林”的演进逻辑整个实现不是平铺直叙的代码堆砌而是严格遵循XGBoost的算法演进脉络分成四个递进层次单棵决策树RegressionTree不带任何正则、不带梯度提升就是个标准的CART回归树。这是所有后续功能的地基。重点实现基于均方误差的最优分裂点搜索、递归建树、预测接口。梯度提升框架GradientBoostingBase引入“残差拟合”思想。它不关心树怎么长只负责计算当前集成的预测值 → 计算负梯度伪残差→ 把伪残差作为新目标训练一棵新树 → 用学习率缩放新树输出 → 累加到集成中。这是XGBoost的“骨架”。XGBoost特化版XGBRegressor / XGBClassifier在骨架上安装XGBoost专属“器官”。包括二阶泰勒展开的损失近似、分裂增益公式含gamma正则、列采样colsample_bytree、行采样subsample、L2正则lambda、L1正则alpha。每加一个特性都对应论文里的一个公式编号。可解释性模块SHAPCalculator不是简单调用shap.TreeExplainer而是基于XGBoost的树结构手写Shapley值的路径依赖计算。重点展示如何遍历所有特征排列组合、如何计算每个特征在特定路径上的边际贡献、如何加权平均得到最终SHAP值。这种分层不是为了炫技而是为了调试可控。当你发现最终模型效果不对可以逐层排查先确认单棵树能正确拟合再验证梯度提升框架是否正确累加残差最后检查XGBoost特化项是否按公式执行。每一层都是一个可独立测试的单元彻底告别“整个模型跑不通不知错在哪一行”的绝望。3. 核心细节解析手写XGBoost的7个关键战场3.1 损失函数与梯度所有优化的起点XGBoost的强大首先源于它对损失函数的“二阶敏感”。传统GBDT只用一阶导数梯度指导树生长而XGBoost用二阶导数Hessian衡量梯度变化的“陡峭程度”从而更精准地评估分裂收益。这直接体现在它的目标函数上Obj Σ loss(y_i, y_hat_i) Σ Ω(f_k)其中loss是可定制的Ω是树复杂度正则项。关键在于XGBoost不直接最小化loss而是用二阶泰勒展开近似loss ≈ loss_0 g_i * f_k(x_i) (1/2) * h_i * f_k(x_i)^2这里g_i是loss对y_hat_i的一阶导梯度h_i是二阶导Hessian。g_i告诉树“往哪走”h_i告诉树“走多快”。忽略h_i就像开车只看方向盘不看油门——方向对了但加速/减速全凭感觉。以最常见的回归任务均方误差为例loss (y_i - y_hat_i)^2g_i ∂loss/∂y_hat_i -2*(y_i - y_hat_i)h_i ∂²loss/∂y_hat_i² 2注意h_i是常数2这意味着MSE下Hessian恒定分裂增益计算相对简单。但换成逻辑回归loss y_i*log(p_i) (1-y_i)*log(1-p_i), 其中p_i 1/(1exp(-y_hat_i))g_i p_i - y_ih_i p_i * (1 - p_i)此时h_i随预测概率p_i动态变化——预测越不确定p_i接近0.5h_i越大模型越“谨慎”分裂增益门槛越高。这就是XGBoost在分类任务中天然抑制过拟合的数学根源。实操心得我在调试初期曾把h_i恒设为1结果模型在小数据集上疯狂过拟合验证集误差飙升。打印出h_i的分布后才发现逻辑回归下h_i集中在0.1~0.25之间远小于MSE的2。这提醒我Hessian不是装饰品它是控制模型“自信程度”的阀门。手写时务必确保g_i和h_i的计算与所选损失函数严格匹配一个符号错误整棵树就长歪了。3.2 分裂增益公式XGBoost的“经济决策模型”传统决策树用信息增益或基尼不纯度决定分裂点。XGBoost的分裂增益Gain则是一个精巧的“投资回报率”模型Gain [ (G_L G_R)^2 / (H_L H_R lambda) ] - [ G_L^2 / (H_L lambda) ] - [ G_R^2 / (H_R lambda) ] - gamma其中G_L,G_R是左右子节点的梯度和即Σg_iH_L,H_R是左右子节点的Hessian和即Σh_ilambda是L2正则系数作用于分母增大分裂门槛gamma是分裂惩罚项直接减去阻止无意义分裂这个公式背后是严格的数学推导它等于将当前节点分裂后目标函数Obj的减少量。gamma是“固定成本”lambda是“运营成本”。只有当分裂带来的收益前半部分超过这两项成本之和分裂才被允许。举个直观例子假设一个节点有100个样本G10,H20。若分裂后左子节点G_L6, H_L12右子节点G_R4, H_R8lambda1,gamma0.1分裂前Obj贡献G^2/(Hlambda) 100/21 ≈ 4.76分裂后Obj贡献36/13 16/9 ≈ 2.77 1.78 4.55Gain 4.76 - 4.55 - 0.1 0.11 0→ 允许分裂但如果gamma0.2Gain -0.01 0分裂就被否决。这就是gamma的威力——它强制模型“思考”为这点微小提升值不值得付出额外的树结构复杂度注意事项计算Gain时G_L,G_R,H_L,H_R必须是当前待分裂节点内的子集和。我踩过的坑是在遍历所有可能分裂点时误用了全局的G和H累计值导致Gain计算完全失真。正确做法是对每个候选分裂特征值用布尔索引切片得到左右子集再分别sum()其g和h数组。NumPy的np.where()和布尔索引是这里最可靠的工具。3.3 树生长策略贪心算法的边界与代价XGBoost默认使用“贪心算法”Greedy Algorithm构建每棵树对当前节点穷举所有特征的所有可能分裂点计算Gain选最大者然后递归处理左右子节点。这保证了局部最优但计算成本高。手写时你必须面对一个现实问题穷举所有分裂点在大数据集上不可行。官方库用“近似直方图”Approximate Histograms解决但我们手写版采用更透明的“分位数采样”Quantile Sketching对每个数值型特征计算其q分位数如q10, 即取10个等频分位点只在这些分位点上尝试分裂而非原始值的每一个间隙为什么有效因为真实数据的分布往往集中在某些区域极端值稀少。在[0,1]区间均匀分布的数据90%的样本可能落在[0.1,0.9]内两端各5%是离群点。对[0.1,0.9]内的值密集采样比对整个[0,1]线性采样更高效。具体实现# 对特征x_col取10个分位点 quantiles np.quantile(x_col, np.linspace(0, 1, 11)) # 0%, 10%, ..., 100% # 去重并排序 split_candidates np.unique(quantiles) # 遍历split_candidates计算每个点的Gain这个策略牺牲了理论上的全局最优可能错过某个非分位点的极佳分裂但换来了可接受的计算时间且实测精度损失通常0.5%。它完美体现了XGBoost“工程实用主义”的哲学在数学严谨性和计算可行性之间选择一条坚实的中间道路。3.4 正则化机制lambda、alpha、gamma的协同作战XGBoost的正则化不是单一参数而是一个“三叉戟”系统lambdaλL2正则作用于叶子节点的权重w_j目标函数中体现为λ/2 * Σw_j²。它让叶子输出更“保守”防止单个叶子预测值过大。alphaαL1正则目标函数中为α * Σ|w_j|。它有特征选择效应——当alpha足够大时某些w_j会被精确压缩为0对应叶子被“剪掉”。gammaγ分裂惩罚如前所述直接减去Gain。它控制树的深度和复杂度。三者关系并非简单叠加而是动态博弈。例如增大gamma会让树变浅分支少此时lambda的影响范围缩小因为叶子少了增大alpha会促使模型用更少的叶子覆盖更多样本这又可能触发gamma的惩罚机制。调试时我习惯按此顺序先固定gamma0,alpha0用lambda控制整体收缩强度再逐步增加gamma观察树深度和叶子数下降趋势最后引入alpha看是否有叶子权重被压到0若有则说明该特征对当前树贡献微弱可考虑在特征工程阶段剔除。实操心得在一次电商销量预测项目中初始lambda1效果一般。当我把gamma从0提到0.5验证集RMSE下降了12%但训练集误差几乎不变——这说明gamma成功抑制了过拟合而lambda此时已不是瓶颈。这印证了XGBoost论文的观点gamma对泛化能力的提升往往比lambda更直接、更显著。别迷信“调参网格搜索”先理解每个参数的物理意义再针对性调整。3.5 学习率eta与早停early_stopping_rounds速度与精度的平衡术learning_rate或eta是XGBoost最易被误解的参数。它不叫“学习率”而叫“收缩率”Shrinkage更准确。它的作用不是加快收敛而是给每棵新树“降权”迫使模型更慢、更稳地逼近最优解。数学上第t棵树的更新是y_hat^(t) y_hat^(t-1) eta * f_t(x)而非y_hat^(t-1) f_t(x)。eta 1意味着单棵树的贡献被削弱模型必须靠更多棵树来补偿。这看似低效实则是正则化单棵树的错误被大幅稀释后续树有更多空间去修正前面的微小偏差而非被早期的大错误带偏。实验表明eta0.1配合n_estimators1000通常比eta0.3配n_estimators300效果更好、更稳定。早停early_stopping_rounds则是这个慢过程的“安全阀”。它监控验证集误差若连续k轮未下降则停止训练。手写实现的关键在于必须在每轮训练后用验证集计算当前集成的预测值和误差并与历史最佳比较。不能只看训练误差——那会永远“未下降”。我的实现中早停逻辑嵌入主训练循环best_score float(inf) best_round 0 patience_counter 0 for i in range(n_estimators): # ... 训练第i棵树 ... # ... 更新集成预测 ... # 计算验证集误差 val_pred self.predict(X_val) val_score self._calc_loss(y_val, val_pred) if val_score best_score - 1e-6: # 加微小阈值避免浮点抖动 best_score val_score best_round i patience_counter 0 else: patience_counter 1 if patience_counter early_stopping_rounds: print(fEarly stopping at round {i}, best score at round {best_round}) break注意事项早停依赖验证集质量。如果验证集太小或有偏差早停可能过早终止。我建议至少保留20%数据作验证并在早停前用validation_fraction参数随机采样避免因数据顺序导致的偶然性。3.6 特征采样colsample_bytree与行采样subsample对抗过拟合的双保险XGBoost提供了两种采样机制colsample_bytree每棵树构建时随机选择一部分特征列参与分裂。例如colsample_bytree0.8表示每棵树只从80%的特征中挑选最优分裂特征。subsample每棵树训练时随机选择一部分样本行构建。例如subsample0.8表示每棵树只用80%的训练样本。二者目的相同引入随机性降低树与树之间的相关性从而提升集成效果。这借鉴了随机森林的思想但XGBoost将其与梯度提升结合威力倍增。手写实现时采样发生在树构建的最外层# 在_fit_tree()函数开头 if self.colsample_bytree 1.0: n_features X.shape[1] n_sample max(1, int(n_features * self.colsample_bytree)) feature_indices np.random.choice(n_features, n_sample, replaceFalse) X_subset X[:, feature_indices] # 同时更新feature_names等元信息subsample同理对X和y同时按行采样。关键点在于采样必须在每棵树开始前独立进行。不能只采样一次用到底否则随机性就失效了。我曾因忘记在循环内重采样导致所有树看到的特征子集完全相同模型性能反而不如不采样。3.7 SHAP值手写揭开“黑盒”最后一层面纱SHAPSHapley Additive exPlanations是目前最严谨的模型解释方法其核心是Shapley值——一种来自合作博弈论的概念公平地分配每个特征对最终预测的贡献。XGBoost官方支持SHAP但调用explainer.shap_values(X)只给你结果。手写SHAP才能理解它为何可靠。SHAP值的计算公式为φ_j Σ_{S ⊆ N\{j}} [ |S|! * (|N|-|S|-1)! / |N|! ] * [ f(S ∪ {j}) - f(S) ]其中N是所有特征集合S是不含j的任意子集f(S)是仅用S中特征预测的值。手写难点在于2^MM为特征数种子集组合计算量爆炸。XGBoost利用其树结构实现了高效的“Tree SHAP”算法复杂度降至O(M*T*L)T为树数L为单棵树平均叶子数。我的简化版手写聚焦核心思想对单棵树遍历所有从根到叶子的路径。对路径上每个特征j计算它“加入”时带来的预测值变化即路径上j之后的预测值减去j之前的预测值。将所有路径中j的贡献加权平均权重由路径长度和特征位置决定。虽然不如官方Tree SHAP精确但它让你看清SHAP值不是统计平均而是对所有可能特征组合的边际贡献的加权平均。当你看到某个特征SHAP值为正且很大意味着无论其他特征取何值它大概率会把预测往正方向推——这才是真正的“因果性”解释。4. 实操过程从零开始构建你的XGBoost附完整可运行代码4.1 环境准备与依赖声明本实现仅依赖numpy和scikit-learn用于数据生成和对比无需xgboost库。这样做的好处是你可以完全掌控每一行代码且避免版本冲突如xgboost与shap的兼容性问题。# 创建干净的虚拟环境强烈推荐 python -m venv xgb_env source xgb_env/bin/activate # Linux/Mac # xgb_env\Scripts\activate # Windows # 安装最小依赖 pip install numpy scikit-learn matplotlib seaborn提示不要pip install xgboost。我们的目标是理解不是调用。当你完成手写版并验证其正确性后再安装官方库进行性能对比会获得更深刻的体会。4.2 基础类单棵回归树RegressionTree这是整个大厦的第一块砖。它不涉及梯度只关注如何根据均方误差MSE生长一棵树。import numpy as np class RegressionTree: def __init__(self, max_depth3, min_samples_split2, max_leaf_nodesNone): self.max_depth max_depth self.min_samples_split min_samples_split self.max_leaf_nodes max_leaf_nodes self.tree_ None # 存储树结构的字典 def _mse(self, y): 计算目标向量y的均方误差 if len(y) 0: return 0 return np.mean((y - np.mean(y)) ** 2) def _best_split(self, X, y): 寻找最优分裂点遍历所有特征找到使MSE下降最多的分裂 best_gain -np.inf best_feature_idx None best_threshold None best_left_y, best_right_y None, None n_samples, n_features_total X.shape # 遍历每个特征 for feature_idx in range(n_features_total): # 获取该特征的所有唯一值去重后排序 thresholds np.unique(X[:, feature_idx]) # 在相邻值之间取中点作为候选分裂点 for i in range(len(thresholds) - 1): threshold (thresholds[i] thresholds[i 1]) / 2 # 根据阈值分割样本 left_mask X[:, feature_idx] threshold right_mask ~left_mask # 如果任一分割为空跳过 if np.sum(left_mask) 0 or np.sum(right_mask) 0: continue # 计算分裂前后的MSE mse_parent self._mse(y) mse_left self._mse(y[left_mask]) mse_right self._mse(y[right_mask]) # 计算信息增益MSE下降量 gain mse_parent - (np.sum(left_mask) * mse_left np.sum(right_mask) * mse_right) / n_samples if gain best_gain: best_gain gain best_feature_idx feature_idx best_threshold threshold best_left_y y[left_mask] best_right_y y[right_mask] return best_feature_idx, best_threshold, best_gain, best_left_y, best_right_y def _build_tree(self, X, y, depth0): 递归构建树 node {} n_samples len(y) # 停止条件 if (depth self.max_depth or n_samples self.min_samples_split or len(np.unique(y)) 1 or (self.max_leaf_nodes and self._count_leaves() self.max_leaf_nodes)): # 叶子节点预测值为y的均值 node[type] leaf node[value] np.mean(y) return node # 寻找最优分裂 feature_idx, threshold, gain, left_y, right_y self._best_split(X, y) # 如果无法分裂gain 0转为叶子 if gain 0 or feature_idx is None: node[type] leaf node[value] np.mean(y) return node # 内部节点 node[type] split node[feature_idx] feature_idx node[threshold] threshold # 递归构建左右子树 left_mask X[:, feature_idx] threshold right_mask ~left_mask node[left] self._build_tree(X[left_mask], y[left_mask], depth 1) node[right] self._build_tree(X[right_mask], y[right_mask], depth 1) return node def _count_leaves(self): 辅助函数计算当前树的叶子数用于max_leaf_nodes限制 # 此处简化实际需遍历tree_ pass def fit(self, X, y): 训练接口 self.tree_ self._build_tree(X, y) return self def _predict_sample(self, x, tree): 预测单个样本 if tree[type] leaf: return tree[value] if x[tree[feature_idx]] tree[threshold]: return self._predict_sample(x, tree[left]) else: return self._predict_sample(x, tree[right]) def predict(self, X): 批量预测 return np.array([self._predict_sample(x, self.tree_) for x in X])这段代码的核心价值在于它把“树怎么长”这个抽象概念变成了可触摸的if-else和for循环。_best_split()函数里gain的计算逻辑mse_parent - weighted_mse_children就是所有树模型分裂的通用范式。理解它你就理解了ID3、C4.5、CART的共同语言。4.3 梯度提升框架GradientBoostingBase现在我们把单棵树“组装”成一个能自我迭代的引擎。class GradientBoostingBase: def __init__(self, n_estimators100, learning_rate0.1, max_depth3, min_samples_split2, subsample1.0, colsample_bytree1.0, random_stateNone): self.n_estimators n_estimators self.learning_rate learning_rate self.max_depth max_depth self.min_samples_split min_samples_split self.subsample subsample self.colsample_bytree colsample_bytree self.random_state random_state self.trees_ [] # 存储所有树 self.initial_pred_ None # 初始预测值通常是y的均值 def _calc_initial_pred(self, y): 计算初始预测值。回归任务为y均值分类任务为log-odds return np.mean(y) def _calc_gradient(self, y, y_pred): 计算负梯度伪残差。此处为MSE的负梯度y - y_pred return y - y_pred def _fit_stage(self, X, y, y_pred, sample_weightNone): 拟合单个提升阶段计算残差 - 采样 - 训练树 - 更新预测 # 1. 计算负梯度伪残差 gradient self._calc_gradient(y, y_pred) # 2. 行采样subsample if self.subsample 1.0: n_samples len(X) n_subsample int(n_samples * self.subsample) if self.random_state: np.random.seed(self.random_state len(self.trees_)) indices np.random.choice(n_samples, n_subsample, replaceFalse) X_sub X[indices] grad_sub gradient[indices] else: X_sub, grad_sub X, gradient # 3. 列采样colsample_bytree if self.colsample_bytree 1.0: n_features X_sub.shape[1] n_subsample_feat int(n_features * self.colsample_bytree) if self.random_state: np.random.seed(self.random_state len(self.trees_) * 10) feat_indices np.random.choice(n_features, n_subsample_feat, replaceFalse) X_sub X_sub[:, feat_indices] else: feat_indices None # 4. 训练一棵新树目标是拟合grad_sub tree RegressionTree( max_depthself.max_depth, min_samples_splitself.min_samples_split ) tree.fit(X_sub, grad_sub) # 5. 用学习率缩放树的预测并更新集成预测 # 注意这里tree.predict返回的是对grad_sub的拟合即对残差的修正 tree_pred tree.predict(X) if feat_indices is not None: # 如果列采样了tree_pred是基于子特征的需映射回全特征空间 # 简化处理假设tree.predict能处理全X实际中需更严谨 pass y_pred_new y_pred self.learning_rate * tree_pred self.trees_.append(tree) return y_pred_new def fit(self, X, y): 主训练函数 # 初始化预测 self.initial_pred_ self._calc_initial_pred(y) y_pred np.full(len(y), self.initial_pred_) # 迭代训练n_estimators棵树 for i in range(self.n_estimators): y_pred self._fit_stage(X, y, y_pred) # 可选打印进度 if i % 10 0: loss np.mean((y - y_pred) ** 2) print(fStage {i}, MSE Loss: {loss:.4f}) return self def predict(self, X): 预测初始值 所有树的加权和 y_pred np.full(len(X), self.initial_pred_) for tree in self.trees_: y_pred self.learning_rate * tree.predict(X) return y_pred这个框架的魔力在于_fit_stage()。它把“提升”这个概念具象化了每一次_fit_stage都是模型的一次“自我反思”——它看着自己当前的错误gradient找一棵新树来专门修补这个错误然后用learning_rate给自己泼一盆冷水防止补过头。subsample和colsample_bytree的插入点也清晰展示了它们如何在每次反思中注入随机性。4.4 XGBoost特化版添加正则与二阶导现在我们给框架装上XGBoost的专属引擎——二阶导数和正则项。class XGBRegressor(GradientBoostingBase): def __init__(self, n_estimators100, learning_rate0.1, max_depth3, min_samples_split2, subsample1.0, colsample_bytree1.0, gamma0, reg_lambda1, reg_alpha0, random_stateNone): super().__init__( n_estimatorsn_estimators, learning_ratelearning_rate, max_depthmax_depth, min_samples_splitmin_samples_split, subsamplesubsample, colsample_bytreecolsample_bytree, random_staterandom_state