共计 2777 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点分析
美赛 C 题通常涉及复杂的社会经济或环境系统建模,数据特点包括:

- 混合型数据:同时包含数值型(如温度、GDP)和类别型(如地区、政策等级)变量
- 缺失值常见:调查数据常存在 10%-30% 的缺失比例
- 多维度特征:原始特征可能达 50+ 个,但有效特征有限
新手常见问题:
- 直接使用原始数据导致树结构过于复杂
- 对类别型特征简单赋值(如用 1 - 5 代替红 / 黄 / 蓝)造成信息损失
- 忽略特征间的交互作用
算法选型依据
主流决策树算法对比:
| 算法 | 分裂标准 | 支持特征类型 | 适用场景 |
|---|---|---|---|
| ID3 | 信息增益 | 仅类别型 | 分类问题 |
| C4.5 | 信息增益比 | 混合类型 | 分类优先 |
| CART | 基尼系数 | 混合类型 | 分类 / 回归 |
选择 scikit-learn 的 DecisionTreeClassifier 原因:
- 默认使用 CART 算法,兼容数值和类别特征
- 提供 pruning 等防过拟合机制
- 与 sklearn 生态无缝衔接(管道 /Pipeline)
核心实现步骤
数据预处理
# 缺失值处理
from sklearn.impute import SimpleImputer
# 数值列用中位数填充
num_imputer = SimpleImputer(strategy='median')
df[num_cols] = num_imputer.fit_transform(df[num_cols])
# 类别列用众数填充
cat_imputer = SimpleImputer(strategy='most_frequent')
df[cat_cols] = cat_imputer.fit_transform(df[cat_cols])
# 类别特征编码
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(handle_unknown='ignore')
cat_encoded = encoder.fit_transform(df[cat_cols]).toarray()
# 特征合并
import numpy as np
X = np.hstack([df[num_cols].values, cat_encoded])
y = df['target'].values
模型构建
from sklearn.tree import DecisionTreeClassifier
# 关键参数说明
model = DecisionTreeClassifier(
max_depth=5, # 控制树深防过拟合
min_samples_split=10, # 节点最少样本数
criterion='gini', # 也可选 'entropy'
class_weight='balanced' # 处理类别不平衡
)
model.fit(X_train, y_train)
可视化实现
from sklearn.tree import export_graphviz
import graphviz
dot_data = export_graphviz(
model,
out_file=None,
feature_names=num_cols + encoder.get_feature_names_out().tolist(),
class_names=['Class0','Class1'],
filled=True
)
graph = graphviz.Source(dot_data)
graph.render('decision_tree') # 生成 PDF 文件
模型调优策略
网格搜索示例
from sklearn.model_selection import GridSearchCV
param_grid = {'max_depth': [3, 5, 7],
'min_samples_split': [5, 10, 15],
'criterion': ['gini', 'entropy']
}
grid = GridSearchCV(DecisionTreeClassifier(),
param_grid,
cv=5, # 5 折交叉验证
scoring='f1_macro' # 美赛常用指标
)
grid.fit(X_train, y_train)
print(f'最佳参数:{grid.best_params_}')
剪枝技术
- 预剪枝(Pre-pruning):
- 通过 max_depth 等参数限制生长
-
使用 min_impurity_decrease 设置分裂阈值
-
后剪枝(Post-pruning):
path = model.cost_complexity_pruning_path(X_train, y_train) ccp_alphas = path.ccp_alphas pruned_models = [] for ccp_alpha in ccp_alphas: pruned_model = DecisionTreeClassifier(ccp_alpha=ccp_alpha) pruned_model.fit(X_train, y_train) pruned_models.append(pruned_model)
特征重要性分析
import matplotlib.pyplot as plt
importance = model.feature_importances_
feat_names = num_cols + encoder.get_feature_names_out().tolist()
plt.figure(figsize=(10,6))
plt.barh(range(len(importance)), importance, tick_label=feat_names)
plt.title('Feature Importance')
plt.show()
竞赛避坑指南
类别不平衡处理
- 设置 class_weight 参数
- 使用 SMOTE 过采样:
from imblearn.over_sampling import SMOTE smote = SMOTE(random_state=42) X_res, y_res = smote.fit_resample(X_train, y_train)
连续特征分箱
# 等宽分箱
pd.cut(df['temperature'], bins=5)
# 等频分箱
pd.qcut(df['income'], q=4)
模型序列化
import joblib
# 保存完整 pipeline(含预处理和模型)joblib.dump(pipeline, 'submission_model.pkl')
# 比赛提交时同时保存:# 1. 模型文件
# 2. 预处理参数(如分箱边界、编码映射)# 3. 使用的 Python 版本说明
延伸思考
- 如何处理基数超过 100 的分类特征?(如城市名称)
- 当特征间存在强相关性时,决策树表现会受什么影响?
- 如何将决策树模型转化为可解释的决策规则?
通过以上流程,参赛者可系统性地完成从数据清洗到模型部署的全过程,避免常见失误。建议在实际应用中先构建基线模型,再逐步迭代优化。
正文完
