“中国软件杯”大学生软件设计大赛——龙源风电赛道比赛预测基线

本文围绕风电功率预测赛题展开,介绍了龙源电力提供的数据集,包括数据字段、采集方式及预测时间要求。阐述了用PaddleTS模型库处理数据的流程,如预处理、分析,还讲解了单模型和集成模型的训练、评估、保存等,以及回测、提交说明和常见问题解答。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

“中国软件杯”大学生软件设计大赛——龙源风电赛道比赛预测基线 - 创想鸟

1 赛题数据

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

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

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

考虑到风电场的特殊性,不同风机间的地理位置也是序列预测的一个重要参考价值。但是本次比赛未给出相关信息,因此我们可以考虑用纯时间序列预测生成基线结果。

1.1 注意事项

为更加真实地模拟风电企业生产运行的实际情况,在本次比赛中,用于评测的数据集中,除历史数据外,还会提供预测时间段从天气预报中获得的数据字段,包括:

WINDSPEED 预测风速WINDDIRECTION 风向TEMPERATURE 温度HUMIDITY 湿度PRESSURE 气压

本基线未使用到上述信息,选手们在优化模型时,可以尝试利用好天气预报数据,以取得更好的预测结果。

2 PaddleTS模型库

PaddleTS是一款基于飞桨深度学习框架的开源时序建模算法库,其具备统一的时序数据结构、全面的基础模型功能、丰富的数据处理和分析算子以及领先的深度时序算法,可以帮助开发者实现时序数据处理、分析、建模、预测全流程,在预测性维护、智慧能耗分析、价格销量预估等场景中有重要应用价值。

本文就以PaddleTS模型库的实现风机功率预测基线开发为例,介绍如何使用模型库快速、批量生成多路时间序列的预测结果。

In [2]

# 安装模型库!pip install paddlets!pip install ray!pip install optuna

   In [3]

#导入需要的包%matplotlib inlineimport osimport pandas as pdimport numpy as npimport matplotlib.pyplot as pltfrom sklearn.preprocessing import StandardScaler, MinMaxScalerimport datetimeimport paddletsfrom paddlets import TSDatasetfrom paddlets import TimeSeriesfrom paddlets.models.forecasting import MLPRegressor, LSTNetRegressorfrom paddlets.transform import Fill, StandardScalerfrom paddlets.metrics import MSE, MAEfrom paddlets.analysis import AnalysisReport, Summaryfrom paddlets.datasets.repository import get_datasetimport warningswarnings.filterwarnings('ignore')

       

/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddlets/utils/backtest.py:6: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working  from collections import defaultdict, Iterable

       

3 数据集分析

3.1 数据预处理

In [312]

# 解压缩数据集!unzip -O GBK 功率预测竞赛赛题与数据集.zip

   

本次比赛重点侧重于模型算法的落地应用,因此,数据集高度贴近风电企业实际情况,选手开发数据集处理方法,需要同时适配不同格式的风机数据。预选赛提供10个风场的数据,区域赛会提供新的10个风场数据用于新的训练和预测,数据格式的情况可能会更为复杂,本文总结了风场数据可能存在的各种情况:

“中国软件杯”大学生软件设计大赛——龙源风电赛道比赛预测基线 - 创想鸟        

针对以上问题,可以确定如下的数据集处理思路:

将风机数据统一为csv格式将同一个风机的多条数据进行拼接,按时间戳进行排序,合成一个文件In [313]

def data_preprocess(data_dir):    files = os.listdir(data_dir)    # 第一步,完成数据格式统一    for f in files:        # 获取文件路径        data_file = os.path.join(data_dir, f)        # 获取文件名后缀        data_type = os.path.splitext(data_file)[-1]        # 获取文件名前缀        data_name = os.path.splitext(data_file)[0]        # 如果是excel文件,进行转换        if data_type == '.xlsx':            # 需要特别注意的是,在读取excel文件时要指定空值的显示方式,否则会在保存时以字符“.”代替,影响后续的数据分析            data_xls = pd.read_excel(data_file, index_col=0, na_values='')            data_xls.to_csv(data_name + '.csv', encoding='utf-8')            # 顺便删除原文件            os.remove(data_file)    # 第二步,完成多文件的合并,文件目录要重新更新一次    files = os.listdir(data_dir)    for f in files:        # 获取文件路径        data_file = os.path.join(data_dir, f)        # 获取文件名前缀        data_basename = os.path.basename(data_file)        # 检查风机数据是否有多个数据文件        if len(data_basename.split('-')) > 1:            merge_list = []            # 找出该风机的所有数据文件            matches = [ f for f in files if (f.find(data_basename.split('-')[0] + '-') > -1)]            for i in matches:                # 读取风机这部分数据                data_df = pd.read_csv(os.path.join(data_dir, i), index_col=False, keep_default_na=False)                merge_list.append(data_df)            if len(merge_list) > 0:                all_data = pd.concat(merge_list,axis=0,ignore_index=True).fillna(".")                all_data.to_csv(os.path.join(data_dir, data_basename.split('-')[0]+ '.csv'),index=False)             for i in matches:                # 删除这部分数据文件                os.remove(os.path.join(data_dir, i))            # 更新文件目录            files = os.listdir(data_dir)

   In [314]

data_preprocess('功率预测竞赛赛题与数据集')

   

至此,我们完成了数据集的批量预处理,将各种风机不同格式、不同数量文件的数据集进行了统一。这样,在后续的时序数据预测中,我们只需要对csv格式的时序数据进行处理即可。

In [1]

os.listdir('功率预测竞赛赛题与数据集')

   

在这里,我们也需要准备一个存放预测结果的文件目录。

In [1]

!mkdir submit

   

3.2 风机数据EDA

由于赛题给出的不同风机采集数据字段都是一致的,仅仅是文件格式和数量不同。因此,在我们统一风机数据文件格式后,只需要抽出其中一两个风机的数据进行探索性数据分析即可——其它风机的情况是大体相同的。

在这里,我们取刚才转换格式并合并文件的一个风机数据进行分析,比如04号风机(如果前面的数据处理有错误,在这里就能发现)。

In [3]

# 读取一份数据文件,这里需要特别注意时间戳字段的格式指定df = pd.read_csv('功率预测竞赛赛题与数据集/04.csv',parse_dates=['DATATIME'],infer_datetime_format=True,dayfirst=True)

   In [99]

# 查看04号风机的运行工况数据df.head()

       

             DATATIME  WINDSPEED  PREPOWER  WINDDIRECTION  TEMPERATURE   2021-10-02 00:00:00        4.1    6744.4             39         24.6   1 2021-10-02 00:00:00        4.1    7949.5             39         24.6   2 2021-10-02 00:15:00        4.1    6622.2             38         24.6   3 2021-10-02 00:15:00        4.1    7926.1             38         24.6   4 2021-10-02 00:30:00        4.0    6522.1             35         24.5      HUMIDITY  PRESSURE  ROUND(A.WS,1)  ROUND(A.POWER,0)     YD15  0        94      1008            4.0            7078.0   5906.0  1        94      1008            4.0            7078.0   5906.0  2        94      1008            4.5           11209.0   8721.0  3        94      1008            4.5           11209.0   8721.0  4        94      1008            4.6           11467.0  11973.0

               In [100]

df.columns

       

Index(['DATATIME', 'WINDSPEED', 'PREPOWER', 'WINDDIRECTION', 'TEMPERATURE',       'HUMIDITY', 'PRESSURE', 'ROUND(A.WS,1)', 'ROUND(A.POWER,0)', 'YD15'],      dtype='object')

               In [101]

df.info()

       

RangeIndex: 89868 entries, 0 to 89867Data columns (total 10 columns): #   Column            Non-Null Count  Dtype         ---  ------            --------------  -----          0   DATATIME          89868 non-null  datetime64[ns] 1   WINDSPEED         89868 non-null  float64        2   PREPOWER          89868 non-null  float64        3   WINDDIRECTION     89868 non-null  int64          4   TEMPERATURE       89868 non-null  float64        5   HUMIDITY          89868 non-null  int64          6   PRESSURE          89868 non-null  int64          7   ROUND(A.WS,1)     89349 non-null  float64        8   ROUND(A.POWER,0)  89349 non-null  float64        9   YD15              89552 non-null  float64       dtypes: datetime64[ns](1), float64(6), int64(3)memory usage: 6.9 MB

       

由于风机工况数据文件并不大,读者也可以直接打开文件查看。这时会发现最后一天的ROUND(A.POWER,0),YD15两个字段数据基本为空,这就是出题方希望我们预测填空的数据。

不过,这里需要我们注意的是,csv文件里的时间戳未必是顺序排列的,最后一天的时间戳未必在文件末段,由于PaddleTS会自动对时间排序,我们在下面数据分析的时候也能发现这个问题。

In [4]

df.tail()

   

接下来,我们使用PaddleTS快速完成数据集的初步分析。 PaddleTS的Analsis模块能够帮助我们更简便地理解数据,同时,它还提供了多种多样的Anayzer帮助用户洞察数据,也提供了Report API给用户展示多种Analyzer组合分析的数据。

In [103]

# 因为数据批次不同,数据集中有一些时间戳重复的脏数据,送入paddlets前要进行处理,本赛题要求保留第一个数据df.drop_duplicates(subset = ['DATATIME'],keep='first',inplace=True)

   In [104]

# 我们选取最后30天的风机工况数据进行可视化target_cov_dataset = TSDataset.load_from_dataframe(    df[-4*24*30:],    time_col='DATATIME',    target_cols='YD15',    observed_cov_cols=['WINDSPEED', 'PREPOWER', 'WINDDIRECTION', 'TEMPERATURE',       'HUMIDITY', 'PRESSURE', 'ROUND(A.WS,1)', 'ROUND(A.POWER,0)'],    freq='15min',    fill_missing_dates=False)# 由于不同指标的数值差异较大,在绘图时选取数值范围相近的组合分开绘制可视化图表target_cov_dataset.plot(['WINDSPEED', 'TEMPERATURE', 'ROUND(A.WS,1)'])

       


               

               In [105]

target_cov_dataset.plot(['WINDDIRECTION', 'HUMIDITY', 'PRESSURE'])

       


               

               In [106]

target_cov_dataset.plot(['PREPOWER', 'ROUND(A.POWER,0)','YD15'])

       


               

               In [107]

target_cov_dataset.summary()

       

                 YD15    WINDSPEED      PREPOWER  WINDDIRECTION  TEMPERATURE  missing      0.032986     0.000000      0.000000       0.000000     0.000000   count     2785.000000  2880.000000   2880.000000    2880.000000  2880.000000   mean     14332.472531     4.243854  14898.320499     110.196875    27.239931   std      17475.732789     2.368603  16348.756167     101.818709     2.493907   min       -841.000000     0.000000   1194.570760       0.000000    21.300000   25%        247.000000     2.400000   3507.291667      28.000000    25.300000   50%       5775.000000     3.900000   7376.846199      62.000000    26.700000   75%      25184.000000     5.900000  20275.775000     202.000000    29.200000   max      64725.000000    11.000000  75210.200000     359.000000    34.100000               HUMIDITY     PRESSURE  ROUND(A.WS,1)  ROUND(A.POWER,0)  missing     0.000000     0.000000       0.034028          0.034028  count    2880.000000  2880.000000    2782.000000       2782.000000  mean       81.825694  1006.615972       5.030410      17404.443925  std        12.432304     2.870195       2.798523      20852.864000  min        49.000000   998.000000       0.700000       -355.000000  25%        72.000000  1005.000000       2.800000        902.500000  50%        84.000000  1007.000000       4.400000       7770.000000  75%        92.000000  1008.000000       6.800000      28848.500000  max        99.000000  1013.000000      15.100000      80409.000000

               In [108]

target_cov_dataset.max()

       

YD15                64725.0WINDSPEED              11.0PREPOWER            75210.2WINDDIRECTION         359.0TEMPERATURE            34.1HUMIDITY               99.0PRESSURE             1013.0ROUND(A.WS,1)          15.1ROUND(A.POWER,0)    80409.0dtype: float64

               

Analsis模块目前支持的分析器有:

Summary : 总结指标,目前支持均值、方差、最小值、25%中位数、50%中位数、75%中位数、最大值、缺失百分比、平稳性p值。

Max : 计算给定列的最大值。

FFT : 基于快速傅里叶变换的信号频域分析。

STFT : 基于短时傅里叶变换的信号时频分析。

CWT : 基于连续小波变换的信号时频分析。

我们也可以根据实际需求让它生成对应的时序数据分析报告。

In [109]

report = AnalysisReport(target_cov_dataset, ["summary"])# 加入时频分析后,需要较长的计算时间# report = AnalysisReport(target_cov_dataset, ["summary","fft","stft","cwt"])

   In [110]

report.export_docx_report()

       

[2023-04-03 10:57:44,069] [paddlets.analysis.analysis_report] [INFO] save report succcess, save at ./analysis_report.docx

       

               

4 模型应用

4.1 数据集准备

在数据集分析时,我们可以不对数据缺失值进行处理以保持数据原貌。但是在模型训练时,则必须指定缺失值的处理方式,否则训练会报错。同时,PaddleTS要求输入的协变量为float格式,对于个别字段,需要进行指定。

In [4]

df = pd.read_csv('功率预测竞赛赛题与数据集/01.csv',parse_dates=['DATATIME'],infer_datetime_format=True,dayfirst=True,dtype={'WINDDIRECTION':np.float64, 'HUMIDITY':np.float64, 'PRESSURE':np.float64})

   In [5]

# 因为数据批次不同,数据集中有一些时间戳重复的脏数据,送入paddlets前要进行处理,本赛题要求保留第一个数据df.drop_duplicates(subset = ['DATATIME'],keep='first',inplace=True)

   In [6]

df.info()

       

Int64Index: 26208 entries, 0 to 34586Data columns (total 10 columns): #   Column            Non-Null Count  Dtype         ---  ------            --------------  -----          0   DATATIME          26208 non-null  datetime64[ns] 1   WINDSPEED         26208 non-null  float64        2   PREPOWER          26208 non-null  float64        3   WINDDIRECTION     26208 non-null  float64        4   TEMPERATURE       26208 non-null  float64        5   HUMIDITY          26208 non-null  float64        6   PRESSURE          26208 non-null  float64        7   ROUND(A.WS,1)     25943 non-null  float64        8   ROUND(A.POWER,0)  25943 non-null  float64        9   YD15              26113 non-null  float64       dtypes: datetime64[ns](1), float64(9)memory usage: 2.2 MB

       In [8]

target_cov_dataset = TSDataset.load_from_dataframe(    df,    time_col='DATATIME',    target_cols=['ROUND(A.POWER,0)', 'YD15'],    observed_cov_cols=['WINDSPEED', 'PREPOWER', 'WINDDIRECTION', 'TEMPERATURE',       'HUMIDITY', 'PRESSURE', 'ROUND(A.WS,1)'],    freq='15min',    fill_missing_dates=True,    fillna_method = 'pre')

   

关于数据集的切分,有两种方式。一是直接指定数据集切分比例;二是根据时序数据的特点,PaddleTS也支持通过时间戳的节点进行划分。

In [9]

# train_dataset, val_test_dataset = target_cov_dataset.split(0.7)# val_dataset, test_dataset = val_test_dataset.split(0.3)# train_dataset.plot(add_data=[val_dataset,test_dataset], labels=['Val', 'Test'])

       


               

               

由于本项目是一个面向落地应用的场景,选手在设计数据集划分方式时,可以优先考虑通过时间戳划分数据集。

需要注意的是,赛题是这么要求的:

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

所以我们在进行切分时,建议优先按照05:00这个时间点进行划分。

In [54]

_ , train_dataset = target_cov_dataset.split('2021-04-30 04:45:00')train_dataset, val_test_dataset = train_dataset.split('2021-07-31 04:45:00')val_dataset, test_dataset = val_test_dataset.split('2021-08-31 04:45:00')# 最后一天的工况数据需要预测ROUND(A.POWER,0)和YD15两个字段,而且输入数据只到前一天的早上5点test_dataset, pred_dataset = test_dataset.split('2021-09-30 04:45:00')train_dataset.plot(add_data=[val_dataset,test_dataset], labels=['Val', 'Test'])

       


               

               

数据处理时,可以将指定列的值缩放为零均值和单位方差来实现归一化转换。

In [11]

scaler = StandardScaler()scaler.fit(train_dataset)train_dataset_scaled = scaler.transform(train_dataset)val_test_dataset_scaled = scaler.transform(val_test_dataset)val_dataset_scaled = scaler.transform(val_dataset)test_dataset_scaled = scaler.transform(test_dataset)# 如果使用了归一化,在后续模型预测时要进行数据集逆转换# test_dataset = scaler.inverse_transform(ts_test_scaled)

   

4.2 方案一:单模型预测

4.2.1 单模型训练

我们先引入LSTM模型,做单模型的单时间序列预测任务示例。

注意到我们需要用前一日05:00之前的风机数据,预测次日00:00至23:45的实际功率——从数据输入,到数据输出,不是连续的。

这与一般时序模型处理的数据情况存在不同,因此我们要进行一些处理,即:

因赛AIGC 因赛AIGC

因赛AIGC解决营销全链路应用场景

因赛AIGC 73 查看详情 因赛AIGC 用前一日05:00之前的风机数据,预测05:00之后到次日23:45的实际功率对于输出,裁剪掉05:00之后次日00:00之间的预测结果In [56]

lstm = LSTNetRegressor(    in_chunk_len = (24 + 19) * 7 * 4,    out_chunk_len = (24 + 19) * 4,    max_epochs=10,    optimizer_params= dict(learning_rate=5e-3),)

   In [58]

lstm.fit(train_dataset, val_dataset)

       

[2023-04-13 23:38:57,682] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 000| loss: 861660534.464798| val_0_mae: 15910.196289| 0:00:17s[2023-04-13 23:39:14,833] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 001| loss: 702688372.799785| val_0_mae: 13043.845703| 0:00:34s[2023-04-13 23:39:31,568] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 002| loss: 629983447.554781| val_0_mae: 12294.802734| 0:00:51s[2023-04-13 23:39:48,294] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 003| loss: 605644464.240311| val_0_mae: 12514.247070| 0:01:07s[2023-04-13 23:40:05,296] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 004| loss: 653346019.209870| val_0_mae: 12663.069336| 0:01:24s[2023-04-13 23:40:22,370] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 005| loss: 636977429.988467| val_0_mae: 12120.254883| 0:01:41s[2023-04-13 23:40:39,917] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 006| loss: 627197534.103258| val_0_mae: 12355.960938| 0:01:59s[2023-04-13 23:40:56,507] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 007| loss: 649966813.987394| val_0_mae: 15012.541992| 0:02:15s[2023-04-13 23:41:13,214] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 008| loss: 677125166.586027| val_0_mae: 14301.767578| 0:02:32s[2023-04-13 23:41:29,917] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 009| loss: 641291815.119218| val_0_mae: 11663.125977| 0:02:49s[2023-04-13 23:41:29,922] [paddlets.models.common.callbacks.callbacks] [INFO] Stop training because you reached max_epochs = 10 with best_epoch = 9 and best_val_0_mae = 11663.125977[2023-04-13 23:41:29,923] [paddlets.models.common.callbacks.callbacks] [INFO] Best weights from best epoch are automatically used!

       In [59]

subset_test_pred_dataset = lstm.predict(val_dataset)subset_test_dataset, _ = test_dataset.split(len(subset_test_pred_dataset.target))

   In [60]

# 模型评估mae = MAE()mae(subset_test_dataset, subset_test_pred_dataset)

       

{'ROUND(A.POWER,0)': 16030.454186816549, 'YD15': 15601.535719760628}

               In [61]

# 预测结果可视化subset_test_dataset, _ = test_dataset.split(len(subset_test_pred_dataset.target))subset_test_dataset.plot(add_data=subset_test_pred_dataset, labels=['Pred'])

       


               

               

上图显示的是训练模型对1号风机未来(19+24)小时预测结果与测试集实际趋势的对比。

4.2.2 单模型保存和加载

In [38]

# 模型保存lstm.save("lstm")

   In [40]

# 也可以动转静保存lstm.save("./model", network_model=True, dygraph_to_static=True)

   In [41]

# 模型加载from paddlets.models.model_loader import loadloaded_lstm = load("lstm")

   

4.2.3 生成预测结果

In [62]

# 模型预测result = loaded_lstm.predict(test_dataset)result.to_dataframe()[19*4:]

       

                     ROUND(A.POWER,0)          YD152021-10-01 00:00:00      29912.177734  29194.8867192021-10-01 00:15:00      20358.554688  21656.3886722021-10-01 00:30:00      29699.613281  29963.5664062021-10-01 00:45:00      27510.537109  26607.9531252021-10-01 01:00:00      16971.984375  16437.027344...                               ...           ...2021-10-01 22:45:00      -7202.264648  -7223.2470702021-10-01 23:00:00      -7145.840332  -6936.3242192021-10-01 23:15:00      -3206.446045  -3385.2082522021-10-01 23:30:00       2323.184082   2423.1303712021-10-01 23:45:00         87.688187    660.455383[96 rows x 2 columns]

               In [63]

# 截取次日预测数据result = result.to_dataframe()[19*4:]result = result.reset_index()# 传入风场风机IDresult['TurbID'] = 1# 重新调整字段名称和顺序result.rename(columns={"index": "Datetime"}, inplace=True)result = result[['TurbID', 'Datetime', 'ROUND(A.POWER,0)', 'YD15']]

   In [64]

result.to_csv('submit/01.csv',index=False)

   

4.3 方案二:集成模型预测

集成模型是用集成学习的思想去把多个PaddleTS的预测器集合成一个预测器。目前PaddleTS支持两种集成预测器StackingEnsembleForecaster 和 WeightingEnsembleForecaster

步骤包括:

1.准备数据

2.准备模型

3.组装和拟合模型

4.进行回测

5.模型保存与加载

接下来,我们用集成模型来预测1号风机的输出功率变化。首先是准备集成预测器需要的底层模型,这里我们选择RNN,LSTM和MLP。

4.3.1 集成模型训练

In [65]

from paddlets.models.forecasting import MLPRegressorfrom paddlets.models.forecasting import LSTNetRegressorfrom paddlets.models.forecasting import RNNBlockRegressorlstm_params = {    'sampling_stride': (24 + 19) * 4,    'eval_metrics':["mse", "mae"],    'batch_size': 8,    'max_epochs': 20,    'patience': 10}rnn_params = {    'sampling_stride': (24 + 19) * 4,    'eval_metrics': ["mse", "mae"],    'batch_size': 8,    'max_epochs': 20,    'patience': 10}mlp_params = {    'sampling_stride': (24 + 19) * 4,    'eval_metrics': ["mse", "mae"],    'batch_size': 8,    'max_epochs': 20,    'patience': 10,    'use_bn': True,}

   

为了保持模型预测的一致性, in_chunk_len, out_chunk_len, skip_chunk_len, 这三个参数已经被提取到集成模型中,在组装模型时统一设定。

In [66]

from paddlets.ensemble import StackingEnsembleForecasterreg = StackingEnsembleForecaster(in_chunk_len= (24 + 19) * 7 * 4,out_chunk_len= (24 + 19) * 4,skip_chunk_len=0,estimators=[(LSTNetRegressor, lstm_params),(RNNBlockRegressor, rnn_params), (MLPRegressor, mlp_params)])

   In [67]

# 模型拟合reg.fit(train_dataset, val_dataset)

       

[2023-04-13 23:45:41,784] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 000| loss: 2157854766.545455| val_0_mse: 434634944.000000| val_0_mae: 15134.261719| 0:00:00s[2023-04-13 23:45:42,201] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 001| loss: 1274874944.000000| val_0_mse: 404811008.000000| val_0_mae: 14455.226562| 0:00:00s[2023-04-13 23:45:42,616] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 002| loss: 864954938.181818| val_0_mse: 383082656.000000| val_0_mae: 13955.348633| 0:00:01s[2023-04-13 23:45:43,028] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 003| loss: 654667089.454545| val_0_mse: 364152256.000000| val_0_mae: 13681.997070| 0:00:01s[2023-04-13 23:45:43,452] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 004| loss: 520725451.636364| val_0_mse: 367876256.000000| val_0_mae: 13655.694336| 0:00:02s[2023-04-13 23:45:43,903] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 005| loss: 426495889.454545| val_0_mse: 358582400.000000| val_0_mae: 13374.958984| 0:00:02s[2023-04-13 23:45:44,328] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 006| loss: 366549453.090909| val_0_mse: 364607712.000000| val_0_mae: 13544.072266| 0:00:02s[2023-04-13 23:45:44,741] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 007| loss: 314228002.909091| val_0_mse: 362071616.000000| val_0_mae: 13440.303711| 0:00:03s[2023-04-13 23:45:45,167] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 008| loss: 262892445.090909| val_0_mse: 360921376.000000| val_0_mae: 13412.265625| 0:00:03s[2023-04-13 23:45:45,584] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 009| loss: 221938609.454545| val_0_mse: 358694112.000000| val_0_mae: 13378.511719| 0:00:04s[2023-04-13 23:45:45,999] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 010| loss: 190502526.545455| val_0_mse: 360426240.000000| val_0_mae: 13350.240234| 0:00:04s[2023-04-13 23:45:46,464] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 011| loss: 170810132.363636| val_0_mse: 356882144.000000| val_0_mae: 13284.953125| 0:00:05s[2023-04-13 23:45:46,906] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 012| loss: 148189114.909091| val_0_mse: 360419648.000000| val_0_mae: 13336.097656| 0:00:05s[2023-04-13 23:45:47,352] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 013| loss: 132365503.272727| val_0_mse: 358436960.000000| val_0_mae: 13260.419922| 0:00:05s[2023-04-13 23:45:47,787] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 014| loss: 118677271.272727| val_0_mse: 359307424.000000| val_0_mae: 13311.393555| 0:00:06s[2023-04-13 23:45:48,202] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 015| loss: 105317205.454545| val_0_mse: 363960896.000000| val_0_mae: 13361.411133| 0:00:06s[2023-04-13 23:45:48,613] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 016| loss: 90016433.454545| val_0_mse: 363362176.000000| val_0_mae: 13335.208984| 0:00:07s[2023-04-13 23:45:49,026] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 017| loss: 80012225.090909| val_0_mse: 366721696.000000| val_0_mae: 13422.874023| 0:00:07s[2023-04-13 23:45:49,438] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 018| loss: 73471033.454545| val_0_mse: 367162976.000000| val_0_mae: 13388.984375| 0:00:08s[2023-04-13 23:45:49,861] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 019| loss: 65201690.909091| val_0_mse: 373351680.000000| val_0_mae: 13544.246094| 0:00:08s[2023-04-13 23:45:49,865] [paddlets.models.common.callbacks.callbacks] [INFO] Stop training because you reached max_epochs = 20 with best_epoch = 13 and best_val_0_mae = 13260.419922[2023-04-13 23:45:49,866] [paddlets.models.common.callbacks.callbacks] [INFO] Best weights from best epoch are automatically used!/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddlets/automl/searcher.py:4: DeprecationWarning: The module `ray.tune.suggest` has been moved to `ray.tune.search` and the old location will be deprecated soon. Please adjust your imports to point to the new location. Example: Do a global search and replace `ray.tune.suggest` with `ray.tune.search`.  from ray.tune.suggest import BasicVariantGenerator/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddlets/automl/searcher.py:5: DeprecationWarning: The module `ray.tune.suggest.optuna` has been moved to `ray.tune.search.optuna` and the old location will be deprecated soon. Please adjust your imports to point to the new location. Example: Do a global search and replace `ray.tune.suggest.optuna` with `ray.tune.search.optuna`.  from ray.tune.suggest.optuna import OptunaSearch/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddlets/automl/searcher.py:6: DeprecationWarning: The module `ray.tune.suggest.flaml` has been moved to `ray.tune.search.flaml` and the old location will be deprecated soon. Please adjust your imports to point to the new location. Example: Do a global search and replace `ray.tune.suggest.flaml` with `ray.tune.search.flaml`.  from ray.tune.suggest.flaml import CFO/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddlets/automl/searcher.py:8: DeprecationWarning: The module `ray.tune.suggest.bohb` has been moved to `ray.tune.search.bohb` and the old location will be deprecated soon. Please adjust your imports to point to the new location. Example: Do a global search and replace `ray.tune.suggest.bohb` with `ray.tune.search.bohb`.  from ray.tune.suggest.bohb import TuneBOHB/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddlets/automl/search_space_configer.py:8: DeprecationWarning: The module `ray.tune.sample` has been moved to `ray.tune.search.sample` and the old location will be deprecated soon. Please adjust your imports to point to the new location. Example: Do a global search and replace `ray.tune.sample` with `ray.tune.search.sample`.  from ray.tune.sample import Float, Integer, Categorical[2023-04-13 23:45:52,255] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 000| loss: 1096603042.909091| val_0_mse: 332518752.000000| val_0_mae: 11341.488281| 0:00:00s[2023-04-13 23:45:52,481] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 001| loss: 1096600740.363636| val_0_mse: 332517600.000000| val_0_mae: 11341.460938| 0:00:00s[2023-04-13 23:45:52,691] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 002| loss: 1096598539.636364| val_0_mse: 332516480.000000| val_0_mae: 11341.433594| 0:00:00s[2023-04-13 23:45:52,899] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 003| loss: 1096596171.636364| val_0_mse: 332515360.000000| val_0_mae: 11341.407227| 0:00:00s[2023-04-13 23:45:53,107] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 004| loss: 1096593943.272727| val_0_mse: 332514336.000000| val_0_mae: 11341.378906| 0:00:01s[2023-04-13 23:45:53,316] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 005| loss: 1096591592.727273| val_0_mse: 332513280.000000| val_0_mae: 11341.353516| 0:00:01s[2023-04-13 23:45:53,525] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 006| loss: 1096589457.454545| val_0_mse: 332512160.000000| val_0_mae: 11341.325195| 0:00:01s[2023-04-13 23:45:53,747] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 007| loss: 1096587252.363636| val_0_mse: 332511008.000000| val_0_mae: 11341.296875| 0:00:01s[2023-04-13 23:45:53,965] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 008| loss: 1096585006.545455| val_0_mse: 332509888.000000| val_0_mae: 11341.268555| 0:00:01s[2023-04-13 23:45:54,175] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 009| loss: 1096582626.909091| val_0_mse: 332508768.000000| val_0_mae: 11341.241211| 0:00:02s[2023-04-13 23:45:54,392] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 010| loss: 1096580346.181818| val_0_mse: 332507712.000000| val_0_mae: 11341.212891| 0:00:02s[2023-04-13 23:45:54,603] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 011| loss: 1096578077.090909| val_0_mse: 332506624.000000| val_0_mae: 11341.184570| 0:00:02s[2023-04-13 23:45:54,815] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 012| loss: 1096575848.727273| val_0_mse: 332505504.000000| val_0_mae: 11341.157227| 0:00:02s[2023-04-13 23:45:55,038] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 013| loss: 1096573777.454545| val_0_mse: 332504480.000000| val_0_mae: 11341.129883| 0:00:03s[2023-04-13 23:45:55,258] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 014| loss: 1096571537.454545| val_0_mse: 332503328.000000| val_0_mae: 11341.101562| 0:00:03s[2023-04-13 23:45:55,470] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 015| loss: 1096569122.909091| val_0_mse: 332502272.000000| val_0_mae: 11341.075195| 0:00:03s[2023-04-13 23:45:55,692] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 016| loss: 1096566970.181818| val_0_mse: 332501216.000000| val_0_mae: 11341.047852| 0:00:03s[2023-04-13 23:45:55,903] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 017| loss: 1096564654.545455| val_0_mse: 332500128.000000| val_0_mae: 11341.020508| 0:00:03s[2023-04-13 23:45:56,115] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 018| loss: 1096562478.545455| val_0_mse: 332498976.000000| val_0_mae: 11340.991211| 0:00:04s[2023-04-13 23:45:56,341] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 019| loss: 1096560087.272727| val_0_mse: 332497888.000000| val_0_mae: 11340.962891| 0:00:04s[2023-04-13 23:45:56,344] [paddlets.models.common.callbacks.callbacks] [INFO] Stop training because you reached max_epochs = 20 with best_epoch = 19 and best_val_0_mae = 11340.962891[2023-04-13 23:45:56,345] [paddlets.models.common.callbacks.callbacks] [INFO] Best weights from best epoch are automatically used![2023-04-13 23:45:57,232] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 000| loss: 1096593489.454545| val_0_mse: 332512032.000000| val_0_mae: 11341.327148| 0:00:00s[2023-04-13 23:45:57,250] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 001| loss: 1096573323.636364| val_0_mse: 332509792.000000| val_0_mae: 11341.268555| 0:00:00s[2023-04-13 23:45:57,267] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 002| loss: 1096560116.363636| val_0_mse: 332507104.000000| val_0_mae: 11341.200195| 0:00:00s[2023-04-13 23:45:57,284] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 003| loss: 1096545108.363636| val_0_mse: 332503424.000000| val_0_mae: 11341.111328| 0:00:00s[2023-04-13 23:45:57,301] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 004| loss: 1096528826.181818| val_0_mse: 332500064.000000| val_0_mae: 11341.030273| 0:00:00s[2023-04-13 23:45:57,317] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 005| loss: 1096513128.727273| val_0_mse: 332497216.000000| val_0_mae: 11340.960938| 0:00:00s[2023-04-13 23:45:57,336] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 006| loss: 1096494036.363636| val_0_mse: 332495008.000000| val_0_mae: 11340.905273| 0:00:00s[2023-04-13 23:45:57,355] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 007| loss: 1096474176.000000| val_0_mse: 332495168.000000| val_0_mae: 11340.908203| 0:00:00s[2023-04-13 23:45:57,379] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 008| loss: 1096451537.454545| val_0_mse: 332496992.000000| val_0_mae: 11340.953125| 0:00:00s[2023-04-13 23:45:57,398] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 009| loss: 1096427194.181818| val_0_mse: 332501024.000000| val_0_mae: 11341.063477| 0:00:00s[2023-04-13 23:45:57,416] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 010| loss: 1096412264.727273| val_0_mse: 332499232.000000| val_0_mae: 11341.024414| 0:00:00s[2023-04-13 23:45:57,435] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 011| loss: 1096390341.818182| val_0_mse: 332495904.000000| val_0_mae: 11340.942383| 0:00:00s[2023-04-13 23:45:57,476] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 012| loss: 1096363077.818182| val_0_mse: 332491328.000000| val_0_mae: 11340.830078| 0:00:00s[2023-04-13 23:45:57,494] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 013| loss: 1096338699.636364| val_0_mse: 332490176.000000| val_0_mae: 11340.796875| 0:00:00s[2023-04-13 23:45:57,512] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 014| loss: 1096300602.181818| val_0_mse: 332488480.000000| val_0_mae: 11340.750977| 0:00:00s[2023-04-13 23:45:57,529] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 015| loss: 1096276672.000000| val_0_mse: 332485792.000000| val_0_mae: 11340.683594| 0:00:00s[2023-04-13 23:45:57,547] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 016| loss: 1096274327.272727| val_0_mse: 332479360.000000| val_0_mae: 11340.515625| 0:00:00s[2023-04-13 23:45:57,581] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 017| loss: 1096231005.090909| val_0_mse: 332478176.000000| val_0_mae: 11340.479492| 0:00:00s[2023-04-13 23:45:57,600] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 018| loss: 1096195223.272727| val_0_mse: 332475232.000000| val_0_mae: 11340.405273| 0:00:00s[2023-04-13 23:45:57,617] [paddlets.models.common.callbacks.callbacks] [INFO] epoch 019| loss: 1096174196.363636| val_0_mse: 332471264.000000| val_0_mae: 11340.309570| 0:00:00s[2023-04-13 23:45:57,619] [paddlets.models.common.callbacks.callbacks] [INFO] Stop training because you reached max_epochs = 20 with best_epoch = 19 and best_val_0_mae = 11340.309570[2023-04-13 23:45:57,620] [paddlets.models.common.callbacks.callbacks] [INFO] Best weights from best epoch are automatically used!

       In [73]

subset_test_pred_dataset = reg.predict(val_dataset)subset_test_dataset, _ = test_dataset.split(len(subset_test_pred_dataset.target))

   In [74]

# 模型评估mae = MAE()mae(subset_test_dataset, subset_test_pred_dataset)

       

{'ROUND(A.POWER,0)': 6370.233882897048, 'YD15': 6344.061808114118}

               

从评估指标的表现看,集成模型的表现进步较大。当然,它牺牲了一些训练时间,实际开发时,选手需要综合用户体验和预测效果,进行适当取舍。

In [70]

subset_test_dataset, _ = test_dataset.split(len(subset_test_pred_dataset.target))subset_test_dataset.plot(add_data=subset_test_pred_dataset, labels=['Pred'])

       


               

               

4.3.2 集成模型保存和加载

顾名思义,集成模型保存的时候是分了多个模型,因此,加载的时候是分别加载的。

In [75]

reg.save("reg")

   In [76]

# 集成模型加载loaded_model0 = load("./reg/paddlets-ensemble-model0")loaded_model1 = load("./reg/paddlets-ensemble-model1")loaded_model2 = load("./reg/paddlets-ensemble-model2")

   

4.3.3 生成预测结果

尽管分开加载集成模型显得有些麻烦,但是,我们此时还可以设置不同集成模型的权重,比如:让表现更好的模型权重更大。这样,或许可以进一步提升预测效果。

In [77]

# 输出集成模型预测结果loaded_model0.predict(test_dataset).to_dataframe()[19*4:]*0.2 + loaded_model1.predict(test_dataset).to_dataframe()[19*4:]*0.4 + loaded_model2.predict(test_dataset).to_dataframe()[19*4:]*0.4

       

                     ROUND(A.POWER,0)          YD152021-10-01 00:00:00      11058.841797  11355.4082032021-10-01 00:15:00       5787.002930   5482.8969732021-10-01 00:30:00       8437.157227   8588.4365232021-10-01 00:45:00      11250.096680  11751.4306642021-10-01 01:00:00      -1825.403076  -1882.670776...                               ...           ...2021-10-01 22:45:00       9834.172852   9847.2285162021-10-01 23:00:00      -2804.005127  -2564.6779792021-10-01 23:15:00       3135.439209   3154.8193362021-10-01 23:30:00       6587.747559   6881.8530272021-10-01 23:45:00       -523.073730   -509.810883[96 rows x 2 columns]

               In [118]

# 保存集成模型预测结果# result = (loaded_model0.predict(test_dataset).to_dataframe()[19*4:]*0.2 + loaded_model1.predict(test_dataset).to_dataframe()[19*4:]*0.4 + loaded_model2.predict(test_dataset).to_dataframe()[19*4:]*0.4).to_csv('01.csv')

   In [ ]

# result = result.reset_index()# # 传入风场风机ID# result['TurbID'] = 1# # 重新调整字段名称和顺序# result.rename(columns={"index": "datetime"}, inplace=True)# result = result[['TurbID', 'Datetime', 'ROUND(A.POWER,0)', 'YD15']]

   In [ ]

# result.to_csv('submit/01.csv',index=False)

   

4.4 回测

回测用给定模型获得的历史上的模拟预测,是用来评测模型预测准确率的重要工具“中国软件杯”大学生软件设计大赛——龙源风电赛道比赛预测基线 - 创想鸟        

回测是一个迭代过程,回测用固定预测窗口在数据集上进行重复预测,然后通过固定步长向前移动到训练集的末尾。如上图所示,桔色部分是长度为3的预测窗口。在每次迭代中,预测窗口会向前移动3个长度,同样训练集也会向后扩张三个长度。这个过程会持续到窗口移动到数据末尾。

在PaddleTS中,可以通过下面的方式实现回测。

In [78]

from paddlets.utils import backtestfrom paddlets.metrics import MAEmae, ts_pred = backtest(data=test_dataset,                model=lstm,                start=0.5,  # the point after "start" as the first point                metric=MAE(),                return_predicts=True                )

       

[2023-04-13 23:50:25,982] [paddlets.utils.backtest] [INFO] Parameter 'predict_window' not set, default set to model.out_chunk_len 172[2023-04-13 23:50:25,983] [paddlets.utils.backtest] [INFO] Parameter 'stride' not set, default set to predict_window 172Backtest Progress: 100%|██████████| 9/9 [00:00<00:00, 18.41it/s]

       In [79]

mae

       

{'ROUND(A.POWER,0)': 19261.904, 'YD15': 19188.62}

               

4.5 模型保存与提交说明

从赛题给的风机数据中我们可以发现,不同风机数据起始时间、预测日期均不一致。因此,选手需要逐个风机进行预测并生成提交文件。从另一个角度说,不同风机可以使用同一个时序模型、超参数,也可以使用不同的模型、超参数,选手可以自行设置。

提交方法:

保存每个风机的实际功率预测模型文件根据发布的评测脚本示例调整提交代码在评测区打包上传评测脚本

5 常见问题与解答

问题1:预测指标是什么?为什么会有ROUND(A.POWER,0)和YD15之分?

答:最终用于评估的预测指标只有YD15。ROUND(A.POWER,0)和YD15都是要预测的实际功率,只是二者在业务上计量口径不同。由于比赛使用的是风电企业真实生产过程中的数据,由于测量设备和网络传输问题,YD15可能出现数据异常(包括用于评测的输入数据)。此时业务上可以使用ROUND(A.POWER,0)作为替代;如果ROUND(A.POWER,0)也存在异常,则需要用PREPOWER字段进行替代。选手在设计方案时,需要考虑到这种情况。

问题2:PREPOWER字段的含义?与YD15的区别

答:PREPOWER是风机系统自己生成的预测功率。我们可以认为,是风电企业目前通过自有的算法模型生成的功率预测结果,而本次比赛,正是为了寻找比原有预测方案效果更好的模型。实际选手需要预测的是YD15。

问题3:如何定义YD15存在异常? 在本赛题中,YD15异常包括两种情况:

空值在一段时间内、其它字段正常变化时,YD15持续完全不变

除以上两种情况之外,YD15的数值变化都可认为是正常现象,如为0或负值。

6 提交说明

6.1 提交格式

测评数据的格式如下:

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

       

注意:存放模型参数的model文件夹、存放预测结果的文件夹pred的名称以及predict.py的文件命名应与要求一致。

最后将按照平均日准确率高低进行排名。

“中国软件杯”大学生软件设计大赛——龙源风电赛道比赛预测基线 - 创想鸟        

6.2 测评数据示例

为便于选手理解,本项目同时提供了测评数据示例,请选手查看项目的infile和outfile两个目录。说明如下:

infile中存放的是模型要预测的输入数据,outfile中的同名文件是对应的ground truth,测评时,会将选手在pred目录生成的同名文件与其比对outfile中”ROUND(A.POWER,0)”只是参考字段,不会影响测评结果infile中的TurbID代表要预测的风场ID,方便选手在选择模型时进行匹配选手要特别注意infile中给出的输入时间序列长度(已给定),避免运行时数据报错

在项目用Gradio实现PaddleTS时序预测全流程可视化:以风电预测任务为例中,给出了简易的模型测试demo,选手可以参照修改,仿照测评数据示例,自行进行线下测评。

以上就是“中国软件杯”大学生软件设计大赛——龙源风电赛道比赛预测基线的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/317030.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
在Linux系统上使用PyCharm进行深度学习的配置方法
上一篇 2025年11月5日 08:09:53
笔记本合上盖子再打开就黑屏无法唤醒
下一篇 2025年11月5日 08:10:08

相关推荐

  • composer require-dev和require有什么不同_Composer Require与Require-Dev区别解析

    require用于声明项目运行必需的依赖,如框架、数据库组件和第三方SDK,这些包会随项目部署到生产环境;2. require-dev用于声明仅在开发和测试阶段需要的工具,如PHPUnit、PHPStan、Faker等,不会默认部署到生产环境;3. 安装时composer install根据环境决定…

    2026年5月10日
    1000
  • Matplotlib 地图中多类型图例的创建与优化

    Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化

    本教程旨在解决matplotlib地图可视化中,如何在一个图例中同时展示颜色块(如区域分类)和自定义标记(如特定兴趣点)的问题。文章详细介绍了当传统`patch`对象无法正确显示标记时,如何利用`matplotlib.lines.line2d`创建标记图例句柄,并将其与颜色块图例句柄合并,从而生成一…

    2026年5月10日 用户投稿
    100
  • Golang JSON序列化:控制敏感字段暴露的最佳实践

    本教程探讨golang中如何高效控制结构体字段在json序列化时的可见性。当需要将包含敏感信息的结构体数组转换为json响应时,通过利用`encoding/json`包提供的结构体标签,特别是`json:”-“`,可以轻松实现对特定字段的忽略,从而避免敏感数据泄露,确保api…

    2026年5月10日
    000
  • 利用海象运算符简化条件赋值:Python教程与最佳实践

    本文旨在探讨Python中海象运算符(:=)在条件赋值场景下的应用。通过对比传统if/else语句与海象运算符,以及条件表达式,分析海象运算符在简化代码、提高可读性方面的优势与局限性。并通过具体示例,展示如何在列表推导式等场景下合理使用海象运算符,同时强调其潜在的复杂性及替代方案,帮助开发者更好地掌…

    2026年5月10日
    100
  • Debian syslog性能优化技巧有哪些

    提升Debian系统syslog (通常基于rsyslog)性能,关键在于精简配置和高效处理日志。以下策略能有效优化日志管理,提升系统整体性能: 精简配置,高效加载: 在rsyslog配置文件中,仅加载必要的输入、输出和解析模块。 使用全局指令设置日志级别和格式,避免不必要的处理。 自定义模板: 创…

    2026年5月10日
    000
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • c++中的SFINAE技术是什么_c++模板编程中的SFINAE原理与应用

    SFINAE 是“替换失败不是错误”的原则,指模板实例化时若参数替换导致错误,只要存在其他合法候选,编译器不报错而是继续重载决议。它用于条件启用模板、类型检测等场景,如通过 decltype 或 enable_if 控制函数重载,实现类型特征判断。尽管 C++20 引入 Concepts 简化了部分…

    2026年5月10日
    000
  • Go语言mgo查询构建:深入理解bson.M与日期范围查询的正确实践

    本文旨在解决go语言mgo库中构建复杂查询时,特别是涉及嵌套`bson.m`和日期范围筛选的常见错误。我们将深入剖析`bson.m`的类型特性,解释为何直接索引`interface{}`会导致“invalid operation”错误,并提供一种推荐的、结构清晰的代码重构方案,以确保查询条件能够正确…

    2026年5月10日
    100
  • RichHandler与Rich Progress集成:解决显示冲突的教程

    在使用rich库的`richhandler`进行日志输出并同时使用`progress`组件时,可能会遇到显示错乱或溢出问题。这通常是由于为`richhandler`和`progress`分别创建了独立的`console`实例导致的。解决方案是确保日志处理器和进度条组件共享同一个`console`实例…

    2026年5月10日
    000
  • 理解编程指令:当结果正确,但实现方式不符要求时

    本文探讨了在编程实践中,即使程序输出了正确的结果,但若其实现方式未能严格遵循既定指令,仍可能被视为“不正确”的问题。我们将通过具体示例,对比直接求和与累加求和两种实现策略,强调理解和遵守编程规范的重要性,以确保代码的健壮性、可维护性及符合项目要求。 在软件开发过程中,我们经常会遇到这样的情况:编写的…

    2026年5月10日
    000
  • Golang goroutine与channel调试技巧

    使用go run -race检测数据竞争,结合runtime.NumGoroutine监控协程数量,通过pprof分析阻塞调用栈,利用select超时避免永久阻塞,有效排查goroutine泄漏、死锁和数据竞争问题。 Go语言的goroutine和channel是并发编程的核心,但它们也带来了调试上…

    2026年5月10日
    000
  • 《魔兽世界》将于6月11日开启国服回归技术测试

    《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试

    《%ign%ignore_a_1%re_a_1%》官方宣布,将于6月11日开启国服回归技术测试,时间为7天,并称可以在6月内正式开服,玩家们可以访问官网下载战网客户端并预下载“巫妖王之怒”客户端,技术测试详情见下图。 WordAi WordAI是一个AI驱动的内容重写平台 53 查看详情 以上就是《…

    2026年5月10日 用户投稿
    200
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000
  • php常量怎么用_PHP常量(define/const)定义与使用方法

    PHP中可通过define函数和const关键字定义常量,用于存储不可变值。define适用于全局作用域,支持动态名称和条件定义,如define(‘SITE_NAME’, ‘MyWebsite’);const在编译时生效,语法简洁但限制多,只能在类或全…

    2026年5月10日
    000
  • 如何在HTML中插入表单元素_HTML表单控件与输入类型使用指南

    HTML表单通过标签构建,包含action和method属性定义数据提交目标与方式,常用input类型如text、password、email等适配不同输入需求,配合label、required、placeholder提升可用性,结合textarea、select、button等控件实现完整交互,是…

    2026年5月10日
    100
  • 网站标题关键词更新后,搜索引擎为何仍显示旧标题?

    网站标题更新后,搜索引擎为何显示旧标题? 网站SEO优化中,站长常修改网站标题关键词,期望搜索结果显示自定义标题。然而,即使更新标签、meta keywords、meta description和结构化数据中的name属性后,搜索结果仍显示旧标题,这令人费解。本文将对此进行解释。 问题:站长修改了网…

    2026年5月10日
    100
  • 创建指定大小并填充特定数据的Golang文件教程

    本文将介绍如何使用Golang创建一个指定大小的文件,并用特定数据填充它。我们将使用 `os` 包提供的函数来创建和截断文件,从而实现快速生成大文件的目的。示例代码展示了如何创建一个10MB的文件,并将其填充为全零数据。掌握这些方法,可以方便地在例如日志系统或磁盘队列等场景中,预先创建测试文件或初始…

    2026年5月10日
    000
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • 如何插入查询结果数据_SQL插入Select查询结果方法

    如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法

    使用INSERT INTO…SELECT语句可高效插入数据,通过NOT EXISTS、LEFT JOIN、MERGE语句或唯一约束避免重复;表结构不一致时可通过别名、类型转换、默认值或计算字段处理;结合存储过程可提升可维护性,支持参数化与动态SQL。 将查询结果数据插入到另一个表中,可以…

    2026年5月10日 用户投稿
    000
  • 使用 WebCodecs VideoDecoder 实现精确逐帧回退

    本文档旨在解决在使用 WebCodecs VideoDecoder 进行视频解码时,实现精确逐帧回退的问题。通过比较帧的时间戳与目标帧的时间戳,可以避免渲染中间帧,从而提高用户体验。本文将提供详细的解决方案和示例代码,帮助开发者实现精确的视频帧控制。 在使用 WebCodecs VideoDecod…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信