Kaggle冠军解读:风电场短期风况预测任务方案

2023-12-27 08:10

本文主要是介绍Kaggle冠军解读:风电场短期风况预测任务方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

9612b09de9cab99ccc63ab382b58fd35.png

赛题背景

近年来,随着陆上风电机组装机厂址的扩展,在天气突变较多的地区安装的风力发电机组受到气象变化的影响愈发显著。在风况突变时,由于控制系统的滞后性,容易导致机组出现载荷过大,甚至是倒机的情况,造成重大经济损失。同时,现有超短期风功率预测的准确性较差,导致风功率预测系统对电网调度的参考价值不大,并且会导致业主产生大量的发电量计划考核。由于常见的激光雷达等风速测量产品单价高昂、受天气影响较大,难以实现批量化的应用部署,且在大时间空间尺度下仍难以具有可靠的前瞻性。因此,可靠的超短期风况预测迫在眉睫。

超短期风况预测是一个世界性难题,如果能通过大数据、人工智能技术预测出每台机组在未来短时间内的风速和风向数据,可以提升风电机组的控制前瞻性、提高风电机组的载荷安全性;同时,现有超短期风功率预测能力的提升,将带来显著的安全价值和经济效益。

本次比赛由深圳保安区人民政府与中国信息通信研究院联合主办,提供了来自工业生产中的真实数据与场景,希望结合工业与AI大数据,解决实际生产任务中面临的挑战。


数据分析


训练集说明

两个风场各两年的训练数据:

1. 每个风场25台风电机组,提供各台机组的机舱风速、风向、温度、功率和对应的小时级气象数据;

2. 风场1的风机编号为:x26-x50,训练集数据范围为2018、2019年;

3. 风场2的风机编号为:x25-x49,训练集数据范围为2017、2018年;

4. 各机组的数据文件按 /训练集/[风场]/[机组]/[日期].csv的方式存储;

5. 气象数据存储在 /训练集/[风场] 文件夹下。



测试集说明

1. 测试集分为两个文件夹:测试集初赛、测试集决赛,初赛和决赛的文件夹组织形式一致;

2. 初赛和决赛文件夹各包括80个时段的数据,每个时段1小时数据(30S分辨率,时间以秒数表达),春夏秋冬各20个时段,初赛编号1-20,决赛编号21-40;即初赛的时段编号为春_01-冬_20共80个;决赛的时段编号为春_21-冬_40共80个;

3. 各机组的数据文件按照 /测试集_**/[风场]/[机组]/[时段].csv 的方式存储;

4. 气象数据存储在 /测试集_**/[风场]/ 文件夹下,共80个时段风场所在地的风速风向数据,每个时段提供过去12小时和未来1小时的风速和风向数据。时段编码同上,时间编码为-11~2,其中0~1这个小时正好对应的是机舱的1小时数据。

dcd350a01aa835d156b5c0fd9b716d63.png

上图为测试集合数据划分方式,-11~1时间段之内积累的数据作为模型的输入,用于预测未来10分钟内(1~2之间)的风速和风向。


缺失值

数据中的缺失值主要来自两个方面,一种是当天的数据记录存在缺失,另一种是某些时间段的数据存在缺失。多种缺失情况导致在填充数据时存在遗漏问题,仅使用一种方式填充缺失值会导致缺失值的填充出现缺漏,因此比赛中我们同时使用了 forward fillbackward fill均值填充以保证填充覆盖率。这样处理可能会引入噪音,但是神经网络对于噪音有一定的容忍度,因而最终的训练效果影响并不大。考虑到训练数据、未来的测试数据中都可能存在缺失数据,而且它们的记录方式是相同的,因此我们没有去掉存在缺失值的数据,同时对它们使用了相同的填充方式,避免因为预处理不同导致数据分布不一致问题的出现。


模型思路介绍


模型结构

比赛中,我们采用了Encoder-Decoder 形式的模型,通过序列模型挖掘输入序列中的信息,再通过Decoder进行预测。这里的Encoder、Decoder有多种选择,例如常见的序列模型LSTM,或者近几年兴起的Transformer。比赛中我们在Decoder侧堆叠了多层LSTM,Encoder侧只使用了一层LSTM。模型中没有加入dropout进行正则化,这是考虑到数据中本身就存在大量的高频噪音,再加入dropout会导致模型收敛缓慢,影响模型的训练效率。我们使用飞桨框架搭建模型结构,后来发现飞桨官方的自然语言处理模型库PaddleNLP(https://github.com/PaddlePaddle/PaddleNLP)提供了方便的数据处理API、丰富的网络结构和预训练模型以及分类、生成等各种NLP应用示例,很适合打比赛,后续会考虑用起来。

ef1df032cefcdd2d731323ef3832aa52.png

使用飞桨框架构建的最终模型结构的代码如下:

class network(nn.Layer):def __init__(self, name_scope='baseline'):super(network, self).__init__(name_scope)name_scope = self.full_name()self.lstm1 = paddle.nn.LSTM(128, 128, direction =  'bidirectional', dropout=0.0)self.lstm2 = paddle.nn.LSTM(25, 128, direction =  'bidirectional', dropout=0.0)self.embedding_layer1= paddle.nn.Embedding(100, 4)self.embedding_layer2 = paddle.nn.Embedding(100, 16)self.mlp1 = paddle.nn.Linear(29, 128)self.mlp_bn1 = paddle.nn.BatchNorm(120)self.bn2 = paddle.nn.BatchNorm(14)self.mlp2 = paddle.nn.Linear(1536, 256)self.mlp_bn2 = paddle.nn.BatchNorm(256)self.lstm_out1 = paddle.nn.LSTM(256, 256, direction =  'bidirectional', dropout=0.0)self.lstm_out2 = paddle.nn.LSTM(512, 128, direction =  'bidirectional', dropout=0.0)self.lstm_out3 = paddle.nn.LSTM(256, 64, direction =  'bidirectional', dropout=0.0)self.lstm_out4 = paddle.nn.LSTM(128, 64, direction =  'bidirectional', dropout=0.0)self.output = paddle.nn.Linear(128, 2, )self.sigmoid = paddle.nn.Sigmoid()# 网络的前向计算函数def forward(self, input1, input2):embedded1 = self.embedding_layer1(paddle.cast(input1[:,:,0], dtype='int64'))embedded2 = self.embedding_layer2(paddle.cast(input1[:,:,1]+input1[:,:,0] # * 30, dtype='int64'))x1 = paddle.concat([embedded1, embedded2, input1[:,:,2:], input1[:,:,-2:-1] * paddle.sin(np.pi * 2 *input1[:,:,-1:]), input1[:,:,-2:-1] * paddle.cos(np.pi * 2 *input1[:,:,-1:]), paddle.sin(np.pi * 2 *input1[:,:,-1:]),paddle.cos(np.pi * 2 *input1[:,:,-1:]),], axis=-1)     # 4+16+5+2+2 = 29x1 = self.mlp1(x1)x1 = self.mlp_bn1(x1)x1 = paddle.nn.ReLU()(x1)x2 = paddle.concat([embedded1[:,:14], embedded2[:,:14], input2[:,:,:-1], input2[:,:,-2:-1] * paddle.sin(np.pi * 2 * input2[:,:,-1:]/360.), input2[:,:,-2:-1] * paddle.cos(np.pi * 2 * input2[:,:,-1:]/360.), paddle.sin(np.pi * 2 * input2[:,:,-1:]/360.),paddle.cos(np.pi * 2 * input2[:,:,-1:]/360.),], axis=-1) # 4+16+1+2+2 = 25x2 = self.bn2(x2)x1_lstm_out, (hidden, _) = self.lstm1(x1) x1 = paddle.concat([hidden[-2, :, :], hidden[-1, :, :],paddle.max(x1_lstm_out, axis=1),paddle.mean(x1_lstm_out, axis=1)], axis=-1)x2_lstm_out, (hidden, _) = self.lstm2(x2) x2 = paddle.concat([hidden[-2, :, :], hidden[-1, :, :],paddle.max(x2_lstm_out, axis=1),paddle.mean(x2_lstm_out, axis=1)], axis=-1)x = paddle.concat([x1, x2], axis=-1)x = self.mlp2(x)x = self.mlp_bn2(x)x = paddle.nn.ReLU()(x)# decoderx = paddle.stack([x]*20, axis=1)x = self.lstm_out1(x)[0]x = self.lstm_out2(x)[0]x = self.lstm_out3(x)[0]x = self.lstm_out4(x)[0]x = self.output(x)output = self.sigmoid(x)*2-1output = paddle.cast(output, dtype='float32')return output

飞桨框架在训练模型时有多种方式,可以像其他深度学习框架一样,通过梯度回传进行训练,也可以利用高度封装后的API进行训练。在使用高层API训练时,我们需要准备好数据的generator和模型结构。

飞桨框架中generator的封装方式如下,使用效率很高:

class TrainDataset(Dataset):def __init__(self, x_train_array, x_train_array2, y_train_array=None, mode='train'):# 样本数量self.training_data = x_train_array.astype('float32')self.training_data2 = x_train_array2.astype('float32')self.mode = modeif self.mode=='train':self.training_label = y_train_array.astype('float32')self.num_samples = self.training_data.shape[0] def __getitem__(self, idx):data = self.training_data[idx]data2 = self.training_data2[idx]if self.mode=='train':label = self.training_label[idx]return [data, data2],  labelelse:return [data, data2]def __len__(self):# 返回样本总数量return self.num_samples

准备好generator后,便可以直接使用fit接口进行训练:

model = paddle.Model(network(), inputs=inputs)
model.prepare(optimizer=paddle.optimizer.Adam(learning_rate=0.002, parameters=model.parameters()),loss=paddle.nn.L1Loss(),)
model.fit(train_data=train_loader, eval_data=valid_loader,epochs=10, verbose=1,)


优化pipeline

对于不同风机的数据,我们提取特征的方式是相同的,因此我们可以利用python的Parallel库进一步优化代码的性能,提升迭代的效率。核心代码如下:

# 生成训练数据
def generate_train_data(station, id):df = read_data(station, id, 'train').valuesreturn extract_train_data(df)# 通过并行运算生成训练集合
train_data = []
for station in [1, 2]:train_data_tmp = Parallel(n_jobs = -1, verbose = 1)(delayed(lambda x: generate_train_data(station, x))(id) for id in tqdm(range(25)))train_data = train_data + train_data_tmp

这里提升的效率与CPU的核心个数成正比,比赛中我们使用了8核CPU,因此可以在数据生成上提升8倍的效率。


拟合风向的问题

本次比赛的预测标签包含风速风向,其中对于风向,由于角度是循环的,我们有 6216f8c985bc3e4405a5188fa780d6a7.png评价函数为 MAE。在训练阶段,直接预测风向会存在问题,因为0与1代表着相同的意义,模型在遇到风向为0/1的情况时预测为它们的均值0.5,导致误差。这里我们通过将风向角度转化为风向在垂直方向上的分量,来避免直接预测风向,同时可以避免拟合风向带来的问题。


处理噪音

在取得A榜第一名的成绩后,我们尝试对数据中存在的噪音进行处理。由于对输入侧进行处理的风险比较大,容易抹除输入特征中的有效信号,于是我们选择对标签进行平滑处理。我们将模型预测后的值与原标签做加权平均,接着使用平滑后的新标签进行训练,实现了在A榜上0.1分的提升。


实验结果

比赛的分数由如下公式计算得出:

84c6fec3c46d3ac889bb7d524cb34ad4.png

其中,d373d704d87422f9bc3d4557eaf6ea59.png为平均绝对误差。实验结果如下表所示。不难发现,比赛成绩的提升主要来自于对数据与标签的处理,这也是我们在建模时最应该重视的两个要素。

0b6bc4632cf62210c9b726d5572e2814.png


赛后感想

这一次工业大数据比赛中,我们在风况预测赛道重型配件需求预测赛道中均取得了二等奖的好成绩。通过这一次比赛,我们发现工业场景下的数据质量可能并不理想,对缺失值、噪音都需要进行细心处理。在处理时间序列预测任务时,历史数据的积累中可能并不包括未来遇到的突发情况,仅仅依赖模型可能会存在较大的偏差,这也是我们在建模时需要格外关注的问题。

studio项目链接:

https://aistudio.baidu.com/aistudio/projectdetail/3260925

Paddle地址: 

https://github.com/PaddlePaddle/Paddle

PaddleNLP地址:

https://github.com/PaddlePaddle/PaddleNLP

参考文献

[1] 工业大数据产业创新平台 https://www.industrial-bigdata.com/Competition

相关推荐

  • 这里有一份轻量级文字识别技术创新大赛优胜团队的修炼秘籍送给你!

  • 轻量级文字识别技术创新大赛亚军方案分享

8c02795884dbebdf7b8e4a5d0a718a89.gif

关注【飞桨PaddlePaddle】公众号

获取更多技术内容~

这篇关于Kaggle冠军解读:风电场短期风况预测任务方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/542446

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

JavaFX应用更新检测功能(在线自动更新方案)

JavaFX开发的桌面应用属于C端,一般来说需要版本检测和自动更新功能,这里记录一下一种版本检测和自动更新的方法。 1. 整体方案 JavaFX.应用版本检测、自动更新主要涉及一下步骤: 读取本地应用版本拉取远程版本并比较两个版本如果需要升级,那么拉取更新历史弹出升级控制窗口用户选择升级时,拉取升级包解压,重启应用用户选择忽略时,本地版本标志为忽略版本用户选择取消时,隐藏升级控制窗口 2.

如何选择SDR无线图传方案

在开源软件定义无线电(SDR)领域,有几个项目提供了无线图传的解决方案。以下是一些开源SDR无线图传方案: 1. **OpenHD**:这是一个远程高清数字图像传输的开源解决方案,它使用SDR技术来实现高清视频的无线传输。OpenHD项目提供了一个完整的工具链,包括发射器和接收器的硬件设计以及相应的软件。 2. **USRP(Universal Software Radio Periphera

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

GPT系列之:GPT-1,GPT-2,GPT-3详细解读

一、GPT1 论文:Improving Language Understanding by Generative Pre-Training 链接:https://cdn.openai.com/research-covers/languageunsupervised/language_understanding_paper.pdf 启发点:生成loss和微调loss同时作用,让下游任务来适应预训

MyBatis 切换不同的类型数据库方案

下属案例例当前结合SpringBoot 配置进行讲解。 背景: 实现一个工程里面在部署阶段支持切换不同类型数据库支持。 方案一 数据源配置 关键代码(是什么数据库,该怎么配就怎么配) spring:datasource:name: test# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource# @需要修改 数据库连接及驱动u