FourCastNet / model /fourcastnet.py
OneScience's picture
Upload folder using huggingface_hub
ff5b2f1 verified
Raw
History Blame Contribute Delete
5.18 kB
import numpy as np
import torch
import torch.nn as nn
from timm.models.layers import trunc_normal_
from einops import rearrange
from onescience.modules.embedding.fourcastnetembedding import FourCastNetEmbedding
from onescience.modules.fuser.fourcastnetfuser import FourCastNetFuser
class FourCastNet(nn.Module):
"""
FourCastNet 的主模型实现。
该模型使用以下组件完成输入编码与主干特征提取:
- `OneEmbedding(style="FourCastNetEmbedding")`
- 将二维气象场切分为二维 patch token 序列
- `OneFuser(style="FourCastNetFuser")`
- 在二维 patch 网格上重复执行 AFNO 频域混合与 MLP 通道混合
在当前实现中:
- 输入为二维单时刻气象场 `(Batch, Channels, Height, Width)`
- patch embedding 输出会加上可学习位置编码
- token 序列随后恢复成 `(PatchGridHeight, PatchGridWidth)` 二维网格
- 多层 `FourCastNetFuser` 在 patch 网格上完成主干特征提取
- 最终通过线性头恢复回目标变量场
Args:
img_size (tuple[int, int]):
输入空间尺寸 `(Height, Width)`。
patch_size (tuple[int, int]):
patch 切分尺寸 `(PatchHeight, PatchWidth)`。
in_chans (int):
输入变量通道数。
out_chans (int):
输出变量通道数。
embed_dim (int):
patch embedding 特征维度。
depth (int):
主干 `FourCastNetFuser` 堆叠层数。
mlp_ratio (float):
每层 MLP 隐层放大倍数。
drop_rate (float):
dropout 比例。
drop_path_rate (float):
按层递增的 Stochastic Depth 最大比例。
num_blocks (int):
AFNO 的通道分块数。
sparsity_threshold (float):
AFNO 的 soft shrink 阈值。
hard_thresholding_fraction (float):
AFNO 保留的频率模式比例。
"""
def __init__(
self,
img_size=(720, 1440),
patch_size=(8, 8),
in_chans=19,
out_chans=19,
embed_dim=768,
depth=12,
mlp_ratio=4.0,
drop_rate=0.0,
drop_path_rate=0.0,
num_blocks=8,
sparsity_threshold=0.01,
hard_thresholding_fraction=1.0,
):
super().__init__()
self.img_size = img_size
self.patch_size = patch_size
self.in_chans = in_chans
self.out_chans = out_chans
self.num_features = self.embed_dim = embed_dim
self.num_blocks = num_blocks
num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0])
drop_path = np.linspace(0, drop_path_rate, depth).tolist()
self.pos_embed = nn.Parameter(torch.zeros(1, num_patches, embed_dim))
self.pos_drop = nn.Dropout(p=drop_rate)
self.patch_grid_height = img_size[0] // self.patch_size[0]
self.patch_grid_width = img_size[1] // self.patch_size[1]
self.patch_embed = FourCastNetEmbedding(
img_size=img_size,
patch_size=patch_size,
in_chans=in_chans,
embed_dim=embed_dim,
)
self.blocks = nn.ModuleList([
FourCastNetFuser(
dim=embed_dim,
mlp_ratio=mlp_ratio,
drop=drop_rate,
drop_path=drop_path[i],
num_blocks=num_blocks,
sparsity_threshold=sparsity_threshold,
hard_thresholding_fraction=hard_thresholding_fraction,
)
for i in range(depth)
])
self.head = nn.Linear(
embed_dim,
self.out_chans * self.patch_size[0] * self.patch_size[1],
bias=False,
)
trunc_normal_(self.pos_embed, std=0.02)
self.apply(self._init_weights)
def _init_weights(self, m):
if isinstance(m, nn.Linear):
trunc_normal_(m.weight, std=0.02)
if isinstance(m, nn.Linear) and m.bias is not None:
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.LayerNorm):
nn.init.constant_(m.bias, 0)
nn.init.constant_(m.weight, 1.0)
@torch.jit.ignore
def no_weight_decay(self):
return {'pos_embed', 'cls_token'}
def forward(self, x):
"""
Args:
x (torch.Tensor):
输入张量,形状为 `(Batch, Channels, Height, Width)`。
Returns:
torch.Tensor:
输出张量,形状为 `(Batch, out_chans, Height, Width)`。
"""
Batch = x.shape[0]
x = self.patch_embed(x)
x = x + self.pos_embed
x = self.pos_drop(x)
x = x.reshape(Batch, self.patch_grid_height, self.patch_grid_width, self.embed_dim)
for blk in self.blocks:
x = blk(x)
x = self.head(x)
x = rearrange(
x,
"b h w (p1 p2 c_out) -> b c_out (h p1) (w p2)",
p1=self.patch_size[0],
p2=self.patch_size[1],
h=self.patch_grid_height,
w=self.patch_grid_width,
)
return x