当前位置: 首页 > article >正文

【机器学习实战】kaggle背包价格预测(堆叠的实战用法)

本人主页:机器学习司猫白

机器学习专栏:机器学习实战

PyTorch入门专栏:PyTorch入门

深度学习实战:深度学习

ok,话不多说,我们进入正题吧

本次分享基于一文搞懂:什么是Stacking堆叠?手把手带你搭建堆叠模型,附有python源码和数据集。

数据集说明

该竞赛的数据集是学生背包价格预测数据集中训练的深度学习模型生成的。特征分布与原始分布接近但不完全相同。请随意将原始数据集用作本竞赛的一部分,既要探索差异,又要查看将原始内容纳入训练是否可以改善模型性能。

文件

  • Train.CSV-培训数据集;Price是目标
  • train_extra.csv-更多的培训数据!
  • test.csv-测试数据集;您的目标是预测Price每一行
  • sample_submission.csv-以正确格式的示例提交文件。

源码解析

第一步,打开数据文件

import numpy as np 
import pandas as pd 

train_df = pd.read_csv('/kaggle/input/playground-series-s5e2/train.csv')
test_df = pd.read_csv('/kaggle/input/playground-series-s5e2/test.csv')
train_extra_df = pd.read_csv('/kaggle/input/playground-series-s5e2/training_extra.csv')

通过info()函数,查看数据基本信息。

 
train_df.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 300000 entries, 0 to 299999
Data columns (total 11 columns):
 #   Column                Non-Null Count   Dtype  
---  ------                --------------   -----  
 0   id                    300000 non-null  int64  
 1   Brand                 290295 non-null  object 
 2   Material              291653 non-null  object 
 3   Size                  293405 non-null  object 
 4   Compartments          300000 non-null  float64
 5   Laptop Compartment    292556 non-null  object 
 6   Waterproof            292950 non-null  object 
 7   Style                 292030 non-null  object 
 8   Color                 290050 non-null  object 
 9   Weight Capacity (kg)  299862 non-null  float64
 10  Price                 300000 non-null  float64
dtypes: float64(3), int64(1), object(7)
memory usage: 25.2+ MB
train_extra_df.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3694318 entries, 0 to 3694317
Data columns (total 11 columns):
 #   Column                Dtype  
---  ------                -----  
 0   id                    int64  
 1   Brand                 object 
 2   Material              object 
 3   Size                  object 
 4   Compartments          float64
 5   Laptop Compartment    object 
 6   Waterproof            object 
 7   Style                 object 
 8   Color                 object 
 9   Weight Capacity (kg)  float64
 10  Price                 float64
dtypes: float64(3), int64(1), object(7)
memory usage: 310.0+ MB

test_df.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 200000 entries, 0 to 199999
Data columns (total 10 columns):
 #   Column                Non-Null Count   Dtype  
---  ------                --------------   -----  
 0   id                    200000 non-null  int64  
 1   Brand                 193773 non-null  object 
 2   Material              194387 non-null  object 
 3   Size                  195619 non-null  object 
 4   Compartments          200000 non-null  float64
 5   Laptop Compartment    195038 non-null  object 
 6   Waterproof            195189 non-null  object 
 7   Style                 194847 non-null  object 
 8   Color                 193215 non-null  object 
 9   Weight Capacity (kg)  199923 non-null  float64
dtypes: float64(2), int64(1), object(7)
memory usage: 15.3+ MB

通过Info()可以发现,Brand,Material,Size,Laptop Compartment,Waterproof,Style,Color,Weight Capacity (kg)列在数据集中有少量的缺失值。

第二步,数据预处理

num_columns = ['Compartments', 'Weight Capacity (kg)']
object_columns = ['Brand', 'Material', 'Size', 'Laptop Compartment', 'Waterproof', 'Style', 'Color']

# Fill missing values in numeric columns with the median
for col in num_columns:
    test_df[col] = test_df[col].fillna(test_df[col].median())

# Fill missing values in numeric columns with the median
for col in num_columns:
    train_df[col] = train_df[col].fillna(train_df[col].median())

# Fill missing values in object columns with 'unknown'
for col in object_columns:
    test_df[col] = test_df[col].fillna('unknown')

# Fill missing values in object columns with 'unknown'
for col in object_columns:
    train_df[col] = train_df[col].fillna('unknown')

为了将训练集和测试集维度保持一致,这里选择了填充处理,也可以对训练集进行删除缺失值操作,对测试集使用值进行填充也能保持维度一致。并且由于考虑到使用堆叠,因此不使用train_extra_df数据集,该数据集数据量较大,影响运算速度。接下来,准备对object类型数据进行编码操作。

# 遍历所有object类型的字段,查看这些字段的unique()值
for column in train_df.select_dtypes(include=['object']).columns:
    unique_values = train_df[column].unique()
    print(f"Unique values in '{column}': {unique_values}")
Unique values in 'Brand': ['Jansport' 'Under Armour' 'Nike' 'Adidas' 'Puma' 'unknown']
Unique values in 'Material': ['Leather' 'Canvas' 'Nylon' 'unknown' 'Polyester']
Unique values in 'Size': ['Medium' 'Small' 'Large' 'unknown']
Unique values in 'Laptop Compartment': ['Yes' 'No' 'unknown']
Unique values in 'Waterproof': ['No' 'Yes' 'unknown']
Unique values in 'Style': ['Tote' 'Messenger' 'unknown' 'Backpack']
Unique values in 'Color': ['Black' 'Green' 'Red' 'Blue' 'Gray' 'Pink' 'unknown']

根据object类型数据的值,可以放心进行独热编码,不会因为独热编码导致维度爆炸。

train = pd.get_dummies(train_df, columns=object_columns, drop_first=True, dtype=int)
test  = pd.get_dummies( test_df, columns=object_columns, drop_first=True, dtype=int)


train.shape,test.shape
((300000, 29), (200000, 28))

编码完毕。

第三步,进行模型训练

from sklearn.model_selection import train_test_split, KFold
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import LinearRegression
import lightgbm as lgb
import xgboost as xgb
from catboost import CatBoostRegressor
import optuna
import numpy as np

# 数据准备
x = train.drop(columns=['Price','id'])
y = train['Price']
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)

# ================== 定义三个模型的超参数优化函数 ==================

# LightGBM优化函数
def objective_lgb(trial):
    params = {
        'objective': 'regression',
        'boosting_type': 'gbdt',
        'max_depth': trial.suggest_int('max_depth', 3, 8),
        'num_leaves': trial.suggest_int('num_leaves', 20, 150),
        'min_child_samples': trial.suggest_int('min_child_samples', 20, 200),
        'learning_rate': trial.suggest_float('learning_rate', 1e-4, 0.05),
        'subsample': trial.suggest_float('subsample', 0.6, 1.0),
        'colsample_bytree': trial.suggest_float('colsample_bytree', 0.6, 1.0),
        'reg_alpha': trial.suggest_float('reg_alpha', 1e-2, 50.0),
        'reg_lambda': trial.suggest_float('reg_lambda', 1e-2, 50.0),
        'random_state': 42
    }
    model = lgb.LGBMRegressor(**params)
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    return np.sqrt(mean_squared_error(y_test, pred))

# XGBoost优化函数
def objective_xgb(trial):
    params = {
        'objective': 'reg:squarederror',
        'max_depth': trial.suggest_int('max_depth', 3, 8),
        'learning_rate': trial.suggest_float('learning_rate', 1e-4, 0.1),
        'subsample': trial.suggest_float('subsample', 0.6, 1.0),
        'colsample_bytree': trial.suggest_float('colsample_bytree', 0.6, 1.0),
        'alpha': trial.suggest_float('alpha', 1e-2, 50.0),
        'lambda': trial.suggest_float('lambda', 1e-2, 50.0),
        'n_estimators': trial.suggest_int('n_estimators', 100, 1000),
        'random_state': 42
    }
    model = xgb.XGBRegressor(**params)
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    return np.sqrt(mean_squared_error(y_test, pred))

# CatBoost优化函数
def objective_cat(trial):
    params = {
        'loss_function': 'RMSE',
        'depth': trial.suggest_int('depth', 3, 8),
        'learning_rate': trial.suggest_float('learning_rate', 1e-4, 0.1),
        'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 1e-2, 50.0),
        'subsample': trial.suggest_float('subsample', 0.6, 1.0),
        'iterations': trial.suggest_int('iterations', 100, 1000),
        'verbose': False,
        'random_state': 42
    }
    model = CatBoostRegressor(**params)
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    return np.sqrt(mean_squared_error(y_test, pred))

# ================== 执行超参数优化 ==================
# 优化LightGBM
study_lgb = optuna.create_study(direction='minimize')
study_lgb.optimize(objective_lgb, n_trials=50)
best_lgb = study_lgb.best_params
best_lgb['random_state'] = 42

# 优化XGBoost
study_xgb = optuna.create_study(direction='minimize')
study_xgb.optimize(objective_xgb, n_trials=50)
best_xgb = study_xgb.best_params
best_xgb['random_state'] = 42

# 优化CatBoost
study_cat = optuna.create_study(direction='minimize')
study_cat.optimize(objective_cat, n_trials=50)
best_cat = study_cat.best_params
best_cat['verbose'] = False
best_cat['random_state'] = 42

# ================== 基模型定义 ==================
lgb_model = lgb.LGBMRegressor(**best_lgb)
xgb_model = xgb.XGBRegressor(**best_xgb)
cat_model = CatBoostRegressor(**best_cat)

# ================== 生成堆叠特征 ==================
def generate_stacking_features(model, X_train, y_train, X_test, n_splits=5):
    kf = KFold(n_splits=n_splits, shuffle=True, random_state=42)
    oof_train = np.zeros(X_train.shape[0])
    oof_test = np.zeros(X_test.shape[0])
    
    for train_idx, val_idx in kf.split(X_train):
        X_tr, y_tr = X_train.iloc[train_idx], y_train.iloc[train_idx]
        X_val = X_train.iloc[val_idx]
        
        model.fit(X_tr, y_tr)
        oof_train[val_idx] = model.predict(X_val)
        oof_test += model.predict(X_test)
    
    oof_test /= n_splits
    return oof_train.reshape(-1, 1), oof_test.reshape(-1, 1)

# 生成各模型的OOF特征
lgb_oof, lgb_test = generate_stacking_features(lgb_model, X_train, y_train, X_test)
xgb_oof, xgb_test = generate_stacking_features(xgb_model, X_train, y_train, X_test)
cat_oof, cat_test = generate_stacking_features(cat_model, X_train, y_train, X_test)

# 堆叠特征合并
stacked_X_train = np.concatenate([lgb_oof, xgb_oof, cat_oof], axis=1)
stacked_X_test = np.concatenate([lgb_test, xgb_test, cat_test], axis=1)

# ================== 训练元模型 ==================
meta_model = LinearRegression()
meta_model.fit(stacked_X_train, y_train)

# ================== 评估堆叠模型 ==================
stacked_pred = meta_model.predict(stacked_X_test)
rmse = np.sqrt(mean_squared_error(y_test, stacked_pred))

print("LightGBM最佳参数:", best_lgb)
print("XGBoost最佳参数:", best_xgb)
print("CatBoost最佳参数:", best_cat)
print(f"堆叠模型的RMSE: {rmse:.5f}")
LightGBM最佳参数: {'max_depth': 6, 'num_leaves': 102, 'min_child_samples': 29, 'learning_rate': 0.04811541008025368, 'subsample': 0.6373168094008929, 'colsample_bytree': 0.6287351039931749, 'reg_alpha': 24.94216574124474, 'reg_lambda': 13.3381875824349, 'random_state': 42}
XGBoost最佳参数: {'max_depth': 4, 'learning_rate': 0.0242976335510153, 'subsample': 0.7392383751300301, 'colsample_bytree': 0.7081780053291772, 'alpha': 1.3548669745281217, 'lambda': 0.1300423102945354, 'n_estimators': 445, 'random_state': 42}
CatBoost最佳参数: {'depth': 4, 'learning_rate': 0.06257815809830002, 'l2_leaf_reg': 23.148679122356665, 'subsample': 0.6957264471620155, 'iterations': 410, 'verbose': False, 'random_state': 42}
堆叠模型的RMSE: 38.90021

这里实现了一个多模型堆叠(Stacking)的方法,结合了三种流行的机器学习模型:LightGBM、XGBoost和CatBoost,并使用Optuna库对这些模型进行超参数优化。然后,将这些模型的预测结果作为特征,通过一个线性回归模型作为元学习器(Meta-model)进行二次预测。以下是详细的解释:

1. 数据准备

x = train.drop(columns=['Price', 'id'])
y = train['Price']
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
  • x 是特征数据,y 是目标变量(Price)。通过 train_test_split 将数据集分割为训练集(80%)和测试集(20%)。

2. 定义超参数优化函数

分别为 LightGBM、XGBoost 和 CatBoost 定义了三个优化函数,每个函数都利用 Optuna 自动调整模型的超参数,选择最优超参数以便得到最小化的RMSE(均方根误差)作为评估指标。

- LightGBM优化函数 (objective_lgb)
def objective_lgb(trial):
    params = {
        'objective': 'regression',
        'boosting_type': 'gbdt',
        ...
    }
    model = lgb.LGBMRegressor(**params)
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    return np.sqrt(mean_squared_error(y_test, pred))
  • Optuna会在给定范围内选择合适的超参数,例如 max_depthnum_leaveslearning_rate 等,训练模型后在测试集上预测并返回 RMSE
- XGBoost优化函数 (objective_xgb)
def objective_xgb(trial):
    params = {
        'objective': 'reg:squarederror',
        ...
    }
    model = xgb.XGBRegressor(**params)
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    return np.sqrt(mean_squared_error(y_test, pred))
  • 与 LightGBM 类似,XGBoost 使用 Optuna 进行超参数优化,训练模型并计算 RMSE
- CatBoost优化函数 (objective_cat)
def objective_cat(trial):
    params = {
        'loss_function': 'RMSE',
        ...
    }
    model = CatBoostRegressor(**params)
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    return np.sqrt(mean_squared_error(y_test, pred))
CatBoost的优化与上述类似,使用 Optuna 进行超参数优化。

3. 执行超参数优化

study_lgb.optimize(objective_lgb, n_trials=50)
best_lgb = study_lgb.best_params
study_xgb.optimize(objective_xgb, n_trials=50)
best_xgb = study_xgb.best_params
study_cat.optimize(objective_cat, n_trials=50)
best_cat = study_cat.best_params
  • 每个模型使用 optimize 方法执行超参数优化,n_trials=50 表示进行50次不同超参数组合的尝试,选出每个模型的最佳参数。

4. 定义基模型

lgb_model = lgb.LGBMRegressor(**best_lgb)
xgb_model = xgb.XGBRegressor(**best_xgb)
cat_model = CatBoostRegressor(**best_cat)
  • 使用优化后的最佳超参数初始化三个基模型。

5. 生成堆叠特征

def generate_stacking_features(model, X_train, y_train, X_test, n_splits=5):
    kf = KFold(n_splits=n_splits, shuffle=True, random_state=42)
    oof_train = np.zeros(X_train.shape[0])
    oof_test = np.zeros(X_test.shape[0])
    
    for train_idx, val_idx in kf.split(X_train):
        X_tr, y_tr = X_train.iloc[train_idx], y_train.iloc[train_idx]
        X_val = X_train.iloc[val_idx]
        
        model.fit(X_tr, y_tr)
        oof_train[val_idx] = model.predict(X_val)
        oof_test += model.predict(X_test)
    
    oof_test /= n_splits
    return oof_train.reshape(-1, 1), oof_test.reshape(-1, 1)
  • 生成堆叠特征的关键步骤
    1. 使用 KFold 交叉验证将训练数据分为5折,每折训练模型并在验证集上预测。
    2. 将每一折的验证集预测结果(Out-Of-Fold, OOF)收集到 oof_train 中,同时将每一折在测试集上的预测结果累加到 oof_test 中。
    3. oof_test 是对整个测试集的平均预测结果,避免过拟合。
    4. 最终返回训练集的OOF特征和测试集的OOF预测结果。

6. 堆叠特征合并

stacked_X_train = np.concatenate([lgb_oof, xgb_oof, cat_oof], axis=1)
stacked_X_test = np.concatenate([lgb_test, xgb_test, cat_test], axis=1)
  • 将各个模型的OOF特征合并成一个新的训练集 stacked_X_train 和新的测试集 stacked_X_test。这些特征将作为元学习器的输入。

7. 训练元模型(线性回归)

meta_model = LinearRegression()
meta_model.fit(stacked_X_train, y_train)
  • 这里使用 线性回归 作为元学习器(Meta-model),它接收来自三个基模型的OOF特征作为输入,进行二次训练。

8. 评估堆叠模型

stacked_pred = meta_model.predict(stacked_X_test)
rmse = np.sqrt(mean_squared_error(y_test, stacked_pred))
  • 使用元模型对测试集进行预测,并计算预测结果的 RMSE

9. 打印结果

print("LightGBM最佳参数:", best_lgb)
print("XGBoost最佳参数:", best_xgb)
print("CatBoost最佳参数:", best_cat)
print(f"堆叠模型的RMSE: {rmse:.5f}")
  • 输出最佳超参数和堆叠模型的最终评估结果。

堆叠模型的具体操作流程总结:

  1. 选择多个基模型:选定一组不同的机器学习模型(如 LightGBM, XGBoost, CatBoost),并对它们进行超参数优化,找到最佳模型配置。
  2. 训练基模型并生成OOF特征:通过 KFold 交叉验证生成每个基模型的OOF特征和对测试集的预测结果。
  3. 堆叠特征合并:将不同基模型生成的OOF特征合并成新的训练集和测试集,用于训练元学习器。
  4. 训练元模型:选择一个简单的模型(如线性回归)作为元学习器,对堆叠特征进行训练。
  5. 评估堆叠模型:使用元模型对测试集进行预测,并通过计算 RMSE 等指标评估性能。

第四步,预测测试集,并将结果保存为csv文件

test2 = test.drop(columns=['id'])
# 使用堆叠模型进行预测
# 生成test2数据的OOF特征(需要对test2数据使用训练好的模型进行预测)
lgb_oof_test2, lgb_test2 = generate_stacking_features(lgb_model, X_train, y_train, test2)
xgb_oof_test2, xgb_test2 = generate_stacking_features(xgb_model, X_train, y_train, test2)
cat_oof_test2, cat_test2 = generate_stacking_features(cat_model, X_train, y_train, test2)

# 堆叠特征合并
stacked_X_test2 = np.concatenate([lgb_test2, xgb_test2, cat_test2], axis=1)

# 使用训练好的元模型进行预测
predictions = meta_model.predict(stacked_X_test2)

# 获取test2的id列
ids = test['id'].copy()

# 创建一个 DataFrame,将预测结果和 id 组合在一起
result = pd.DataFrame({
    'id': ids,
    'Price': predictions
})

# 将结果保存到CSV文件中
result.to_csv('prediction_results.csv', index=False)

# 返回结果
result

今天的分享到这里就结束了,感谢大家的观看,制作不易,希望得到大佬们的四连(点赞,收藏,评论,关注)


http://www.kler.cn/a/545920.html

相关文章:

  • 如何保持 mysql 和 redis 中数据的一致性?PegaDB 给出答案
  • 上课啦 | 2月17日软考高项【5月备考班】
  • MacOS使用PhpWebStudy搭建PHP开发环境
  • Express 中间件分类
  • 【Elasticsearch】监控与管理:集群健康检查
  • 双指针思想
  • 2.【BUUCTF】bestphp‘s revenge
  • RK3588开发板部署DeepSeek-R1-Distill-Qwen-1.5B的步骤及问题
  • DeepSeek介绍本地部署保姆级教程
  • STM32F407通过FSMC扩展外部SRAM和NAND FLASH
  • 城电科技| 光伏太阳花:让绿色能源随处绽放
  • 【RK3568】linux嵌入式教程——点亮LED
  • 基于Docker-compose的禅道部署实践:自建MySQL与Redis集成及故障排查指南
  • DeepSeek大模型响应速度优化策略
  • AWS SES 邮件服务退信/投诉处理与最佳实践指南
  • mysql大数据量分页查询
  • easyexcel快速使用
  • Java的synchronized是怎么实现的?
  • Jenkins | Jenkins安装
  • HTN77A0:超低静态功耗、0.7A同步降压转换器特性资料参数