Datawhale AI 夏令营 电力需求预测挑战赛

目录

Task 1:

赛题背景

赛题任务

评审规则

1.数据说明

2.评审规则

代码

​编辑

Task 2:

入门lightgbm,开始特征工程

基础概念入门

GBDT

LightGBM

代码

(1)导入模块

(2)探索性数据分析(EDA)

(3)特征工程

(4)模型训练与测试集预测

Task 3:尝试深度学习方案

优化方案详解

特征优化

参考代码

模型融合

代码


Task 1:

赛题背景

随着全球经济的快速发展和城市化进程的加速,电力系统面临着越来越大的挑战。电力需求的准确预测对于电网的稳定运行、能源的有效管理以及可再生能源的整合至关重要。

然而,电力需求受到多种因素的影响,为了提高电力需求预测的准确性和可靠性,推动智能电网和可持续能源系统的发展,本场以“电力需求预测”为赛题的数据算法挑战赛。选手需要根据历史数据构建有效的模型,能够准确的预测未来电力需求。

赛题任务

给定多个房屋对应电力消耗历史N天的相关序列数据等信息,预测房屋对应电力的消耗。

评审规则

1.数据说明

赛题数据由训练集和测试集组成,为了保证比赛的公平性,将每日日期进行脱敏,用1-N进行标识,即1为数据集最近一天,其中1-10为测试集数据。数据集由字段id(房屋id)、 dt(日标识)、type(房屋类型)、target(实际电力消耗)组成。

特征字段字段描述
id房屋id
dt日标识
type房屋类型
target实际电力消耗,预测目标

2.评审规则

预测结果以 mean square error 作为评判标准,具体公式如下:

其中,是真实电力消耗,是预测电力消耗。

代码

# 1. 导入需要用到的相关库
# 导入 pandas 库,用于数据处理和分析
import pandas as pd
# 导入 numpy 库,用于科学计算和多维数组操作
import numpy as np

# 2. 读取训练集和测试集
# 使用 read_csv() 函数从文件中读取训练集数据,文件名为 'train.csv'
train = pd.read_csv('train.csv')
# 使用 read_csv() 函数从文件中读取测试集数据,文件名为 'test.csv'
test = pd.read_csv('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')

# 5. 保存结果文件到本地
test[['id','dt','target']].to_csv('submit.csv', index=None)

解释:

  1. 导入库:首先,代码导入了需要用到的库,包括 pandas(用于数据处理和分析)。

  2. 读取数据:代码通过使用 pd.read_csv 函数从文件中读取训练集和测试集数据,并将其存储在 train.csvtest.csv 两个数据框中。

  3. 计算最近时间的用电均值:计算训练数据最近11-20单位时间内对应id的目标均值,可以用来反映最近的用电情况。

  4. 将用电均值直接作为预测结果:这里使用merge函数根据'id'列将testtarget_mean两个DataFrame进行左连接,这意味着测试集的所有行都会保留。

  5. 保存结果文件到本地:使用to_csv()函数将测试集的'id''dt''target'列保存为CSV文件,文件名为'submit.csv'index=None参数表示在保存时不包含行索引。

得分:

Task 1部分主要是跑通基础的baseline,没有太多要求,我是在飞桨Studio上运行的,下载submit.csv文件上传就可以得到相应分数,主要是计算均值作为预测结果,误差肯定很大,在Task2和Task3中会采取不同的方法进行优化。

Task 2:

入门lightgbm,开始特征工程

  • 解决该问题常见的几种思路是什么?

本次问题是回归预测,能够取得不错效果的话,常规思路一般为使用机器学习模型,如LightGBM、XGBoost,或者使用深度学习模型(神经网络等)进行实践,在模型的搭建上就比较复杂,需要自己构建模型结构,对于数值数据需要进行标准化处理;

  • Task2版本的教程使用什么思路?

Task1 的baseline我们是基于经验模型(使用均值作为结果数据)来解决的问题;

Task2 版本教程将使用机器学习模型解决本次问题,模型使用简单,数据不需要过多预处理;

使用机器学习方法一般主要需要从 获取数据&增强特征提取模型 三个方面下手。

  • 使用机器学习方法有哪几个步骤?

一般的使用机器学习模型解决问题的主要步骤为探索性数据分析、数据预处理、提取特征、切分训练集与验证集、训练模型、预测结果

基础概念入门

GBDT

GBDT (Gradient Boosting Decision Tree) 是机器学习中一个长盛不衰的模型,其主要思想是利用弱分类器(决策树)迭代训练以得到最优模型,该模型具有训练效果好、不易过拟合等优点。GBDT不仅在工业界应用广泛,通常被用于多分类、点击率预测、搜索排序等任务;在各种数据挖掘竞赛中也是致命武器,据统计Kaggle上的比赛有一半以上的冠军方案都是基于GBDT

LightGBM

LightGBM(Light Gradient Boosting Machine)是一个实现GBDT算法的框架,支持高效率的并行训练,并且具有更快的训练速度、更低的内存消耗、更好的准确率、支持分布式可以快速处理海量数据等优点。LightGBM 框架中还包括随机森林和逻辑回归等模型。通常应用于二分类、多分类和排序等场景。例如:在个性化商品推荐场景中,通常需要做点击预估模型。使用用户过往的行为(点击、曝光未点击、购买等)作为训练数据,来预测用户点击或购买的概率。根据用户行为和用户属性提取一些特征,包括:

  • 类别特征(Categorical Feature):字符串类型,如性别(男/女)。

  • 物品类型:服饰、玩具和电子等。

  • 数值特征(Numrical Feature):整型或浮点型,如用户活跃度或商品价格等。

更多内容可见 LightGBM 中文文档LightGBM英文文档

代码

(1)导入模块

此部分包含代码所需的模块

import numpy as np
import pandas as pd
import lightgbm as lgb
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')

这一步大家常出现一个错误:

这是因为没有安装lightgbm,我在这一步也出现了,切记一定要先安装,安装指令:!pip install lightgbm==3.3.0,之后就没问题了。

(2)探索性数据分析(EDA)

在数据准备阶段,主要读取训练数据和测试数据,并进行基本的数据展示。

train = pd.read_csv('./data/train.csv')
test = pd.read_csv('./data/test.csv')

数据简单介绍:

  • 其中id为房屋id,

  • dt为日标识,训练数据dt最小为11,不同id对应序列长度不同;

  • type为房屋类型,通常而言不同类型的房屋整体消耗存在比较大的差异;

  • target为实际电力消耗,也是我们的本次比赛的预测目标。

下面进行简单的可视化分析,帮助我们对数据有个简单的了解。

  • 不同type类型对应target的柱状图

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()

  • id为00037f39cf的按dt为序列关于target的折线图

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)特征工程

这里主要构建了 历史平移特征 窗口统计特征;每种特征都是有理可据的,具体说明如下:

  • 历史平移特征:通过历史平移获取上个阶段的信息;如下图所示,可以将d-1时间的信息给到d时间,d时间信息给到d+1时间,这样就实现了平移一个单位的特征构建。

  • 窗口统计特征:窗口统计可以构建不同的窗口大小,然后基于窗口范围进统计均值、最大值、最小值、中位数、方差的信息,可以反映最近阶段数据的变化情况。如下图所示,可以将d时刻之前的三个时间单位的信息进行统计构建特征给我d时刻。

完整代码如下:

# 合并训练数据和测试数据,并进行排序
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']]

(4)模型训练与测试集预测

这里选择使用Lightgbm模型,也是通常作为数据挖掘比赛的基线模型,在不需要过程调参的情况的也能得到比较稳定的分数。

另外需要注意的训练集和验证集的构建:因为数据存在时序关系,所以需要严格按照时序进行切分,

  • 这里选择原始给出训练数据集中dt为30之后的数据作为训练数据,之前的数据作为验证数据

  • 这样保证了数据不存在穿越问题(不使用未来数据预测历史数据)

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=[], verbose_eval=500, early_stopping_rounds=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)

得分:

Task 3:尝试深度学习方案

优化方案详解

在提分实践部分,将在原有Baseline基础上做更多优化,一般优化思路,从特征工程与模型中来思考。

优化方法建议:

  1. 提取更多特征:在数据挖掘比赛中,特征总是最终制胜法宝,去思考什么信息可以帮助我们提高预测精准度,然后将其转化为特征输入到模型。

  2. 尝试不同的模型:模型间存在很大的差异,预测结果也会不一样,比赛的过程就是不断的实验和试错的过程,通过不断的实验寻找最佳模型,同时帮助自身加强模型的理解能力。

特征优化

这里主要构建了历史平移特征、差分特征、和窗口统计特征;每种特征都是有理可据的,具体说明如下:

(1)历史平移特征:通过历史平移获取上个阶段的信息;

(2)差分特征:可以帮助获取相邻阶段的增长差异,描述数据的涨减变化情况。在此基础上还可以构建相邻数据比值变化、二阶差分等;

(3)窗口统计特征:窗口统计可以构建不同的窗口大小,然后基于窗口范围进统计均值、最大值、最小值、中位数、方差的信息,可以反映最近阶段数据的变化情况。

参考代码

# 合并训练数据和测试数据
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().values
    data[f'target_win{win}_max'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').max().values
    data[f'target_win{win}_min'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').min().values
    data[f'target_win{win}_std'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').std().values

# 历史平移 + 窗口统计
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().values
    data[f'target_shift10_win{win}_max'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').max().values
    data[f'target_shift10_win{win}_min'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').min().values
    data[f'target_shift10_win{win}_sum'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').sum().values
    data[f'target_shift710win{win}_std'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').std().values

模型融合

进行模型融合的前提是有多个模型的输出结果,比如使用catboost、xgboost和lightgbm三个模型分别输出三个结果,这时就可以将三个结果进行融合,最常见的是将结果直接进行加权平均融合。

下面我们构建了cv_model函数,内部可以选择使用lightgbm、xgboost和catboost模型,可以依次跑完这三个模型,然后将三个模型的结果进行取平均进行融合。

对于每个模型均选择经典的K折交叉验证方法进行离线评估,大体流程如下:

1、K折交叉验证会把样本数据随机的分成K份;

2、每次随机的选择K-1份作为训练集,剩下的1份做验证集;

3、当这一轮完成后,重新随机选择K-1份来训练数据;

4、最后将K折预测结果取平均作为最终提交结果。

代码

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

这里大家可能不知道怎么下载submit文件,要加上代码保存结果到本地,然后提交。

test['target'] = final_test
test[['id','dt','target']].to_csv('submit.csv', index=None)

还有问题是要提前安装xgboost,catboost,不然会报错。如下图所示:

方法:

系统访问权限被拒绝,所以只能为当前用户安装,代码如下:

!pip install --user xgboost -i https://mirror.baidu.com/pypi/simple(镜像源百度、豆瓣都可行)

!pip install --user catboost -i https://mirror.baidu.com/pypi/simple

如果装了一遍后再次出现ModuleNotFoundError,则重启内核。

模型融合这部分需要跑的时间较长,耐心等待。

运行提交文件后,评分在236分,就先码到这里,其实还有很多不错的深度学习方法,小伙伴们可以去尝试尝试,试试看有没有更好的优化方案。

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值