机器学习模型概念
GBDT
Gradient Boosting Decision Tree (GBDT) 是一种集成学习方法,通过串行训练多棵决策树来提升预测性能。每棵树都专注于修正前一棵树的残差或梯度,从而逐步减少模型的预测误差。
GBDT利用梯度下降优化损失函数,具有灵活性强、泛化能力好的特点,适用于回归、分类和排序等多种预测任务,是数据科学竞赛和实际应用中常用的高效预测工具之一,不仅在工业界应用广泛,还通常被用于多分类、点击率预测、搜索排序等任务。
LightGBM
LightGBM(Light Gradient Boosting Machine)是一个高效的GBDT(Gradient Boosting Decision Tree)框架,它以其快速的训练速度、低内存消耗和优秀的预测准确率而闻名。LightGBM支持并行训练和分布式处理,特别适合处理大规模数据集。除了GBDT外,LightGBM还支持随机森林和逻辑回归等模型,广泛应用于二分类、多分类和排序等各种机器学习场景。
举例来说,在个性化商品推荐中,可以利用LightGBM构建点击预估模型。通过分析用户的行为数据(如点击、曝光未点击、购买等),以及用户的属性特征(如性别、年龄段等),提取出类别特征(例如商品类型)、数值特征(例如用户活跃度)等作为模型的输入特征。这些特征可以帮助预测用户是否会点击或购买某个商品,从而实现个性化的推荐。
对进阶代码各模块的理解及改进
数据集
使用原Task1中的data文件夹中的test.csv及train.csv作为数据集。
(1)导入模块
此部分包含代码所需模块,但原代码增添了太多,选择优化:
import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
import gc
# 关闭警告输出
import warnings
warnings.filterwarnings('ignore')
(2)探索性数据分析
在数据准备阶段,主要读取训练数据和测试数据,并进行基本的数据展示。相对于原代码,在读取数据时进行内存优化。相较于pandas
的直接读取,通过parse_dates
参数将日期列解析为 datetime
类型,并通过 dtype
参数显式设置数据类型,以节省内存。
train = pd.read_csv('./data/train.csv', parse_dates=['dt'], dtype={'id': 'object', 'target': 'float32'})
test = pd.read_csv('./data/test.csv', parse_dates=['dt'], dtype={'id': 'object'})
其中d为房屋id,dt为日标识,训练数据dt最小为11,不同id对应序列长度不同;type为房屋类型,通常而言不同类型的房屋整体消耗存在比较大的差异;target为实际电力消耗,也是我们的本次比赛的预测目标。
可视化分析
对数据进行基础了解:
不同type类型对应target的柱状图
使用 Pandas 的 groupby
函数计算不同 type
类别下 target
的平均值,并绘制成柱状图,以展示不同类别的目标变量平均值。
type_target_df = train.groupby('type')['target'].mean().reset_index()
plt.figure(figsize=(8, 4))
plt.bar(type_target_df['type'], type_target_df['target'], color=['blue', 'green'])
plt.xlabel('Type')
plt.ylabel('Average Target Value')
plt.title('Bar Chart of Target by Type')
plt.show()
id为00037f39cf的按dt为序列关于target的折线图
根据特定的 id
值筛选数据,绘制该 id
对应的时间序列图,展示该对象在不同时间点上的目标变量值。
specific_id_df = train[train['id'] == '00037f39cf']
plt.figure(figsize=(10, 5))
plt.plot(specific_id_df['dt'], specific_id_df['target'], marker='o', linestyle='-')
plt.xlabel('DateTime')
plt.ylabel('Target Value')
plt.title("Line Chart of Target for ID '00037f39cf'")
plt.show()
(3)特征工程
构建历史平移特征和窗口统计特征。这里对两者进行解释:
历史平移特征(Historical Shift Feature)
通过历史平移获取上个阶段的信息,即基于过去观察值的特征,常见于对时间序列下的趋势学习,通过比较当前观察值与过去的观察值来捕捉长期依赖关系。这种方法对于识别趋势和周期性变化非常有效,因为它允许模型在时间维度上学习数据的演变方式。其创建方法为平移,即将时间序列的当前值与所选择的相应的历史值相比,代码中的具体实现形式为shift方法。
窗口统计特征(Window Statistics Feature)
基于时间窗口内的数据点计算,用于捕捉时间序列中部分时点的特征,如平均值等。它提供了对局部数据模式的直观理解,因此适合于捕捉时间序列中的短期波动和异常值。例如本案例中定义固定或可变的时间窗口,在其中计算所需的统计值,得到特征值。
历史平移特征适合于分析时间序列中的长期模式和趋势,而窗口统计特征则更适用于捕捉短期内的变化和异常。这两种方法的结合可以提高模型对时间序列数据的全面理解和预测能力。
总体逻辑如下:
- 将训练集和测试集数据合并,并按照
id
和dt
进行降序排序,为后续特征工程做准备。- 使用
shift
方法对目标变量target
进行历史平移操作,创建了一系列的滞后特征。- 计算历史滑动窗口统计特征,例如过去三个时点(
last10_target
,last11_target
,last12_target
)的平均值。- 相对于原代码,使用
inplace=True
和reset_index(drop=True)
来优化数据的合并和排序过程,避免不必要的内存复制。并对历史平移和窗口统计的操作进行了调整,确保代码逻辑清晰且效率高。- 数据切分,将包含
target
值的数据作为训练集,将target
为空的数据作为测试集。- 确定输入特征列,不包括
id
、target
和dt
。
# 合并训练数据和测试数据,并进行排序
data = pd.concat([test, train], axis=0, ignore_index=True)
data.sort_values(['id','dt'], ascending=False, inplace=True)
data.reset_index(drop=True, inplace=True)
# 历史平移
for i in range(10, 30):
data[f'last{i}_target'] = data.groupby(['id'])['target'].shift(i)
# 窗口统计
data['win3_mean_target'] = data[['last10_target', 'last11_target', 'last12_target']].mean(axis=1)
# 数据切分
train = data[data['target'].notnull()].reset_index(drop=True)
test = data[data['target'].isnull()].reset_index(drop=True)
train_cols = [f for f in data.columns if f not in ['id', 'target', 'dt']]
(4)模型训练优化
def time_model(model, train_df, test_df, cols):
trn_x, trn_y = train_df[train_df['dt'] >= '31']['last10_target'], train_df[train_df['dt'] >= '31']['target']
val_x, val_y = train_df[train_df['dt'] <= '30']['last10_target'], train_df[train_df['dt'] <= '30']['target']
lgb_params = {
'boosting_type': 'gbdt',
'objective': 'regression',
'metric': 'mse',
'min_child_weight': 5,
'num_leaves': 32,
'lambda_l2': 10,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_freq': 4,
'learning_rate': 0.05,
'seed': 2024,
'nthread': 16,
'verbose': -1,
}
train_data = lgb.Dataset(trn_x.values.reshape(-1, 1), label=trn_y.values)
valid_data = lgb.Dataset(val_x.values.reshape(-1, 1), label=val_y.values, reference=train_data)
model = lgb.train(lgb_params, train_data, 50000, valid_sets=[train_data, valid_data],
categorical_feature=[], verbose_eval=500, early_stopping_rounds=500)
val_pred = model.predict(val_x.values.reshape(-1, 1), num_iteration=model.best_iteration)
test_pred = model.predict(test_df['last10_target'].values.reshape(-1, 1), num_iteration=model.best_iteration)
score = mean_squared_error(val_pred, val_y)
print(f"MSE Score: {score}")
return val_pred, test_pred
lgb_oof, lgb_test = time_model(lgb, train, test, train_cols)
- 定义
time_model
函数进行模型训练和预测。- 划分训练集和验证集(这里的切分方式可能有误,需要调整为正确的时间切分方式)。
- 设置LightGBM模型参数。
- 使用LightGBM模型训练,并进行预测。
- 计算验证集的均方误差(MSE)评分。
参数说明:
1. lgb.Dataset(trn_x.values.reshape(-1, 1), label=trn_y.values)
1.1
trn_x.values.reshape(-1, 1)
trn_x.values
:将Pandas DataFrametrn_x
转换为NumPy数组。这是因为LightGBM需要NumPy数组格式的数据输入。.reshape(-1, 1)
:将NumPy数组重新调整为一个列向量(即每一行代表一个样本,只有一列特征)。这里的-1
表示自动计算行数,1
表示将特征列数设为1。1.2
label=trn_y.values
trn_y.values
:将Pandas Seriestrn_y
转换为NumPy数组,用于提供训练样本的目标值(标签)。1.3
lgb.Dataset
lgb.Dataset
:构建LightGBM的数据集对象,它是LightGBM的核心数据结构,专门用于存储训练数据和标签。这个数据集对象会在后续的模型训练中使用。
2. lgb.train(lgb_params, train_data, 50000, ...)
lgb.train
:这是LightGBM库中的训练函数,用于训练一个Boosting模型。lgb_params
:一个字典,包含了LightGBM模型的超参数,例如:
boosting_type
: 提升类型(例如'gbdt','dart','goss'等)。objective
: 目标函数(例如'regression'表示回归任务)。metric
: 评估指标(例如'mse'表示均方误差)。min_child_weight
,num_leaves
,lambda_l2
,feature_fraction
,bagging_fraction
,bagging_freq
,learning_rate
,seed
,nthread
,verbose
等:其他模型的超参数。train_data
:之前创建的LightGBM训练数据集。50000
:指定最大训练轮数(即最多进行50000次迭代)。valid_sets
:一个列表,包含了训练和验证数据集。模型在训练过程中会在这些数据集上进行评估,以便进行早停和模型选择。categorical_feature
:指定哪些特征是类别特征。在这里是空列表,表示没有类别特征。verbose_eval
:设置日志输出的频率。这里设置为500,表示每500次迭代输出一次日志信息。early_stopping_rounds
:设置早停的轮数。如果在500次迭代内验证集的性能没有提升,训练将会提前停止,以防止过拟合。
3. 验证集预测
val_pred = model.predict(val_x.values.reshape(-1, 1), num_iteration=model.best_iteration)
model.predict
:用于生成预测值。val_x.values.reshape(-1, 1)
:将验证集特征数据调整为列向量形式。num_iteration=model.best_iteration
:指定使用最佳迭代次数进行预测。model.best_iteration
是训练过程中根据验证集性能自动确定的最佳迭代次数。
test_pred = model.predict(test_df['last10_target'].values.reshape(-1, 1), num_iteration=model.best_iteration)
test_df['last10_target'].values.reshape(-1, 1)
:将测试集的last10_target
特征数据调整为列向量形式。
相较于原代码,优化如下:
1. 数据切分和特征选择
之前的代码:在特征选择上仅使用了last10_target
作为特征列。
trn_x, trn_y = train_df[train_df['dt'] >= '31']['last10_target'], train_df[train_df['dt'] >= '31']['target']
val_x, val_y = train_df[train_df['dt'] <= '30']['last10_target'], train_df[train_df['dt'] <= '30']['target']
优化后的代码:使用了更多的特征列(cols
),这些特征列包含所有生成的历史平移特征和窗口统计特征,从而提供了更多的信息供模型学习。
trn_x, trn_y = train_df[train_df.dt >= 31][cols], train_df[train_df.dt >= 31]['target']
val_x, val_y = train_df[train_df.dt <= 30][cols], train_df[train_df.dt <= 30]['target']
2. 数据构建
之前的代码:将特征数据重新调整了形状,只使用单个特征last10_target
。
train_data = lgb.Dataset(trn_x.values.reshape(-1, 1), label=trn_y.values)
valid_data = lgb.Dataset(val_x.values.reshape(-1, 1), label=val_y.values, reference=train_data)
优化后的代码:直接使用多列特征数据,不再调整形状,从而使模型能够使用更多的特征信息进行训练。
train_matrix = lgb.Dataset(trn_x, label=trn_y)
valid_matrix = lgb.Dataset(val_x, label=val_y)
3. Lightgbm模型训练
之前的代码 :使用了train_data
和valid_data
进行模型训练,优化后将前面优化后的数据作进行模型训练,使代码保持一致。
4. 预测和评估(同3)
(5)保存预测结果
test['target'] = lgb_test
test[['id', 'dt', 'target']].to_csv('submit.csv', index=None)
预测结果展示
ID为2的分数为原代码,ID为1的是改进后代码:
在本项目中,通过对GBDT和LightGBM的深入理解及其在时间序列预测中的应用,我体会到数据预处理和特征工程的重要性。合理的数据解析和内存优化提升了模型的运行效率和预测性能。通过构建历史平移特征和窗口统计特征,模型更好地捕捉了时间序列中的长期趋势和短期波动。模型训练中的参数设置和数据切分策略的优化,提高了模型的准确性和鲁棒性。整个过程中,通过不断迭代和改进代码,每次调整都带来了性能提升和理解的深入。LightGBM作为高效的GBDT框架,其快速训练和低内存消耗使其在处理大规模数据集时表现优异,满足了实际应用中大规模数据处理和实时预测的需求。本次任务总体来说是为了熟悉GDBT模型及Lightgbm的基本流程。