本文作者:艾宏峰

  • 算法工程师
  • M6 Global赛道总排名4th
  • KDD Cup 2022风电功率预测飞桨赛道5th

“中国软件杯”大学生软件设计大赛——龙源风电赛道,5月31日预选赛截止,80%选手将升级区域赛,欢送大家放松报名

赛题背景

随着清洁能源的疾速倒退,风力发电曾经成为可再生能源的重要组成部分,然而风具备随机性特点,惯例天气预报无奈精确反映出风电场所在区域的实在风速,从而造成发电功率预测准确率低下,影响电力供需平衡。因而,进步风电功率预测的准确性,为电网调度提供迷信撑持,对我国能源产业有非常重要的价值。

此次赛题由百度飞桨和龙源电力设置,数据集由寰球最大风电经营企业龙源电力提供,采集自实在风力发电数据,要求选手基于百度飞桨 PaddlePaddle 设计一个能够通过深度学习技术实现对风力发电进行功率预测及治理的软件系统。

时序预测技术介绍

工夫序列预测技术是指基于历史数据和工夫变化规律,通过数学模型和算法对将来发展趋势进行预测的一种技术。工夫序列预测技术广泛应用于经济、金融、交通、气象等畛域,以帮忙人们做出更加精确的决策。
时序预测从不同角度看有不同分类:

  • 实现原理的角度,能够分为:传统统计学,机器学习(又分非深度学习和深度学习);
  • 预测步长辨别,能够分为单步预测和多步预测;
  • 输出变量辨别,能够分为自回归预测和应用协变量进行预测;
  • 输入后果辨别,能够分为点预测和概率预测;
  • 指标个数辨别,能够分为一元、多元、多重工夫序列预测

这些分类是不同角度下的分类,同一种算法往往只能是分类中的一种,例如传统的统计学算法只适宜做自回归预测而不适宜协变量预测。

工夫序列预测技术的钻研历史能够追溯到20世纪初期。最早的工夫序列预测办法是基于工夫平均法和线性趋势法,起初倒退出了指数平滑法、ARIMA 模型、神经网络模型等预测办法。随着机器学习和深度学习的倒退,工夫序列预测技术也失去了一直的拓展和翻新,比方 Transformer 与时序的联合。

风电功率预测钻研意义与价值

工夫序列预测技术有着宽泛的利用场景。例如,

  • 经济畛域,工夫序列预测技术能够用于股票市场预测、经济增长预测、通货膨胀预测等。
  • 交通畛域,工夫序列预测技术能够用于公交车到站工夫预测、交通拥堵预测等。
  • 气象畛域,工夫序列预测技术能够用于气象灾祸预警、天气变动预测等。

而在风电功率预测上,其钻研意义和价值更不容忽视,它们包含:

  • 进步风电发电效率:通过精准预测风电功率,能够合理安排风电机组的运行,进步风电发电效率,缩小能源节约。
  • 保障电网稳固运行:风电功率预测能够帮忙电网运营商及时调整电网负荷,防止电网过载或供电有余等问题,保障电网稳固运行。
  • 促成可再生能源倒退:风电功率预测能够进步风电发电的可靠性和经济性,促成可再生能源倒退,升高对传统能源的依赖。
  • 升高能源老本:通过精准预测风电功率,能够无效防止风电机组的过剩和有余,升高能源老本,进步能源利用率。
  • 推动智能电网建设:风电功率预测是智能电网建设的重要组成部分,能够实现对风电发电的精准监控和治理,推动智能电网建设。

赛制赛段

  • 预选赛

5月31日截止,算法赛,80%选手升级区域赛;

  • 区域赛

6-7月,算法赛60%+软件赛40%,颁发省级奖项;

  • 总决赛

8月,软件赛,颁发国赛奖项。

赛题数据

本赛题数据集由寰球最大风电经营企业龙源电力提供,采集自实在风力发电数据。预选赛训练数据和区域赛训练数据别离为不同10个风电场近一年的运行数据共30万余条,每15分钟采集一次,包含风速、风向、温度、湿度、气压和实在功率等,具体的数据字段中英文对应如下:

  • WINDSPEED 预测风速
  • WINDDIRECTION 风向
  • TEMPERATURE 温度
  • HUMIDITY 湿度
  • PRESSURE 气压
  • PREPOWER 预测功率(系统生成)
  • ROUND(A.WS,1)理论风速
  • ROUND(A.POWER,0) 理论功率(计量口径一)
  • YD15 理论功率(预测指标,计量口径二)

注:“预测风速”字段,指的是由权威的气象机构,像是中央气象台、欧洲国家气象中心等公布的商业气象数据源。从工夫线来说,理论功率预测须要提前 36 个小时、72 个小时、240 的小时等取得数值天气预报,从而进行功率的预测。

数据注意事项

  • 原始数据集存在不同格局的风机数据,须要额定的数据拼接解决工作;
  • 每个风机的最初一天的 ROUND(A.POWER,0)和 YD15 两个字段数据根本为空,这是出题方心愿咱们预测填空的数据;
  • csv 内的工夫戳未必有序,须要自行排序;
  • 数据存在缺失和离群值;
  • 数据存在反复样本(即同一时间戳有多条样本)。以风机04为例,反复样本数量: 34764, 占比: 38.68340232340766%。

以风机4为例的EDA后果展现

官网对数据的一些回复

  • 因为测量设施和网络传输问题,YD15 可能呈现数据异样(包含用于评测的输出数据)。
  • 实际上因为一些脏数据的存在,YD15 有时候会缺失或异样,这个时候 ROUND(A.POWER,0)如果有正常值的话,能够被视为YD15的代替。
  • YD15 的异样值解决规定是,当 YD15 为空时,依照逻辑顺次用 ROUND(A.POWER,0)、PREPOWER 进行替换。
  • 如何定义 YD15 存在异样?在本赛题中,YD15 异样包含两种状况:(1) 空值,(2) 在一段时间内、其它字段失常变动时,YD15 继续齐全不变。除以上两种状况之外,YD15 的数值变动都可认为是失常景象,如为 0 或负值。

集体补充

  • 当理论风速为 0 时,存在功率>0 有些异样,而后有些风速过大>12.5,存在功率为 0 的异样。
  • 指标列 ROUND(A.POWER,0)和 YD15,与风速 WINDSPEED、PREPOWER、PRESSURE 和 ROUND(A.WS,1)强相干;
  • 指标列 ROUND(A.POWER,0)和 YD15 之间就有很强的相关性;

评测阐明

算法局部

本次较量要求选手将算法模型提交至人工智能学习与实训社区 AI Studio 进行主动评测,预选赛凋谢10个风场数据,区域赛凋谢新的10个风场数据,预选赛和区域赛算法问题各占30%。

要求选手基于飞桨 PaddlePaddle 依据官网提供的数据集,设计一种利用当日05:00之前的数据,预测次日00:00至23:45理论功率的办法。准确率按日统计,依据10个风电场均匀准确率进行排名;准确率雷同的情景下,依据每日单点的均匀最大偏差绝对值排名。

def calc_acc(y_true, y_pred):    rmse = np.sqrt(np.mean((y_true - y_pred)**2))    return 1 - rmse/201000

软件局部

本次较量要求选手基于 Web 技术实现:

  • 数据可视化

将预测后果以图表等模式展现进去,便于用户进行察看和剖析;

  • 实时更新与滚动预测

可能基于提供的数据实时模仿实在功率、预测功率及其之间的差别,通过调节过来不同长度的时间段,以更新将来预测后果,且预测的时间段可调节;

  • 响应式设计

反对多种终端,包含 PC 端、挪动端等,以适应不同设施的屏幕尺寸和分辨率;

  • 其余翻新附加性能

提交阐明

测评数据的格局如下:

| --- ./infile(内置于测评零碎中,参赛选手不可见)     | --- 0001in.csv     | --- 0002in.csv     | ---  ...

参赛选手须要提交一个命名为 submission.zip 的压缩包,并且压缩包内应蕴含:

| --- ./model # 寄存模型的目录,并且大小不超过200M(必选)| --- ./env # 寄存依赖库的目录(可选)| --- predict.py # 评估代码(必选)| --- pip-requirements.txt # 寄存依赖库的文件(可选)| --- …

参赛选手在代码提交页面提交压缩包后,测评零碎会解压选手提交的压缩包,并执行如下命令:

python predict.py

测评文件 predict.py 应该实现的性能是:读取 infile 文件夹下的测评数据,并将预测后果保留到 pred 文件夹中。

| --- ./pred(须要选手生成)     | --- 0001out.csv     | --- 0002out.csv     | --- …

基线模型流程

基于飞桨 PaddlePaddle 的多任务 LSTM 时序预测基线模型 pipeline 如下:

以工夫序列举例,因为个别测试集也会是将来数据,所以咱们也要保障训练集是历史数据,而划分出的验证集是将来数据,不然会产生“工夫穿梭”的数据泄露问题,导致模型过拟合(例如用将来预测历史数据),这个时候就有两种验证划分形式可参考应用:

  • TimeSeriesSplit:Sklearn 提供的 TimeSeriesSplit;
  • 固定窗口滑动划分法:固定工夫窗口,一直在数据集上滑动,取得训练集和验证集。(集体举荐这种)

在时序工作中,有2类数据源,如下图所示:

(1)动态变量(Static Covariates):不会随工夫变动的变量,例如风机id、风机地位;

(2)时变变量(Time-dependent Inputs):随工夫变动的变量;

  • 过来观测的时变变量(Past-observed Inputs):过来可知,但将来不可知,例如历史风速、温度、气压等
  • 先验已知将来的时变变量(Apriori-known Future Inputs):过来和将来都可知,例如天气预报将来风速、温度、气压等;

数据加载器的代码如下:

# unix工夫戳转换def to_unix_time(dt):    # timestamp to unix    epoch = datetime.datetime.utcfromtimestamp(0)    return int((dt - epoch).total_seconds())def from_unix_time(unix_time):    # unix to timestamp    return datetime.datetime.utcfromtimestamp(unix_time)class TSDataset(paddle.io.Dataset):    """时序DataSet    划分数据集、适配dataloader所需的dataset格局    ref: https://github.com/thuml/Autoformer/blob/main/data_provider/data_loader.py    """    def __init__(self, data,                  ts_col='DATATIME',                 use_cols =['WINDSPEED', 'PREPOWER', 'WINDDIRECTION', 'TEMPERATURE', 'HUMIDITY',                  'PRESSURE', 'ROUND(A.WS,1)', 'ROUND(A.POWER,0)', 'YD15',                 'month', 'day', 'weekday', 'hour', 'minute'],                 labels = ['ROUND(A.POWER,0)', 'YD15'],                  input_len = 24*4*5, pred_len = 24*4, stride=19*4, data_type='train',                 train_ratio = 0.7, val_ratio = 0.15):        super(TSDataset, self).__init__()        self.ts_col = ts_col        # 工夫戳列        self.use_cols = use_cols    # 训练时应用的特色列        self.labels = labels        # 待预测的标签列        self.input_len = input_len  # 模型输出数据的样本点长度,15分钟距离,一个小时14个点,近5天的数据就是24*4*5        self.pred_len = pred_len    # 预测长度,预测次日00:00至23:45理论功率,即1天:24*4        self.data_type = data_type  # 须要加载的数据类型        self.scale = True           # 是否须要标准化        self.train_ratio = train_ratio # 训练集划分比例        self.val_ratio = val_ratio  # 验证集划分比例        # 因为赛题要求利用当日05:00之前的数据,预测次日00:00至23:45理论功率        # 所以x和label要距离19*4个点        self.stride = stride        assert data_type in ['train', 'val', 'test']    # 确保data_type输出符合要求        type_map = {'train': 0, 'val': 1, 'test': 2}        self.set_type = type_map[self.data_type]        self.transform(data)    def transform(self, df):        # 获取unix工夫戳、输出特色和预测标签        time_stamps, x_values, y_values = df[self.ts_col].apply(lambda x:to_unix_time(x)).values, df[self.use_cols].values, df[self.labels].values        # 划分数据集        # 这里能够按需设置划分比例        num_train = int(len(df) * self.train_ratio)        num_vali = int(len(df) * self.val_ratio)        num_test = len(df) - num_train - num_vali        border1s = [0, num_train-self.input_len-self.stride, len(df)-num_test-self.input_len-self.stride]        border2s = [num_train, num_train + num_vali, len(df)]        # 获取data_type下的左右数据截取边界        border1 = border1s[self.set_type]        border2 = border2s[self.set_type]            # 标准化        self.scaler = StandardScaler()        if self.scale:            # 应用训练集失去scaler对象            train_data = x_values[border1s[0]:border2s[0]]            self.scaler.fit(train_data)            data = self.scaler.transform(x_values)            # 保留scaler            pickle.dump(self.scaler, open('/home/aistudio/submission/model/scaler.pkl', 'wb'))        else:            data = x_values        # array to paddle tensor        self.time_stamps = paddle.to_tensor(time_stamps[border1:border2], dtype='int64')        self.data_x = paddle.to_tensor(data[border1:border2], dtype='float32')        self.data_y = paddle.to_tensor(y_values[border1:border2], dtype='float32')      def __getitem__(self, index):        """        实现__getitem__办法,定义指定index时如何获取数据,并返回单条数据(训练数据)        """        # 因为赛题要求利用当日05:00之前的数据,预测次日00:00至23:45理论功率        # 所以x和label要距离19*4个点        s_begin = index        s_end = s_begin + self.input_len        r_begin = s_end + self.stride        r_end = r_begin + self.pred_len        # TODO 能够减少对将来可见数据的获取        seq_x = self.data_x[s_begin:s_end]        seq_y = self.data_y[r_begin:r_end]        ts_x = self.time_stamps[s_begin:s_end]        ts_y = self.time_stamps[r_begin:r_end]        return seq_x, seq_y, ts_x, ts_y    def __len__(self):        """        实现__len__办法,返回数据集总数目        """        return len(self.data_x) - self.input_len - self.stride - self.pred_len  + 1class TSPredDataset(paddle.io.Dataset):    """时序Pred DataSet    划分数据集、适配dataloader所需的dataset格局    ref: https://github.com/thuml/Autoformer/blob/main/data_provider/data_loader.py    """    def __init__(self, data,                  ts_col='DATATIME',                 use_cols =['WINDSPEED', 'PREPOWER', 'WINDDIRECTION', 'TEMPERATURE', 'HUMIDITY',                  'PRESSURE', 'ROUND(A.WS,1)', 'ROUND(A.POWER,0)', 'YD15',                 'month', 'day', 'weekday', 'hour', 'minute'],                 labels = ['ROUND(A.POWER,0)', 'YD15'],                   input_len = 24*4*5, pred_len = 24*4, stride=19*4):        super(TSPredDataset, self).__init__()        self.ts_col = ts_col        # 工夫戳列        self.use_cols = use_cols    # 训练时应用的特色列        self.labels = labels        # 待预测的标签列        self.input_len = input_len  # 模型输出数据的样本点长度,15分钟距离,一个小时14个点,近5天的数据就是24*4*5        self.pred_len = pred_len    # 预测长度,预测次日00:00至23:45理论功率,即1天:24*4        # 因为赛题要求利用当日05:00之前的数据,预测次日00:00至23:45理论功率        # 所以x和label要距离19*4个点        self.stride = stride                self.scale = True           # 是否须要标准化        self.transform(data)    def transform(self, df):        # 获取unix工夫戳、输出特色和预测标签        time_stamps, x_values, y_values = df[self.ts_col].apply(lambda x:to_unix_time(x)).values, df[self.use_cols].values, df[self.labels].values        # 截取边界        border1 = len(df) - self.input_len - self.stride - self.pred_len        border2 = len(df)           # 标准化        self.scaler = StandardScaler()        if self.scale:            # 读取预训练好的scaler            self.scaler = pickle.load(open('/home/aistudio/submission/model/scaler.pkl', 'rb'))            data = self.scaler.transform(x_values)        else:            data = x_values        # array to paddle tensor        self.time_stamps = paddle.to_tensor(time_stamps[border1:border2], dtype='int64')        self.data_x = paddle.to_tensor(data[border1:border2], dtype='float32')        self.data_y = paddle.to_tensor(y_values[border1:border2], dtype='float32')      def __getitem__(self, index):        """        实现__getitem__办法,定义指定index时如何获取数据,并返回单条数据(训练数据)        """        # 因为赛题要求利用当日05:00之前的数据,预测次日00:00至23:45理论功率        # 所以x和label要距离19*4个点        s_begin = index        s_end = s_begin + self.input_len        r_begin = s_end + self.stride        r_end = r_begin + self.pred_len        # TODO 能够减少对将来可见数据的获取        seq_x = self.data_x[s_begin:s_end]        seq_y = self.data_y[r_begin:r_end]        ts_x = self.time_stamps[s_begin:s_end]        ts_y = self.time_stamps[r_begin:r_end]        return seq_x, seq_y, ts_x, ts_y    def __len__(self):        """        实现__len__办法,返回数据集总数目        """        return len(self.data_x) - self.input_len - self.stride - self.pred_len  + 1

模型代码如下:

class MultiTaskLSTM(paddle.nn.Layer):    """多任务LSTM时序预测模型    LSTM为共享层网络,对两个预测指标别离有两个分支独立线性层网络    TODO 其实该模型就是个Encoder,如果后续要引入天气预测将来的变量,补充个Decoder,    而后Encoder负责历史变量的编码,Decoder负责将 编码后的历史编码后果 和 它编码将来变量的编码后果 合并后,做解码预测即可    """    def __init__(self,feat_num=14, hidden_size=64, num_layers=2, dropout_rate=0.7, input_len=120*4, pred_len=24*4):        super(MultiTaskLSTM, self).__init__()        # LSTM为共享层网络        self.lstm_layer = paddle.nn.LSTM(feat_num, hidden_size,                                     num_layers=num_layers,                                     direction='forward',                                     dropout=dropout_rate)        # 为'ROUND(A.POWER,0)'构建分支网络        self.linear1_1 = paddle.nn.Linear(in_features=input_len*hidden_size, out_features=hidden_size*2)        self.linear1_2 = paddle.nn.Linear(in_features=hidden_size*2, out_features=hidden_size)        self.linear1_3 = paddle.nn.Linear(in_features=hidden_size, out_features=pred_len)        # 为'YD15'构建分支网络         self.linear2_1 = paddle.nn.Linear(in_features=input_len*hidden_size, out_features=hidden_size*2)        self.linear2_2 = paddle.nn.Linear(in_features=hidden_size*2, out_features=hidden_size)        self.linear2_3 = paddle.nn.Linear(in_features=hidden_size, out_features=pred_len)        self.dropout = paddle.nn.Dropout(dropout_rate)    def forward(self, x):        # x形态大小为[batch_size, input_len, feature_size]        # output形态大小为[batch_size, input_len, hidden_size]        # hidden形态大小为[num_layers, batch_size, hidden_size]        output, (hidden, cell) = self.lstm_layer(x)        # output: [batch_size, input_len, hidden_size] -> [batch_size, input_len*hidden_size]        output = paddle.reshape(output, [len(output), -1])        output1 = self.linear1_1(output)        output1 = self.dropout(output1)        output1 = self.linear1_2(output1)        output1 = self.dropout(output1)        output1 = self.linear1_3(output1)        output2 = self.linear2_1(output)        output2 = self.dropout(output2)        output2 = self.linear2_2(output2)        output2 = self.dropout(output2)        output2 = self.linear2_3(output2)        # outputs: ([batch_size, pre_len, 1], [batch_size, pre_len, 1])        return [output1, output2]

模型训练、验证和测试代码:

def train(df, turbine_id):    # 设置数据集    train_dataset = TSDataset(df, input_len = input_len, pred_len = pred_len, data_type='train')    val_dataset = TSDataset(df, input_len = input_len, pred_len = pred_len, data_type='val')    test_dataset = TSDataset(df, input_len = input_len, pred_len = pred_len, data_type='test')    print(f'LEN | train_dataset:{len(train_dataset)}, val_dataset:{len(val_dataset)}, test_dataset:{len(test_dataset)}')    # 设置数据读取器    train_loader = paddle.io.DataLoader(train_dataset, shuffle=True, batch_size=batch_size, drop_last=True)    val_loader = paddle.io.DataLoader(val_dataset, shuffle=False, batch_size=batch_size, drop_last=True)    test_loader = paddle.io.DataLoader(test_dataset, shuffle=False, batch_size=1, drop_last=False)    # 设置模型    model = MultiTaskLSTM()    # 设置优化器    scheduler = paddle.optimizer.lr.ReduceOnPlateau(learning_rate=learning_rate, factor=0.5, patience=3, verbose=True)    opt = paddle.optimizer.Adam(learning_rate=scheduler, parameters=model.parameters())    # 设置损失    mse_loss = MultiTaskMSELoss()    train_loss = []    valid_loss = []    train_epochs_loss = []    valid_epochs_loss = []    early_stopping = EarlyStopping(patience=patience, verbose=True, ckp_save_path=f'/home/aistudio/submission/model/model_checkpoint_windid_{turbine_id}.pdparams')    for epoch in tqdm(range(epoch_num)):        # =====================train============================        train_epoch_loss, train_epoch_mse1,  train_epoch_mse2 = [], [], []        model.train() # 开启训练        for batch_id, data in enumerate(train_loader()):                         x = data[0]            y = data[1]            # 预测            outputs = model(x)            # 计算损失            mse1, mse2, avg_loss = mse_loss(outputs, y)            # 反向流传            avg_loss.backward()            # 梯度降落            opt.step()            # 清空梯度            opt.clear_grad()            train_epoch_loss.append(avg_loss.numpy()[0])            train_loss.append(avg_loss.item())            train_epoch_mse1.append(mse1.item())            train_epoch_mse2.append(mse2.item())        train_epochs_loss.append(np.average(train_epoch_loss))        print("epoch={}/{} of train | loss={}, MSE of ROUND(A.POWER,0):{}, MSE of YD15:{} ".format(epoch, epoch_num,        np.average(train_epoch_loss), np.average(train_epoch_mse1), np.average(train_epoch_mse2)))        # =====================valid============================        model.eval() # 开启评估/预测        valid_epoch_loss, valid_epochs_mse1,  valid_epochs_mse2 = [], [], []        for batch_id, data in enumerate(val_loader()):             x = data[0]            y = data[1]            outputs = model(x)            mse1, mse2, avg_loss = mse_loss(outputs, y)            valid_epoch_loss.append(avg_loss.numpy()[0])            valid_loss.append(avg_loss.numpy()[0])            valid_epochs_mse1.append(mse1.item())            valid_epochs_mse2.append(mse2.item())        valid_epochs_loss.append(np.average(valid_epoch_loss))        print('Valid: MSE of ROUND(A.POWER,0):{}, MSE of YD15:{}'.format(np.average(train_epoch_mse1), np.average(train_epoch_mse2)))        # ==================early stopping======================        early_stopping(valid_epochs_loss[-1], model=model)        if early_stopping.early_stop:            print(f"Early stopping at Epoch {epoch-patience}")            break    print('Train & Valid: ')    plt.figure(figsize=(12,3))    plt.subplot(121)    plt.plot(train_loss[:],label="train")    plt.title("train_loss")    plt.xlabel('iteration')    plt.subplot(122)    plt.plot(train_epochs_loss[1:],'-o',label="train")    plt.plot(valid_epochs_loss[1:],'-o',label="valid")    plt.title("epochs_loss")    plt.xlabel('epoch')    plt.legend()    plt.tight_layout()    plt.show()    # =====================test============================    # 加载最优epoch节点下的模型    model = MultiTaskLSTM()    model.set_state_dict(paddle.load(f'/home/aistudio/submission/model/model_checkpoint_windid_{turbine_id}.pdparams'))    model.eval() # 开启评估/预测    test_loss, test_epoch_mse1, test_epoch_mse2 = [], [], []    test_accs1, test_accs2 = [], []     for batch_id, data in tqdm(enumerate(test_loader())):         x = data[0]        y = data[1]        ts_y = [from_unix_time(x) for x in data[3].numpy().squeeze(0)]        outputs = model(x)        mse1, mse2, avg_loss = mse_loss(outputs, y)        acc1 = calc_acc(y.numpy().squeeze(0)[:,0], outputs[0].numpy().squeeze(0))        acc2 = calc_acc(y.numpy().squeeze(0)[:,1], outputs[1].numpy().squeeze(0))        test_loss.append(avg_loss.numpy()[0])        test_epoch_mse1.append(mse1.numpy()[0])        test_epoch_mse2.append(mse2.numpy()[0])        test_accs1.append(acc1)        test_accs2.append(acc2)    print('Test: ')    print('MSE of ROUND(A.POWER,0):{}, MSE of YD15:{}'.format(np.average(test_epoch_mse1), np.average(test_epoch_mse2)))    print('Mean MSE:', np.mean(test_loss))    print('ACC of ROUND(A.POWER,0):{}, ACC of YD15:{}'.format(np.average(test_accs1), np.average(test_accs2)))

更多代码,详见:

  • 龙源风电赛Baseline - 多任务LSTM深度网络模型 (Paddle)

https://aistudio.baidu.com/aistudio/projectdetail/5911966?con...

提分技巧

开掘风机间关联信息

尽管赛方没给出不同风机的地理位置,且不同风机的数据分布时间段不齐全不统一,但能够从风场维度,联结多个风机发展数据分析,看是否能挖掘出风机之间的关联信息,以缩小单风机的数据噪声、帮忙填补缺失或解决异样等。

还有例如求功率/温度相关性,Kmeans 聚类获取风机 cluster,把近邻风机的特色们求均值退出特色,或者分 cluster 建模预测。

<p align=right>ST-Tree module:ST-Tree:Spatio-Partitioned Time-Phased Tree Model。它算每个风机之间的皮尔逊系数,再用 K-means 聚类,而后针对聚类的风机训练 LightGBM。 </p>
<p align=right>--KDD 第1名计划(海康)</p>

  • 在图网络表征上,选手是把与风机有 Top-K 高相关性的风机们,确认连贯边,构建图关系。
  • 用 k-shape 算法将风机聚类成39类,每类风机用一个 LightGBM 预测。
  • 从属风机可被看做是一类风机们,它们之间有最类似的发电法则。通过均匀从属风机的功率,当做特色退出模型,能缓解数据噪声。

<p align=right>--KDD 第4名计划(清华、浙江工商、多伦多大学)</p>

分段建模预测

风机的发电功率预测难度是挺大的,模型个别偏好均值预测,所以咱们要争取把可预测性强的短期预测局部预测好,再思考如何晋升模型的中长期预测能力。其实短期预测能够思考递归预测,因为它强调时序前后依赖,对短期信息依赖水平会更高些,有余在于中后期会误差累积。所以在中长期则能够思考多步生成式预测。

论文《DeepSpatio-TemporalWind Power Forecasting》反馈到:

  • 随着工夫滞后项的减少,自相关系数迅速衰减,阐明短期预测可能性更大,中长期就比拟艰难了。
  • 随机抽取几个不同的风速时序,结果显示风速时序没有展现出极其的长期依赖。而且 GRU 用更少的参数反而防止了过拟合问题。

下图展现了头几个小时下的 MAE,也反映出雷同的论断。所以风电功率预测时大多用户抉择了分段建模预测。

其余

  • 异样值解决,尤其是标签改正: 例如风速过大但功率为0的异样,在特定风速下的离群功率等;
  • 标签交融: 交融两个标签,以 YD15 为主,A.Power 为辅;
  • 利用天气预报的数据: 加 decoder 局部退出即可; 
  • 开掘更多特色: 差分序列、同时刻风场/邻近风机的特色均值/标准差等;
  • 尝试树模型: XGB、LGB 等;
  • 模型参数调优: optuna;
  • 模型交融: 人工加权或配合寻参算法。

官网交换QQ群

QQ 搜寻479266219退出官网交换 QQ 群~