纯小白,非专业,仅有自学Python的基础。以此次夏令营为契机,入门下机器学习。
目标:1.把代码的每一步搞懂。2.对接触到的计算机名词有个理解概念。3.对机器学习数据分析有个理解概念。
赛题解读
赛题任务
本场以“电力需求预测”为赛题的数据算法挑战赛。选手需要根据历史数据构建有效的模型,能够准确的预测未来电力需求。
给定多个房屋对应电力消耗历史N天的相关序列数据等信息,预测房屋对应电力的消耗。
赛题分析
本赛题是一个典型的时间序列问题
时间序列问题是指对按时间顺序排列的数据点进行分析和预测的问题,往往用来做未来的趋势预测。比如,基于历史股票每天的股价,预测未来股票的价格走向。
简单来说,本次赛题的目标很简单清晰——【训练 时序预测模型 助力电力需求预测】
时间序列预测问题可以通过多种建模方法来解决,包括传统的时间序列模型、机器学习模型和深度学习模型。
Task1: baseline学习
baseline
# 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)
baseline精读学习
1. 导入库
import pandas as pd
import numpy as np
- import 语句导入;as 指定别名
- Pandas 是基于 NumPy的一个数据分析和操作库,提供了快速、灵活和表达力强的数据结构,旨在使数据清洗和分析工作变得更加简单易行。
📝eg. 使用pandas读取一个CSV文件并进行简单的数据处理
import pandas as pd
# 读取CSV文件
df = pd.read_csv('data.csv')
# 查看数据的前几行
print(df.head())
# 统计数据的描述性统计信息(如平均值、标准差、最小值、最大值等)
print(df.describe())
- NumPy 提供了多维数组对象、派生对象(如掩码数组和矩阵)以及用于快速操作数组的各种例程,包括数学、逻辑、形状操作、排序、选择、I/O、离散傅立叶变换、基本线性代数、基本统计运算、随机模拟等等。
📝eg. 使用numpy创建一个数组并进行简单的数学运算
import numpy as np
# 创建一个numpy数组
arr = np.array([1, 2, 3, 4, 5])
# 打印数组
print("数组:", arr)
# 计算数组的平均值
mean_value = np.mean(arr)
print("平均值:", mean_value)
# 计算数组的标准差
std_deviation = np.std(arr)
print("标准差:", std_deviation)
- 学习资料:Pandas教程(文字版:https://inter.joyfulpandas.datawhale.club/Home.html )(视频版:https://www.bilibili.com/video/BV1tK4y177AF/ )(图书:《pandas数据处理与分析》 )机器学习教程( https://linklearner.com/learn/summary/13 )
2. 读取数据
# 使用 read_csv() 函数从文件中读取训练集数据,文件名为 'train.csv'
train = pd.read_csv('train.csv')
# 使用 read_csv() 函数从文件中读取测试集数据,文件名为 'test.csv'
test = pd.read_csv('test.csv')
- 代码通过使用
pd.read_csv
函数从文件中读取训练集和测试集数据,并将其存储在train.csv
和test.csv
两个数据框中。
3. 计算最近时间的用电均值
target_mean = train[train['dt']<=20].groupby(['id'])['target'].mean().reset_index()
- 计算训练数据最近11-20单位时间内对应id的目标均值,可以用来反映最近的用电情况。
train
: train是一个pandas的DataFrame对象,包含了训练数据。train['dt'] <= 20
: 这是一个条件筛选操作,它选择了train数据中满足条件dt <= 20
的行。这意味着只选择了dt列中数值小于或等于20的行。train[train['dt'] <= 20]
: 将上述条件应用于train数据集,得到一个新的DataFrame,其中包含满足条件的行。.groupby(['id'])['target']
: 这是pandas中的分组操作。它首先根据id列对数据进行分组,然后选择target列。.mean()
: 在分组操作之后,对每个分组中的target列计算均值。这样会得到一个包含每个id及其对应target均值的序列。.reset_index()
: 这一步是为了将分组后的结果重新设置索引。默认情况下,.groupby()
操作会将分组的列作为索引,但使用.reset_index()
可以将这些索引重新变成列,使得最终结果是一个带有id和target均值的DataFrame。target_mean
: 最终,将处理后的数据赋值给target_mean变量。这个变量包含了每个id在dt小于或等于20的条件下,对应的target列的均值。
4. 将用电均值直接作为预测结果
test = test.merge(target_mean, on=['id'], how='left')
- 这里使用
merge
函数根据'id'
列将test
和target_mean
两个DataFrame进行左连接,这意味着测试集的所有行都会保留。 .merge()
: 这是pandas中用于数据合并的函数。它允许根据一个或多个键将不同DataFrame中的行连接起来。on=['id']
: 指定了连接操作的键,这里是’id’列。.merge()
函数会基于这个键将两个DataFrame进行合并how='left'
: 这是指定连接方式的参数。'left'
表示使用test中的键(即’id’列)作为左连接的基础。左连接保留了test中的所有行,并且将target_mean中与之匹配的行合并过来。如果test中的某些id在target_mean中找不到对应的均值,则会用NaN
填充。
5. 保存结果文件到本地
test[['id','dt','target']].to_csv('submit.csv', index=None)
- 使用
to_csv()
函数将测试集的'id'
、'dt'
和'target'
列保存为CSV文件,文件名为'submit.csv'
。index=None
参数表示在保存时不包含行索引。
Task2:使用LightGBM模型
LightGBM模型
LightGBM(Light Gradient Boosting Machine)是一种基于梯度提升树(Gradient Boosting Decision Tree,GBDT)的机器学习算法,支持高效率的并行训练,并且具有更快的训练速度、更低的内存消耗、更好的准确率、支持分布式可以快速处理海量数据等优点。
使用场景
LightGBM 框架中还包括随机森林和逻辑回归等模型。通常应用于二分类、多分类和排序等场景。
- 分类问题: 如信用风险评估、用户分类等。
- 回归问题: 如房价预测、销量预测等。
- 排序问题: 如推荐系统中的排序
训练过程
在训练 LightGBM 模型时,通常会使用训练数据和验证数据集(用于早期停止)。模型会通过多轮迭代,每一轮迭代都会构建一棵新的树来逐步改善模型的性能。在每一轮迭代中,模型会根据损失函数的梯度来调整树的结构,以最小化损失函数。
结束条件
训练过程通常会设置一些结束条件,比如达到指定的树的数量、损失函数变化不大等。本次进阶代码中的 “Training until validation scores don’t improve for 500 rounds” 就是一种结束条件,即如果在验证集上的评分在连续的500轮中没有改善,就停止训练,以避免过拟合。
进阶代码精读
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')
- 导入NumPy库(用于数值计算)、Pandas库(用于数据处理和分析)、LightGBM库(用于训练和预测),并约定别名。
- ❗ 报错点:批量运行安装不上LightGBM库,需要单独运行命令行
pip install lightgbm==3.3.0
- 从Scikit-Learn库中导入指定的评估指标函数:mean_squared_log_error(均方对数误差)、mean_absolute_error(平均绝对误差)、mean_squared_error(均方误差)。这些函数用于评估预测模型的性能。
- 导入tqdm库(用于在循环中显示进度条),导入Python标准库中的sys模块(访问和操作运行环境)、os模块(与操作系统交互)、gc模块(释放内存空间)、argarse模块(解析命令行参数)。
- 导入Python标准库中的warning模块(处理警告信息),调用warnings模块的filterwarnings函数(忽略警告信息)。
2.探索性数据分析(EDA)
导入数据
train = pd.read_csv('./data/train.csv')
test = pd.read_csv('./data/test.csv')
查看数据
print(train.head())
print(test.head())
- 可以使用 .head() 方法来查看数据的前几行,默认显示前5行
-
- 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()
![](https://i-blog.csdnimg.cn/direct/c18823a646b548bd83ccf7cf1ea3bffc.png#pic_center)
② 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()
![](https://i-blog.csdnimg.cn/direct/1b629f1df5d848d5a67a9866644ce439.png#pic_center)
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)
pd.concat()
函数用于沿着指定轴(axis=0,即行方向)将两个数据集test和train合并为一个新的数据集data。参数ignore_index=True
表示忽略原始数据集的索引,生成一个新的连续索引。data.sort_values()
方法按照指定的列或列组进行排序。在这里,首先按照id列进行排序(降序),如果id相同,则按照dt列排序(同样是降序)。参数ascending=False
表示降序排列。reset_index(drop=True)
是为了重新设置索引。drop=True
表示丢弃原始索引,生成新的连续索引,这样可以保证索引的连续性和顺序性。
② 历史平移
# 历史平移
for i in range(10,30):
data[f'last{i}_target'] = data.groupby(['id'])['target'].shift(i)
for i in range(10,30)
: 这个循环从10到29(即i的取值范围是10到29),每次循环创建一个新的特征列.data.groupby(['id'])['target'].shift(i)
这部分代码是关键:data.groupby(['id'])
根据id列进行分组,这意味着对于每个唯一的id值,会有一个独立的分组。['target'].shift(i)
对于每个分组,对target列进行平移操作,即将target列的值向上平移i行。这样,对于每个样本,会生成i个时间步长前的target值作为新特征。
data[f'last{i}_target']
是为新生成的特征列命名,格式为last{i}_target,其中{i}表示当前循环的i值,用于标识每个特征的时间步长。f'...'
是一种格式化字符串的方式,其中的表达式会被求值并插入到字符串中的 {} 括号内。相比传统的字符串格式化方法(如.format()
或%
操作符),f-string 更加直观、简洁,并且性能更好。
📝f-string使用示例:
name = "Alice"
age = 30
# 使用 f-string 格式化字符串
message = f"My name is {name} and I am {age} years old."
print(message) # 输出: My name is Alice and I am 30 years old.
③ 窗口统计
# 窗口统计
data[f'win3_mean_target'] = (data['last10_target'] + data['last11_target'] + data['last12_target']) / 3
- 创建新特征列:
data[f'win3_mean_target']
创建了一个名为 win3_mean_target 的新特征列。 - 计算平均值:(
data['last10_target'] + data['last11_target'] + data['last12_target']
) 是一个算术表达式,将 last10_target、last11_target 和 last12_target 列对应位置的值相加。/ 3
对上述求和结果进行除以3的操作,即计算这三列的平均值。 - 赋值:计算得到的平均值被赋值给
data[f'win3_mean_target']
这一列,每一行的值是相应三个历史目标变量的平均值。
④ 数据切分
# 进行数据切分
train = data[data.target.notnull()].reset_index(drop=True)
test = data[data.target.isnull()].reset_index(drop=True)
- 将处理后的数据集根据target列是否为
null
进行切分,从而将其分为训练集train和测试集test。.reset_index(drop=True)
是为了重新设置索引,并丢弃旧的索引。
⑤ 确定输入特征
# 确定输入特征
train_cols = [f for f in data.columns if f not in ['id','target']]
- 列表推导式:
data.columns
返回 data 数据框中的所有列名组成的列表。 - 条件过滤:
if f not in ['id','target']
:列表推导式中的条件部分,保证只有那些列名 f 不是 ‘id’ 或 ‘target’ 的列被包含在 train_cols 列表中。 - 结果:
train_cols
是一个包含了所有不包括 ‘id’ 和 ‘target’ 列的列名的列表。
4.模型训练与测试集预测
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)
- 函数定义:
def
表示函数的定义开始,紧接着是函数名time_model
。time_model 函数接受四个参数:lgb(LightGBM模型对象)、train_df(训练数据集)、test_df(测试数据集)、cols(用于训练的特征列) - 训练集和验证集切分:
train_df
根据日期dt
切分为训练集(日期大于等于31)和验证集(日期小于等于30),分别提取特征列 cols 和目标列 ‘target’。 - 构建模型输入数据:使用 LightGBM 的
Dataset
类构建训练集和验证集的数据格式,其中trn_x
和val_x
是特征数据,trn_y
和val_y
是对应的目标数据。 - LightGBM 参数设置:设置 LightGBM 模型的参数,包括 boosting 类型、优化目标、评估指标、叶子节点数、正则化参数等。(📝补充:
max_depth
设置树的深度参数,可用于限制每棵树的最大深度,控制决策树的复杂度,防止过拟合。) - 训练模型:使用设置好的参数
lgb_params
对模型进行训练,迭代 50000 次。使用验证集valid_matrix
进行验证,设置了每隔 500 次迭代输出日志信息,并设置了早停机制,如果连续 500 次迭代验证集上的性能没有提升,则停止训练。 - 验证集和测试集结果预测:使用训练好的模型
model
对验证集val_x
和测试集test_df
的特征列 cols 进行预测。 - 离线分数评估:使用均方误差(MSE)评估模型在验证集上的表现,并将评分打印输出。
- 返回预测结果:返回验证集和测试集的预测结果
val_pred
和test_pred
。 - 调用 time_model 函数:调用
time_model
函数,传入 LightGBM 模型对象 lgb、训练集 train、测试集 test 和训练特征列 train_cols,获得验证集和测试集的预测结果。 - 保存结果文件到本地:将测试集 test 的预测结果 lgb_test 存入 ‘target’ 列,并将包含 ‘id’、‘dt’ 和 ‘target’ 列的数据框保存为 CSV 文件
'submit.csv'
,不包含索引。
————未完待续————