【Datawhale AI 夏令营】AI极端降水预报赛道个人笔记

环境:

    1.Windows环境:Windows11
    2.运行环境:python-3.9.10
          numpy-1.23.0
          torch-1.10.0+cu102

一、ConvLSTM模型

1.为何用它

第一次看到这份baseline的时候,我很诧异,原来一个简单的卷积层就能做降水预报这样子高难度的事情。通过加深模型以及增多epoch轮数,比赛成绩切实有效地蹭蹭往上涨。然后,我开始想,能不能使用其他模型来做这个任务,而第一次直播课告诉了我这个答案——ConvLSTM模型。

2.模型简介

我上网看到了一篇博客,它让我知道了ConvLSTM模型提出的目的就是为了解决降水预报问题,同时,模型还可以解决其他时空序列的预测问题。ConvLSTM模型不仅像LSTM一样可以捕获时间特征,还可以像CNN一样捕获空间特征,并且作者通过实验证明了ConvLSTM在获取时空关系上比LSTM有更好的效果。

3.模型搭建

我仔细阅读了上面博客的代码,并结合chatGPT给出的示例代码,写了如下代码:

(1)ConvLSTMCell

import torch  
import torch.nn as nn

class ConvLSTMCell(nn.Module):
    def __init__(self, input_dim, hidden_dim, kernel_size, bias=True):
        super(ConvLSTMCell, self).__init__()
        self.input_dim = input_dim
        self.hidden_dim = hidden_dim  
        self.kernel_size = kernel_size
        self.bias = bias
        self.conv = nn.Conv2d(in_channels=input_dim+hidden_dim,
                              out_channels=4*hidden_dim,
                              kernel_size=kernel_size,
                              padding=kernel_size//2,
                              bias=bias)

    def forward(self, x, h, c):  
        combined = torch.cat((x, h), dim=1)
        combined_conv = self.conv(combined)
        cc_i, cc_f, cc_o, cc_g = torch.split(combined_conv, self.hidden_dim, dim=1)
        
        # 计算门
        i = torch.sigmoid(cc_i)  # 输入门
        f = torch.sigmoid(cc_f)  # 遗忘门
        o = torch.sigmoid(cc_o)  # 输出门
        g = torch.tanh(cc_g)     # 单元状态
        
        c_next = f * c + i * g
        h_next = o * torch.tanh(c_next)  
        return h_next, c_next  

(2)ConvLSTM

class ConvLSTM(nn.Module):  
    def __init__(self, input_dim, hidden_dim, kernel_size, num_layers, bias=True):  
        super(ConvLSTM, self).__init__()
        self.layers = nn.ModuleList()  

        for i in range(num_layers):
            input_size = input_dim if i == 0 else hidden_dim
            cell = ConvLSTMCell(input_dim=input_size, hidden_dim=hidden_dim, kernel_size=kernel_size, bias=bias)
            self.layers.append(cell)
    
    def forward(self, x):
        # Get size
        batch_size, time_steps, channels, height, width = x.size()
        # Init hidden
        h, c = [None] * len(self.layers), [None] * len(self.layers)
        for i, layer in enumerate(self.layers):
            h[i], c[i] = (torch.zeros(batch_size, layer.hidden_dim, height, width).to(x.device),  
                          torch.zeros(batch_size, layer.hidden_dim, height, width).to(x.device))
        # Init input data
        layer_output_list = []
        current_layer_input = x
        for i, layer in enumerate(self.layers):  
            current_layer_output = []
            for t in range(time_steps):
                h[i], c[i] = layer(current_layer_input[:, t, :, :, :], h[i], c[i])
                current_layer_output.append(h[i])
            # Output data → Next input data
            current_layer_input = torch.stack(current_layer_output, dim=1)
            layer_output_list.append(current_layer_input)
        # Return last layer output
        return layer_output_list[-1]

(3)ConvLSTMModel

class ConvLSTMModel(nn.Module):  
    def __init__(self, input_dim, hidden_dim, kernel_size, num_layers, time_steps, output_dim):  
        super(ConvLSTMModel, self).__init__()
        self.convlstm = ConvLSTM(input_dim, hidden_dim, kernel_size, num_layers)  
        self.conv = nn.Conv2d(time_steps*hidden_dim, time_steps*output_dim, 3, 1, 1)

    def forward(self, x):  
        last_output = self.convlstm(x)
        batch_size, time_steps, hidden_dim, height, width = tuple(last_output.shape)
        last_output = last_output.reshape(batch_size, -1, height, width)
        output = self.conv(last_output)
        output = output.reshape(batch_size, time_steps, -1, height, width)
        return output

(4)训练模型

# 使用示例
input_shape = (10, 5, 3, 64, 64)
X_train = torch.rand(input_shape).float()
y_train = torch.randn(10, 5, 1, 64, 64)

# 创建模型
model = ConvLSTMModel(input_dim=3, hidden_dim=16, kernel_size=3, num_layers=2, time_steps=5, output_dim=1)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# 训练模型
model.train()
num_epochs = 100
for epoch in range(num_epochs):  
    optimizer.zero_grad()
    outputs = model(X_train)
    loss = criterion(outputs, y_train)  
    loss.backward()  
    optimizer.step()  
    print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')

二、经历感悟

通过参加比赛,让我真真切切学习到了课本之外的内容,听大佬的解说,更加深入地理解五个上分思路(数据、模型、损失函数、训练方式、超参数)的做法。
对于数据,可以采用更多的数据,以及不使用全部的特征。
对于模型,可以思考如何加深模型,但是加深模型并不一定会带来效果提升。另外,比赛禁止预训练模型,但可以使用预训练模型结构,再随机初始化参数。
对于损失函数,并非只能使用传统的MSE,还可以使用SmoothL1Loss()替代。另外,赛事评测使用的是CSI指标,可以自己编写损失函数去接近原有的任务。
对于训练方式,可以将所有数据都作为训练数据,也可以划分训练集和验证集,然后选择验证损失最小的模型。

三、参考文献

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值