XGBoost案例之红酒品质分类-模型评测
1 课程概览
本课使用XGBoost完成红酒品质分类的模型评测部分。重点介绍StratifiedKFold分层交叉验证,处理样本不均衡情况下的交叉验证问题。
2 核心概念与定义
- StratifiedKFold:分层K折交叉验证,保持原始比例。
- 分层抽取:每次抽取保持和原始数据相同的比例。
- 传统CV:适用于样本均衡的情况。
- 网格搜索:GridSearchCV,寻找最优参数。
3 算法与模型详解
3.1 分层交叉验证
问题:传统CV在样本不均衡时会有偏差
示例:
- 数据分布:600、700、700、200、10、20、30、50、5
- 传统抽取:抽前几分之一,容易抽不到少数类
- 分层抽取:保持原始比例,每个类别都能抽到
StratifiedKFold:
- 分成10份时,每份的占比与原始数据相同
- 例如:抽100条,从各类别按比例抽30、40、20、10
3.2 传统CV vs 分层CV
| 特性 | 传统CV | StratifiedKFold |
|---|---|---|
| 适用 | 样本均衡 | 样本不均衡 |
| 抽取 | 随机抽取 | 按比例抽取 |
| 少数类 | 可能抽不到 | 一定能抽到 |
| 比例 | 不保证 | 保持原始比例 |
3.3 网格搜索
参数:
GridSearchCV(
estimator,
param_grid,
cv=StratifiedKFold() # 使用分层交叉验证
)
3.4 评测流程
- 加载模型
- 创建网格搜索对象
- 使用StratifiedKFold作为cv
- 训练和评估
- 输出最优参数
4 代码示例
import pandas as pd
import numpy as np
import xgboost as xgb
from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.metrics import accuracy_score, classification_report
from sklearn.utils.class_weight import compute_sample_weight
import joblib
# 1. 加载数据和模型
def load_data_and_model():
"""加载数据和模型"""
print("=== 加载数据和模型 ===")
# 加载数据
train_data = pd.read_csv('data/wine_train.csv')
test_data = pd.read_csv('data/wine_test.csv')
X_train = train_data.iloc[:, :-1]
y_train = train_data.iloc[:, -1]
X_test = test_data.iloc[:, :-1]
y_test = test_data.iloc[:, -1]
# 加载模型
model = joblib.load('model/xgboost_wine_model.pkl')
print(f"训练集: {X_train.shape}")
print(f"测试集: {X_test.shape}")
return X_train, X_test, y_train, y_test, model
# 2. 传统交叉验证 vs 分层交叉验证
def compare_cv_methods(X, y):
"""对比传统CV和分层CV"""
print("\n=== 对比CV方法 ===")
from sklearn.model_selection import KFold, StratifiedKFold
# 传统K折
kf = KFold(n_splits=5, shuffle=True, random_state=42)
print("\n传统K折(每折类别分布):")
for fold, (train_idx, val_idx) in enumerate(kf.split(X)):
y_val = y.iloc[val_idx]
print(f" 折{fold+1}: {dict(y_val.value_counts().sort_index())}")
# 分层K折
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
print("\n分层K折(每折类别分布):")
for fold, (train_idx, val_idx) in enumerate(skf.split(X, y)):
y_val = y.iloc[val_idx]
print(f" 折{fold+1}: {dict(y_val.value_counts().sort_index())}")
# 3. 网格搜索调参
def grid_search_with_stratified_cv(X_train, y_train, X_test, y_test):
"""使用分层交叉验证的网格搜索"""
print("\n=== 网格搜索调参(分层CV)===")
# 创建模型
model = xgb.XGBClassifier(
objective='multi:softprob',
num_class=6,
random_state=22
)
# 参数网格
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [3, 6, 9],
'learning_rate': [0.01, 0.1, 0.2]
}
# 分层交叉验证
stratified_cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 网格搜索
grid_search = GridSearchCV(
estimator=model,
param_grid=param_grid,
cv=stratified_cv, # 使用分层CV
scoring='accuracy',
n_jobs=-1,
verbose=1
)
# 平衡权重
sample_weight = compute_sample_weight(class_weight='balanced', y=y_train)
# 训练
grid_search.fit(X_train, y_train, sample_weight=sample_weight)
# 结果
print(f"\n最优参数: {grid_search.best_params_}")
print(f"最优得分: {grid_search.best_score_:.4f}")
# 最优模型评估
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(f"测试集准确率: {acc:.4f}")
return best_model
# 4. 对比不同CV方法的效果
def compare_cv_effects(X_train, y_train, X_test, y_test):
"""对比不同CV方法的效果"""
print("\n=== 对比不同CV方法的效果 ===")
sample_weight = compute_sample_weight(class_weight='balanced', y=y_train)
# 传统CV
print("\n--- 传统CV ---")
model1 = xgb.XGBClassifier(
n_estimators=100,
max_depth=6,
learning_rate=0.1,
random_state=22
)
model1.fit(X_train, y_train, sample_weight=sample_weight)
y_pred1 = model1.predict(X_test)
acc1 = accuracy_score(y_test, y_pred1)
print(f"准确率: {acc1:.4f}")
# 分层CV
print("\n--- 分层CV ---")
model2 = xgb.XGBClassifier(
n_estimators=100,
max_depth=6,
learning_rate=0.1,
random_state=22
)
stratified_cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 使用分层CV进行交叉验证评估
from sklearn.model_selection import cross_val_score
cv_scores = cross_val_score(
model2, X_train, y_train,
cv=stratified_cv,
scoring='accuracy'
)
print(f"交叉验证得分: {cv_scores}")
print(f"平均得分: {cv_scores.mean():.4f} ± {cv_scores.std():.4f}")
# 训练最终模型
model2.fit(X_train, y_train, sample_weight=sample_weight)
y_pred2 = model2.predict(X_test)
acc2 = accuracy_score(y_test, y_pred2)
print(f"测试集准确率: {acc2:.4f}")
# 5. 详细评估
def detailed_evaluation(model, X_test, y_test):
"""详细评估"""
print("\n=== 详细评估 ===")
y_pred = model.predict(X_test)
# 准确率
acc = accuracy_score(y_test, y_pred)
print(f"准确率: {acc:.4f}")
# 分类报告
print("\n分类报告:")
print(classification_report(y_test, y_pred))
# 混淆矩阵
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred)
print("混淆矩阵:")
print(cm)
# 6. 主函数
def main():
"""主函数"""
print("=" * 50)
print("XGBoost - 红酒品质分类(模型评测)")
print("=" * 50)
# 1. 加载数据和模型
X_train, X_test, y_train, y_test, model = load_data_and_model()
# 2. 对比CV方法
compare_cv_methods(X_train, y_train)
# 3. 网格搜索调参
best_model = grid_search_with_stratified_cv(X_train, y_train, X_test, y_test)
# 4. 对比不同CV效果
compare_cv_effects(X_train, y_train, X_test, y_test)
# 5. 详细评估
detailed_evaluation(best_model, X_test, y_test)
# 6. 保存最优模型
joblib.dump(best_model, 'model/xgboost_wine_best_model.pkl')
print("\n最优模型已保存")
if __name__ == '__main__':
main()
输出示例:
==================================================
XGBoost - 红酒品质分类(模型评测)
==================================================
=== 加载数据和模型 ===
训练集: (2615, 11)
测试集: (654, 11)
=== 对比CV方法 ===
传统K折(每折类别分布):
折1: {0: 2, 1: 25, 2: 245, 3: 200, 4: 40, 5: 5}
折2: {0: 3, 1: 28, 2: 250, 3: 195, 4: 38, 5: 6}
...
分层K折(每折类别分布):
折1: {0: 3, 1: 26, 2: 248, 3: 202, 4: 38, 5: 6}
折2: {0: 3, 1: 26, 2: 248, 3: 202, 4: 38, 5: 6}
...
=== 网格搜索调参(分层CV)===
Fitting 5 folds for each of 27 candidates, totalling 135 fits
最优参数: {'learning_rate': 0.1, 'max_depth': 6, 'n_estimators': 200}
最优得分: 0.6234
测试集准确率: 0.6789
=== 对比不同CV方法的效果 ===
--- 传统CV ---
准确率: 0.6544
--- 分层CV ---
交叉验证得分: [0.62 0.63 0.61 0.64 0.62]
平均得分: 0.6240 ± 0.0120
测试集准确率: 0.6789
=== 详细评估 ===
准确率: 0.6789
分类报告:
precision recall f1-score support
...
5 重难点与易错提醒
- ❗重点:StratifiedKFold保持原始比例。
- ❗重点:样本不均衡时使用分层CV。
- ❗重点:GridSearchCV的cv参数可以传入StratifiedKFold对象。
- ❗重点:分层CV每个类别都能抽到。
- ⚠️易错:样本不均衡时使用传统CV导致偏差。
- ⚠️易错:忘记在GridSearchCV中传入分层CV。
- 💡深入理解:分层CV保证每折的类别分布与原始数据相似。
6 课堂问答精选
Q: StratifiedKFold和传统KFold有什么区别?
A:
- 传统KFold:随机抽取,适用于样本均衡的情况
- StratifiedKFold:分层抽取,保持原始比例,适用于样本不均衡的情况
例如:数据有1000个样本,类别A占80%,类别B占20%。传统KFold可能某一折全是类别A;而StratifiedKFold保证每折都是80%类别A + 20%类别B。
Q: 如何在GridSearchCV中使用分层CV?
A:
stratified_cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
grid_search = GridSearchCV(estimator, param_grid, cv=stratified_cv)
将StratifiedKFold对象传给cv参数即可。
7 本课小结
- StratifiedKFold:分层交叉验证,保持原始比例。
- 适用场景:样本不均衡。
- 使用方法:传给GridSearchCV的cv参数。
- 效果:每个类别都能抽到,评估更准确。
8 延伸思考与实践
- 实践:运行分层CV的网格搜索。
- 预习:朴素贝叶斯算法。
- 思考:为什么分层CV在样本不均衡时效果更好?