基于降水预测模型的极端降水预报 ---Datawhale AI夏令营

204 阅读6分钟

这次比赛基于伏羲气象大模型,主要是为了进一步优化和提升未来三天的极端降水预测,并以一个数据维度为1x72xHxW(其中72对应未来72个小时预测,H为纬向网格数,W为经向网格数),数据类型为float16,存储为pt格式,每个pt文件组成最后的.zip文件进行提交

Task1:跑通baseline

step1:速通baseline指南

按照datawhale官方文档(linklearner.com/activity/12…)并选择降水预测页面即可找到教程,对着教程就能速通

------数据准备

在使用预测模型前需要先完成数据准备工作,确保模型能正确载入数据

step2:准备数据集并解压

!pip install xarray[complete]
!unzip -q -n weather.round1.train.gt.2019-2021.zip -d groundtruth
!unzip -q -n weather.round1.test.zip -d test

step3:导入库函数并构建数据集路径

可能在fcst_steps这里会看不懂,官方解释是指预测的时间步长, 从第1小时到第72小时, 间隔为1小时

import os
import pandas as pd
import xarray as xr
from torch.utils.data import Dataset, DataLoader
​
feature_path = 'feature'
gt_path = 'groundtruth'
years = ['2021']
fcst_steps = list(range(1, 73, 1))

step4:创建数据集类

分别创建了两个数据类,方便后续存取,Feature类用于处理特征数据,GT 类用于处理地面数据

class Feature:
    def __init__(self):
        self.path = feature_path
        self.years = years
        self.fcst_steps = fcst_steps
        self.features_paths_dict = self.get_features_paths()
​
    def get_features_paths(self):
        init_time_path_dict = {}
        for year in self.years:
            init_time_dir_year = os.listdir(os.path.join(self.path, year))
            for init_time in sorted(init_time_dir_year):
                init_time_path_dict[pd.to_datetime(init_time)] = os.path.join(self.path, year, init_time)
        return init_time_path_dict
​
    def get_fts(self, init_time):
        return xr.open_mfdataset(self.features_paths_dict.get(init_time) + '/*').sel(lead_time=self.fcst_steps).isel(
            time=0)
    
# GroundTruth部分
class GT:
    def __init__(self):
        self.path = gt_path
        self.years = years
        self.fcst_steps = fcst_steps
        self.gt_paths = [os.path.join(self.path, f'{year}.nc') for year in self.years]
        self.gts = xr.open_mfdataset(self.gt_paths)
​
    def parser_gt_timestamps(self, init_time):
        return [init_time + pd.Timedelta(f'{fcst_step}h') for fcst_step in self.fcst_steps]
​
    def get_gts(self, init_time):
​
        return self.gts.sel(time=self.parser_gt_timestamps(init_time))

step5:定义数据读取类并加载数据

编写mydataset类通过创建 Feature和 GT 类的实例,分别用于获取特征数据和地面真实数据,并能返回数据集中初始化时间的数量,

然后实例化mydataset类来查看数据数量并通过DataLoader来查看数据集的数据

class mydataset(Dataset):
    def __init__(self):
        self.ft = Feature()
        self.gt = GT()
        self.features_paths_dict = self.ft.features_paths_dict
        self.init_times = list(self.features_paths_dict.keys())
​
    def __getitem__(self, index):
        init_time = self.init_times[index]
        ft_item = self.ft.get_fts(init_time).to_array().isel(variable=0).values
        print(type(ft_item))
        gt_item = self.gt.get_gts(init_time).to_array().isel(variable=0).values
        print(type(gt_item))
        return ft_item, gt_item
​
    def __len__(self):
        return len(list(self.init_times))
        
my_data = mydataset()
print('sample num:', mydataset().__len__())
train_loader = DataLoader(my_data, batch_size=1, shuffle=True)

--------模型搭建并进行训练

完成了数据集的准备接着我们需要对降水预测模型进行搭建及数据载入训练

step6:模型搭建

通过构建包含一个卷积层的简单神经网络并以mse函数为损失函数以完成降水预测模型

import torch.nn as nn
​
class Model(nn.Module):
    def __init__(self, num_in_ch, num_out_ch):
        super(Model, self).__init__()
        self.conv1 = nn.Conv2d(num_in_ch, num_out_ch, 3, 1, 1)
​
    def forward(self, x):
        B, S, C, W, H = tuple(x.shape)
        x = x.reshape(B, -1, W, H)
        out = self.conv1(x)
        out = out.reshape(B, S, W, H)
        return out
​
in_varibales = 24
in_times = len(fcst_steps)
out_varibales = 1
out_times = len(fcst_steps)
input_size = in_times * in_varibales
output_size = out_times * out_varibales
model = Model(input_size, output_size).cuda()
loss_func = nn.MSELoss()

step7:模型训练

将模型优化器设置为Adam并把步长设置0.001,并进行一次迭代训练

import numpy as np
import torch
num_epochs = 1
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
​
for epoch in range(num_epochs):
    for index, (ft_item, gt_item) in enumerate(train_loader):
        ft_item = ft_item.cuda().float()
        gt_item = gt_item.cuda().float()
        print(type(ft_item))
        print(type(gt_item))
        
        # 前向传播
        output_item = model(ft_item)
        loss = loss_func(output_item, gt_item)
        
        # 反向传播并进行优化器优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        #每十步输出一次损失
        if (index+1) % 10 == 0:
            print(f"Epoch [{epoch+1}/{num_epochs}], Step [{index+1}/{len(train_loader)}], Loss: {loss.item():.4f}")
step8:模型保存

把模型保存下来,模型文件名为model_weights.pth

torch.save(model.state_dict(), 'model_weights.pth')

--------训练完成后保存输出预测数据

step9:模型预测输出

加载一个预训练的模型,使用这个模型对一组测试数据进行预测,并将预测结果保存到文件中,最后将这些结果压缩成一个zip文件。然后通过提交这个zip文件即可查看自己的分数啦

model.load_state_dict(torch.load('model_weights.pth'))
model.eval()
import os
​
test_data_path = "test/weather.round1.test"
os.makedirs("./output", exist_ok=True)
for index, test_data_file in enumerate(os.listdir(test_data_path)):
    test_data = torch.load(os.path.join(test_data_path, test_data_file))
    test_data = test_data.cuda().float()
    
    output_item = model(test_data)
    
    print(f"Output shape for sample {test_data_file.split('.')[0]}: {output_item.shape}")
    
    output_path = f"output/{test_data_file}"
    torch.save(output_item.cpu(), output_path)
    # Load the model weights
    model.load_state_dict(torch.load("model_weights.pth"))
!zip -r output.zip output

Task2:从baseline入门时间序列分析任务

从数据分析和机器学习到时间序列分析
  • 理解数据特性

    • 数据分析的第一步是理解数据的基本特性,包括数据的分布、趋势、季节性等。
    • 时间序列数据具有时间依赖性,即数据点之间的顺序和时间间隔对分析结果有重要影响。
  • 数据预处理

    • 在机器学习中,数据预处理包括清洗、标准化和特征选择。
    • 时间序列分析中,数据预处理还可能包括处理缺失值、异常值、数据平滑和去趋势化。
  • 特征工程

    • 机器学习中的特征工程侧重于从现有数据中构造新特征,以提高模型的预测能力。
    • 时间序列分析中,特征工程可能包括创建滞后特征、滚动统计特征、时间窗口特征等。
  • 选择合适的模型

    • 机器学习模型如线性回归和决策树适用于静态数据集,但可能不足以捕捉时间序列数据的动态特性。
    • 时间序列分析通常需要使用能够处理时间依赖性的模型,如ARIMA、季节性ARIMA(SARIMA)、指数平滑等。
  • 时间序列特有的模型

    • 除了传统的统计模型外,深度学习模型如循环神经网络(RNN)、长短期记忆网络(LSTM)和门控循环单元(GRU)也被广泛应用于时间序列预测。
    • Transformer模型由于其捕捉长距离依赖的能力,在时间序列预测中也显示出了巨大潜力。
  • 模型评估

    • 在机器学习中,模型评估通常使用准确率、召回率、F1分数等指标。
    • 时间序列分析中,模型评估更侧重于预测的准确性,常用的评估指标包括均方误差(MSE)、均方根误差(RMSE)和平均绝对误差(MAE)。

参数修改尝试:

  • 初始

    • 数据 2021年
    • epoch 1
    • optimizer Adam 0.001
    • score 0.0013
  • 增大epoch及增大步长

    • 数据 2021年
    • epoch 1
    • optimizer Adam 0.01
    • score 0.0171
  • 增大epoch、增大步长及增加优化器

    • 数据 2021年
    • epoch 10
    • optimizer Adam 0.01
    • 每四次迭代减小步长为之前的0.85
    • score 0.0202
  • 加深网络层数

    • 数据 2021年
    • 增加为三个卷积层
    • epoch 10
    • optimizer Adam 0.001
    • 每四次迭代减小步长为之前的0.85
    • score 0.0205

遇到的问题:

1、通道数不匹配

将最后一层卷积层的输出转化成output_size,同时如果具有池化层还需计算池化后的通道数

2、提交答案分数为零

进行数据清洗,把错误数据清洗出去