科大讯飞2022商品销量智能预测挑战赛

摘要:

合集:AI案例-ML-零售业
赛题:商品销量智能预测挑战赛
主办方:科大讯飞股份有限公司
主页:http://challenge.xfyun.cn/topic/info?type=product-sales
AI问题:时间序列预测问题
数据集:商品历史销量数据和商品月订单数据。训练集83,788条数据,共53个时序特征和统计特征。测试集20,948条数据。
数据集价值:支持时间序列预测
解决方案:CatBoost、XGBoost和LightGBM三种梯度提升算法

一、赛题描述

随着企业持续产生的商品销量,其数据对于自身营销规划、市场分析、物流规划都有重要意义。但是销量预测的影响因素繁多,传统的基于统计的计量模型,比如时间序列模型等由于对现实的假设情况过多,导致预测结果较差。因此需要更加优秀的智能AI算法,以提高预测的准确性,从而助力企业降低库存成本、缩短交货周期、提高企业抗风险能力。本次大赛提供了商品销量历史数据作为训练样本,参赛选手需基于提供的样本构建模型,预测商品未来的销售量。

二、数据集说明

本次比赛为参赛选手提供了两类数据:商品历史销量数据和商品月订单数据。商品历史需求销量数据提供了商品编码、日期、是否促销、商品销售量。商品月订单数据提供了商品编码、商品类型、月份、订单数量、商品月初和月末库存量。(label空值的含义表示该商品当天无销量)。

商品历史销量数据

商品历史销量数据字段定义:字段名、数据类型、说明。

product_id, string, 商品编码
date, string, 日期
is_sale_day, int , 是否促销
label,float,商品销售量

数据样例:1.csv

商品id时间总销量浏览量抖音转化率视频个数直播个数直播销量视频销量视频达人直播达人
【七月妈妈】山东红薯粉条500g*3袋2021/7/8 16:00000000000
【七月妈妈】山东红薯粉条500g*3袋2021/7/9 16:00000000000
【七月妈妈】山东红薯粉条500g*3袋2021/7/10 16:00000000000

商品月订单数据

商品月订单表格字段定义: 字段名、数据类型、说明。

product_id, string, 商品编码
type, string, 商品类型
year,string, 年
month,string, 月
order, float, 商品月订单量
begin_stock, float, 商品月初库存
end stock, float, 商品月末库存

数据集版权许可协议

GPL-v3

三、解决方案样例

解决方案

源码:E_commerce_Sales_Forecast.ipynb

本案例的主要目标是基于历史销售数据构建模型,预测商品未来的销售量。系统使用了CatBoost、XGBoost和LightGBM三种梯度提升算法。

安装开发包

【本样例运行环境的关键版本信息】
python                   3.12.3
lightgbm                 3.3.0
catboost                 1.2.7
xgboost                 1.6.0

导入相关系统库

import pandas as pd
import numpy as np
import pickle as pkl
import os
from sklearn.model_selection import train_test_split
from catboost import CatBoostRegressor
from sklearn.model_selection import KFold
from bayes_opt import BayesianOptimization
from sklearn.metrics import mean_squared_error
import xgboost as xgb
import lightgbm as lgb
import matplotlib.pyplot as plt

主要工作流程

1 数据准备

  • 从”./data”目录读取多个CSV文件
  • 合并所有数据到一个DataFrame
  • 处理时间字段,提取星期数
  • 标记购物节日期(如双11、双12等促销时段)
path_list = os.listdir("./data")
csv_list = list()

for file_name in path_list:
   df = pd.read_csv("./data/" + file_name)
   df["是否为购物节"] = df_time["是否为购物节"].copy(deep=True)
   df["前一周购物节天数"] = df_time["前一周购物节天数"].copy(deep=True)
   df["未来一周购物节天数"] = df_time["未来一周购物节天数"].copy(deep=True)
   
   generate_dataset(df)
   csv_list.append(df)

dataset = pd.concat(csv_list, axis=0)
dataset_goods = list(dataset.商品id.unique())

2 特征工程

在时间序列分析中,滞后值(Lag Values) 是指将某个变量的历史值(过去时间点的观测值)作为当前时间点的特征。这种操作能够帮助模型捕捉数据的时序依赖关系,例如趋势、周期性和季节性。

滞后值是通过 shift() 方法生成的,表示将时间序列数据向后移动 n 个时间步。例如:

  • 总销量_forward_1 = 昨天的销量(当前时间点前1天的值)
  • 总销量_forward_2 = 前天的销量(当前时间点前2天的值)
  • 总销量_forward_14 = 14天前的销量(当前时间点前14天的值)

总销量backward_1 到 总销量backward_7为未来的数据,这些列用于计算 目标变量 “后七天平均销量”(即未来7天的日均销量)。

核心特征生成函数,主要功能:

  • 生成时间序列特征:前1-14天的销量、浏览量等指标的滞后值。
  • 计算统计特征:前一周/前两周的平均值、方差、最大值、最小值
  • 计算比率特征:直播销量占比、视频销量占比等
  • 添加购物节相关特征:前一周/未来一周的购物节天数
  • 添加商品级别的聚合特征(总销量、总浏览量等)
  • 生成销量变化趋势特征(平均值增量、方差值增量)
  • 以上前1-14天的销量相关的特征生成后写入输入特征变量 train_dataset_x。生成未来7天平均销量(作为目标变量) 写入变量 train_dataset_y。
def generate_dataset(data_df: pd.DataFrame, cols=None):
   if cols is None:
       cols = ['总销量', '直播个数', '直播销量', '视频销量', '直播达人', '直播销量占比', '视频销量占比']

   remove_cols = list()
   data_df["时间"] = data_df["时间"].apply(lambda x: str(x).split(" ")[0])
   data_df["时间"] = pd.to_datetime(data_df["时间"])
   data_df["星期数"] = data_df["时间"].dt.isocalendar().week
   data_df["星期数"] = data_df["星期数"].astype(int)
   data_df["直播销量占比"] = data_df.apply(lambda x: 0.0 if x["总销量"] == 0 else x["直播销量"] / x["总销量"], axis=1)
   data_df["视频销量占比"] = data_df.apply(lambda x: 0.0 if x["总销量"] == 0 else x["视频销量"] / x["总销量"], axis=1)
   backward_list = list()
   
   for name in cols:
       forward_list = list()

       for _idx in range(1, 15):
           data_df[name + "_forward_" + str(_idx)] = data_df[name].shift(periods=_idx, fill_value=0)
           remove_cols.append(name + "_forward_" + str(_idx))
           forward_list.append(name + "_forward_" + str(_idx))

       forward_list.append(name)
       tmp_df_1 = data_df[forward_list[:7]]
       tmp_df_2 = data_df[forward_list[7:]]
       tmp_df = data_df[forward_list]
       data_df[name + "_前第一周天平均值"] = tmp_df_1.mean(axis=1)
       data_df[name + "_前第二周天平均值"] = tmp_df_2.mean(axis=1)
       data_df[name + "_前第一周天方差值"] = tmp_df_1.mean(axis=1)
       data_df[name + "_前第二周天方差值"] = tmp_df_2.mean(axis=1)
       data_df[name + "_平均值增量"] = data_df[name + "_前第一周天平均值"] - data_df[name + "_前第二周天平均值"]
       data_df[name + "_方差值增量"] = data_df[name + "_前第一周天方差值"] - data_df[name + "_前第二周天方差值"]
       data_df[name + "_前两周最大值"] = tmp_df.max(axis=1)
       data_df[name + "_前两周最小值"] = tmp_df.min(axis=1)

       if name == "总销量":
           for j in range(1, 8):
               data_df[name + "_backward_" + str(j)] = data_df[name].shift(periods=j * (-1), fill_value=0)
               backward_list.append(name + "_backward_" + str(j))
               remove_cols.append(name + "_backward_" + str(j))

           tmp_df = data_df[backward_list]
           data_df["后七天平均销量"] = tmp_df.mean(axis=1)

       data_df = data_df.copy()  # 如果 DataFrame 已经高度碎片化,可以通过复制 DataFrame 来重新组织内存,消除碎片

   for _idx, date_time in enumerate(["2021-12-26", "2021-12-25", "2021-12-24", "2021-12-23", "2021-12-22", "2021-12-21"]):
       data_df.loc[data_df[data_df["时间"] == date_time].index, "后七天平均销量"] = data_df.loc[
           data_df[data_df["时间"] == date_time].index, backward_list[:_idx + 1]].mean(axis=1)
   
   data_df.drop(columns=remove_cols, inplace=True)

训练集、测试集和特征变量、输出/预测目标变量的定义如下。后七天平均销量为预测目标变量。

test_dataset = dataset_test.drop(columns=["后七天平均销量", "时间"])
dataset_train.drop(columns=["时间", "是否为0"], inplace=True)
train_dataset_x = dataset_train.drop(columns=["后七天平均销量"])
train_dataset_y = dataset_train["后七天平均销量"].copy(deep=True)

3 模型构建与训练

实现了三种模型:

  • CatBoost:处理类别特征能力强的梯度提升算法
  • XGBoost:高效的梯度提升决策树算法
  • LightGBM:基于直方图的梯度提升算法,训练速度快
# 定义模型参数
params_catboost = {"iterations": 1000,
                  "learning_rate": 0.03,
                  "l2_leaf_reg": 3,
                  "bagging_temperature": 1,
                  "subsample": 0.66,
                  "random_strength": 1,
                  "depth": 6,
                  "rsm": 1,
                  "one_hot_max_size": 2,
                  "leaf_estimation_method": "Gradient",
                  "fold_len_multiplier": 2,
                  "border_count": 128,
                  "random_seed": 2022,
                  "loss_function": "RMSE",
                  "eval_metric": "RMSE",
                  "od_type": "Iter",
                  "od_wait": 50}

params_lightgbm = {"n_estimators": 300,
                  "learning_rate": 0.1,
                  "num_leaves": 500,
                  "max_depth": 6,
                  "min_data_in_leaf": 1000,
                  "lambda_l1": 1,
                  "lambda_l2": 1,
                  "min_gain_to_split": 1,
                  "bagging_fraction": 0.8,
                  "feature_fraction": 0.8,
                  "random_state": 2022,
                  "metric": "rmse"}

params_xgboost = {'booster': 'gbtree',
                 'objective': 'reg:squarederror',
                 'eval_metric': 'rmse',
                 'seed': 2022,
                 'learning_rate': 0.01,
                 'gamma': 0.1,
                 'min_child_weight': 1.1,
                 'max_depth': 5,
                 'lambda': 10,
                 'subsample': 0.7,
                 'colsample_bytree': 0.7,
                 'colsample_bylevel': 0.7,
                 'tree_method': 'exact'}

模型训练函数model_fun()

通用模型训练函数,支持:

  • 数据分割(训练集/测试集)。训练数据:2021-07-08 到 2021-12-24。测试数据:2021-12-27。
  • 不同模型的训练(CatBoost/XGBoost/LightGBM)
  • 训练过程监控和评估
def model_fun(train_sample, train_label, params, model, model_name="catboost"):
   print("------------------- 训练 {} 模型 -------------------".format(model_name))
   x_train, x_test, y_train, y_test = train_test_split(train_sample, train_label, test_size=0.2, random_state=2022)
   _model = model(**params)

   print("x_train.shape: {}".format(x_train.shape))
   print("y_train.shape: {}".format(y_train.shape))
   print("x_test.shape: {}".format(x_test.shape))
   print("y_test.shape: {}".format(y_test.shape))
   _model.fit(x_train, y_train)
   
   if model_name.lower() == "catboost":
       _model.fit(x_train, y_train, eval_set=[(x_train, y_train), (x_test, y_test)], cat_features=["商品id"], verbose=50)
   elif model_name.lower() == "lightgbm":
       _model.fit(x_train, y_train, eval_set=[(x_train, y_train), (x_test, y_test)], eval_metric='rmse', verbose=50)
   else:
       _model.fit(x_train, y_train)

   train_pred = _model.predict(x_train)
   train_mse = mean_squared_error(y_train, train_pred)

   test_pred = _model.predict(x_test)
   test_mse = mean_squared_error(y_test, test_pred)

   result_dic = {"train_mse": train_mse, "test_mse": test_mse}
   return _model, result_dic

xgboost为例:

# xgboost
model_tree_xgb, model_result_xgb = model_fun(train_dataset_x, train_dataset_y, params_xgboost, xgb.XGBRegressor, "xgboost")
feature_importance_xgb = feature_importance_model(model_tree_xgb, "xgboost", train_columns)
feature_importance_xgb.head(20)

输出:

------------------- 训练 xgboost 模型 -------------------
x_train.shape : (83788, 53)
y_train.shape : (83788,)
x_test.shape : (20948, 53)
y_test.shape : (20948,)

输出前20种特征feature_importance_xgb.head(20)。其中总销量backward_1到总销量backward_7为预测后7天的销量值。

FeatureImportance
总销量_前第二周天方差值0.268957
总销量_前两周最小值0.137244
总销量_前第二周天平均值0.088891
总销量_前第一周天平均值0.088605
总销量_backward_10.023013
总销量_backward_20.033763
总销量_backward_30.074188
总销量_backward_40.087193
总销量_backward_50.080564
总销量_backward_60.029972
总销量_backward_70.010891
总销量_前第一周天方差值0.023166
总销量_前两周最大值0.006882
总销量_forward_90.006779
总销量_forward_20.005585
总销量_forward_110.004291
直播达人0.004564
浏览量_all0.004505
视频个数0.003872
总销量0.002667

4 模型评估与优化

函数 get_result() 执行完整的模型训练和预测流程:

  1. 10折交叉验证
  2. 模型训练
  3. 验证集评估
  4. 测试集预测
  5. 计算均方误差(MSE)作为评估指标
# 模型预测
def get_result(params_dic, model, model_name: str, x_data, y_data, test_data):
   print("------------------------- get_result -------------------------")
   ans = []
   mean_score = 0
   sk = KFold(n_splits=10, shuffle=True, random_state=2022)
   _model = model(**params_dic)

   for train_index, test_index in sk.split(x_data, y_data):
       x_train = x_data.iloc[train_index]
       y_train = y_data.iloc[train_index]
       x_test = x_data.iloc[test_index]
       y_test = y_data.iloc[test_index]

       if model_name.lower() == "catboost":
           regressor_model = _model.fit(x_train, y_train, eval_set=(x_test, y_test), verbose=500,
                                        cat_features=["商品id"])
       elif model_name.lower() == "lightgbm":
           regressor_model = _model.fit(x_train, y_train, eval_set=(x_test, y_test), verbose=500)
       else:
           regressor_model = _model.fit(x_train, y_train)

       y_pred = regressor_model.predict(x_test)
       test_mse = mean_squared_error(y_test, y_pred)
       print("model 验证MSE:{}".format(test_mse))
       mean_score += test_mse / 10.
       y_test_pred = regressor_model.predict(test_data)
       ans.append(y_test_pred)

   print("10折平均MSE:{}".format(mean_score))
   model_pred = sum(ans) / 10.
   return model_pred

5 预测与结果输出

  • 对测试数据进行预测
  • 将预测结果保存为CSV文件
result_fuse = pd.read_csv("./submission/_提交示例.csv")
result_fuse["未来一周天均销量"] = (result_cat["未来一周天均销量"] + result_lgbm["未来一周天均销量"] + result_xgb["未来一周天均销量"]) / 3.8
result_fuse.to_csv("./submission/result_fuse.csv", encoding="utf_8_sig", index=False)

result_xgboost.csv 数据样例:

商品id未来一周天均销量
【七月妈妈】山东红薯粉条500g*3袋-0.554255605
【李老板】爆款 俄罗斯风味国产紫皮糖每包158g 拍1发5包0.7535468339920044
45°帝王经典100ml五粮液股份有限公司出品0.06480810791254044
【伦哥专享】21新米 十月稻田 长粒香米 2.5kg*4袋-0.504981279
【自嗨锅】粉丝福利酥麻牛肉粉自热锅 86g-2.378837585
【池妈专属】池妈@鸭脖60g/根 原味or香辣79.9元/8根2.0607380867004395
【宠粉款】大号葡式蛋挞皮90个 欣乐家庭烘培(1800g)0.22446861863136292
晟迪180g*2袋 香辣孜然三角骨 冷冻保存 顺丰包邮0.06403211504220963

技术亮点

  1. 全面的特征工程:生成了丰富的时序特征和统计特征,考虑了购物节等特殊时段的影响。
  2. 多模型集成:同时使用三种主流梯度提升算法,可以利用不同算法的优势。
  3. 自动化调参:使用贝叶斯优化自动寻找最优超参数,减少人工调参工作量。
  4. 稳健的评估:采用10折交叉验证,确保模型评估结果的可靠性。
  5. 高效实现:利用CatBoost对类别特征的原生支持,减少了特征编码的工作量。

源码开源协议

GPL-v3

四、获取案例套装

需要登录后才允许下载文件包。登录

发表评论