Spaces:
Running on Zero
Running on Zero
Delete folder code/qwenvl/modalities/weather with huggingface_hub
Browse files- code/qwenvl/modalities/weather/__init__.py +0 -86
- code/qwenvl/modalities/weather/configs/config_weather_0p25_channel70.json +0 -45
- code/qwenvl/modalities/weather/data/__init__.py +0 -3
- code/qwenvl/modalities/weather/data/era5_dataset.py +0 -96
- code/qwenvl/modalities/weather/data/polaris_data_utils.py +0 -191
- code/qwenvl/modalities/weather/decoder.py +0 -256
- code/qwenvl/modalities/weather/encoder.py +0 -479
- code/qwenvl/modalities/weather/internal/__init__.py +0 -11
- code/qwenvl/modalities/weather/internal/helpers.py +0 -45
- code/qwenvl/modalities/weather/internal/polaris_attention.py +0 -198
- code/qwenvl/modalities/weather/internal/polaris_layers.py +0 -448
- code/qwenvl/modalities/weather/internal/polaris_swin.py +0 -527
- code/qwenvl/modalities/weather/processor.py +0 -32
- code/qwenvl/modalities/weather/projector.py +0 -29
code/qwenvl/modalities/weather/__init__.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
| 1 |
-
"""Weather modality package.
|
| 2 |
-
|
| 3 |
-
Polaris-style global forecast support, integrated as a standard modality
|
| 4 |
-
under the ``ModalityRouter`` framework.
|
| 5 |
-
|
| 6 |
-
The package contributes:
|
| 7 |
-
|
| 8 |
-
* ``WeatherEncoder`` — Polaris ``CubeEmbedConv`` + Swin encoder + meteo merger.
|
| 9 |
-
* ``IdentityWeatherProjector`` — pass-through; merger already projects to LLM hidden.
|
| 10 |
-
* ``WeatherDecoder`` — Polaris meteo head + Charbonnier regression loss
|
| 11 |
-
(registers ``compute_loss_from_hidden`` for the router).
|
| 12 |
-
|
| 13 |
-
Special tokens: ``<|weather_start|>``, ``<|weather_end|>``, ``<|weather_pad|>``.
|
| 14 |
-
|
| 15 |
-
Adding the modality at runtime:
|
| 16 |
-
|
| 17 |
-
--weather_config_path qwenvl/modalities/weather/configs/config_weather_0p25_channel70.json
|
| 18 |
-
--tune_weather_encoder True --tune_weather_decoder True
|
| 19 |
-
"""
|
| 20 |
-
|
| 21 |
-
import logging
|
| 22 |
-
|
| 23 |
-
from .encoder import WeatherEncoder
|
| 24 |
-
from .projector import IdentityWeatherProjector
|
| 25 |
-
from .decoder import WeatherDecoder
|
| 26 |
-
from .processor import WeatherProcessor
|
| 27 |
-
|
| 28 |
-
logger = logging.getLogger(__name__)
|
| 29 |
-
|
| 30 |
-
MODALITY_CONFIG_KEY = "weather_config"
|
| 31 |
-
|
| 32 |
-
TOKEN_DEFS = {
|
| 33 |
-
"weather": {
|
| 34 |
-
"start": "<|weather_start|>",
|
| 35 |
-
"end": "<|weather_end|>",
|
| 36 |
-
"pad": "<|weather_pad|>",
|
| 37 |
-
},
|
| 38 |
-
}
|
| 39 |
-
|
| 40 |
-
__all__ = [
|
| 41 |
-
"WeatherEncoder",
|
| 42 |
-
"IdentityWeatherProjector",
|
| 43 |
-
"WeatherDecoder",
|
| 44 |
-
"WeatherProcessor",
|
| 45 |
-
"register_modality",
|
| 46 |
-
"MODALITY_CONFIG_KEY",
|
| 47 |
-
"TOKEN_DEFS",
|
| 48 |
-
]
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
def register_modality(router, config, llm_hidden_size: int):
|
| 52 |
-
"""Register the weather modality with the ``ModalityRouter``.
|
| 53 |
-
|
| 54 |
-
Args:
|
| 55 |
-
router: ModalityRouter instance.
|
| 56 |
-
config: Qwen3VLWeatherConfig (Polaris-style hyperparams).
|
| 57 |
-
llm_hidden_size: Hidden size of the LLM backbone — overrides
|
| 58 |
-
``config.qwenvl_dim`` so the merger / mlp_qwen2swin
|
| 59 |
-
bridges are sized to the actual LLM in use.
|
| 60 |
-
"""
|
| 61 |
-
# Force qwenvl_dim to match the active LLM, ignoring whatever was saved
|
| 62 |
-
# in the JSON config. (The merger is randomly initialised when
|
| 63 |
-
# init_weather_from is used, so 3584→4096 transitions are seamless.)
|
| 64 |
-
if config.qwenvl_dim != llm_hidden_size:
|
| 65 |
-
logger.info(
|
| 66 |
-
f"[weather] overriding qwenvl_dim {config.qwenvl_dim} → {llm_hidden_size} "
|
| 67 |
-
f"(active LLM hidden size)"
|
| 68 |
-
)
|
| 69 |
-
config.qwenvl_dim = llm_hidden_size
|
| 70 |
-
|
| 71 |
-
encoder = WeatherEncoder(config)
|
| 72 |
-
projector = IdentityWeatherProjector(qwenvl_dim=llm_hidden_size)
|
| 73 |
-
decoder = WeatherDecoder(config, encoder=encoder)
|
| 74 |
-
|
| 75 |
-
router.register_modality(
|
| 76 |
-
"weather",
|
| 77 |
-
encoder=encoder,
|
| 78 |
-
projector=projector,
|
| 79 |
-
decoder=decoder,
|
| 80 |
-
is_image_like=True,
|
| 81 |
-
)
|
| 82 |
-
logger.info(
|
| 83 |
-
f"[weather] registered: in_chans={config.in_chans}, "
|
| 84 |
-
f"image_size={config.image_size}, patch_size={config.patch_size}, "
|
| 85 |
-
f"hidden={config.hidden_size}, qwenvl_dim={config.qwenvl_dim}"
|
| 86 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/configs/config_weather_0p25_channel70.json
DELETED
|
@@ -1,45 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"polaris_vision_config": {
|
| 3 |
-
"in_chans": 70,
|
| 4 |
-
"in_frames": 1,
|
| 5 |
-
"const_chans": 6,
|
| 6 |
-
|
| 7 |
-
"upper_chans": 70,
|
| 8 |
-
|
| 9 |
-
"hidden_size": 2048,
|
| 10 |
-
"image_size": [721, 1440],
|
| 11 |
-
"patch_size": 6,
|
| 12 |
-
"window_size": 20,
|
| 13 |
-
"encoder_depth": 12,
|
| 14 |
-
"decoder_depth": 12,
|
| 15 |
-
"num_heads": 32,
|
| 16 |
-
"mlp_ratio": 4.0,
|
| 17 |
-
"attn_type": "flash",
|
| 18 |
-
"mask_type": "h",
|
| 19 |
-
"norm_type": "adarms",
|
| 20 |
-
"ffn_type": "geglu_ffn",
|
| 21 |
-
"n_kv_heads": null,
|
| 22 |
-
|
| 23 |
-
"embed_mode": "add",
|
| 24 |
-
"embed_types": ["step", "hour", "doy", "lead_hour"],
|
| 25 |
-
"embed_freq": 256,
|
| 26 |
-
|
| 27 |
-
"frame_interval": "6h",
|
| 28 |
-
|
| 29 |
-
"qwenvl_dim": 3584,
|
| 30 |
-
|
| 31 |
-
"meteo_out_channels": 70,
|
| 32 |
-
"meteo_output_size": [721, 1440],
|
| 33 |
-
"meteo_token_id": 151655,
|
| 34 |
-
|
| 35 |
-
"meteo_loss_type": "l1_channel",
|
| 36 |
-
"meteo_loss_weight": 1.0,
|
| 37 |
-
"meteo_loss_coef": 1.0,
|
| 38 |
-
"lead_hour_scaling": true,
|
| 39 |
-
"pl_chans": null,
|
| 40 |
-
|
| 41 |
-
"meteo_data_path": "<PATH_TO_ERA5_ZARR>",
|
| 42 |
-
|
| 43 |
-
"_attn_implementation": "eager"
|
| 44 |
-
}
|
| 45 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/data/__init__.py
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
"""Weather data utilities (ERA5 dataset / collator / context loader)."""
|
| 2 |
-
|
| 3 |
-
from .polaris_data_utils import load_meteorological_buffers # noqa: F401
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/data/era5_dataset.py
DELETED
|
@@ -1,96 +0,0 @@
|
|
| 1 |
-
"""Bio_qwen3vl-style ERA5 dataset.
|
| 2 |
-
|
| 3 |
-
Produces samples ready for the bio_qwen3vl pipeline:
|
| 4 |
-
|
| 5 |
-
* ``input_ids`` / ``attention_mask`` / ``position_ids`` / ``labels``:
|
| 6 |
-
the chat template with ``<|weather_start|><|weather_pad|>*N<|weather_end|>``
|
| 7 |
-
expanded to ``meteo_num_tokens`` pad tokens.
|
| 8 |
-
|
| 9 |
-
* ``weather_input_ids`` / ``weather_attention_mask`` / ``weather_grid_thw``:
|
| 10 |
-
dummy placeholders sized to ``swin_H * swin_W`` so the
|
| 11 |
-
``ModalityRouter.scatter_all`` finds them under the standard kwargs.
|
| 12 |
-
|
| 13 |
-
* The actual meteorological data — ``meteo_values`` / ``targets`` / ``times`` /
|
| 14 |
-
``lead_hours`` / ``polaris_task`` / optional ``channel_mask`` — is
|
| 15 |
-
carried in a ``"meteo_data"`` dict so the collator can stack across
|
| 16 |
-
the batch in fp32.
|
| 17 |
-
|
| 18 |
-
The collator unpacks ``meteo_data`` into the canonical
|
| 19 |
-
``weather_meteo_values`` / ``weather_targets`` / ``weather_times`` /
|
| 20 |
-
``weather_lead_hours`` / ``weather_polaris_task`` kwargs that the encoder
|
| 21 |
-
reads.
|
| 22 |
-
"""
|
| 23 |
-
|
| 24 |
-
from __future__ import annotations
|
| 25 |
-
|
| 26 |
-
import json
|
| 27 |
-
from dataclasses import dataclass
|
| 28 |
-
from typing import Any, Dict, List, Optional, Sequence
|
| 29 |
-
|
| 30 |
-
import numpy as np
|
| 31 |
-
import pandas as pd
|
| 32 |
-
import torch
|
| 33 |
-
import transformers
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
IGNORE_INDEX = -100
|
| 37 |
-
DEFAULT_IMAGE_TOKEN = "<|image_pad|>" # only used for compatibility; the
|
| 38 |
-
# weather pipeline uses <|weather_pad|>
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
# ---------------------------------------------------------------------------
|
| 42 |
-
# Chat-template builder for the meteo task
|
| 43 |
-
# ---------------------------------------------------------------------------
|
| 44 |
-
|
| 45 |
-
def _build_meteo_messages(
|
| 46 |
-
input_text: str,
|
| 47 |
-
meteo_pad_token: str,
|
| 48 |
-
weather_start_token: str,
|
| 49 |
-
weather_end_token: str,
|
| 50 |
-
meteo_num_tokens: int,
|
| 51 |
-
) -> List[Dict[str, Any]]:
|
| 52 |
-
"""Build a 2-turn system+user message list with the meteo placeholder.
|
| 53 |
-
|
| 54 |
-
The placeholder string is the same as what Polaris originally used,
|
| 55 |
-
swapping ``<|vision_start|>``→``<|weather_start|>`` and
|
| 56 |
-
``<|image_pad|>``→``<|weather_pad|>`` so the bio_qwen3vl router knows
|
| 57 |
-
where to scatter the encoder output.
|
| 58 |
-
"""
|
| 59 |
-
pads = meteo_pad_token * meteo_num_tokens
|
| 60 |
-
user_text = (
|
| 61 |
-
f"{weather_start_token}{pads}\n{weather_end_token}"
|
| 62 |
-
f"Describe this image."
|
| 63 |
-
)
|
| 64 |
-
return [
|
| 65 |
-
{"role": "system",
|
| 66 |
-
"content": [{"type": "text", "text": f"forecast\n{input_text}"}]},
|
| 67 |
-
{"role": "user",
|
| 68 |
-
"content": [{"type": "text", "text": user_text}]},
|
| 69 |
-
]
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
def preprocess_meteo_chat(
|
| 73 |
-
input_text: str,
|
| 74 |
-
processor,
|
| 75 |
-
meteo_pad_token: str,
|
| 76 |
-
weather_start_token: str,
|
| 77 |
-
weather_end_token: str,
|
| 78 |
-
meteo_num_tokens: int,
|
| 79 |
-
add_assistant_prompt: bool = True,
|
| 80 |
-
) -> Dict[str, torch.Tensor]:
|
| 81 |
-
msgs = _build_meteo_messages(
|
| 82 |
-
input_text, meteo_pad_token, weather_start_token,
|
| 83 |
-
weather_end_token, meteo_num_tokens,
|
| 84 |
-
)
|
| 85 |
-
full = processor.apply_chat_template(
|
| 86 |
-
msgs,
|
| 87 |
-
tokenize=True,
|
| 88 |
-
return_dict=True,
|
| 89 |
-
return_tensors="pt",
|
| 90 |
-
add_generation_prompt=add_assistant_prompt,
|
| 91 |
-
)
|
| 92 |
-
input_ids = full["input_ids"]
|
| 93 |
-
if isinstance(input_ids, list):
|
| 94 |
-
input_ids = torch.tensor(input_ids).unsqueeze(0)
|
| 95 |
-
full["input_ids"] = input_ids
|
| 96 |
-
return full
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/data/polaris_data_utils.py
DELETED
|
@@ -1,191 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
-
import torch
|
| 5 |
-
import torch.nn.functional as F
|
| 6 |
-
import xarray as xr
|
| 7 |
-
from einops import rearrange
|
| 8 |
-
|
| 9 |
-
__all__ = [
|
| 10 |
-
"make_seq", "crop_dataarray", "filter_dataset_channels", "load_meteorological_buffers",
|
| 11 |
-
"TYPHOON_PAD_MULTIPLE", "pad_spatial_to_multiple", "crop_spatial_pad",
|
| 12 |
-
"build_channel_map", "pad_channels", "extract_valid_channels",
|
| 13 |
-
]
|
| 14 |
-
|
| 15 |
-
TYPHOON_PAD_MULTIPLE = 120
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def pad_spatial_to_multiple(x: torch.Tensor, multiple: int = TYPHOON_PAD_MULTIPLE):
|
| 19 |
-
H, W = x.shape[-2], x.shape[-1]
|
| 20 |
-
pad_h = (multiple - H % multiple) % multiple
|
| 21 |
-
pad_w = (multiple - W % multiple) % multiple
|
| 22 |
-
pt, pb = pad_h // 2, pad_h - pad_h // 2
|
| 23 |
-
pl, pr = pad_w // 2, pad_w - pad_w // 2
|
| 24 |
-
if pad_h > 0 or pad_w > 0:
|
| 25 |
-
x = F.pad(x, (pl, pr, pt, pb), mode="constant", value=0)
|
| 26 |
-
return x, (pt, pb, pl, pr)
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
def crop_spatial_pad(x: torch.Tensor, pad_info):
|
| 30 |
-
pt, pb, pl, pr = pad_info
|
| 31 |
-
hs = slice(pt, x.shape[-2] - pb if pb > 0 else x.shape[-2])
|
| 32 |
-
ws = slice(pl, x.shape[-1] - pr if pr > 0 else x.shape[-1])
|
| 33 |
-
return x[..., hs, ws]
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
def build_channel_map(src_names, ref_names):
|
| 37 |
-
ref_idx = {n: i for i, n in enumerate(ref_names)}
|
| 38 |
-
src_to_ref = np.array([ref_idx.get(str(n), -1) for n in src_names], dtype=np.int64)
|
| 39 |
-
mask = np.zeros(len(ref_names), dtype=bool)
|
| 40 |
-
for i in src_to_ref:
|
| 41 |
-
if 0 <= i < len(ref_names):
|
| 42 |
-
mask[i] = True
|
| 43 |
-
return src_to_ref, mask
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
def pad_channels(x_src, src_to_ref, ref_c):
|
| 47 |
-
c = x_src.shape[0]
|
| 48 |
-
out = np.zeros((ref_c,) + x_src.shape[1:], dtype=np.float32)
|
| 49 |
-
for j in range(c):
|
| 50 |
-
i = int(src_to_ref[j])
|
| 51 |
-
if 0 <= i < ref_c:
|
| 52 |
-
out[i] = x_src[j]
|
| 53 |
-
return out
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
def extract_valid_channels(x_ref, src_to_ref, src_c):
|
| 57 |
-
out = np.zeros((src_c,) + x_ref.shape[1:], dtype=np.float32)
|
| 58 |
-
for j in range(src_c):
|
| 59 |
-
i = int(src_to_ref[j])
|
| 60 |
-
if 0 <= i < x_ref.shape[0]:
|
| 61 |
-
out[j] = x_ref[i]
|
| 62 |
-
return out
|
| 63 |
-
|
| 64 |
-
# 🚨 需要剔除的通道
|
| 65 |
-
DEFAULT_REMOVE_CHANNELS = [
|
| 66 |
-
"q2m", "d2m", "sst", "ws100m", "u100m", "v100m",
|
| 67 |
-
"lcc", "mcc", "hcc", "tcc", "ssr", "ssrd", "fdir", "ttr", "tcw", "tp"
|
| 68 |
-
]
|
| 69 |
-
|
| 70 |
-
def make_seq(ds, total_frames, frame_interval, frame_step=1, ignore_times=[]):
|
| 71 |
-
i = 0
|
| 72 |
-
inds = []
|
| 73 |
-
times = ds.time.values
|
| 74 |
-
ignore_timestamps = {pd.to_datetime(t) for t in ignore_times}
|
| 75 |
-
|
| 76 |
-
while i < len(times) - total_frames:
|
| 77 |
-
cur_sequence = []
|
| 78 |
-
for j in range(i, i + total_frames - 1):
|
| 79 |
-
if times[j+1] - times[j] == frame_interval:
|
| 80 |
-
current_time = pd.to_datetime(times[j])
|
| 81 |
-
if current_time in ignore_timestamps:
|
| 82 |
-
continue
|
| 83 |
-
cur_sequence.append(j)
|
| 84 |
-
|
| 85 |
-
if len(cur_sequence) == total_frames - 1:
|
| 86 |
-
inds.append(i)
|
| 87 |
-
|
| 88 |
-
i += frame_step
|
| 89 |
-
|
| 90 |
-
return np.array(inds, dtype=np.int32)
|
| 91 |
-
|
| 92 |
-
def crop_dataarray(ds, image_size=None, latlon_range=None):
|
| 93 |
-
if "lat" in ds.dims:
|
| 94 |
-
ilats = np.arange(ds.lat.size)
|
| 95 |
-
if latlon_range is not None:
|
| 96 |
-
lat_min, lat_max, lon_min, lon_max = latlon_range
|
| 97 |
-
ilats = np.where((ds.lat >= lat_min) & (ds.lat <= lat_max))[0]
|
| 98 |
-
if image_size is not None:
|
| 99 |
-
ilats = ilats[:image_size[0]]
|
| 100 |
-
ds = ds.isel(lat=ilats)
|
| 101 |
-
|
| 102 |
-
if "lon" in ds.dims:
|
| 103 |
-
ilons = np.arange(ds.lon.size)
|
| 104 |
-
if latlon_range is not None:
|
| 105 |
-
lat_min, lat_max, lon_min, lon_max = latlon_range
|
| 106 |
-
ilons = np.where((ds.lon >= lon_min) & (ds.lon <= lon_max))[0]
|
| 107 |
-
if image_size is not None:
|
| 108 |
-
ilons = ilons[:image_size[1]]
|
| 109 |
-
ds = ds.isel(lon=ilons)
|
| 110 |
-
return ds
|
| 111 |
-
|
| 112 |
-
def filter_dataset_channels(ds, remove_channels=None):
|
| 113 |
-
"""
|
| 114 |
-
统一的通道过滤函数:Dataset 和 Buffer 读取都调用这个函数。
|
| 115 |
-
"""
|
| 116 |
-
if remove_channels is None:
|
| 117 |
-
remove_channels = DEFAULT_REMOVE_CHANNELS
|
| 118 |
-
|
| 119 |
-
if isinstance(ds, xr.Dataset):
|
| 120 |
-
ds = ds[list(ds.data_vars)[0]]
|
| 121 |
-
|
| 122 |
-
all_channels = ds.channel.values.tolist()
|
| 123 |
-
keep_channels = [c for c in all_channels if c not in remove_channels]
|
| 124 |
-
keep_inds = [i for i, c in enumerate(all_channels) if c in keep_channels]
|
| 125 |
-
|
| 126 |
-
out_ds = ds.sel(channel=keep_channels)
|
| 127 |
-
return out_ds, keep_channels, keep_inds
|
| 128 |
-
|
| 129 |
-
def load_meteorological_buffers(
|
| 130 |
-
data_path: str,
|
| 131 |
-
image_size=None,
|
| 132 |
-
latlon_range=None,
|
| 133 |
-
remove_channels=None,
|
| 134 |
-
buffer_types=None,
|
| 135 |
-
index_names=None
|
| 136 |
-
):
|
| 137 |
-
"""
|
| 138 |
-
统一的数据提取。
|
| 139 |
-
"""
|
| 140 |
-
if buffer_types is None:
|
| 141 |
-
buffer_types = [
|
| 142 |
-
"mean", "std", "diff_mean", "diff_std", "climate",
|
| 143 |
-
"const", "weight", "channel_mask", "land_mask", "station_mask", "rps_mask", "dry_mask"
|
| 144 |
-
]
|
| 145 |
-
if index_names is None:
|
| 146 |
-
index_names = dict(logid=[], uid=["u10m"], vid=["v10m"], accumid=[])
|
| 147 |
-
|
| 148 |
-
ds = xr.open_zarr(data_path)
|
| 149 |
-
if "level" in ds.dims:
|
| 150 |
-
ds = ds.rename({"level": "channel"})
|
| 151 |
-
ds = crop_dataarray(ds, image_size, latlon_range)
|
| 152 |
-
|
| 153 |
-
# 🚨 调用统一过滤函数
|
| 154 |
-
ds, keep_channels, keep_inds = filter_dataset_channels(ds, remove_channels)
|
| 155 |
-
|
| 156 |
-
indices = {}
|
| 157 |
-
for k, prefixes in index_names.items():
|
| 158 |
-
inds = [i for i, name in enumerate(keep_channels) if name in prefixes]
|
| 159 |
-
if len(inds) > 0:
|
| 160 |
-
indices[k] = inds
|
| 161 |
-
|
| 162 |
-
coords = dict(lat=ds.lat.values.tolist(), lon=ds.lon.values.tolist())
|
| 163 |
-
|
| 164 |
-
buffers = {}
|
| 165 |
-
for k in buffer_types:
|
| 166 |
-
file_name = os.path.join(data_path, f"{k}.nc")
|
| 167 |
-
if not os.path.exists(file_name):
|
| 168 |
-
continue
|
| 169 |
-
|
| 170 |
-
da = xr.open_dataarray(file_name)
|
| 171 |
-
da = crop_dataarray(da, image_size, latlon_range)
|
| 172 |
-
|
| 173 |
-
if k in ["mean", "std", "diff_mean", "diff_std"]:
|
| 174 |
-
if "channel" in da.dims:
|
| 175 |
-
da = da.sel(channel=keep_channels)
|
| 176 |
-
else:
|
| 177 |
-
da_vals = da.values[keep_inds]
|
| 178 |
-
da = xr.DataArray(da_vals, coords={"channel": keep_channels}, dims=("channel",))
|
| 179 |
-
|
| 180 |
-
values = da if k == "climate" else da.values
|
| 181 |
-
|
| 182 |
-
if k == "const":
|
| 183 |
-
values = rearrange(values, "c h w -> 1 c h w")
|
| 184 |
-
elif k == "weight" and values.ndim == 1:
|
| 185 |
-
values = rearrange(values, "h -> 1 h 1")
|
| 186 |
-
elif k in ["mean", "std", "diff_mean", "diff_std"]:
|
| 187 |
-
values = rearrange(values, "c -> c 1 1")
|
| 188 |
-
|
| 189 |
-
buffers[k] = values
|
| 190 |
-
|
| 191 |
-
return keep_channels, indices, coords, buffers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/decoder.py
DELETED
|
@@ -1,256 +0,0 @@
|
|
| 1 |
-
"""Weather decoder: Polaris meteo head + regression-style loss.
|
| 2 |
-
|
| 3 |
-
Adapts ``PolarisMeteoHead`` to the bio_qwen3vl ``ModalityRouter``. The
|
| 4 |
-
decoder is invoked by ``compute_decoder_losses`` via the custom
|
| 5 |
-
``compute_loss_from_hidden`` hook (see ``qwenvl/registry/modality_router.py``):
|
| 6 |
-
that path bypasses the standard CE-on-decode-logits flow used by RNA /
|
| 7 |
-
protein / mol since meteo prediction is a regression problem on a 5D
|
| 8 |
-
tensor field.
|
| 9 |
-
|
| 10 |
-
Inputs needed at loss time live in two places:
|
| 11 |
-
|
| 12 |
-
* ``hidden_states`` (LLM output) — selects the meteo-token positions.
|
| 13 |
-
* ``self.encoder._step_cache`` — set by ``WeatherEncoder.forward`` and
|
| 14 |
-
contains ``condition_embed`` / ``patch_embed_pre_swin`` /
|
| 15 |
-
``meteo_values`` / ``targets`` / ``lead_hours``.
|
| 16 |
-
|
| 17 |
-
The encoder reference is wired up in ``register_modality`` so the decoder
|
| 18 |
-
does not have to be passed it through every forward.
|
| 19 |
-
"""
|
| 20 |
-
|
| 21 |
-
from __future__ import annotations
|
| 22 |
-
|
| 23 |
-
import logging
|
| 24 |
-
from typing import Any, Dict, Optional, Tuple
|
| 25 |
-
|
| 26 |
-
import numpy as np
|
| 27 |
-
import pandas as pd
|
| 28 |
-
import torch
|
| 29 |
-
import torch.nn as nn
|
| 30 |
-
import torch.nn.functional as F
|
| 31 |
-
from einops import rearrange
|
| 32 |
-
from torch.utils.checkpoint import checkpoint
|
| 33 |
-
|
| 34 |
-
from .internal.polaris_attention import precompute_freqs_cis
|
| 35 |
-
from .internal.polaris_layers import AdaLN, DoubleDeconvHead, remove_small_scales
|
| 36 |
-
from .internal.polaris_swin import SwinBlock
|
| 37 |
-
from .internal.helpers import round_to_multiple
|
| 38 |
-
|
| 39 |
-
logger = logging.getLogger(__name__)
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
class WeatherDecoder(nn.Module):
|
| 43 |
-
"""Polaris meteo head + regression loss path.
|
| 44 |
-
|
| 45 |
-
The original Polaris ``PolarisMeteoHead.forward`` is wrapped in
|
| 46 |
-
``compute_loss_from_hidden`` so it can pull the matching encoder cache
|
| 47 |
-
and produce a scalar loss directly from LLM hidden states.
|
| 48 |
-
"""
|
| 49 |
-
|
| 50 |
-
def __init__(self, config, encoder):
|
| 51 |
-
super().__init__()
|
| 52 |
-
self.config = config
|
| 53 |
-
# Hold the encoder reference in a 1-element list so PyTorch's
|
| 54 |
-
# automatic Module-attribute registration does NOT pull the
|
| 55 |
-
# encoder's parameters in as decoder children (which would
|
| 56 |
-
# duplicate them under both encoders.weather.* and decoders.weather.*
|
| 57 |
-
# in the state_dict).
|
| 58 |
-
self._encoder_ref = [encoder]
|
| 59 |
-
|
| 60 |
-
self.hidden_size = config.hidden_size
|
| 61 |
-
self.num_heads = config.num_heads
|
| 62 |
-
self.qwenvl_dim = config.qwenvl_dim
|
| 63 |
-
self.patch_size = config.patch_size
|
| 64 |
-
self.upper_chans = config.upper_chans
|
| 65 |
-
self.lower_chans = config.lower_chans
|
| 66 |
-
self.decoder_depth = config.decoder_depth
|
| 67 |
-
|
| 68 |
-
if isinstance(config.image_size, int):
|
| 69 |
-
in_h = in_w = config.image_size
|
| 70 |
-
else:
|
| 71 |
-
in_h, in_w = config.image_size
|
| 72 |
-
if self.patch_size == 1:
|
| 73 |
-
swin_H = in_h // 2 * 2
|
| 74 |
-
swin_W = in_w
|
| 75 |
-
else:
|
| 76 |
-
swin_H = in_h // self.patch_size
|
| 77 |
-
swin_W = in_w // self.patch_size
|
| 78 |
-
self._swin_HW = (swin_H, swin_W)
|
| 79 |
-
|
| 80 |
-
max_seq_len = swin_H * swin_W
|
| 81 |
-
freqs_cos, freqs_sin = precompute_freqs_cis(self.hidden_size // self.num_heads, max_seq_len)
|
| 82 |
-
self.register_buffer("freqs_cos", freqs_cos, persistent=False)
|
| 83 |
-
self.register_buffer("freqs_sin", freqs_sin, persistent=False)
|
| 84 |
-
self._rope_buffers_checked = False
|
| 85 |
-
|
| 86 |
-
# Cross-modal back projector: LLM hidden → swin hidden.
|
| 87 |
-
# Trained at ``mm_projector_lr`` (matched on substring "mlp_qwen2swin").
|
| 88 |
-
self.mlp_qwen2swin = nn.Sequential(
|
| 89 |
-
nn.Linear(self.qwenvl_dim, self.qwenvl_dim),
|
| 90 |
-
nn.GELU(),
|
| 91 |
-
nn.Linear(self.qwenvl_dim, self.hidden_size),
|
| 92 |
-
)
|
| 93 |
-
|
| 94 |
-
if config.embed_mode == "add":
|
| 95 |
-
self.embed_dim = config.hidden_size
|
| 96 |
-
elif config.embed_mode == "cat":
|
| 97 |
-
self.embed_dim = round_to_multiple(config.hidden_size // len(config.embed_types)) * len(config.embed_types)
|
| 98 |
-
else:
|
| 99 |
-
raise ValueError(f"Invalid embed_mode: {config.embed_mode}")
|
| 100 |
-
|
| 101 |
-
self.decoder_layers = nn.ModuleList()
|
| 102 |
-
for i in range(self.decoder_depth):
|
| 103 |
-
blk = SwinBlock(
|
| 104 |
-
dim=self.hidden_size, embed_dim=self.embed_dim, num_heads=self.num_heads,
|
| 105 |
-
input_size=(swin_H, swin_W), window_size=config.window_size,
|
| 106 |
-
shift_size=0 if i % 2 == 0 else config.window_size // 2,
|
| 107 |
-
mlp_ratio=config.mlp_ratio, attn_type=config.attn_type,
|
| 108 |
-
mask_type=config.mask_type, norm_type=config.norm_type,
|
| 109 |
-
ffn_type=config.ffn_type, n_kv_heads=config.n_kv_heads,
|
| 110 |
-
attn_implementation="eager",
|
| 111 |
-
)
|
| 112 |
-
self.decoder_layers.append(blk)
|
| 113 |
-
|
| 114 |
-
self.norm_layer = AdaLN(self.hidden_size, embed_dim=self.embed_dim)
|
| 115 |
-
self.pred_layer = DoubleDeconvHead(
|
| 116 |
-
in_chans=self.hidden_size, upper_chans=self.upper_chans,
|
| 117 |
-
lower_chans=self.lower_chans, patch_size=self.patch_size,
|
| 118 |
-
)
|
| 119 |
-
|
| 120 |
-
self.gradient_checkpointing = False
|
| 121 |
-
|
| 122 |
-
# Provide the same context-injection API the encoder has, so the
|
| 123 |
-
# training entry can call it on the decoder too without checking
|
| 124 |
-
# which side actually owns the buffers.
|
| 125 |
-
def inject_meteorological_context(self, *args, **kwargs):
|
| 126 |
-
# The encoder is the canonical owner of mean/std/nanmask/...
|
| 127 |
-
# We ignore here to avoid duplicate state.
|
| 128 |
-
return
|
| 129 |
-
|
| 130 |
-
def _ensure_finite_rope_buffers(self) -> None:
|
| 131 |
-
"""Rebuild the RoPE tables if the non-persistent buffers came up broken.
|
| 132 |
-
|
| 133 |
-
``freqs_cos`` / ``freqs_sin`` are ``persistent=False`` (not in the
|
| 134 |
-
checkpoint) and are meant to be filled at ``__init__``. Under a
|
| 135 |
-
meta-device ``from_pretrained`` load they can be ``to_empty``'d to
|
| 136 |
-
uninitialised memory. A valid table has ``cos[0]==1`` and ``sin[0]==0``
|
| 137 |
-
for every column — checking row 0 catches garbage that happens to be
|
| 138 |
-
finite (large floats) which an isfinite-only check would miss.
|
| 139 |
-
"""
|
| 140 |
-
if self._rope_buffers_checked:
|
| 141 |
-
return
|
| 142 |
-
with torch.no_grad():
|
| 143 |
-
row0 = self.freqs_cos[0].detach().to(torch.float32)
|
| 144 |
-
row0_sin = self.freqs_sin[0].detach().to(torch.float32)
|
| 145 |
-
needs_recompute = (
|
| 146 |
-
not torch.isfinite(self.freqs_cos).all()
|
| 147 |
-
or not torch.isfinite(self.freqs_sin).all()
|
| 148 |
-
or not torch.allclose(row0, torch.ones_like(row0), atol=1e-5)
|
| 149 |
-
or not torch.allclose(row0_sin, torch.zeros_like(row0_sin), atol=1e-5)
|
| 150 |
-
)
|
| 151 |
-
if needs_recompute:
|
| 152 |
-
logger.warning(
|
| 153 |
-
"[weather] broken decoder RoPE buffers detected "
|
| 154 |
-
"(nan/inf/zeros); recomputing freqs_cos/freqs_sin"
|
| 155 |
-
)
|
| 156 |
-
max_seq_len = int(self._swin_HW[0] * self._swin_HW[1])
|
| 157 |
-
freqs_cos, freqs_sin = precompute_freqs_cis(
|
| 158 |
-
self.hidden_size // self.num_heads, max_seq_len,
|
| 159 |
-
)
|
| 160 |
-
with torch.no_grad():
|
| 161 |
-
self.freqs_cos.copy_(freqs_cos.to(self.freqs_cos.device, self.freqs_cos.dtype))
|
| 162 |
-
self.freqs_sin.copy_(freqs_sin.to(self.freqs_sin.device, self.freqs_sin.dtype))
|
| 163 |
-
self._rope_buffers_checked = True
|
| 164 |
-
|
| 165 |
-
# ------------------------------------------------------------------
|
| 166 |
-
# Loss computation (the only ModalityRouter-callable entry point)
|
| 167 |
-
# ------------------------------------------------------------------
|
| 168 |
-
|
| 169 |
-
def predict_from_hidden(
|
| 170 |
-
self,
|
| 171 |
-
hidden_states: torch.Tensor,
|
| 172 |
-
**kwargs,
|
| 173 |
-
) -> Optional[torch.Tensor]:
|
| 174 |
-
"""Decode LLM hidden states into a meteo prediction tensor.
|
| 175 |
-
|
| 176 |
-
Pure inference: no loss, no NaN-guard side effects. Reusable by
|
| 177 |
-
the rollout engine, which needs to call this multiple times within
|
| 178 |
-
one optimizer step (warm-up steps + selected step).
|
| 179 |
-
|
| 180 |
-
Returns ``meteo_output`` of shape ``[B, C, H, W]`` (the ``pred_layer``
|
| 181 |
-
currently emits a single-frame prediction; rollout stitches frames
|
| 182 |
-
externally), or ``None`` if no weather pad tokens are present.
|
| 183 |
-
"""
|
| 184 |
-
encoder = self._encoder_ref[0]
|
| 185 |
-
cache = getattr(encoder, "_step_cache", None) or {}
|
| 186 |
-
if not cache:
|
| 187 |
-
return None
|
| 188 |
-
|
| 189 |
-
self._ensure_finite_rope_buffers()
|
| 190 |
-
|
| 191 |
-
condition_embed = cache["condition_embed"]
|
| 192 |
-
patch_embed_post_swin = cache["patch_embed_post_swin"]
|
| 193 |
-
meteo_values = cache["meteo_values"]
|
| 194 |
-
lead_hours = cache["lead_hours"]
|
| 195 |
-
input_size = cache["input_size"]
|
| 196 |
-
T_in = cache["T_in"]
|
| 197 |
-
|
| 198 |
-
input_ids = kwargs.get("input_ids")
|
| 199 |
-
weather_pad_id = self._weather_pad_id_from_config(kwargs)
|
| 200 |
-
if input_ids is None or weather_pad_id is None:
|
| 201 |
-
raise RuntimeError(
|
| 202 |
-
"WeatherDecoder.predict_from_hidden: input_ids / weather pad id missing."
|
| 203 |
-
)
|
| 204 |
-
|
| 205 |
-
meteo_mask = (input_ids == weather_pad_id)
|
| 206 |
-
B, _, D = hidden_states.shape
|
| 207 |
-
L_per_sample = int(meteo_mask.sum(dim=1)[0].item())
|
| 208 |
-
if L_per_sample == 0:
|
| 209 |
-
return None
|
| 210 |
-
meteo_states = hidden_states[meteo_mask].view(B, L_per_sample, D)
|
| 211 |
-
|
| 212 |
-
h = self.mlp_qwen2swin(meteo_states)
|
| 213 |
-
if patch_embed_post_swin is not None:
|
| 214 |
-
h = h + patch_embed_post_swin
|
| 215 |
-
|
| 216 |
-
# Skip gradient checkpointing under torch.no_grad() (e.g. rollout
|
| 217 |
-
# warm-up) — checkpoint() warns and offers no benefit when the
|
| 218 |
-
# outputs don't require grad.
|
| 219 |
-
use_gc = (
|
| 220 |
-
self.gradient_checkpointing
|
| 221 |
-
and self.training
|
| 222 |
-
and torch.is_grad_enabled()
|
| 223 |
-
)
|
| 224 |
-
for blk in self.decoder_layers:
|
| 225 |
-
if use_gc:
|
| 226 |
-
h = checkpoint(
|
| 227 |
-
blk, h, condition_embed, self.freqs_cos, self.freqs_sin,
|
| 228 |
-
use_reentrant=False,
|
| 229 |
-
)
|
| 230 |
-
else:
|
| 231 |
-
h = blk(h, condition_embed, self.freqs_cos, self.freqs_sin)
|
| 232 |
-
|
| 233 |
-
h = self.norm_layer(h, condition_embed)
|
| 234 |
-
h = rearrange(h, "n (h w) c -> n c h w", h=input_size[0] // self.patch_size // 2 * 2)
|
| 235 |
-
meteo_output = self.pred_layer(
|
| 236 |
-
h,
|
| 237 |
-
residual=meteo_values[:, -1],
|
| 238 |
-
input_size=input_size,
|
| 239 |
-
lead_hour=condition_embed.new_tensor([0.0]) if lead_hours is None else lead_hours,
|
| 240 |
-
target_frames=T_in - 1,
|
| 241 |
-
)
|
| 242 |
-
return meteo_output
|
| 243 |
-
|
| 244 |
-
# ------------------------------------------------------------------
|
| 245 |
-
# Helpers
|
| 246 |
-
# ------------------------------------------------------------------
|
| 247 |
-
|
| 248 |
-
@staticmethod
|
| 249 |
-
def _weather_pad_id_from_config(kwargs) -> Optional[int]:
|
| 250 |
-
"""Extract ``<|weather_pad|>`` id.
|
| 251 |
-
|
| 252 |
-
We rely on the model upstream to pass it under the key
|
| 253 |
-
``__weather_pad_id__`` (set by the model wrapper before calling
|
| 254 |
-
``compute_decoder_losses``). Falls back to None when missing.
|
| 255 |
-
"""
|
| 256 |
-
return kwargs.get("__weather_pad_id__")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/encoder.py
DELETED
|
@@ -1,479 +0,0 @@
|
|
| 1 |
-
"""Weather encoder: Polaris Swin-ViT + meteo merger (projector to LLM hidden).
|
| 2 |
-
|
| 3 |
-
Contract:
|
| 4 |
-
|
| 5 |
-
* ``forward(input_ids, attention_mask, **extra_kwargs) -> (latent, mask)``.
|
| 6 |
-
``input_ids`` / ``attention_mask`` are placeholders sized to the number of
|
| 7 |
-
meteo tokens; the real meteorological tensors arrive through ``extra_kwargs``
|
| 8 |
-
(``meteo_values``, ``times``, ``lead_hours``, …) under the ``weather_`` prefix.
|
| 9 |
-
|
| 10 |
-
* The encoder pre-computes ``condition_embed`` and the patch embed and stores
|
| 11 |
-
them in ``self._step_cache`` for the matching ``WeatherDecoder`` to reuse.
|
| 12 |
-
|
| 13 |
-
* Mean / std / nanmask buffers (from ``inject_meteorological_context``) are
|
| 14 |
-
stored on the encoder so ``unnormalize`` / lat-weighting / channel masking
|
| 15 |
-
run without any external wrapper.
|
| 16 |
-
"""
|
| 17 |
-
|
| 18 |
-
from __future__ import annotations
|
| 19 |
-
|
| 20 |
-
import logging
|
| 21 |
-
from typing import Any, Dict, List, Optional, Tuple
|
| 22 |
-
|
| 23 |
-
import numpy as np
|
| 24 |
-
import pandas as pd
|
| 25 |
-
import torch
|
| 26 |
-
import torch.nn as nn
|
| 27 |
-
import torch.nn.functional as F
|
| 28 |
-
from einops import rearrange
|
| 29 |
-
from torch.utils.checkpoint import checkpoint
|
| 30 |
-
|
| 31 |
-
from .internal.polaris_attention import precompute_freqs_cis
|
| 32 |
-
from .internal.polaris_layers import (
|
| 33 |
-
AdaLN,
|
| 34 |
-
CubeEmbedConv,
|
| 35 |
-
LayerNorm,
|
| 36 |
-
PatchEmbedConv,
|
| 37 |
-
TimestepEmbed,
|
| 38 |
-
)
|
| 39 |
-
from .internal.polaris_swin import SwinBlock
|
| 40 |
-
from .internal.helpers import round_to_multiple
|
| 41 |
-
|
| 42 |
-
try:
|
| 43 |
-
from transformers.models.qwen2.modeling_qwen2 import Qwen2RMSNorm as _MergerNorm
|
| 44 |
-
except Exception: # pragma: no cover
|
| 45 |
-
_MergerNorm = nn.LayerNorm
|
| 46 |
-
|
| 47 |
-
logger = logging.getLogger(__name__)
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
class PolarisMeteoPatchMerger(nn.Module):
|
| 51 |
-
"""Cross-modal projector ('merger'): Swin hidden → LLM hidden.
|
| 52 |
-
|
| 53 |
-
Same arithmetic as the original Polaris ``PolarisMeteoPatchMerger``;
|
| 54 |
-
serves as the modality projector for the ``ModalityRouter`` (an
|
| 55 |
-
``IdentityProjector`` is registered separately so the router can scatter
|
| 56 |
-
its output directly).
|
| 57 |
-
"""
|
| 58 |
-
|
| 59 |
-
def __init__(self, dim: int, context_dim: int) -> None:
|
| 60 |
-
super().__init__()
|
| 61 |
-
self.hidden_size = context_dim
|
| 62 |
-
self.ln_q = _MergerNorm(context_dim, eps=1e-6)
|
| 63 |
-
self.mlp = nn.Sequential(
|
| 64 |
-
nn.Linear(self.hidden_size, self.hidden_size),
|
| 65 |
-
nn.GELU(),
|
| 66 |
-
nn.Linear(self.hidden_size, dim),
|
| 67 |
-
)
|
| 68 |
-
|
| 69 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 70 |
-
bsz, seqlen, _ = x.shape
|
| 71 |
-
x = self.ln_q(x)
|
| 72 |
-
x = x.view(-1, self.hidden_size)
|
| 73 |
-
x = self.mlp(x)
|
| 74 |
-
x = x.view(bsz, seqlen, -1)
|
| 75 |
-
return x
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
class WeatherEncoder(nn.Module):
|
| 79 |
-
"""Polaris-style Swin encoder + meteo merger.
|
| 80 |
-
|
| 81 |
-
The forward signature is dictated by ``ModalityRouter.encode_and_project``:
|
| 82 |
-
it takes ``input_ids`` / ``attention_mask`` (here used only to derive a
|
| 83 |
-
batch size; their real meaning is conveyed by the placeholder pad
|
| 84 |
-
tokens scattered into the LLM input embeddings) and a number of
|
| 85 |
-
keyword-only extras forwarded by the collator under the ``weather_``
|
| 86 |
-
prefix (already stripped by the router).
|
| 87 |
-
"""
|
| 88 |
-
|
| 89 |
-
# Exposed so the router can route per-modality state_dict keys cleanly.
|
| 90 |
-
is_image_like = True
|
| 91 |
-
|
| 92 |
-
def __init__(self, config):
|
| 93 |
-
super().__init__()
|
| 94 |
-
self.config = config
|
| 95 |
-
self.hidden_size = config.hidden_size
|
| 96 |
-
self.image_size = config.image_size
|
| 97 |
-
self.patch_size = config.patch_size
|
| 98 |
-
self.window_size = config.window_size
|
| 99 |
-
self.encoder_depth = config.encoder_depth
|
| 100 |
-
self.num_heads = config.num_heads
|
| 101 |
-
self.const_chans = config.const_chans
|
| 102 |
-
|
| 103 |
-
self.patch_embed = CubeEmbedConv(
|
| 104 |
-
in_chans=config.in_chans,
|
| 105 |
-
out_chans=self.hidden_size,
|
| 106 |
-
in_frames=config.in_frames,
|
| 107 |
-
norm_func=None,
|
| 108 |
-
flatten=True,
|
| 109 |
-
patch_size=self.patch_size,
|
| 110 |
-
)
|
| 111 |
-
|
| 112 |
-
if self.const_chans > 0:
|
| 113 |
-
self.const_embed = PatchEmbedConv(
|
| 114 |
-
self.const_chans, self.hidden_size, config.patch_size,
|
| 115 |
-
norm_func=LayerNorm, flatten=True,
|
| 116 |
-
)
|
| 117 |
-
|
| 118 |
-
self.embed_mode = config.embed_mode
|
| 119 |
-
self.embed_types = list(config.embed_types)
|
| 120 |
-
self.embed_freq = config.embed_freq
|
| 121 |
-
self._init_time_embeddings()
|
| 122 |
-
|
| 123 |
-
if isinstance(config.image_size, int):
|
| 124 |
-
in_h = in_w = config.image_size
|
| 125 |
-
else:
|
| 126 |
-
in_h, in_w = config.image_size
|
| 127 |
-
if self.patch_size == 1:
|
| 128 |
-
swin_H = in_h // 2 * 2
|
| 129 |
-
swin_W = in_w
|
| 130 |
-
else:
|
| 131 |
-
swin_H = in_h // self.patch_size
|
| 132 |
-
swin_W = in_w // self.patch_size
|
| 133 |
-
self._swin_HW = (swin_H, swin_W)
|
| 134 |
-
|
| 135 |
-
self.encoder_layers = nn.ModuleList()
|
| 136 |
-
for i in range(self.encoder_depth):
|
| 137 |
-
blk = SwinBlock(
|
| 138 |
-
dim=self.hidden_size, embed_dim=self.embed_dim, num_heads=self.num_heads,
|
| 139 |
-
input_size=(swin_H, swin_W), window_size=self.window_size,
|
| 140 |
-
shift_size=0 if i % 2 == 0 else self.window_size // 2,
|
| 141 |
-
mlp_ratio=config.mlp_ratio, attn_type=config.attn_type,
|
| 142 |
-
mask_type=config.mask_type, norm_type=config.norm_type,
|
| 143 |
-
ffn_type=config.ffn_type, n_kv_heads=config.n_kv_heads,
|
| 144 |
-
attn_implementation="eager",
|
| 145 |
-
)
|
| 146 |
-
self.encoder_layers.append(blk)
|
| 147 |
-
|
| 148 |
-
# Cross-modal merger: Swin hidden → LLM hidden (qwenvl_dim).
|
| 149 |
-
# `ModalityRouter` registers an IdentityProjector for `weather` since
|
| 150 |
-
# the upscaling already happens here.
|
| 151 |
-
self.meteo_merger = PolarisMeteoPatchMerger(
|
| 152 |
-
dim=config.qwenvl_dim, context_dim=self.hidden_size,
|
| 153 |
-
)
|
| 154 |
-
|
| 155 |
-
max_seq_len = swin_H * swin_W
|
| 156 |
-
freqs_cos, freqs_sin = precompute_freqs_cis(self.hidden_size // self.num_heads, max_seq_len)
|
| 157 |
-
self.register_buffer("freqs_cos", freqs_cos, persistent=False)
|
| 158 |
-
self.register_buffer("freqs_sin", freqs_sin, persistent=False)
|
| 159 |
-
|
| 160 |
-
self.gradient_checkpointing = False
|
| 161 |
-
|
| 162 |
-
# ── Lazy data buffers (mean/std/nanmask/const/weight/...) ───
|
| 163 |
-
# Populated by ``inject_meteorological_context``. Held in fp32
|
| 164 |
-
# CPU tensors and copied to device on demand to keep memory low.
|
| 165 |
-
self._polaris_data_loaded = False
|
| 166 |
-
self.channels: List[str] = []
|
| 167 |
-
self.indices: Dict[str, List[int]] = {}
|
| 168 |
-
self.coords: Dict[str, list] = {}
|
| 169 |
-
self._fp32_master: Dict[str, torch.Tensor] = {}
|
| 170 |
-
|
| 171 |
-
# Per-forward cache shared with the matching WeatherDecoder.
|
| 172 |
-
# Cleared at the start of every encoder forward to avoid leaking
|
| 173 |
-
# graph-disconnected tensors across steps.
|
| 174 |
-
self._step_cache: Dict[str, Any] = {}
|
| 175 |
-
|
| 176 |
-
# ------------------------------------------------------------------
|
| 177 |
-
# RoPE buffer self-heal
|
| 178 |
-
# ------------------------------------------------------------------
|
| 179 |
-
|
| 180 |
-
def _ensure_rope_buffers(self) -> None:
|
| 181 |
-
"""Rebuild the RoPE tables if the non-persistent buffers came up broken.
|
| 182 |
-
|
| 183 |
-
``freqs_cos`` / ``freqs_sin`` are ``persistent=False`` (not in the
|
| 184 |
-
checkpoint) and are meant to be filled at ``__init__``. Under a
|
| 185 |
-
meta-device ``from_pretrained`` load they can be ``to_empty``'d to
|
| 186 |
-
uninitialised memory. A valid table has ``cos[0]==1`` and ``sin[0]==0``
|
| 187 |
-
for every column — checking row 0 catches garbage that is finite (large
|
| 188 |
-
floats) which an isfinite-only check would miss.
|
| 189 |
-
"""
|
| 190 |
-
with torch.no_grad():
|
| 191 |
-
row0 = self.freqs_cos[0].detach().to(torch.float32)
|
| 192 |
-
row0_sin = self.freqs_sin[0].detach().to(torch.float32)
|
| 193 |
-
needs_recompute = (
|
| 194 |
-
not torch.isfinite(self.freqs_cos).all()
|
| 195 |
-
or not torch.isfinite(self.freqs_sin).all()
|
| 196 |
-
or not torch.allclose(row0, torch.ones_like(row0), atol=1e-5)
|
| 197 |
-
or not torch.allclose(row0_sin, torch.zeros_like(row0_sin), atol=1e-5)
|
| 198 |
-
)
|
| 199 |
-
if not needs_recompute:
|
| 200 |
-
return
|
| 201 |
-
head_dim = self.hidden_size // self.num_heads
|
| 202 |
-
max_seq_len = self.freqs_cos.shape[0]
|
| 203 |
-
new_cos, new_sin = precompute_freqs_cis(head_dim, max_seq_len)
|
| 204 |
-
with torch.no_grad():
|
| 205 |
-
self.freqs_cos.copy_(new_cos.to(self.freqs_cos.device, self.freqs_cos.dtype))
|
| 206 |
-
self.freqs_sin.copy_(new_sin.to(self.freqs_sin.device, self.freqs_sin.dtype))
|
| 207 |
-
|
| 208 |
-
# ------------------------------------------------------------------
|
| 209 |
-
# Time-conditioning
|
| 210 |
-
# ------------------------------------------------------------------
|
| 211 |
-
|
| 212 |
-
def _init_time_embeddings(self):
|
| 213 |
-
if self.embed_mode == "add":
|
| 214 |
-
self.embed_dim = self.hidden_size
|
| 215 |
-
elif self.embed_mode == "cat":
|
| 216 |
-
self.embed_dim = round_to_multiple(self.hidden_size // len(self.embed_types)) * len(self.embed_types)
|
| 217 |
-
else:
|
| 218 |
-
raise ValueError(f"Invalid embed_mode: {self.embed_mode}")
|
| 219 |
-
|
| 220 |
-
# Optional dropout on time-embedding outputs (anti-overfit for
|
| 221 |
-
# ``hour`` / ``doy``). Read from config; default 0.0 keeps the
|
| 222 |
-
# legacy zero-dropout behaviour identical to upstream Polaris.
|
| 223 |
-
time_embed_dropout = float(getattr(self.config, "time_embed_dropout", 0.0))
|
| 224 |
-
for k in self.embed_types:
|
| 225 |
-
embed_layer = TimestepEmbed(
|
| 226 |
-
self.embed_dim, frequency=self.embed_freq,
|
| 227 |
-
is_periodic=(k in ["hour", "doy"]), sinusoidal=True,
|
| 228 |
-
dropout=time_embed_dropout,
|
| 229 |
-
)
|
| 230 |
-
self.add_module(f"{k}_embed", embed_layer)
|
| 231 |
-
|
| 232 |
-
def _forward_embedding(self, conds: Dict[str, torch.Tensor]) -> torch.Tensor:
|
| 233 |
-
if self.embed_mode == "add":
|
| 234 |
-
embed = 0
|
| 235 |
-
for k in self.embed_types:
|
| 236 |
-
embedding = getattr(self, f"{k}_embed")
|
| 237 |
-
embed = embed + embedding(conds[k])
|
| 238 |
-
return embed
|
| 239 |
-
# cat
|
| 240 |
-
embeds = [getattr(self, f"{k}_embed")(conds[k]) for k in self.embed_types]
|
| 241 |
-
return torch.cat(embeds, dim=1)
|
| 242 |
-
|
| 243 |
-
# ------------------------------------------------------------------
|
| 244 |
-
# Meteo data buffer management
|
| 245 |
-
# ------------------------------------------------------------------
|
| 246 |
-
|
| 247 |
-
def inject_meteorological_context(self, channels, indices, coords, buffers):
|
| 248 |
-
"""Inject ERA5 mean/std/nanmask buffers onto this encoder.
|
| 249 |
-
|
| 250 |
-
Stores fp32 tensors on CPU; ``_get_fp32_const`` copies to device on
|
| 251 |
-
demand. Mirrors the original Polaris API so external loaders
|
| 252 |
-
(``polaris_data_utils.load_meteorological_buffers``) work unchanged.
|
| 253 |
-
"""
|
| 254 |
-
self.channels = channels
|
| 255 |
-
self.indices = indices
|
| 256 |
-
self.coords = coords
|
| 257 |
-
for k, v in buffers.items():
|
| 258 |
-
if isinstance(v, np.ndarray):
|
| 259 |
-
t = torch.from_numpy(v).float()
|
| 260 |
-
elif torch.is_tensor(v):
|
| 261 |
-
t = v.detach().float()
|
| 262 |
-
else:
|
| 263 |
-
setattr(self, k, v)
|
| 264 |
-
continue
|
| 265 |
-
self._fp32_master[k] = t.cpu()
|
| 266 |
-
self._polaris_data_loaded = True
|
| 267 |
-
|
| 268 |
-
def _get_fp32_const(self, name: str, device: torch.device) -> torch.Tensor:
|
| 269 |
-
if not self._polaris_data_loaded or name not in self._fp32_master:
|
| 270 |
-
raise RuntimeError(
|
| 271 |
-
f"WeatherEncoder data not loaded! Call inject_meteorological_context() "
|
| 272 |
-
f"before using '{name}'."
|
| 273 |
-
)
|
| 274 |
-
return self._fp32_master[name].to(device=device, dtype=torch.float32, non_blocking=True)
|
| 275 |
-
|
| 276 |
-
# ------------------------------------------------------------------
|
| 277 |
-
# Input pre-processing (replicates Polaris reset_input / nan handling)
|
| 278 |
-
# ------------------------------------------------------------------
|
| 279 |
-
|
| 280 |
-
def _reset_output(self, x: torch.Tensor) -> torch.Tensor:
|
| 281 |
-
if "nanmask" in self._fp32_master:
|
| 282 |
-
nanmask = self._fp32_master["nanmask"].to(x.device)[: x.shape[-3]]
|
| 283 |
-
x = x * (~nanmask)
|
| 284 |
-
if "channel_mask" in self._fp32_master:
|
| 285 |
-
cmask = self._fp32_master["channel_mask"].to(x.device)[: x.shape[-3]]
|
| 286 |
-
x = x * cmask
|
| 287 |
-
return x
|
| 288 |
-
|
| 289 |
-
def _reset_input(self, x: torch.Tensor) -> torch.Tensor:
|
| 290 |
-
x = self._reset_output(x)
|
| 291 |
-
accumid = self.indices.get("accumid", [])
|
| 292 |
-
if len(accumid) > 0:
|
| 293 |
-
if torch.is_grad_enabled():
|
| 294 |
-
x = x.clone()
|
| 295 |
-
x[:, :, accumid] = 0
|
| 296 |
-
return x
|
| 297 |
-
|
| 298 |
-
# ------------------------------------------------------------------
|
| 299 |
-
# Conditioning factory (hour, doy, step, lead_hour, optional const)
|
| 300 |
-
# ------------------------------------------------------------------
|
| 301 |
-
|
| 302 |
-
def get_condition(
|
| 303 |
-
self,
|
| 304 |
-
t: int,
|
| 305 |
-
times,
|
| 306 |
-
lead_hour,
|
| 307 |
-
device: torch.device,
|
| 308 |
-
dtype: torch.dtype,
|
| 309 |
-
) -> Dict[str, torch.Tensor]:
|
| 310 |
-
times = pd.DatetimeIndex(times)
|
| 311 |
-
B = len(times)
|
| 312 |
-
|
| 313 |
-
if isinstance(lead_hour, torch.Tensor):
|
| 314 |
-
lead_np = lead_hour.detach().to(dtype=torch.float32, device="cpu").numpy()
|
| 315 |
-
else:
|
| 316 |
-
lead_np = np.asarray(lead_hour, dtype=np.float32)
|
| 317 |
-
|
| 318 |
-
if lead_np.ndim == 0:
|
| 319 |
-
lead_np = np.full((B,), float(lead_np), dtype=np.float32)
|
| 320 |
-
elif lead_np.size == 1 and B > 1:
|
| 321 |
-
lead_np = np.full((B,), float(lead_np.reshape(-1)[0]), dtype=np.float32)
|
| 322 |
-
elif lead_np.size != B:
|
| 323 |
-
raise ValueError(f"lead_hour size mismatch: got {lead_np.size}, expected {B}")
|
| 324 |
-
|
| 325 |
-
cur_times = times + pd.to_timedelta(lead_np * t, unit="h")
|
| 326 |
-
used_times = cur_times
|
| 327 |
-
|
| 328 |
-
hour_np = used_times.hour.values.astype(np.float32) / 24.0
|
| 329 |
-
doy_np = (np.minimum(365, used_times.dayofyear.values).astype(np.float32) / 365.0)
|
| 330 |
-
|
| 331 |
-
hour = torch.tensor(hour_np, dtype=dtype, device=device)
|
| 332 |
-
doy = torch.tensor(doy_np, dtype=dtype, device=device)
|
| 333 |
-
|
| 334 |
-
Tmax = getattr(self.config, "max_rollout_steps", 200)
|
| 335 |
-
step_scalar = float(np.log1p(float(t)) / np.log1p(float(Tmax)))
|
| 336 |
-
step = torch.full((B,), step_scalar, dtype=dtype, device=device)
|
| 337 |
-
if self.training:
|
| 338 |
-
step = step + torch.empty((B,), device=device, dtype=dtype).uniform_(-0.005, 0.005)
|
| 339 |
-
|
| 340 |
-
lead_hour_t = torch.tensor(lead_np, dtype=dtype, device=device)
|
| 341 |
-
|
| 342 |
-
conds = dict(hour=hour, doy=doy, step=step, lead_hour=lead_hour_t)
|
| 343 |
-
if "const" in self._fp32_master:
|
| 344 |
-
conds["const"] = self._get_fp32_const("const", device).type(dtype)
|
| 345 |
-
return conds
|
| 346 |
-
|
| 347 |
-
# ------------------------------------------------------------------
|
| 348 |
-
# Encoder forward (ModalityRouter contract)
|
| 349 |
-
# ------------------------------------------------------------------
|
| 350 |
-
|
| 351 |
-
def forward(
|
| 352 |
-
self,
|
| 353 |
-
input_ids: torch.Tensor,
|
| 354 |
-
attention_mask: torch.Tensor,
|
| 355 |
-
meteo_values: Optional[torch.Tensor] = None,
|
| 356 |
-
targets: Optional[torch.Tensor] = None,
|
| 357 |
-
times: Optional[Any] = None,
|
| 358 |
-
lead_hours: Optional[torch.Tensor] = None,
|
| 359 |
-
polaris_task: Optional[Any] = None,
|
| 360 |
-
channel_mask: Optional[torch.Tensor] = None,
|
| 361 |
-
step_idx: int = 0,
|
| 362 |
-
**_unused,
|
| 363 |
-
) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
|
| 364 |
-
"""Encode meteorological inputs.
|
| 365 |
-
|
| 366 |
-
Args:
|
| 367 |
-
input_ids / attention_mask: dummy placeholders sized
|
| 368 |
-
``[B, swin_H * swin_W]``. Required by the router signature
|
| 369 |
-
but not used computationally.
|
| 370 |
-
meteo_values: ``[B, T_in, C, H, W]`` float32 (smuggled as int32 by
|
| 371 |
-
the collator and reinterpreted upstream) — already on device.
|
| 372 |
-
times: pandas timestamps for the current batch (one per sample).
|
| 373 |
-
lead_hours: ``[B]`` int / float — forecast lead hours.
|
| 374 |
-
channel_mask: optional ``[B, C, 1, 1]`` channel mask.
|
| 375 |
-
step_idx: int rollout step index (0 for single-step training).
|
| 376 |
-
|
| 377 |
-
Returns:
|
| 378 |
-
latent: ``[B, swin_H * swin_W, qwenvl_dim]`` after meteo_merger.
|
| 379 |
-
latent_mask: None.
|
| 380 |
-
"""
|
| 381 |
-
if meteo_values is None:
|
| 382 |
-
raise ValueError("WeatherEncoder.forward requires `meteo_values` in extras.")
|
| 383 |
-
|
| 384 |
-
# Rebuild RoPE freqs if the checkpoint load left them uninitialised
|
| 385 |
-
# (non-persistent buffers under a meta-device load come up as garbage).
|
| 386 |
-
self._ensure_rope_buffers()
|
| 387 |
-
|
| 388 |
-
# NaN→zero, fp32 normalisation
|
| 389 |
-
if meteo_values.dtype == torch.int32:
|
| 390 |
-
meteo_values = meteo_values.contiguous().view(torch.float32)
|
| 391 |
-
meteo_values = meteo_values.contiguous().to(torch.float32)
|
| 392 |
-
nanmask_local = torch.isnan(meteo_values[-1, -1])
|
| 393 |
-
if torch.any(nanmask_local):
|
| 394 |
-
self._fp32_master["nanmask"] = nanmask_local.cpu()
|
| 395 |
-
meteo_values = torch.nan_to_num(meteo_values)
|
| 396 |
-
meteo_values = self._reset_input(meteo_values)
|
| 397 |
-
if channel_mask is not None:
|
| 398 |
-
self._fp32_master["channel_mask"] = channel_mask[0].cpu()
|
| 399 |
-
|
| 400 |
-
device = meteo_values.device
|
| 401 |
-
# Compute conditions & feed encoder. Use the encoder's compute dtype
|
| 402 |
-
# (e.g. bf16) — meteo_values is fp32 here due to the upstream
|
| 403 |
-
# NaN/normalisation cast, but the embedding MLPs run in bf16 and
|
| 404 |
-
# would otherwise see a dtype mismatch in F.linear.
|
| 405 |
-
conds = self.get_condition(step_idx, times, lead_hours, device=device, dtype=self._embed_dtype())
|
| 406 |
-
|
| 407 |
-
meteo_compute = meteo_values.to(self._embed_dtype())
|
| 408 |
-
meteo_feature = self.patch_embed(meteo_compute, conds["lead_hour"])
|
| 409 |
-
patch_embed = meteo_feature
|
| 410 |
-
|
| 411 |
-
if self.const_chans > 0 and "const" in conds:
|
| 412 |
-
const = conds["const"].to(device=device, dtype=meteo_compute.dtype)
|
| 413 |
-
meteo_feature = meteo_feature + self.const_embed(const)
|
| 414 |
-
|
| 415 |
-
condition_embed = self._forward_embedding(conds)
|
| 416 |
-
|
| 417 |
-
# Skip gradient checkpointing under torch.no_grad() — e.g. rollout
|
| 418 |
-
# warm-up steps — otherwise PyTorch emits a per-block "None of the
|
| 419 |
-
# inputs have requires_grad=True" warning and `checkpoint` adds
|
| 420 |
-
# pointless bookkeeping for a path that won't backprop anyway.
|
| 421 |
-
use_gc = (
|
| 422 |
-
self.gradient_checkpointing
|
| 423 |
-
and self.training
|
| 424 |
-
and torch.is_grad_enabled()
|
| 425 |
-
)
|
| 426 |
-
for blk in self.encoder_layers:
|
| 427 |
-
if use_gc:
|
| 428 |
-
meteo_feature = checkpoint(
|
| 429 |
-
blk, meteo_feature, condition_embed, self.freqs_cos, self.freqs_sin,
|
| 430 |
-
use_reentrant=False,
|
| 431 |
-
)
|
| 432 |
-
else:
|
| 433 |
-
meteo_feature = blk(meteo_feature, condition_embed, self.freqs_cos, self.freqs_sin)
|
| 434 |
-
|
| 435 |
-
# `meteo_feature` is the post-swin encoder output (Polaris's
|
| 436 |
-
# `meteo_embeds`). The matching head uses it as the skip connection
|
| 437 |
-
# *after* mlp_qwen2swin, so cache before applying the merger.
|
| 438 |
-
meteo_embeds_post_swin = meteo_feature
|
| 439 |
-
|
| 440 |
-
merged = self.meteo_merger(meteo_feature) # → [B, L, qwenvl_dim]
|
| 441 |
-
|
| 442 |
-
# Cache for the decoder. We deliberately keep references to all
|
| 443 |
-
# the intermediate tensors needed by the head so the decoder can
|
| 444 |
-
# reuse them without recomputing the encoder side.
|
| 445 |
-
# ``patch_embed_post_swin`` matches the Polaris ``meteo_embeds``
|
| 446 |
-
# variable used in ``polaris_head(meteo_feature_patch_embed=...)``
|
| 447 |
-
# under the use_language=True flow — i.e. the encoder swin output.
|
| 448 |
-
self._step_cache = {
|
| 449 |
-
"condition_embed": condition_embed,
|
| 450 |
-
"patch_embed_post_swin": meteo_embeds_post_swin,
|
| 451 |
-
"meteo_values": meteo_values,
|
| 452 |
-
"targets": targets,
|
| 453 |
-
"times": times,
|
| 454 |
-
"lead_hours": lead_hours,
|
| 455 |
-
"input_size": meteo_values.shape[-2:],
|
| 456 |
-
"T_in": meteo_values.shape[1],
|
| 457 |
-
}
|
| 458 |
-
|
| 459 |
-
# Mask is None — every meteo token is valid; the router will infer
|
| 460 |
-
# the count from the pad-token mask.
|
| 461 |
-
return merged, None
|
| 462 |
-
|
| 463 |
-
def _embed_dtype(self) -> torch.dtype:
|
| 464 |
-
# Canonical encoder compute dtype (handles bf16 / fp32 uniformly).
|
| 465 |
-
for p in self.parameters():
|
| 466 |
-
return p.dtype
|
| 467 |
-
return torch.float32
|
| 468 |
-
|
| 469 |
-
@torch.no_grad()
|
| 470 |
-
def unnormalize(self, x: torch.Tensor, fill_type: str = "zero") -> torch.Tensor:
|
| 471 |
-
"""Undo channel-wise mean/std normalisation. fp32 throughout."""
|
| 472 |
-
mean = self._get_fp32_const("mean", x.device)
|
| 473 |
-
std = self._get_fp32_const("std", x.device)
|
| 474 |
-
x = x.to(torch.float32) * std + mean
|
| 475 |
-
logid = self.indices.get("logid", [])
|
| 476 |
-
if len(logid) > 0:
|
| 477 |
-
v = x[:, :, logid].clamp(min=0, max=7)
|
| 478 |
-
x[:, :, logid] = torch.expm1(v)
|
| 479 |
-
return self._reset_output(x)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/internal/__init__.py
DELETED
|
@@ -1,11 +0,0 @@
|
|
| 1 |
-
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
-
# Portions adapted from the Polaris weather-forecasting codebase (released here
|
| 3 |
-
# under Apache-2.0 with the authors' permission) and from the Swin Transformer
|
| 4 |
-
# (Microsoft, MIT). See THIRD_PARTY_LICENSES.md at the repo root.
|
| 5 |
-
"""Internal Polaris support modules (attention, layers, swin, helpers).
|
| 6 |
-
|
| 7 |
-
These are adapted from the original Polaris codebase and keep the Swin
|
| 8 |
-
Transformer window-attention / patch-merging design. They are kept as a private
|
| 9 |
-
submodule so the public weather encoder/decoder/projector can pin against a
|
| 10 |
-
stable internal API.
|
| 11 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/internal/helpers.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
| 1 |
-
import collections.abc
|
| 2 |
-
from itertools import repeat
|
| 3 |
-
from inspect import isfunction
|
| 4 |
-
from torch.nn import functional as F
|
| 5 |
-
|
| 6 |
-
__all__ = ["to_2tuple", "round_to_multiple", "exists", "default", "append_dims"]
|
| 7 |
-
|
| 8 |
-
def _ntuple(n):
|
| 9 |
-
def parse(x):
|
| 10 |
-
if isinstance(x, collections.abc.Iterable):
|
| 11 |
-
return x
|
| 12 |
-
return tuple(repeat(x, n))
|
| 13 |
-
return parse
|
| 14 |
-
|
| 15 |
-
to_1tuple = _ntuple(1)
|
| 16 |
-
to_2tuple = _ntuple(2)
|
| 17 |
-
to_3tuple = _ntuple(3)
|
| 18 |
-
to_4tuple = _ntuple(4)
|
| 19 |
-
to_ntuple = _ntuple
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
def round_to_multiple(x: int, multiple_of: int = 32) -> int:
|
| 23 |
-
"""Round up x to the nearest multiple of multiple_of."""
|
| 24 |
-
return int(multiple_of * ((x + multiple_of - 1) // multiple_of))
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
def exists(val):
|
| 28 |
-
return val is not None
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
def default(val, d):
|
| 32 |
-
if exists(val):
|
| 33 |
-
return val
|
| 34 |
-
return d() if isfunction(d) else d
|
| 35 |
-
|
| 36 |
-
def append_dims(x, target_dims):
|
| 37 |
-
"""Appends dimensions to the end of a tensor until it has target_dims dimensions."""
|
| 38 |
-
dims_to_append = target_dims - x.ndim
|
| 39 |
-
if dims_to_append < 0:
|
| 40 |
-
raise ValueError(
|
| 41 |
-
f"input has {x.ndim} dims but target_dims is {target_dims}, which is less"
|
| 42 |
-
)
|
| 43 |
-
return x[(...,) + (None,) * dims_to_append]
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/internal/polaris_attention.py
DELETED
|
@@ -1,198 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import torch
|
| 3 |
-
from typing import Optional
|
| 4 |
-
from torch import nn
|
| 5 |
-
import torch.nn.functional as F
|
| 6 |
-
from einops import rearrange
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
__all__ = ["FlashAttention", "precompute_freqs_cis"]
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class RMSNorm(nn.Module):
|
| 13 |
-
def __init__(self, dim: int, eps: float = 1e-6, compile: bool = False):
|
| 14 |
-
super().__init__()
|
| 15 |
-
self.eps = eps
|
| 16 |
-
self.weight = nn.Parameter(torch.ones(dim))
|
| 17 |
-
self.rmsnorm_fn = (
|
| 18 |
-
torch.compile(self.compute_rmsnorm, fullgraph=True)
|
| 19 |
-
if compile
|
| 20 |
-
else self.compute_rmsnorm
|
| 21 |
-
)
|
| 22 |
-
|
| 23 |
-
@staticmethod
|
| 24 |
-
def compute_rmsnorm(x: torch.Tensor, weight: torch.Tensor, eps: float):
|
| 25 |
-
def _norm(x, eps):
|
| 26 |
-
return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + eps)
|
| 27 |
-
|
| 28 |
-
output = _norm(x.float(), eps).type_as(x)
|
| 29 |
-
return output * weight
|
| 30 |
-
|
| 31 |
-
def forward(self, x: torch.Tensor):
|
| 32 |
-
return self.rmsnorm_fn(x, self.weight, self.eps)
|
| 33 |
-
|
| 34 |
-
def reset_parameters(self):
|
| 35 |
-
torch.nn.init.ones_(self.weight) # type: ignore
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def precompute_freqs_cis(dim: int, end: int, theta: float = 10000.0):
|
| 39 |
-
freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
|
| 40 |
-
t = torch.arange(end, device=freqs.device) # type: ignore
|
| 41 |
-
freqs = torch.outer(t, freqs).float() # type: ignore
|
| 42 |
-
freqs_cos = torch.cos(freqs) # real part
|
| 43 |
-
freqs_sin = torch.sin(freqs) # imaginary part
|
| 44 |
-
return freqs_cos, freqs_sin
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor):
|
| 48 |
-
ndim = x.ndim
|
| 49 |
-
assert 0 <= 1 < ndim
|
| 50 |
-
assert freqs_cis.shape == (x.shape[1], x.shape[-1])
|
| 51 |
-
shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
|
| 52 |
-
return freqs_cis.view(shape)
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
def apply_rotary_emb(xq: torch.Tensor, xk: torch.Tensor, freqs_cos: torch.Tensor, freqs_sin: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
| 56 |
-
xq, xk = xq.contiguous(), xk.contiguous()
|
| 57 |
-
freqs_cos, freqs_sin = freqs_cos.contiguous(), freqs_sin.contiguous()
|
| 58 |
-
|
| 59 |
-
xq_r, xq_i = xq.float().reshape(*xq.shape[:-1], -1, 2).unbind(-1)
|
| 60 |
-
xk_r, xk_i = xk.float().reshape(*xk.shape[:-1], -1, 2).unbind(-1)
|
| 61 |
-
|
| 62 |
-
seq_len = xq_r.shape[1]
|
| 63 |
-
assert seq_len <= freqs_cos.shape[0], (
|
| 64 |
-
f"seq_len={seq_len} exceeds precomputed freqs buffer size={freqs_cos.shape[0]}"
|
| 65 |
-
)
|
| 66 |
-
freqs_cos = freqs_cos[:seq_len]
|
| 67 |
-
freqs_sin = freqs_sin[:seq_len]
|
| 68 |
-
|
| 69 |
-
freqs_cos = rearrange(freqs_cos, "n c -> 1 n 1 c")
|
| 70 |
-
freqs_sin = rearrange(freqs_sin, "n c -> 1 n 1 c")
|
| 71 |
-
|
| 72 |
-
# Apply rotation using real numbers
|
| 73 |
-
xq_out_r = xq_r * freqs_cos - xq_i * freqs_sin
|
| 74 |
-
xq_out_i = xq_r * freqs_sin + xq_i * freqs_cos
|
| 75 |
-
xk_out_r = xk_r * freqs_cos - xk_i * freqs_sin
|
| 76 |
-
xk_out_i = xk_r * freqs_sin + xk_i * freqs_cos
|
| 77 |
-
|
| 78 |
-
# Combine real and imaginary parts
|
| 79 |
-
xq_out = torch.stack([xq_out_r, xq_out_i], dim=-1).flatten(-2)
|
| 80 |
-
xk_out = torch.stack([xk_out_r, xk_out_i], dim=-1).flatten(-2)
|
| 81 |
-
|
| 82 |
-
return xq_out.to(xq.dtype), xk_out.to(xk.dtype)
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
def repeat_kv(x: torch.Tensor, n_rep: int) -> torch.Tensor:
|
| 86 |
-
"""torch.repeat_interleave(x, dim=2, repeats=n_rep)"""
|
| 87 |
-
bs, slen, n_kv_heads, head_dim = x.shape
|
| 88 |
-
if n_rep == 1:
|
| 89 |
-
return x
|
| 90 |
-
return (
|
| 91 |
-
x[:, :, :, None, :]
|
| 92 |
-
.expand(bs, slen, n_kv_heads, n_rep, head_dim)
|
| 93 |
-
.reshape(bs, slen, n_kv_heads * n_rep, head_dim)
|
| 94 |
-
)
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
class FlashAttention(nn.Module):
|
| 99 |
-
def __init__(
|
| 100 |
-
self,
|
| 101 |
-
dim: int,
|
| 102 |
-
n_heads: int = 32,
|
| 103 |
-
n_kv_heads: Optional[int] = None,
|
| 104 |
-
max_seq_len: Optional[int] = None,
|
| 105 |
-
dropout: float = 0.0,
|
| 106 |
-
is_causal: bool = False,
|
| 107 |
-
attn_implementation = None,
|
| 108 |
-
):
|
| 109 |
-
super().__init__()
|
| 110 |
-
|
| 111 |
-
self.n_heads = n_heads
|
| 112 |
-
self.n_kv_heads = n_heads if n_kv_heads is None else n_kv_heads
|
| 113 |
-
self.n_rep = self.n_heads // self.n_kv_heads
|
| 114 |
-
self.head_dim = dim // n_heads
|
| 115 |
-
|
| 116 |
-
self.wq = nn.Linear(dim, n_heads * self.head_dim, bias=False)
|
| 117 |
-
self.wk = nn.Linear(dim, self.n_kv_heads * self.head_dim, bias=False)
|
| 118 |
-
self.wv = nn.Linear(dim, self.n_kv_heads * self.head_dim, bias=False)
|
| 119 |
-
self.wo = nn.Linear(n_heads * self.head_dim, dim, bias=False)
|
| 120 |
-
|
| 121 |
-
self.attn_dropout = nn.Dropout(dropout)
|
| 122 |
-
self.resid_dropout = nn.Dropout(dropout)
|
| 123 |
-
self.dropout = dropout
|
| 124 |
-
self.max_seq_len = max_seq_len
|
| 125 |
-
self.is_causal = is_causal
|
| 126 |
-
|
| 127 |
-
def init_weights(self, init_std: float):
|
| 128 |
-
for linear in (self.wq, self.wk, self.wv):
|
| 129 |
-
nn.init.trunc_normal_(linear.weight, mean=0.0, std=0.02)
|
| 130 |
-
nn.init.trunc_normal_(self.wo.weight, mean=0.0, std=init_std)
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
def forward(
|
| 134 |
-
self,
|
| 135 |
-
x: torch.Tensor,
|
| 136 |
-
freqs_cos: torch.Tensor=None,
|
| 137 |
-
freqs_sin: torch.Tensor=None,
|
| 138 |
-
mask: torch.Tensor = None,
|
| 139 |
-
):
|
| 140 |
-
bsz, seq_len, _ = x.shape
|
| 141 |
-
# QKV
|
| 142 |
-
xq, xk, xv = self.wq(x), self.wk(x), self.wv(x)
|
| 143 |
-
|
| 144 |
-
if self.max_seq_len is None:
|
| 145 |
-
xq = xq.view(bsz, seq_len, self.n_heads, self.head_dim)
|
| 146 |
-
xk = xk.view(bsz, seq_len, self.n_kv_heads, self.head_dim)
|
| 147 |
-
xv = xv.view(bsz, seq_len, self.n_kv_heads, self.head_dim)
|
| 148 |
-
else:
|
| 149 |
-
nseq = self.max_seq_len // seq_len
|
| 150 |
-
xq = xq.view(-1, nseq*seq_len, self.n_heads, self.head_dim)
|
| 151 |
-
xk = xk.view(-1, nseq*seq_len, self.n_kv_heads, self.head_dim)
|
| 152 |
-
xv = xv.view(bsz, seq_len, self.n_kv_heads, self.head_dim)
|
| 153 |
-
|
| 154 |
-
# RoPE relative positional embeddings
|
| 155 |
-
if not (freqs_cos is None or freqs_sin is None):
|
| 156 |
-
xq, xk = apply_rotary_emb(xq, xk, freqs_cos, freqs_sin)
|
| 157 |
-
|
| 158 |
-
if self.max_seq_len is not None:
|
| 159 |
-
xq = xq.view(bsz, seq_len, self.n_heads, self.head_dim)
|
| 160 |
-
xk = xk.view(bsz, seq_len, self.n_kv_heads, self.head_dim)
|
| 161 |
-
|
| 162 |
-
# # grouped multiquery attention: expand out keys and values
|
| 163 |
-
# xk = repeat_kv(xk, self.n_rep) # (bs, seq_len, n_heads, head_dim)
|
| 164 |
-
# xv = repeat_kv(xv, self.n_rep) # (bs, seq_len, n_heads, head_dim)
|
| 165 |
-
|
| 166 |
-
# Repeat KV heads if necessary
|
| 167 |
-
if self.n_heads != self.n_kv_heads:
|
| 168 |
-
xk = xk.repeat_interleave(self.n_rep, dim=2)
|
| 169 |
-
xv = xv.repeat_interleave(self.n_rep, dim=2)
|
| 170 |
-
|
| 171 |
-
# make heads into a batch dimension
|
| 172 |
-
xq = xq.transpose(1, 2) # (bs, n_heads, seq_len, head_dim)
|
| 173 |
-
xk = xk.transpose(1, 2)
|
| 174 |
-
xv = xv.transpose(1, 2)
|
| 175 |
-
|
| 176 |
-
if mask is not None:
|
| 177 |
-
mask = mask.to(xq).unsqueeze(1).contiguous()
|
| 178 |
-
nW = mask.shape[0]
|
| 179 |
-
if nW < bsz:
|
| 180 |
-
mask = mask.repeat([bsz // nW, 1, 1, 1])
|
| 181 |
-
|
| 182 |
-
output = F.scaled_dot_product_attention(
|
| 183 |
-
xq, xk, xv,
|
| 184 |
-
attn_mask=mask,
|
| 185 |
-
dropout_p=self.dropout if self.training else 0.0,
|
| 186 |
-
is_causal=self.is_causal
|
| 187 |
-
)
|
| 188 |
-
|
| 189 |
-
# restore time as batch dimension and concat heads
|
| 190 |
-
output = output.transpose(1, 2).contiguous().view(bsz, seq_len, -1)
|
| 191 |
-
|
| 192 |
-
# final projection into the residual stream
|
| 193 |
-
output = self.wo(output)
|
| 194 |
-
output = self.resid_dropout(output)
|
| 195 |
-
return output
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/internal/polaris_layers.py
DELETED
|
@@ -1,448 +0,0 @@
|
|
| 1 |
-
# polaris_layers.py
|
| 2 |
-
from __future__ import annotations
|
| 3 |
-
|
| 4 |
-
import math
|
| 5 |
-
from typing import Optional, Tuple, Union, List
|
| 6 |
-
|
| 7 |
-
import torch
|
| 8 |
-
import torch.nn as nn
|
| 9 |
-
import torch.nn.functional as F
|
| 10 |
-
from einops import rearrange
|
| 11 |
-
|
| 12 |
-
# ---------------------------------------------------------
|
| 13 |
-
# 基础归一化与嵌入模块 (保留兼容性)
|
| 14 |
-
# ---------------------------------------------------------
|
| 15 |
-
class LayerNorm(nn.Module):
|
| 16 |
-
def __init__(self, dim, eps=1e-6, elementwise_affine=True):
|
| 17 |
-
super().__init__()
|
| 18 |
-
self.eps = eps
|
| 19 |
-
self.elementwise_affine = elementwise_affine
|
| 20 |
-
if self.elementwise_affine:
|
| 21 |
-
self.weight = nn.Parameter(torch.ones(dim))
|
| 22 |
-
|
| 23 |
-
def _norm(self, x, dim=-1):
|
| 24 |
-
u = x.mean(dim, keepdim=True)
|
| 25 |
-
s = x.var(dim, keepdim=True)
|
| 26 |
-
return (x - u) / torch.sqrt(s + self.eps)
|
| 27 |
-
|
| 28 |
-
def forward(self, x):
|
| 29 |
-
output = self._norm(x.float()).to(x)
|
| 30 |
-
if self.elementwise_affine:
|
| 31 |
-
output = output * self.weight
|
| 32 |
-
return output
|
| 33 |
-
|
| 34 |
-
class RMS_norm(nn.Module):
|
| 35 |
-
def __init__(self, dim, eps=1e-6, bias=False):
|
| 36 |
-
super().__init__()
|
| 37 |
-
self.eps = eps
|
| 38 |
-
self.weight = nn.Parameter(torch.ones(dim))
|
| 39 |
-
self.bias = nn.Parameter(torch.zeros(dim)) if bias else None
|
| 40 |
-
|
| 41 |
-
def forward(self, x):
|
| 42 |
-
# normalize over last dim
|
| 43 |
-
rms = x.pow(2).mean(dim=-1, keepdim=True).add(self.eps).sqrt()
|
| 44 |
-
y = x / rms * self.weight
|
| 45 |
-
if self.bias is not None:
|
| 46 |
-
y = y + self.bias
|
| 47 |
-
return y
|
| 48 |
-
|
| 49 |
-
class RMSNorm(nn.Module):
|
| 50 |
-
def __init__(self, dim: int, eps: float = 1e-6):
|
| 51 |
-
super().__init__()
|
| 52 |
-
self.eps = eps
|
| 53 |
-
self.weight = nn.Parameter(torch.ones(dim))
|
| 54 |
-
|
| 55 |
-
def _norm(self, x, dim=-1):
|
| 56 |
-
return x * torch.rsqrt(x.pow(2).mean(dim, keepdim=True) + self.eps)
|
| 57 |
-
|
| 58 |
-
def forward(self, x: torch.Tensor):
|
| 59 |
-
output = self._norm(x.float()).type_as(x)
|
| 60 |
-
return output * self.weight
|
| 61 |
-
|
| 62 |
-
class PatchEmbedConv(nn.Module):
|
| 63 |
-
def __init__(
|
| 64 |
-
self,
|
| 65 |
-
in_chans,
|
| 66 |
-
out_chans,
|
| 67 |
-
patch_size=4,
|
| 68 |
-
norm_func=None,
|
| 69 |
-
flatten=False,
|
| 70 |
-
):
|
| 71 |
-
super().__init__()
|
| 72 |
-
self.patch_size = (patch_size, patch_size)
|
| 73 |
-
self.flatten = flatten
|
| 74 |
-
|
| 75 |
-
# ⭐ 关键:patch_size=1 时保持老行为;否则按 patch_size 下采样
|
| 76 |
-
if self.patch_size == (1, 1):
|
| 77 |
-
# 保留你原来的设计:kernel_size=(2,1), stride=1
|
| 78 |
-
kernel_size = (2, 1)
|
| 79 |
-
stride = (1, 1)
|
| 80 |
-
padding = (0, 0)
|
| 81 |
-
else:
|
| 82 |
-
# 正常的 patch 下采样:kernel_size=stride=patch_size
|
| 83 |
-
kernel_size = self.patch_size
|
| 84 |
-
stride = self.patch_size
|
| 85 |
-
padding = (0, 0)
|
| 86 |
-
|
| 87 |
-
self.proj = nn.Conv2d(
|
| 88 |
-
in_chans,
|
| 89 |
-
out_chans,
|
| 90 |
-
kernel_size=kernel_size,
|
| 91 |
-
stride=stride,
|
| 92 |
-
padding=padding,
|
| 93 |
-
)
|
| 94 |
-
self.norm = norm_func(out_chans) if norm_func else nn.Identity()
|
| 95 |
-
|
| 96 |
-
def forward(self, x):
|
| 97 |
-
embed = self.proj(x)
|
| 98 |
-
if self.flatten:
|
| 99 |
-
embed = rearrange(embed, 'n c h w -> n (h w) c')
|
| 100 |
-
return self.norm(embed)
|
| 101 |
-
|
| 102 |
-
class WeatherLayerNorm(nn.Module):
|
| 103 |
-
"""
|
| 104 |
-
专为气象 (B, C, H, W) 格式设计的高效 LayerNorm (Channel-first)。
|
| 105 |
-
适用于深层全卷积潜空间演化。
|
| 106 |
-
"""
|
| 107 |
-
def __init__(self, normalized_shape, eps=1e-6):
|
| 108 |
-
super().__init__()
|
| 109 |
-
self.weight = nn.Parameter(torch.ones(normalized_shape))
|
| 110 |
-
self.bias = nn.Parameter(torch.zeros(normalized_shape))
|
| 111 |
-
self.eps = eps
|
| 112 |
-
|
| 113 |
-
def forward(self, x):
|
| 114 |
-
# x: (B, C, H, W)
|
| 115 |
-
u = x.mean(1, keepdim=True)
|
| 116 |
-
s = (x - u).pow(2).mean(1, keepdim=True)
|
| 117 |
-
x = (x - u) / torch.sqrt(s + self.eps)
|
| 118 |
-
return self.weight[:, None, None] * x + self.bias[:, None, None]
|
| 119 |
-
|
| 120 |
-
class AdaLN(nn.Module):
|
| 121 |
-
def __init__(self, dim, embed_dim=None):
|
| 122 |
-
super().__init__()
|
| 123 |
-
self.norm = LayerNorm(dim, eps=1e-6, elementwise_affine=False)
|
| 124 |
-
in_dim = embed_dim if embed_dim else dim
|
| 125 |
-
self.scale_shift = nn.Sequential(
|
| 126 |
-
nn.SiLU(),
|
| 127 |
-
nn.Linear(in_dim, 2 * dim, bias=True),
|
| 128 |
-
)
|
| 129 |
-
|
| 130 |
-
def forward(self, x, embed):
|
| 131 |
-
scale, shift = self.scale_shift(embed).chunk(2, dim=-1)
|
| 132 |
-
x = self.norm(x) * (1 + scale[:, None]) + shift[:, None]
|
| 133 |
-
return x
|
| 134 |
-
|
| 135 |
-
def sincos_embedding(x, embed_dim, max_period=10000, is_periodic=False):
|
| 136 |
-
omega = torch.arange(embed_dim//2, dtype=x.dtype, device=x.device)
|
| 137 |
-
if is_periodic:
|
| 138 |
-
x = 2 * torch.pi * x
|
| 139 |
-
else:
|
| 140 |
-
omega /= embed_dim / 2.
|
| 141 |
-
omega = 1. / max_period ** omega
|
| 142 |
-
out = torch.einsum('m,d->md', x.reshape(-1), omega)
|
| 143 |
-
emb_sin = torch.sin(out)
|
| 144 |
-
emb_cos = torch.cos(out)
|
| 145 |
-
emb = torch.cat([emb_sin, emb_cos], dim=1)
|
| 146 |
-
return emb
|
| 147 |
-
|
| 148 |
-
class TimestepEmbed(nn.Module):
|
| 149 |
-
def __init__(self, hidden_size, frequency=256, is_periodic=False, sinusoidal=True, dropout=0.0):
|
| 150 |
-
super().__init__()
|
| 151 |
-
self.mlp = nn.Sequential(
|
| 152 |
-
nn.Linear(frequency, hidden_size),
|
| 153 |
-
nn.SiLU(),
|
| 154 |
-
nn.Linear(hidden_size, hidden_size),
|
| 155 |
-
)
|
| 156 |
-
self.frequency = frequency
|
| 157 |
-
self.is_periodic = is_periodic
|
| 158 |
-
self.sinusoidal = sinusoidal
|
| 159 |
-
# Optional output-side dropout. Helps prevent the encoder from
|
| 160 |
-
# latching onto specific (hour, doy) combinations when training
|
| 161 |
-
# data spans many years and most doy values appear repeatedly.
|
| 162 |
-
# ``dropout=0.0`` (default) is a no-op so existing checkpoints
|
| 163 |
-
# see identical behaviour.
|
| 164 |
-
self.dropout = nn.Dropout(p=dropout) if dropout > 0 else nn.Identity()
|
| 165 |
-
|
| 166 |
-
def forward(self, x):
|
| 167 |
-
if not self.sinusoidal:
|
| 168 |
-
return self.dropout(self.mlp(x))
|
| 169 |
-
embed = sincos_embedding(
|
| 170 |
-
x.float(), self.frequency, is_periodic=self.is_periodic
|
| 171 |
-
).type_as(x)
|
| 172 |
-
embed = self.mlp(embed)
|
| 173 |
-
return self.dropout(embed)
|
| 174 |
-
|
| 175 |
-
# ---------------------------------------------------------
|
| 176 |
-
# 气象定制:潜空间大核卷积 Block
|
| 177 |
-
# ---------------------------------------------------------
|
| 178 |
-
class GlobalWeatherConvBlock(nn.Module):
|
| 179 |
-
"""
|
| 180 |
-
经度循环大核 CNN Block,专为地球流体物理推演设计。
|
| 181 |
-
替代原有的 Window Attention,解决网格伪影并大幅提升感受野。
|
| 182 |
-
"""
|
| 183 |
-
def __init__(self, dim: int, drop_path: float = 0.0):
|
| 184 |
-
super().__init__()
|
| 185 |
-
# 7x7 大核深度可分离卷积
|
| 186 |
-
self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, groups=dim, bias=False)
|
| 187 |
-
self.norm = WeatherLayerNorm(dim)
|
| 188 |
-
|
| 189 |
-
# Inverted Bottleneck: 1x1 卷积放大 4 倍通道进行特征融合
|
| 190 |
-
self.pwconv1 = nn.Conv2d(dim, 4 * dim, kernel_size=1)
|
| 191 |
-
self.act = nn.GELU()
|
| 192 |
-
self.pwconv2 = nn.Conv2d(4 * dim, dim, kernel_size=1)
|
| 193 |
-
|
| 194 |
-
self.drop_path = nn.Dropout(drop_path) if drop_path > 0. else nn.Identity()
|
| 195 |
-
|
| 196 |
-
def forward(self, x):
|
| 197 |
-
shortcut = x
|
| 198 |
-
|
| 199 |
-
x = F.pad(x, pad=(3, 3, 3, 3), mode='replicate')
|
| 200 |
-
|
| 201 |
-
x = x.contiguous()
|
| 202 |
-
|
| 203 |
-
x = self.dwconv(x)
|
| 204 |
-
x = self.norm(x)
|
| 205 |
-
x = self.pwconv1(x)
|
| 206 |
-
x = self.act(x)
|
| 207 |
-
x = self.pwconv2(x)
|
| 208 |
-
|
| 209 |
-
return shortcut + self.drop_path(x)
|
| 210 |
-
|
| 211 |
-
# ---------------------------------------------------------
|
| 212 |
-
# Encoder: 单帧气象特征提取网络
|
| 213 |
-
# ---------------------------------------------------------
|
| 214 |
-
class CubeEmbedConv(nn.Module):
|
| 215 |
-
"""
|
| 216 |
-
重构后的单帧推演 Encoder。
|
| 217 |
-
包含三阶段:前置抗混叠 -> Patch下采样 -> 潜空间大核深层演化。
|
| 218 |
-
"""
|
| 219 |
-
def __init__(
|
| 220 |
-
self,
|
| 221 |
-
in_chans: int = 70,
|
| 222 |
-
out_chans: int = 2048,
|
| 223 |
-
in_frames: int = 1, # 现固定为单帧输入
|
| 224 |
-
patch_size: int = 6, # 默认 6 倍下采样 (120x240)
|
| 225 |
-
depth: int = 2, # 潜空间推演层数 (建议 8-16)
|
| 226 |
-
flatten: bool = False,
|
| 227 |
-
norm_func: Optional[nn.Module] = None,
|
| 228 |
-
# 兼容旧签名的冗余参数,防止外部调用报错
|
| 229 |
-
temporal_pad_to: int = 2,
|
| 230 |
-
attn_heads: int = 8,
|
| 231 |
-
attn_dropout: float = 0.0,
|
| 232 |
-
window_size: int = 8,
|
| 233 |
-
keep_time_dim: bool = False,
|
| 234 |
-
feat_chans: int = 2048,
|
| 235 |
-
):
|
| 236 |
-
super().__init__()
|
| 237 |
-
self.in_chans = in_chans
|
| 238 |
-
self.out_chans = out_chans
|
| 239 |
-
self.flatten = flatten
|
| 240 |
-
|
| 241 |
-
# 阶段一:高分辨率前置特征融合 (抗混叠,避免下采样丢失高频气象细节)
|
| 242 |
-
inter_chans = 256
|
| 243 |
-
self.stage1_smooth = nn.Sequential(
|
| 244 |
-
nn.Conv2d(in_chans, inter_chans, kernel_size=7, stride=1, padding=3, padding_mode='replicate'),
|
| 245 |
-
WeatherLayerNorm(inter_chans),
|
| 246 |
-
nn.GELU()
|
| 247 |
-
)
|
| 248 |
-
|
| 249 |
-
# 阶段二:Patch 强力下采样 (无重叠,直接拉升到目标潜空间通道数)
|
| 250 |
-
self.stage2_patchify = nn.Sequential(
|
| 251 |
-
nn.Conv2d(inter_chans, out_chans, kernel_size=patch_size, stride=patch_size),
|
| 252 |
-
WeatherLayerNorm(out_chans)
|
| 253 |
-
)
|
| 254 |
-
|
| 255 |
-
# 阶段三:潜空间大核全局演化 (替代 Window Attention)
|
| 256 |
-
self.stage3_evolution = nn.Sequential(
|
| 257 |
-
*[GlobalWeatherConvBlock(dim=out_chans) for _ in range(depth)]
|
| 258 |
-
)
|
| 259 |
-
|
| 260 |
-
self.norm = norm_func(out_chans) if norm_func is not None else nn.Identity()
|
| 261 |
-
|
| 262 |
-
def forward(
|
| 263 |
-
self,
|
| 264 |
-
x: torch.Tensor,
|
| 265 |
-
lead_hour: Optional[int] = None, # 兼容签名
|
| 266 |
-
lead_hour_nums: Optional[int] = None, # 兼容签名
|
| 267 |
-
) -> torch.Tensor:
|
| 268 |
-
"""
|
| 269 |
-
x: (B, T, C, H, W) 或 (B, C, H, W). 期望 T==1。
|
| 270 |
-
"""
|
| 271 |
-
# 1. 处理维度,兼容 T 维度
|
| 272 |
-
if x.ndim == 5:
|
| 273 |
-
assert x.shape[1] == 1, f"CubeEmbedConv expects single frame (T=1), got {x.shape}"
|
| 274 |
-
x = x.squeeze(1) # 转换为 (B, C, H, W)
|
| 275 |
-
|
| 276 |
-
# print("x0",x.shape)
|
| 277 |
-
# 2. 三阶段推演
|
| 278 |
-
x = self.stage1_smooth(x)
|
| 279 |
-
# print("x1",x.shape)
|
| 280 |
-
x = self.stage2_patchify(x)
|
| 281 |
-
# print("x2",x.shape)
|
| 282 |
-
z = self.stage3_evolution(x)
|
| 283 |
-
# print("z",z.shape)
|
| 284 |
-
|
| 285 |
-
# 3. 输出格式化
|
| 286 |
-
if self.flatten:
|
| 287 |
-
z_tok = rearrange(z, "b c h w -> b (h w) c")
|
| 288 |
-
return self.norm(z_tok)
|
| 289 |
-
|
| 290 |
-
return self.norm(z)
|
| 291 |
-
|
| 292 |
-
# ---------------------------------------------------------
|
| 293 |
-
# Decoder: 气象场重构网络
|
| 294 |
-
# ---------------------------------------------------------
|
| 295 |
-
def remove_small_scales(x, scale_factor=0.5, mode="bilinear", random_scale=False):
|
| 296 |
-
import numpy as np
|
| 297 |
-
if random_scale:
|
| 298 |
-
scale_factor = np.random.choice([1 / s for s in range(1, 13)])
|
| 299 |
-
if scale_factor < 1:
|
| 300 |
-
down = F.interpolate(x, scale_factor=scale_factor, mode=mode, align_corners=False)
|
| 301 |
-
x = F.interpolate(down, size=x.shape[-2:], mode=mode, align_corners=False)
|
| 302 |
-
return x
|
| 303 |
-
|
| 304 |
-
class SmoothDeconv(nn.Module):
|
| 305 |
-
"""
|
| 306 |
-
数学精确匹配 patch_size 的平滑反卷积。
|
| 307 |
-
彻底修复原版本中存在的空间偏移(Phase Shift)和形状裁切问题。
|
| 308 |
-
"""
|
| 309 |
-
def __init__(
|
| 310 |
-
self,
|
| 311 |
-
in_channels: int,
|
| 312 |
-
out_channels: int,
|
| 313 |
-
kernel_size: Union[int, tuple],
|
| 314 |
-
stride: Union[int, tuple],
|
| 315 |
-
bias: bool = True,
|
| 316 |
-
):
|
| 317 |
-
super().__init__()
|
| 318 |
-
# 直接使用与 stride 完全相等的 kernel_size,避免 checkerboard artifacts
|
| 319 |
-
self.deconv = nn.ConvTranspose2d(
|
| 320 |
-
in_channels,
|
| 321 |
-
out_channels,
|
| 322 |
-
kernel_size=kernel_size,
|
| 323 |
-
stride=stride,
|
| 324 |
-
padding=0, # 完美对齐,不需要人为 padding
|
| 325 |
-
output_padding=0,
|
| 326 |
-
bias=bias
|
| 327 |
-
)
|
| 328 |
-
# 可选:后接一层普通卷积做进一步平滑
|
| 329 |
-
self.smooth = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1, padding_mode='replicate')
|
| 330 |
-
|
| 331 |
-
def forward(self, x, output_size=None):
|
| 332 |
-
x = self.deconv(x)
|
| 333 |
-
x = self.smooth(x)
|
| 334 |
-
# 如果有极微小的边界舍入误差,进行安全裁切
|
| 335 |
-
if output_size is not None and x.shape[-2:] != output_size:
|
| 336 |
-
x = x[..., :output_size[0], :output_size[1]]
|
| 337 |
-
return x
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
class ProgressiveUpsample(nn.Module):
|
| 341 |
-
"""
|
| 342 |
-
阶梯式渐进上采样模块。
|
| 343 |
-
完美平衡“信息保留”与“防显存溢出(OOM)”的矛盾,严格遵循流体力学的连续性。
|
| 344 |
-
"""
|
| 345 |
-
def __init__(self, in_channels: int, out_channels: int):
|
| 346 |
-
super().__init__()
|
| 347 |
-
|
| 348 |
-
# 阶段一:空间放大 2 倍,通道 2048 -> 512
|
| 349 |
-
self.stage1_conv = nn.Sequential(
|
| 350 |
-
nn.Conv2d(in_channels, 512, kernel_size=3, padding=1, padding_mode='replicate'),
|
| 351 |
-
nn.GELU()
|
| 352 |
-
)
|
| 353 |
-
|
| 354 |
-
# 阶段二:空间放大 3 倍,通道 512 -> 128
|
| 355 |
-
self.stage2_conv = nn.Sequential(
|
| 356 |
-
nn.Conv2d(512, 128, kernel_size=3, padding=1, padding_mode='replicate'),
|
| 357 |
-
nn.GELU()
|
| 358 |
-
)
|
| 359 |
-
|
| 360 |
-
# 最终映射:128 -> 具体的物理变量通道数 (如 upper_chans)
|
| 361 |
-
self.head = nn.Conv2d(128, out_channels, kernel_size=3, padding=1, padding_mode='replicate')
|
| 362 |
-
|
| 363 |
-
def forward(self, x, target_size=(721, 1440)):
|
| 364 |
-
# x 初始状态: (B, 2048, 120, 240)
|
| 365 |
-
|
| 366 |
-
# --- Stage 1: 先插值放大空间,再卷积融合降维 (保留高频信息) ---
|
| 367 |
-
x = F.interpolate(x, scale_factor=2.0, mode='bilinear', align_corners=False)
|
| 368 |
-
x = self.stage1_conv(x) # 此时形状: (B, 512, 240, 480)
|
| 369 |
-
|
| 370 |
-
# --- Stage 2: 再次插值放大空间,再降维 ---
|
| 371 |
-
x = F.interpolate(x, scale_factor=3.0, mode='bilinear', align_corners=False)
|
| 372 |
-
x = self.stage2_conv(x) # 此时形状: (B, 128, 720, 1440)
|
| 373 |
-
|
| 374 |
-
# --- 解决 720 与 721 的地球极点拓扑问题 ---
|
| 375 |
-
current_h, current_w = x.shape[-2:]
|
| 376 |
-
target_h, target_w = target_size
|
| 377 |
-
|
| 378 |
-
if current_h != target_h or current_w != target_w:
|
| 379 |
-
pad_h = target_h - current_h # 721 - 720 = 1
|
| 380 |
-
pad_w = target_w - current_w # 1440 - 1440 = 0
|
| 381 |
-
x = F.pad(x, (0, pad_w, 0, pad_h), mode='replicate')
|
| 382 |
-
|
| 383 |
-
# --- 最终映射输出 ---
|
| 384 |
-
x = self.head(x)
|
| 385 |
-
return x
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
class DoubleDeconvHead(nn.Module):
|
| 389 |
-
def __init__(
|
| 390 |
-
self,
|
| 391 |
-
in_chans: int,
|
| 392 |
-
upper_chans: int,
|
| 393 |
-
lower_chans: int,
|
| 394 |
-
patch_size: int,
|
| 395 |
-
):
|
| 396 |
-
super().__init__()
|
| 397 |
-
self.lower_chans = lower_chans
|
| 398 |
-
self.upper_chans = upper_chans
|
| 399 |
-
|
| 400 |
-
# 换用阶梯式渐进上采样
|
| 401 |
-
self.upper_head = ProgressiveUpsample(in_channels=in_chans, out_channels=upper_chans)
|
| 402 |
-
|
| 403 |
-
if self.lower_chans > 0:
|
| 404 |
-
self.lower_head = ProgressiveUpsample(in_channels=in_chans, out_channels=lower_chans)
|
| 405 |
-
|
| 406 |
-
def forward(self, h, residual=None, input_size=(721, 1440), lead_hour=None, target_frames: int = 1):
|
| 407 |
-
|
| 408 |
-
# ==========================================
|
| 409 |
-
# 🚨 修复 1:安全处理 Batch Tensor 级别的 lead_hour
|
| 410 |
-
# ==========================================
|
| 411 |
-
if lead_hour is not None:
|
| 412 |
-
# lead_hour shape: (B,)
|
| 413 |
-
# 等价于 max(lead_hour // 6, 1) 的 Tensor 写法
|
| 414 |
-
lead_step = torch.clamp(lead_hour // 6, min=1.0)
|
| 415 |
-
# 广播到 (B, 1, 1, 1) 以便与图像特征相乘
|
| 416 |
-
lead_step = lead_step.view(-1, 1, 1, 1).to(h.dtype)
|
| 417 |
-
else:
|
| 418 |
-
lead_step = 1.0
|
| 419 |
-
|
| 420 |
-
# ==========================================
|
| 421 |
-
# 2. 解码预测变化量 (Delta)
|
| 422 |
-
# ==========================================
|
| 423 |
-
pred = self.upper_head(h, target_size=input_size)
|
| 424 |
-
output = pred * lead_step
|
| 425 |
-
|
| 426 |
-
if self.lower_chans > 0:
|
| 427 |
-
lower = self.lower_head(h, target_size=input_size)
|
| 428 |
-
# 如果下层也需要乘时间步,取消下面的注释:
|
| 429 |
-
# lower = lower * lead_step
|
| 430 |
-
output = torch.cat([output, lower], dim=1)
|
| 431 |
-
|
| 432 |
-
# ==========================================
|
| 433 |
-
# 🚨 修复 2:残差相加必须在 FP32 精度下进行!
|
| 434 |
-
# ==========================================
|
| 435 |
-
if residual is not None:
|
| 436 |
-
|
| 437 |
-
res_frame = residual[:, -1] if residual.ndim == 5 else residual
|
| 438 |
-
res_frame = remove_small_scales(res_frame)
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
# 强制提升至 FP32 抵抗 bf16 的精度吞噬
|
| 442 |
-
output = output.to(torch.float32)
|
| 443 |
-
res_frame = res_frame.to(torch.float32)
|
| 444 |
-
|
| 445 |
-
output = output + res_frame
|
| 446 |
-
|
| 447 |
-
# 返回 fp32 结果,直接交给外面的 MSE Loss 计算(Loss也需要 fp32 保证稳定)
|
| 448 |
-
return output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/internal/polaris_swin.py
DELETED
|
@@ -1,527 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import torch
|
| 3 |
-
import torch.nn as nn
|
| 4 |
-
import torch.nn.functional as F
|
| 5 |
-
from .polaris_attention import FlashAttention
|
| 6 |
-
from .polaris_layers import LayerNorm, RMSNorm
|
| 7 |
-
from .helpers import to_2tuple
|
| 8 |
-
|
| 9 |
-
__all__ = ["SwinBlock", "GeGLU_FFN"]
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class GeGLU(nn.Module):
|
| 13 |
-
def forward(self, x):
|
| 14 |
-
x, gate = x.chunk(2, dim = -1)
|
| 15 |
-
return F.gelu(gate) * x
|
| 16 |
-
|
| 17 |
-
class GeGLU_FFN(nn.Module):
|
| 18 |
-
def __init__(
|
| 19 |
-
self,
|
| 20 |
-
dim,
|
| 21 |
-
hidden_dim=None,
|
| 22 |
-
multiple_of: int = 32,
|
| 23 |
-
dropout=0,
|
| 24 |
-
):
|
| 25 |
-
super().__init__()
|
| 26 |
-
if hidden_dim is None:
|
| 27 |
-
hidden_dim = 4 * dim
|
| 28 |
-
hidden_dim = int(2 * hidden_dim / 3)
|
| 29 |
-
hidden_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of)
|
| 30 |
-
self.fc1 = nn.Linear(dim, hidden_dim * 2, bias=False)
|
| 31 |
-
self.act = GeGLU()
|
| 32 |
-
self.fc2 = nn.Linear(hidden_dim, dim, bias=False)
|
| 33 |
-
self.drop = nn.Dropout(dropout)
|
| 34 |
-
|
| 35 |
-
def init_weights(self, init_std: float):
|
| 36 |
-
nn.init.trunc_normal_(self.fc1.weight, mean=0.0, std=0.02)
|
| 37 |
-
nn.init.trunc_normal_(self.fc2.weight, mean=0.0, std=init_std)
|
| 38 |
-
|
| 39 |
-
def forward(self, x):
|
| 40 |
-
return self.drop(self.fc2(self.act(self.fc1(x))))
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
class SwiGLU_FFN(nn.Module):
|
| 44 |
-
def __init__(
|
| 45 |
-
self,
|
| 46 |
-
dim: int,
|
| 47 |
-
hidden_dim = None,
|
| 48 |
-
multiple_of: int = 32,
|
| 49 |
-
dropout: float = 0.0,
|
| 50 |
-
):
|
| 51 |
-
super().__init__()
|
| 52 |
-
if hidden_dim is None:
|
| 53 |
-
hidden_dim = 4 * dim
|
| 54 |
-
hidden_dim = int(2 * hidden_dim / 3)
|
| 55 |
-
hidden_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of)
|
| 56 |
-
self.w1 = nn.Linear(dim, hidden_dim, bias=False)
|
| 57 |
-
self.w2 = nn.Linear(hidden_dim, dim, bias=False)
|
| 58 |
-
self.w3 = nn.Linear(dim, hidden_dim, bias=False)
|
| 59 |
-
self.dropout = nn.Dropout(dropout)
|
| 60 |
-
|
| 61 |
-
def forward(self, x):
|
| 62 |
-
return self.dropout(self.w2(F.silu(self.w1(x)) * self.w3(x)))
|
| 63 |
-
|
| 64 |
-
def init_weights(self, init_std: float):
|
| 65 |
-
nn.init.trunc_normal_(self.w1.weight, mean=0.0, std=0.02)
|
| 66 |
-
for linear in (self.w2, self.w3):
|
| 67 |
-
nn.init.trunc_normal_(linear.weight, mean=0.0, std=init_std)
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
def window_partition(x, window_size):
|
| 71 |
-
"""
|
| 72 |
-
Args:
|
| 73 |
-
x: (B, H, W, C)
|
| 74 |
-
window_size: (win_h, win_w)
|
| 75 |
-
|
| 76 |
-
Returns:
|
| 77 |
-
windows: (num_windows*B, win_h, win_w, C)
|
| 78 |
-
"""
|
| 79 |
-
B, H, W, C = x.shape
|
| 80 |
-
x = x.view(B, H // window_size[0], window_size[0], W // window_size[1], window_size[1], C)
|
| 81 |
-
windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size[0], window_size[1], C)
|
| 82 |
-
return windows
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
def window_reverse(windows, window_size, H, W):
|
| 86 |
-
"""
|
| 87 |
-
Args:
|
| 88 |
-
windows: (num_windows*B, window_size, window_size, C)
|
| 89 |
-
window_size: (win_h, win_w)
|
| 90 |
-
H (int): Height of image
|
| 91 |
-
W (int): Width of image
|
| 92 |
-
|
| 93 |
-
Returns:
|
| 94 |
-
x: (B, H, W, C)
|
| 95 |
-
"""
|
| 96 |
-
B = int(windows.shape[0] / (H * W / window_size[0] / window_size[1]))
|
| 97 |
-
x = windows.view(B, H // window_size[0], W // window_size[1], window_size[0], window_size[1], -1)
|
| 98 |
-
x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1)
|
| 99 |
-
return x
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
class WindowAttention(nn.Module):
|
| 104 |
-
r""" Window based multi-head self attention (W-MSA) module with relative position bias.
|
| 105 |
-
It supports both of shifted and non-shifted window.
|
| 106 |
-
|
| 107 |
-
Args:
|
| 108 |
-
dim (int): Number of input channels.
|
| 109 |
-
window_size (tuple[int]): The height and width of the window.
|
| 110 |
-
num_heads (int): Number of attention heads.
|
| 111 |
-
qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True
|
| 112 |
-
qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set
|
| 113 |
-
attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0
|
| 114 |
-
proj_drop (float, optional): Dropout ratio of output. Default: 0.0
|
| 115 |
-
"""
|
| 116 |
-
|
| 117 |
-
def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.):
|
| 118 |
-
|
| 119 |
-
super().__init__()
|
| 120 |
-
self.dim = dim
|
| 121 |
-
self.window_size = window_size # Wh, Ww
|
| 122 |
-
self.num_heads = num_heads
|
| 123 |
-
head_dim = dim // num_heads
|
| 124 |
-
self.scale = qk_scale or head_dim ** -0.5
|
| 125 |
-
|
| 126 |
-
# define a parameter table of relative position bias
|
| 127 |
-
self.relative_position_bias_table = nn.Parameter(
|
| 128 |
-
torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads)) # 2*Wh-1 * 2*Ww-1, nH
|
| 129 |
-
|
| 130 |
-
# get pair-wise relative position index for each token inside the window
|
| 131 |
-
coords_h = torch.arange(self.window_size[0])
|
| 132 |
-
coords_w = torch.arange(self.window_size[1])
|
| 133 |
-
coords = torch.stack(torch.meshgrid([coords_h, coords_w], indexing='ij')) # 2, Wh, Ww
|
| 134 |
-
coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww
|
| 135 |
-
relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww
|
| 136 |
-
relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2
|
| 137 |
-
relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0
|
| 138 |
-
relative_coords[:, :, 1] += self.window_size[1] - 1
|
| 139 |
-
relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1
|
| 140 |
-
relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww
|
| 141 |
-
self.register_buffer("relative_position_index", relative_position_index)
|
| 142 |
-
|
| 143 |
-
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 144 |
-
self.attn_drop = nn.Dropout(attn_drop)
|
| 145 |
-
self.proj = nn.Linear(dim, dim)
|
| 146 |
-
self.proj_drop = nn.Dropout(proj_drop)
|
| 147 |
-
nn.init.trunc_normal_(self.relative_position_bias_table, std=.02)
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
def forward(self, x, mask=None, **kwargs):
|
| 151 |
-
"""
|
| 152 |
-
Args:
|
| 153 |
-
x: input features with shape of (num_windows*B, N, C)
|
| 154 |
-
mask: (0/-inf) mask with shape of (num_windows, Wh*Ww, Wh*Ww) or None
|
| 155 |
-
"""
|
| 156 |
-
B_, N, C = x.shape
|
| 157 |
-
|
| 158 |
-
qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
|
| 159 |
-
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
|
| 160 |
-
|
| 161 |
-
q = q * self.scale
|
| 162 |
-
attn = (q @ k.transpose(-2, -1))
|
| 163 |
-
|
| 164 |
-
relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(
|
| 165 |
-
self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1) # Wh*Ww,Wh*Ww,nH
|
| 166 |
-
relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww
|
| 167 |
-
attn = attn + relative_position_bias.unsqueeze(0)
|
| 168 |
-
|
| 169 |
-
if mask is not None:
|
| 170 |
-
nW = mask.shape[0]
|
| 171 |
-
attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0)
|
| 172 |
-
attn = attn.view(-1, self.num_heads, N, N)
|
| 173 |
-
attn = attn.softmax(dim=-1)
|
| 174 |
-
else:
|
| 175 |
-
attn = attn.softmax(dim=-1)
|
| 176 |
-
|
| 177 |
-
attn = self.attn_drop(attn)
|
| 178 |
-
|
| 179 |
-
x = (attn @ v).transpose(1, 2).reshape(B_, N, C)
|
| 180 |
-
x = self.proj(x)
|
| 181 |
-
x = self.proj_drop(x)
|
| 182 |
-
return x
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
class WindowAttentionV2(nn.Module):
|
| 187 |
-
r""" Window based multi-head self attention (W-MSA) module with relative position bias.
|
| 188 |
-
It supports both of shifted and non-shifted window.
|
| 189 |
-
Args:
|
| 190 |
-
dim (int): Number of input channels.
|
| 191 |
-
window_size (tuple[int]): The height and width of the window.
|
| 192 |
-
num_heads (int): Number of attention heads.
|
| 193 |
-
qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True
|
| 194 |
-
attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0
|
| 195 |
-
proj_drop (float, optional): Dropout ratio of output. Default: 0.0
|
| 196 |
-
"""
|
| 197 |
-
|
| 198 |
-
def __init__(self, dim, window_size, num_heads, attn_drop=0., proj_drop=0.):
|
| 199 |
-
|
| 200 |
-
super().__init__()
|
| 201 |
-
self.dim = dim
|
| 202 |
-
self.window_size = window_size # Wh, Ww
|
| 203 |
-
self.num_heads = num_heads
|
| 204 |
-
|
| 205 |
-
self.logit_scale = nn.Parameter(torch.log(10 * torch.ones((num_heads, 1, 1))), requires_grad=True)
|
| 206 |
-
|
| 207 |
-
# mlp to generate continuous relative position bias
|
| 208 |
-
self.cpb_mlp = nn.Sequential(nn.Linear(2, 512, bias=True),
|
| 209 |
-
nn.ReLU(inplace=True),
|
| 210 |
-
nn.Linear(512, num_heads, bias=False))
|
| 211 |
-
|
| 212 |
-
# get relative_coords_table
|
| 213 |
-
relative_coords_h = torch.arange(-(self.window_size[0] - 1), self.window_size[0], dtype=torch.float32)
|
| 214 |
-
relative_coords_w = torch.arange(-(self.window_size[1] - 1), self.window_size[1], dtype=torch.float32)
|
| 215 |
-
|
| 216 |
-
relative_coords_table = torch.stack(
|
| 217 |
-
torch.meshgrid([relative_coords_h,relative_coords_w], indexing='ij')
|
| 218 |
-
).permute(1, 2, 0).contiguous().unsqueeze(0) # 1, 2*Wh-1, 2*Ww-1, 2
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
relative_coords_table[:, :, :, 0] /= (self.window_size[0] - 1)
|
| 222 |
-
relative_coords_table[:, :, :, 1] /= (self.window_size[1] - 1)
|
| 223 |
-
relative_coords_table *= 8 # normalize to -8, 8
|
| 224 |
-
|
| 225 |
-
relative_coords_table = torch.sign(relative_coords_table) * torch.log2(
|
| 226 |
-
torch.abs(relative_coords_table) + 1.0) / np.log2(8)
|
| 227 |
-
|
| 228 |
-
self.register_buffer("relative_coords_table", relative_coords_table)
|
| 229 |
-
|
| 230 |
-
# get pair-wise relative position index for each token inside the window
|
| 231 |
-
coords_h = torch.arange(self.window_size[0])
|
| 232 |
-
coords_w = torch.arange(self.window_size[1])
|
| 233 |
-
coords = torch.stack(torch.meshgrid([coords_h, coords_w], indexing='ij')) # 2, Wh, Ww
|
| 234 |
-
coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww
|
| 235 |
-
relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww
|
| 236 |
-
relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2
|
| 237 |
-
relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0
|
| 238 |
-
relative_coords[:, :, 1] += self.window_size[1] - 1
|
| 239 |
-
relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1
|
| 240 |
-
relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww
|
| 241 |
-
self.register_buffer("relative_position_index", relative_position_index)
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
self.qkv = nn.Linear(dim, dim * 3, bias=False)
|
| 245 |
-
self.q_bias = nn.Parameter(torch.zeros(dim))
|
| 246 |
-
self.v_bias = nn.Parameter(torch.zeros(dim))
|
| 247 |
-
self.register_buffer("k_bias", torch.zeros(dim).half())
|
| 248 |
-
# self.register_buffer('k_bias', torch.zeros(dim), persistent=False)
|
| 249 |
-
|
| 250 |
-
self.attn_drop = nn.Dropout(attn_drop)
|
| 251 |
-
self.out_proj = nn.Linear(dim, dim)
|
| 252 |
-
self.proj_drop = nn.Dropout(proj_drop)
|
| 253 |
-
self.softmax = nn.Softmax(dim=-1)
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
def forward(self, x, mask=None, **kwargs):
|
| 257 |
-
"""
|
| 258 |
-
Args:
|
| 259 |
-
x: input features with shape of (num_windows*B, N, C)
|
| 260 |
-
mask: (0/-inf) mask with shape of (num_windows, Wh*Ww, Wh*Ww) or None
|
| 261 |
-
"""
|
| 262 |
-
B_, N, C = x.shape
|
| 263 |
-
|
| 264 |
-
k_bias = self.k_bias.to(self.q_bias)
|
| 265 |
-
qkv_bias = torch.cat([self.q_bias, k_bias, self.v_bias])
|
| 266 |
-
# qkv_bias = torch.cat((self.q_bias, self.k_bias, self.v_bias))
|
| 267 |
-
|
| 268 |
-
qkv = F.linear(input=x, weight=self.qkv.weight, bias=qkv_bias)
|
| 269 |
-
qkv = qkv.reshape(B_, N, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
| 270 |
-
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
|
| 271 |
-
|
| 272 |
-
# cosine attention
|
| 273 |
-
attn = (F.normalize(q, dim=-1) @ F.normalize(k, dim=-1).transpose(-2, -1))
|
| 274 |
-
|
| 275 |
-
logit_scale = self.logit_scale.clamp(max=np.log(1. / 0.01)).exp()
|
| 276 |
-
attn = attn * logit_scale
|
| 277 |
-
|
| 278 |
-
relative_position_bias_table = self.cpb_mlp(self.relative_coords_table.to(x)).view(-1, self.num_heads)
|
| 279 |
-
relative_position_bias = relative_position_bias_table[self.relative_position_index.view(-1)].view(
|
| 280 |
-
self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1) # Wh*Ww,Wh*Ww,nH
|
| 281 |
-
relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww
|
| 282 |
-
relative_position_bias = 16 * torch.sigmoid(relative_position_bias)
|
| 283 |
-
|
| 284 |
-
attn = attn + relative_position_bias.unsqueeze(0)
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
if mask is not None:
|
| 288 |
-
nW = mask.shape[0]
|
| 289 |
-
attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0)
|
| 290 |
-
attn = attn.view(-1, self.num_heads, N, N)
|
| 291 |
-
attn = self.softmax(attn)
|
| 292 |
-
else:
|
| 293 |
-
attn = self.softmax(attn)
|
| 294 |
-
|
| 295 |
-
attn = self.attn_drop(attn).to(v)
|
| 296 |
-
|
| 297 |
-
x = (attn @ v).transpose(1, 2).reshape(B_, N, C)
|
| 298 |
-
x = self.out_proj(x)
|
| 299 |
-
x = self.proj_drop(x)
|
| 300 |
-
return x
|
| 301 |
-
|
| 302 |
-
def _compute_attn_mask(H, W, window_size, shift_size, mask_type):
|
| 303 |
-
img_mask = torch.zeros((1, H, W, 1))
|
| 304 |
-
h_slices = (slice(0, -window_size[0]),
|
| 305 |
-
slice(-window_size[0], -shift_size[0]),
|
| 306 |
-
slice(-shift_size[0], None))
|
| 307 |
-
w_slices = (slice(0, -window_size[1]),
|
| 308 |
-
slice(-window_size[1], -shift_size[1]),
|
| 309 |
-
slice(-shift_size[1], None))
|
| 310 |
-
cnt = 0
|
| 311 |
-
for h in h_slices:
|
| 312 |
-
for w in w_slices:
|
| 313 |
-
if mask_type == 'h':
|
| 314 |
-
img_mask[:, h, :, :] = cnt
|
| 315 |
-
elif mask_type == 'w':
|
| 316 |
-
img_mask[:, :, w, :] = cnt
|
| 317 |
-
elif mask_type == 'hw':
|
| 318 |
-
img_mask[:, h, w, :] = cnt
|
| 319 |
-
cnt += 1
|
| 320 |
-
mask_windows = window_partition(img_mask, window_size)
|
| 321 |
-
mask_windows = mask_windows.view(-1, window_size[0] * window_size[1])
|
| 322 |
-
attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
|
| 323 |
-
attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0))
|
| 324 |
-
return attn_mask
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
from transformers.modeling_layers import GradientCheckpointingLayer
|
| 328 |
-
class SwinBlock(GradientCheckpointingLayer):
|
| 329 |
-
def __init__(
|
| 330 |
-
self, dim, num_heads,
|
| 331 |
-
input_size, window_size=7, shift_size=0, embed_dim=None,
|
| 332 |
-
attn_type='v1', mask_type='hw', norm_type="ln", ffn_type="geglu_ffn",
|
| 333 |
-
qk_scale=None, n_kv_heads=None,
|
| 334 |
-
mlp_ratio=4., drop=0., attn_drop=0.,
|
| 335 |
-
attn_implementation="flash_attention_2",
|
| 336 |
-
**kwargs
|
| 337 |
-
):
|
| 338 |
-
super().__init__()
|
| 339 |
-
if embed_dim is None:
|
| 340 |
-
embed_dim = dim
|
| 341 |
-
self.dim = dim
|
| 342 |
-
self.default_input_size = tuple(int(s) for s in input_size)
|
| 343 |
-
self.num_heads = num_heads
|
| 344 |
-
self.window_size = to_2tuple(window_size)
|
| 345 |
-
self.shift_size = to_2tuple(shift_size)
|
| 346 |
-
self.mlp_ratio = mlp_ratio
|
| 347 |
-
self.norm_type = norm_type
|
| 348 |
-
self.attn_type = attn_type
|
| 349 |
-
self.ffn_type = ffn_type
|
| 350 |
-
self.mask_type = mask_type
|
| 351 |
-
self._attn_implementation = attn_implementation
|
| 352 |
-
|
| 353 |
-
default_full_window = all(
|
| 354 |
-
w == s for w, s in zip(self.window_size, self.default_input_size)
|
| 355 |
-
)
|
| 356 |
-
|
| 357 |
-
assert 0 <= self.shift_size[0] < self.window_size[0], "shift_size must in 0-window_size"
|
| 358 |
-
assert 0 <= self.shift_size[1] < self.window_size[1], "shift_size must in 0-window_size"
|
| 359 |
-
|
| 360 |
-
if norm_type == "ln":
|
| 361 |
-
self.norm1 = LayerNorm(dim, eps=1e-6)
|
| 362 |
-
self.norm2 = LayerNorm(dim, eps=1e-6)
|
| 363 |
-
elif norm_type == "adaln":
|
| 364 |
-
self.norm1 = LayerNorm(dim, eps=1e-6, elementwise_affine=False)
|
| 365 |
-
self.norm2 = LayerNorm(dim, eps=1e-6, elementwise_affine=False)
|
| 366 |
-
adaln_linear = nn.Linear(embed_dim, 6 * dim, bias=True)
|
| 367 |
-
nn.init.zeros_(adaln_linear.weight)
|
| 368 |
-
nn.init.zeros_(adaln_linear.bias)
|
| 369 |
-
self.adaln = nn.Sequential(nn.SiLU(), adaln_linear)
|
| 370 |
-
elif norm_type == "adarms":
|
| 371 |
-
self.norm1 = RMSNorm(dim, eps=1e-5)
|
| 372 |
-
self.norm2 = RMSNorm(dim, eps=1e-5)
|
| 373 |
-
adaln_linear = nn.Linear(embed_dim, 4 * dim, bias=True)
|
| 374 |
-
nn.init.zeros_(adaln_linear.weight)
|
| 375 |
-
nn.init.zeros_(adaln_linear.bias)
|
| 376 |
-
self.adaln = nn.Sequential(nn.SiLU(), adaln_linear)
|
| 377 |
-
else:
|
| 378 |
-
raise ValueError(f"norm_type {norm_type} not supported")
|
| 379 |
-
|
| 380 |
-
if attn_type == 'v1':
|
| 381 |
-
self.attn = WindowAttention(
|
| 382 |
-
dim, window_size=self.window_size, num_heads=num_heads,
|
| 383 |
-
qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop)
|
| 384 |
-
elif attn_type == 'v2':
|
| 385 |
-
self.attn = WindowAttentionV2(
|
| 386 |
-
dim, window_size=self.window_size, num_heads=num_heads,
|
| 387 |
-
attn_drop=attn_drop, proj_drop=drop)
|
| 388 |
-
elif attn_type == "flash":
|
| 389 |
-
self.attn = FlashAttention(
|
| 390 |
-
dim=dim,
|
| 391 |
-
n_heads=num_heads,
|
| 392 |
-
n_kv_heads=n_kv_heads,
|
| 393 |
-
attn_implementation=self._attn_implementation,
|
| 394 |
-
dropout=attn_drop,
|
| 395 |
-
is_causal=False,
|
| 396 |
-
max_seq_len=None if default_full_window else int(np.prod(self.default_input_size)),
|
| 397 |
-
)
|
| 398 |
-
else:
|
| 399 |
-
raise ValueError(f"attn_type {attn_type} not supported")
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
if ffn_type == "geglu_ffn":
|
| 403 |
-
self.mlp = GeGLU_FFN(dim)
|
| 404 |
-
elif ffn_type == "swiglu_ffn":
|
| 405 |
-
self.mlp = SwiGLU_FFN(dim)
|
| 406 |
-
else:
|
| 407 |
-
raise ValueError(f"ffn_type {ffn_type} not supported")
|
| 408 |
-
|
| 409 |
-
if not default_full_window and max(self.shift_size) > 0:
|
| 410 |
-
H, W = self.default_input_size
|
| 411 |
-
attn_mask = _compute_attn_mask(H, W, self.window_size, self.shift_size, mask_type)
|
| 412 |
-
else:
|
| 413 |
-
attn_mask = None
|
| 414 |
-
self.register_buffer("attn_mask", attn_mask, persistent=False)
|
| 415 |
-
|
| 416 |
-
self._attn_mask_cache = {}
|
| 417 |
-
|
| 418 |
-
def init_weights(self, init_std):
|
| 419 |
-
for norm in (self.norm1, self.norm2):
|
| 420 |
-
norm.reset_parameters()
|
| 421 |
-
self.attn.init_weights(init_std)
|
| 422 |
-
self.mlp.init_weights(init_std)
|
| 423 |
-
|
| 424 |
-
def _get_attn_mask(self, H, W, device):
|
| 425 |
-
if max(self.shift_size) == 0:
|
| 426 |
-
return None
|
| 427 |
-
if (H, W) == tuple(self.default_input_size):
|
| 428 |
-
return self.attn_mask
|
| 429 |
-
key = (H, W)
|
| 430 |
-
if key not in self._attn_mask_cache:
|
| 431 |
-
self._attn_mask_cache[key] = _compute_attn_mask(
|
| 432 |
-
H, W, self.window_size, self.shift_size, self.mask_type
|
| 433 |
-
)
|
| 434 |
-
return self._attn_mask_cache[key].to(device)
|
| 435 |
-
|
| 436 |
-
def _resolve_input_size(self, L):
|
| 437 |
-
if L == self.default_input_size[0] * self.default_input_size[1]:
|
| 438 |
-
return self.default_input_size
|
| 439 |
-
wh, ww = self.window_size
|
| 440 |
-
sqrt_l = int(L ** 0.5)
|
| 441 |
-
for H in range(sqrt_l, 0, -1):
|
| 442 |
-
if L % H != 0:
|
| 443 |
-
continue
|
| 444 |
-
W = L // H
|
| 445 |
-
if H % wh == 0 and W % ww == 0:
|
| 446 |
-
return (H, W)
|
| 447 |
-
raise ValueError(
|
| 448 |
-
f"Cannot resolve seq_len={L} into (H, W) divisible by window_size={self.window_size}"
|
| 449 |
-
)
|
| 450 |
-
|
| 451 |
-
def window_attention(self, x, freqs_cos, freqs_sin, input_size):
|
| 452 |
-
H, W = input_size
|
| 453 |
-
B, L, C = x.shape
|
| 454 |
-
|
| 455 |
-
x = x.view(B, H, W, C)
|
| 456 |
-
|
| 457 |
-
if max(self.shift_size) > 0:
|
| 458 |
-
shifted_x = torch.roll(x, shifts=(-self.shift_size[0], -self.shift_size[1]), dims=(1, 2))
|
| 459 |
-
else:
|
| 460 |
-
shifted_x = x
|
| 461 |
-
|
| 462 |
-
x_windows = window_partition(shifted_x, self.window_size)
|
| 463 |
-
x_windows = x_windows.view(-1, self.window_size[0] * self.window_size[1], C)
|
| 464 |
-
|
| 465 |
-
attn_mask = self._get_attn_mask(H, W, x.device)
|
| 466 |
-
|
| 467 |
-
if self.attn_type == "flash" and (H, W) != tuple(self.default_input_size):
|
| 468 |
-
old_max_seq = self.attn.max_seq_len
|
| 469 |
-
self.attn.max_seq_len = None
|
| 470 |
-
attn_windows = self.attn(x_windows, freqs_cos=freqs_cos, freqs_sin=freqs_sin, mask=attn_mask)
|
| 471 |
-
self.attn.max_seq_len = old_max_seq
|
| 472 |
-
else:
|
| 473 |
-
attn_windows = self.attn(x_windows, freqs_cos=freqs_cos, freqs_sin=freqs_sin, mask=attn_mask)
|
| 474 |
-
|
| 475 |
-
attn_windows = attn_windows.view(-1, self.window_size[0], self.window_size[1], C)
|
| 476 |
-
shifted_x = window_reverse(attn_windows, self.window_size, H, W)
|
| 477 |
-
|
| 478 |
-
if max(self.shift_size) > 0:
|
| 479 |
-
x = torch.roll(shifted_x, shifts=(self.shift_size[0], self.shift_size[1]), dims=(1, 2))
|
| 480 |
-
else:
|
| 481 |
-
x = shifted_x
|
| 482 |
-
|
| 483 |
-
x = x.view(B, H * W, C)
|
| 484 |
-
return x
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
def forward(
|
| 488 |
-
self,
|
| 489 |
-
x: torch.Tensor,
|
| 490 |
-
embed: torch.Tensor=None,
|
| 491 |
-
freqs_cos: torch.Tensor=None,
|
| 492 |
-
freqs_sin: torch.Tensor=None,
|
| 493 |
-
**kwargs,
|
| 494 |
-
):
|
| 495 |
-
B, L, C = x.shape
|
| 496 |
-
input_size = self._resolve_input_size(L)
|
| 497 |
-
is_full_window = all(w == s for w, s in zip(self.window_size, input_size))
|
| 498 |
-
|
| 499 |
-
shortcut = x
|
| 500 |
-
|
| 501 |
-
if self.norm_type == "adaln":
|
| 502 |
-
gamma = self.adaln(embed).unsqueeze(1)
|
| 503 |
-
scale_msa, shift_msa, gate_msa, scale_mlp, shift_mlp, gate_mlp = gamma.chunk(6, dim=-1)
|
| 504 |
-
x = self.norm1(x) * (1 + scale_msa) + shift_msa
|
| 505 |
-
elif self.norm_type == "adarms":
|
| 506 |
-
scale_msa, gate_msa, scale_mlp, gate_mlp = self.adaln(embed).unsqueeze(1).chunk(4, dim=-1)
|
| 507 |
-
x = self.norm1(x) * (1 + scale_msa)
|
| 508 |
-
else:
|
| 509 |
-
x = self.norm1(x)
|
| 510 |
-
|
| 511 |
-
if is_full_window:
|
| 512 |
-
x = self.attn(x, freqs_cos, freqs_sin)
|
| 513 |
-
else:
|
| 514 |
-
x = self.window_attention(x, freqs_cos, freqs_sin, input_size)
|
| 515 |
-
|
| 516 |
-
if self.norm_type == "adaln":
|
| 517 |
-
x = shortcut + gate_msa * x
|
| 518 |
-
x = x + gate_mlp*self.mlp(self.norm2(x)*(1+scale_mlp)+shift_mlp)
|
| 519 |
-
elif self.norm_type == "adarms":
|
| 520 |
-
x = shortcut + gate_msa * x
|
| 521 |
-
x = x + gate_mlp*self.mlp(self.norm2(x)*(1+scale_mlp))
|
| 522 |
-
else:
|
| 523 |
-
x = shortcut + x
|
| 524 |
-
x = x + self.mlp(self.norm2(x))
|
| 525 |
-
|
| 526 |
-
return x
|
| 527 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/processor.py
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
"""Weather processor (placeholder).
|
| 2 |
-
|
| 3 |
-
The bio modalities use a per-modality processor to turn raw inputs into
|
| 4 |
-
encoder tensors. Weather is different: the ``ERA5QwenVLDataset`` (in
|
| 5 |
-
``qwenvl/modalities/weather/data/era5_dataset.py``) directly emits all
|
| 6 |
-
encoder tensors plus the chat-template placeholder. This file exists
|
| 7 |
-
only so that ``BaseProcessor``-style callers — if any — see a concrete
|
| 8 |
-
class for the modality.
|
| 9 |
-
"""
|
| 10 |
-
|
| 11 |
-
from __future__ import annotations
|
| 12 |
-
|
| 13 |
-
from typing import Any, Dict, Optional, Tuple
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
class WeatherProcessor:
|
| 17 |
-
"""No-op processor; the dataset class does the real work."""
|
| 18 |
-
|
| 19 |
-
modality_name = "weather"
|
| 20 |
-
|
| 21 |
-
def process_input(self, raw_input: Any, **kwargs) -> Dict[str, Any]:
|
| 22 |
-
raise NotImplementedError(
|
| 23 |
-
"Weather inputs come straight from ERA5QwenVLDataset; the dataset "
|
| 24 |
-
"produces encoder-ready tensors directly."
|
| 25 |
-
)
|
| 26 |
-
|
| 27 |
-
def build_placeholder(
|
| 28 |
-
self, raw_input: Any, is_output: bool = False, **kwargs,
|
| 29 |
-
) -> Tuple[str, Optional[Any]]:
|
| 30 |
-
raise NotImplementedError(
|
| 31 |
-
"Use the chat-template helper in qwenvl/modalities/weather/data/era5_dataset.py."
|
| 32 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code/qwenvl/modalities/weather/projector.py
DELETED
|
@@ -1,29 +0,0 @@
|
|
| 1 |
-
"""Identity projector for the weather modality.
|
| 2 |
-
|
| 3 |
-
The Polaris-style ``meteo_merger`` already runs inside ``WeatherEncoder``
|
| 4 |
-
and projects swin hidden → ``qwenvl_dim``. The router still expects a
|
| 5 |
-
projector module so we register a no-op ``IdentityWeatherProjector`` to
|
| 6 |
-
keep the registration logic uniform with the bio modalities.
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
from __future__ import annotations
|
| 10 |
-
|
| 11 |
-
import torch
|
| 12 |
-
import torch.nn as nn
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
class IdentityWeatherProjector(nn.Module):
|
| 16 |
-
"""Pass-through projector. Encoder already outputs qwenvl_dim."""
|
| 17 |
-
|
| 18 |
-
def __init__(self, qwenvl_dim: int):
|
| 19 |
-
super().__init__()
|
| 20 |
-
self.qwenvl_dim = qwenvl_dim
|
| 21 |
-
# Single dummy parameter so that an "is the projector trainable"
|
| 22 |
-
# check does not return False unexpectedly when tune_weather_projector
|
| 23 |
-
# is left at its default True; the router treats projectors with
|
| 24 |
-
# no trainable params as frozen which would short-circuit the
|
| 25 |
-
# dummy_forward gradient sync path.
|
| 26 |
-
self._noop = nn.Parameter(torch.zeros(1), requires_grad=False)
|
| 27 |
-
|
| 28 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 29 |
-
return x
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|