报名仅剩十天!又一开发者公布高分方案源代码,助力软件杯选手高效解题

本文主要是介绍报名仅剩十天!又一开发者公布高分方案源代码,助力软件杯选手高效解题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

‍‍

3fbc61f700238c62dac0671fb6fdca6c.jpeg

本文作者:艾宏峰

算法工程师

M6 Global赛道总排名4th

KDD Cup 2022风电功率预测飞桨赛道5th

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

9af1ec4bccb8a833ae8de56299188aef.png

赛题背景

随着清洁能源的快速发展,风力发电已经成为可再生能源的重要组成部分,然而风具有随机性特点,常规天气预报无法准确反映出风电场所在区域的真实风速,从而造成发电功率预测准确率低下,影响电力供需平衡。因此,提高风电功率预测的准确性,为电网调度提供科学支撑,对我国能源产业有十分重要的价值。

0c8d622398dac71e18cd3c9d9213669c.png

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

db40c39fbd04e437705b5de6a9026d23.png

时序预测技术介绍

时间序列预测技术是指基于历史数据和时间变化规律,通过数学模型和算法对未来发展趋势进行预测的一种技术。时间序列预测技术广泛应用于经济、金融、交通、气象等领域,以帮助人们做出更加准确的决策。

时序预测从不同角度看有不同分类:

  • 实现原理的角度,可以分为:传统统计学,机器学习(又分非深度学习和深度学习);

  • 预测步长区分,可以分为单步预测和多步预测;

  • 输入变量区分,可以分为自回归预测和使用协变量进行预测;

  • 输出结果区分,可以分为点预测和概率预测;

  • 目标个数区分,可以分为一元、多元、多重时间序列预测

17df2814f1a198442a3c790739cd1516.png

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

时间序列预测技术的研究历史可以追溯到20世纪初期。最早的时间序列预测方法是基于时间平均法和线性趋势法,后来发展出了指数平滑法、ARIMA 模型、神经网络模型等预测方法。随着机器学习和深度学习的发展,时间序列预测技术也得到了不断的拓展和创新,比如 Transformer 与时序的结合。

ef2b4859a4478ad54ddbb78897b6752b.png

59670e3e154dd17f2bd988cb7e699771.png

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

时间序列预测技术有着广泛的应用场景。例如,

  • 经济领域,时间序列预测技术可以用于股票市场预测、经济增长预测、通货膨胀预测等。

  • 交通领域,时间序列预测技术可以用于公交车到站时间预测、交通拥堵预测等。

  • 气象领域,时间序列预测技术可以用于气象灾害预警、天气变化预测等。

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

  • 提高风电发电效率:通过精准预测风电功率,可以合理安排风电机组的运行,提高风电发电效率,减少能源浪费。

  • 保障电网稳定运行:风电功率预测可以帮助电网运营商及时调整电网负荷,避免电网过载或供电不足等问题,保障电网稳定运行。

  • 促进可再生能源发展:风电功率预测可以提高风电发电的可靠性和经济性,促进可再生能源发展,降低对传统能源的依赖。

  • 降低能源成本:通过精准预测风电功率,可以有效避免风电机组的过剩和不足,降低能源成本,提高能源利用率。

  • 推动智能电网建设:风电功率预测是智能电网建设的重要组成部分,可以实现对风电发电的精准监控和管理,推动智能电网建设。

f0e3342ce41f024369e853e6c56cc84e.png

赛制赛段

预选赛

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

区域赛

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

总决赛

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

b0b8bc33c21daf6fcb3866ae545b0708.png

赛题数据

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

  • WINDSPEED 预测风速

  • WINDDIRECTION 风向

  • TEMPERATURE 温度

  • HUMIDITY 湿度

  • PRESSURE 气压

  • PREPOWER 预测功率(系统生成)

  • ROUND(A.WS,1)实际风速

  • ROUND(A.POWER,0) 实际功率(计量口径一)

  • YD15 实际功率(预测目标,计量口径二)

7fec4192dcac795306da0b1cb3020c98.png

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

2087f7722b3ba3bcbe772f791bd80356.png

数据注意事项

  • 原始数据集存在不同格式的风机数据,需要额外的数据拼接处理工作;

  • 每个风机的最后一天的 ROUND(A.POWER,0)和 YD15 两个字段数据基本为空,这是出题方希望我们预测填空的数据;

  • csv 内的时间戳未必有序,需要自行排序;

  • 数据存在缺失和离群值;

  • 数据存在重复样本(即同一时间戳有多条样本)。以风机04为例,重复样本数量: 34764, 占比: 38.68340232340766%。

以风机4为例的EDA结果展示

e2a126661cd9ab9f19178d328faec0c9.png

315381696b41144b683aad3afbe4b263.png

877031061b6100d3df6afd3437a89b05.png

061244920658370f0748aab4a7cc98fa.png

官方对数据的一些回复

  • 由于测量设备和网络传输问题,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 之间就有很强的相关性;

55102c3973659c5e454b9b3e9bd0b31b.png

评测说明

3676acce26fd0a2a24bb1f5effa8daa4.png

算法部分

本次比赛要求选手将算法模型提交至人工智能学习与实训社区 AI Studio 进行自动评测,预选赛开放10个风场数据,区域赛开放新的10个风场数据,预选赛和区域赛算法成绩各占30%。

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

38332e81e5598b368dfe3fa570eac3df.png

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

21351565e6640f1ecd6c517f65110d4b.png软件部分

本次比赛要求选手基于 Web 技术实现:

数据可视化

将预测结果以图表等形式展示出来,便于用户进行观察和分析;

实时更新与滚动预测

能够基于提供的数据实时模拟真实功率、预测功率及其之间的差异,通过调节过去不同长度的时间段,以更新未来预测结果,且预测的时间段可调节;

响应式设计

支持多种终端,包括 PC 端、移动端等,以适应不同设备的屏幕尺寸和分辨率;

其他创新附加功能

e100e7c704a26589f94812834a45fdd8.png

提交说明

测评数据的格式如下:

| --- ./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| --- …

4998f672bbd0c04d2019228f8be99824.png

基线模型流程

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

ade36bb9cf96f1d54c752ae85a7d5e63.png

以时间序列举例,因为一般测试集也会是未来数据,所以我们也要保证训练集是历史数据,而划分出的验证集是未来数据,不然会发生“时间穿越”的数据泄露问题,导致模型过拟合(例如用未来预测历史数据),这个时候就有两种验证划分方式可参考使用:

  • TimeSeriesSplit:Sklearn 提供的 TimeSeriesSplit;

  • 固定窗口滑动划分法:固定时间窗口,不断在数据集上滑动,获得训练集和验证集。(个人推荐这种)

92fc19287b52230d537dc71a9f1a2109.png

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

9bd331d7f5ae2a472867860109782726.png

(1)静态变量(Static Covariates):不会随时间变化的变量,例如风机id、风机位置;

(2)时变变量(Time-dependent Inputs):随时间变化的变量;

  • 过去观测的时变变量(Past-observed Inputs):过去可知,但未来不可知,例如历史风速、温度、气压等

  • 先验已知未来的时变变量(Apriori-known Future Inputs):过去和未来都可知,例如天气预报未来风速、温度、气压等;

数据加载器的代码如下:

# unix时间戳转换
def to_unix_time(dt):# timestamp to unixepoch = datetime.datetime.utcfromtimestamp(0)return int((dt - epoch).total_seconds())def from_unix_time(unix_time):# unix to timestampreturn 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*5self.pred_len = pred_len    # 预测长度,预测次日00:00至23:45实际功率,即1天:24*4self.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 = strideassert 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_valiborder1s = [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)# 保存scalerpickle.dump(self.scaler, open('/home/aistudio/submission/model/scaler.pkl', 'wb'))else:data = x_values# array to paddle tensorself.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 = indexs_end = s_begin + self.input_lenr_begin = s_end + self.strider_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_ydef __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*5self.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_lenborder2 = len(df)   # 标准化self.scaler = StandardScaler()if self.scale:# 读取预训练好的scalerself.scaler = pickle.load(open('/home/aistudio/submission/model/scaler.pkl', 'rb'))data = self.scaler.transform(x_values)else:data = x_values# array to paddle tensorself.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 = indexs_end = s_begin + self.input_lenr_begin = s_end + self.strider_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_ydef __len__(self):"""实现__len__方法,返回数据集总数目"""return len(self.data_x) - self.input_len - self.stride - self.pred_len  + 1

1cdaa14b594f79ba8eee439afa276aa4.png

模型代码如下:

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}")breakprint('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?contributionType=1&sUid=397884&shared=1&ts=1683175097635

a5660b7c6546af23e6f9b8b4ccdd4070.png

提分技巧

735681731d857a56c4492015a545071a.png

挖掘风机间关联信息

虽然赛方没给出不同风机的地理位置,且不同风机的数据分布时间段不完全不一致,但可以从风场维度,联合多个风机开展数据分析,看是否能挖掘出风机之间的关联信息,以减少单风机的数据噪声、帮助填补缺失或处理异常等。

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

a6765f3987cec0d3a946b440bf144637.png

ST-Tree module:ST-Tree:Spatio-Partitioned Time-Phased Tree Model。它算每个风机之间的皮尔逊系数,再用 K-means 聚类,然后针对聚类的风机训练 LightGBM。

--KDD 第1名方案(海康)

e968eede6b68dfa8bfc813c65fc8e429.png

  • 在图网络表征上,选手是把与风机有 Top-K 高相关性的风机们,确认连接边,构建图关系。

  • 用 k-shape 算法将风机聚类成39类,每类风机用一个 LightGBM 预测。

  • 附属风机可被看做是一类风机们,它们之间有最相似的发电规律。通过平均附属风机的功率,当做特征加入模型,能缓解数据噪声。

--KDD 第4名方案(清华、浙江工商、多伦多大学)

8083b5a6f6c3b780fad4186b272c0766.png

分段建模预测

风机的发电功率预测难度是挺大的,模型一般偏好均值预测,所以我们要争取把可预测性强的短期预测部分预测好,再考虑如何提升模型的中长期预测能力。其实短期预测可以考虑递归预测,因为它强调时序前后依赖,对短期信息依赖程度会更高些,不足在于中后期会误差累积。所以在中长期则可以考虑多步生成式预测。

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

  • 随着时间滞后项的增加,自相关系数迅速衰减,说明短期预测可能性更大,中长期就比较困难了。

  • 随机抽取几个不同的风速时序,结果显示风速时序没有展示出极端的长期依赖。而且 GRU 用更少的参数反而避免了过拟合问题。

6855989c77050d4417dfd31a87adba51.png

8988b44b807e84427e12f12bb7df1fe1.png

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

b8403a7718c7f516cfe7c5dd50998a49.png

ae60264dd97526e98caa35b0c2a53e75.png

其他

  • 异常值处理,尤其是标签矫正: 例如风速过大但功率为0的异常,在特定风速下的离群功率等;

  • 标签融合: 融合两个标签,以 YD15 为主,A.Power 为辅;

  • 利用天气预报的数据: 加 decoder 部分加入即可; 

  • 挖掘更多特征: 差分序列、同时刻风场/邻近风机的特征均值/标准差等;

  • 尝试树模型: XGB、LGB 等;

  • 模型参数调优: optuna;

  • 模型融合: 人工加权或配合寻参算法。

官方交流QQ群

QQ 搜索479266219,加入官方交流 QQ 群~

这篇关于报名仅剩十天!又一开发者公布高分方案源代码,助力软件杯选手高效解题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

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

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

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

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

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

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

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

客户案例:安全海外中继助力知名家电企业化解海外通邮困境

1、客户背景 广东格兰仕集团有限公司(以下简称“格兰仕”),成立于1978年,是中国家电行业的领军企业之一。作为全球最大的微波炉生产基地,格兰仕拥有多项国际领先的家电制造技术,连续多年位列中国家电出口前列。格兰仕不仅注重业务的全球拓展,更重视业务流程的高效与顺畅,以确保在国际舞台上的竞争力。 2、需求痛点 随着格兰仕全球化战略的深入实施,其海外业务快速增长,电子邮件成为了关键的沟通工具。

软件设计师备考——计算机系统

学习内容源自「软件设计师」 上午题 #1 计算机系统_哔哩哔哩_bilibili 目录 1.1.1 计算机系统硬件基本组成 1.1.2 中央处理单元 1.CPU 的功能 1)运算器 2)控制器 RISC && CISC 流水线控制 存储器  Cache 中断 输入输出IO控制方式 程序查询方式 中断驱动方式 直接存储器方式(DMA)  ​编辑 总线 ​编辑

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2