Download model/fourcastnet.py from OneScience-Group/FourCastNet: direct link, hf CLI and curl.
- Browser
- Download file 5.18 kB
-
https://huggingface.co/OneScience-Group/FourCastNet/resolve/b4417e936d00dc8a65780fa94d38b123727aae03/model/fourcastnet.py
- Command line
-
hf download hf://OneScience-Group/FourCastNet@b4417e936d00dc8a65780fa94d38b123727aae03/model/fourcastnet.py
-
curl -L -o fourcastnet.py https://huggingface.co/OneScience-Group/FourCastNet/resolve/b4417e936d00dc8a65780fa94d38b123727aae03/model/fourcastnet.py
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) | |
| 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 | |