Task1:赛事初探——降水预测模型搭建过程概述
原任务地址:https://datawhaler.feishu.cn/wiki/OdXjwwUNYifP76ktaBLcPYPonJc
天气预报和降水预测听起来像是高难度的技术活,但其实在人工智能时代,这些任务已经变得相对容易。通过收集大量的气象数据,比如温度、湿度、气压、风速等,我们可以使用数据分析和深度学习技术来构建预测模型。
具体来说,首先需要收集和整理历史气象数据,然后通过数据预处理,比如清洗、归一化等步骤,让数据更适合模型训练。接下来,选择合适的深度学习模型,比如卷积神经网络(CNN)、循环神经网络(RNN)或者长短期记忆网络(LSTM)等,来学习数据中的模式和规律。通过训练,模型能够捕捉到气象数据之间的复杂关系,并预测未来的天气变化。
Part1 什么是机器学习, 什么是深度学习?
机器学习
机器学习(Machine Learning)是一种使计算机系统利用数据来提高性能的技术。它允许计算机通过经验来学习,而无需进行显式的编程。机器学习通常包括以下几个步骤:
- 数据收集:收集用于训练模型的数据。
- 数据预处理:清洗、转换和规范化数据,以便于模型处理。
- 特征选择:确定哪些数据特征对于预测任务最为重要。
- 模型选择:选择一个或多个算法来训练数据。
- 模型训练:使用算法对数据进行训练,以识别数据中的模式和关系。
- 模型评估:评估模型的性能,确保其准确性和泛化能力。
- 模型部署:将训练好的模型应用于实际问题中。
机器学习算法可以分为几种类型,包括监督学习、无监督学习、半监督学习和强化学习。
机器学习的应用领域:
- 医疗健康:在疾病诊断、药物发现、患者数据预测等方面有广泛应用。
- 金融科技:用于风险管理、欺诈检测、算法交易等。
- 自然语言处理:机器翻译、情感分析、语音识别等。
- 图像识别:用于面部识别、医学成像分析、自动驾驶车辆的视觉系统等。
- 推荐系统:电商网站、视频流媒体服务等使用机器学习来推荐商品或内容
机器学习的关键算法:
- 线性回归:用于预测连续值输出。
- 逻辑回归:用于分类问题,尤其是二分类问题。
- 决策树:用于分类和回归问题,通过学习简单的决策规则从数据特征中推断目标值。
- 随机森林:集成学习方法,构建多个决策树并将它们的预测结果结合起来。
- 支持向量机(SVM):在特征空间中找到最优的分割超平面。
- K最近邻(KNN):根据测试数据点的K个最近邻居来进行分类或回归。
深度学习
深度学习(Deep Learning)是机器学习的一个子集,它使用一种称为人工神经网络的算法,特别是那些具有多个隐藏层的深层网络。深度学习模型能够学习数据中的复杂模式和高级特征,这使得它们在图像和语音识别、自然语言处理等领域表现出色。深度学习的关键特点包括:
- 多层结构:模型由多个层次的神经元组成,能够学习从简单到复杂的特征。
- 自动特征提取:与机器学习中的一些方法不同,深度学习模型能够自动从原始数据中提取特征,无需人工干预。
- 大数据需求:为了训练深度学习模型,通常需要大量的数据。
深度学习的应用领域:
- 计算机视觉:图像分类、目标检测、图像分割等。
- 自然语言处理:语言翻译、情感分析、文本生成等。
- 语音识别:将语音转换为文本,用于智能助手和自动字幕生成。
- 强化学习:在游戏、机器人控制等领域,通过与环境的交互学习最优策略。
- 生物信息学:蛋白质结构预测、基因表达分析等。
深度学习的架构:
- 卷积神经网络(CNN):特别适用于图像识别任务,能够自动学习图像中的空间层次结构。
- 循环神经网络(RNN):适合处理序列数据,如时间序列分析、自然语言处理等。
- 长短期记忆网络(LSTM):一种特殊的RNN,能够学习长期依赖关系,常用于语言模型和机器翻译。
- 生成对抗网络(GAN):由生成器和判别器组成,通过对抗过程生成新的、逼真的数据样本。
- Transformer:基于自注意力机制的模型,广泛应用于自然语言处理任务,如机器翻译、文本摘要等。
总的来说,机器学习是让计算机通过数据学习并做出智能决策的一系列技术和方法,而深度学习是利用深层神经网络来实现更复杂学习任务的特定类型的机器学习。两者都推动了人工智能的发展,并在多个领域中发挥着重要作用。
Part2 什么是pytorch?
PyTorch是一个广泛使用的开源机器学习库,它为深度学习提供了强大的支持。这个库以其动态计算图而闻名,这意味着在执行过程中可以随时调整计算流程,这为模型的快速迭代和调试提供了极大的便利。PyTorch的API设计简洁,易于理解,使得即使是初学者也能快速上手。同时,它支持广泛的深度学习架构,包括卷积神经网络、循环神经网络等,适用于图像识别、自然语言处理等多种应用场景。
此外,PyTorch内置了对NVIDIA CUDA的深度集成,使得在GPU上进行模型训练和推理变得非常高效,大大加快了计算速度。PyTorch还拥有一个活跃的开发者社区,提供了大量的预训练模型和工具,这为研究人员和开发者提供了丰富的资源,促进了知识的共享和技术的创新。无论是在学术研究还是工业应用中,PyTorch都因其灵活性和易用性而受到青睐,成为推动深度学习发展的重要力量。
PyTorch还特别注重从研究到生产环境的过渡,提供了模型序列化、优化和部署的工具,使得研究成果能够更容易地转化为实际应用。总的来说,PyTorch是一个功能全面、性能优异的深度学习框架,它为机器学习领域的研究者和实践者提供了一个强大的平台,以实现和部署复杂的人工智能模型。
Part3 赛题初探
赛事链接:http://competition.sais.com.cn/competitionDetail/532234/competitionData
这个赛事是一个专注于极端降水预测的挑战,要求参赛者利用人工智能技术对即将到来的降水事件进行准确预测。
-
数据集:赛事提供了两个主要的数据集,ERA5和FuXi。ERA5数据集是再分析数据,提供了地面和大气的多个气象变量,而FuXi数据集则是由复旦大学开发的,专注于气象要素的预测模型。
-
数据特性:这些数据集具有高时间分辨率(每小时)和空间分辨率(0.25度),覆盖了特定的地理区域,这为参赛者提供了丰富的数据来进行模型训练和测试。
-
气象要素:FuXi数据集包含了多种气象要素,如位势高度、温度、风速分量、绝对湿度、2米温度和露点温度、风速分量、云量以及累积降水等,这些都是预测降水的关键变量。
-
预报时长:参赛者需要预测未来72小时内的气象情况,这要求模型不仅要捕捉短期的气象变化,还要能够预测中期的气象趋势。
通过对这些数据集的深入分析和理解,参赛者可以构建出能够准确预测极端降水事件的模型。这不仅需要对气象学有深刻的认识,还需要运用先进的数据科学技术,包括机器学习和深度学习算法,来处理和分析这些复杂的数据。通过这样的赛事,可以推动气象预测技术的发展,提高对未来极端天气事件的预测能力。
baseline解读
Dataset类的定义
# 构建Dataset部分
class mydataset(Dataset):
"""
定义了一个名为mydataset的类,继承自Dataset。该类用于加载和处理特征(ft)和标签(gt)数据。
"""
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):
"""
返回init_times列表的长度,即初始化时间戳的数量
"""
return len(list(self.init_times))
这段代码是一个自定义的Dataset
类,用于数据加载和处理,通常用于机器学习和深度学习框架中,如PyTorch。下面是对代码的逐行分析:
-
通过
__init__
方法初始化,创建了两个实例:self.ft
用于获取特征数据,self.gt
用于获取目标(标签)数据。这些实例是从Feature
和GT
类创建的。 -
self.features_paths_dict
是一个字典,存储了特征数据的路径,而self.init_times
是一个包含初始化时间戳的列表,这些时间戳是从字典的键中提取出来的。 -
__getitem__
方法允许通过索引访问数据集中的单个项目。给定一个索引index
,它会从self.ft
和self.gt
对象中检索对应初始化时间戳的特定数据。 -
在
__getitem__
方法中,特征数据和目标数据分别被打印出其类型,这有助于调试,确保数据以预期的格式被加载。 -
__len__
方法返回数据集中项目的数量,这是基于初始化时间戳的数量。在机器学习框架中,这个方法通常被用来确定训练循环中的迭代次数。
这个mydataset
类的主要作用是封装了特征和目标数据的加载逻辑,使得在训练机器学习模型时可以方便地获取数据。通过继承Dataset
类,它可能被用于与深度学习框架(如PyTorch)配合使用,以实现数据的批量加载和预处理。
# Feature部分
class Feature:
"""
定义了一个名为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)中,最后返回该字典。
"""
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):
"""
根据给定的初始化时间(init_time),从特征路径字典(features_paths_dict)中获取对应路径,然后使用xr.open_mfdataset函数打开该路径下的所有数据集,并根据预测步数(fcst_steps)和时间维度(time=0)进行数据选择和筛选,最后返回所选数据。
"""
return xr.open_mfdataset(self.features_paths_dict.get(init_time) + '/*').sel(lead_time=self.fcst_steps).isel(
time=0)
# GroundTruth部分
class GT:
"""
定义了一个名为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):
"""
接收一个初始时间init_time,并根据fcst_steps生成了对应的一系列时间戳。
"""
return [init_time + pd.Timedelta(f'{fcst_step}h') for fcst_step in self.fcst_steps]
def get_gts(self, init_time):
"""
接收一个初始时间init_time,利用parser_gt_timestamps方法生成对应的时间戳列表,并通过self.gts.sel(time=...)选择了gts中对应时间的数据。最后返回所选数据。
"""
return self.gts.sel(time=self.parser_gt_timestamps(init_time))
这段代码定义了两个类,Feature
和GT
,它们分别用于处理特征数据和地面真实数据(Ground Truth,即GT)。以下是对这两个类的连续分析:
Feature类
-
Feature
类负责管理特定路径下的特征数据,这些数据可能是气象数据,用于机器学习或深度学习模型的训练和预测。 -
在
__init__
方法中,类接收路径feature_path
、年份列表years
和预测步数fcst_steps
作为参数,并存储在实例变量中。然后调用get_features_paths
方法来构建特征数据路径的字典。 -
get_features_paths
方法遍历指定路径下每个年份的文件夹,对每个年份的初始化时间目录进行排序,并将每个初始化时间对应的路径存储到字典init_time_path_dict
中。 -
get_fts
方法根据给定的初始化时间init_time
,从特征路径字典中获取对应路径,并使用xr.open_mfdataset
函数打开该路径下的所有数据集。然后根据预测步数fcst_steps
和时间维度(time=0)进行数据选择和筛选,最后返回所选数据。
GT类
-
GT
类用于处理一系列时间序列数据,这些数据可能代表实际观测到的气象数据,用于作为模型训练的标签。 -
在
__init__
方法中,类接收路径gt_path
、年份列表years
和预测步数fcst_steps
作为参数,并存储在实例变量中。然后构建每个年份对应的数据路径列表,并使用xr.open_mfdataset
函数打开所有数据集,存储在gts
实例变量中。 -
parser_gt_timestamps
方法接收一个初始时间init_time
,并根据fcst_steps
生成一系列时间戳,这些时间戳用于后续的数据选择。 -
get_gts
方法接收一个初始时间init_time
,利用parser_gt_timestamps
方法生成对应的时间戳列表,并通过self.gts.sel(time=...)
选择了gts
中对应时间的数据,最后返回所选数据。
这两个类都使用了xarray
库来处理多维数据集,这是一个常用于气象和地球科学数据的Python库。Feature
类和GT
类通过组织和管理数据路径、读取和筛选数据,为机器学习模型提供了数据输入。这些类的设计允许灵活地访问和处理不同时间点和年份的数据,这对于时间序列预测任务非常重要。通过这种方式,可以有效地将数据加载到机器学习模型中,进行训练和预测。
模型搭建
# 模型构建部分
import torch.nn as nn
class Model(nn.Module):
"""
定义了一个继承自nn.Module的Model类,用于构建深度学习模型
"""
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 = x.reshape(B, -1, W, H) # 将输入x转换为形状为(B, S, W, H)的格式
out = self.conv1(x) # 将x通过卷积层进行计算(3×3的卷积核)
out = out.reshape(B, S, W, H) # 将输出out转换为形状为(B, S, W, H)的格式
return out
# define model
# 定义输入变量的数量,这里假设为24
in_varibales = 24
# 获取预报步骤的数量,用于计算输入序列的长度
in_times = len(fcst_steps)
# 定义输出变量的数量,这里假设为1
out_varibales = 1
# 与输入序列相同,获取预报步骤的数量,用于计算输出序列的长度
out_times = len(fcst_steps)
# 计算模型的输入尺寸,即输入序列的总长度
input_size = in_times * in_varibales
# 计算模型的输出尺寸,即输出序列的总长度
output_size = out_times * out_varibales
# 初始化模型实例,并将其转移到CUDA设备上以利用GPU加速计算
model = Model(input_size, output_size).cuda()
这段代码实现了一个基于PyTorch的深度学习模型,用于处理多变量时间序列数据,常见于气象预测等应用。模型包括一个2D卷积层,用于提取特征。输入数据首先被重新排列,然后通过卷积层进行处理,最后输出结果再次被重新排列以保持原始数据的形状。
模型训练
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) # 使用Adam优化器
# for epoch in tqdm(range(num_epochs)):
# 训练模型多个周期
for epoch in range(num_epochs):
# 遍历训练集中的每一批数据
for index, (ft_item, gt_item) in enumerate(train_loader):
# 将特征数据批和目标数据批转移到GPU进行计算
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()
# 每10步打印一次损失值
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') # 保存模型权重
这段代码是深度学习模型训练过程的一个示例。首先,它导入了必要的库,包括NumPy和PyTorch,以及一个可选的进度条库tqdm
(已被注释掉)。然后设置了训练的轮数(num_epochs
),并初始化了Adam优化器。
在训练循环中,代码遍历了训练数据加载器train_loader
中的所有批次。对于每个批次,它将特征数据ft_item
和目标数据gt_item
转移到GPU上,并打印了它们转换后的类型。接着,模型通过前向传播计算输出和损失,然后通过反向传播计算梯度,并使用优化器更新模型的参数。
训练过程中,每10个批次,代码会打印出当前的损失值,以便于监控训练进度。最后,训练完成后,模型的权重被保存到文件model_weights.pth
中,以便之后可以重新加载这些权重进行进一步的训练或评估。
基于baseline的修改
模型定义
class Encoder(nn.Module):
def __init__(self, in_channels, out_channels):
super(Encoder, self).__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)
self.pool = nn.MaxPool2d(kernel_size=2, stride=1)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
x = self.pool(x)
return x
class Decoder(nn.Module):
def __init__(self, in_channels, out_channels):
super(Decoder, self).__init__()
self.deconv1 = nn.ConvTranspose2d(in_channels, out_channels, kernel_size=2, stride=1)
self.conv1 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)
def forward(self, x):
x = F.relu(self.deconv1(x))
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
return x
class MetNet(nn.Module):
def __init__(self, in_channels, out_channels):
super(MetNet, self).__init__()
self.conv1 = nn.Conv2d(in_channels, 128, 3, 1, 1)
self.encoder = Encoder(128, 64) # 确保Encoder的输入通道数为128
self.decoder = Decoder(64, out_channels) # 确保Decoder的输入通道数为64
def forward(self, x):
B, S, C, W, H = tuple(x.shape)
x = x.reshape(B, -1, W, H) # 调整输入数据的形状
x = self.conv1(x)
encoded = self.encoder(x)
decoded = self.decoder(encoded)
decoded = decoded.reshape(B, S, W, H) # 调整输出数据的形状
return decoded
这段代码定义了一个名为MetNet
的神经网络模型,它采用了编码器-解码器架构,适用于需要特征提取和逐步重建的任务,如图像分割或时间序列预测。
Encoder
类通过两个卷积层和一个最大池化层逐步提取特征并降低数据的空间维度。Decoder
类则通过一个转置卷积层和两个卷积层逐步恢复数据的空间维度,同时重建特征。
MetNet
类结合了这两个模块,首先通过一个卷积层进行初步特征提取,然后使用编码器进一步提取特征,最后通过解码器重建数据。模型的forward
方法定义了数据通过这个网络的方式,包括输入数据的形状调整,以及编码和解码过程。
整个模型的设计允许它处理多通道的输入数据,通过调整通道数来适应不同复杂度的特征提取和重建任务。这种架构特别适合于处理具有序列和空间依赖性的多维数据集。
模型训练
import numpy as np
import torch
from tqdm import tqdm
from torch.optim.lr_scheduler import CosineAnnealingLR
# Train the model
num_epochs = 100
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 实例化余弦退火调度器
scheduler = CosineAnnealingLR(optimizer, T_max=num_epochs, eta_min=1e-7)
train_losses = []
for epoch in tqdm(range(num_epochs)):
# for epoch in range(num_epochs):
# print(f'epoch -------- {epoch}')
train_loss = 0
for index, (ft_item, gt_item) in enumerate(train_loader):
ft_item = ft_item.cuda().float()
gt_item = gt_item.cuda().float()
# Forward pass
output_item = model(ft_item)
loss = loss_func(output_item, gt_item)
train_loss += loss
# 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}")
train_losses.append(train_loss)
# 更新学习率
scheduler.step()
# # 在每个epoch结束后进行验证
# val_loss = validate(model, val_loader, loss_func)
# print(f"Epoch [{epoch+1}/{num_epochs}], Validation Loss: {val_loss.item():.4f}")
# val_losses.append(val_loss)
# Save the model weights
torch.save(model.state_dict(), 'model_weights.pth')
这段代码展示了一个深度学习模型训练流程的实现,其中包括了一些高级的训练技巧和优化策略,以提高模型的性能和训练效率:
-
余弦退火学习率调度器(Cosine Annealing Scheduler):代码中引入了一个学习率调度器
CosineAnnealingLR
,它根据余弦函数的变化规律动态调整学习率。这种调度器在每个epoch中逐渐减小学习率至一个最小值,然后在下一个epoch重新开始,形成一个周期性的变化,有助于模型在训练过程中跳出局部最优,达到更优的解。 -
训练损失的记录与分析:代码中创建了一个列表
train_losses
来记录每个epoch的训练损失。这不仅有助于监控模型在训练过程中的损失下降情况,而且对于分析模型的收敛行为和调整训练策略非常重要。 -
损失函数的累积计算:在每个epoch的训练循环中,损失函数的值被累加到
train_loss
变量中,这样可以在epoch结束后计算平均损失,为模型的评估和监控提供了更全面的信息。
总体来说,这段代码通过结合动态学习率调整、实时进度监控、详尽的损失记录等策略,构建了一个成熟且自动化的深度学习训练流程。
损失曲线展示
import torch
import matplotlib.pyplot as plt
# 将所有损失张量移动到CPU,并转换为NumPy数组
train_losses_numpy = [loss.cpu().item() for loss in train_losses]
# 现在你可以使用matplotlib绘制损失曲线了
plt.plot(train_losses_numpy)
plt.title('Loss Curve')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()
这段代码通过PyTorch和Matplotlib实现了深度学习模型训练损失的可视化。它首先将训练过程中记录的损失值从GPU转移到CPU,并转换成NumPy数组格式。然后,利用Matplotlib绘制了一个损失曲线图,横轴表示训练的轮数(epoch),纵轴表示损失值。图表的标题、X轴和Y轴的标签都被设置得清晰明了,最后通过plt.show()
展示出这个曲线图,使得观察者可以直观地看到模型训练过程中损失值的变化趋势。这种曲线图对于分析模型的学习和收敛情况非常有帮助。