#Datawhale Al夏令营 #机器学习#AI
不如沉默踏实做 2024-07-23 14:01:03 阅读 97
电力需求预测挑战赛是一项旨在提高电力需求预测准确性的比赛,对于参赛者来说,掌握相关技术和方法至关重要。以下是一份电力需求预测挑战赛笔记,包含了一些关键点和策略。
(我是一只蒟蒻小白,如果有说错的地方还请指正我会修改)
task1 了解赛事并跑通baseline
赛事链接#2024 iFLYTEK AI开发者大赛-讯飞开放平台
【步骤】
1.进入报名比赛,然后进入从零入门机器学习竞赛-Baseline - 飞桨AI Studio星河社区点击运行
进入环境后
2.点击上方按钮会生成一个submit.csv的文件
3.进入赛事链接进行提交并获得相应分数,评审规则如上(此时你已经成功跑通baseline啦!)
(如果步骤不够清晰请看Docs)
下面是对比赛的基本了解
【训练时序预测模型助力电力需求预测】
电力需求的准确预测对于电网的稳定运行、能源的有效管理以及可再生能源的整合至关重要。
赛题任务
给定多个房屋对应电力消耗历史N天的相关序列数据等信息,预测房屋对应电力的消耗。
赛题数据简介
赛题数据由训练集和测试集组成,为了保证比赛的公平性,将每日日期进行脱敏,用1-N进行标识。
即1为数据集最近一天,其中1-10为测试集数据。
这时候有人要问为什么测试1-10,用的是11-20的数据?
Answer:因为1-10是你要给出的答案,baseline是计算11-20的平均值作为答案。
数据集由字段id(房屋id)、 dt(日标识)、type(房屋类型)、target(实际电力消耗)组成。
baseline1
<code># 1. 导入需要用到的相关库
# 导入 pandas 库,用于数据处理和分析
import pandas as pd
# 导入 numpy 库,用于科学计算和多维数组操作
import numpy as np
# 2. 读取训练集和测试集
# 使用 read_csv() 函数从文件中读取训练集数据,文件名为 'train.csv'
train = pd.read_csv('./data/data283931/train.csv')
# 使用 read_csv() 函数从文件中读取测试集数据,文件名为 'train.csv'
test = pd.read_csv('./data/data283931/test.csv')
# 3. 计算训练数据最近11-20单位时间内对应id的目标均值
target_mean = train[train['dt']<=20].groupby(['id'])['target'].mean().reset_index()
# 4. 将target_mean作为测试集结果进行合并
test = test.merge(target_mean, on=['id'], how='left')code>
# 5. 保存结果文件到本地
test[['id','dt','target']].to_csv('submit.csv', index=None)
baseline讲解
1. 导入需要用到的相关库
import pandas as pd
import numpy as np
pandas
是一个强大的数据分析库,它提供了快速、灵活和表达性强的数据结构,用于处理结构化数据(类似于Excel表格)。numpy
是一个Python库,用于进行科学计算。它提供了高性能的多维数组对象和一系列用于处理数组的工具。
2. 读取训练集和测试集
train = pd.read_csv('./data/data283931/train.csv')
test = pd.read_csv('./data/data283931/test.csv')
read_csv()
是pandas
库中的一个函数,用于从CSV文件中读取数据并将其转换为DataFrame对象。DataFrame是pandas
中的基本数据结构。在这两行代码中,您分别读取了名为train.csv
和test.csv
的文件,并将它们存储在train
和test
变量中。
3. 计算训练数据最近11-20单位时间内对应id的目标均值
target_mean = train[train['dt']<=20].groupby(['id'])['target'].mean().reset_index()
train[train['dt']<=20]
:这是一个条件筛选,它选取了train
DataFrame中dt
列小于或等于20的所有行。.groupby(['id'])
:这是对数据进行分组的方法。在这里,您按照id
列对数据进行分组。['target'].mean()
:在分组之后,这个表达式计算每个组中target
列的平均值。.reset_index()
:这个方法将分组后的索引重置,将id
从索引转换为列。
4. 将target_mean作为测试集结果进行合并
test = test.merge(target_mean, on=['id'], how='left')code>
merge()
是pandas
中的函数,用于合并两个DataFrame。on=['id']
指定了合并的键,即按照id
列进行合并。how='left'code> 指定了合并的类型,在这里是左连接(left join),这意味着保留左侧DataFrame(这里是
test
)的所有行,并根据id
列将右侧DataFrame(这里是target_mean
)的值合并进来。如果左侧DataFrame中的某些id
在右侧DataFrame中没有对应的值,则结果中这些id
对应的target
列将为NaN。
5. 保存结果文件到本地
test[['id','dt','target']].to_csv('submit.csv', index=None)
[['id','dt','target']]
:这是一个列选择操作,它选取了test
DataFrame中的id
、dt
和target
列。to_csv()
是pandas
中的函数,用于将DataFrame保存为CSV文件。'submit.csv'
是输出文件的名称。index=None
表示在保存CSV文件时不包括DataFrame的索引列。
这段代码的总体目的是读取训练和测试数据集,计算训练集中每个
id
在最后11-20个时间单位的目标平均值,然后将这些平均值合并到测试集中,并保存结果。这是典型的数据预处理步骤,特别是在需要特征工程的情况下。
相关随堂笔记
1. 时间序列问题的定义
在电力需求预测挑战赛中,时间序列问题通常涉及以下方面:
定义: 时间序列问题是一种数据分析问题,它关注的是按时间顺序排列的数据点集合,目的是通过这些历史数据来预测未来的数据点。在电力需求预测中,时间序列问题可以具体定义为:使用过去一段时间内的电力消耗数据来预测未来一段时间内的电力需求。
关键要素:
时间:数据点按照时间顺序排列,时间可以是小时、日、周、月等。
观测值:在每一个时间点上的电力消耗量。
预测目标:通常是未来一段时间内的电力消耗量。
特点:
趋势:数据随时间呈现上升或下降的趋势。
季节性:数据呈现出周期性的波动,如日季节性(一天内的用电高峰和低谷)、年季节性(季节性气候变化导致的用电量变化)。
周期性:除了季节性之外,还可能存在其他周期性的波动。
噪声:数据中可能包含随机波动或误差。
挑战:
非平稳性:时间序列数据可能包含趋势或季节性,使得数据统计特性随时间变化。
异常值:数据中可能包含异常点,这些点可能是由特殊事件或数据录入错误引起的。
多变量影响:电力需求可能受到多种因素的影响,如天气、节假日、经济活动等。
2. 传统时序模型
传统时序模型是基于统计学原理构建的模型,它们在电力需求预测中有着广泛的应用。
常见的传统时序模型包括:
自回归模型 (AR):模型假设当前值是前几个时期值的线性组合。
移动平均模型 (MA):模型假设当前值是基于前几个时期误差项的线性组合。
自回归移动平均模型 (ARMA):结合了AR和MA模型的特点。
自回归积分移动平均模型 (ARIMA):适用于非平稳时间序列,通过差分使序列平稳。
季节性自回归积分移动平均模型 (SARIMA):ARIMA模型的扩展,加入了季节性成分。
特点:
参数化:这些模型通常有一组可调整的参数,如ARIMA模型中的p(自回归项数)、d(差分阶数)、q(移动平均项数)。
统计假设:传统模型通常基于一些统计假设,如数据平稳性、误差项的独立性等。
解释性:这些模型通常具有一定的解释性,可以分析不同时间成分(如趋势、季节性)对预测的影响。
优势:
成熟:传统模型在统计学中有较长的历史,理论和实践都比较成熟。
易于实现:许多统计软件和库都提供了传统时序模型的实现。
局限性:
线性假设:传统模型通常假设变量之间是线性关系,这可能不适用于复杂的非线性系统。
平稳性要求:许多传统模型需要时间序列是平稳的,这可能导致在处理实际数据时需要进行繁琐的预处理。
预测能力:对于具有复杂模式和大量外部影响因素的时间序列,传统模型的预测能力可能有限。
task2 使用进阶的机器学习模型lightgbm解决问题
基础概念入门
GBDT
GBDT (Gradient Boosting Decision Tree) 是机器学习中一个长盛不衰的模型,其主要思想是利用弱分类器(决策树)迭代训练以得到最优模型,该模型具有训练效果好、不易过拟合等优点。
GBDT不仅在工业界应用广泛,通常被用于多分类、点击率预测、搜索排序等任务;在各种数据挖掘竞赛中也是致命武器,据统计Kaggle上的比赛有一半以上的冠军方案都是基于GBDT。
LightGBM
LightGBM(Light Gradient Boosting Machine)是一个实现GBDT算法的框架,支持高效率的并行训练,并且具有更快的训练速度、更低的内存消耗、更好的准确率、支持分布式可以快速处理海量数据等优点。
LightGBM 框架中还包括随机森林和逻辑回归等模型。通常应用于二分类、多分类和排序等场景。
例如:在个性化商品推荐场景中,通常需要做点击预估模型。使用用户过往的行为(点击、曝光未点击、购买等)作为训练数据,来预测用户点击或购买的概率。根据用户行为和用户属性提取一些特征,包括:
类别特征(Categorical Feature):字符串类型,如性别(男/女)。
物品类型:服饰、玩具和电子等。
数值特征(Numrical Feature):整型或浮点型,如用户活跃度或商品价格等。
更多内容可见 LightGBM 中文文档、LightGBM英文文档
一般思路
本次问题是回归预测,能够取得不错效果的话,常规思路一般为使用机器学习模型,如LightGBM、XGBoost,或者使用深度学习模型(神经网络等)进行实践,在模型的搭建上就比较复杂,需要自己构建模型结构,对于数值数据需要进行标准化处理;
Task2 版本教程将使用机器学习模型解决本次问题,模型使用简单,数据不需要过多预处理;使用机器学习方法一般主要需要从 获取数据&增强、特征提取和模型 三个方面下手。
一般的使用机器学习模型解决问题的主要步骤为探索性数据分析、数据预处理、提取特征、切分训练集与验证集、训练模型、预测结果。
进阶代码baseline2
import numpy as np
import pandas as pd
!pip install lightgbm
import lightgbm as lgb
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_log_error, mean_absolute_error, mean_squared_error
import tqdm
import sys
import os
import gc
import argparse
import warnings
warnings.filterwarnings('ignore')
train = pd.read_csv('./data/data283931/train.csv')
test = pd.read_csv('./data/data283931/test.csv')
# 合并训练数据和测试数据,并进行排序
data = pd.concat([test, train], axis=0, ignore_index=True)
data = data.sort_values(['id','dt'], ascending=False).reset_index(drop=True)
# 历史平移
for i in range(10,30):
data[f'last{i}_target'] = data.groupby(['id'])['target'].shift(i)
# 窗口统计
data[f'win3_mean_target'] = (data['last10_target'] + data['last11_target'] + data['last12_target']) / 3
# 进行数据切分
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']]
def time_model(lgb, train_df, test_df, 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']
# 构建模型输入数据
train_matrix = lgb.Dataset(trn_x, label=trn_y)
valid_matrix = lgb.Dataset(val_x, label=val_y)
# lightgbm参数
lgb_params = {
'boosting_type': 'gbdt',
'objective': 'regression',
'metric': 'mse',
'min_child_weight': 5,
'num_leaves': 2 ** 5,
'lambda_l2': 10,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_freq': 4,
'learning_rate': 0.05,
'seed': 2024,
'nthread' : 16,
'verbose' : -1,
}
# 训练模型
model = lgb.train(lgb_params, train_matrix, 50000, valid_sets=[train_matrix, valid_matrix], categorical_feature=[], callbacks=[lgb.early_stopping(500), lgb.log_evaluation(500)])
# 验证集和测试集结果预测
val_pred = model.predict(val_x, num_iteration=model.best_iteration)
test_pred = model.predict(test_df[cols], num_iteration=model.best_iteration)
# 离线分数评估
score = mean_squared_error(val_pred, val_y)
print(score)
return val_pred, test_pred
lgb_oof, lgb_test = time_model(lgb, train, test, train_cols)
# 保存结果文件到本地
test['target'] = lgb_test
test[['id','dt','target']].to_csv('submit.csv', index=None)
进阶baseline2精讲
首先了解一些基本数据,在data目录下text.csv中下图这些数据
其中id为房屋id,
dt为日标识,训练数据dt最小为11,不同id对应序列长度不同;
type为房屋类型,通常而言不同类型的房屋整体消耗存在比较大的差异;
target为实际电力消耗,也是我们的本次比赛的预测目标。
(1)导入模块
<code>import numpy as np
import pandas as pd
!pip install lightgbm
import lightgbm as lgb
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_log_error, mean_absolute_error, mean_squared_error
import tqdm
import sys
import os
import gc
import argparse
import warnings
warnings.filterwarnings('ignore')
import numpy as np
: 导入NumPy库,通常用于数值计算和大型多维数组操作。import pandas as pd
: 导入Pandas库,通常用于数据处理和分析。!pip install lightgbm
: 这行代码是在Jupyter Notebook或Google Colab中使用Shell命令来安装LightGBM库,这是一个基于决策树的学习算法的高效梯度增强框架。import lightgbm as lgb
: 导入LightGBM库。import matplotlib.pyplot as plt
: 导入Matplotlib库中的pyplot模块,通常用于绘制图表。from sklearn.metrics import mean_squared_log_error, mean_absolute_error, mean_squared_error
: 从Scikit-learn库中导入几个用于评估模型性能的函数。import tqdm
: 导入tqdm库,它提供了一个快速、扩展性强的进度条。import sys
: 导入sys模块,它提供了对解释器使用或维护的某些变量的访问,以及与解释器强烈相关的函数。import os
: 导入os模块,它提供了许多与操作系统交互的函数。import gc
: 导入垃圾回收模块,用于手动管理Python的内存分配。import argparse
: 导入argparse模块,它用于解析命令行参数。import warnings
: 导入warnings模块,用于处理警告信息。warnings.filterwarnings('ignore')
: 设置警告过滤器,忽略所有警告信息。
这段代码主要用于设置一个数据分析或机器学习的环境,为后续的数据处理、模型训练和评估做准备。需要注意的是,第3行通常不会出现在普通的Python脚本中,因为它是一个Shell命令,用于安装包,这在运行脚本之前通常已经完成。
(2)探索性数据分析(EDA)
train = pd.read_csv('./data/data283931/train.csv')
test = pd.read_csv('./data/data283931/test.csv')
主要是读取训练数据和测试数据
为了更加客观可以进行可视化处理,因此导入了柱状图和折线图
import matplotlib.pyplot as plt
# 不同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()
import matplotlib.pyplot as plt
: 导入Matplotlib库的pyplot模块,用于绘图。type_target_df = train.groupby('type')['target'].mean().reset_index()
:
train.groupby('type')
: 根据数据集train
中的type
列进行分组。['target'].mean()
: 对每个分组中的target
列计算平均值。.reset_index()
: 将分组后的结果转换回DataFrame,并重置索引。plt.figure(figsize=(8, 4))
: 创建一个新的图形,并设置图形的大小为宽8英寸、高4英寸。plt.bar(type_target_df['type'], type_target_df['target'], color=['blue', 'green'])
:
绘制柱状图,其中type_target_df['type']
是x轴上的类别标签,type_target_df['target']
是每个类别的平均值。color=['blue', 'green']
指定柱状图的颜色。这里只有两种颜色,如果type
类型多于两种,颜色将循环使用。plt.xlabel('Type')
: 设置x轴的标签为’Type’。plt.ylabel('Average Target Value')
: 设置y轴的标签为’Average Target Value’。plt.title('Bar Chart of Target by Type')
: 设置图形的标题为’Bar Chart of Target by Type’。plt.show()
: 显示绘制的图形。
显示图形:
<code>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='-')code>
plt.xlabel('DateTime')
plt.ylabel('Target Value')
plt.title("Line Chart of Target for ID '00037f39cf'")
plt.show()
specific_id_df = train[train['id'] == '00037f39cf']
:
从train
DataFrame中选择所有行,其中id
列的值为’00037f39cf’。结果是一个新的DataFrame,只包含特定ID的数据。plt.figure(figsize=(10, 5))
: 创建一个新的图形,并设置图形的大小为宽10英寸、高5英寸。plt.plot(specific_id_df['dt'], specific_id_df['target'], marker='o', linestyle='-')code>:
绘制折线图,其中specific_id_df['dt']
是x轴上的时间点,specific_id_df['target']
是y轴上的目标值。marker='o'code>表示每个数据点都用圆圈标记。
linestyle='-'code>表示数据点之间用实线连接。
plt.xlabel('DateTime')
: 设置x轴的标签为’DateTime’。plt.ylabel('Target Value')
: 设置y轴的标签为’Target Value’。plt.title("Line Chart of Target for ID '00037f39cf'")
: 设置图形的标题为"Line Chart of Target for ID ‘00037f39cf’"。plt.show()
: 显示绘制的图形。
折线图:
(3)特征工程
这里主要构建了 历史平移特征 和 窗口统计特征;每种特征都是有理可据的,具体说明如下:
历史平移特征:通过历史平移获取上个阶段的信息;如下图所示,可以将d-1时间的信息给到d时间,d时间信息给到d+1时间,这样就实现了平移一个单位的特征构建。
窗口统计特征:窗口统计可以构建不同的窗口大小,然后基于窗口范围进统计均值、最大值、最小值、中位数、方差的信息,可以反映最近阶段数据的变化情况。如下图所示,可以将d时刻之前的三个时间单位的信息进行统计构建特征给我d时刻。
历史平移特征(Lag Features)
历史平移特征是指使用过去时间点的数据作为当前时间点的特征。它们的主要用途包括:
捕捉时间依赖性:许多时间序列数据具有时间依赖性,即未来的值可能受到过去值的影响。通过包含历史平移特征,模型可以学习这种依赖性。趋势和季节性:平移特征可以帮助模型识别数据中的趋势和季节性模式。自回归模型:在自回归模型(如ARIMA)中,历史平移特征是核心,因为模型直接利用过去的数据点来预测未来的值。提高预测准确性:通过包含过去的信息,模型可以更准确地预测未来。
窗口统计特征(Window Statistical Features)
窗口统计特征是指在一个时间窗口内计算的各种统计量,这些特征通常基于过去一段时间的观测数据。它们的主要用途包括:
聚合信息:通过计算窗口内的统计量(如平均值、中位数、标准差等),可以聚合过去一段时间的信息,帮助模型捕捉长期模式。平滑波动:窗口统计特征可以平滑短期波动,使模型更加关注长期趋势。捕捉周期性:对于具有固定周期性的时间序列,窗口统计特征可以用来捕捉这种周期性。特征多样性:通过引入不同的统计量,可以为模型提供更丰富的特征集,这有助于提高模型的性能。
以下是窗口统计特征的一些具体例子:
移动平均(Moving Average):过去N个时间点的平均值。移动中位数(Moving Median):过去N个时间点的中位数。移动标准差(Moving Standard Deviation):过去N个时间点的标准差,用于衡量波动性。移动最大值/最小值(Moving Max/Min):过去N个时间点的最大值或最小值。
<code># 合并训练数据和测试数据,并进行排序
data = pd.concat([test, train], axis=0, ignore_index=True)
data = data.sort_values(['id','dt'], ascending=False).reset_index(drop=True)
# 历史平移
for i in range(10,30):
data[f'last{i}_target'] = data.groupby(['id'])['target'].shift(i)
# 窗口统计
data[f'win3_mean_target'] = (data['last10_target'] + data['last11_target'] + data['last12_target']) / 3
# 进行数据切分
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']]
合并训练数据和测试数据,并进行排序
data = pd.concat([test, train], axis=0, ignore_index=True)
:
使用pd.concat
函数将test
和train
两个DataFrame在垂直方向(axis=0)上合并。ignore_index=True
参数表示合并后的DataFrame将有一个新的索引,而不是保留原来的索引。data = data.sort_values(['id','dt'], ascending=False).reset_index(drop=True)
:
使用sort_values
方法根据id
和dt
列对合并后的数据进行降序排序。注意一定要是降序因为只能用今天来预测明天,若升序则会影响测试集。ascending=False
参数表示降序排序。reset_index(drop=True)
将排序后的DataFrame的索引重置,并丢弃旧的索引。
历史平移
for i in range(10,30)
:
循环从10到29(包含29)。data[f'last{i}_target'] = data.groupby(['id'])['target'].shift(i)
:
对于每个i
值,创建一个新的列,名为last{i}_target
。使用groupby(['id'])
按id
分组,然后对每个组的target
列应用shift(i)
,将数据向下移动i
个位置,从而创建历史平移特征。
窗口统计
data[f'win3_mean_target'] = (data['last10_target'] + data['last11_target'] + data['last12_target']) / 3
:
创建一个名为win3_mean_target
的新列,它计算last10_target
、last11_target
和last12_target
三列的平均值,作为3个时间点的移动平均窗口统计特征。
进行数据切分
train = data[data.target.notnull()].reset_index(drop=True)
:
选择target
列非空的数据行作为训练集,并重置索引。test = data[data.target.isnull()].reset_index(drop=True)
:
选择target
列为空的数据行作为测试集,并重置索引。
确定输入特征
train_cols = [f for f in data.columns if f not in ['id','target']]
:
创建一个列表train_cols
,包含除了id
和target
列之外的所有列名,这些列将用作模型的输入特征。
注意:在创建历史平移特征时,代码使用了groupby(['id'])
,这意味着每个id
的平移是独立的。在创建窗口统计特征时,只计算了一个简单的3个时间点的移动平均,但在实际应用中,可能需要根据具体情况计算更复杂或更长的窗口统计特征。
(4)模型训练与测试集预测
这里选择使用Lightgbm模型,也是通常作为数据挖掘比赛的基线模型,在不需要过程调参的情况的也能得到比较稳定的分数。
def time_model(lgb, train_df, test_df, 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']
# 构建模型输入数据
train_matrix = lgb.Dataset(trn_x, label=trn_y)
valid_matrix = lgb.Dataset(val_x, label=val_y)
# lightgbm参数
lgb_params = {
'boosting_type': 'gbdt',
'objective': 'regression',
'metric': 'mse',
'min_child_weight': 5,
'num_leaves': 2 ** 5,
'lambda_l2': 10,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_freq': 4,
'learning_rate': 0.05,
'seed': 2024,
'nthread' : 16,
'verbose' : -1,
}
# 训练模型
model = lgb.train(lgb_params, train_matrix, 50000, valid_sets=[train_matrix, valid_matrix], categorical_feature=[], callbacks=[lgb.early_stopping(500), lgb.log_evaluation(500)])
# 验证集和测试集结果预测
val_pred = model.predict(val_x, num_iteration=model.best_iteration)
test_pred = model.predict(test_df[cols], num_iteration=model.best_iteration)
# 离线分数评估
score = mean_squared_error(val_pred, val_y)
print(score)
return val_pred, test_pred
lgb_oof, lgb_test = time_model(lgb, train, test, train_cols)
# 保存结果文件到本地
test['target'] = lgb_test
test[['id','dt','target']].to_csv('submit.csv', index=None)
time_model
函数定义
参数说明:
lgb
: 导入的LightGBM库。train_df
: 包含训练数据的DataFrame。test_df
: 包含测试数据的DataFrame。cols
: 用来训练模型的特征列。训练集和验证集切分:
trn_x, trn_y = train_df[train_df.dt>=31][cols], train_df[train_df.dt>=31]['target']
: 选择dt
大于等于31的数据作为训练集,并分别提取特征和目标变量。val_x, val_y = train_df[train_df.dt<=30][cols], train_df[train_df.dt<=30]['target']
: 选择dt
小于等于30的数据作为验证集,并分别提取特征和目标变量。构建模型输入数据:
train_matrix = lgb.Dataset(trn_x, label=trn_y)
: 创建训练集的LightGBM数据集。valid_matrix = lgb.Dataset(val_x, label=val_y)
: 创建验证集的LightGBM数据集。LightGBM参数:
定义了一组用于训练LightGBM模型的参数。训练模型:
使用lgb.train
函数训练模型,并设置早停(early stopping)和日志评估(log evaluation)。验证集和测试集结果预测:
使用训练好的模型对验证集和测试集进行预测。离线分数评估:
使用均方误差(MSE)评估验证集的预测结果。返回:
返回验证集和测试集的预测结果。
调用time_model
函数
lgb_oof, lgb_test = time_model(lgb, train, test, train_cols)
: 调用time_model
函数,并传入相应的参数。
保存结果文件到本地
test['target'] = lgb_test
: 将测试集的预测结果添加到test
DataFrame的target
列。test[['id','dt','target']].to_csv('submit.csv', index=None)
: 将包含id
、dt
和target
列的测试集数据保存到本地CSV文件submit.csv
中。
在这一部分我一直卡在一个报错上,(如果没有报错请自行忽略以下)
报错:
原因就是在不同版本的 LightGBM 中,可能会有不同的参数命名或功能增减。通常情况下,LightGBM 的更新会增加新的功能和参数,同时修复之前版本中的一些问题。
如果是3.3.0版本将上文25行代码替换,如果是最新版本则不做修改
<code>model = lgb.train(lgb_params, train_matrix, 50000, valid_sets=[train_matrix, valid_matrix],
categorical_feature=[], verbose_eval=500, early_stopping_rounds=500)
训练结束后直接运行全部代码
生成submit.csv文件然后去比赛链接提交结果,这时你会发现分数已经降低啦qwq
参考资料
下一站,向冠军冲击!
我的机器学习之路
我的机器学习入门清单及路线!
机器学习神器Scikit-Learn保姆教程!
《Datawhale人工智能培养方案》发布!
《机器学习算法竞赛实战》书籍!
时间序列预测方法总结
《数据竞赛入门讲义》.pdf
双节棍「大师」鱼佬亲传武功秘籍:如何进行一场数据挖掘算法竞赛?
Task3:进阶优化
特征提取通常涉及以下几个主要方面:
趋势特征:识别数据中的长期趋势,如线性或非线性增长、减少等。
例如,可以通过计算移动平均来提取趋势。
季节性特征:检测数据中的周期性模式,如日、周、月或年的季节性变化。
季节性指数计算可以用来提取季节性特征。
周期性特征:与季节性相似,但周期性不一定与固定的时间间隔相关。
例如,傅立叶变换可以用来识别数据中的不同频率成分。
统计特征:包括均值、中位数、方差、标准差、偏度和峰度等。
这些基础统计量可以提供数据分布的重要信息。
波动特征:如自相关函数(ACF)和偏自相关函数(PACF)来检测数据的自相关性。
这些特征对于建立ARIMA等模型非常重要。
峰谷特征:检测时间序列中的局部最大值和最小值。
可以通过求导和设置阈值来识别峰谷。
形状特征:如波形特征、复杂度、转折点等。
这些特征有助于识别时间序列的形状变化。
频率特征:使用小波变换等方法提取时间序列在不同频率上的特征。
第一次优化:可以加入更多的特征进行优化代码如下
<code># 合并训练数据和测试数据
data = pd.concat([train, test], axis=0).reset_index(drop=True)
data = data.sort_values(['id','dt'], ascending=False).reset_index(drop=True)
# 历史平移
for i in range(10,36):
data[f'target_shift{i}'] = data.groupby('id')['target'].shift(i)
# 历史平移 + 差分特征
for i in range(1,4):
data[f'target_shift10_diff{i}'] = data.groupby('id')['target_shift10'].diff(i)
# 窗口统计
for win in [15,30,50,70]:
data[f'target_win{win}_mean'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').mean().valuescode>
data[f'target_win{win}_max'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').max().valuescode>
data[f'target_win{win}_min'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').min().valuescode>
data[f'target_win{win}_std'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').std().valuescode>
# 历史平移 + 窗口统计
for win in [7,14,28,35,50,70]:
data[f'target_shift10_win{win}_mean'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').mean().valuescode>
data[f'target_shift10_win{win}_max'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').max().valuescode>
data[f'target_shift10_win{win}_min'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').min().valuescode>
data[f'target_shift10_win{win}_sum'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').sum().valuescode>
data[f'target_shift710win{win}_std'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').std().valuescode>
历史平移特征:例如 target_shift10
,它表示将 ‘target’ 列的值根据 ‘id’ 分组后向右移动 10 个位置。历史平移 + 差分特征:例如 target_shift10_diff1
,它是基于 target_shift10
的差分特征。窗口统计特征:例如 target_win15_mean
,它是基于 ‘target’ 列的窗口大小为 15 的移动平均。历史平移 + 窗口统计特征:例如 target_shift10_win7_mean
,它是基于 target_shift10
的窗口大小为 7 的移动平均。
提交后没有太大的提升
暂时来到了251分
第二次优化选择了lightgbm、xgboost和catboost模型的融合
对于每个模型均选择经典的K折交叉验证方法进行离线评估,大体流程如下:
1、K折交叉验证会把样本数据随机的分成K份;
2、每次随机的选择K-1份作为训练集,剩下的1份做验证集;
3、当这一轮完成后,重新随机选择K-1份来训练数据;
4、最后将K折预测结果取平均作为最终提交结果。
<code>from sklearn.model_selection import StratifiedKFold, KFold, GroupKFold
import lightgbm as lgb
import xgboost as xgb
from catboost import CatBoostRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error
def cv_model(clf, train_x, train_y, test_x, clf_name, seed = 2024):
'''
clf:调用模型
train_x:训练数据
train_y:训练数据对应标签
test_x:测试数据
clf_name:选择使用模型名
seed:随机种子
'''
folds = 5
kf = KFold(n_splits=folds, shuffle=True, random_state=seed)
oof = np.zeros(train_x.shape[0])
test_predict = np.zeros(test_x.shape[0])
cv_scores = []
for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
print('************************************ {} ************************************'.format(str(i+1)))
trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
if clf_name == "lgb":
train_matrix = clf.Dataset(trn_x, label=trn_y)
valid_matrix = clf.Dataset(val_x, label=val_y)
params = {
'boosting_type': 'gbdt',
'objective': 'regression',
'metric': 'mae',
'min_child_weight': 6,
'num_leaves': 2 ** 6,
'lambda_l2': 10,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_freq': 4,
'learning_rate': 0.1,
'seed': 2023,
'nthread' : 16,
'verbose' : -1,
}
model = clf.train(params, train_matrix, 1000, valid_sets=[train_matrix, valid_matrix],
categorical_feature=[], verbose_eval=200, early_stopping_rounds=100)
val_pred = model.predict(val_x, num_iteration=model.best_iteration)
test_pred = model.predict(test_x, num_iteration=model.best_iteration)
if clf_name == "xgb":
xgb_params = {
'booster': 'gbtree',
'objective': 'reg:squarederror',
'eval_metric': 'mae',
'max_depth': 5,
'lambda': 10,
'subsample': 0.7,
'colsample_bytree': 0.7,
'colsample_bylevel': 0.7,
'eta': 0.1,
'tree_method': 'hist',
'seed': 520,
'nthread': 16
}
train_matrix = clf.DMatrix(trn_x , label=trn_y)
valid_matrix = clf.DMatrix(val_x , label=val_y)
test_matrix = clf.DMatrix(test_x)
watchlist = [(train_matrix, 'train'),(valid_matrix, 'eval')]
model = clf.train(xgb_params, train_matrix, num_boost_round=1000, evals=watchlist, verbose_eval=200, early_stopping_rounds=100)
val_pred = model.predict(valid_matrix)
test_pred = model.predict(test_matrix)
if clf_name == "cat":
params = {'learning_rate': 0.1, 'depth': 5, 'bootstrap_type':'Bernoulli','random_seed':2023,
'od_type': 'Iter', 'od_wait': 100, 'random_seed': 11, 'allow_writing_files': False}
model = clf(iterations=1000, **params)
model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
metric_period=200,
use_best_model=True,
cat_features=[],
verbose=1)
val_pred = model.predict(val_x)
test_pred = model.predict(test_x)
oof[valid_index] = val_pred
test_predict += test_pred / kf.n_splits
score = mean_absolute_error(val_y, val_pred)
cv_scores.append(score)
print(cv_scores)
return oof, test_predict
# 选择lightgbm模型
lgb_oof, lgb_test = cv_model(lgb, train[train_cols], train['target'], test[train_cols], 'lgb')
# 选择xgboost模型
xgb_oof, xgb_test = cv_model(xgb, train[train_cols], train['target'], test[train_cols], 'xgb')
# 选择catboost模型
cat_oof, cat_test = cv_model(CatBoostRegressor, train[train_cols], train['target'], test[train_cols], 'cat')
# 进行取平均融合
final_test = (lgb_test + xgb_test + cat_test) / 3
最后付一份深度学习优化方案
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense, RepeatVector, TimeDistributed
from keras.optimizers import Adam
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
# 数据预处理
def preprocess_data(df, look_back=100):
# 将数据按照id进行分组
grouped = df.groupby('id')
datasets = {}
for id, group in grouped:
datasets[id] = group.values
# 准备训练数据集
X, Y = [], []
for id, data in datasets.items():
for i in range(10, 15): # 每个id构建5个序列
a = data[i:(i + look_back), 3]
a = np.append(a, np.array([0]*(100-len(a))))
X.append(a[::-1])
Y.append(data[i-10:i, 3][::-1])
# 准备测试数据集
OOT = []
for id, data in datasets.items():
a = data[:100, 3]
a = np.append(a, np.array([0]*(100-len(a))))
OOT.append(a[::-1])
return np.array(X, dtype=np.float64), np.array(Y, dtype=np.float64), np.array(OOT, dtype=np.float64)
# 定义模型
def build_model(look_back, n_features, n_output):
model = Sequential()
model.add(LSTM(50, input_shape=(look_back, n_features)))
model.add(RepeatVector(n_output))
model.add(LSTM(50, return_sequences=True))
model.add(TimeDistributed(Dense(1)))
model.compile(loss='mean_squared_error', optimizer=Adam(0.001))code>
return model
# 构建和训练模型
look_back = 100 # 序列长度
n_features = 1 # 假设每个时间点只有一个特征
n_output = 10 # 预测未来10个时间单位的值
# 预处理数据
X, Y, OOT = preprocess_data(train, look_back=look_back)
# 构建模型
model = build_model(look_back, n_features, n_output)
# 训练模型
model.fit(X, Y, epochs=10, batch_size=64, verbose=1)
# 进行预测
predicted_values = model.predict(OOT)
效果貌似并不是很好
之后尝试加入小波算法等
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。