讯飞锂离子电池温度预测_学习笔记

发布时间 2023-07-26 16:14:33作者: 琦家出品
  • 讯飞锂离子电池温度预测_学习笔记

环境配置

在我们进行程序运行之前,需要将程序运行过程中所需要的第三方库进行下载

  • numpy/pandas : 是十分常见的数据处理的第三方库,我们需要利用它们进行文件数据的读入与写出,以及对数据进行增删改查等各项操作
  • lightgbm : 这是我们本次比赛所使用的机器学习模型,该模型类属于集成学习,它的具体思想是通过将多个学习器进行组合,常可获得比单一学习器显著游学的泛化性能(摘选自《机器学习》周志华)
  • scikit-learn : 我们在使用过程中使用sklearn来作为它的名称进行包的导入,该包可以说是市面上最流行最易上手的机器学习第三方库了,它为各个模型都提供了简单易上手的API,同时它还内置了许多辅助我们进行训练的函数API,比如本次的数据集的切分,损失的计算都是通过该模型完成的
  • tqdm : 该第三方库,主要是帮助我们进行训练进程的可视化操作。
%pip install numpy pandas lightgbm scikit-learn tqdm

我们可以直接在需要执行的系统命令前添加"!"或者"%" 我们就可以实现在jupyter notebook中进行第三方库的下载

包的导入

# 导入所需要的库
import pandas as pd # 用于处理数据的工具
import lightgbm as lgb # 机器学习模型 LightGBM
from sklearn.metrics import mean_absolute_error # 评分 MAE 的计算函数
from sklearn.model_selection import train_test_split # 拆分训练集与验证集工具
from tqdm import tqdm # 显示循环的进度条工具
import matplotlib.pyplot as plt
import warnings

warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning)

数据读取

输入的读入部分,利用的是pandas库中对csv文件的读入命令,同时定义提交数据的数据结构(DataFrame)

# 数据准备
train_dataset = pd.read_csv("./data/data228216/train.csv") # 原始训练数据。
test_dataset = pd.read_csv("./data/data228216/test.csv") # 原始测试数据(用于提交)。

submit = pd.DataFrame() # 定义提交的最终数据。
submit["序号"] = test_dataset["序号"] # 对齐测试数据的序号。

MAE_scores = dict() # 定义评分项。

数据处理

train_columns = train_dataset[2:17*3]
from sklearn.preprocessing import StandardScaler, Normalizer

z_scaler= Normalizer()
z_data = z_scaler.fit_transform(train_dataset[train_columns])
train_dataset[train_columns] = z_data

x_scaler= Normalizer()
x_data = x_scaler.fit_transform(test_dataset[train_columns])
test_dataset[train_columns] = x_data

参数设置

在这一部分,我们定义了需要预测的类别数目以及,lightgbm所学的各项参数

pred_labels = list(train_dataset.columns[-34:]) # 需要预测的标签。
train_set, valid_set = train_test_split(train_dataset, test_size=0.2, random_state=1) # 拆分数据集。

lgb_params = {
        'boosting_type': 'gbdt',        # 使用的Boosting方法。
        'objective': 'regression',      # 使用的任务的概念。
        'metric': 'mae',                # 使用的任务的评价指标。
        'min_child_weight': 0.001,          # 训练样本最小个数
        'num_leaves': 2 ** 6 - 1,           # 训练样本的节点数
        'lambda_l2':8,                 # 训练样本的L2正则化系数,改项主要是为了防止过拟合的情况下,增大L1正则化系数。
        'feature_fraction': 0.8,        # 训练样本的特征面积比例
        'bagging_fraction': 0.8,        # 训练样本的基本抽样比例
        'bagging_freq': 2,              # 训练样本的基本抽样次数,与bagging_fraction一起计算
        'learning_rate': 0.05,           # 学习率 [0.05, 0.2] 我们可以在该区间进行合理的调节
        'seed': 2023,                   # 随机种子
        'nthread' : 16,                 # 线程数
        'verbose' : -1,                 # 静默模式,静默模式下,会在显示结果时
        'max_depth' : 6,
        'early_stopping_round' : 50     # 验证模型是否停止,调用该参数可以帮助我们在验证模型结束的阈值,默认值为20
    }

no_info = lgb.callback.log_evaluation(period=-1) # 禁用训练日志输出。

特征工程

  • 我们通过观察不同上部温度设定于下部温度设定以及实际的温度情况可以发现大部分情况下,实际变化变化情况与实践呈现出一定的规律,以及与温度流量等因素相关联,所以我们可以通过从两个方向进行特征
  1. 时间序列抽取特征,既包括时间列特征,也包括历史数据的平移
  2. 通过交叉各个指标,得到融合特征
def data_feature(data: pd.DataFrame, pred_labels: list=None) -> pd.DataFrame:
    
    data = data.copy() # 复制数据,避免后续影响原始数据。
    data = data.drop(columns=["序号"]) # 去掉”序号“特征。
    #########################
    '''
    时间数据特征抽取
    我们利用pandas讲数据中的时间特征进行提取,并利用讲时间进行划分,这样做是有道理的,因为我们通过观察数据的分布,发现数据具有一定的周期性
    '''
    
    data["时间"] = pd.to_datetime(data["时间"]) # 将”时间“特征的文本内容转换为 Pandas 可处理的格式。
    data["month"] = data["时间"].dt.month # 添加新特征“month”,代表”当前月份“。
    data["day"] = data["时间"].dt.day # 添加新特征“day”,代表”当前日期“。
    data["hour"] = data["时间"].dt.hour # 添加新特征“hour”,代表”当前小时“。
    data["minute"] = data["时间"].dt.minute # 添加新特征“minute”,代表”当前分钟“。
    data["weekofyear"] = data["时间"].dt.isocalendar().week.astype(int) # 添加新特征“weekofyear”,代表”当年第几周“,并转换成 int,否则 LightGBM 无法处理。
    data["dayofyear"] = data["时间"].dt.dayofyear # 添加新特征“dayofyear”,代表”当年第几日“。
    data["dayofweek"] = data["时间"].dt.dayofweek # 添加新特征“dayofweek”,代表”当周第几日“。
    data["is_weekend"] = data["时间"].dt.dayofweek // 6 # 添加新特征“is_weekend”,代表”是否是周末“,1 代表是周末,0 代表不是周末。

    # 如果不处理掉该特征,程序会报错
    data = data.drop(columns=["时间"]) # LightGBM 无法处理这个特征,它已体现在其他特征中,故丢弃。

    #########################
    '''
    我们为了从数据中提取到更加有用的信息,最新的baseline的改进版,主要通过
    1、对流量与上下部设定温度进行交叉融合,得到新的特征
    2、通过对历史数据进行平移和差分的操作,使得模型在学习当前数据的同时,可以获得上一个时刻的数据,习得历史信息

    '''
    # 交叉特征
    for i in range(1,18):
        data[f'流量{i}/上部温度设定{i}'] = data[f'流量{i}'] / data[f'上部温度设定{i}']   
        data[f'流量{i}/下部温度设定{i}'] = data[f'流量{i}'] / data[f'下部温度设定{i}']
        data[f'上部温度设定{i}/下部温度设定{i}'] = data[f'上部温度设定{i}'] / data[f'下部温度设定{i}']
        
    # 历史平移
        data[f'last1_流量{i}'] = data[f'流量{i}'].shift(1)
        data[f'last1_上部温度设定{i}'] = data[f'上部温度设定{i}'].shift(1)
        data[f'last1_下部温度设定{i}'] = data[f'下部温度设定{i}'].shift(1)

    # 差分特征
        data[f'last1_diff_流量{i}'] = data[f'流量{i}'].diff(1)
        data[f'last1_diff_上部温度设定{i}'] = data[f'上部温度设定{i}'].diff(1)
        data[f'last1_diff_下部温度设定{i}'] = data[f'下部温度设定{i}'].diff(1)
        
    # 窗口统计
    for i in range(1,18):
        data[f'win3_mean_流量{i}'] = (data[f'流量{i}'].shift(1) + data[f'流量{i}'].shift(2) + data[f'流量{i}'].shift(3)) / 3
        data[f'win3_mean_上部温度设定{i}'] = (data[f'上部温度设定{i}'].shift(1) + data[f'上部温度设定{i}'].shift(2) + data[f'上部温度设定{i}'].shift(3)) / 3
        data[f'win3_mean_下部温度设定{i}'] = (data[f'下部温度设定{i}'].shift(1) + data[f'下部温度设定{i}'].shift(2) + data[f'下部温度设定{i}'].shift(3)) / 3

    if pred_labels: # 如果提供了 pred_labels 参数,则执行该代码块。
        data = data.drop(columns=[*pred_labels]) # 去掉所有待预测的标签。
    
    return data # 返回最后处理的数据。

test_features = data_feature(test_dataset) # 处理测试集的时间特征,无需 pred_labels。

模型训练

  • 需要注意的是,我们在此处并没有使用sklearn中常见的fit方法,而是使用了train函数,这两种方法其实得到模型的效果是一样的

训练过程说明

  • 利用for循环来以此遍历需要训练的模型的目标值(目标值存放在pred_labels中,其中存放我们需要预测的34个标签)
  • 根据上述代码块得到的抽取特征函数来处理训练集,将得到的特征存放在train_features中。并且根据当前循环所要预测的标签,得到训练的ground truth(,同时由于我们使用的是train方法,所以我们需要进行一个数据格式的转化,将数据转化为lgb.Dataset格式)
  • 对验证集进行相同的操作
  • 处理好数据之后,我们开始定义模型,将模型的的参数传入,即lgb_params, 并且将训练数据,测试数据,模型迭代的次数以此传入, 模型开始训练
  • 模型训练好之后,开始对本轮训练到的模型进行评估,利用MAE来作为评估指标,并将其存放在MAE_scores列表中,作为我们后期进行训练效果检测的依据

# 从所有待预测特征中依次取出标签进行训练与预测。
for pred_label in tqdm(pred_labels):
    train_features = data_feature(train_set, pred_labels=pred_labels) # 处理训练集的时间特征。
    train_labels = train_set[pred_label] # 训练集的标签数据。
    train_data = lgb.Dataset(train_features, label=train_labels) # 将训练集转换为 LightGBM 可处理的类型。

    valid_features = data_feature(valid_set, pred_labels=pred_labels) # 处理验证集的时间特征。
    valid_labels = valid_set[pred_label] # 验证集的标签数据。
    valid_data = lgb.Dataset(valid_features, label=valid_labels) # 将验证集转换为 LightGBM 可处理的类型。

    # 训练模型,参数依次为:导入模型设定参数、导入训练集、设定模型迭代次数(200)、导入验证集、禁止输出日志
    model = lgb.train(lgb_params, train_data, 2000 , valid_sets=valid_data, callbacks=[no_info])

    valid_pred = model.predict(valid_features, num_iteration=model.best_iteration) # 选择效果最好的模型进行验证集预测。
    test_pred = model.predict(test_features, num_iteration=model.best_iteration) # 选择效果最好的模型进行测试集预测。
    MAE_score = mean_absolute_error(valid_pred, valid_labels) # 计算验证集预测数据与真实数据的 MAE。
    MAE_scores[pred_label] = MAE_score # 将对应标签的 MAE 值 存入评分项中。

    submit[pred_label] = test_pred # 将测试集预测数据存入最终提交数据中。

模型写出

在数据写出的时候,我们也有几点需要注意,如

# 运行完成后,在本项目的文件目录中找到submit_result.csv文件,下载提交即可。
submit.to_csv('submit.csv', index=False) # 保存最后的预测结果到 submit_result.csv。

此处to_csv中的index参数,此处是为了使得模型在存入过程中,保证与所需提交格式一致,

# 运行完成后,在本项目的文件目录中找到submit_result.csv文件,下载提交即可。
# submit.to_csv('28.csv', index=False) # 保存最后的预测结果到 submit_result.csv。
print(MAE_scores) # 查看各项的 MAE 值。
sum(MAE_scores.values())