时间序列预测-Time Series Forecasting Problem Search

Time Series Forecasting

第二届世界科学智能大赛地球科学赛道-AI极端降水预报
深度学习建模, 我的思考

  1. 确定研究的问题,分析已有的数据
  2. 选择合适的方法,确定模型结构
  3. 训练过程中可能出现的问题以及对应的解决方案

我认为的难点:

  1. 首选确认好所需要用到的数据,哪些可以作为训练,哪些作为测试,并构建可以输入模型的数据结构:
    • 深度学习有相当一部分的工作量都在数据集的处理、分割等工作上(通过Task1的Baseline我也发现很多人在学习群里问有关数据的下载、数据存放、路径等问题)
    • 确定自己理想的数据集结构,定义处理函数(读取、分割、存储),转为Pytorch-DataLoader
    • 相信你自己,能够完美处理好数据基本上已经超过很多人了
  2. 能够创建数据结构之后,选择你认为合适的方法(算法)[LSTM, CNN, Transform-Family, …],搭建完整的模型架构(不要怕搭建的结构不合理,只要你的模型能够跑起来,完整输出预测结果,再去解决出现的问题)
  3. 将训练数据与测试数据输入模型当中开始训练,观察Loss变化(绘图),调整模型结构或方法,针对出现的问题不断完善模型结构

本文章代码来源于Datawhale-Task2:抽丝剥茧——降水预测baseline详解

首先,本次比赛是研究天气预测,给定的数据为:气象要素+对应时段的ERA5降水数据,具体变量名如下:

变量含义变量含义
z位势高度u10m10m风U分量
t温度v10m10m风V分量
q绝对湿度lcc低云量
u风速U分量mcc中云量
v风速V分量hcc高云量
t2m2m温度tcc总云量
d2m2m露点温度tp累积降水

其中tp为target。

Task2中,前一部分工作量集中在构建数据集上(给出的数据按照年份划分,并且里面的结构对于新手来说有点复杂);
接下来重点讲解EnhancedModel(第一个,长的那个):

class EnhancedModel(nn.Module):
    def __init__(self, num_in_ch, num_out_ch):
        super(EnhancedModel, self).__init__()
        self.conv1 = nn.Conv2d(num_in_ch, 64, kernel_size=3, padding=1)
        self.batchnorm = nn.BatchNorm2d(64)
        self.activation = nn.ReLU()
        self.conv2 = nn.Conv2d(64, 64, kernel_size=3, padding=1)
        self.conv3 = nn.Conv2d(64, 64, kernel_size=3, padding=1)
        self.conv4 = nn.Conv2d(64, num_out_ch, kernel_size=3, padding=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 = self.activation(out)
        out = self.conv2(out)
        out = self.activation(out)
        out = self.conv3(out)
        out = self.activation(out)
        out = self.conv4(out)
        out = self.activation(out)
        out = out.reshape(B, S, W, H)
        return out

相对来说比较简单的模型结构:卷积、批量标准化、激活函数

  1. 卷积层:Conv2d
    torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode=‘zeros’, device=None, dtype=None)
    本次用到的参数:
    in_channels: 输入维度
    out_channels: 输出维度
    kernel_size: 卷积核大小(整数或元组): 整数n代表卷积核大小为nn,元组(x,y)代表卷积核大小为xy
    stride: 卷积核下一次移动的步长(步长:假如数据shape为13*13,步长为1则代表每次卷积核移动一个数据的长度大小范围)
    padding: 在卷积核进行任何操作之前,数据填充的长度大小

  2. BatchNorm2d: 通过调整和缩放激活来规范化小批量的输入。有助于通过减少内部协变量位移来稳定和加快训练过程。
    一般输入的参数为上一层输出的维度大小

  3. ReLU激活函数
    引入非线性变换
    ReLU(x) = max(0,x)

forward函数更为简单,作用就是用以及搭建好的层结构处理输入数据,本次搭建的模型如下:

输入
卷积层1
ReLU
卷积层2
ReLU
卷积层3
ReLU
卷积层4
ReLU
Reshape
输出

搭建完成,输入超参数(上面模型当中涉及到的变量,给定值的大小)
定义损失函数(一堆,自己去选)

模型参数初始化

import torch.nn.init as init
def init_weights(m):
    if isinstance(m, nn.Conv2d):
        init.xavier_uniform_(m.weight)
        if m.bias is not None:
            init.constant_(m.bias, 0)
model.apply(init_weights)

这里是定义了一个初始化函数,因为Baseline模型只有Conv,这里面通过isinstance(m, nn.Conv2d)遍历上面定义的模型各个层结构(m代表当前层,nn.Conv2d代表2d卷积层)。如果当前层为nn.Conv2d则进行 init.xavier_uniform_1初始化参数(m.weight代表当前卷积层卷积核的具体数值),跳过ReLU激活层。
m.bias代表偏置(卷积层一般不要,特殊的我没见过)

  • 如果你自己添加了其他的层(比如:nn.Linear线性层),也可以在第一个if后添加else或elif-else语句(结合isinstance, m.weight)。

参数初始化完成后,设置优化器(optimizer,一般Adam效果就还可以),开始训练模型

num_epochs = 20
optimizer = torch.optim.Adam(model.parameters(), lr=0.0004, weight_decay=1e-6)

# for epoch in tqdm(range(num_epochs)):
os.makedirs('./model', exist_ok=True)
for epoch in range(num_epochs):
    model.train()
    loss = 0.0
    for index, (ft_item, gt_item) in enumerate(train_loader):
        ft_item = ft_item.cuda().float()
        gt_item = gt_item.cuda().float()
        # print("gt", gt_item.max(), gt_item.min())
        # Backward and optimize
        optimizer.zero_grad()
        # Forward pass
        output_item = model(ft_item)
        # print(output_item.max(), output_item.min())
        loss = loss_func(output_item, gt_item)
            
        loss.backward()
        optimizer.step()
            
        loss += loss.item()
        # 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():.6f}")
            loss = 0.0
    # Save the model weights
    torch.save(model.state_dict(), f'./model/model_weights_{epoch}.pth')
    model.eval()
    val_loss = 0.0
    with torch.no_grad():
        for index, (ft_item, gt_item) in enumerate(val_loader):
            ft_item = ft_item.cuda().float()
            gt_item = gt_item.cuda().float()
            output_item = model(ft_item)
            val_loss = loss_func(output_item.max(), gt_item.max())
            val_loss += val_loss.item()
    val_loss /= len(val_loader)
    print(f"[Epoch {epoch+1}/{num_epochs}], Validation Loss: {val_loss:.6f}")

print("Done!")

测试部分不再解释…

我认为本次比赛对于新手来说,最大的难点是数据集处理与模型输入输出的合理设置

  • 卷积层、线性层、激活层这些挑挑选选总能替换

  1. 参数初始化的一种方法(还有其他方法,可以在bing.com
    上搜索Pytorch卷积层初始化方法学习) ↩︎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值