File size: 6,179 Bytes
57e06a2
 
09af8e7
57e06a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09af8e7
57e06a2
 
 
 
 
 
 
09af8e7
57e06a2
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# CAS-Canglong — native PyTorch, bit-exact vs the official code (S2S global SST)

[← Model zoo](../README.md) · [All model pages](README.md) · **Paper:** *Advanced Science*, "Improved Skills for Predicting Sub-Seasonal to Seasonal Global Sea Surface Temperature", [doi:10.1002/advs.78271](https://doi.org/10.1002/advs.78271) (preprint: arXiv:2409.05369)

`weatherai/models/canglong` re-implements CAS-Canglong, a 3-D Earth-specific transformer (Pangu-style, U-shaped with a skip
concatenation) that predicts global sea surface temperature 1–3 months ahead from 16 monthly frames of 8 ocean/atmosphere
variables on the 0.25° ERA5 grid. Official code: [GISWLH/CAS-Canglong](https://github.com/GISWLH/CAS-Canglong) (MIT; the model is
defined in `notebook/model.ipynb`, helpers in `utils/`). Weights: figshare [10.6084/m9.figshare.26779969](https://doi.org/10.6084/m9.figshare.26779969)
(not committed here). Parameter names equal the official module, so an official `state_dict` strict-loads.

```python

import torch

from weatherai.models.canglong import Canglong, CanglongConfig, Canglong_lite, canglong_loss, load_official



m = Canglong_lite()                                   # 0.29 M params, 8 months x 8 vars on 32x64 -> 3 months; CPU friendly

y = m(torch.randn(1, 8, 8, 32, 64))                   # (1, 1, 3, 32, 64) normalised SST

loss = canglong_loss(y[0, 0], torch.randn(3, 32, 64)); loss.backward()   # official loss: lead-weighted MSE + correlation penalty



full = Canglong(CanglongConfig.official())            # 43.9 M params; input (B, 8, 16, 721, 1440) -> (B, 1, 3, 721, 1440)

model = load_official("canglong3_0005_600ep_base.tar")   # figshare checkpoint (torch.save of the module; trusted file)

```

* **Structure:** 3-D patch embedding (2 months × 4 × 4 px) → 8 × 181 × 360 tokens, embed 72 → 2 Earth-specific blocks →
  down-sample (8 × 91 × 180, 144 ch) → 6 + 6 blocks → up-sample → 2 blocks → concat with the first-stage skip → transposed-conv
  recovery (1 × 8 × 8) → centre crop to 3 × 721 × 1440. Window (2, 6, 12), heads (6, 12, 12, 6), drop-path 0–0.2.
  Channel 0 is SST (land set to 0, then per-variable standardisation); the notebook appends net solar radiation as channel 7.
* **`load_official`** accepts the released full-module pickle (the notebook's `__main__` / `canglong.*` / `weatherlearn.*`

  classes are unpickled as empty stand-ins and only their tensors are read), plain state dicts, `nn.DataParallel` prefixes and

  the WeatherLearn naming (`proj` / `conv`) used by `notebook/Infer.ipynb`.

* **Verified (CPU, fp32, random weights; `scripts/canglong_parity.py`, `docs/results/canglong_parity*.json`):** the unchanged

  official notebook cell + `utils` is executed and its `state_dict` strict-loaded into the native model.

  Full size (1 × 8 × 16 × 721 × 1440, 43,906,258 params): **max |Δ| = 0.0**, output (1, 1, 3, 721, 1440), forward 52 s native /

  64 s official on 4 CPU cores, peak RSS 9.1 GB for both models together. Lite config: forward max |Δ| = 0.0, gradients through

  the official loss identical (max rel. error 0.0 over 115 parameter tensors). 5 unit tests (stored lite reference, parameter

  count, pickled-module loading with stubbed classes, state-dict variants, training-loss decrease).

* **Official-code behaviour reproduced on purpose:**
  1. The output patch (1, 8, 8) is twice the embedding patch (4, 4); the recovered 1448 × 2880 field is centre-cropped to
     721 × 1440, so output pixels are decoded from the central half of the token grid (token rows 45–135 of 181, columns 90–269

     of 360). The time axis keeps the last 3 of the 8 time tokens (the crop depth is Pangu's 13 levels).

  2. Every second block shifts its windows by a hard-coded (1, 3, 6) (= window // 2 for the official window).

  3. `patchrecovery2d` / `patchrecovery3d` are computed and discarded, `patchembed2d` / `patchembed3d` are never called; they

     are kept (`legacy_heads=True`) only so official checkpoints strict-load. The native forward skips them (same output).

  4. Loss: lead weights (1, 1.5, 2) multiply *inside* the square, and the correlation penalty box (named `nino` in the

     notebook, rows 288:336 × cols 480:640) covers 18°N–6°N, 120°E–160°E on the 90°N→90°S, 0→359.75°E grid.

* **Released checkpoint on released data (GPU, fp32; `scripts/canglong_official_checkpoint.py`,

  [`docs/results/canglong_official_checkpoint.json`](https://github.com/GISWLH/WeatherAI/blob/main/docs/results/canglong_official_checkpoint.json), 2026-10-18):** the figshare

  `canglong3_0005_600ep_base.tar`, `normalized_data.pt` and `ocean_mask.npy` (MD5s equal to the figshare record) on an

  NVIDIA RTX 4090. `load_official` and the unchanged notebook cell strict-load the same weights; on the last 16-month window

  of the released data the two outputs are **identical (max |Δ| = 0.0)**, finite, 0.8 s per full-size forward. Against the

  following 3 months of that file the predicted SST has area-weighted ocean RMSE 1.20 / 1.01 / 0.87 K at leads 1–3

  (persistence 1.19 / 2.39 / 3.59 K) — one window, a sanity check rather than a skill estimate.

* **GPU smoke:** `scripts/gpu_smoke.py canglong` (lite parity + lite training step + one full-size forward) is wired into the

  HF Space; the same smoke on CPU (2026-10-10): **PASS** — lite vs official reference max |Δ| 0.0, lite gradients finite,

  full-size forward 54 s. Log: [model_status.md](https://github.com/GISWLH/WeatherAI/blob/main/docs/model_status.md).

* **Live forecasts from the same model family:** [canglong.earth](https://canglong.earth) and the Hugging Face Space

  [LonghaoWang/CAS-Canglong](https://huggingface.co/spaces/LonghaoWang/CAS-Canglong) (16-month SST and ENSO outlook).

  The operational 16-month model is a later Swin-transformer variant described in the

  [CAS-Canglong repository](https://github.com/GISWLH/CAS-Canglong); it is not part of this port.

* **NOT verified / not done:** the paper's skill scores over the full hindcast period, the data pipeline (ERA5 download,

  normalisation statistics), training at full size.