#Datawhale AI 夏令营#AI+大气科学(极端天气预测赛事)学习笔记

前言

ok,这是datawhale夏令营的一部分,这个赛题来源于第二届世界科学智能大赛地球科学赛道:AI极端降水预报。

本次大赛考察选手基于业界领先的伏羲气象大模型输出,进一步优化和提升未来三天的极端降水预测。赛事提供历史时段伏羲气象大模型未来72小时逐小时的多个气象要素以及对应时段的ERA5降水数据,输出给定时段的基于伏羲气象大模型要素作为输入的AI极端降水预测。

赛事数据

赛事提供的数据如下,除此之外不允许使用其他数据。

我们可以对提供的数据进行简单的划分。1:gt数据,也就是标签,也就是比赛的目标数据(数据来自欧洲中期天气预报中心(ECMWF)的再分析数据ERA5),赛事组织方提供了2019-2021三年的gt数据,用来训练;2.ft数据,训练数据,这是伏羲大模型生成的特征数据,同样提供了2019-2021三年的数据,数据量比较大,解压前就有42G,这些数据可以供我们来训练模型,ft和gt的数据是按照时间戳一一对应的;3.test数据,这部分是没有标签的,是供我们来进行推理的,推理的结果我们存成一个zip文件,用来提供。

赛题简单分析

参考赛事方提供的背景,初步研读下来,我们可以得出,这是一个时间序列预测问题,是一个回归问题。我们需要使用历史的天气数据来预测未来72小时,是否会发生极端降水。通俗来说, 时间序列预测模型目的构建一个全时段可用的时间序列函数, 根据输入可以精准的得到目的的输出, 比如本次赛题中的降水预测模型。

baseline搭建

对赛题初步分析后,我们已经明确了这个赛事的目标是什么,这个问题应该如何去解决。

首先我们需要搭建一个baseline出来,对数据进行简单的梳理,把竞赛的整个流程跑通。baseline有助于我们更好的了解问题和数据,可以检验我们流程,检查数据,以便后续在baseline的基础上进行进一步的挖掘,提升优化上分。这是baseline的作用,也是我们进行竞赛,一般拿到赛题后,要尽快完成的第一件事。baseline也是后续我们比较模型性能的一个基准。通常baseline都会相对简单一些,方便我们快速跑通。

Datawhale已经帮我们搭建好了baseline了,那么接下来我就在此基础上,简单的分享一下baseline的思路。

数据集准备和处理

本次赛事,我们使用的魔搭社区的Studio,我们可以开一个GPU机器,新手是有100小时的免费时长的,当然我们也可以用本地机器来跑,如果是游戏本的话,建议配置RTX4060以上。

开一个新机器,首先我们需要安装必备的库。这里我们需要使用xarray库。

xarray是一个 Python 库,用于处理和分析多维数组数据。它构建在 NumPy 和 pandas 之上,并为多维数据提供了更高级别的接口。xarray特别适用于气象学、海洋学以及其他涉及多维数据集的领域,比如那些通常存储在 NetCDF 文件中的数据。

!pip install xarray[complete]
!apt update&&apt install axel 

我们需要下载所需要的数据集,因为是baseline,所以我们只选用了一部分数据。我们将数据进行解压到对应的文件,加上Feature(ft)数据,那么我们所需要的feature数据,groundtruth数据,test数据就齐活了。baseline中,我们只使用了2021年的部分数据。

!axel -n 5 -o weather.round1.train.gt.2019-2021.zip 'https://tianchi-race-prod-sh.oss-cn-shanghai.aliyuncs.com/file/race/documents/532234/bigFile/weather.round1.train.gt.2019-2021.zip?Expires=1721991780&OSSAccessKeyId=LTAI5t7fj2oKqzKgLGz6kGQc&Signature=B14IJ7j7JAzy2i2K1XGSVNtwGvs%3D&response-content-disposition=attachment%3B%20'
!axel -n 5 -o weather.round1.test.zip 'https://tianchi-race-prod-sh.oss-cn-shanghai.aliyuncs.com/file/race/documents/532234/bigFile/weather.round1.test.zip?Expires=1721991827&OSSAccessKeyId=LTAI5t7fj2oKqzKgLGz6kGQc&Signature=1XtjX3ljMwfDFiPKLKIkm7FCMRM%3D&response-content-disposition=attachment%3B%20'
!unzip -q -n weather.round1.train.gt.2019-2021.zip -d groundtruth
!unzip -q -n weather.round1.test.zip -d test

比赛的数据分为数据特征(feature数据)和数据真值(groundtruth),这两者就是我们需要的训练数据和标签。他们分别存在feature和groundtruth文件夹中,我们导入所需的库,并配置好数据集的路径。

import os
import pandas as pd
import xarray as xr
from torch.utils.data import Dataset, DataLoader
# path config
feature_path = 'feature' #自定义路径并修改为自己的路径
gt_path = 'groundtruth' #自定义路径并修改为自己的路径
years = ['2021']
fcst_steps = list(range(1, 73, 1))

我们需要定义Feature类和GT类(groundtruth) ,定义好数据集,以方便后续的操作。

# Feature部分
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))

接下来我们定义数据加载器,我们使用PyTorch框架。

# 构建Dataset部分
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))

 我们可以打印查看我们的数据数量。然后dataload.

# 可以查看一下已经构建的dataset
# define dataset
my_data = mydataset()
print('sample num:', mydataset().__len__())
train_loader = DataLoader(my_data, batch_size=1, shuffle=True)

这样数据的加载和预处理部分就完成了,baseline其实没有对这一块进行过多的深度探讨,这一块是有很多的信息供我们后续挖掘的,有人说,一个好的数据科学家80%的时间都是在处理数据,可以看出数据的价值,数据对一个深度学习模型的重要性,这是大厦的地基,是我们进行一切炼丹的基础。

模型定义

数据部分处理完了,我们开始定义我们的模型,作为一个时间序列问题,我们是有很多模型来进行选择的,baseline采用了一维卷积来处理,而且只是一层的卷积网络。我们可以利用PyTorch框架来搭建模型,定义好Class Model。

直接上代码:

# 模型构建部分
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

# define model
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()

模型训练

首先定义损失函数:

回归问题,我们可以使用MSE来评估结果。MSE(Mean Squared Error,均方误差)是一种常用的损失函数,尤其在回归任务中被广泛应用。MSE 的计算方式是将预测值与真实值之间的差的平方求和,然后除以样本数量,得到的均值就是 MSE 的值。

# define loss
loss_func = nn.MSELoss()

接下来训练模型,我们需要定义好num_epochs,lr,优化器等等,然后train吧,train完保存成.pth格式。.pth格式在PyTorch里面通常用于保存神经网络模型的权重和偏置等参数。这个我们后续推理的时候要用到。

import numpy as np
import torch
# from tqdm import tqdm
# Train the model
num_epochs = 1
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# for epoch in tqdm(range(num_epochs)):
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))
        
        # Forward pass
        output_item = model(ft_item)
        loss = loss_func(output_item, gt_item)
        
        # Backward and optimize
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        # Print the loss for every 10 steps
        if (index+1) % 10 == 0:
            print(f"Epoch [{epoch+1}/{num_epochs}], Step [{index+1}/{len(train_loader)}], Loss: {loss.item():.4f}")

# Save the model weights
torch.save(model.state_dict(), 'model_weights.pth')

模型推理(预测)

接下来就是模型推理环节啦,这里我们需要用到test数据作为输入,加载之前训练好的模型,进行预测,来上代码:

# Inference
# Load the model weights
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()
    
    # Forward pass
    output_item = model(test_data)
    
    # Print the output shape
    print(f"Output shape for sample {test_data_file.split('.')[0]}: {output_item.shape}")
    
    # Save the output
    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"))

好了,到这里,我们的baseline就跑完啦。把输出结果打包压缩,就可以上传啦,会得到我们的第一个分数哦。撒花。。。

!zip -r output.zip output

小结

做一个小结吧,一般拿到一个竞赛题,我们需要进行赛题分析,然后搭建baseline,通常baseline包括1)数据集准备和处理;2)模型定义;3)模型训练;4)模型预测,这几个流程。baseline相对比较简单,后续我们有很大的优化空间。优化的思路也可以从baseline的这几个流程着手。

一些优化思路

使用更多的数据;使用更深的模型;使用不同的模型架构;调整超参数;特征工程;模型融合等。

跑baseline过程中,遇到的一些问题以及解决:

1)第一次跑完baseline,得分为0,程序没有任何报错。没有深究原因。第二次我用完整的数据跑了一遍,分数可以达到0.1左右;

2)用完整数据跑的过程中,train的时候有一个KeyError报错,原因是因为部分gt数据缺失,没有对应的值,后来参考了baseline进阶版,修改了这一块的程序,解决;

3)我用本地机器跑的,是WINDOWS系统,一开始程序压缩后的结果,最后提交文件的时候,提交失败,"bad error file",我就自己手动把output文件压缩完,再提交,解决。

看完了的小伙伴,觉得有用的话,就请点赞关注收藏吧。

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值