Upload folder using huggingface_hub
Browse files- .gitattributes +21 -9
- README.md +155 -0
- conf/config.yaml +21 -0
- config.json +53 -0
- configuration.json +1 -0
- model/corrdiff.py +102 -0
- scripts/fake_data.py +23 -0
- scripts/inference.py +37 -0
- scripts/result.py +21 -0
- scripts/train.py +44 -0
- weight/.gitkeep +0 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,47 @@
|
|
| 1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 4 |
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bin.* filter=lfs diff=lfs merge=lfs -text
|
| 5 |
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 6 |
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 11 |
*.model filter=lfs diff=lfs merge=lfs -text
|
| 12 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
| 13 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 14 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 15 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 16 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
| 17 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 18 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 19 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 20 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 21 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 22 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 23 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 24 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 25 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
*.zstandard filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.db* filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.ark* filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
**/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
**/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
**/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*.gguf* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
*.ggml filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
*.llamafile* filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
*.pt2 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 46 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 47 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
frameworks: PyTorch
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
license: apache-2.0
|
| 6 |
+
tags:
|
| 7 |
+
- OneScience
|
| 8 |
+
- Earth Science
|
| 9 |
+
- Weather Downscaling
|
| 10 |
+
- Diffusion Model
|
| 11 |
+
- ERA5
|
| 12 |
+
- CorrDiff
|
| 13 |
+
- arxiv:2309.15214
|
| 14 |
+
tasks: []
|
| 15 |
+
datasets:
|
| 16 |
+
- OneScience/ERA5
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
<p align="center">
|
| 20 |
+
<strong>
|
| 21 |
+
<span style="font-size: 30px;">CorrDiff</span>
|
| 22 |
+
</strong>
|
| 23 |
+
</p>
|
| 24 |
+
|
| 25 |
+
# Model Introduction
|
| 26 |
+
|
| 27 |
+
CorrDiff (Residual Corrective Diffusion Model) is a two-stage generative model for kilometer-scale regional weather downscaling. It downscales approximately 25 km ERA5 fields to approximately 2 km regional weather fields over Taiwan and synthesizes radar reflectivity that is not present in the input.
|
| 28 |
+
|
| 29 |
+
Paper: Residual Corrective Diffusion Modeling for Km-scale Atmospheric Downscaling
|
| 30 |
+
|
| 31 |
+
https://arxiv.org/abs/2309.15214
|
| 32 |
+
|
| 33 |
+
# Model Description
|
| 34 |
+
|
| 35 |
+
CorrDiff was proposed by NVIDIA and its collaborators for kilometer-scale regional weather downscaling and was trained with coarse-resolution ERA5 reanalysis and high-resolution WRF regional model data from Taiwan's Central Weather Administration.
|
| 36 |
+
The model is suitable for converting coarse-resolution global weather fields into high-resolution regional weather fields and producing probabilistic weather predictions.
|
| 37 |
+
|
| 38 |
+
# Use Cases
|
| 39 |
+
|
| 40 |
+
| Scenario | Description |
|
| 41 |
+
| :---: | :--- |
|
| 42 |
+
| Weather forecast training | Train CorrDiff with ERA5 HDF5 data. |
|
| 43 |
+
| Local quick validation | Use synthetic data to check data loading, model training, inference, and result visualization. |
|
| 44 |
+
| ModelScope / OneCode execution | Download the standalone model package, install dependencies, and run the scripts directly. |
|
| 45 |
+
| Multi-GPU training | Launch multi-process training with `torchrun`. |
|
| 46 |
+
|
| 47 |
+
# Usage Guide
|
| 48 |
+
|
| 49 |
+
## 1. OneCode Usage
|
| 50 |
+
|
| 51 |
+
Experience intelligent one-click AI4S programming through the OneCode online environment:
|
| 52 |
+
|
| 53 |
+
[Click to Experience Intelligent One-Click AI4S Programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
|
| 54 |
+
|
| 55 |
+
## 2. Manual Installation and Usage
|
| 56 |
+
|
| 57 |
+
**Hardware Requirements**
|
| 58 |
+
|
| 59 |
+
- A GPU or DCU is recommended.
|
| 60 |
+
- CPU can be used for import and small-scale connectivity verification; full training and inference will be slow.
|
| 61 |
+
- DCU users must install DTK in advance. DTK 25.04.2 or above, or the OneScience recommended version matching your cluster, is recommended.
|
| 62 |
+
|
| 63 |
+
### Download the Model Package
|
| 64 |
+
|
| 65 |
+
```bash
|
| 66 |
+
hf download OneScience-Group/CorrDiff --local-dir ./CorrDiff
|
| 67 |
+
cd CorrDiff
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
### Install the Runtime Environment
|
| 71 |
+
|
| 72 |
+
**DCU Environment**
|
| 73 |
+
|
| 74 |
+
```bash
|
| 75 |
+
# Please activate DTK and CONDA first
|
| 76 |
+
conda create -n onescience311 python=3.11 -y
|
| 77 |
+
conda activate onescience311
|
| 78 |
+
# uv installation is supported
|
| 79 |
+
pip install onescience[earth-dcu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
**GPU Environment**
|
| 83 |
+
|
| 84 |
+
```bash
|
| 85 |
+
# Please activate CONDA first
|
| 86 |
+
conda create -n onescience311 python=3.11 -y libstdcxx-ng=12 libgcc-ng=12 gcc_linux-64=12 gxx_linux-64=12
|
| 87 |
+
conda activate onescience311
|
| 88 |
+
# uv installation is supported
|
| 89 |
+
pip install onescience[earth-gpu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
### Training Data Introduction
|
| 93 |
+
|
| 94 |
+
The OneScience community provides an ERA5 data slice that can be downloaded as follows:
|
| 95 |
+
|
| 96 |
+
```bash
|
| 97 |
+
hf download --repo-type dataset OneScience-Group/ERA5 --local-dir ./data
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
When real data is unavailable, generate synthetic data for pipeline validation:
|
| 101 |
+
|
| 102 |
+
```bash
|
| 103 |
+
python scripts/fake_data.py --output data/era5_corrdiff.npz
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
### Training
|
| 107 |
+
|
| 108 |
+
Single GPU:
|
| 109 |
+
|
| 110 |
+
```bash
|
| 111 |
+
python scripts/train.py
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
Multi-GPU:
|
| 115 |
+
|
| 116 |
+
```bash
|
| 117 |
+
torchrun --nproc_per_node=8 --nnodes=1 --rdzv_id=1000 --rdzv_backend=c10d --max_restarts=0 --master_addr="localhost" --master_port=29500 scripts/train.py
|
| 118 |
+
```
|
| 119 |
+
|
| 120 |
+
Training outputs:
|
| 121 |
+
|
| 122 |
+
```text
|
| 123 |
+
data/checkpoints/model_bak.pth
|
| 124 |
+
data/checkpoints/trloss.npy
|
| 125 |
+
data/checkpoints/valoss.npy
|
| 126 |
+
```
|
| 127 |
+
|
| 128 |
+
### Training Weights
|
| 129 |
+
|
| 130 |
+
This repository provides weights trained on 39 years of ERA5 reanalysis data in the `weight/` folder. The weight files will be uploaded soon and are expected to be available in the near future.
|
| 131 |
+
|
| 132 |
+
### Inference
|
| 133 |
+
|
| 134 |
+
```bash
|
| 135 |
+
python scripts/inference.py
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
Prediction results are written to `result/output/` by default.
|
| 139 |
+
|
| 140 |
+
### Evaluation and Visualization
|
| 141 |
+
|
| 142 |
+
```bash
|
| 143 |
+
python scripts/result.py
|
| 144 |
+
```
|
| 145 |
+
|
| 146 |
+
# Official OneScience Resources
|
| 147 |
+
|
| 148 |
+
| Platform | OneScience Main Repository | Skills Repository |
|
| 149 |
+
| --- | --- | --- |
|
| 150 |
+
| Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
|
| 151 |
+
| GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
|
| 152 |
+
|
| 153 |
+
# Citation and License
|
| 154 |
+
|
| 155 |
+
- This repository is a reproduction of the original CorrDiff paper.
|
conf/config.yaml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Lightweight OneScience configuration for the official CorrDiff example.
|
| 2 |
+
model:
|
| 3 |
+
regression_config: conf/config_training_taiwan_regression.yaml
|
| 4 |
+
diffusion_config: conf/config_training_taiwan_diffusion.yaml
|
| 5 |
+
generation_config: conf/config_generate_taiwan.yaml
|
| 6 |
+
input_channels: 12
|
| 7 |
+
output_channels: 4
|
| 8 |
+
input_shape: [36, 36]
|
| 9 |
+
output_shape: [448, 448]
|
| 10 |
+
ensemble_size: 32
|
| 11 |
+
|
| 12 |
+
data:
|
| 13 |
+
type: cwb_zarr
|
| 14 |
+
path: ./data/era5_corrdiff.npz
|
| 15 |
+
input_variables: [tcwv, t2m, u10m, v10m, t500, z500, u500, v500, t850, z850, u850, v850]
|
| 16 |
+
output_variables: [t2m, u10m, v10m, maximum_radar_reflectivity]
|
| 17 |
+
|
| 18 |
+
runtime:
|
| 19 |
+
device: auto
|
| 20 |
+
output_dir: ./result/output
|
| 21 |
+
seed: 42
|
config.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_name": "CorrDiff",
|
| 3 |
+
"model_type": "corrdiff",
|
| 4 |
+
"architectures": [
|
| 5 |
+
"CorrDiff"
|
| 6 |
+
],
|
| 7 |
+
"framework": "PyTorch",
|
| 8 |
+
"domain": "atmosphere",
|
| 9 |
+
"task": "regional-weather-downscaling",
|
| 10 |
+
"implementation": {
|
| 11 |
+
"entry_point": "model/corrdiff.py",
|
| 12 |
+
"scope": "compact two-stage reproduction of the CorrDiff regression and residual diffusion architecture"
|
| 13 |
+
},
|
| 14 |
+
"architecture": {
|
| 15 |
+
"family": "residual corrective diffusion model",
|
| 16 |
+
"input_grid_shape": [
|
| 17 |
+
36,
|
| 18 |
+
36
|
| 19 |
+
],
|
| 20 |
+
"output_grid_shape": [
|
| 21 |
+
448,
|
| 22 |
+
448
|
| 23 |
+
],
|
| 24 |
+
"input_channels": 12,
|
| 25 |
+
"output_channels": 4,
|
| 26 |
+
"base_channels": 32,
|
| 27 |
+
"mean_model": "RegressionUNet",
|
| 28 |
+
"residual_model": "ResidualDiffusionUNet",
|
| 29 |
+
"conditioning": [
|
| 30 |
+
"bilinearly upsampled coarse input",
|
| 31 |
+
"high-resolution regression mean",
|
| 32 |
+
"noise level sigma"
|
| 33 |
+
],
|
| 34 |
+
"normalization": "group_norm",
|
| 35 |
+
"activation": "silu",
|
| 36 |
+
"sampling_steps": 4,
|
| 37 |
+
"sigma_max": 1.0,
|
| 38 |
+
"sigma_min": 0.01
|
| 39 |
+
},
|
| 40 |
+
"data": {
|
| 41 |
+
"dataset": "ERA5 and CWA-WRF",
|
| 42 |
+
"input_spatial_resolution_km": 25,
|
| 43 |
+
"output_spatial_resolution_km": 2,
|
| 44 |
+
"time_step_hours": 1,
|
| 45 |
+
"input_steps": 1,
|
| 46 |
+
"output_steps": 1,
|
| 47 |
+
"protocol": "synthetic_era5_corrdiff"
|
| 48 |
+
},
|
| 49 |
+
"configuration_sources": [
|
| 50 |
+
"conf/config.yaml",
|
| 51 |
+
"model/corrdiff.py"
|
| 52 |
+
]
|
| 53 |
+
}
|
configuration.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"framework":"Pytorch","task":"weather_downscaling"}
|
model/corrdiff.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compact CorrDiff implementation for the OneScience model package."""
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
import torch
|
| 5 |
+
from torch import nn
|
| 6 |
+
from torch.nn import functional as F
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ConvBlock(nn.Module):
|
| 10 |
+
def __init__(self, in_channels, out_channels):
|
| 11 |
+
super().__init__()
|
| 12 |
+
self.block = nn.Sequential(
|
| 13 |
+
nn.Conv2d(in_channels, out_channels, 3, padding=1),
|
| 14 |
+
nn.GroupNorm(8, out_channels),
|
| 15 |
+
nn.SiLU(),
|
| 16 |
+
nn.Conv2d(out_channels, out_channels, 3, padding=1),
|
| 17 |
+
nn.GroupNorm(8, out_channels),
|
| 18 |
+
nn.SiLU(),
|
| 19 |
+
)
|
| 20 |
+
self.skip = nn.Conv2d(in_channels, out_channels, 1)
|
| 21 |
+
|
| 22 |
+
def forward(self, x):
|
| 23 |
+
return self.block(x) + self.skip(x)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class RegressionUNet(nn.Module):
|
| 27 |
+
"""Mean predictor: 12x36x36 coarse fields to 4x448x448 fields."""
|
| 28 |
+
|
| 29 |
+
def __init__(self, in_channels=12, out_channels=4, base_channels=32):
|
| 30 |
+
super().__init__()
|
| 31 |
+
self.encoder = nn.Sequential(
|
| 32 |
+
ConvBlock(in_channels, base_channels),
|
| 33 |
+
ConvBlock(base_channels, base_channels * 2),
|
| 34 |
+
ConvBlock(base_channels * 2, base_channels * 4),
|
| 35 |
+
)
|
| 36 |
+
self.head = nn.Sequential(
|
| 37 |
+
ConvBlock(base_channels * 4, base_channels * 2),
|
| 38 |
+
nn.Conv2d(base_channels * 2, out_channels, 1),
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
def forward(self, x):
|
| 42 |
+
x = self.encoder[0](x)
|
| 43 |
+
x = F.avg_pool2d(x, 2)
|
| 44 |
+
x = self.encoder[1](x)
|
| 45 |
+
x = F.avg_pool2d(x, 2)
|
| 46 |
+
x = self.encoder[2](x)
|
| 47 |
+
x = self.head[0](x)
|
| 48 |
+
x = F.interpolate(x, size=(448, 448), mode="bilinear", align_corners=False)
|
| 49 |
+
return self.head[1](x)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class ResidualDiffusionUNet(nn.Module):
|
| 53 |
+
"""Conditional residual denoiser used by the compact EDM-style sampler."""
|
| 54 |
+
|
| 55 |
+
def __init__(self, condition_channels=16, out_channels=4, base_channels=32):
|
| 56 |
+
super().__init__()
|
| 57 |
+
self.time = nn.Sequential(
|
| 58 |
+
nn.Linear(1, base_channels), nn.SiLU(), nn.Linear(base_channels, base_channels)
|
| 59 |
+
)
|
| 60 |
+
self.net = nn.Sequential(
|
| 61 |
+
ConvBlock(condition_channels, base_channels),
|
| 62 |
+
ConvBlock(base_channels, base_channels * 2),
|
| 63 |
+
ConvBlock(base_channels * 2, base_channels),
|
| 64 |
+
nn.Conv2d(base_channels, out_channels, 1),
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
def forward(self, noisy_residual, condition, sigma):
|
| 68 |
+
x = torch.cat((noisy_residual, condition), dim=1)
|
| 69 |
+
embedding = self.time(torch.log(sigma).reshape(-1, 1).float())
|
| 70 |
+
x = self.net[0](x)
|
| 71 |
+
x = x + embedding[:, :, None, None]
|
| 72 |
+
for layer in self.net[1:]:
|
| 73 |
+
x = layer(x)
|
| 74 |
+
return x
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class CorrDiff(nn.Module):
|
| 78 |
+
"""Two-stage mean plus stochastic residual atmospheric downscaler."""
|
| 79 |
+
|
| 80 |
+
def __init__(self, in_channels=12, out_channels=4, base_channels=32):
|
| 81 |
+
super().__init__()
|
| 82 |
+
self.regression = RegressionUNet(in_channels, out_channels, base_channels)
|
| 83 |
+
self.diffusion = ResidualDiffusionUNet(in_channels + 2 * out_channels, out_channels, base_channels)
|
| 84 |
+
|
| 85 |
+
def mean(self, coarse):
|
| 86 |
+
return self.regression(coarse)
|
| 87 |
+
|
| 88 |
+
def denoise(self, noisy_residual, coarse, mean, sigma):
|
| 89 |
+
coarse = F.interpolate(coarse, size=mean.shape[-2:], mode="bilinear", align_corners=False)
|
| 90 |
+
return self.diffusion(noisy_residual, torch.cat((coarse, mean), dim=1), sigma)
|
| 91 |
+
|
| 92 |
+
def forward(self, coarse, steps=4):
|
| 93 |
+
mean = self.mean(coarse)
|
| 94 |
+
residual = torch.randn_like(mean)
|
| 95 |
+
schedule = torch.linspace(1.0, 0.01, steps, device=coarse.device)
|
| 96 |
+
for sigma in schedule:
|
| 97 |
+
sigma_batch = sigma.expand(coarse.shape[0])
|
| 98 |
+
residual = residual - 0.2 * self.denoise(residual, coarse, mean, sigma_batch)
|
| 99 |
+
return mean + residual
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
__all__ = ["CorrDiff", "RegressionUNet", "ResidualDiffusionUNet"]
|
scripts/fake_data.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
import argparse
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def main():
|
| 7 |
+
parser = argparse.ArgumentParser()
|
| 8 |
+
parser.add_argument("--output", default="data/fake_corrdiff.npz")
|
| 9 |
+
parser.add_argument("--samples", type=int, default=1)
|
| 10 |
+
args = parser.parse_args()
|
| 11 |
+
path = Path(args.output)
|
| 12 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 13 |
+
rng = np.random.default_rng(42)
|
| 14 |
+
inputs = rng.standard_normal((args.samples, 12, 36, 36)).astype("float32")
|
| 15 |
+
targets = rng.standard_normal((args.samples, 4, 448, 448)).astype("float32")
|
| 16 |
+
np.savez_compressed(path, input=inputs, target=targets)
|
| 17 |
+
print(f"input: {inputs.shape}")
|
| 18 |
+
print(f"target: {targets.shape}")
|
| 19 |
+
print(f"saved: {path}")
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
if __name__ == "__main__":
|
| 23 |
+
main()
|
scripts/inference.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import numpy as np
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
import sys
|
| 7 |
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
| 8 |
+
from model.corrdiff import CorrDiff
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def main():
|
| 12 |
+
parser = argparse.ArgumentParser(description="Run CorrDiff inference")
|
| 13 |
+
parser.add_argument("--data", default="data/era5_corrdiff.npz")
|
| 14 |
+
parser.add_argument("--checkpoint", default="data/checkpoints/model_bak.pth")
|
| 15 |
+
parser.add_argument("--ensemble-size", type=int, default=1)
|
| 16 |
+
parser.add_argument("--output", default="result/output/predictions.npz")
|
| 17 |
+
args = parser.parse_args()
|
| 18 |
+
data = np.load(args.data)
|
| 19 |
+
coarse = torch.from_numpy(data["input"])
|
| 20 |
+
model = CorrDiff()
|
| 21 |
+
checkpoint = Path(args.checkpoint)
|
| 22 |
+
if checkpoint.exists():
|
| 23 |
+
model.load_state_dict(torch.load(checkpoint, map_location="cpu")["model"])
|
| 24 |
+
model.eval()
|
| 25 |
+
samples = []
|
| 26 |
+
with torch.no_grad():
|
| 27 |
+
for _ in range(args.ensemble_size):
|
| 28 |
+
samples.append(model(coarse).numpy().astype("float32"))
|
| 29 |
+
output = Path(args.output)
|
| 30 |
+
output.parent.mkdir(parents=True, exist_ok=True)
|
| 31 |
+
np.savez_compressed(output, prediction=np.stack(samples), input=data["input"])
|
| 32 |
+
print(f"prediction: {np.stack(samples).shape}")
|
| 33 |
+
print(f"saved: {output}")
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
if __name__ == "__main__":
|
| 37 |
+
main()
|
scripts/result.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def main():
|
| 7 |
+
parser = argparse.ArgumentParser(description="Evaluate CorrDiff NPZ predictions")
|
| 8 |
+
parser.add_argument("--prediction", default="result/output/predictions.npz")
|
| 9 |
+
parser.add_argument("--target", default="data/era5_corrdiff.npz")
|
| 10 |
+
args = parser.parse_args()
|
| 11 |
+
prediction = np.load(args.prediction)["prediction"]
|
| 12 |
+
target = np.load(args.target)["target"]
|
| 13 |
+
mae = np.abs(prediction.mean(axis=0) - target).mean()
|
| 14 |
+
output = Path(args.prediction).with_name("metrics.npz")
|
| 15 |
+
np.savez(output, mae=np.array(mae, dtype="float32"))
|
| 16 |
+
print(f"ensemble={prediction.shape[0]} mae={mae:.6f}")
|
| 17 |
+
print(f"saved: {output}")
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
if __name__ == "__main__":
|
| 21 |
+
main()
|
scripts/train.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import numpy as np
|
| 4 |
+
import torch
|
| 5 |
+
from torch.nn import functional as F
|
| 6 |
+
|
| 7 |
+
import sys
|
| 8 |
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
| 9 |
+
from model.corrdiff import CorrDiff
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def main():
|
| 13 |
+
parser = argparse.ArgumentParser(description="Train compact CorrDiff on NPZ data")
|
| 14 |
+
parser.add_argument("--data", default="data/era5_corrdiff.npz")
|
| 15 |
+
parser.add_argument("--steps", type=int, default=2)
|
| 16 |
+
parser.add_argument("--output", default="data/checkpoints/model_bak.pth")
|
| 17 |
+
args = parser.parse_args()
|
| 18 |
+
data = np.load(args.data)
|
| 19 |
+
coarse = torch.from_numpy(data["input"])
|
| 20 |
+
target = torch.from_numpy(data["target"])
|
| 21 |
+
model = CorrDiff()
|
| 22 |
+
optimizer = torch.optim.Adam(model.parameters(), lr=2e-4)
|
| 23 |
+
model.train()
|
| 24 |
+
for step in range(args.steps):
|
| 25 |
+
mean = model.mean(coarse)
|
| 26 |
+
residual = target - mean
|
| 27 |
+
sigma = torch.rand(coarse.shape[0]).clamp_min(0.01)
|
| 28 |
+
noisy = residual + sigma[:, None, None, None] * torch.randn_like(residual)
|
| 29 |
+
predicted = model.denoise(noisy, coarse, mean.detach(), sigma)
|
| 30 |
+
loss = F.mse_loss(mean, target) + F.mse_loss(predicted, residual)
|
| 31 |
+
optimizer.zero_grad()
|
| 32 |
+
loss.backward()
|
| 33 |
+
optimizer.step()
|
| 34 |
+
print(f"step={step + 1} loss={loss.item():.6f}")
|
| 35 |
+
output = Path(args.output)
|
| 36 |
+
output.parent.mkdir(parents=True, exist_ok=True)
|
| 37 |
+
torch.save({"model": model.state_dict(), "format": "corrdiff-compact-v1"}, output)
|
| 38 |
+
np.save(output.parent / "trloss.npy", np.asarray([loss.item()], dtype="float32"))
|
| 39 |
+
np.save(output.parent / "valoss.npy", np.asarray([loss.item()], dtype="float32"))
|
| 40 |
+
print(f"saved: {output}")
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
if __name__ == "__main__":
|
| 44 |
+
main()
|
weight/.gitkeep
ADDED
|
File without changes
|