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

【机器学习实战】kaggle playground最新竞赛,预测贴纸数量--python源码+解析

hello,本次分享kaggle playground最新竞赛,预测贴纸数量。

目标:此挑战的目标是预测不同国家/地区的贴纸销量

评估:使用平均绝对百分比误差 (MAPE)评估提交的内容。

数据描述

对于此挑战,你将预测来自不同(真实!)国家/地区的不同虚拟商店的各种 Kaggle 品牌贴纸的多年销售额。该数据集是完全合成的,但包含您在现实世界数据中看到的许多影响,例如周末和假期影响、季节性等。

文件

  • train.csv - 训练集,其中包括每个日期-国家/地区-商店-商品组合的销售数据。
  • test.csv - 测试集;您的任务是预测num_sold每个日期-国家/地区-商店-商品组合的相应商品销售额 ( )。
  • Sample_submission.csv - 正确格式的示例提交文件。

评估指标说明

Mean Absolute Percentage Error (MAPE) 是回归任务中常用的评估指标之一,它衡量了预测值与真实值之间的相对误差,公式如下:
MAPE = 1 n ∑ i = 1 n ∣ y i − y ^ i y i ∣ × 100 \text{MAPE} = \frac{1}{n} \sum_{i=1}^{n} \left| \frac{y_i - \hat{y}_i}{y_i} \right| \times 100 MAPE=n1i=1n yiyiy^i ×100

其中,𝑦𝑖 是实际值,𝑦^𝑖是预测值,n 是样本数量。对于回归问题,MAPE 提供了一个直观的百分比误差度量,通常用于评估模型在实际应用中的性能。

源码

import numpy as np 
import pandas as pd

train_data=pd.read_csv('train.csv')
test_data=pd.read_csv('test.csv')
train_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 230130 entries, 0 to 230129
Data columns (total 6 columns):
 #   Column    Non-Null Count   Dtype  
---  ------    --------------   -----  
 0   id        230130 non-null  int64  
 1   date      230130 non-null  object 
 2   country   230130 non-null  object 
 3   store     230130 non-null  object 
 4   product   230130 non-null  object 
 5   num_sold  221259 non-null  float64
dtypes: float64(1), int64(1), object(4)
memory usage: 10.5+ MB
train_data.head()
	id	date	country	store	product	num_sold
0	0	2010-01-01	Canada	Discount Stickers	Holographic Goose	NaN
1	1	2010-01-01	Canada	Discount Stickers	Kaggle	973.0
2	2	2010-01-01	Canada	Discount Stickers	Kaggle Tiers	906.0
3	3	2010-01-01	Canada	Discount Stickers	Kerneler	423.0
4	4	2010-01-01	Canada	Discount Stickers	Kerneler Dark Mode	491.0

可以看到数据除了时间变量,和目标变量其余变量都为分类变量。并且发现目标变量num_sold有少量缺失值。

train_data1 = train_data.dropna()
# 遍历所有object类型的字段,查看这些字段的unique()值
for column in train_data1.select_dtypes(include=['object']).columns:
    unique_values = train_data1[column].unique()
    print(f"Unique values in '{column}': {unique_values}")
Unique values in 'country': ['Canada' 'Finland' 'Italy' 'Kenya' 'Norway' 'Singapore']
Unique values in 'store': ['Discount Stickers' 'Stickers for Less' 'Premium Sticker Mart']
Unique values in 'product': ['Kaggle' 'Kaggle Tiers' 'Kerneler' 'Kerneler Dark Mode'
 'Holographic Goose']

根据分类变量的值,可以对其使用独热编码。

# 划分好目标变量和特征
train_data2=train_data1.drop(['id','num_sold'], axis=1)
test_data2=test_data.drop(['id'], axis=1)
label=train_data1['num_sold']
train_data2.shape, test_data2.shape
from sklearn.preprocessing import OneHotEncoder

def encode_categorical_features(train_df, test_df, categorical_columns):
    """
    对指定的类别型特征进行 One-Hot 编码,并对训练集和测试集进行特征对齐。
    
    参数:
    - train_df: 训练集 DataFrame
    - test_df: 测试集 DataFrame
    - categorical_columns: 需要编码的类别型特征列名列表
    """
    encoder = OneHotEncoder(sparse_output=False, handle_unknown='ignore')  # 创建 OneHotEncoder
    train_encoded = train_data1.copy()  # 使用传入的训练集数据
    test_encoded = test_data.copy()  # 使用传入的测试集数据
    
    for column in categorical_columns:
        # 对训练集进行 fit_transform
        train_encoded_array = encoder.fit_transform(train_df[[column]])
        # 对测试集使用训练集的规则进行 transform
        test_encoded_array = encoder.transform(test_df[[column]])
        
        # 将编码后的数据转换为 DataFrame
        train_encoded_df = pd.DataFrame(train_encoded_array, 
                                        columns=encoder.get_feature_names_out([column]), 
                                        index=train_df.index)
        test_encoded_df = pd.DataFrame(test_encoded_array, 
                                       columns=encoder.get_feature_names_out([column]), 
                                       index=test_df.index)
        
        # 合并编码后的数据到原始 DataFrame 中
        train_encoded = pd.concat([train_encoded, train_encoded_df], axis=1)
        test_encoded = pd.concat([test_encoded, test_encoded_df], axis=1)
        
        # 删除原始的类别型列
        train_encoded.drop(column, axis=1, inplace=True)
        test_encoded.drop(column, axis=1, inplace=True)
    
    # 确保训练集和测试集的列顺序一致
    test_encoded = test_encoded.reindex(columns=train_encoded.columns, fill_value=0)
    
    return train_encoded, test_encoded

# 需要编码的列名
encoder_columns = ['country', 'store', 'product']

# 调用函数进行编码,传入指定的类别列
train_data_encoded, test_data_encoded = encode_categorical_features(train_data2, test_data2, encoder_columns)

# 查看结果维度
print("训练集维度: ", train_data_encoded.shape)
print("测试集维度: ", test_data_encoded.shape)
训练集维度:  (221259, 17)
测试集维度:  (98550, 17)

编码做完,剩下就是时间变量了

def generate_date_features(df):
    """为传入的 DataFrame 生成日期相关特征,并删除原始日期列"""
    
    # 将 'date' 列转换为日期类型
    df['date'] = pd.to_datetime(df['date'])

    # 提取年、月、日、星期等特征
    df['year'] = df['date'].dt.year
    df['month'] = df['date'].dt.month
    df['day'] = df['date'].dt.day
    df['day_of_week'] = df['date'].dt.dayofweek  # 星期几 (0=Monday, 6=Sunday)
    df['week_of_year'] = df['date'].dt.isocalendar().week  # 一年中的周数

    # 周期性特征 (月和日的正弦和余弦转换)
    df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12) 
    df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)
    df['day_sin'] = np.sin(2 * np.pi * df['day'] / 31)  # 以31天为周期
    df['day_cos'] = np.cos(2 * np.pi * df['day'] / 31)

    # 进一步提取更细粒度的特征
    df['quarter'] = df['date'].dt.quarter  # 季度 (1, 2, 3, 4)
    df['is_leap_year'] = df['date'].dt.is_leap_year  # 是否闰年
    df['days_in_month'] = df['date'].dt.days_in_month  # 本月天数
    df['is_weekend'] = (df['day_of_week'] >= 5).astype(int)  # 是否周末 (1=周末, 0=工作日)

    # 创建特征:日期周数与月份相关
    df['month_day_of_week'] = (df['day_of_week'] + 1) % 7  # 可以加1做一个周期性的变换

    # 删除原始 'date' 列
    df = df.drop(columns=['date'])

    return df

# 对 train_data1 和 test_data 分别调用 generate_date_features 函数
train_data_feature = generate_date_features(train_data_encoded)
test_data_feature = generate_date_features(test_data_encoded)

train_data_feature.shape, test_data_feature.shape
((221259, 30), (98550, 30))

这一步操作的目的是从日期列中提取出多个有用的特征,以便模型能够更好地捕捉时间上的规律。具体来说:

  • 年、月、日、星期几、周数:帮助模型识别时间中的周期性变化,如年度、月份、星期几等。
  • 周期性特征(正弦和余弦转换):将月和日转换为正弦和余弦值,帮助模型识别日期的周期性规律。
  • 季度、闰年、每月天数、是否周末:提供更细粒度的信息,帮助模型理解季节变化、闰年影响以及工作日和周末的区别。
  • 日期周数与月份的相关特征:通过对星期几进行变换,帮助模型理解每个月的工作日与周末的差异。
  • 最后,删除原始的 date 列,因为这些新特征已经包含了日期信息,避免冗余。
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_percentage_error
import lightgbm as lgb
import optuna

x = train_data_feature
y = label

# 切分数据集
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)

# 定义目标函数
def objective(trial):
    params = {
        'objective': 'regression',  
        'boosting_type': 'gbdt',
        'max_depth': trial.suggest_int('max_depth', 3, 10),
        'num_leaves': trial.suggest_int('num_leaves', 20, 150),
        'min_child_samples': trial.suggest_int('min_child_samples', 10, 100),
        'min_child_weight': trial.suggest_float('min_child_weight', 1e-3, 10.0),
        'subsample': trial.suggest_float('subsample', 0.6, 1.0),
        'colsample_bytree': trial.suggest_float('colsample_bytree', 0.6, 1.0),
        'learning_rate': trial.suggest_float('learning_rate', 1e-4, 0.1),
        'reg_lambda': trial.suggest_float('reg_lambda', 1e-3, 10.0),
        'reg_alpha': trial.suggest_float('reg_alpha', 1e-3, 10.0),
    }
    
    model = lgb.LGBMRegressor(**params, random_state=42)  
    model.fit(X_train, y_train)  # 使用原始的训练数据
    y_pred = model.predict(X_test)  # 使用原始的测试数据
    mape = mean_absolute_percentage_error(y_test, y_pred)  # 使用 MAPE 作为评估指标
    return mape

# 启动优化
study = optuna.create_study(direction='minimize')  # MAPE 越小越好,所以方向是 minimize
study.optimize(objective, n_trials=50)

# 使用最佳参数训练模型
best_params = study.best_trial.params
best_model = lgb.LGBMRegressor(**best_params, random_state=42)
best_model.fit(X_train, y_train) 
# 评估
y_pred = best_model.predict(X_test)  
mape = mean_absolute_percentage_error(y_test, y_pred)

# 输出结果
print("最佳参数: ", best_params)
print("MAPE: {:.5f}".format(mape))
最佳参数:  {'max_depth': 7, 'num_leaves': 147, 'min_child_samples': 35, 'min_child_weight': 0.16523950085204664, 'subsample': 0.85660019823679, 'colsample_bytree': 0.9903873300498585, 'learning_rate': 0.09608040411129001, 'reg_lambda': 0.2377823209800927, 'reg_alpha': 2.660750861644525}
MAPE: 0.00558

通过训练可得MAPE为0.00558。

在这里插入图片描述
已经是一个较高的水准了。

注意:
标准化有助于加速许多机器学习算法的收敛,尤其是像梯度下降这样的优化算法。然而,LightGBM 等树模型不太依赖标准化,因此不进行标准化操作,模型的表现可能不会有显著变化,甚至有可能在某些情况下有所提高。
若使用其他模型(例如线性回归、支持向量机等),是否进行标准化可能会遇到不同的效果。

本次分享就结束了,希望大家点赞关注。


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

相关文章:

  • maven的pom.xml配置详解
  • C++ static关键字(八股总结)
  • PYTHON与JAVA执行时间对比
  • pip error: microsoft visual c++ 14.0 or greater is required
  • 设计模式 行为型 策略模式(Strategy Pattern)与 常见技术框架应用 解析
  • Linux:操作系统不朽的传说
  • Qt C++ 软件调试内存分析工具Heob(推荐三颗星)
  • 用matlab调用realterm一次性发送16进制数
  • python-leetcode-跳跃游戏
  • python学opencv|读取图像(二十四)使用cv2.putText()绘制文字进阶-倾斜文字
  • Spring MVC 介绍与实践
  • 2025年AI和AR谁才是智能眼镜的未来
  • Java中String对象创建的方式
  • 【SQL serve】教材数据库(6)
  • 外观模式——C++实现
  • Ubuntu 安装英伟达显卡驱动问题记录
  • 面试场景题系列:设计爬虫系统
  • 新能源电动汽车动力电池技术
  • OSPF一些基础概念
  • 云从科技Java面试题及参考答案
  • STM32F1学习——PWMI模式(IC输入捕获)
  • uniapp H5页面实现懒加载
  • Fireworks AI:图像/PDF非文本内容转LLM可读文本
  • pytorch torch.scatter_reduce函数介绍
  • 使用Java Selenium修改打开页面窗口大小
  • 线程-8-日志_线程池