kashif HF Staff commited on
Commit
75b6a4a
·
verified ·
1 Parent(s): a5734df

Expand the ensemble instructions

Browse files
Files changed (1) hide show
  1. README.md +56 -19
README.md CHANGED
@@ -27,7 +27,7 @@ simply several draws — which here is just the batch dimension.
27
  | [WeatherNext2](https://huggingface.co/kashif/weathernext2) | 0.25° (721×1440) | 40,962 | 183.8M | yes |
28
  | [WeatherNextCyclones](https://huggingface.co/kashif/weathernext-cyclones) | 0.25° (721×1440) | 40,962 | 183.8M | no |
29
 
30
- These weights correspond to `WeatherNext2_<2025_model1`, trained on data through 2024 and fine-tuned for
31
  initialization from operational ECMWF HRES analysis. All four independently trained members (`model1`–`model4`) are included; see [Ensembles](#ensembles).
32
 
33
  ## Usage
@@ -36,7 +36,7 @@ initialization from operational ECMWF HRES analysis. All four independently trai
36
  pip install transformers torch scipy
37
  ```
38
 
39
- The model works in a normalized space; [`WeatherNext2Processor`] owns everything physical — the per-variable
40
  normalization statistics, the calendar-derived forcings, and the residual connection back to an atmospheric state.
41
 
42
  ```python
@@ -78,33 +78,41 @@ for step in range(20): # 5 days
78
 
79
  ## Ensembles
80
 
81
- There are two independent ensembles here, and useful forecasts combine both.
82
 
83
- **1. Noise ensemble (within one checkpoint).** This is the FGN mechanism: each member is one draw of the
84
- 32-dimensional noise vector through the same weights. Members are the batch axis, and stay independent through an
85
- autoregressive rollout.
 
86
 
87
  ```python
88
  members = 8
89
  inputs = processor(state, seconds_since_epoch=valid_time)
90
- inputs = {key: value.repeat(members, *([1] * (value.ndim - 1))) for key, value in inputs.items()}
91
  with torch.no_grad():
92
- outputs = model(**inputs, generator=torch.Generator().manual_seed(0))
 
93
  ```
94
 
95
- At 0.25° one member needs roughly 50 GB, so batching all members at once usually will not fit on a single device.
96
- Loop over draws instead (or shard them across devices); the result is identical.
97
 
98
  ```python
 
99
  predictions = []
100
  for member in range(members):
101
  noise = torch.randn(1, model.config.noise_channels, generator=torch.Generator().manual_seed(member))
102
  with torch.no_grad():
103
- predictions.append(model(**inputs, noise=noise).prediction)
104
  ```
105
 
106
- **2. Multi-model ensemble (across checkpoints).** The released product is four independently trained networks. Member 1
107
- is at the repository root; all four are also available as subfolders, so you can loop uniformly.
 
 
 
 
 
108
 
109
  ```python
110
  REPO = "kashif/weathernext2"
@@ -113,15 +121,44 @@ def load_member(member: int, revision: str = "main"):
113
  return WeatherNext2ForWeatherForecasting.from_pretrained(
114
  REPO, subfolder=f"model{member}", revision=revision
115
  ).eval()
116
-
117
- models = [load_member(i) for i in range(1, 5)] # or a subset, they are ~700 MB each
118
  ```
119
 
120
- `subfolder` works the same way for [`WeatherNext2Processor`] and `AutoConfig`, since each subfolder holds its own
121
- `config.json` and `preprocessor_config.json`. The processors are identical across members, so loading one is enough.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
- Combining the two gives the full ensemble: `num_models x num_noise_draws` trajectories, from which you take the
124
- ensemble mean, spread, or whatever quantiles the downstream product needs.
125
 
126
  ## Model details
127
 
 
27
  | [WeatherNext2](https://huggingface.co/kashif/weathernext2) | 0.25° (721×1440) | 40,962 | 183.8M | yes |
28
  | [WeatherNextCyclones](https://huggingface.co/kashif/weathernext-cyclones) | 0.25° (721×1440) | 40,962 | 183.8M | no |
29
 
30
+ These weights correspond to `WeatherNext2_<2025_model{1..4}`, trained on data through 2024 and fine-tuned for
31
  initialization from operational ECMWF HRES analysis. All four independently trained members (`model1`–`model4`) are included; see [Ensembles](#ensembles).
32
 
33
  ## Usage
 
36
  pip install transformers torch scipy
37
  ```
38
 
39
+ The model works in a normalized space; `WeatherNext2Processor` owns everything physical — the per-variable
40
  normalization statistics, the calendar-derived forcings, and the residual connection back to an atmospheric state.
41
 
42
  ```python
 
78
 
79
  ## Ensembles
80
 
81
+ There are two independent ensembles here, and the operational product combines both.
82
 
83
+ ### 1. Noise ensemble (within one checkpoint)
84
+
85
+ This is the FGN mechanism: each member is one draw of the 32-dimensional noise vector through the *same* weights.
86
+ Members ride on the batch axis and stay independent through an autoregressive rollout.
87
 
88
  ```python
89
  members = 8
90
  inputs = processor(state, seconds_since_epoch=valid_time)
91
+ batched = {key: value.repeat(members, *([1] * (value.ndim - 1))) for key, value in inputs.items()}
92
  with torch.no_grad():
93
+ outputs = model(**batched, generator=torch.Generator().manual_seed(0))
94
+ # outputs.prediction is (members, channels, lat, lon)
95
  ```
96
 
97
+ At 0.25° a single member needs roughly 50 GB, so batching all of them at once will usually not fit on one device.
98
+ Looping over draws gives identical results with a constant memory footprint:
99
 
100
  ```python
101
+ single = processor(state, seconds_since_epoch=valid_time) # batch of 1
102
  predictions = []
103
  for member in range(members):
104
  noise = torch.randn(1, model.config.noise_channels, generator=torch.Generator().manual_seed(member))
105
  with torch.no_grad():
106
+ predictions.append(model(**single, noise=noise).prediction)
107
  ```
108
 
109
+ Seeding per member (rather than drawing from one stream) means the first N members are reproducible regardless of how
110
+ many you end up running the same property the original implementation gets from `jax.random.fold_in`.
111
+
112
+ ### 2. Multi-model ensemble (across checkpoints)
113
+
114
+ The released product is four independently trained networks. Member 1 is at the repository root; all four are also
115
+ available as subfolders, so you can loop uniformly.
116
 
117
  ```python
118
  REPO = "kashif/weathernext2"
 
121
  return WeatherNext2ForWeatherForecasting.from_pretrained(
122
  REPO, subfolder=f"model{member}", revision=revision
123
  ).eval()
 
 
124
  ```
125
 
126
+ `subfolder` composes with `revision`, and works the same way for `WeatherNext2Processor` and `AutoConfig` each
127
+ subfolder carries its own `config.json` and `preprocessor_config.json`. The processors are identical across members,
128
+ so loading one is enough.
129
+
130
+ ### Putting them together
131
+
132
+ The full ensemble is `num_models × num_noise_draws` trajectories. Loading one member at a time keeps peak memory at
133
+ roughly one model:
134
+
135
+ ```python
136
+ import numpy as np
137
+ import torch
138
+
139
+ processor = WeatherNext2Processor.from_pretrained(REPO)
140
+ inputs = processor(state, seconds_since_epoch=valid_time)
141
+
142
+ forecasts = []
143
+ for member in range(1, 5):
144
+ model = load_member(member)
145
+ for draw in range(4):
146
+ noise = torch.randn(
147
+ 1, model.config.noise_channels,
148
+ generator=torch.Generator().manual_seed(1000 * member + draw),
149
+ )
150
+ with torch.no_grad():
151
+ prediction = model(**inputs, noise=noise).prediction
152
+ forecasts.append(processor.postprocess(prediction, state)["2m_temperature"])
153
+ del model # free before loading the next member
154
+
155
+ stack = np.concatenate(forecasts, axis=0) # (16, lat, lon)
156
+ ensemble_mean = stack.mean(axis=0)
157
+ ensemble_spread = stack.std(axis=0)
158
+ ```
159
 
160
+ For multi-step forecasts each trajectory carries its own state, so keep one `state` per member and advance them
161
+ separately (or keep members on the batch axis, which `advance_state` handles for you).
162
 
163
  ## Model details
164