在Kaggle上打比赛头两年,我的杀手锏一直是XGBoost。从Titanic到各类结构化数据的Top解决方案,XGBoost几乎无处不在。很多新手问我要怎么入门比赛,我的答案永远是:先把XGBoost吃透,它能带你赢下大部分结构化数据比赛。
这篇内容不是教科书式的算法讲解,而是我作为参赛者摸爬滚打积累的实战经验总结。不管你是准备注册Kaggle账号开始第一场比赛的新手,还是已经用过XGBoost但总感觉调参不得章法的进阶玩家,这篇内容都会给你一套可以直接拿去用的打法。
1. 为什么在Kaggle赛场上XGBoost如此能打
1.1 从决策树到梯度提升:XGBoost的算法逻辑
很多人在看XGBoost代码之前,先被算法名字吓住了。拆开看其实很直白:XGBoost的全称是eXtreme Gradient Boosting,极端梯度提升。它做的事情是用一堆弱学习器(通常是CART回归树)串行地去拟合前一轮预测的残差,每一棵新树都在修正前面所有树的错误,最终把所有树的预测结果加起来作为最终预测。
打个比方,这就像你请一群实习生依次修改同一篇稿子。第一个人把结构理顺,第二个人把错别字改掉,第三个人把语句润色,每个人只管修上一版剩下的问题,最终稿子的质量就会远超任何单个人的能力。这就是Boosting的核心思路。
XGBoost相比传统GBDT做了几个关键加速和优化。它在目标函数里加入了正则化项,防止模型过度拟合训练集;对损失函数做了二阶泰勒展开,比只用一阶梯度的GBDT收敛更精准;在特征分裂时支持列抽样,类似随机森林的做法,降低树之间的相关性。这些改进让XGBoost在精度和速度上都成了当年的大杀器,即便放到今天,依然是结构化数据比赛里最稳的基模型之一。
1.2 对比LightGBM和CatBoost,什么场景该选XGBoost
Kaggle赛场上,XGBoost、LightGBM、CatBoost三大梯度提升框架经常同台竞技。很多人来问我到底该学哪个,我的建议是:三个都装,但优先把XGBoost玩熟。
LightGBM用的是基于直方图的算法,训练速度在数据量大时优势明显,对内存占用更友好,在KS类比赛和高维稀疏特征上经常表现更好。CatBoost对类别特征有原生支持,如果你面对的是大量离散类别变量,能省掉很多手动编码工作,而且它对参数扰动不敏感,不容易过拟合。
那XGBoost的不可替代性在哪?主要三点:一是系统接口成熟,XGBoost的API设计非常规范,从原生接口到sklearn接口都稳定可靠,几乎不会出现兼容性问题;二是对中等规模数据(几万到几十万行)的表现极其稳定,调参空间大,找到一个合适参数组合后泛化能力往往很能打;三是有大量历史比赛的Top方案可以借鉴,遇到问题查资料时最容易找到答案的就是XGBoost相关的内容。
我的经验法则是:如果数据在10万行以内、特征以数值型为主、比赛类型是二分类或回归,先跑XGBoost作为基线,再用LightGBM去做对比融合,往往融合后的分数比单一模型高出一截。
1.3 用图解的方式理解XGBoost训练流程
只看文字描述XGBoost算法还是有点抽象,我用最直观的方式把训练流程拆成五个阶段帮你建立整体画面感。
第一阶段是初始化预测值。对于二分类任务,初始预测通常设为训练集正样本比例的logit值;回归任务则设为目标均值。第二阶段是计算每个样本的梯度信息,也就是损失函数对当前预测值的一阶导数和二阶导数,XGBoost就是靠它们来指导树的构建。第三个阶段是建树,从根节点开始逐个特征尝试所有可能的分裂点,选出让增益最大的特征和阈值进行分裂。第四阶段是给每棵新树计算叶子节点权重,让整体损失最小化。第五阶段是更新模型,把新树的预测结果按学习率缩放后加到之前的预测值上,然后回到第二阶段循环。
这个过程里最关键的就是第三个阶段的增益计算。XGBoost并不像传统GBDT那样去枚举所有特征的所有可能切分点,而是通过排序后扫描的方式,加上对缺失值自动学习默认方向,让分裂点搜索效率大幅提升。这也是为什么很多刷过XGBoost代码的人都会感叹,同样的数据量下它明显比早期GBDT快一个量级。
2. 赛前准备:环境搭建与通用数据流程
2.1 从注册账号到环境准备
Kaggle注册不了是新手问得最多的问题之一。如果你在注册时遇到验证码刷不出来或者页面加载缓慢,大概率是网络环境导致的,这时候可以排查一下浏览器插件拦截、更换网络环境、或者稍后再试。注册本身只需要邮箱验证,不需要绑手机,流程不算复杂。
环境上我建议直接用Anaconda管理Python环境,创建独立的虚拟环境来跑比赛,避免不同项目的依赖互相污染。安装XGBoost非常简单,在终端执行一行命令就行:
pip install xgboost如果想让训练跑得更快,可以安装GPU版本,需要先安装CUDA工具包,然后:
pip install xgboost-gpu这里要提醒一句,GPU版本不一定在所有机器上都比CPU版本快。如果你的数据集只有几千行,CPU版本反而更省事,因为数据在CPU和GPU之间传输也有开销。我自己的习惯是:数据集在10万行以上且特征多时开GPU,否则直接用CPU跑,调参时也不用来回折腾环境。
另外强烈建议安装Optuna,后面讲超参数自动设置时会用到:
pip install optuna2.2 数据探索不是走过场
拿到比赛数据,不要急着上模型。Kaggle上80%的比赛,EDA阶段都能让你发现一些隐藏的加分项。
先看数据规模、特征数量、目标变量分布、缺失值比例,然后用pandas做一些快速统计。重点看几个方面:目标变量是否均衡,如果不均衡后面要针对性处理;特征中是否存在大量缺失值,这会影响参数选择;特征之间是否有强相关性,这可能导致模型倾向某些特征。
二分类任务里尤其要注意正负样本的比例。很多新手拿到不平衡数据直接训练,结果模型把所有样本都预测成多数类,线上分数很难看。之前打过一场信用卡欺诈比赛,正样本不到1%,直接跑XGBoost得到的AUC只有0.6出头,处理完类别不均衡后直接冲到0.89。处理方式有很多,最简单的是调整scale_pos_weight参数,后面会详细讲。
数据探索还有一个容易被忽视的环节:检查训练集和测试集的分布差异。如果某个特征在训练集和测试集的取值范围、均值差异很大,说明这个特征可能不可靠,或者比赛主办方在构造数据时做了某种划分,这种人往往要在特征工程和验证策略上格外小心。
2.3 验证策略:交叉验证还是时序划分
验证策略是整个比赛流程中最容易被忽视、但影响最大的一个环节。验证集划分不合理,你做再多特征工程和调参都是白费功夫。
Kaggle上大多数比赛提供的是随机划分的训练集和测试集,这种情况下K折交叉验证是最稳妥的选择。我一般用5折或10折,根据数据量决定。数据量在1万行以内用10折,让每一折的训练数据更充分;数据量在10万行以上用5折就够了,再多的折数只会增加训练时间,收益很小。
但要注意,有些比赛的数据是带时间性质的,比如预测未来某个时间段的销量或点击率,这时候不能用随机K折,要用时序划分。时序划分的核心原则是训练数据必须早于验证数据,否则就是数据泄漏。
XGBoost做交叉验证可以直接用自带接口:
import xgboost as xgb from sklearn.model_selection import KFold kf = KFold(n_splits=5, shuffle=True, random_state=42) scores = [] for train_idx, valid_idx in kf.split(X_train): dtrain = xgb.DMatrix(X_train.iloc[train_idx], label=y_train[train_idx]) dvalid = xgb.DMatrix(X_train.iloc[valid_idx], label=y_train[valid_idx]) params = { 'objective': 'binary:logistic', 'eval_metric': 'auc', 'max_depth': 6, 'learning_rate': 0.01, 'subsample': 0.8, 'colsample_bytree': 0.8 } model = xgb.train(params, dtrain, num_boost_round=1000, evals=[(dvalid, 'valid')], early_stopping_rounds=50, verbose_eval=False) pred = model.predict(dvalid, iteration_range=(0, model.best_iteration + 1)) scores.append(roc_auc_score(y_train[valid_idx], pred))这里有个个人心得:交叉验证的随机种子要在每次实验之间保持一致,否则你改了特征之后分数的变化可能只是随机波动,导致误判特征的有效性。我一般固定random_state为42或2024,所有实验都用同一个种子,这样每次对比才有参考意义。
3. XGBoost核心参数详解与超参数自动设置
3.1 参数体系拆解
XGBoost参数多,但真正影响模型效果的其实就三类:树结构参数、正则化参数和学习控制参数。
树结构参数中最重要的是max_depth,控制树的最大深度。深度越大模型越复杂,越容易过拟合。Kaggle赛场上,Max_depth在3到8之间最常见,数据量大、特征多时可以适当加深,但超过12的情况很少。还有一个容易被忽略的是min_child_weight,它控制叶子节点所需的最小样本权重和,值越大模型越保守。处理噪声大、数据量小的比赛时,把这个参数调高能明显抑制过拟合。
正则化参数有reg_alpha和reg_lambda,分别对应L1和L2正则化系数,前面提到的目标函数里的正则项就是它们。这两个参数的主要作用是惩罚复杂模型,值越大模型越保守。XGBoost默认的reg_lambda是1,reg_alpha是0,实际使用时我通常会优先调reg_alpha,因为它能产生稀疏解,在特征较多时效果更好。
学习控制参数里最核心的是learning_rate(也叫eta)和n_estimators(也叫num_boost_round)。学习率控制每棵树贡献的权重,学习率越低,需要更多棵树来拟合,但泛化能力往往更好。标准做法是先用一个中等学习率如0.1快速跑出基线,再降到0.01甚至0.005配合early stopping做精调。subsample和colsample_bytree分别控制行采样和列采样比例,这俩其实也是正则化手段,能降低模型方差。
3.2 超参数自动设置:从网格搜索到贝叶斯优化
新手最纠结的就是参数怎么调。网格搜索(GridSearchCV)能理解,但效率太低,参数一多就组合爆炸。随机搜索比网格好一点,但整体上还是比较盲目。现在Kaggle上手效率最高的是贝叶斯优化,Optuna是最常用的实现库。
我用Optuna做XGBoost超参数自动设置时,一般这么写:
import optuna import xgboost as xgb from sklearn.model_selection import cross_val_score def objective(trial): params = { 'max_depth': trial.suggest_int('max_depth', 3, 10), 'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3, log=True), 'subsample': trial.suggest_float('subsample', 0.5, 1.0), 'colsample_bytree': trial.suggest_float('colsample_bytree', 0.5, 1.0), 'reg_alpha': trial.suggest_float('reg_alpha', 1e-8, 10.0, log=True), 'reg_lambda': trial.suggest_float('reg_lambda', 1e-8, 10.0, log=True), 'min_child_weight': trial.suggest_int('min_child_weight', 1, 20), 'n_estimators': trial.suggest_int('n_estimators', 100, 2000), 'random_state': 42 } model = xgb.XGBClassifier(**params, use_label_encoder=False, eval_metric='logloss') score = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc', n_jobs=-1).mean() return score study = optuna.create_study(direction='maximize') study.optimize(objective, n_trials=100) print(study.best_params)这里有个重要技巧:搜索空间不要一开始就设太大,先用较少的n_trials跑出大致方向,再缩小搜索范围精调。比如先固定learning_rate为0.1,只搜max_depth和subsample,确定这两个之后再调正则化参数。这样可以减少搜索时间,也能让参数组合更加合理。
实践中用过网格搜索和贝叶斯优化做过对比,同样的数据,网格搜索跑300次得到的最佳AUC是0.871,Optuna跑100次就达到了0.874,节省了将近70%的时间,效果反而更好。现在我在Kaggle上几乎全部用Optuna,只在最终确认参数组合时才手动调整一两个参数。
3.3 二分类模型和回归模型的关键配置差异
XGBoost同时支持分类和回归,但二分类和回归在目标函数、评估指标和参数选择上有明显差异。
二分类任务的objective通常设为binary:logistic,输出的是预测概率值,评估指标常用AUC或logloss。这个时候关注的重点是类别不平衡时的处理。最简单的方式是设置scale_pos_weight,它的计算公式是负样本数量除以正样本数量。这个值设置得当,模型会给少数类更高的权重,AUC往往能明显提升。
回归任务的objective设为reg:squarederror(原reg:linear在旧版本中已废弃),评估指标通常是RMSE或MAE。回归任务里要注意目标变量的分布,如果目标值严重右偏,先取对数变换再训练。之前打过一场销量预测比赛,目标值分布跨度非常大,直接训练RMSE惨不忍睹,上来直接对目标做log1p变换,分数立刻提升了一大截。预测完记得要做逆变换,否则提交结果会被判无效。
下面是我整理的参数配置速查表,适用大多数Kaggle的二分类和回归场景:
| 参数 | 二分类默认参考 | 回归默认参考 | 调整方向 |
|---|---|---|---|
| objective | binary:logistic | reg:squarederror | 根据任务类型切换 |
| eval_metric | auc / logloss | rmse / mae | 跟比赛评估指标走 |
| max_depth | 6 | 6 | 过拟合就降,欠拟合就升 |
| learning_rate | 0.01 | 0.01 | 精调阶段降到0.005 |
| subsample | 0.8 | 0.8 | 数据噪声大时降到0.6 |
| colsample_bytree | 0.8 | 0.8 | 特征多时降到0.5 |
| scale_pos_weight | 负样本/正样本 | 不需要 | 只在类别不平衡时用 |
| reg_alpha | 0 | 0 | 特征多时可开始调 |
| reg_lambda | 1 | 1 | 过拟合时增大 |
4. 实战全流程:从特征工程到提交得分
4.1 特征编码与数据清洗
很多玩XGBoost的人都是直接把数据塞进模型,这点其实挺浪费的。XGBoost虽然比很多传统模型更耐脏数据,但合理的数据清洗和特征工程依然能带来几个百分点的提升。
缺失值处理上,XGBoost本身对缺失值有自动学习默认方向的能力,所以不一定非要把缺失值填掉。但如果你对业务有一定理解,手动填充效果会更明确。数值型特征可以用中位数填充,类别特征可以用众数填充。还有一种做法是保留缺失值本身作为一个有效状态,让模型自己学习缺失情况下的分裂方向。
类别特征的处理,我强烈建议先用LabelEncoder把类别值转为整数。XGBoost天然支持数值型的类别特征,所以LabelEncoder就够了,不需要做OneHot。如果类别数量特别多,比如上万个类别,可以用目标编码(Target Encoding),也就是用该类别的目标变量均值替换原始类别值。不过目标编码有泄漏风险,必须放在交叉验证内部做,否则验证分数会虚高。
特征工程里还有一个高性价比的操作是构建交叉特征。比如你有年龄和收入两个特征,可以构造年龄分组乘收入等级。XGBoost虽然能自己学习特征交互,但显式构造出有业务含义的交叉特征,往往能让模型在少走弯路的同时获得更稳定的提升。
这里给出一个通用的特征工程流程:
- 删除唯一值过多的无序类别特征和ID类特征,这类特征对模型没有信息量还有过拟合风险。
- 对缺失率超过70%的特征,考虑直接删除或做缺失标志特征。
- 数值特征做统计特征的衍生,如groupby某个类别特征后的均值、标准差、计数。
- 对长尾分布特征做log1p变换或分箱处理。
- 最后做一次离群值处理,用IQR或百分位数法将极端值限定在合理范围内。
4.2 训练、早停与模型保存
进入模型训练阶段,核心要点就是早停机制。XGBoost原生支持early_stopping_rounds,当验证集分数连续多轮没有提升时,训练自动终止,避免白白训练几千棵树还过拟合。
用sklearn接口训练时,可以这样设置:
model = xgb.XGBClassifier( max_depth=6, learning_rate=0.01, n_estimators=2000, subsample=0.8, colsample_bytree=0.8, reg_alpha=0.1, reg_lambda=1.0, eval_metric='auc', use_label_encoder=False, random_state=42 ) model.fit( X_train, y_train, eval_set=[(X_valid, y_valid)], early_stopping_rounds=50, verbose=100 )这里有个细节要注意:early_stopping_rounds设置太小容易在局部波动中提前停止,通常设为50到100比较合适。当学习率很低时比如0.01,可能需要更多轮次才能看出趋势,early_stopping_rounds可以适当放大。
模型训练完成后务必保存,方便后续做模型融合或回溯分析。保存模型用原生接口的save_model或者joblib都可以:
import joblib joblib.dump(model, 'xgb_model.pkl')个人经验是,训练阶段把best_iteration记下来,预测时也要用对应的迭代次数。很多新手在训练时用了early stopping,但预测时忘了限制迭代次数,导致模型实际上还是用了全部2000棵树,效果大打折扣。
4.3 特征重要性与模型解释
XGBoost自带特征重要性接口,很多人在比赛里只用它来做特征筛选,但我觉得更重要的是用它来反推数据质量与业务逻辑。
训练完模型后,可以这样查看特征重要性:
importance = model.feature_importances_ feat_names = X_train.columns for name, imp in sorted(zip(feat_names, importance), key=lambda x: x[1], reverse=True)[:20]: print(f'{name}: {imp:.4f}')这里的feature_importances_是基于特征被选中做分裂的次数统计的,还有基于增益的weight、gain、cover等不同口径。我建议用gain类型的重要性来判断,也就是特征对模型损失减少的平均贡献,这个更符合特征的实质预测力。在xgb.train接口里可以设置importance_type='gain'来获取。
特征重要性排名能帮你发现一些有意思的问题。如果某个ID类特征重要性特别高,比如客户ID或者时间戳排名第一,那你就要警惕是不是发生了数据泄漏。之前打过一场比赛,有个用户在特征重要性里发现了一个疑似未来信息泄露的特征,最终巧妙地利用这个特征在公榜上拿到了极高的分数。这种操作在比赛里虽然争议不少,但确实能训练你对信息敏感度的判断力。
特征筛选上,重要性倒序排列后,去掉尾部贡献接近0的特征,往往能轻微提升泛化能力,同时加快训练速度。但注意不要一上来就大刀阔斧地删特征,先根据重要性从低到高逐步删除,每删一批就做一次交叉验证对比分数,用事实说话。
4.4 提交预测与分数提升策略
Kaggle的提交机制是:提交CSV文件到平台,格式包含ID列和预测值列。二分类任务提交的是预测概率,回归任务提交的是预测数值。
写CSV的时候一定要核对行数是否和测试集行数一致,ID列顺序是否和测试集一致。这个错误特别低级但特别常见,我见过太多人在截止前最后一小时因为CSV格式问题手忙脚乱。
快速提交时用pandas即可:
submission = pd.DataFrame({ 'id': test_id, 'target': pred_proba }) submission.to_csv('submission.csv', index=False)如果目标是二分类概率,直接用概率值提交,除非比赛明确要求0/1标签。很多比赛用AUC做评估,提交概率比提交类别值更合理。如果比赛要求输出类别,则需要自行定阈值,一般用0.5,但如果数据不平衡,可以去找验证集上让F1分数最大的阈值。
分数提升策略上,最稳定的路线是三段式:第一段用默认参数跑出一个基线分数,确保流程正确;第二段用贝叶斯优化精调参数,通常能提升0.5到1个百分点;第三段做模型融合,把XGBoost、LightGBM、CatBoost甚至一个简单的逻辑回归融合进来,加权平均或Stacking,通常还能再提升1到2个百分点。
模型融合里最简单的加权平均是这样做的:先用交叉验证得到每个模型的验证集预测概率,然后在验证集上搜索每个模型的权重,让融合后的AUC最大,得到最优权重后再把每个模型的测试集预测按相同权重融合。
from scipy.optimize import minimize def negative_auc(weights): weights = np.abs(weights) / np.sum(np.abs(weights)) blend = sum(w * pred for w, pred in zip(weights, preds_valid)) return -roc_auc_score(y_valid, blend) init_weights = np.ones(len(preds_valid)) / len(preds_valid) result = minimize(negative_auc, init_weights, method='Nelder-Mead') optimal_weights = np.abs(result.x) / np.sum(np.abs(result.x))5. 常见问题与排查技巧实录
5.1 过拟合症状识别与精准打击
XGBoost训练时最常见的坑是过拟合,典型症状是训练集分数越来越高,验证集分数先升后降,或者公榜分数远低于私榜分数。
过拟合的应对有一个由浅入深的排查顺序。第一看数据量是否太小,数据量只有几千行时,先降低max_depth到3或4,同时把min_child_weight调大到10以上,大概率能把过拟合压下来。第二看特征数量,如果特征有几百个,增加colsample_bytree的调参空间,设置成0.5左右让每棵树只看随机一半特征。第三看正则化,依次尝试reg_alpha从0.01到1、reg_lambda从1到10,观察验证集分数的变化。
有一条实战铁律:过拟合时不要一开始就去加更多数据或者做复杂的数据增强,先把模型变简单,看验证分数变化,这才是最可控的路径。我见过很多人一遇到分数下降就疯狂堆更多特征,结果越堆越差,最后才发现是模型复杂度过高的问题。
5.2 类别不平衡和数据泄漏的边界识别
类别不平衡是二分类比赛的常见陷阱,处理不当会直接导致预测概率分布偏移,AUC还能看,但logloss和实际排名会拉胯。
XGBoost处理不平衡最优雅的方式是scale_pos_weight参数,计算公式是负样本数除以正样本数。比如正样本只有1000个,负样本有9000个,scale_pos_weight就设成9。这个参数会让模型在分裂时更偏向少数类,效果通常比过采样好,因为它不会引入重复样本带来的偏差。如果效果还不够,可以结合自定义损失函数或者对少数类做SMOTE,但复杂度会上升,我建议先试scale_pos_weight,配合交叉验证对比分数。
另一个必须严格警惕的是数据泄漏。数据泄漏的典型特征包括:某个特征的预测力强到离谱,验证集AUC接近1;测试集中出现了训练集完全相同或高度相似的行;特征重要性里排名第一的变量从业务逻辑上不可能在预测时获得。发现这些情况时,要果断回到特征构建环节排查,把泄漏特征剔除重新训练,否则在公榜刷分一时爽,进入私榜评分环节往往排名断崖式下跌。
5.3 Kaggle平台操作技巧与提交注意点
Kaggle注册不了的问题前面提了,这里说一些平台操作的实用技巧。
Kaggle比赛一般分两个榜单,Public Leaderboard和Private Leaderboard。公开榜是测试集一部分数据的排名,截止后会用另一部分数据重新计算排名。很多新手盯着公开榜刷分,最后私榜排名大跌,就是因为过拟合了公开榜。我的策略是:公开榜分数只做参考,一切以本地交叉验证的分数为准。本地CV跟公开榜的趋势一致,说明验证策略可靠;如果两者相差很大,优先怀疑验证策略或数据分布差异。
提交次数在比赛中往往是受限的,比如每天5次,所以不要盲目提交测试集预测来试探分数。正确的做法是,任何模型改动都在本地交叉验证中完成评估,只有交叉验证分数确实提升时才提交一次确认。这样既能避免提交次数浪费,也能保持清晰的实验记录。
平台上的Notebook对于配置较低的本地电脑是很好的补充,可以把特征工程和训练流程放到上面跑。Kaggle Notebook自带GPU和TPU资源,虽然不能完全替代本地开发,但在需要大批量跑交叉验证或者集成模型时非常实用。
5.4 实战心法:XGBoost之外你还需要什么
最后说说比工具本身更重要的东西。XGBoost只是工具,能让你在Kaggle上真正赢下比赛的是完整的方法论。
首先要有清晰的实验记录习惯。我每场比赛都维护一个实验日志,记录每次修改的内容、交叉验证分数、公开榜分数、备注信息。没有记录的实验等于白做,因为你永远不知道自己改了什么导致分数提升了。
其次要注重基线系统的完整性。很多新手急于调参,实际上跑通一个完整的基线流程:数据加载、特征工程、交叉验证、模型训练、提交文件生成,是最重要的一步。基线的意义在于给你一个可对照的锚点,所有后续改进都基于基线的分数对比。
最后要懂得复盘。每场Kaggle比赛结束后,去翻看Top队伍的解决方案分享,学习他们的特征构造思路和模型融合手段,把这些经验沉淀下来。Kaggle的价值不在于拿了多少块奖牌,而在于每一场比赛都在逼你把从数据清洗、特征构建、模型训练到结果分析的全链路能力反复打磨一遍。
在我个人经历里,XGBoost是带我走进机器学习实战的第一把钥匙,也是我推荐给所有结构化数据比赛新手的首选武器。把它的核心参数、调优逻辑和常见坑位都摸透之后,你再去看LightGBM、CatBoost,甚至深度学习模型,都会有一种触类旁通的通透感。希望这篇内容能帮你少走一些弯路,早日拿下属于你的第一枚Kaggle奖牌。