BiliSakura commited on
Commit
539f49e
·
verified ·
1 Parent(s): 83051b8

Upload MMEarth transformers checkpoints with model card metadata

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. README.md +164 -0
  2. mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/config.json +70 -0
  3. mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/image_processing_mmearth.py +189 -0
  4. mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/model.safetensors +3 -0
  5. mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/modeling_mmearth.py +292 -0
  6. mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/pipeline_mmearth.py +68 -0
  7. mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/preprocessor_config.json +18 -0
  8. mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/config.json +70 -0
  9. mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/image_processing_mmearth.py +189 -0
  10. mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/model.safetensors +3 -0
  11. mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/modeling_mmearth.py +292 -0
  12. mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/pipeline_mmearth.py +68 -0
  13. mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/preprocessor_config.json +18 -0
  14. mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/config.json +70 -0
  15. mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/image_processing_mmearth.py +189 -0
  16. mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/model.safetensors +3 -0
  17. mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/modeling_mmearth.py +292 -0
  18. mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/pipeline_mmearth.py +68 -0
  19. mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/preprocessor_config.json +18 -0
  20. mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/config.json +70 -0
  21. mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/image_processing_mmearth.py +189 -0
  22. mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/model.safetensors +3 -0
  23. mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/modeling_mmearth.py +292 -0
  24. mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/pipeline_mmearth.py +68 -0
  25. mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/preprocessor_config.json +18 -0
  26. mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/config.json +70 -0
  27. mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/image_processing_mmearth.py +189 -0
  28. mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/model.safetensors +3 -0
  29. mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/modeling_mmearth.py +292 -0
  30. mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/pipeline_mmearth.py +68 -0
  31. mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/preprocessor_config.json +18 -0
  32. mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/config.json +70 -0
  33. mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/image_processing_mmearth.py +189 -0
  34. mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/model.safetensors +3 -0
  35. mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/modeling_mmearth.py +292 -0
  36. mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/pipeline_mmearth.py +68 -0
  37. mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/preprocessor_config.json +18 -0
  38. mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/config.json +61 -0
  39. mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/image_processing_mmearth.py +189 -0
  40. mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/model.safetensors +3 -0
  41. mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/modeling_mmearth.py +292 -0
  42. mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/pipeline_mmearth.py +68 -0
  43. mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/preprocessor_config.json +18 -0
  44. mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/config.json +61 -0
  45. mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/image_processing_mmearth.py +189 -0
  46. mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/model.safetensors +3 -0
  47. mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/modeling_mmearth.py +292 -0
  48. mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/pipeline_mmearth.py +68 -0
  49. mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/preprocessor_config.json +18 -0
  50. mmearth-convnextv2-atto-s2-1m-64-uncertainty-56x8/config.json +70 -0
README.md ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ language:
4
+ - en
5
+ tags:
6
+ - remote-sensing
7
+ - earth-observation
8
+ - self-supervised-learning
9
+ - satellite
10
+ - multispectral
11
+ - feature-extraction
12
+ - convnext
13
+ - mae
14
+ - mmearth
15
+ - mp-mae
16
+ - transformers
17
+ library_name: transformers
18
+ pipeline_tag: feature-extraction
19
+ ---
20
+
21
+ # MMEarth Transformers Models
22
+
23
+ Hugging Face–compatible checkpoints converted from the official [MMEarth](https://arxiv.org/abs/2405.02771) MP-MAE pretrained weights. Each subfolder is a standalone model repo layout (`config.json`, `model.safetensors`, preprocessor, and remote code) for geospatial feature extraction.
24
+
25
+ ## Model Description
26
+
27
+ These models are ConvNeXt V2 encoders pretrained with Multi Pretext Masked Autoencoding (MP-MAE) on the [MMEarth](https://github.com/vishalned/MMEarth-data) multi-modal geospatial dataset. Checkpoints cover different pretext task configurations (all modalities, S2-only, RGB/BGR, image-level, pixel-level) and model sizes (atto, tiny).
28
+
29
+ All folders ship self-contained remote code (`modeling_mmearth.py`, processor, pipeline) and load with `trust_remote_code=True`.
30
+
31
+ **Developed by:** [MMEarth Authors](https://github.com/vishalned/MMEarth-train)
32
+ **Converted for Hugging Face by:** BiliSakura
33
+ **License (weights):** MIT
34
+ **Original paper:** [MMEarth: Exploring Multi-Modal Pretext Tasks For Geospatial Representation Learning](https://arxiv.org/abs/2405.02771) (ECCV 2024)
35
+
36
+ ## Available checkpoints (10 models)
37
+
38
+ | Folder | Input | Size | Dataset | Loss | Image | Patch | Ch |
39
+ |--------|-------|------|---------|------|-------|-------|----|
40
+ | `mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8` | all_mod | atto | 1M_64 | uncertainty | 56 | 8 | 12 |
41
+ | `mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8` | all_mod | atto | 1M_64 | unweighted | 56 | 8 | 12 |
42
+ | `mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16` | all_mod | atto | 1M_128 | uncertainty | 112 | 16 | 12 |
43
+ | `mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16` | all_mod | atto | 100k_128 | uncertainty | 112 | 16 | 12 |
44
+ | `mmearth-convnextv2-tiny-all-mod-1m-64-uncertainty-56x8` | all_mod | tiny | 1M_64 | uncertainty | 56 | 8 | 12 |
45
+ | `mmearth-convnextv2-atto-s2-1m-64-uncertainty-56x8` | S2 | atto | 1M_64 | uncertainty | 56 | 8 | 12 |
46
+ | `mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8` | rgb (BGR) | atto | 1M_64 | uncertainty | 56 | 8 | 3 |
47
+ | `mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16` | rgb (BGR) | atto | 1M_128 | uncertainty | 112 | 16 | 3 |
48
+ | `mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8` | img_mod | atto | 1M_64 | uncertainty | 56 | 8 | 12 |
49
+ | `mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8` | pix_mod | atto | 1M_64 | uncertainty | 56 | 8 | 12 |
50
+
51
+ Legacy `.pth` filename mapping is in [`conversion_manifest.json`](conversion_manifest.json).
52
+
53
+ ## Usage
54
+
55
+ Processors default to **`do_resize: false`**. Inputs keep native height and width. Apply per-band MMEarth normalization when you have dataset statistics (`image_mean` / `image_std`).
56
+
57
+ ```python
58
+ from transformers import pipeline
59
+ import numpy as np
60
+
61
+ MODEL = "/path/to/MMEarth-transformers/mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8"
62
+
63
+ pipe = pipeline(
64
+ task="mmearth-feature-extraction",
65
+ model=MODEL,
66
+ trust_remote_code=True,
67
+ )
68
+
69
+ # RGB/BGR: 3 bands at native size (56×56 for this checkpoint)
70
+ image = np.random.rand(56, 56, 3).astype(np.float32) * 1000
71
+ features = pipe(image, pool=True, return_tensors=True)
72
+ print(features.shape) # torch.Size([1, 320])
73
+ ```
74
+
75
+ 12-band Sentinel-2 (all_mod / S2 checkpoints):
76
+
77
+ ```python
78
+ MODEL = "/path/to/MMEarth-transformers/mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8"
79
+ pipe = pipeline(task="mmearth-feature-extraction", model=MODEL, trust_remote_code=True)
80
+
81
+ image = np.random.rand(56, 56, 12).astype(np.float32) * 1000
82
+ features = pipe(image, pool=True, return_tensors=True)
83
+ print(features.shape) # torch.Size([1, 320])
84
+ ```
85
+
86
+ Dense spatial token map:
87
+
88
+ ```python
89
+ tokens = pipe(image, pool=False, return_tensors=True)
90
+ print(tokens.shape) # [1, num_patches, hidden_size]
91
+ ```
92
+
93
+ To resize to the pretraining reference size:
94
+
95
+ ```python
96
+ features = pipe(image, pool=True, return_tensors=True, image_processor_kwargs={"do_resize": True})
97
+ ```
98
+
99
+ Load components directly:
100
+
101
+ ```python
102
+ from transformers import AutoModel, AutoImageProcessor
103
+
104
+ model = AutoModel.from_pretrained(MODEL, trust_remote_code=True)
105
+ processor = AutoImageProcessor.from_pretrained(MODEL, trust_remote_code=True)
106
+ ```
107
+
108
+ ## Custom pipeline
109
+
110
+ Each checkpoint registers a custom pipeline in `config.json`:
111
+
112
+ ```json
113
+ "custom_pipelines": {
114
+ "mmearth-feature-extraction": {
115
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
116
+ "pt": ["AutoModel"]
117
+ }
118
+ }
119
+ ```
120
+
121
+ This follows the [HuggingFace custom pipeline pattern](https://huggingface.co/docs/transformers/add_new_pipeline): remote code ships with the model folder, and `trust_remote_code=True` loads `MMEarthImageFeatureExtractionPipeline`, which extends the standard `ImageFeatureExtractionPipeline` with numpy array and file path support.
122
+
123
+ The built-in `image-feature-extraction` task also works:
124
+
125
+ ```python
126
+ pipe = pipeline(task="image-feature-extraction", model=MODEL, trust_remote_code=True)
127
+ ```
128
+
129
+ ## Normalization
130
+
131
+ MMEarth pretraining normalizes each band with dataset-specific mean/std from `data_*_band_stats.json`. The converted preprocessor defaults to `do_normalize: false` because band statistics are not embedded in the legacy checkpoints. Provide your own `image_mean` / `image_std` when preprocessing:
132
+
133
+ ```python
134
+ features = pipe(
135
+ image,
136
+ pool=True,
137
+ return_tensors=True,
138
+ image_processor_kwargs={
139
+ "do_normalize": True,
140
+ "image_mean": [...], # one value per channel
141
+ "image_std": [...],
142
+ },
143
+ )
144
+ ```
145
+
146
+ RGB checkpoints were trained with **BGR** channel order (bands B4, B3, B2). The processor swaps RGB→BGR when `channel_order="bgr"`.
147
+
148
+ ## Dependencies
149
+
150
+ - `transformers`, `torch`, `timm`, `safetensors`
151
+ - `opencv-python` (multispectral resize with more than 4 channels when `do_resize=True`)
152
+
153
+ ## Citation
154
+
155
+ ```bibtex
156
+ @inproceedings{nedungadi2024mmearth,
157
+ title={MMEarth: Exploring multi-modal pretext tasks for geospatial representation learning},
158
+ author={Nedungadi, Vishal and Kariryaa, Ankit and Oehmcke, Stefan and Belongie, Serge and Igel, Christian and Lang, Nico},
159
+ booktitle={European Conference on Computer Vision},
160
+ pages={164--182},
161
+ year={2024},
162
+ organization={Springer}
163
+ }
164
+ ```
mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/config.json ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MMEarthModel"
4
+ ],
5
+ "band_names": [
6
+ "B1",
7
+ "B2",
8
+ "B3",
9
+ "B4",
10
+ "B5",
11
+ "B6",
12
+ "B7",
13
+ "B8A",
14
+ "B8",
15
+ "B9",
16
+ "B11",
17
+ "B12"
18
+ ],
19
+ "channel_order": "rgb",
20
+ "checkpoint_stage": "pretrain",
21
+ "dataset": "100k_128",
22
+ "depths": [
23
+ 2,
24
+ 2,
25
+ 6,
26
+ 2
27
+ ],
28
+ "do_rescale": false,
29
+ "drop_path_rate": 0.0,
30
+ "dtype": "float32",
31
+ "hidden_act": "gelu",
32
+ "hidden_size": 320,
33
+ "hidden_sizes": [
34
+ 40,
35
+ 80,
36
+ 160,
37
+ 320
38
+ ],
39
+ "id2label": {},
40
+ "image_mean": null,
41
+ "image_size": 112,
42
+ "image_std": null,
43
+ "input_modality": "all_mod",
44
+ "label2id": {},
45
+ "layer_norm_eps": 1e-06,
46
+ "loss_aggr": "uncertainty",
47
+ "model_size": "atto",
48
+ "model_type": "mmearth",
49
+ "num_channels": 12,
50
+ "num_stages": 4,
51
+ "patch_size": 16,
52
+ "rescale_factor": 1.0,
53
+ "transformers_version": "5.0.0",
54
+ "use_orig_stem": false,
55
+ "auto_map": {
56
+ "AutoConfig": "modeling_mmearth.MMEarthConfig",
57
+ "AutoModel": "modeling_mmearth.MMEarthModel",
58
+ "AutoModelForImageClassification": "modeling_mmearth.MMEarthForImageClassification"
59
+ },
60
+ "custom_pipelines": {
61
+ "mmearth-feature-extraction": {
62
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
63
+ "pt": [
64
+ "AutoModel"
65
+ ]
66
+ }
67
+ },
68
+ "legacy_checkpoint": "checkpoint-199.pth",
69
+ "legacy_source": "pt-all_mod_atto_100k_128_uncertainty_112-16"
70
+ }
mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/image_processing_mmearth.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """Image processor for MMEarth models."""
6
+
7
+ from typing import Optional, Union
8
+
9
+ import numpy as np
10
+
11
+ from transformers.image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict
12
+ from transformers.image_transforms import resize, to_channel_dimension_format
13
+ from transformers.image_utils import (
14
+ ChannelDimension,
15
+ ImageInput,
16
+ PILImageResampling,
17
+ infer_channel_dimension_format,
18
+ make_flat_list_of_images,
19
+ to_numpy_array,
20
+ valid_images,
21
+ validate_preprocess_arguments,
22
+ )
23
+ from transformers.utils import TensorType, filter_out_non_signature_kwargs, logging
24
+
25
+
26
+ logger = logging.get_logger(__name__)
27
+
28
+
29
+ def _resize_multispectral(image: np.ndarray, size: dict[str, int], input_data_format: ChannelDimension) -> np.ndarray:
30
+ target_height, target_width = size["height"], size["width"]
31
+
32
+ if input_data_format == ChannelDimension.FIRST:
33
+ image = np.transpose(image, (1, 2, 0))
34
+
35
+ height, width, _ = image.shape
36
+ if height == target_height and width == target_width:
37
+ resized = image
38
+ else:
39
+ try:
40
+ import cv2
41
+ except ImportError as exc:
42
+ raise ImportError(
43
+ "Multispectral resize requires OpenCV (`opencv-python`) when input has more than 4 channels."
44
+ ) from exc
45
+ resized = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
46
+
47
+ if input_data_format == ChannelDimension.FIRST:
48
+ return np.transpose(resized, (2, 0, 1))
49
+ return resized
50
+
51
+
52
+ def _reorder_channels(image: np.ndarray, channel_order: str, input_data_format: ChannelDimension) -> np.ndarray:
53
+ if channel_order != "bgr":
54
+ return image
55
+
56
+ if input_data_format == ChannelDimension.FIRST:
57
+ if image.shape[0] < 3:
58
+ return image
59
+ return image[[2, 1, 0], ...]
60
+ if image.shape[-1] < 3:
61
+ return image
62
+ return image[..., [2, 1, 0]]
63
+
64
+
65
+ class MMEarthImageProcessor(BaseImageProcessor):
66
+ """
67
+ Image processor for MMEarth ConvNeXt V2 encoders.
68
+
69
+ RGB checkpoints were trained with BGR channel order. Set `channel_order="bgr"` (default for RGB models) to swap
70
+ the first three channels from RGB to BGR before inference.
71
+ """
72
+
73
+ model_input_names = ["pixel_values"]
74
+
75
+ def __init__(
76
+ self,
77
+ do_resize: bool = True,
78
+ size: Optional[dict[str, int]] = None,
79
+ resample: PILImageResampling = PILImageResampling.BILINEAR,
80
+ do_rescale: bool = False,
81
+ rescale_factor: float = 1.0,
82
+ do_normalize: bool = True,
83
+ image_mean: Optional[Union[float, list[float]]] = None,
84
+ image_std: Optional[Union[float, list[float]]] = None,
85
+ do_convert_rgb: bool = False,
86
+ channel_order: str = "rgb",
87
+ **kwargs,
88
+ ):
89
+ super().__init__(**kwargs)
90
+ size = size if size is not None else {"height": 112, "width": 112}
91
+ self.do_resize = do_resize
92
+ self.size = size
93
+ self.resample = resample
94
+ self.do_rescale = do_rescale
95
+ self.rescale_factor = rescale_factor
96
+ self.do_normalize = do_normalize
97
+ self.image_mean = image_mean
98
+ self.image_std = image_std
99
+ self.do_convert_rgb = do_convert_rgb
100
+ self.channel_order = channel_order
101
+
102
+ @filter_out_non_signature_kwargs()
103
+ def preprocess(
104
+ self,
105
+ images: ImageInput,
106
+ do_resize: Optional[bool] = None,
107
+ size: Optional[dict[str, int]] = None,
108
+ resample: Optional[PILImageResampling] = None,
109
+ do_rescale: Optional[bool] = None,
110
+ rescale_factor: Optional[float] = None,
111
+ do_normalize: Optional[bool] = None,
112
+ image_mean: Optional[Union[float, list[float]]] = None,
113
+ image_std: Optional[Union[float, list[float]]] = None,
114
+ return_tensors: Optional[Union[str, TensorType]] = None,
115
+ data_format: Union[str, ChannelDimension] = ChannelDimension.FIRST,
116
+ input_data_format: Optional[Union[str, ChannelDimension]] = None,
117
+ do_convert_rgb: Optional[bool] = None,
118
+ channel_order: Optional[str] = None,
119
+ ):
120
+ do_resize = do_resize if do_resize is not None else self.do_resize
121
+ size = size if size is not None else self.size
122
+ size = get_size_dict(size, default_to_square=True)
123
+ resample = resample if resample is not None else self.resample
124
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
125
+ rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor
126
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
127
+ image_mean = image_mean if image_mean is not None else self.image_mean
128
+ image_std = image_std if image_std is not None else self.image_std
129
+ do_convert_rgb = do_convert_rgb if do_convert_rgb is not None else self.do_convert_rgb
130
+ channel_order = channel_order if channel_order is not None else self.channel_order
131
+
132
+ if do_normalize and (image_mean is None or image_std is None):
133
+ raise ValueError("Normalization requires `image_mean` and `image_std` with one value per channel.")
134
+
135
+ images = make_flat_list_of_images(images)
136
+ if not valid_images(images):
137
+ raise ValueError("Invalid image type. Must be PIL, numpy, or torch tensor.")
138
+
139
+ validate_preprocess_arguments(
140
+ do_rescale=do_rescale,
141
+ rescale_factor=rescale_factor,
142
+ do_normalize=do_normalize,
143
+ image_mean=image_mean,
144
+ image_std=image_std,
145
+ do_resize=do_resize,
146
+ size=size,
147
+ resample=resample,
148
+ )
149
+
150
+ processed_images = []
151
+ for image in images:
152
+ image = to_numpy_array(image)
153
+ if do_convert_rgb:
154
+ image = self._convert_image_to_rgb(image)
155
+
156
+ if input_data_format is None:
157
+ try:
158
+ input_data_format = infer_channel_dimension_format(image)
159
+ except ValueError:
160
+ input_data_format = ChannelDimension.LAST
161
+
162
+ image = _reorder_channels(image, channel_order=channel_order, input_data_format=input_data_format)
163
+
164
+ if do_resize:
165
+ num_channels = image.shape[0] if input_data_format == ChannelDimension.FIRST else image.shape[-1]
166
+ if num_channels > 4:
167
+ image = _resize_multispectral(image, size=size, input_data_format=input_data_format)
168
+ else:
169
+ image = resize(
170
+ image,
171
+ size=(size["height"], size["width"]),
172
+ resample=resample,
173
+ input_data_format=input_data_format,
174
+ )
175
+
176
+ if do_rescale:
177
+ image = image * rescale_factor
178
+
179
+ if do_normalize:
180
+ image = self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)
181
+
182
+ image = to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)
183
+ processed_images.append(image)
184
+
185
+ data = {"pixel_values": processed_images}
186
+ return BatchFeature(data=data, tensor_type=return_tensors)
187
+
188
+
189
+ __all__ = ["MMEarthImageProcessor"]
mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:73ad3615ab31506d9e2829912385adcb82140d3ab9c07784d37db8af5f2caaae
3
+ size 13573176
mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/modeling_mmearth.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ """Self-contained MMEarth model and config for trust_remote_code loading."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Optional
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from timm.models.layers import DropPath, trunc_normal_
11
+ from torch import nn
12
+
13
+ from transformers.configuration_utils import PretrainedConfig as PreTrainedConfig
14
+ from transformers.modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
15
+ from transformers.modeling_utils import PreTrainedModel
16
+ from transformers.processing_utils import Unpack
17
+ from transformers.utils import TransformersKwargs, logging
18
+
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+ MODEL_SIZE_PRESETS = {
23
+ "atto": {"depths": [2, 2, 6, 2], "hidden_sizes": [40, 80, 160, 320]},
24
+ "femto": {"depths": [2, 2, 6, 2], "hidden_sizes": [48, 96, 192, 384]},
25
+ "pico": {"depths": [2, 2, 6, 2], "hidden_sizes": [64, 128, 256, 512]},
26
+ "nano": {"depths": [2, 2, 8, 2], "hidden_sizes": [80, 160, 320, 640]},
27
+ "tiny": {"depths": [3, 3, 9, 3], "hidden_sizes": [96, 192, 384, 768]},
28
+ "base": {"depths": [3, 3, 27, 3], "hidden_sizes": [128, 256, 512, 1024]},
29
+ "large": {"depths": [3, 3, 27, 3], "hidden_sizes": [192, 384, 768, 1536]},
30
+ "huge": {"depths": [3, 3, 27, 3], "hidden_sizes": [352, 704, 1408, 2816]},
31
+ }
32
+
33
+
34
+ class MMEarthConfig(PreTrainedConfig):
35
+ model_type = "mmearth"
36
+
37
+ def __init__(
38
+ self,
39
+ depths: list[int] | None = None,
40
+ hidden_sizes: list[int] | None = None,
41
+ num_channels: int = 12,
42
+ image_size: int = 112,
43
+ patch_size: int = 16,
44
+ drop_path_rate: float = 0.0,
45
+ layer_norm_eps: float = 1e-6,
46
+ hidden_act: str = "gelu",
47
+ use_orig_stem: bool = False,
48
+ model_size: str = "atto",
49
+ input_modality: str = "all_mod",
50
+ channel_order: str = "rgb",
51
+ dataset: str = "1M_128",
52
+ loss_aggr: str = "uncertainty",
53
+ checkpoint_stage: str = "pretrain",
54
+ image_mean: list[float] | None = None,
55
+ image_std: list[float] | None = None,
56
+ do_rescale: bool = False,
57
+ rescale_factor: float = 1.0,
58
+ num_labels: int = 0,
59
+ **kwargs,
60
+ ):
61
+ super().__init__(**kwargs)
62
+ preset = MODEL_SIZE_PRESETS[model_size]
63
+ self.model_size = model_size
64
+ self.input_modality = input_modality
65
+ self.channel_order = channel_order
66
+ self.dataset = dataset
67
+ self.loss_aggr = loss_aggr
68
+ self.checkpoint_stage = checkpoint_stage
69
+ self.num_channels = num_channels
70
+ self.image_size = image_size
71
+ self.patch_size = patch_size
72
+ self.drop_path_rate = drop_path_rate
73
+ self.layer_norm_eps = layer_norm_eps
74
+ self.hidden_act = hidden_act
75
+ self.use_orig_stem = use_orig_stem
76
+ self.num_labels = num_labels
77
+ self.do_rescale = do_rescale
78
+ self.rescale_factor = rescale_factor
79
+ self.depths = depths if depths is not None else preset["depths"]
80
+ self.hidden_sizes = hidden_sizes if hidden_sizes is not None else preset["hidden_sizes"]
81
+ self.hidden_size = self.hidden_sizes[-1]
82
+ self.num_stages = len(self.depths)
83
+ self.image_mean = image_mean
84
+ self.image_std = image_std
85
+
86
+
87
+ class MMEarthLayerNorm(nn.Module):
88
+ def __init__(self, normalized_shape: int, eps: float = 1e-6, data_format: str = "channels_last"):
89
+ super().__init__()
90
+ self.weight = nn.Parameter(torch.ones(normalized_shape))
91
+ self.bias = nn.Parameter(torch.zeros(normalized_shape))
92
+ self.eps = eps
93
+ self.data_format = data_format
94
+ self.normalized_shape = (normalized_shape,)
95
+
96
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
97
+ if self.data_format == "channels_last":
98
+ return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
99
+ u = x.mean(1, keepdim=True)
100
+ s = (x - u).pow(2).mean(1, keepdim=True)
101
+ x = (x - u) / torch.sqrt(s + self.eps)
102
+ return self.weight[:, None, None] * x + self.bias[:, None, None]
103
+
104
+
105
+ class MMEarthGRN(nn.Module):
106
+ def __init__(self, dim: int):
107
+ super().__init__()
108
+ self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
109
+ self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
110
+
111
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
112
+ gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
113
+ nx = gx / (gx.mean(dim=-1, keepdim=True) + 1e-4)
114
+ return self.gamma * (x * nx) + self.beta + x
115
+
116
+
117
+ class MMEarthConvNeXtBlock(nn.Module):
118
+ def __init__(self, dim: int, drop_path: float = 0.0):
119
+ super().__init__()
120
+ self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
121
+ self.norm = MMEarthLayerNorm(dim, eps=1e-6)
122
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
123
+ self.act = nn.GELU()
124
+ self.grn = MMEarthGRN(4 * dim)
125
+ self.pwconv2 = nn.Linear(4 * dim, dim)
126
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
127
+
128
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
129
+ input_tensor = x
130
+ x = self.dwconv(x)
131
+ x = x.permute(0, 2, 3, 1)
132
+ x = self.norm(x)
133
+ x = self.pwconv1(x)
134
+ x = self.act(x)
135
+ x = self.grn(x)
136
+ x = self.pwconv2(x)
137
+ x = x.permute(0, 3, 1, 2)
138
+ return input_tensor + self.drop_path(x)
139
+
140
+
141
+ class MMEarthPreTrainedModel(PreTrainedModel):
142
+ config_class = MMEarthConfig
143
+ config: MMEarthConfig
144
+ base_model_prefix = "mmearth"
145
+ main_input_name = "pixel_values"
146
+ input_modalities = ("image",)
147
+ supports_gradient_checkpointing = True
148
+ _no_split_modules = ["MMEarthConvNeXtBlock"]
149
+
150
+ def _init_weights(self, module):
151
+ super()._init_weights(module)
152
+ if isinstance(module, (nn.Conv2d, nn.Linear)):
153
+ trunc_normal_(module.weight, std=0.02)
154
+ if module.bias is not None:
155
+ nn.init.constant_(module.bias, 0)
156
+
157
+
158
+ class MMEarthModel(MMEarthPreTrainedModel):
159
+ def __init__(self, config: MMEarthConfig, add_pooling_layer: bool = True):
160
+ super().__init__(config)
161
+ self.config = config
162
+ self.add_pooling_layer = add_pooling_layer
163
+ depths = config.depths
164
+ dims = config.hidden_sizes
165
+ patch_size = config.patch_size
166
+ num_stages = len(depths)
167
+ self.downsample_layers = nn.ModuleList()
168
+ if config.use_orig_stem:
169
+ self.stem_orig = nn.Sequential(
170
+ nn.Conv2d(
171
+ config.num_channels,
172
+ dims[0],
173
+ kernel_size=patch_size // (2 ** (num_stages - 1)),
174
+ stride=patch_size // (2 ** (num_stages - 1)),
175
+ ),
176
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
177
+ )
178
+ self.initial_conv = None
179
+ self.stem = None
180
+ else:
181
+ self.stem_orig = None
182
+ self.initial_conv = nn.Sequential(
183
+ nn.Conv2d(config.num_channels, dims[0], kernel_size=3, stride=1),
184
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
185
+ nn.GELU(),
186
+ )
187
+ stem_kernel = patch_size // (2 ** (num_stages - 1))
188
+ self.stem = nn.Sequential(
189
+ nn.Conv2d(
190
+ dims[0],
191
+ dims[0],
192
+ kernel_size=stem_kernel,
193
+ stride=stem_kernel,
194
+ padding=stem_kernel // 2,
195
+ groups=dims[0],
196
+ ),
197
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
198
+ )
199
+ for i in range(3):
200
+ self.downsample_layers.append(
201
+ nn.Sequential(
202
+ MMEarthLayerNorm(dims[i], eps=config.layer_norm_eps, data_format="channels_first"),
203
+ nn.Conv2d(dims[i], dims[i + 1], kernel_size=2, stride=2),
204
+ )
205
+ )
206
+ dp_rates = [
207
+ x.item()
208
+ for x in torch.linspace(0, config.drop_path_rate, sum(depths), device=torch.device("cpu"))
209
+ ]
210
+ cur = 0
211
+ self.stages = nn.ModuleList()
212
+ for i in range(num_stages):
213
+ stage = nn.Sequential(
214
+ *[MMEarthConvNeXtBlock(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]
215
+ )
216
+ self.stages.append(stage)
217
+ cur += depths[i]
218
+ self.norm = nn.LayerNorm(dims[-1], eps=config.layer_norm_eps)
219
+ self.post_init()
220
+
221
+ def _forward_stem(self, x: torch.Tensor) -> torch.Tensor:
222
+ if self.config.use_orig_stem:
223
+ return self.stem_orig(x)
224
+ x = self.initial_conv(x)
225
+ return self.stem(x)
226
+
227
+ def forward_features(self, pixel_values: torch.Tensor) -> torch.Tensor:
228
+ x = self._forward_stem(pixel_values)
229
+ x = self.stages[0](x)
230
+ for i in range(3):
231
+ x = self.downsample_layers[i](x)
232
+ x = self.stages[i + 1](x)
233
+ return x
234
+
235
+ def forward(
236
+ self,
237
+ pixel_values: Optional[torch.Tensor] = None,
238
+ return_dict: Optional[bool] = None,
239
+ **kwargs: Unpack[TransformersKwargs],
240
+ ) -> BaseModelOutputWithPooling:
241
+ if pixel_values is None:
242
+ raise ValueError("You must specify `pixel_values`")
243
+ pixel_values = pixel_values.to(dtype=self.dtype)
244
+ if return_dict is None:
245
+ return_dict = self.config.use_return_dict
246
+ spatial_features = self.forward_features(pixel_values)
247
+ last_hidden_state = spatial_features.flatten(2).transpose(1, 2)
248
+ pooled_output = self.norm(spatial_features.mean([-2, -1])) if self.add_pooling_layer else None
249
+ if not return_dict:
250
+ return (last_hidden_state, pooled_output)
251
+ return BaseModelOutputWithPooling(last_hidden_state=last_hidden_state, pooler_output=pooled_output)
252
+
253
+
254
+ class MMEarthForImageClassification(MMEarthPreTrainedModel):
255
+ def __init__(self, config: MMEarthConfig):
256
+ super().__init__(config)
257
+ self.num_labels = config.num_labels
258
+ self.mmearth = MMEarthModel(config, add_pooling_layer=True)
259
+ self.classifier = (
260
+ nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
261
+ )
262
+ self.post_init()
263
+
264
+ def forward(
265
+ self,
266
+ pixel_values: Optional[torch.Tensor] = None,
267
+ labels: Optional[torch.Tensor] = None,
268
+ return_dict: Optional[bool] = None,
269
+ **kwargs: Unpack[TransformersKwargs],
270
+ ) -> ImageClassifierOutput:
271
+ outputs = self.mmearth(pixel_values=pixel_values, return_dict=True, **kwargs)
272
+ logits = self.classifier(outputs.pooler_output)
273
+ loss = None
274
+ if labels is not None:
275
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
276
+ if not return_dict:
277
+ output = (logits,) + outputs[1:]
278
+ return ((loss,) + output) if loss is not None else output
279
+ return ImageClassifierOutput(
280
+ loss=loss,
281
+ logits=logits,
282
+ hidden_states=outputs.hidden_states,
283
+ attentions=outputs.attentions,
284
+ )
285
+
286
+
287
+ __all__ = [
288
+ "MMEarthConfig",
289
+ "MMEarthForImageClassification",
290
+ "MMEarthModel",
291
+ "MMEarthPreTrainedModel",
292
+ ]
mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/pipeline_mmearth.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """MMEarth image feature extraction pipeline."""
6
+
7
+ from typing import Any, Union
8
+
9
+ from transformers.pipelines.base import GenericTensor, build_pipeline_init_args
10
+ from transformers.pipelines.image_feature_extraction import ImageFeatureExtractionPipeline
11
+ from transformers.utils import add_end_docstrings, is_vision_available
12
+
13
+
14
+ if is_vision_available():
15
+ from transformers.image_utils import load_image
16
+
17
+
18
+ @add_end_docstrings(
19
+ build_pipeline_init_args(has_image_processor=True),
20
+ """
21
+ pool (`bool`, *optional*, defaults to `False`):
22
+ Whether or not to return the pooled output. If `False`, the model will return the raw hidden states.
23
+ """,
24
+ )
25
+ class MMEarthImageFeatureExtractionPipeline(ImageFeatureExtractionPipeline):
26
+ """
27
+ MMEarth image feature extraction pipeline.
28
+
29
+ This pipeline wraps [`MMEarthModel`] for Sentinel-2 multispectral and RGB/BGR geospatial feature extraction.
30
+ It extends [`ImageFeatureExtractionPipeline`] with support for numpy arrays and file paths in addition to
31
+ standard image inputs.
32
+ """
33
+
34
+ def _sanitize_parameters(
35
+ self,
36
+ image_processor_kwargs=None,
37
+ return_tensors=None,
38
+ pool=None,
39
+ **kwargs,
40
+ ):
41
+ preprocess_params = {} if image_processor_kwargs is None else dict(image_processor_kwargs)
42
+ if "timeout" in kwargs:
43
+ preprocess_params["timeout"] = kwargs["timeout"]
44
+
45
+ postprocess_params = {}
46
+ if pool is not None:
47
+ postprocess_params["pool"] = pool
48
+ if return_tensors is not None:
49
+ postprocess_params["return_tensors"] = return_tensors
50
+
51
+ return preprocess_params, {}, postprocess_params
52
+
53
+ def preprocess(self, image, timeout=None, **image_processor_kwargs) -> dict[str, GenericTensor]:
54
+ if not isinstance(image, (list, tuple)) and not hasattr(image, "shape"):
55
+ image = load_image(image, timeout=timeout)
56
+ model_inputs = self.image_processor(image, return_tensors="pt", **image_processor_kwargs)
57
+ model_inputs = model_inputs.to(self.dtype)
58
+ return model_inputs
59
+
60
+ def __call__(
61
+ self,
62
+ *args: Union[str, Any, list[Any]],
63
+ **kwargs: Any,
64
+ ) -> list[Any]:
65
+ return super().__call__(*args, **kwargs)
66
+
67
+
68
+ __all__ = ["MMEarthImageFeatureExtractionPipeline"]
mmearth-convnextv2-atto-all-mod-100k-128-uncertainty-112x16/preprocessor_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "MMEarthImageProcessor",
3
+ "size": {
4
+ "height": 112,
5
+ "width": 112
6
+ },
7
+ "do_resize": false,
8
+ "do_rescale": false,
9
+ "rescale_factor": 1.0,
10
+ "do_normalize": false,
11
+ "do_convert_rgb": false,
12
+ "channel_order": "rgb",
13
+ "image_mean": null,
14
+ "image_std": null,
15
+ "auto_map": {
16
+ "AutoImageProcessor": "image_processing_mmearth.MMEarthImageProcessor"
17
+ }
18
+ }
mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/config.json ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MMEarthModel"
4
+ ],
5
+ "band_names": [
6
+ "B1",
7
+ "B2",
8
+ "B3",
9
+ "B4",
10
+ "B5",
11
+ "B6",
12
+ "B7",
13
+ "B8A",
14
+ "B8",
15
+ "B9",
16
+ "B11",
17
+ "B12"
18
+ ],
19
+ "channel_order": "rgb",
20
+ "checkpoint_stage": "pretrain",
21
+ "dataset": "1M_128",
22
+ "depths": [
23
+ 2,
24
+ 2,
25
+ 6,
26
+ 2
27
+ ],
28
+ "do_rescale": false,
29
+ "drop_path_rate": 0.0,
30
+ "dtype": "float32",
31
+ "hidden_act": "gelu",
32
+ "hidden_size": 320,
33
+ "hidden_sizes": [
34
+ 40,
35
+ 80,
36
+ 160,
37
+ 320
38
+ ],
39
+ "id2label": {},
40
+ "image_mean": null,
41
+ "image_size": 112,
42
+ "image_std": null,
43
+ "input_modality": "all_mod",
44
+ "label2id": {},
45
+ "layer_norm_eps": 1e-06,
46
+ "loss_aggr": "uncertainty",
47
+ "model_size": "atto",
48
+ "model_type": "mmearth",
49
+ "num_channels": 12,
50
+ "num_stages": 4,
51
+ "patch_size": 16,
52
+ "rescale_factor": 1.0,
53
+ "transformers_version": "5.0.0",
54
+ "use_orig_stem": false,
55
+ "auto_map": {
56
+ "AutoConfig": "modeling_mmearth.MMEarthConfig",
57
+ "AutoModel": "modeling_mmearth.MMEarthModel",
58
+ "AutoModelForImageClassification": "modeling_mmearth.MMEarthForImageClassification"
59
+ },
60
+ "custom_pipelines": {
61
+ "mmearth-feature-extraction": {
62
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
63
+ "pt": [
64
+ "AutoModel"
65
+ ]
66
+ }
67
+ },
68
+ "legacy_checkpoint": "checkpoint-199.pth",
69
+ "legacy_source": "pt-all_mod_atto_1M_128_uncertainty_112-16"
70
+ }
mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/image_processing_mmearth.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """Image processor for MMEarth models."""
6
+
7
+ from typing import Optional, Union
8
+
9
+ import numpy as np
10
+
11
+ from transformers.image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict
12
+ from transformers.image_transforms import resize, to_channel_dimension_format
13
+ from transformers.image_utils import (
14
+ ChannelDimension,
15
+ ImageInput,
16
+ PILImageResampling,
17
+ infer_channel_dimension_format,
18
+ make_flat_list_of_images,
19
+ to_numpy_array,
20
+ valid_images,
21
+ validate_preprocess_arguments,
22
+ )
23
+ from transformers.utils import TensorType, filter_out_non_signature_kwargs, logging
24
+
25
+
26
+ logger = logging.get_logger(__name__)
27
+
28
+
29
+ def _resize_multispectral(image: np.ndarray, size: dict[str, int], input_data_format: ChannelDimension) -> np.ndarray:
30
+ target_height, target_width = size["height"], size["width"]
31
+
32
+ if input_data_format == ChannelDimension.FIRST:
33
+ image = np.transpose(image, (1, 2, 0))
34
+
35
+ height, width, _ = image.shape
36
+ if height == target_height and width == target_width:
37
+ resized = image
38
+ else:
39
+ try:
40
+ import cv2
41
+ except ImportError as exc:
42
+ raise ImportError(
43
+ "Multispectral resize requires OpenCV (`opencv-python`) when input has more than 4 channels."
44
+ ) from exc
45
+ resized = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
46
+
47
+ if input_data_format == ChannelDimension.FIRST:
48
+ return np.transpose(resized, (2, 0, 1))
49
+ return resized
50
+
51
+
52
+ def _reorder_channels(image: np.ndarray, channel_order: str, input_data_format: ChannelDimension) -> np.ndarray:
53
+ if channel_order != "bgr":
54
+ return image
55
+
56
+ if input_data_format == ChannelDimension.FIRST:
57
+ if image.shape[0] < 3:
58
+ return image
59
+ return image[[2, 1, 0], ...]
60
+ if image.shape[-1] < 3:
61
+ return image
62
+ return image[..., [2, 1, 0]]
63
+
64
+
65
+ class MMEarthImageProcessor(BaseImageProcessor):
66
+ """
67
+ Image processor for MMEarth ConvNeXt V2 encoders.
68
+
69
+ RGB checkpoints were trained with BGR channel order. Set `channel_order="bgr"` (default for RGB models) to swap
70
+ the first three channels from RGB to BGR before inference.
71
+ """
72
+
73
+ model_input_names = ["pixel_values"]
74
+
75
+ def __init__(
76
+ self,
77
+ do_resize: bool = True,
78
+ size: Optional[dict[str, int]] = None,
79
+ resample: PILImageResampling = PILImageResampling.BILINEAR,
80
+ do_rescale: bool = False,
81
+ rescale_factor: float = 1.0,
82
+ do_normalize: bool = True,
83
+ image_mean: Optional[Union[float, list[float]]] = None,
84
+ image_std: Optional[Union[float, list[float]]] = None,
85
+ do_convert_rgb: bool = False,
86
+ channel_order: str = "rgb",
87
+ **kwargs,
88
+ ):
89
+ super().__init__(**kwargs)
90
+ size = size if size is not None else {"height": 112, "width": 112}
91
+ self.do_resize = do_resize
92
+ self.size = size
93
+ self.resample = resample
94
+ self.do_rescale = do_rescale
95
+ self.rescale_factor = rescale_factor
96
+ self.do_normalize = do_normalize
97
+ self.image_mean = image_mean
98
+ self.image_std = image_std
99
+ self.do_convert_rgb = do_convert_rgb
100
+ self.channel_order = channel_order
101
+
102
+ @filter_out_non_signature_kwargs()
103
+ def preprocess(
104
+ self,
105
+ images: ImageInput,
106
+ do_resize: Optional[bool] = None,
107
+ size: Optional[dict[str, int]] = None,
108
+ resample: Optional[PILImageResampling] = None,
109
+ do_rescale: Optional[bool] = None,
110
+ rescale_factor: Optional[float] = None,
111
+ do_normalize: Optional[bool] = None,
112
+ image_mean: Optional[Union[float, list[float]]] = None,
113
+ image_std: Optional[Union[float, list[float]]] = None,
114
+ return_tensors: Optional[Union[str, TensorType]] = None,
115
+ data_format: Union[str, ChannelDimension] = ChannelDimension.FIRST,
116
+ input_data_format: Optional[Union[str, ChannelDimension]] = None,
117
+ do_convert_rgb: Optional[bool] = None,
118
+ channel_order: Optional[str] = None,
119
+ ):
120
+ do_resize = do_resize if do_resize is not None else self.do_resize
121
+ size = size if size is not None else self.size
122
+ size = get_size_dict(size, default_to_square=True)
123
+ resample = resample if resample is not None else self.resample
124
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
125
+ rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor
126
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
127
+ image_mean = image_mean if image_mean is not None else self.image_mean
128
+ image_std = image_std if image_std is not None else self.image_std
129
+ do_convert_rgb = do_convert_rgb if do_convert_rgb is not None else self.do_convert_rgb
130
+ channel_order = channel_order if channel_order is not None else self.channel_order
131
+
132
+ if do_normalize and (image_mean is None or image_std is None):
133
+ raise ValueError("Normalization requires `image_mean` and `image_std` with one value per channel.")
134
+
135
+ images = make_flat_list_of_images(images)
136
+ if not valid_images(images):
137
+ raise ValueError("Invalid image type. Must be PIL, numpy, or torch tensor.")
138
+
139
+ validate_preprocess_arguments(
140
+ do_rescale=do_rescale,
141
+ rescale_factor=rescale_factor,
142
+ do_normalize=do_normalize,
143
+ image_mean=image_mean,
144
+ image_std=image_std,
145
+ do_resize=do_resize,
146
+ size=size,
147
+ resample=resample,
148
+ )
149
+
150
+ processed_images = []
151
+ for image in images:
152
+ image = to_numpy_array(image)
153
+ if do_convert_rgb:
154
+ image = self._convert_image_to_rgb(image)
155
+
156
+ if input_data_format is None:
157
+ try:
158
+ input_data_format = infer_channel_dimension_format(image)
159
+ except ValueError:
160
+ input_data_format = ChannelDimension.LAST
161
+
162
+ image = _reorder_channels(image, channel_order=channel_order, input_data_format=input_data_format)
163
+
164
+ if do_resize:
165
+ num_channels = image.shape[0] if input_data_format == ChannelDimension.FIRST else image.shape[-1]
166
+ if num_channels > 4:
167
+ image = _resize_multispectral(image, size=size, input_data_format=input_data_format)
168
+ else:
169
+ image = resize(
170
+ image,
171
+ size=(size["height"], size["width"]),
172
+ resample=resample,
173
+ input_data_format=input_data_format,
174
+ )
175
+
176
+ if do_rescale:
177
+ image = image * rescale_factor
178
+
179
+ if do_normalize:
180
+ image = self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)
181
+
182
+ image = to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)
183
+ processed_images.append(image)
184
+
185
+ data = {"pixel_values": processed_images}
186
+ return BatchFeature(data=data, tensor_type=return_tensors)
187
+
188
+
189
+ __all__ = ["MMEarthImageProcessor"]
mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f2f0e7cecf8534809972730b4960c07078387fc72f97e3d255ce20286803815b
3
+ size 13573176
mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/modeling_mmearth.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ """Self-contained MMEarth model and config for trust_remote_code loading."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Optional
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from timm.models.layers import DropPath, trunc_normal_
11
+ from torch import nn
12
+
13
+ from transformers.configuration_utils import PretrainedConfig as PreTrainedConfig
14
+ from transformers.modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
15
+ from transformers.modeling_utils import PreTrainedModel
16
+ from transformers.processing_utils import Unpack
17
+ from transformers.utils import TransformersKwargs, logging
18
+
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+ MODEL_SIZE_PRESETS = {
23
+ "atto": {"depths": [2, 2, 6, 2], "hidden_sizes": [40, 80, 160, 320]},
24
+ "femto": {"depths": [2, 2, 6, 2], "hidden_sizes": [48, 96, 192, 384]},
25
+ "pico": {"depths": [2, 2, 6, 2], "hidden_sizes": [64, 128, 256, 512]},
26
+ "nano": {"depths": [2, 2, 8, 2], "hidden_sizes": [80, 160, 320, 640]},
27
+ "tiny": {"depths": [3, 3, 9, 3], "hidden_sizes": [96, 192, 384, 768]},
28
+ "base": {"depths": [3, 3, 27, 3], "hidden_sizes": [128, 256, 512, 1024]},
29
+ "large": {"depths": [3, 3, 27, 3], "hidden_sizes": [192, 384, 768, 1536]},
30
+ "huge": {"depths": [3, 3, 27, 3], "hidden_sizes": [352, 704, 1408, 2816]},
31
+ }
32
+
33
+
34
+ class MMEarthConfig(PreTrainedConfig):
35
+ model_type = "mmearth"
36
+
37
+ def __init__(
38
+ self,
39
+ depths: list[int] | None = None,
40
+ hidden_sizes: list[int] | None = None,
41
+ num_channels: int = 12,
42
+ image_size: int = 112,
43
+ patch_size: int = 16,
44
+ drop_path_rate: float = 0.0,
45
+ layer_norm_eps: float = 1e-6,
46
+ hidden_act: str = "gelu",
47
+ use_orig_stem: bool = False,
48
+ model_size: str = "atto",
49
+ input_modality: str = "all_mod",
50
+ channel_order: str = "rgb",
51
+ dataset: str = "1M_128",
52
+ loss_aggr: str = "uncertainty",
53
+ checkpoint_stage: str = "pretrain",
54
+ image_mean: list[float] | None = None,
55
+ image_std: list[float] | None = None,
56
+ do_rescale: bool = False,
57
+ rescale_factor: float = 1.0,
58
+ num_labels: int = 0,
59
+ **kwargs,
60
+ ):
61
+ super().__init__(**kwargs)
62
+ preset = MODEL_SIZE_PRESETS[model_size]
63
+ self.model_size = model_size
64
+ self.input_modality = input_modality
65
+ self.channel_order = channel_order
66
+ self.dataset = dataset
67
+ self.loss_aggr = loss_aggr
68
+ self.checkpoint_stage = checkpoint_stage
69
+ self.num_channels = num_channels
70
+ self.image_size = image_size
71
+ self.patch_size = patch_size
72
+ self.drop_path_rate = drop_path_rate
73
+ self.layer_norm_eps = layer_norm_eps
74
+ self.hidden_act = hidden_act
75
+ self.use_orig_stem = use_orig_stem
76
+ self.num_labels = num_labels
77
+ self.do_rescale = do_rescale
78
+ self.rescale_factor = rescale_factor
79
+ self.depths = depths if depths is not None else preset["depths"]
80
+ self.hidden_sizes = hidden_sizes if hidden_sizes is not None else preset["hidden_sizes"]
81
+ self.hidden_size = self.hidden_sizes[-1]
82
+ self.num_stages = len(self.depths)
83
+ self.image_mean = image_mean
84
+ self.image_std = image_std
85
+
86
+
87
+ class MMEarthLayerNorm(nn.Module):
88
+ def __init__(self, normalized_shape: int, eps: float = 1e-6, data_format: str = "channels_last"):
89
+ super().__init__()
90
+ self.weight = nn.Parameter(torch.ones(normalized_shape))
91
+ self.bias = nn.Parameter(torch.zeros(normalized_shape))
92
+ self.eps = eps
93
+ self.data_format = data_format
94
+ self.normalized_shape = (normalized_shape,)
95
+
96
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
97
+ if self.data_format == "channels_last":
98
+ return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
99
+ u = x.mean(1, keepdim=True)
100
+ s = (x - u).pow(2).mean(1, keepdim=True)
101
+ x = (x - u) / torch.sqrt(s + self.eps)
102
+ return self.weight[:, None, None] * x + self.bias[:, None, None]
103
+
104
+
105
+ class MMEarthGRN(nn.Module):
106
+ def __init__(self, dim: int):
107
+ super().__init__()
108
+ self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
109
+ self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
110
+
111
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
112
+ gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
113
+ nx = gx / (gx.mean(dim=-1, keepdim=True) + 1e-4)
114
+ return self.gamma * (x * nx) + self.beta + x
115
+
116
+
117
+ class MMEarthConvNeXtBlock(nn.Module):
118
+ def __init__(self, dim: int, drop_path: float = 0.0):
119
+ super().__init__()
120
+ self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
121
+ self.norm = MMEarthLayerNorm(dim, eps=1e-6)
122
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
123
+ self.act = nn.GELU()
124
+ self.grn = MMEarthGRN(4 * dim)
125
+ self.pwconv2 = nn.Linear(4 * dim, dim)
126
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
127
+
128
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
129
+ input_tensor = x
130
+ x = self.dwconv(x)
131
+ x = x.permute(0, 2, 3, 1)
132
+ x = self.norm(x)
133
+ x = self.pwconv1(x)
134
+ x = self.act(x)
135
+ x = self.grn(x)
136
+ x = self.pwconv2(x)
137
+ x = x.permute(0, 3, 1, 2)
138
+ return input_tensor + self.drop_path(x)
139
+
140
+
141
+ class MMEarthPreTrainedModel(PreTrainedModel):
142
+ config_class = MMEarthConfig
143
+ config: MMEarthConfig
144
+ base_model_prefix = "mmearth"
145
+ main_input_name = "pixel_values"
146
+ input_modalities = ("image",)
147
+ supports_gradient_checkpointing = True
148
+ _no_split_modules = ["MMEarthConvNeXtBlock"]
149
+
150
+ def _init_weights(self, module):
151
+ super()._init_weights(module)
152
+ if isinstance(module, (nn.Conv2d, nn.Linear)):
153
+ trunc_normal_(module.weight, std=0.02)
154
+ if module.bias is not None:
155
+ nn.init.constant_(module.bias, 0)
156
+
157
+
158
+ class MMEarthModel(MMEarthPreTrainedModel):
159
+ def __init__(self, config: MMEarthConfig, add_pooling_layer: bool = True):
160
+ super().__init__(config)
161
+ self.config = config
162
+ self.add_pooling_layer = add_pooling_layer
163
+ depths = config.depths
164
+ dims = config.hidden_sizes
165
+ patch_size = config.patch_size
166
+ num_stages = len(depths)
167
+ self.downsample_layers = nn.ModuleList()
168
+ if config.use_orig_stem:
169
+ self.stem_orig = nn.Sequential(
170
+ nn.Conv2d(
171
+ config.num_channels,
172
+ dims[0],
173
+ kernel_size=patch_size // (2 ** (num_stages - 1)),
174
+ stride=patch_size // (2 ** (num_stages - 1)),
175
+ ),
176
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
177
+ )
178
+ self.initial_conv = None
179
+ self.stem = None
180
+ else:
181
+ self.stem_orig = None
182
+ self.initial_conv = nn.Sequential(
183
+ nn.Conv2d(config.num_channels, dims[0], kernel_size=3, stride=1),
184
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
185
+ nn.GELU(),
186
+ )
187
+ stem_kernel = patch_size // (2 ** (num_stages - 1))
188
+ self.stem = nn.Sequential(
189
+ nn.Conv2d(
190
+ dims[0],
191
+ dims[0],
192
+ kernel_size=stem_kernel,
193
+ stride=stem_kernel,
194
+ padding=stem_kernel // 2,
195
+ groups=dims[0],
196
+ ),
197
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
198
+ )
199
+ for i in range(3):
200
+ self.downsample_layers.append(
201
+ nn.Sequential(
202
+ MMEarthLayerNorm(dims[i], eps=config.layer_norm_eps, data_format="channels_first"),
203
+ nn.Conv2d(dims[i], dims[i + 1], kernel_size=2, stride=2),
204
+ )
205
+ )
206
+ dp_rates = [
207
+ x.item()
208
+ for x in torch.linspace(0, config.drop_path_rate, sum(depths), device=torch.device("cpu"))
209
+ ]
210
+ cur = 0
211
+ self.stages = nn.ModuleList()
212
+ for i in range(num_stages):
213
+ stage = nn.Sequential(
214
+ *[MMEarthConvNeXtBlock(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]
215
+ )
216
+ self.stages.append(stage)
217
+ cur += depths[i]
218
+ self.norm = nn.LayerNorm(dims[-1], eps=config.layer_norm_eps)
219
+ self.post_init()
220
+
221
+ def _forward_stem(self, x: torch.Tensor) -> torch.Tensor:
222
+ if self.config.use_orig_stem:
223
+ return self.stem_orig(x)
224
+ x = self.initial_conv(x)
225
+ return self.stem(x)
226
+
227
+ def forward_features(self, pixel_values: torch.Tensor) -> torch.Tensor:
228
+ x = self._forward_stem(pixel_values)
229
+ x = self.stages[0](x)
230
+ for i in range(3):
231
+ x = self.downsample_layers[i](x)
232
+ x = self.stages[i + 1](x)
233
+ return x
234
+
235
+ def forward(
236
+ self,
237
+ pixel_values: Optional[torch.Tensor] = None,
238
+ return_dict: Optional[bool] = None,
239
+ **kwargs: Unpack[TransformersKwargs],
240
+ ) -> BaseModelOutputWithPooling:
241
+ if pixel_values is None:
242
+ raise ValueError("You must specify `pixel_values`")
243
+ pixel_values = pixel_values.to(dtype=self.dtype)
244
+ if return_dict is None:
245
+ return_dict = self.config.use_return_dict
246
+ spatial_features = self.forward_features(pixel_values)
247
+ last_hidden_state = spatial_features.flatten(2).transpose(1, 2)
248
+ pooled_output = self.norm(spatial_features.mean([-2, -1])) if self.add_pooling_layer else None
249
+ if not return_dict:
250
+ return (last_hidden_state, pooled_output)
251
+ return BaseModelOutputWithPooling(last_hidden_state=last_hidden_state, pooler_output=pooled_output)
252
+
253
+
254
+ class MMEarthForImageClassification(MMEarthPreTrainedModel):
255
+ def __init__(self, config: MMEarthConfig):
256
+ super().__init__(config)
257
+ self.num_labels = config.num_labels
258
+ self.mmearth = MMEarthModel(config, add_pooling_layer=True)
259
+ self.classifier = (
260
+ nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
261
+ )
262
+ self.post_init()
263
+
264
+ def forward(
265
+ self,
266
+ pixel_values: Optional[torch.Tensor] = None,
267
+ labels: Optional[torch.Tensor] = None,
268
+ return_dict: Optional[bool] = None,
269
+ **kwargs: Unpack[TransformersKwargs],
270
+ ) -> ImageClassifierOutput:
271
+ outputs = self.mmearth(pixel_values=pixel_values, return_dict=True, **kwargs)
272
+ logits = self.classifier(outputs.pooler_output)
273
+ loss = None
274
+ if labels is not None:
275
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
276
+ if not return_dict:
277
+ output = (logits,) + outputs[1:]
278
+ return ((loss,) + output) if loss is not None else output
279
+ return ImageClassifierOutput(
280
+ loss=loss,
281
+ logits=logits,
282
+ hidden_states=outputs.hidden_states,
283
+ attentions=outputs.attentions,
284
+ )
285
+
286
+
287
+ __all__ = [
288
+ "MMEarthConfig",
289
+ "MMEarthForImageClassification",
290
+ "MMEarthModel",
291
+ "MMEarthPreTrainedModel",
292
+ ]
mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/pipeline_mmearth.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """MMEarth image feature extraction pipeline."""
6
+
7
+ from typing import Any, Union
8
+
9
+ from transformers.pipelines.base import GenericTensor, build_pipeline_init_args
10
+ from transformers.pipelines.image_feature_extraction import ImageFeatureExtractionPipeline
11
+ from transformers.utils import add_end_docstrings, is_vision_available
12
+
13
+
14
+ if is_vision_available():
15
+ from transformers.image_utils import load_image
16
+
17
+
18
+ @add_end_docstrings(
19
+ build_pipeline_init_args(has_image_processor=True),
20
+ """
21
+ pool (`bool`, *optional*, defaults to `False`):
22
+ Whether or not to return the pooled output. If `False`, the model will return the raw hidden states.
23
+ """,
24
+ )
25
+ class MMEarthImageFeatureExtractionPipeline(ImageFeatureExtractionPipeline):
26
+ """
27
+ MMEarth image feature extraction pipeline.
28
+
29
+ This pipeline wraps [`MMEarthModel`] for Sentinel-2 multispectral and RGB/BGR geospatial feature extraction.
30
+ It extends [`ImageFeatureExtractionPipeline`] with support for numpy arrays and file paths in addition to
31
+ standard image inputs.
32
+ """
33
+
34
+ def _sanitize_parameters(
35
+ self,
36
+ image_processor_kwargs=None,
37
+ return_tensors=None,
38
+ pool=None,
39
+ **kwargs,
40
+ ):
41
+ preprocess_params = {} if image_processor_kwargs is None else dict(image_processor_kwargs)
42
+ if "timeout" in kwargs:
43
+ preprocess_params["timeout"] = kwargs["timeout"]
44
+
45
+ postprocess_params = {}
46
+ if pool is not None:
47
+ postprocess_params["pool"] = pool
48
+ if return_tensors is not None:
49
+ postprocess_params["return_tensors"] = return_tensors
50
+
51
+ return preprocess_params, {}, postprocess_params
52
+
53
+ def preprocess(self, image, timeout=None, **image_processor_kwargs) -> dict[str, GenericTensor]:
54
+ if not isinstance(image, (list, tuple)) and not hasattr(image, "shape"):
55
+ image = load_image(image, timeout=timeout)
56
+ model_inputs = self.image_processor(image, return_tensors="pt", **image_processor_kwargs)
57
+ model_inputs = model_inputs.to(self.dtype)
58
+ return model_inputs
59
+
60
+ def __call__(
61
+ self,
62
+ *args: Union[str, Any, list[Any]],
63
+ **kwargs: Any,
64
+ ) -> list[Any]:
65
+ return super().__call__(*args, **kwargs)
66
+
67
+
68
+ __all__ = ["MMEarthImageFeatureExtractionPipeline"]
mmearth-convnextv2-atto-all-mod-1m-128-uncertainty-112x16/preprocessor_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "MMEarthImageProcessor",
3
+ "size": {
4
+ "height": 112,
5
+ "width": 112
6
+ },
7
+ "do_resize": false,
8
+ "do_rescale": false,
9
+ "rescale_factor": 1.0,
10
+ "do_normalize": false,
11
+ "do_convert_rgb": false,
12
+ "channel_order": "rgb",
13
+ "image_mean": null,
14
+ "image_std": null,
15
+ "auto_map": {
16
+ "AutoImageProcessor": "image_processing_mmearth.MMEarthImageProcessor"
17
+ }
18
+ }
mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/config.json ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MMEarthModel"
4
+ ],
5
+ "band_names": [
6
+ "B1",
7
+ "B2",
8
+ "B3",
9
+ "B4",
10
+ "B5",
11
+ "B6",
12
+ "B7",
13
+ "B8A",
14
+ "B8",
15
+ "B9",
16
+ "B11",
17
+ "B12"
18
+ ],
19
+ "channel_order": "rgb",
20
+ "checkpoint_stage": "pretrain",
21
+ "dataset": "1M_64",
22
+ "depths": [
23
+ 2,
24
+ 2,
25
+ 6,
26
+ 2
27
+ ],
28
+ "do_rescale": false,
29
+ "drop_path_rate": 0.0,
30
+ "dtype": "float32",
31
+ "hidden_act": "gelu",
32
+ "hidden_size": 320,
33
+ "hidden_sizes": [
34
+ 40,
35
+ 80,
36
+ 160,
37
+ 320
38
+ ],
39
+ "id2label": {},
40
+ "image_mean": null,
41
+ "image_size": 56,
42
+ "image_std": null,
43
+ "input_modality": "all_mod",
44
+ "label2id": {},
45
+ "layer_norm_eps": 1e-06,
46
+ "loss_aggr": "uncertainty",
47
+ "model_size": "atto",
48
+ "model_type": "mmearth",
49
+ "num_channels": 12,
50
+ "num_stages": 4,
51
+ "patch_size": 8,
52
+ "rescale_factor": 1.0,
53
+ "transformers_version": "5.0.0",
54
+ "use_orig_stem": false,
55
+ "auto_map": {
56
+ "AutoConfig": "modeling_mmearth.MMEarthConfig",
57
+ "AutoModel": "modeling_mmearth.MMEarthModel",
58
+ "AutoModelForImageClassification": "modeling_mmearth.MMEarthForImageClassification"
59
+ },
60
+ "custom_pipelines": {
61
+ "mmearth-feature-extraction": {
62
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
63
+ "pt": [
64
+ "AutoModel"
65
+ ]
66
+ }
67
+ },
68
+ "legacy_checkpoint": "checkpoint-199.pth",
69
+ "legacy_source": "pt-all_mod_atto_1M_64_uncertainty_56-8"
70
+ }
mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/image_processing_mmearth.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """Image processor for MMEarth models."""
6
+
7
+ from typing import Optional, Union
8
+
9
+ import numpy as np
10
+
11
+ from transformers.image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict
12
+ from transformers.image_transforms import resize, to_channel_dimension_format
13
+ from transformers.image_utils import (
14
+ ChannelDimension,
15
+ ImageInput,
16
+ PILImageResampling,
17
+ infer_channel_dimension_format,
18
+ make_flat_list_of_images,
19
+ to_numpy_array,
20
+ valid_images,
21
+ validate_preprocess_arguments,
22
+ )
23
+ from transformers.utils import TensorType, filter_out_non_signature_kwargs, logging
24
+
25
+
26
+ logger = logging.get_logger(__name__)
27
+
28
+
29
+ def _resize_multispectral(image: np.ndarray, size: dict[str, int], input_data_format: ChannelDimension) -> np.ndarray:
30
+ target_height, target_width = size["height"], size["width"]
31
+
32
+ if input_data_format == ChannelDimension.FIRST:
33
+ image = np.transpose(image, (1, 2, 0))
34
+
35
+ height, width, _ = image.shape
36
+ if height == target_height and width == target_width:
37
+ resized = image
38
+ else:
39
+ try:
40
+ import cv2
41
+ except ImportError as exc:
42
+ raise ImportError(
43
+ "Multispectral resize requires OpenCV (`opencv-python`) when input has more than 4 channels."
44
+ ) from exc
45
+ resized = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
46
+
47
+ if input_data_format == ChannelDimension.FIRST:
48
+ return np.transpose(resized, (2, 0, 1))
49
+ return resized
50
+
51
+
52
+ def _reorder_channels(image: np.ndarray, channel_order: str, input_data_format: ChannelDimension) -> np.ndarray:
53
+ if channel_order != "bgr":
54
+ return image
55
+
56
+ if input_data_format == ChannelDimension.FIRST:
57
+ if image.shape[0] < 3:
58
+ return image
59
+ return image[[2, 1, 0], ...]
60
+ if image.shape[-1] < 3:
61
+ return image
62
+ return image[..., [2, 1, 0]]
63
+
64
+
65
+ class MMEarthImageProcessor(BaseImageProcessor):
66
+ """
67
+ Image processor for MMEarth ConvNeXt V2 encoders.
68
+
69
+ RGB checkpoints were trained with BGR channel order. Set `channel_order="bgr"` (default for RGB models) to swap
70
+ the first three channels from RGB to BGR before inference.
71
+ """
72
+
73
+ model_input_names = ["pixel_values"]
74
+
75
+ def __init__(
76
+ self,
77
+ do_resize: bool = True,
78
+ size: Optional[dict[str, int]] = None,
79
+ resample: PILImageResampling = PILImageResampling.BILINEAR,
80
+ do_rescale: bool = False,
81
+ rescale_factor: float = 1.0,
82
+ do_normalize: bool = True,
83
+ image_mean: Optional[Union[float, list[float]]] = None,
84
+ image_std: Optional[Union[float, list[float]]] = None,
85
+ do_convert_rgb: bool = False,
86
+ channel_order: str = "rgb",
87
+ **kwargs,
88
+ ):
89
+ super().__init__(**kwargs)
90
+ size = size if size is not None else {"height": 112, "width": 112}
91
+ self.do_resize = do_resize
92
+ self.size = size
93
+ self.resample = resample
94
+ self.do_rescale = do_rescale
95
+ self.rescale_factor = rescale_factor
96
+ self.do_normalize = do_normalize
97
+ self.image_mean = image_mean
98
+ self.image_std = image_std
99
+ self.do_convert_rgb = do_convert_rgb
100
+ self.channel_order = channel_order
101
+
102
+ @filter_out_non_signature_kwargs()
103
+ def preprocess(
104
+ self,
105
+ images: ImageInput,
106
+ do_resize: Optional[bool] = None,
107
+ size: Optional[dict[str, int]] = None,
108
+ resample: Optional[PILImageResampling] = None,
109
+ do_rescale: Optional[bool] = None,
110
+ rescale_factor: Optional[float] = None,
111
+ do_normalize: Optional[bool] = None,
112
+ image_mean: Optional[Union[float, list[float]]] = None,
113
+ image_std: Optional[Union[float, list[float]]] = None,
114
+ return_tensors: Optional[Union[str, TensorType]] = None,
115
+ data_format: Union[str, ChannelDimension] = ChannelDimension.FIRST,
116
+ input_data_format: Optional[Union[str, ChannelDimension]] = None,
117
+ do_convert_rgb: Optional[bool] = None,
118
+ channel_order: Optional[str] = None,
119
+ ):
120
+ do_resize = do_resize if do_resize is not None else self.do_resize
121
+ size = size if size is not None else self.size
122
+ size = get_size_dict(size, default_to_square=True)
123
+ resample = resample if resample is not None else self.resample
124
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
125
+ rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor
126
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
127
+ image_mean = image_mean if image_mean is not None else self.image_mean
128
+ image_std = image_std if image_std is not None else self.image_std
129
+ do_convert_rgb = do_convert_rgb if do_convert_rgb is not None else self.do_convert_rgb
130
+ channel_order = channel_order if channel_order is not None else self.channel_order
131
+
132
+ if do_normalize and (image_mean is None or image_std is None):
133
+ raise ValueError("Normalization requires `image_mean` and `image_std` with one value per channel.")
134
+
135
+ images = make_flat_list_of_images(images)
136
+ if not valid_images(images):
137
+ raise ValueError("Invalid image type. Must be PIL, numpy, or torch tensor.")
138
+
139
+ validate_preprocess_arguments(
140
+ do_rescale=do_rescale,
141
+ rescale_factor=rescale_factor,
142
+ do_normalize=do_normalize,
143
+ image_mean=image_mean,
144
+ image_std=image_std,
145
+ do_resize=do_resize,
146
+ size=size,
147
+ resample=resample,
148
+ )
149
+
150
+ processed_images = []
151
+ for image in images:
152
+ image = to_numpy_array(image)
153
+ if do_convert_rgb:
154
+ image = self._convert_image_to_rgb(image)
155
+
156
+ if input_data_format is None:
157
+ try:
158
+ input_data_format = infer_channel_dimension_format(image)
159
+ except ValueError:
160
+ input_data_format = ChannelDimension.LAST
161
+
162
+ image = _reorder_channels(image, channel_order=channel_order, input_data_format=input_data_format)
163
+
164
+ if do_resize:
165
+ num_channels = image.shape[0] if input_data_format == ChannelDimension.FIRST else image.shape[-1]
166
+ if num_channels > 4:
167
+ image = _resize_multispectral(image, size=size, input_data_format=input_data_format)
168
+ else:
169
+ image = resize(
170
+ image,
171
+ size=(size["height"], size["width"]),
172
+ resample=resample,
173
+ input_data_format=input_data_format,
174
+ )
175
+
176
+ if do_rescale:
177
+ image = image * rescale_factor
178
+
179
+ if do_normalize:
180
+ image = self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)
181
+
182
+ image = to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)
183
+ processed_images.append(image)
184
+
185
+ data = {"pixel_values": processed_images}
186
+ return BatchFeature(data=data, tensor_type=return_tensors)
187
+
188
+
189
+ __all__ = ["MMEarthImageProcessor"]
mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cf0cfcaad31d7cd1cf4f7e730bec625d0f4d769eec25bf9aa3bed1e0bb105648
3
+ size 13572696
mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/modeling_mmearth.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ """Self-contained MMEarth model and config for trust_remote_code loading."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Optional
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from timm.models.layers import DropPath, trunc_normal_
11
+ from torch import nn
12
+
13
+ from transformers.configuration_utils import PretrainedConfig as PreTrainedConfig
14
+ from transformers.modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
15
+ from transformers.modeling_utils import PreTrainedModel
16
+ from transformers.processing_utils import Unpack
17
+ from transformers.utils import TransformersKwargs, logging
18
+
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+ MODEL_SIZE_PRESETS = {
23
+ "atto": {"depths": [2, 2, 6, 2], "hidden_sizes": [40, 80, 160, 320]},
24
+ "femto": {"depths": [2, 2, 6, 2], "hidden_sizes": [48, 96, 192, 384]},
25
+ "pico": {"depths": [2, 2, 6, 2], "hidden_sizes": [64, 128, 256, 512]},
26
+ "nano": {"depths": [2, 2, 8, 2], "hidden_sizes": [80, 160, 320, 640]},
27
+ "tiny": {"depths": [3, 3, 9, 3], "hidden_sizes": [96, 192, 384, 768]},
28
+ "base": {"depths": [3, 3, 27, 3], "hidden_sizes": [128, 256, 512, 1024]},
29
+ "large": {"depths": [3, 3, 27, 3], "hidden_sizes": [192, 384, 768, 1536]},
30
+ "huge": {"depths": [3, 3, 27, 3], "hidden_sizes": [352, 704, 1408, 2816]},
31
+ }
32
+
33
+
34
+ class MMEarthConfig(PreTrainedConfig):
35
+ model_type = "mmearth"
36
+
37
+ def __init__(
38
+ self,
39
+ depths: list[int] | None = None,
40
+ hidden_sizes: list[int] | None = None,
41
+ num_channels: int = 12,
42
+ image_size: int = 112,
43
+ patch_size: int = 16,
44
+ drop_path_rate: float = 0.0,
45
+ layer_norm_eps: float = 1e-6,
46
+ hidden_act: str = "gelu",
47
+ use_orig_stem: bool = False,
48
+ model_size: str = "atto",
49
+ input_modality: str = "all_mod",
50
+ channel_order: str = "rgb",
51
+ dataset: str = "1M_128",
52
+ loss_aggr: str = "uncertainty",
53
+ checkpoint_stage: str = "pretrain",
54
+ image_mean: list[float] | None = None,
55
+ image_std: list[float] | None = None,
56
+ do_rescale: bool = False,
57
+ rescale_factor: float = 1.0,
58
+ num_labels: int = 0,
59
+ **kwargs,
60
+ ):
61
+ super().__init__(**kwargs)
62
+ preset = MODEL_SIZE_PRESETS[model_size]
63
+ self.model_size = model_size
64
+ self.input_modality = input_modality
65
+ self.channel_order = channel_order
66
+ self.dataset = dataset
67
+ self.loss_aggr = loss_aggr
68
+ self.checkpoint_stage = checkpoint_stage
69
+ self.num_channels = num_channels
70
+ self.image_size = image_size
71
+ self.patch_size = patch_size
72
+ self.drop_path_rate = drop_path_rate
73
+ self.layer_norm_eps = layer_norm_eps
74
+ self.hidden_act = hidden_act
75
+ self.use_orig_stem = use_orig_stem
76
+ self.num_labels = num_labels
77
+ self.do_rescale = do_rescale
78
+ self.rescale_factor = rescale_factor
79
+ self.depths = depths if depths is not None else preset["depths"]
80
+ self.hidden_sizes = hidden_sizes if hidden_sizes is not None else preset["hidden_sizes"]
81
+ self.hidden_size = self.hidden_sizes[-1]
82
+ self.num_stages = len(self.depths)
83
+ self.image_mean = image_mean
84
+ self.image_std = image_std
85
+
86
+
87
+ class MMEarthLayerNorm(nn.Module):
88
+ def __init__(self, normalized_shape: int, eps: float = 1e-6, data_format: str = "channels_last"):
89
+ super().__init__()
90
+ self.weight = nn.Parameter(torch.ones(normalized_shape))
91
+ self.bias = nn.Parameter(torch.zeros(normalized_shape))
92
+ self.eps = eps
93
+ self.data_format = data_format
94
+ self.normalized_shape = (normalized_shape,)
95
+
96
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
97
+ if self.data_format == "channels_last":
98
+ return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
99
+ u = x.mean(1, keepdim=True)
100
+ s = (x - u).pow(2).mean(1, keepdim=True)
101
+ x = (x - u) / torch.sqrt(s + self.eps)
102
+ return self.weight[:, None, None] * x + self.bias[:, None, None]
103
+
104
+
105
+ class MMEarthGRN(nn.Module):
106
+ def __init__(self, dim: int):
107
+ super().__init__()
108
+ self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
109
+ self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
110
+
111
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
112
+ gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
113
+ nx = gx / (gx.mean(dim=-1, keepdim=True) + 1e-4)
114
+ return self.gamma * (x * nx) + self.beta + x
115
+
116
+
117
+ class MMEarthConvNeXtBlock(nn.Module):
118
+ def __init__(self, dim: int, drop_path: float = 0.0):
119
+ super().__init__()
120
+ self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
121
+ self.norm = MMEarthLayerNorm(dim, eps=1e-6)
122
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
123
+ self.act = nn.GELU()
124
+ self.grn = MMEarthGRN(4 * dim)
125
+ self.pwconv2 = nn.Linear(4 * dim, dim)
126
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
127
+
128
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
129
+ input_tensor = x
130
+ x = self.dwconv(x)
131
+ x = x.permute(0, 2, 3, 1)
132
+ x = self.norm(x)
133
+ x = self.pwconv1(x)
134
+ x = self.act(x)
135
+ x = self.grn(x)
136
+ x = self.pwconv2(x)
137
+ x = x.permute(0, 3, 1, 2)
138
+ return input_tensor + self.drop_path(x)
139
+
140
+
141
+ class MMEarthPreTrainedModel(PreTrainedModel):
142
+ config_class = MMEarthConfig
143
+ config: MMEarthConfig
144
+ base_model_prefix = "mmearth"
145
+ main_input_name = "pixel_values"
146
+ input_modalities = ("image",)
147
+ supports_gradient_checkpointing = True
148
+ _no_split_modules = ["MMEarthConvNeXtBlock"]
149
+
150
+ def _init_weights(self, module):
151
+ super()._init_weights(module)
152
+ if isinstance(module, (nn.Conv2d, nn.Linear)):
153
+ trunc_normal_(module.weight, std=0.02)
154
+ if module.bias is not None:
155
+ nn.init.constant_(module.bias, 0)
156
+
157
+
158
+ class MMEarthModel(MMEarthPreTrainedModel):
159
+ def __init__(self, config: MMEarthConfig, add_pooling_layer: bool = True):
160
+ super().__init__(config)
161
+ self.config = config
162
+ self.add_pooling_layer = add_pooling_layer
163
+ depths = config.depths
164
+ dims = config.hidden_sizes
165
+ patch_size = config.patch_size
166
+ num_stages = len(depths)
167
+ self.downsample_layers = nn.ModuleList()
168
+ if config.use_orig_stem:
169
+ self.stem_orig = nn.Sequential(
170
+ nn.Conv2d(
171
+ config.num_channels,
172
+ dims[0],
173
+ kernel_size=patch_size // (2 ** (num_stages - 1)),
174
+ stride=patch_size // (2 ** (num_stages - 1)),
175
+ ),
176
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
177
+ )
178
+ self.initial_conv = None
179
+ self.stem = None
180
+ else:
181
+ self.stem_orig = None
182
+ self.initial_conv = nn.Sequential(
183
+ nn.Conv2d(config.num_channels, dims[0], kernel_size=3, stride=1),
184
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
185
+ nn.GELU(),
186
+ )
187
+ stem_kernel = patch_size // (2 ** (num_stages - 1))
188
+ self.stem = nn.Sequential(
189
+ nn.Conv2d(
190
+ dims[0],
191
+ dims[0],
192
+ kernel_size=stem_kernel,
193
+ stride=stem_kernel,
194
+ padding=stem_kernel // 2,
195
+ groups=dims[0],
196
+ ),
197
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
198
+ )
199
+ for i in range(3):
200
+ self.downsample_layers.append(
201
+ nn.Sequential(
202
+ MMEarthLayerNorm(dims[i], eps=config.layer_norm_eps, data_format="channels_first"),
203
+ nn.Conv2d(dims[i], dims[i + 1], kernel_size=2, stride=2),
204
+ )
205
+ )
206
+ dp_rates = [
207
+ x.item()
208
+ for x in torch.linspace(0, config.drop_path_rate, sum(depths), device=torch.device("cpu"))
209
+ ]
210
+ cur = 0
211
+ self.stages = nn.ModuleList()
212
+ for i in range(num_stages):
213
+ stage = nn.Sequential(
214
+ *[MMEarthConvNeXtBlock(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]
215
+ )
216
+ self.stages.append(stage)
217
+ cur += depths[i]
218
+ self.norm = nn.LayerNorm(dims[-1], eps=config.layer_norm_eps)
219
+ self.post_init()
220
+
221
+ def _forward_stem(self, x: torch.Tensor) -> torch.Tensor:
222
+ if self.config.use_orig_stem:
223
+ return self.stem_orig(x)
224
+ x = self.initial_conv(x)
225
+ return self.stem(x)
226
+
227
+ def forward_features(self, pixel_values: torch.Tensor) -> torch.Tensor:
228
+ x = self._forward_stem(pixel_values)
229
+ x = self.stages[0](x)
230
+ for i in range(3):
231
+ x = self.downsample_layers[i](x)
232
+ x = self.stages[i + 1](x)
233
+ return x
234
+
235
+ def forward(
236
+ self,
237
+ pixel_values: Optional[torch.Tensor] = None,
238
+ return_dict: Optional[bool] = None,
239
+ **kwargs: Unpack[TransformersKwargs],
240
+ ) -> BaseModelOutputWithPooling:
241
+ if pixel_values is None:
242
+ raise ValueError("You must specify `pixel_values`")
243
+ pixel_values = pixel_values.to(dtype=self.dtype)
244
+ if return_dict is None:
245
+ return_dict = self.config.use_return_dict
246
+ spatial_features = self.forward_features(pixel_values)
247
+ last_hidden_state = spatial_features.flatten(2).transpose(1, 2)
248
+ pooled_output = self.norm(spatial_features.mean([-2, -1])) if self.add_pooling_layer else None
249
+ if not return_dict:
250
+ return (last_hidden_state, pooled_output)
251
+ return BaseModelOutputWithPooling(last_hidden_state=last_hidden_state, pooler_output=pooled_output)
252
+
253
+
254
+ class MMEarthForImageClassification(MMEarthPreTrainedModel):
255
+ def __init__(self, config: MMEarthConfig):
256
+ super().__init__(config)
257
+ self.num_labels = config.num_labels
258
+ self.mmearth = MMEarthModel(config, add_pooling_layer=True)
259
+ self.classifier = (
260
+ nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
261
+ )
262
+ self.post_init()
263
+
264
+ def forward(
265
+ self,
266
+ pixel_values: Optional[torch.Tensor] = None,
267
+ labels: Optional[torch.Tensor] = None,
268
+ return_dict: Optional[bool] = None,
269
+ **kwargs: Unpack[TransformersKwargs],
270
+ ) -> ImageClassifierOutput:
271
+ outputs = self.mmearth(pixel_values=pixel_values, return_dict=True, **kwargs)
272
+ logits = self.classifier(outputs.pooler_output)
273
+ loss = None
274
+ if labels is not None:
275
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
276
+ if not return_dict:
277
+ output = (logits,) + outputs[1:]
278
+ return ((loss,) + output) if loss is not None else output
279
+ return ImageClassifierOutput(
280
+ loss=loss,
281
+ logits=logits,
282
+ hidden_states=outputs.hidden_states,
283
+ attentions=outputs.attentions,
284
+ )
285
+
286
+
287
+ __all__ = [
288
+ "MMEarthConfig",
289
+ "MMEarthForImageClassification",
290
+ "MMEarthModel",
291
+ "MMEarthPreTrainedModel",
292
+ ]
mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/pipeline_mmearth.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """MMEarth image feature extraction pipeline."""
6
+
7
+ from typing import Any, Union
8
+
9
+ from transformers.pipelines.base import GenericTensor, build_pipeline_init_args
10
+ from transformers.pipelines.image_feature_extraction import ImageFeatureExtractionPipeline
11
+ from transformers.utils import add_end_docstrings, is_vision_available
12
+
13
+
14
+ if is_vision_available():
15
+ from transformers.image_utils import load_image
16
+
17
+
18
+ @add_end_docstrings(
19
+ build_pipeline_init_args(has_image_processor=True),
20
+ """
21
+ pool (`bool`, *optional*, defaults to `False`):
22
+ Whether or not to return the pooled output. If `False`, the model will return the raw hidden states.
23
+ """,
24
+ )
25
+ class MMEarthImageFeatureExtractionPipeline(ImageFeatureExtractionPipeline):
26
+ """
27
+ MMEarth image feature extraction pipeline.
28
+
29
+ This pipeline wraps [`MMEarthModel`] for Sentinel-2 multispectral and RGB/BGR geospatial feature extraction.
30
+ It extends [`ImageFeatureExtractionPipeline`] with support for numpy arrays and file paths in addition to
31
+ standard image inputs.
32
+ """
33
+
34
+ def _sanitize_parameters(
35
+ self,
36
+ image_processor_kwargs=None,
37
+ return_tensors=None,
38
+ pool=None,
39
+ **kwargs,
40
+ ):
41
+ preprocess_params = {} if image_processor_kwargs is None else dict(image_processor_kwargs)
42
+ if "timeout" in kwargs:
43
+ preprocess_params["timeout"] = kwargs["timeout"]
44
+
45
+ postprocess_params = {}
46
+ if pool is not None:
47
+ postprocess_params["pool"] = pool
48
+ if return_tensors is not None:
49
+ postprocess_params["return_tensors"] = return_tensors
50
+
51
+ return preprocess_params, {}, postprocess_params
52
+
53
+ def preprocess(self, image, timeout=None, **image_processor_kwargs) -> dict[str, GenericTensor]:
54
+ if not isinstance(image, (list, tuple)) and not hasattr(image, "shape"):
55
+ image = load_image(image, timeout=timeout)
56
+ model_inputs = self.image_processor(image, return_tensors="pt", **image_processor_kwargs)
57
+ model_inputs = model_inputs.to(self.dtype)
58
+ return model_inputs
59
+
60
+ def __call__(
61
+ self,
62
+ *args: Union[str, Any, list[Any]],
63
+ **kwargs: Any,
64
+ ) -> list[Any]:
65
+ return super().__call__(*args, **kwargs)
66
+
67
+
68
+ __all__ = ["MMEarthImageFeatureExtractionPipeline"]
mmearth-convnextv2-atto-all-mod-1m-64-uncertainty-56x8/preprocessor_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "MMEarthImageProcessor",
3
+ "size": {
4
+ "height": 56,
5
+ "width": 56
6
+ },
7
+ "do_resize": false,
8
+ "do_rescale": false,
9
+ "rescale_factor": 1.0,
10
+ "do_normalize": false,
11
+ "do_convert_rgb": false,
12
+ "channel_order": "rgb",
13
+ "image_mean": null,
14
+ "image_std": null,
15
+ "auto_map": {
16
+ "AutoImageProcessor": "image_processing_mmearth.MMEarthImageProcessor"
17
+ }
18
+ }
mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/config.json ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MMEarthModel"
4
+ ],
5
+ "band_names": [
6
+ "B1",
7
+ "B2",
8
+ "B3",
9
+ "B4",
10
+ "B5",
11
+ "B6",
12
+ "B7",
13
+ "B8A",
14
+ "B8",
15
+ "B9",
16
+ "B11",
17
+ "B12"
18
+ ],
19
+ "channel_order": "rgb",
20
+ "checkpoint_stage": "pretrain",
21
+ "dataset": "1M_64",
22
+ "depths": [
23
+ 2,
24
+ 2,
25
+ 6,
26
+ 2
27
+ ],
28
+ "do_rescale": false,
29
+ "drop_path_rate": 0.0,
30
+ "dtype": "float32",
31
+ "hidden_act": "gelu",
32
+ "hidden_size": 320,
33
+ "hidden_sizes": [
34
+ 40,
35
+ 80,
36
+ 160,
37
+ 320
38
+ ],
39
+ "id2label": {},
40
+ "image_mean": null,
41
+ "image_size": 56,
42
+ "image_std": null,
43
+ "input_modality": "all_mod",
44
+ "label2id": {},
45
+ "layer_norm_eps": 1e-06,
46
+ "loss_aggr": "unweighted",
47
+ "model_size": "atto",
48
+ "model_type": "mmearth",
49
+ "num_channels": 12,
50
+ "num_stages": 4,
51
+ "patch_size": 8,
52
+ "rescale_factor": 1.0,
53
+ "transformers_version": "5.0.0",
54
+ "use_orig_stem": false,
55
+ "auto_map": {
56
+ "AutoConfig": "modeling_mmearth.MMEarthConfig",
57
+ "AutoModel": "modeling_mmearth.MMEarthModel",
58
+ "AutoModelForImageClassification": "modeling_mmearth.MMEarthForImageClassification"
59
+ },
60
+ "custom_pipelines": {
61
+ "mmearth-feature-extraction": {
62
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
63
+ "pt": [
64
+ "AutoModel"
65
+ ]
66
+ }
67
+ },
68
+ "legacy_checkpoint": "checkpoint-199.pth",
69
+ "legacy_source": "pt-all_mod_atto_1M_64_unweighted_56-8"
70
+ }
mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/image_processing_mmearth.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """Image processor for MMEarth models."""
6
+
7
+ from typing import Optional, Union
8
+
9
+ import numpy as np
10
+
11
+ from transformers.image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict
12
+ from transformers.image_transforms import resize, to_channel_dimension_format
13
+ from transformers.image_utils import (
14
+ ChannelDimension,
15
+ ImageInput,
16
+ PILImageResampling,
17
+ infer_channel_dimension_format,
18
+ make_flat_list_of_images,
19
+ to_numpy_array,
20
+ valid_images,
21
+ validate_preprocess_arguments,
22
+ )
23
+ from transformers.utils import TensorType, filter_out_non_signature_kwargs, logging
24
+
25
+
26
+ logger = logging.get_logger(__name__)
27
+
28
+
29
+ def _resize_multispectral(image: np.ndarray, size: dict[str, int], input_data_format: ChannelDimension) -> np.ndarray:
30
+ target_height, target_width = size["height"], size["width"]
31
+
32
+ if input_data_format == ChannelDimension.FIRST:
33
+ image = np.transpose(image, (1, 2, 0))
34
+
35
+ height, width, _ = image.shape
36
+ if height == target_height and width == target_width:
37
+ resized = image
38
+ else:
39
+ try:
40
+ import cv2
41
+ except ImportError as exc:
42
+ raise ImportError(
43
+ "Multispectral resize requires OpenCV (`opencv-python`) when input has more than 4 channels."
44
+ ) from exc
45
+ resized = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
46
+
47
+ if input_data_format == ChannelDimension.FIRST:
48
+ return np.transpose(resized, (2, 0, 1))
49
+ return resized
50
+
51
+
52
+ def _reorder_channels(image: np.ndarray, channel_order: str, input_data_format: ChannelDimension) -> np.ndarray:
53
+ if channel_order != "bgr":
54
+ return image
55
+
56
+ if input_data_format == ChannelDimension.FIRST:
57
+ if image.shape[0] < 3:
58
+ return image
59
+ return image[[2, 1, 0], ...]
60
+ if image.shape[-1] < 3:
61
+ return image
62
+ return image[..., [2, 1, 0]]
63
+
64
+
65
+ class MMEarthImageProcessor(BaseImageProcessor):
66
+ """
67
+ Image processor for MMEarth ConvNeXt V2 encoders.
68
+
69
+ RGB checkpoints were trained with BGR channel order. Set `channel_order="bgr"` (default for RGB models) to swap
70
+ the first three channels from RGB to BGR before inference.
71
+ """
72
+
73
+ model_input_names = ["pixel_values"]
74
+
75
+ def __init__(
76
+ self,
77
+ do_resize: bool = True,
78
+ size: Optional[dict[str, int]] = None,
79
+ resample: PILImageResampling = PILImageResampling.BILINEAR,
80
+ do_rescale: bool = False,
81
+ rescale_factor: float = 1.0,
82
+ do_normalize: bool = True,
83
+ image_mean: Optional[Union[float, list[float]]] = None,
84
+ image_std: Optional[Union[float, list[float]]] = None,
85
+ do_convert_rgb: bool = False,
86
+ channel_order: str = "rgb",
87
+ **kwargs,
88
+ ):
89
+ super().__init__(**kwargs)
90
+ size = size if size is not None else {"height": 112, "width": 112}
91
+ self.do_resize = do_resize
92
+ self.size = size
93
+ self.resample = resample
94
+ self.do_rescale = do_rescale
95
+ self.rescale_factor = rescale_factor
96
+ self.do_normalize = do_normalize
97
+ self.image_mean = image_mean
98
+ self.image_std = image_std
99
+ self.do_convert_rgb = do_convert_rgb
100
+ self.channel_order = channel_order
101
+
102
+ @filter_out_non_signature_kwargs()
103
+ def preprocess(
104
+ self,
105
+ images: ImageInput,
106
+ do_resize: Optional[bool] = None,
107
+ size: Optional[dict[str, int]] = None,
108
+ resample: Optional[PILImageResampling] = None,
109
+ do_rescale: Optional[bool] = None,
110
+ rescale_factor: Optional[float] = None,
111
+ do_normalize: Optional[bool] = None,
112
+ image_mean: Optional[Union[float, list[float]]] = None,
113
+ image_std: Optional[Union[float, list[float]]] = None,
114
+ return_tensors: Optional[Union[str, TensorType]] = None,
115
+ data_format: Union[str, ChannelDimension] = ChannelDimension.FIRST,
116
+ input_data_format: Optional[Union[str, ChannelDimension]] = None,
117
+ do_convert_rgb: Optional[bool] = None,
118
+ channel_order: Optional[str] = None,
119
+ ):
120
+ do_resize = do_resize if do_resize is not None else self.do_resize
121
+ size = size if size is not None else self.size
122
+ size = get_size_dict(size, default_to_square=True)
123
+ resample = resample if resample is not None else self.resample
124
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
125
+ rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor
126
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
127
+ image_mean = image_mean if image_mean is not None else self.image_mean
128
+ image_std = image_std if image_std is not None else self.image_std
129
+ do_convert_rgb = do_convert_rgb if do_convert_rgb is not None else self.do_convert_rgb
130
+ channel_order = channel_order if channel_order is not None else self.channel_order
131
+
132
+ if do_normalize and (image_mean is None or image_std is None):
133
+ raise ValueError("Normalization requires `image_mean` and `image_std` with one value per channel.")
134
+
135
+ images = make_flat_list_of_images(images)
136
+ if not valid_images(images):
137
+ raise ValueError("Invalid image type. Must be PIL, numpy, or torch tensor.")
138
+
139
+ validate_preprocess_arguments(
140
+ do_rescale=do_rescale,
141
+ rescale_factor=rescale_factor,
142
+ do_normalize=do_normalize,
143
+ image_mean=image_mean,
144
+ image_std=image_std,
145
+ do_resize=do_resize,
146
+ size=size,
147
+ resample=resample,
148
+ )
149
+
150
+ processed_images = []
151
+ for image in images:
152
+ image = to_numpy_array(image)
153
+ if do_convert_rgb:
154
+ image = self._convert_image_to_rgb(image)
155
+
156
+ if input_data_format is None:
157
+ try:
158
+ input_data_format = infer_channel_dimension_format(image)
159
+ except ValueError:
160
+ input_data_format = ChannelDimension.LAST
161
+
162
+ image = _reorder_channels(image, channel_order=channel_order, input_data_format=input_data_format)
163
+
164
+ if do_resize:
165
+ num_channels = image.shape[0] if input_data_format == ChannelDimension.FIRST else image.shape[-1]
166
+ if num_channels > 4:
167
+ image = _resize_multispectral(image, size=size, input_data_format=input_data_format)
168
+ else:
169
+ image = resize(
170
+ image,
171
+ size=(size["height"], size["width"]),
172
+ resample=resample,
173
+ input_data_format=input_data_format,
174
+ )
175
+
176
+ if do_rescale:
177
+ image = image * rescale_factor
178
+
179
+ if do_normalize:
180
+ image = self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)
181
+
182
+ image = to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)
183
+ processed_images.append(image)
184
+
185
+ data = {"pixel_values": processed_images}
186
+ return BatchFeature(data=data, tensor_type=return_tensors)
187
+
188
+
189
+ __all__ = ["MMEarthImageProcessor"]
mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:232078eaefd5615e3d4e8b9d7b3c0aa5836611b1bea18c3bfaa2f8b3aa397560
3
+ size 13572696
mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/modeling_mmearth.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ """Self-contained MMEarth model and config for trust_remote_code loading."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Optional
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from timm.models.layers import DropPath, trunc_normal_
11
+ from torch import nn
12
+
13
+ from transformers.configuration_utils import PretrainedConfig as PreTrainedConfig
14
+ from transformers.modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
15
+ from transformers.modeling_utils import PreTrainedModel
16
+ from transformers.processing_utils import Unpack
17
+ from transformers.utils import TransformersKwargs, logging
18
+
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+ MODEL_SIZE_PRESETS = {
23
+ "atto": {"depths": [2, 2, 6, 2], "hidden_sizes": [40, 80, 160, 320]},
24
+ "femto": {"depths": [2, 2, 6, 2], "hidden_sizes": [48, 96, 192, 384]},
25
+ "pico": {"depths": [2, 2, 6, 2], "hidden_sizes": [64, 128, 256, 512]},
26
+ "nano": {"depths": [2, 2, 8, 2], "hidden_sizes": [80, 160, 320, 640]},
27
+ "tiny": {"depths": [3, 3, 9, 3], "hidden_sizes": [96, 192, 384, 768]},
28
+ "base": {"depths": [3, 3, 27, 3], "hidden_sizes": [128, 256, 512, 1024]},
29
+ "large": {"depths": [3, 3, 27, 3], "hidden_sizes": [192, 384, 768, 1536]},
30
+ "huge": {"depths": [3, 3, 27, 3], "hidden_sizes": [352, 704, 1408, 2816]},
31
+ }
32
+
33
+
34
+ class MMEarthConfig(PreTrainedConfig):
35
+ model_type = "mmearth"
36
+
37
+ def __init__(
38
+ self,
39
+ depths: list[int] | None = None,
40
+ hidden_sizes: list[int] | None = None,
41
+ num_channels: int = 12,
42
+ image_size: int = 112,
43
+ patch_size: int = 16,
44
+ drop_path_rate: float = 0.0,
45
+ layer_norm_eps: float = 1e-6,
46
+ hidden_act: str = "gelu",
47
+ use_orig_stem: bool = False,
48
+ model_size: str = "atto",
49
+ input_modality: str = "all_mod",
50
+ channel_order: str = "rgb",
51
+ dataset: str = "1M_128",
52
+ loss_aggr: str = "uncertainty",
53
+ checkpoint_stage: str = "pretrain",
54
+ image_mean: list[float] | None = None,
55
+ image_std: list[float] | None = None,
56
+ do_rescale: bool = False,
57
+ rescale_factor: float = 1.0,
58
+ num_labels: int = 0,
59
+ **kwargs,
60
+ ):
61
+ super().__init__(**kwargs)
62
+ preset = MODEL_SIZE_PRESETS[model_size]
63
+ self.model_size = model_size
64
+ self.input_modality = input_modality
65
+ self.channel_order = channel_order
66
+ self.dataset = dataset
67
+ self.loss_aggr = loss_aggr
68
+ self.checkpoint_stage = checkpoint_stage
69
+ self.num_channels = num_channels
70
+ self.image_size = image_size
71
+ self.patch_size = patch_size
72
+ self.drop_path_rate = drop_path_rate
73
+ self.layer_norm_eps = layer_norm_eps
74
+ self.hidden_act = hidden_act
75
+ self.use_orig_stem = use_orig_stem
76
+ self.num_labels = num_labels
77
+ self.do_rescale = do_rescale
78
+ self.rescale_factor = rescale_factor
79
+ self.depths = depths if depths is not None else preset["depths"]
80
+ self.hidden_sizes = hidden_sizes if hidden_sizes is not None else preset["hidden_sizes"]
81
+ self.hidden_size = self.hidden_sizes[-1]
82
+ self.num_stages = len(self.depths)
83
+ self.image_mean = image_mean
84
+ self.image_std = image_std
85
+
86
+
87
+ class MMEarthLayerNorm(nn.Module):
88
+ def __init__(self, normalized_shape: int, eps: float = 1e-6, data_format: str = "channels_last"):
89
+ super().__init__()
90
+ self.weight = nn.Parameter(torch.ones(normalized_shape))
91
+ self.bias = nn.Parameter(torch.zeros(normalized_shape))
92
+ self.eps = eps
93
+ self.data_format = data_format
94
+ self.normalized_shape = (normalized_shape,)
95
+
96
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
97
+ if self.data_format == "channels_last":
98
+ return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
99
+ u = x.mean(1, keepdim=True)
100
+ s = (x - u).pow(2).mean(1, keepdim=True)
101
+ x = (x - u) / torch.sqrt(s + self.eps)
102
+ return self.weight[:, None, None] * x + self.bias[:, None, None]
103
+
104
+
105
+ class MMEarthGRN(nn.Module):
106
+ def __init__(self, dim: int):
107
+ super().__init__()
108
+ self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
109
+ self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
110
+
111
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
112
+ gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
113
+ nx = gx / (gx.mean(dim=-1, keepdim=True) + 1e-4)
114
+ return self.gamma * (x * nx) + self.beta + x
115
+
116
+
117
+ class MMEarthConvNeXtBlock(nn.Module):
118
+ def __init__(self, dim: int, drop_path: float = 0.0):
119
+ super().__init__()
120
+ self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
121
+ self.norm = MMEarthLayerNorm(dim, eps=1e-6)
122
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
123
+ self.act = nn.GELU()
124
+ self.grn = MMEarthGRN(4 * dim)
125
+ self.pwconv2 = nn.Linear(4 * dim, dim)
126
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
127
+
128
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
129
+ input_tensor = x
130
+ x = self.dwconv(x)
131
+ x = x.permute(0, 2, 3, 1)
132
+ x = self.norm(x)
133
+ x = self.pwconv1(x)
134
+ x = self.act(x)
135
+ x = self.grn(x)
136
+ x = self.pwconv2(x)
137
+ x = x.permute(0, 3, 1, 2)
138
+ return input_tensor + self.drop_path(x)
139
+
140
+
141
+ class MMEarthPreTrainedModel(PreTrainedModel):
142
+ config_class = MMEarthConfig
143
+ config: MMEarthConfig
144
+ base_model_prefix = "mmearth"
145
+ main_input_name = "pixel_values"
146
+ input_modalities = ("image",)
147
+ supports_gradient_checkpointing = True
148
+ _no_split_modules = ["MMEarthConvNeXtBlock"]
149
+
150
+ def _init_weights(self, module):
151
+ super()._init_weights(module)
152
+ if isinstance(module, (nn.Conv2d, nn.Linear)):
153
+ trunc_normal_(module.weight, std=0.02)
154
+ if module.bias is not None:
155
+ nn.init.constant_(module.bias, 0)
156
+
157
+
158
+ class MMEarthModel(MMEarthPreTrainedModel):
159
+ def __init__(self, config: MMEarthConfig, add_pooling_layer: bool = True):
160
+ super().__init__(config)
161
+ self.config = config
162
+ self.add_pooling_layer = add_pooling_layer
163
+ depths = config.depths
164
+ dims = config.hidden_sizes
165
+ patch_size = config.patch_size
166
+ num_stages = len(depths)
167
+ self.downsample_layers = nn.ModuleList()
168
+ if config.use_orig_stem:
169
+ self.stem_orig = nn.Sequential(
170
+ nn.Conv2d(
171
+ config.num_channels,
172
+ dims[0],
173
+ kernel_size=patch_size // (2 ** (num_stages - 1)),
174
+ stride=patch_size // (2 ** (num_stages - 1)),
175
+ ),
176
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
177
+ )
178
+ self.initial_conv = None
179
+ self.stem = None
180
+ else:
181
+ self.stem_orig = None
182
+ self.initial_conv = nn.Sequential(
183
+ nn.Conv2d(config.num_channels, dims[0], kernel_size=3, stride=1),
184
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
185
+ nn.GELU(),
186
+ )
187
+ stem_kernel = patch_size // (2 ** (num_stages - 1))
188
+ self.stem = nn.Sequential(
189
+ nn.Conv2d(
190
+ dims[0],
191
+ dims[0],
192
+ kernel_size=stem_kernel,
193
+ stride=stem_kernel,
194
+ padding=stem_kernel // 2,
195
+ groups=dims[0],
196
+ ),
197
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
198
+ )
199
+ for i in range(3):
200
+ self.downsample_layers.append(
201
+ nn.Sequential(
202
+ MMEarthLayerNorm(dims[i], eps=config.layer_norm_eps, data_format="channels_first"),
203
+ nn.Conv2d(dims[i], dims[i + 1], kernel_size=2, stride=2),
204
+ )
205
+ )
206
+ dp_rates = [
207
+ x.item()
208
+ for x in torch.linspace(0, config.drop_path_rate, sum(depths), device=torch.device("cpu"))
209
+ ]
210
+ cur = 0
211
+ self.stages = nn.ModuleList()
212
+ for i in range(num_stages):
213
+ stage = nn.Sequential(
214
+ *[MMEarthConvNeXtBlock(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]
215
+ )
216
+ self.stages.append(stage)
217
+ cur += depths[i]
218
+ self.norm = nn.LayerNorm(dims[-1], eps=config.layer_norm_eps)
219
+ self.post_init()
220
+
221
+ def _forward_stem(self, x: torch.Tensor) -> torch.Tensor:
222
+ if self.config.use_orig_stem:
223
+ return self.stem_orig(x)
224
+ x = self.initial_conv(x)
225
+ return self.stem(x)
226
+
227
+ def forward_features(self, pixel_values: torch.Tensor) -> torch.Tensor:
228
+ x = self._forward_stem(pixel_values)
229
+ x = self.stages[0](x)
230
+ for i in range(3):
231
+ x = self.downsample_layers[i](x)
232
+ x = self.stages[i + 1](x)
233
+ return x
234
+
235
+ def forward(
236
+ self,
237
+ pixel_values: Optional[torch.Tensor] = None,
238
+ return_dict: Optional[bool] = None,
239
+ **kwargs: Unpack[TransformersKwargs],
240
+ ) -> BaseModelOutputWithPooling:
241
+ if pixel_values is None:
242
+ raise ValueError("You must specify `pixel_values`")
243
+ pixel_values = pixel_values.to(dtype=self.dtype)
244
+ if return_dict is None:
245
+ return_dict = self.config.use_return_dict
246
+ spatial_features = self.forward_features(pixel_values)
247
+ last_hidden_state = spatial_features.flatten(2).transpose(1, 2)
248
+ pooled_output = self.norm(spatial_features.mean([-2, -1])) if self.add_pooling_layer else None
249
+ if not return_dict:
250
+ return (last_hidden_state, pooled_output)
251
+ return BaseModelOutputWithPooling(last_hidden_state=last_hidden_state, pooler_output=pooled_output)
252
+
253
+
254
+ class MMEarthForImageClassification(MMEarthPreTrainedModel):
255
+ def __init__(self, config: MMEarthConfig):
256
+ super().__init__(config)
257
+ self.num_labels = config.num_labels
258
+ self.mmearth = MMEarthModel(config, add_pooling_layer=True)
259
+ self.classifier = (
260
+ nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
261
+ )
262
+ self.post_init()
263
+
264
+ def forward(
265
+ self,
266
+ pixel_values: Optional[torch.Tensor] = None,
267
+ labels: Optional[torch.Tensor] = None,
268
+ return_dict: Optional[bool] = None,
269
+ **kwargs: Unpack[TransformersKwargs],
270
+ ) -> ImageClassifierOutput:
271
+ outputs = self.mmearth(pixel_values=pixel_values, return_dict=True, **kwargs)
272
+ logits = self.classifier(outputs.pooler_output)
273
+ loss = None
274
+ if labels is not None:
275
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
276
+ if not return_dict:
277
+ output = (logits,) + outputs[1:]
278
+ return ((loss,) + output) if loss is not None else output
279
+ return ImageClassifierOutput(
280
+ loss=loss,
281
+ logits=logits,
282
+ hidden_states=outputs.hidden_states,
283
+ attentions=outputs.attentions,
284
+ )
285
+
286
+
287
+ __all__ = [
288
+ "MMEarthConfig",
289
+ "MMEarthForImageClassification",
290
+ "MMEarthModel",
291
+ "MMEarthPreTrainedModel",
292
+ ]
mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/pipeline_mmearth.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """MMEarth image feature extraction pipeline."""
6
+
7
+ from typing import Any, Union
8
+
9
+ from transformers.pipelines.base import GenericTensor, build_pipeline_init_args
10
+ from transformers.pipelines.image_feature_extraction import ImageFeatureExtractionPipeline
11
+ from transformers.utils import add_end_docstrings, is_vision_available
12
+
13
+
14
+ if is_vision_available():
15
+ from transformers.image_utils import load_image
16
+
17
+
18
+ @add_end_docstrings(
19
+ build_pipeline_init_args(has_image_processor=True),
20
+ """
21
+ pool (`bool`, *optional*, defaults to `False`):
22
+ Whether or not to return the pooled output. If `False`, the model will return the raw hidden states.
23
+ """,
24
+ )
25
+ class MMEarthImageFeatureExtractionPipeline(ImageFeatureExtractionPipeline):
26
+ """
27
+ MMEarth image feature extraction pipeline.
28
+
29
+ This pipeline wraps [`MMEarthModel`] for Sentinel-2 multispectral and RGB/BGR geospatial feature extraction.
30
+ It extends [`ImageFeatureExtractionPipeline`] with support for numpy arrays and file paths in addition to
31
+ standard image inputs.
32
+ """
33
+
34
+ def _sanitize_parameters(
35
+ self,
36
+ image_processor_kwargs=None,
37
+ return_tensors=None,
38
+ pool=None,
39
+ **kwargs,
40
+ ):
41
+ preprocess_params = {} if image_processor_kwargs is None else dict(image_processor_kwargs)
42
+ if "timeout" in kwargs:
43
+ preprocess_params["timeout"] = kwargs["timeout"]
44
+
45
+ postprocess_params = {}
46
+ if pool is not None:
47
+ postprocess_params["pool"] = pool
48
+ if return_tensors is not None:
49
+ postprocess_params["return_tensors"] = return_tensors
50
+
51
+ return preprocess_params, {}, postprocess_params
52
+
53
+ def preprocess(self, image, timeout=None, **image_processor_kwargs) -> dict[str, GenericTensor]:
54
+ if not isinstance(image, (list, tuple)) and not hasattr(image, "shape"):
55
+ image = load_image(image, timeout=timeout)
56
+ model_inputs = self.image_processor(image, return_tensors="pt", **image_processor_kwargs)
57
+ model_inputs = model_inputs.to(self.dtype)
58
+ return model_inputs
59
+
60
+ def __call__(
61
+ self,
62
+ *args: Union[str, Any, list[Any]],
63
+ **kwargs: Any,
64
+ ) -> list[Any]:
65
+ return super().__call__(*args, **kwargs)
66
+
67
+
68
+ __all__ = ["MMEarthImageFeatureExtractionPipeline"]
mmearth-convnextv2-atto-all-mod-1m-64-unweighted-56x8/preprocessor_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "MMEarthImageProcessor",
3
+ "size": {
4
+ "height": 56,
5
+ "width": 56
6
+ },
7
+ "do_resize": false,
8
+ "do_rescale": false,
9
+ "rescale_factor": 1.0,
10
+ "do_normalize": false,
11
+ "do_convert_rgb": false,
12
+ "channel_order": "rgb",
13
+ "image_mean": null,
14
+ "image_std": null,
15
+ "auto_map": {
16
+ "AutoImageProcessor": "image_processing_mmearth.MMEarthImageProcessor"
17
+ }
18
+ }
mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/config.json ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MMEarthModel"
4
+ ],
5
+ "band_names": [
6
+ "B1",
7
+ "B2",
8
+ "B3",
9
+ "B4",
10
+ "B5",
11
+ "B6",
12
+ "B7",
13
+ "B8A",
14
+ "B8",
15
+ "B9",
16
+ "B11",
17
+ "B12"
18
+ ],
19
+ "channel_order": "rgb",
20
+ "checkpoint_stage": "pretrain",
21
+ "dataset": "1M_64",
22
+ "depths": [
23
+ 2,
24
+ 2,
25
+ 6,
26
+ 2
27
+ ],
28
+ "do_rescale": false,
29
+ "drop_path_rate": 0.0,
30
+ "dtype": "float32",
31
+ "hidden_act": "gelu",
32
+ "hidden_size": 320,
33
+ "hidden_sizes": [
34
+ 40,
35
+ 80,
36
+ 160,
37
+ 320
38
+ ],
39
+ "id2label": {},
40
+ "image_mean": null,
41
+ "image_size": 56,
42
+ "image_std": null,
43
+ "input_modality": "img_mod",
44
+ "label2id": {},
45
+ "layer_norm_eps": 1e-06,
46
+ "loss_aggr": "uncertainty",
47
+ "model_size": "atto",
48
+ "model_type": "mmearth",
49
+ "num_channels": 12,
50
+ "num_stages": 4,
51
+ "patch_size": 8,
52
+ "rescale_factor": 1.0,
53
+ "transformers_version": "5.0.0",
54
+ "use_orig_stem": false,
55
+ "auto_map": {
56
+ "AutoConfig": "modeling_mmearth.MMEarthConfig",
57
+ "AutoModel": "modeling_mmearth.MMEarthModel",
58
+ "AutoModelForImageClassification": "modeling_mmearth.MMEarthForImageClassification"
59
+ },
60
+ "custom_pipelines": {
61
+ "mmearth-feature-extraction": {
62
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
63
+ "pt": [
64
+ "AutoModel"
65
+ ]
66
+ }
67
+ },
68
+ "legacy_checkpoint": "checkpoint-199.pth",
69
+ "legacy_source": "pt-img_mod_atto_1M_64_uncertainty_56-8"
70
+ }
mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/image_processing_mmearth.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """Image processor for MMEarth models."""
6
+
7
+ from typing import Optional, Union
8
+
9
+ import numpy as np
10
+
11
+ from transformers.image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict
12
+ from transformers.image_transforms import resize, to_channel_dimension_format
13
+ from transformers.image_utils import (
14
+ ChannelDimension,
15
+ ImageInput,
16
+ PILImageResampling,
17
+ infer_channel_dimension_format,
18
+ make_flat_list_of_images,
19
+ to_numpy_array,
20
+ valid_images,
21
+ validate_preprocess_arguments,
22
+ )
23
+ from transformers.utils import TensorType, filter_out_non_signature_kwargs, logging
24
+
25
+
26
+ logger = logging.get_logger(__name__)
27
+
28
+
29
+ def _resize_multispectral(image: np.ndarray, size: dict[str, int], input_data_format: ChannelDimension) -> np.ndarray:
30
+ target_height, target_width = size["height"], size["width"]
31
+
32
+ if input_data_format == ChannelDimension.FIRST:
33
+ image = np.transpose(image, (1, 2, 0))
34
+
35
+ height, width, _ = image.shape
36
+ if height == target_height and width == target_width:
37
+ resized = image
38
+ else:
39
+ try:
40
+ import cv2
41
+ except ImportError as exc:
42
+ raise ImportError(
43
+ "Multispectral resize requires OpenCV (`opencv-python`) when input has more than 4 channels."
44
+ ) from exc
45
+ resized = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
46
+
47
+ if input_data_format == ChannelDimension.FIRST:
48
+ return np.transpose(resized, (2, 0, 1))
49
+ return resized
50
+
51
+
52
+ def _reorder_channels(image: np.ndarray, channel_order: str, input_data_format: ChannelDimension) -> np.ndarray:
53
+ if channel_order != "bgr":
54
+ return image
55
+
56
+ if input_data_format == ChannelDimension.FIRST:
57
+ if image.shape[0] < 3:
58
+ return image
59
+ return image[[2, 1, 0], ...]
60
+ if image.shape[-1] < 3:
61
+ return image
62
+ return image[..., [2, 1, 0]]
63
+
64
+
65
+ class MMEarthImageProcessor(BaseImageProcessor):
66
+ """
67
+ Image processor for MMEarth ConvNeXt V2 encoders.
68
+
69
+ RGB checkpoints were trained with BGR channel order. Set `channel_order="bgr"` (default for RGB models) to swap
70
+ the first three channels from RGB to BGR before inference.
71
+ """
72
+
73
+ model_input_names = ["pixel_values"]
74
+
75
+ def __init__(
76
+ self,
77
+ do_resize: bool = True,
78
+ size: Optional[dict[str, int]] = None,
79
+ resample: PILImageResampling = PILImageResampling.BILINEAR,
80
+ do_rescale: bool = False,
81
+ rescale_factor: float = 1.0,
82
+ do_normalize: bool = True,
83
+ image_mean: Optional[Union[float, list[float]]] = None,
84
+ image_std: Optional[Union[float, list[float]]] = None,
85
+ do_convert_rgb: bool = False,
86
+ channel_order: str = "rgb",
87
+ **kwargs,
88
+ ):
89
+ super().__init__(**kwargs)
90
+ size = size if size is not None else {"height": 112, "width": 112}
91
+ self.do_resize = do_resize
92
+ self.size = size
93
+ self.resample = resample
94
+ self.do_rescale = do_rescale
95
+ self.rescale_factor = rescale_factor
96
+ self.do_normalize = do_normalize
97
+ self.image_mean = image_mean
98
+ self.image_std = image_std
99
+ self.do_convert_rgb = do_convert_rgb
100
+ self.channel_order = channel_order
101
+
102
+ @filter_out_non_signature_kwargs()
103
+ def preprocess(
104
+ self,
105
+ images: ImageInput,
106
+ do_resize: Optional[bool] = None,
107
+ size: Optional[dict[str, int]] = None,
108
+ resample: Optional[PILImageResampling] = None,
109
+ do_rescale: Optional[bool] = None,
110
+ rescale_factor: Optional[float] = None,
111
+ do_normalize: Optional[bool] = None,
112
+ image_mean: Optional[Union[float, list[float]]] = None,
113
+ image_std: Optional[Union[float, list[float]]] = None,
114
+ return_tensors: Optional[Union[str, TensorType]] = None,
115
+ data_format: Union[str, ChannelDimension] = ChannelDimension.FIRST,
116
+ input_data_format: Optional[Union[str, ChannelDimension]] = None,
117
+ do_convert_rgb: Optional[bool] = None,
118
+ channel_order: Optional[str] = None,
119
+ ):
120
+ do_resize = do_resize if do_resize is not None else self.do_resize
121
+ size = size if size is not None else self.size
122
+ size = get_size_dict(size, default_to_square=True)
123
+ resample = resample if resample is not None else self.resample
124
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
125
+ rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor
126
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
127
+ image_mean = image_mean if image_mean is not None else self.image_mean
128
+ image_std = image_std if image_std is not None else self.image_std
129
+ do_convert_rgb = do_convert_rgb if do_convert_rgb is not None else self.do_convert_rgb
130
+ channel_order = channel_order if channel_order is not None else self.channel_order
131
+
132
+ if do_normalize and (image_mean is None or image_std is None):
133
+ raise ValueError("Normalization requires `image_mean` and `image_std` with one value per channel.")
134
+
135
+ images = make_flat_list_of_images(images)
136
+ if not valid_images(images):
137
+ raise ValueError("Invalid image type. Must be PIL, numpy, or torch tensor.")
138
+
139
+ validate_preprocess_arguments(
140
+ do_rescale=do_rescale,
141
+ rescale_factor=rescale_factor,
142
+ do_normalize=do_normalize,
143
+ image_mean=image_mean,
144
+ image_std=image_std,
145
+ do_resize=do_resize,
146
+ size=size,
147
+ resample=resample,
148
+ )
149
+
150
+ processed_images = []
151
+ for image in images:
152
+ image = to_numpy_array(image)
153
+ if do_convert_rgb:
154
+ image = self._convert_image_to_rgb(image)
155
+
156
+ if input_data_format is None:
157
+ try:
158
+ input_data_format = infer_channel_dimension_format(image)
159
+ except ValueError:
160
+ input_data_format = ChannelDimension.LAST
161
+
162
+ image = _reorder_channels(image, channel_order=channel_order, input_data_format=input_data_format)
163
+
164
+ if do_resize:
165
+ num_channels = image.shape[0] if input_data_format == ChannelDimension.FIRST else image.shape[-1]
166
+ if num_channels > 4:
167
+ image = _resize_multispectral(image, size=size, input_data_format=input_data_format)
168
+ else:
169
+ image = resize(
170
+ image,
171
+ size=(size["height"], size["width"]),
172
+ resample=resample,
173
+ input_data_format=input_data_format,
174
+ )
175
+
176
+ if do_rescale:
177
+ image = image * rescale_factor
178
+
179
+ if do_normalize:
180
+ image = self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)
181
+
182
+ image = to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)
183
+ processed_images.append(image)
184
+
185
+ data = {"pixel_values": processed_images}
186
+ return BatchFeature(data=data, tensor_type=return_tensors)
187
+
188
+
189
+ __all__ = ["MMEarthImageProcessor"]
mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1ec8508296b7288978f784efa4db9e9df14c52e9e74bb0d618c6eb4d28f45c98
3
+ size 13572696
mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/modeling_mmearth.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ """Self-contained MMEarth model and config for trust_remote_code loading."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Optional
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from timm.models.layers import DropPath, trunc_normal_
11
+ from torch import nn
12
+
13
+ from transformers.configuration_utils import PretrainedConfig as PreTrainedConfig
14
+ from transformers.modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
15
+ from transformers.modeling_utils import PreTrainedModel
16
+ from transformers.processing_utils import Unpack
17
+ from transformers.utils import TransformersKwargs, logging
18
+
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+ MODEL_SIZE_PRESETS = {
23
+ "atto": {"depths": [2, 2, 6, 2], "hidden_sizes": [40, 80, 160, 320]},
24
+ "femto": {"depths": [2, 2, 6, 2], "hidden_sizes": [48, 96, 192, 384]},
25
+ "pico": {"depths": [2, 2, 6, 2], "hidden_sizes": [64, 128, 256, 512]},
26
+ "nano": {"depths": [2, 2, 8, 2], "hidden_sizes": [80, 160, 320, 640]},
27
+ "tiny": {"depths": [3, 3, 9, 3], "hidden_sizes": [96, 192, 384, 768]},
28
+ "base": {"depths": [3, 3, 27, 3], "hidden_sizes": [128, 256, 512, 1024]},
29
+ "large": {"depths": [3, 3, 27, 3], "hidden_sizes": [192, 384, 768, 1536]},
30
+ "huge": {"depths": [3, 3, 27, 3], "hidden_sizes": [352, 704, 1408, 2816]},
31
+ }
32
+
33
+
34
+ class MMEarthConfig(PreTrainedConfig):
35
+ model_type = "mmearth"
36
+
37
+ def __init__(
38
+ self,
39
+ depths: list[int] | None = None,
40
+ hidden_sizes: list[int] | None = None,
41
+ num_channels: int = 12,
42
+ image_size: int = 112,
43
+ patch_size: int = 16,
44
+ drop_path_rate: float = 0.0,
45
+ layer_norm_eps: float = 1e-6,
46
+ hidden_act: str = "gelu",
47
+ use_orig_stem: bool = False,
48
+ model_size: str = "atto",
49
+ input_modality: str = "all_mod",
50
+ channel_order: str = "rgb",
51
+ dataset: str = "1M_128",
52
+ loss_aggr: str = "uncertainty",
53
+ checkpoint_stage: str = "pretrain",
54
+ image_mean: list[float] | None = None,
55
+ image_std: list[float] | None = None,
56
+ do_rescale: bool = False,
57
+ rescale_factor: float = 1.0,
58
+ num_labels: int = 0,
59
+ **kwargs,
60
+ ):
61
+ super().__init__(**kwargs)
62
+ preset = MODEL_SIZE_PRESETS[model_size]
63
+ self.model_size = model_size
64
+ self.input_modality = input_modality
65
+ self.channel_order = channel_order
66
+ self.dataset = dataset
67
+ self.loss_aggr = loss_aggr
68
+ self.checkpoint_stage = checkpoint_stage
69
+ self.num_channels = num_channels
70
+ self.image_size = image_size
71
+ self.patch_size = patch_size
72
+ self.drop_path_rate = drop_path_rate
73
+ self.layer_norm_eps = layer_norm_eps
74
+ self.hidden_act = hidden_act
75
+ self.use_orig_stem = use_orig_stem
76
+ self.num_labels = num_labels
77
+ self.do_rescale = do_rescale
78
+ self.rescale_factor = rescale_factor
79
+ self.depths = depths if depths is not None else preset["depths"]
80
+ self.hidden_sizes = hidden_sizes if hidden_sizes is not None else preset["hidden_sizes"]
81
+ self.hidden_size = self.hidden_sizes[-1]
82
+ self.num_stages = len(self.depths)
83
+ self.image_mean = image_mean
84
+ self.image_std = image_std
85
+
86
+
87
+ class MMEarthLayerNorm(nn.Module):
88
+ def __init__(self, normalized_shape: int, eps: float = 1e-6, data_format: str = "channels_last"):
89
+ super().__init__()
90
+ self.weight = nn.Parameter(torch.ones(normalized_shape))
91
+ self.bias = nn.Parameter(torch.zeros(normalized_shape))
92
+ self.eps = eps
93
+ self.data_format = data_format
94
+ self.normalized_shape = (normalized_shape,)
95
+
96
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
97
+ if self.data_format == "channels_last":
98
+ return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
99
+ u = x.mean(1, keepdim=True)
100
+ s = (x - u).pow(2).mean(1, keepdim=True)
101
+ x = (x - u) / torch.sqrt(s + self.eps)
102
+ return self.weight[:, None, None] * x + self.bias[:, None, None]
103
+
104
+
105
+ class MMEarthGRN(nn.Module):
106
+ def __init__(self, dim: int):
107
+ super().__init__()
108
+ self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
109
+ self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
110
+
111
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
112
+ gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
113
+ nx = gx / (gx.mean(dim=-1, keepdim=True) + 1e-4)
114
+ return self.gamma * (x * nx) + self.beta + x
115
+
116
+
117
+ class MMEarthConvNeXtBlock(nn.Module):
118
+ def __init__(self, dim: int, drop_path: float = 0.0):
119
+ super().__init__()
120
+ self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
121
+ self.norm = MMEarthLayerNorm(dim, eps=1e-6)
122
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
123
+ self.act = nn.GELU()
124
+ self.grn = MMEarthGRN(4 * dim)
125
+ self.pwconv2 = nn.Linear(4 * dim, dim)
126
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
127
+
128
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
129
+ input_tensor = x
130
+ x = self.dwconv(x)
131
+ x = x.permute(0, 2, 3, 1)
132
+ x = self.norm(x)
133
+ x = self.pwconv1(x)
134
+ x = self.act(x)
135
+ x = self.grn(x)
136
+ x = self.pwconv2(x)
137
+ x = x.permute(0, 3, 1, 2)
138
+ return input_tensor + self.drop_path(x)
139
+
140
+
141
+ class MMEarthPreTrainedModel(PreTrainedModel):
142
+ config_class = MMEarthConfig
143
+ config: MMEarthConfig
144
+ base_model_prefix = "mmearth"
145
+ main_input_name = "pixel_values"
146
+ input_modalities = ("image",)
147
+ supports_gradient_checkpointing = True
148
+ _no_split_modules = ["MMEarthConvNeXtBlock"]
149
+
150
+ def _init_weights(self, module):
151
+ super()._init_weights(module)
152
+ if isinstance(module, (nn.Conv2d, nn.Linear)):
153
+ trunc_normal_(module.weight, std=0.02)
154
+ if module.bias is not None:
155
+ nn.init.constant_(module.bias, 0)
156
+
157
+
158
+ class MMEarthModel(MMEarthPreTrainedModel):
159
+ def __init__(self, config: MMEarthConfig, add_pooling_layer: bool = True):
160
+ super().__init__(config)
161
+ self.config = config
162
+ self.add_pooling_layer = add_pooling_layer
163
+ depths = config.depths
164
+ dims = config.hidden_sizes
165
+ patch_size = config.patch_size
166
+ num_stages = len(depths)
167
+ self.downsample_layers = nn.ModuleList()
168
+ if config.use_orig_stem:
169
+ self.stem_orig = nn.Sequential(
170
+ nn.Conv2d(
171
+ config.num_channels,
172
+ dims[0],
173
+ kernel_size=patch_size // (2 ** (num_stages - 1)),
174
+ stride=patch_size // (2 ** (num_stages - 1)),
175
+ ),
176
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
177
+ )
178
+ self.initial_conv = None
179
+ self.stem = None
180
+ else:
181
+ self.stem_orig = None
182
+ self.initial_conv = nn.Sequential(
183
+ nn.Conv2d(config.num_channels, dims[0], kernel_size=3, stride=1),
184
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
185
+ nn.GELU(),
186
+ )
187
+ stem_kernel = patch_size // (2 ** (num_stages - 1))
188
+ self.stem = nn.Sequential(
189
+ nn.Conv2d(
190
+ dims[0],
191
+ dims[0],
192
+ kernel_size=stem_kernel,
193
+ stride=stem_kernel,
194
+ padding=stem_kernel // 2,
195
+ groups=dims[0],
196
+ ),
197
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
198
+ )
199
+ for i in range(3):
200
+ self.downsample_layers.append(
201
+ nn.Sequential(
202
+ MMEarthLayerNorm(dims[i], eps=config.layer_norm_eps, data_format="channels_first"),
203
+ nn.Conv2d(dims[i], dims[i + 1], kernel_size=2, stride=2),
204
+ )
205
+ )
206
+ dp_rates = [
207
+ x.item()
208
+ for x in torch.linspace(0, config.drop_path_rate, sum(depths), device=torch.device("cpu"))
209
+ ]
210
+ cur = 0
211
+ self.stages = nn.ModuleList()
212
+ for i in range(num_stages):
213
+ stage = nn.Sequential(
214
+ *[MMEarthConvNeXtBlock(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]
215
+ )
216
+ self.stages.append(stage)
217
+ cur += depths[i]
218
+ self.norm = nn.LayerNorm(dims[-1], eps=config.layer_norm_eps)
219
+ self.post_init()
220
+
221
+ def _forward_stem(self, x: torch.Tensor) -> torch.Tensor:
222
+ if self.config.use_orig_stem:
223
+ return self.stem_orig(x)
224
+ x = self.initial_conv(x)
225
+ return self.stem(x)
226
+
227
+ def forward_features(self, pixel_values: torch.Tensor) -> torch.Tensor:
228
+ x = self._forward_stem(pixel_values)
229
+ x = self.stages[0](x)
230
+ for i in range(3):
231
+ x = self.downsample_layers[i](x)
232
+ x = self.stages[i + 1](x)
233
+ return x
234
+
235
+ def forward(
236
+ self,
237
+ pixel_values: Optional[torch.Tensor] = None,
238
+ return_dict: Optional[bool] = None,
239
+ **kwargs: Unpack[TransformersKwargs],
240
+ ) -> BaseModelOutputWithPooling:
241
+ if pixel_values is None:
242
+ raise ValueError("You must specify `pixel_values`")
243
+ pixel_values = pixel_values.to(dtype=self.dtype)
244
+ if return_dict is None:
245
+ return_dict = self.config.use_return_dict
246
+ spatial_features = self.forward_features(pixel_values)
247
+ last_hidden_state = spatial_features.flatten(2).transpose(1, 2)
248
+ pooled_output = self.norm(spatial_features.mean([-2, -1])) if self.add_pooling_layer else None
249
+ if not return_dict:
250
+ return (last_hidden_state, pooled_output)
251
+ return BaseModelOutputWithPooling(last_hidden_state=last_hidden_state, pooler_output=pooled_output)
252
+
253
+
254
+ class MMEarthForImageClassification(MMEarthPreTrainedModel):
255
+ def __init__(self, config: MMEarthConfig):
256
+ super().__init__(config)
257
+ self.num_labels = config.num_labels
258
+ self.mmearth = MMEarthModel(config, add_pooling_layer=True)
259
+ self.classifier = (
260
+ nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
261
+ )
262
+ self.post_init()
263
+
264
+ def forward(
265
+ self,
266
+ pixel_values: Optional[torch.Tensor] = None,
267
+ labels: Optional[torch.Tensor] = None,
268
+ return_dict: Optional[bool] = None,
269
+ **kwargs: Unpack[TransformersKwargs],
270
+ ) -> ImageClassifierOutput:
271
+ outputs = self.mmearth(pixel_values=pixel_values, return_dict=True, **kwargs)
272
+ logits = self.classifier(outputs.pooler_output)
273
+ loss = None
274
+ if labels is not None:
275
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
276
+ if not return_dict:
277
+ output = (logits,) + outputs[1:]
278
+ return ((loss,) + output) if loss is not None else output
279
+ return ImageClassifierOutput(
280
+ loss=loss,
281
+ logits=logits,
282
+ hidden_states=outputs.hidden_states,
283
+ attentions=outputs.attentions,
284
+ )
285
+
286
+
287
+ __all__ = [
288
+ "MMEarthConfig",
289
+ "MMEarthForImageClassification",
290
+ "MMEarthModel",
291
+ "MMEarthPreTrainedModel",
292
+ ]
mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/pipeline_mmearth.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """MMEarth image feature extraction pipeline."""
6
+
7
+ from typing import Any, Union
8
+
9
+ from transformers.pipelines.base import GenericTensor, build_pipeline_init_args
10
+ from transformers.pipelines.image_feature_extraction import ImageFeatureExtractionPipeline
11
+ from transformers.utils import add_end_docstrings, is_vision_available
12
+
13
+
14
+ if is_vision_available():
15
+ from transformers.image_utils import load_image
16
+
17
+
18
+ @add_end_docstrings(
19
+ build_pipeline_init_args(has_image_processor=True),
20
+ """
21
+ pool (`bool`, *optional*, defaults to `False`):
22
+ Whether or not to return the pooled output. If `False`, the model will return the raw hidden states.
23
+ """,
24
+ )
25
+ class MMEarthImageFeatureExtractionPipeline(ImageFeatureExtractionPipeline):
26
+ """
27
+ MMEarth image feature extraction pipeline.
28
+
29
+ This pipeline wraps [`MMEarthModel`] for Sentinel-2 multispectral and RGB/BGR geospatial feature extraction.
30
+ It extends [`ImageFeatureExtractionPipeline`] with support for numpy arrays and file paths in addition to
31
+ standard image inputs.
32
+ """
33
+
34
+ def _sanitize_parameters(
35
+ self,
36
+ image_processor_kwargs=None,
37
+ return_tensors=None,
38
+ pool=None,
39
+ **kwargs,
40
+ ):
41
+ preprocess_params = {} if image_processor_kwargs is None else dict(image_processor_kwargs)
42
+ if "timeout" in kwargs:
43
+ preprocess_params["timeout"] = kwargs["timeout"]
44
+
45
+ postprocess_params = {}
46
+ if pool is not None:
47
+ postprocess_params["pool"] = pool
48
+ if return_tensors is not None:
49
+ postprocess_params["return_tensors"] = return_tensors
50
+
51
+ return preprocess_params, {}, postprocess_params
52
+
53
+ def preprocess(self, image, timeout=None, **image_processor_kwargs) -> dict[str, GenericTensor]:
54
+ if not isinstance(image, (list, tuple)) and not hasattr(image, "shape"):
55
+ image = load_image(image, timeout=timeout)
56
+ model_inputs = self.image_processor(image, return_tensors="pt", **image_processor_kwargs)
57
+ model_inputs = model_inputs.to(self.dtype)
58
+ return model_inputs
59
+
60
+ def __call__(
61
+ self,
62
+ *args: Union[str, Any, list[Any]],
63
+ **kwargs: Any,
64
+ ) -> list[Any]:
65
+ return super().__call__(*args, **kwargs)
66
+
67
+
68
+ __all__ = ["MMEarthImageFeatureExtractionPipeline"]
mmearth-convnextv2-atto-img-mod-1m-64-uncertainty-56x8/preprocessor_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "MMEarthImageProcessor",
3
+ "size": {
4
+ "height": 56,
5
+ "width": 56
6
+ },
7
+ "do_resize": false,
8
+ "do_rescale": false,
9
+ "rescale_factor": 1.0,
10
+ "do_normalize": false,
11
+ "do_convert_rgb": false,
12
+ "channel_order": "rgb",
13
+ "image_mean": null,
14
+ "image_std": null,
15
+ "auto_map": {
16
+ "AutoImageProcessor": "image_processing_mmearth.MMEarthImageProcessor"
17
+ }
18
+ }
mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/config.json ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MMEarthModel"
4
+ ],
5
+ "band_names": [
6
+ "B1",
7
+ "B2",
8
+ "B3",
9
+ "B4",
10
+ "B5",
11
+ "B6",
12
+ "B7",
13
+ "B8A",
14
+ "B8",
15
+ "B9",
16
+ "B11",
17
+ "B12"
18
+ ],
19
+ "channel_order": "rgb",
20
+ "checkpoint_stage": "pretrain",
21
+ "dataset": "1M_64",
22
+ "depths": [
23
+ 2,
24
+ 2,
25
+ 6,
26
+ 2
27
+ ],
28
+ "do_rescale": false,
29
+ "drop_path_rate": 0.0,
30
+ "dtype": "float32",
31
+ "hidden_act": "gelu",
32
+ "hidden_size": 320,
33
+ "hidden_sizes": [
34
+ 40,
35
+ 80,
36
+ 160,
37
+ 320
38
+ ],
39
+ "id2label": {},
40
+ "image_mean": null,
41
+ "image_size": 56,
42
+ "image_std": null,
43
+ "input_modality": "pix_mod",
44
+ "label2id": {},
45
+ "layer_norm_eps": 1e-06,
46
+ "loss_aggr": "uncertainty",
47
+ "model_size": "atto",
48
+ "model_type": "mmearth",
49
+ "num_channels": 12,
50
+ "num_stages": 4,
51
+ "patch_size": 8,
52
+ "rescale_factor": 1.0,
53
+ "transformers_version": "5.0.0",
54
+ "use_orig_stem": false,
55
+ "auto_map": {
56
+ "AutoConfig": "modeling_mmearth.MMEarthConfig",
57
+ "AutoModel": "modeling_mmearth.MMEarthModel",
58
+ "AutoModelForImageClassification": "modeling_mmearth.MMEarthForImageClassification"
59
+ },
60
+ "custom_pipelines": {
61
+ "mmearth-feature-extraction": {
62
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
63
+ "pt": [
64
+ "AutoModel"
65
+ ]
66
+ }
67
+ },
68
+ "legacy_checkpoint": "checkpoint-199.pth",
69
+ "legacy_source": "pt-pix_mod_atto_1M_64_uncertainty_56-8"
70
+ }
mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/image_processing_mmearth.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """Image processor for MMEarth models."""
6
+
7
+ from typing import Optional, Union
8
+
9
+ import numpy as np
10
+
11
+ from transformers.image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict
12
+ from transformers.image_transforms import resize, to_channel_dimension_format
13
+ from transformers.image_utils import (
14
+ ChannelDimension,
15
+ ImageInput,
16
+ PILImageResampling,
17
+ infer_channel_dimension_format,
18
+ make_flat_list_of_images,
19
+ to_numpy_array,
20
+ valid_images,
21
+ validate_preprocess_arguments,
22
+ )
23
+ from transformers.utils import TensorType, filter_out_non_signature_kwargs, logging
24
+
25
+
26
+ logger = logging.get_logger(__name__)
27
+
28
+
29
+ def _resize_multispectral(image: np.ndarray, size: dict[str, int], input_data_format: ChannelDimension) -> np.ndarray:
30
+ target_height, target_width = size["height"], size["width"]
31
+
32
+ if input_data_format == ChannelDimension.FIRST:
33
+ image = np.transpose(image, (1, 2, 0))
34
+
35
+ height, width, _ = image.shape
36
+ if height == target_height and width == target_width:
37
+ resized = image
38
+ else:
39
+ try:
40
+ import cv2
41
+ except ImportError as exc:
42
+ raise ImportError(
43
+ "Multispectral resize requires OpenCV (`opencv-python`) when input has more than 4 channels."
44
+ ) from exc
45
+ resized = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
46
+
47
+ if input_data_format == ChannelDimension.FIRST:
48
+ return np.transpose(resized, (2, 0, 1))
49
+ return resized
50
+
51
+
52
+ def _reorder_channels(image: np.ndarray, channel_order: str, input_data_format: ChannelDimension) -> np.ndarray:
53
+ if channel_order != "bgr":
54
+ return image
55
+
56
+ if input_data_format == ChannelDimension.FIRST:
57
+ if image.shape[0] < 3:
58
+ return image
59
+ return image[[2, 1, 0], ...]
60
+ if image.shape[-1] < 3:
61
+ return image
62
+ return image[..., [2, 1, 0]]
63
+
64
+
65
+ class MMEarthImageProcessor(BaseImageProcessor):
66
+ """
67
+ Image processor for MMEarth ConvNeXt V2 encoders.
68
+
69
+ RGB checkpoints were trained with BGR channel order. Set `channel_order="bgr"` (default for RGB models) to swap
70
+ the first three channels from RGB to BGR before inference.
71
+ """
72
+
73
+ model_input_names = ["pixel_values"]
74
+
75
+ def __init__(
76
+ self,
77
+ do_resize: bool = True,
78
+ size: Optional[dict[str, int]] = None,
79
+ resample: PILImageResampling = PILImageResampling.BILINEAR,
80
+ do_rescale: bool = False,
81
+ rescale_factor: float = 1.0,
82
+ do_normalize: bool = True,
83
+ image_mean: Optional[Union[float, list[float]]] = None,
84
+ image_std: Optional[Union[float, list[float]]] = None,
85
+ do_convert_rgb: bool = False,
86
+ channel_order: str = "rgb",
87
+ **kwargs,
88
+ ):
89
+ super().__init__(**kwargs)
90
+ size = size if size is not None else {"height": 112, "width": 112}
91
+ self.do_resize = do_resize
92
+ self.size = size
93
+ self.resample = resample
94
+ self.do_rescale = do_rescale
95
+ self.rescale_factor = rescale_factor
96
+ self.do_normalize = do_normalize
97
+ self.image_mean = image_mean
98
+ self.image_std = image_std
99
+ self.do_convert_rgb = do_convert_rgb
100
+ self.channel_order = channel_order
101
+
102
+ @filter_out_non_signature_kwargs()
103
+ def preprocess(
104
+ self,
105
+ images: ImageInput,
106
+ do_resize: Optional[bool] = None,
107
+ size: Optional[dict[str, int]] = None,
108
+ resample: Optional[PILImageResampling] = None,
109
+ do_rescale: Optional[bool] = None,
110
+ rescale_factor: Optional[float] = None,
111
+ do_normalize: Optional[bool] = None,
112
+ image_mean: Optional[Union[float, list[float]]] = None,
113
+ image_std: Optional[Union[float, list[float]]] = None,
114
+ return_tensors: Optional[Union[str, TensorType]] = None,
115
+ data_format: Union[str, ChannelDimension] = ChannelDimension.FIRST,
116
+ input_data_format: Optional[Union[str, ChannelDimension]] = None,
117
+ do_convert_rgb: Optional[bool] = None,
118
+ channel_order: Optional[str] = None,
119
+ ):
120
+ do_resize = do_resize if do_resize is not None else self.do_resize
121
+ size = size if size is not None else self.size
122
+ size = get_size_dict(size, default_to_square=True)
123
+ resample = resample if resample is not None else self.resample
124
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
125
+ rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor
126
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
127
+ image_mean = image_mean if image_mean is not None else self.image_mean
128
+ image_std = image_std if image_std is not None else self.image_std
129
+ do_convert_rgb = do_convert_rgb if do_convert_rgb is not None else self.do_convert_rgb
130
+ channel_order = channel_order if channel_order is not None else self.channel_order
131
+
132
+ if do_normalize and (image_mean is None or image_std is None):
133
+ raise ValueError("Normalization requires `image_mean` and `image_std` with one value per channel.")
134
+
135
+ images = make_flat_list_of_images(images)
136
+ if not valid_images(images):
137
+ raise ValueError("Invalid image type. Must be PIL, numpy, or torch tensor.")
138
+
139
+ validate_preprocess_arguments(
140
+ do_rescale=do_rescale,
141
+ rescale_factor=rescale_factor,
142
+ do_normalize=do_normalize,
143
+ image_mean=image_mean,
144
+ image_std=image_std,
145
+ do_resize=do_resize,
146
+ size=size,
147
+ resample=resample,
148
+ )
149
+
150
+ processed_images = []
151
+ for image in images:
152
+ image = to_numpy_array(image)
153
+ if do_convert_rgb:
154
+ image = self._convert_image_to_rgb(image)
155
+
156
+ if input_data_format is None:
157
+ try:
158
+ input_data_format = infer_channel_dimension_format(image)
159
+ except ValueError:
160
+ input_data_format = ChannelDimension.LAST
161
+
162
+ image = _reorder_channels(image, channel_order=channel_order, input_data_format=input_data_format)
163
+
164
+ if do_resize:
165
+ num_channels = image.shape[0] if input_data_format == ChannelDimension.FIRST else image.shape[-1]
166
+ if num_channels > 4:
167
+ image = _resize_multispectral(image, size=size, input_data_format=input_data_format)
168
+ else:
169
+ image = resize(
170
+ image,
171
+ size=(size["height"], size["width"]),
172
+ resample=resample,
173
+ input_data_format=input_data_format,
174
+ )
175
+
176
+ if do_rescale:
177
+ image = image * rescale_factor
178
+
179
+ if do_normalize:
180
+ image = self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)
181
+
182
+ image = to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)
183
+ processed_images.append(image)
184
+
185
+ data = {"pixel_values": processed_images}
186
+ return BatchFeature(data=data, tensor_type=return_tensors)
187
+
188
+
189
+ __all__ = ["MMEarthImageProcessor"]
mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:527f40016c44e6183103af0ee0e0668cab617ccafe29868e1ea78570871f0905
3
+ size 13572696
mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/modeling_mmearth.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ """Self-contained MMEarth model and config for trust_remote_code loading."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Optional
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from timm.models.layers import DropPath, trunc_normal_
11
+ from torch import nn
12
+
13
+ from transformers.configuration_utils import PretrainedConfig as PreTrainedConfig
14
+ from transformers.modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
15
+ from transformers.modeling_utils import PreTrainedModel
16
+ from transformers.processing_utils import Unpack
17
+ from transformers.utils import TransformersKwargs, logging
18
+
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+ MODEL_SIZE_PRESETS = {
23
+ "atto": {"depths": [2, 2, 6, 2], "hidden_sizes": [40, 80, 160, 320]},
24
+ "femto": {"depths": [2, 2, 6, 2], "hidden_sizes": [48, 96, 192, 384]},
25
+ "pico": {"depths": [2, 2, 6, 2], "hidden_sizes": [64, 128, 256, 512]},
26
+ "nano": {"depths": [2, 2, 8, 2], "hidden_sizes": [80, 160, 320, 640]},
27
+ "tiny": {"depths": [3, 3, 9, 3], "hidden_sizes": [96, 192, 384, 768]},
28
+ "base": {"depths": [3, 3, 27, 3], "hidden_sizes": [128, 256, 512, 1024]},
29
+ "large": {"depths": [3, 3, 27, 3], "hidden_sizes": [192, 384, 768, 1536]},
30
+ "huge": {"depths": [3, 3, 27, 3], "hidden_sizes": [352, 704, 1408, 2816]},
31
+ }
32
+
33
+
34
+ class MMEarthConfig(PreTrainedConfig):
35
+ model_type = "mmearth"
36
+
37
+ def __init__(
38
+ self,
39
+ depths: list[int] | None = None,
40
+ hidden_sizes: list[int] | None = None,
41
+ num_channels: int = 12,
42
+ image_size: int = 112,
43
+ patch_size: int = 16,
44
+ drop_path_rate: float = 0.0,
45
+ layer_norm_eps: float = 1e-6,
46
+ hidden_act: str = "gelu",
47
+ use_orig_stem: bool = False,
48
+ model_size: str = "atto",
49
+ input_modality: str = "all_mod",
50
+ channel_order: str = "rgb",
51
+ dataset: str = "1M_128",
52
+ loss_aggr: str = "uncertainty",
53
+ checkpoint_stage: str = "pretrain",
54
+ image_mean: list[float] | None = None,
55
+ image_std: list[float] | None = None,
56
+ do_rescale: bool = False,
57
+ rescale_factor: float = 1.0,
58
+ num_labels: int = 0,
59
+ **kwargs,
60
+ ):
61
+ super().__init__(**kwargs)
62
+ preset = MODEL_SIZE_PRESETS[model_size]
63
+ self.model_size = model_size
64
+ self.input_modality = input_modality
65
+ self.channel_order = channel_order
66
+ self.dataset = dataset
67
+ self.loss_aggr = loss_aggr
68
+ self.checkpoint_stage = checkpoint_stage
69
+ self.num_channels = num_channels
70
+ self.image_size = image_size
71
+ self.patch_size = patch_size
72
+ self.drop_path_rate = drop_path_rate
73
+ self.layer_norm_eps = layer_norm_eps
74
+ self.hidden_act = hidden_act
75
+ self.use_orig_stem = use_orig_stem
76
+ self.num_labels = num_labels
77
+ self.do_rescale = do_rescale
78
+ self.rescale_factor = rescale_factor
79
+ self.depths = depths if depths is not None else preset["depths"]
80
+ self.hidden_sizes = hidden_sizes if hidden_sizes is not None else preset["hidden_sizes"]
81
+ self.hidden_size = self.hidden_sizes[-1]
82
+ self.num_stages = len(self.depths)
83
+ self.image_mean = image_mean
84
+ self.image_std = image_std
85
+
86
+
87
+ class MMEarthLayerNorm(nn.Module):
88
+ def __init__(self, normalized_shape: int, eps: float = 1e-6, data_format: str = "channels_last"):
89
+ super().__init__()
90
+ self.weight = nn.Parameter(torch.ones(normalized_shape))
91
+ self.bias = nn.Parameter(torch.zeros(normalized_shape))
92
+ self.eps = eps
93
+ self.data_format = data_format
94
+ self.normalized_shape = (normalized_shape,)
95
+
96
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
97
+ if self.data_format == "channels_last":
98
+ return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
99
+ u = x.mean(1, keepdim=True)
100
+ s = (x - u).pow(2).mean(1, keepdim=True)
101
+ x = (x - u) / torch.sqrt(s + self.eps)
102
+ return self.weight[:, None, None] * x + self.bias[:, None, None]
103
+
104
+
105
+ class MMEarthGRN(nn.Module):
106
+ def __init__(self, dim: int):
107
+ super().__init__()
108
+ self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
109
+ self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
110
+
111
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
112
+ gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
113
+ nx = gx / (gx.mean(dim=-1, keepdim=True) + 1e-4)
114
+ return self.gamma * (x * nx) + self.beta + x
115
+
116
+
117
+ class MMEarthConvNeXtBlock(nn.Module):
118
+ def __init__(self, dim: int, drop_path: float = 0.0):
119
+ super().__init__()
120
+ self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
121
+ self.norm = MMEarthLayerNorm(dim, eps=1e-6)
122
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
123
+ self.act = nn.GELU()
124
+ self.grn = MMEarthGRN(4 * dim)
125
+ self.pwconv2 = nn.Linear(4 * dim, dim)
126
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
127
+
128
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
129
+ input_tensor = x
130
+ x = self.dwconv(x)
131
+ x = x.permute(0, 2, 3, 1)
132
+ x = self.norm(x)
133
+ x = self.pwconv1(x)
134
+ x = self.act(x)
135
+ x = self.grn(x)
136
+ x = self.pwconv2(x)
137
+ x = x.permute(0, 3, 1, 2)
138
+ return input_tensor + self.drop_path(x)
139
+
140
+
141
+ class MMEarthPreTrainedModel(PreTrainedModel):
142
+ config_class = MMEarthConfig
143
+ config: MMEarthConfig
144
+ base_model_prefix = "mmearth"
145
+ main_input_name = "pixel_values"
146
+ input_modalities = ("image",)
147
+ supports_gradient_checkpointing = True
148
+ _no_split_modules = ["MMEarthConvNeXtBlock"]
149
+
150
+ def _init_weights(self, module):
151
+ super()._init_weights(module)
152
+ if isinstance(module, (nn.Conv2d, nn.Linear)):
153
+ trunc_normal_(module.weight, std=0.02)
154
+ if module.bias is not None:
155
+ nn.init.constant_(module.bias, 0)
156
+
157
+
158
+ class MMEarthModel(MMEarthPreTrainedModel):
159
+ def __init__(self, config: MMEarthConfig, add_pooling_layer: bool = True):
160
+ super().__init__(config)
161
+ self.config = config
162
+ self.add_pooling_layer = add_pooling_layer
163
+ depths = config.depths
164
+ dims = config.hidden_sizes
165
+ patch_size = config.patch_size
166
+ num_stages = len(depths)
167
+ self.downsample_layers = nn.ModuleList()
168
+ if config.use_orig_stem:
169
+ self.stem_orig = nn.Sequential(
170
+ nn.Conv2d(
171
+ config.num_channels,
172
+ dims[0],
173
+ kernel_size=patch_size // (2 ** (num_stages - 1)),
174
+ stride=patch_size // (2 ** (num_stages - 1)),
175
+ ),
176
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
177
+ )
178
+ self.initial_conv = None
179
+ self.stem = None
180
+ else:
181
+ self.stem_orig = None
182
+ self.initial_conv = nn.Sequential(
183
+ nn.Conv2d(config.num_channels, dims[0], kernel_size=3, stride=1),
184
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
185
+ nn.GELU(),
186
+ )
187
+ stem_kernel = patch_size // (2 ** (num_stages - 1))
188
+ self.stem = nn.Sequential(
189
+ nn.Conv2d(
190
+ dims[0],
191
+ dims[0],
192
+ kernel_size=stem_kernel,
193
+ stride=stem_kernel,
194
+ padding=stem_kernel // 2,
195
+ groups=dims[0],
196
+ ),
197
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
198
+ )
199
+ for i in range(3):
200
+ self.downsample_layers.append(
201
+ nn.Sequential(
202
+ MMEarthLayerNorm(dims[i], eps=config.layer_norm_eps, data_format="channels_first"),
203
+ nn.Conv2d(dims[i], dims[i + 1], kernel_size=2, stride=2),
204
+ )
205
+ )
206
+ dp_rates = [
207
+ x.item()
208
+ for x in torch.linspace(0, config.drop_path_rate, sum(depths), device=torch.device("cpu"))
209
+ ]
210
+ cur = 0
211
+ self.stages = nn.ModuleList()
212
+ for i in range(num_stages):
213
+ stage = nn.Sequential(
214
+ *[MMEarthConvNeXtBlock(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]
215
+ )
216
+ self.stages.append(stage)
217
+ cur += depths[i]
218
+ self.norm = nn.LayerNorm(dims[-1], eps=config.layer_norm_eps)
219
+ self.post_init()
220
+
221
+ def _forward_stem(self, x: torch.Tensor) -> torch.Tensor:
222
+ if self.config.use_orig_stem:
223
+ return self.stem_orig(x)
224
+ x = self.initial_conv(x)
225
+ return self.stem(x)
226
+
227
+ def forward_features(self, pixel_values: torch.Tensor) -> torch.Tensor:
228
+ x = self._forward_stem(pixel_values)
229
+ x = self.stages[0](x)
230
+ for i in range(3):
231
+ x = self.downsample_layers[i](x)
232
+ x = self.stages[i + 1](x)
233
+ return x
234
+
235
+ def forward(
236
+ self,
237
+ pixel_values: Optional[torch.Tensor] = None,
238
+ return_dict: Optional[bool] = None,
239
+ **kwargs: Unpack[TransformersKwargs],
240
+ ) -> BaseModelOutputWithPooling:
241
+ if pixel_values is None:
242
+ raise ValueError("You must specify `pixel_values`")
243
+ pixel_values = pixel_values.to(dtype=self.dtype)
244
+ if return_dict is None:
245
+ return_dict = self.config.use_return_dict
246
+ spatial_features = self.forward_features(pixel_values)
247
+ last_hidden_state = spatial_features.flatten(2).transpose(1, 2)
248
+ pooled_output = self.norm(spatial_features.mean([-2, -1])) if self.add_pooling_layer else None
249
+ if not return_dict:
250
+ return (last_hidden_state, pooled_output)
251
+ return BaseModelOutputWithPooling(last_hidden_state=last_hidden_state, pooler_output=pooled_output)
252
+
253
+
254
+ class MMEarthForImageClassification(MMEarthPreTrainedModel):
255
+ def __init__(self, config: MMEarthConfig):
256
+ super().__init__(config)
257
+ self.num_labels = config.num_labels
258
+ self.mmearth = MMEarthModel(config, add_pooling_layer=True)
259
+ self.classifier = (
260
+ nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
261
+ )
262
+ self.post_init()
263
+
264
+ def forward(
265
+ self,
266
+ pixel_values: Optional[torch.Tensor] = None,
267
+ labels: Optional[torch.Tensor] = None,
268
+ return_dict: Optional[bool] = None,
269
+ **kwargs: Unpack[TransformersKwargs],
270
+ ) -> ImageClassifierOutput:
271
+ outputs = self.mmearth(pixel_values=pixel_values, return_dict=True, **kwargs)
272
+ logits = self.classifier(outputs.pooler_output)
273
+ loss = None
274
+ if labels is not None:
275
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
276
+ if not return_dict:
277
+ output = (logits,) + outputs[1:]
278
+ return ((loss,) + output) if loss is not None else output
279
+ return ImageClassifierOutput(
280
+ loss=loss,
281
+ logits=logits,
282
+ hidden_states=outputs.hidden_states,
283
+ attentions=outputs.attentions,
284
+ )
285
+
286
+
287
+ __all__ = [
288
+ "MMEarthConfig",
289
+ "MMEarthForImageClassification",
290
+ "MMEarthModel",
291
+ "MMEarthPreTrainedModel",
292
+ ]
mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/pipeline_mmearth.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """MMEarth image feature extraction pipeline."""
6
+
7
+ from typing import Any, Union
8
+
9
+ from transformers.pipelines.base import GenericTensor, build_pipeline_init_args
10
+ from transformers.pipelines.image_feature_extraction import ImageFeatureExtractionPipeline
11
+ from transformers.utils import add_end_docstrings, is_vision_available
12
+
13
+
14
+ if is_vision_available():
15
+ from transformers.image_utils import load_image
16
+
17
+
18
+ @add_end_docstrings(
19
+ build_pipeline_init_args(has_image_processor=True),
20
+ """
21
+ pool (`bool`, *optional*, defaults to `False`):
22
+ Whether or not to return the pooled output. If `False`, the model will return the raw hidden states.
23
+ """,
24
+ )
25
+ class MMEarthImageFeatureExtractionPipeline(ImageFeatureExtractionPipeline):
26
+ """
27
+ MMEarth image feature extraction pipeline.
28
+
29
+ This pipeline wraps [`MMEarthModel`] for Sentinel-2 multispectral and RGB/BGR geospatial feature extraction.
30
+ It extends [`ImageFeatureExtractionPipeline`] with support for numpy arrays and file paths in addition to
31
+ standard image inputs.
32
+ """
33
+
34
+ def _sanitize_parameters(
35
+ self,
36
+ image_processor_kwargs=None,
37
+ return_tensors=None,
38
+ pool=None,
39
+ **kwargs,
40
+ ):
41
+ preprocess_params = {} if image_processor_kwargs is None else dict(image_processor_kwargs)
42
+ if "timeout" in kwargs:
43
+ preprocess_params["timeout"] = kwargs["timeout"]
44
+
45
+ postprocess_params = {}
46
+ if pool is not None:
47
+ postprocess_params["pool"] = pool
48
+ if return_tensors is not None:
49
+ postprocess_params["return_tensors"] = return_tensors
50
+
51
+ return preprocess_params, {}, postprocess_params
52
+
53
+ def preprocess(self, image, timeout=None, **image_processor_kwargs) -> dict[str, GenericTensor]:
54
+ if not isinstance(image, (list, tuple)) and not hasattr(image, "shape"):
55
+ image = load_image(image, timeout=timeout)
56
+ model_inputs = self.image_processor(image, return_tensors="pt", **image_processor_kwargs)
57
+ model_inputs = model_inputs.to(self.dtype)
58
+ return model_inputs
59
+
60
+ def __call__(
61
+ self,
62
+ *args: Union[str, Any, list[Any]],
63
+ **kwargs: Any,
64
+ ) -> list[Any]:
65
+ return super().__call__(*args, **kwargs)
66
+
67
+
68
+ __all__ = ["MMEarthImageFeatureExtractionPipeline"]
mmearth-convnextv2-atto-pix-mod-1m-64-uncertainty-56x8/preprocessor_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "MMEarthImageProcessor",
3
+ "size": {
4
+ "height": 56,
5
+ "width": 56
6
+ },
7
+ "do_resize": false,
8
+ "do_rescale": false,
9
+ "rescale_factor": 1.0,
10
+ "do_normalize": false,
11
+ "do_convert_rgb": false,
12
+ "channel_order": "rgb",
13
+ "image_mean": null,
14
+ "image_std": null,
15
+ "auto_map": {
16
+ "AutoImageProcessor": "image_processing_mmearth.MMEarthImageProcessor"
17
+ }
18
+ }
mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/config.json ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MMEarthModel"
4
+ ],
5
+ "band_names": [
6
+ "B4",
7
+ "B3",
8
+ "B2"
9
+ ],
10
+ "channel_order": "bgr",
11
+ "checkpoint_stage": "pretrain",
12
+ "dataset": "1M_128",
13
+ "depths": [
14
+ 2,
15
+ 2,
16
+ 6,
17
+ 2
18
+ ],
19
+ "do_rescale": false,
20
+ "drop_path_rate": 0.0,
21
+ "dtype": "float32",
22
+ "hidden_act": "gelu",
23
+ "hidden_size": 320,
24
+ "hidden_sizes": [
25
+ 40,
26
+ 80,
27
+ 160,
28
+ 320
29
+ ],
30
+ "id2label": {},
31
+ "image_mean": null,
32
+ "image_size": 112,
33
+ "image_std": null,
34
+ "input_modality": "rgb",
35
+ "label2id": {},
36
+ "layer_norm_eps": 1e-06,
37
+ "loss_aggr": "uncertainty",
38
+ "model_size": "atto",
39
+ "model_type": "mmearth",
40
+ "num_channels": 3,
41
+ "num_stages": 4,
42
+ "patch_size": 16,
43
+ "rescale_factor": 1.0,
44
+ "transformers_version": "5.0.0",
45
+ "use_orig_stem": false,
46
+ "auto_map": {
47
+ "AutoConfig": "modeling_mmearth.MMEarthConfig",
48
+ "AutoModel": "modeling_mmearth.MMEarthModel",
49
+ "AutoModelForImageClassification": "modeling_mmearth.MMEarthForImageClassification"
50
+ },
51
+ "custom_pipelines": {
52
+ "mmearth-feature-extraction": {
53
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
54
+ "pt": [
55
+ "AutoModel"
56
+ ]
57
+ }
58
+ },
59
+ "legacy_checkpoint": "checkpoint-199.pth",
60
+ "legacy_source": "pt-rgb_atto_1M_128_uncertainty_112-16"
61
+ }
mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/image_processing_mmearth.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """Image processor for MMEarth models."""
6
+
7
+ from typing import Optional, Union
8
+
9
+ import numpy as np
10
+
11
+ from transformers.image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict
12
+ from transformers.image_transforms import resize, to_channel_dimension_format
13
+ from transformers.image_utils import (
14
+ ChannelDimension,
15
+ ImageInput,
16
+ PILImageResampling,
17
+ infer_channel_dimension_format,
18
+ make_flat_list_of_images,
19
+ to_numpy_array,
20
+ valid_images,
21
+ validate_preprocess_arguments,
22
+ )
23
+ from transformers.utils import TensorType, filter_out_non_signature_kwargs, logging
24
+
25
+
26
+ logger = logging.get_logger(__name__)
27
+
28
+
29
+ def _resize_multispectral(image: np.ndarray, size: dict[str, int], input_data_format: ChannelDimension) -> np.ndarray:
30
+ target_height, target_width = size["height"], size["width"]
31
+
32
+ if input_data_format == ChannelDimension.FIRST:
33
+ image = np.transpose(image, (1, 2, 0))
34
+
35
+ height, width, _ = image.shape
36
+ if height == target_height and width == target_width:
37
+ resized = image
38
+ else:
39
+ try:
40
+ import cv2
41
+ except ImportError as exc:
42
+ raise ImportError(
43
+ "Multispectral resize requires OpenCV (`opencv-python`) when input has more than 4 channels."
44
+ ) from exc
45
+ resized = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
46
+
47
+ if input_data_format == ChannelDimension.FIRST:
48
+ return np.transpose(resized, (2, 0, 1))
49
+ return resized
50
+
51
+
52
+ def _reorder_channels(image: np.ndarray, channel_order: str, input_data_format: ChannelDimension) -> np.ndarray:
53
+ if channel_order != "bgr":
54
+ return image
55
+
56
+ if input_data_format == ChannelDimension.FIRST:
57
+ if image.shape[0] < 3:
58
+ return image
59
+ return image[[2, 1, 0], ...]
60
+ if image.shape[-1] < 3:
61
+ return image
62
+ return image[..., [2, 1, 0]]
63
+
64
+
65
+ class MMEarthImageProcessor(BaseImageProcessor):
66
+ """
67
+ Image processor for MMEarth ConvNeXt V2 encoders.
68
+
69
+ RGB checkpoints were trained with BGR channel order. Set `channel_order="bgr"` (default for RGB models) to swap
70
+ the first three channels from RGB to BGR before inference.
71
+ """
72
+
73
+ model_input_names = ["pixel_values"]
74
+
75
+ def __init__(
76
+ self,
77
+ do_resize: bool = True,
78
+ size: Optional[dict[str, int]] = None,
79
+ resample: PILImageResampling = PILImageResampling.BILINEAR,
80
+ do_rescale: bool = False,
81
+ rescale_factor: float = 1.0,
82
+ do_normalize: bool = True,
83
+ image_mean: Optional[Union[float, list[float]]] = None,
84
+ image_std: Optional[Union[float, list[float]]] = None,
85
+ do_convert_rgb: bool = False,
86
+ channel_order: str = "rgb",
87
+ **kwargs,
88
+ ):
89
+ super().__init__(**kwargs)
90
+ size = size if size is not None else {"height": 112, "width": 112}
91
+ self.do_resize = do_resize
92
+ self.size = size
93
+ self.resample = resample
94
+ self.do_rescale = do_rescale
95
+ self.rescale_factor = rescale_factor
96
+ self.do_normalize = do_normalize
97
+ self.image_mean = image_mean
98
+ self.image_std = image_std
99
+ self.do_convert_rgb = do_convert_rgb
100
+ self.channel_order = channel_order
101
+
102
+ @filter_out_non_signature_kwargs()
103
+ def preprocess(
104
+ self,
105
+ images: ImageInput,
106
+ do_resize: Optional[bool] = None,
107
+ size: Optional[dict[str, int]] = None,
108
+ resample: Optional[PILImageResampling] = None,
109
+ do_rescale: Optional[bool] = None,
110
+ rescale_factor: Optional[float] = None,
111
+ do_normalize: Optional[bool] = None,
112
+ image_mean: Optional[Union[float, list[float]]] = None,
113
+ image_std: Optional[Union[float, list[float]]] = None,
114
+ return_tensors: Optional[Union[str, TensorType]] = None,
115
+ data_format: Union[str, ChannelDimension] = ChannelDimension.FIRST,
116
+ input_data_format: Optional[Union[str, ChannelDimension]] = None,
117
+ do_convert_rgb: Optional[bool] = None,
118
+ channel_order: Optional[str] = None,
119
+ ):
120
+ do_resize = do_resize if do_resize is not None else self.do_resize
121
+ size = size if size is not None else self.size
122
+ size = get_size_dict(size, default_to_square=True)
123
+ resample = resample if resample is not None else self.resample
124
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
125
+ rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor
126
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
127
+ image_mean = image_mean if image_mean is not None else self.image_mean
128
+ image_std = image_std if image_std is not None else self.image_std
129
+ do_convert_rgb = do_convert_rgb if do_convert_rgb is not None else self.do_convert_rgb
130
+ channel_order = channel_order if channel_order is not None else self.channel_order
131
+
132
+ if do_normalize and (image_mean is None or image_std is None):
133
+ raise ValueError("Normalization requires `image_mean` and `image_std` with one value per channel.")
134
+
135
+ images = make_flat_list_of_images(images)
136
+ if not valid_images(images):
137
+ raise ValueError("Invalid image type. Must be PIL, numpy, or torch tensor.")
138
+
139
+ validate_preprocess_arguments(
140
+ do_rescale=do_rescale,
141
+ rescale_factor=rescale_factor,
142
+ do_normalize=do_normalize,
143
+ image_mean=image_mean,
144
+ image_std=image_std,
145
+ do_resize=do_resize,
146
+ size=size,
147
+ resample=resample,
148
+ )
149
+
150
+ processed_images = []
151
+ for image in images:
152
+ image = to_numpy_array(image)
153
+ if do_convert_rgb:
154
+ image = self._convert_image_to_rgb(image)
155
+
156
+ if input_data_format is None:
157
+ try:
158
+ input_data_format = infer_channel_dimension_format(image)
159
+ except ValueError:
160
+ input_data_format = ChannelDimension.LAST
161
+
162
+ image = _reorder_channels(image, channel_order=channel_order, input_data_format=input_data_format)
163
+
164
+ if do_resize:
165
+ num_channels = image.shape[0] if input_data_format == ChannelDimension.FIRST else image.shape[-1]
166
+ if num_channels > 4:
167
+ image = _resize_multispectral(image, size=size, input_data_format=input_data_format)
168
+ else:
169
+ image = resize(
170
+ image,
171
+ size=(size["height"], size["width"]),
172
+ resample=resample,
173
+ input_data_format=input_data_format,
174
+ )
175
+
176
+ if do_rescale:
177
+ image = image * rescale_factor
178
+
179
+ if do_normalize:
180
+ image = self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)
181
+
182
+ image = to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)
183
+ processed_images.append(image)
184
+
185
+ data = {"pixel_values": processed_images}
186
+ return BatchFeature(data=data, tensor_type=return_tensors)
187
+
188
+
189
+ __all__ = ["MMEarthImageProcessor"]
mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0eb3fccae67ad295325778b6cf730834438b2d24ee0f71226aa7d4da9c8351aa
3
+ size 13560216
mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/modeling_mmearth.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ """Self-contained MMEarth model and config for trust_remote_code loading."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Optional
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from timm.models.layers import DropPath, trunc_normal_
11
+ from torch import nn
12
+
13
+ from transformers.configuration_utils import PretrainedConfig as PreTrainedConfig
14
+ from transformers.modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
15
+ from transformers.modeling_utils import PreTrainedModel
16
+ from transformers.processing_utils import Unpack
17
+ from transformers.utils import TransformersKwargs, logging
18
+
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+ MODEL_SIZE_PRESETS = {
23
+ "atto": {"depths": [2, 2, 6, 2], "hidden_sizes": [40, 80, 160, 320]},
24
+ "femto": {"depths": [2, 2, 6, 2], "hidden_sizes": [48, 96, 192, 384]},
25
+ "pico": {"depths": [2, 2, 6, 2], "hidden_sizes": [64, 128, 256, 512]},
26
+ "nano": {"depths": [2, 2, 8, 2], "hidden_sizes": [80, 160, 320, 640]},
27
+ "tiny": {"depths": [3, 3, 9, 3], "hidden_sizes": [96, 192, 384, 768]},
28
+ "base": {"depths": [3, 3, 27, 3], "hidden_sizes": [128, 256, 512, 1024]},
29
+ "large": {"depths": [3, 3, 27, 3], "hidden_sizes": [192, 384, 768, 1536]},
30
+ "huge": {"depths": [3, 3, 27, 3], "hidden_sizes": [352, 704, 1408, 2816]},
31
+ }
32
+
33
+
34
+ class MMEarthConfig(PreTrainedConfig):
35
+ model_type = "mmearth"
36
+
37
+ def __init__(
38
+ self,
39
+ depths: list[int] | None = None,
40
+ hidden_sizes: list[int] | None = None,
41
+ num_channels: int = 12,
42
+ image_size: int = 112,
43
+ patch_size: int = 16,
44
+ drop_path_rate: float = 0.0,
45
+ layer_norm_eps: float = 1e-6,
46
+ hidden_act: str = "gelu",
47
+ use_orig_stem: bool = False,
48
+ model_size: str = "atto",
49
+ input_modality: str = "all_mod",
50
+ channel_order: str = "rgb",
51
+ dataset: str = "1M_128",
52
+ loss_aggr: str = "uncertainty",
53
+ checkpoint_stage: str = "pretrain",
54
+ image_mean: list[float] | None = None,
55
+ image_std: list[float] | None = None,
56
+ do_rescale: bool = False,
57
+ rescale_factor: float = 1.0,
58
+ num_labels: int = 0,
59
+ **kwargs,
60
+ ):
61
+ super().__init__(**kwargs)
62
+ preset = MODEL_SIZE_PRESETS[model_size]
63
+ self.model_size = model_size
64
+ self.input_modality = input_modality
65
+ self.channel_order = channel_order
66
+ self.dataset = dataset
67
+ self.loss_aggr = loss_aggr
68
+ self.checkpoint_stage = checkpoint_stage
69
+ self.num_channels = num_channels
70
+ self.image_size = image_size
71
+ self.patch_size = patch_size
72
+ self.drop_path_rate = drop_path_rate
73
+ self.layer_norm_eps = layer_norm_eps
74
+ self.hidden_act = hidden_act
75
+ self.use_orig_stem = use_orig_stem
76
+ self.num_labels = num_labels
77
+ self.do_rescale = do_rescale
78
+ self.rescale_factor = rescale_factor
79
+ self.depths = depths if depths is not None else preset["depths"]
80
+ self.hidden_sizes = hidden_sizes if hidden_sizes is not None else preset["hidden_sizes"]
81
+ self.hidden_size = self.hidden_sizes[-1]
82
+ self.num_stages = len(self.depths)
83
+ self.image_mean = image_mean
84
+ self.image_std = image_std
85
+
86
+
87
+ class MMEarthLayerNorm(nn.Module):
88
+ def __init__(self, normalized_shape: int, eps: float = 1e-6, data_format: str = "channels_last"):
89
+ super().__init__()
90
+ self.weight = nn.Parameter(torch.ones(normalized_shape))
91
+ self.bias = nn.Parameter(torch.zeros(normalized_shape))
92
+ self.eps = eps
93
+ self.data_format = data_format
94
+ self.normalized_shape = (normalized_shape,)
95
+
96
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
97
+ if self.data_format == "channels_last":
98
+ return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
99
+ u = x.mean(1, keepdim=True)
100
+ s = (x - u).pow(2).mean(1, keepdim=True)
101
+ x = (x - u) / torch.sqrt(s + self.eps)
102
+ return self.weight[:, None, None] * x + self.bias[:, None, None]
103
+
104
+
105
+ class MMEarthGRN(nn.Module):
106
+ def __init__(self, dim: int):
107
+ super().__init__()
108
+ self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
109
+ self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
110
+
111
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
112
+ gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
113
+ nx = gx / (gx.mean(dim=-1, keepdim=True) + 1e-4)
114
+ return self.gamma * (x * nx) + self.beta + x
115
+
116
+
117
+ class MMEarthConvNeXtBlock(nn.Module):
118
+ def __init__(self, dim: int, drop_path: float = 0.0):
119
+ super().__init__()
120
+ self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
121
+ self.norm = MMEarthLayerNorm(dim, eps=1e-6)
122
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
123
+ self.act = nn.GELU()
124
+ self.grn = MMEarthGRN(4 * dim)
125
+ self.pwconv2 = nn.Linear(4 * dim, dim)
126
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
127
+
128
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
129
+ input_tensor = x
130
+ x = self.dwconv(x)
131
+ x = x.permute(0, 2, 3, 1)
132
+ x = self.norm(x)
133
+ x = self.pwconv1(x)
134
+ x = self.act(x)
135
+ x = self.grn(x)
136
+ x = self.pwconv2(x)
137
+ x = x.permute(0, 3, 1, 2)
138
+ return input_tensor + self.drop_path(x)
139
+
140
+
141
+ class MMEarthPreTrainedModel(PreTrainedModel):
142
+ config_class = MMEarthConfig
143
+ config: MMEarthConfig
144
+ base_model_prefix = "mmearth"
145
+ main_input_name = "pixel_values"
146
+ input_modalities = ("image",)
147
+ supports_gradient_checkpointing = True
148
+ _no_split_modules = ["MMEarthConvNeXtBlock"]
149
+
150
+ def _init_weights(self, module):
151
+ super()._init_weights(module)
152
+ if isinstance(module, (nn.Conv2d, nn.Linear)):
153
+ trunc_normal_(module.weight, std=0.02)
154
+ if module.bias is not None:
155
+ nn.init.constant_(module.bias, 0)
156
+
157
+
158
+ class MMEarthModel(MMEarthPreTrainedModel):
159
+ def __init__(self, config: MMEarthConfig, add_pooling_layer: bool = True):
160
+ super().__init__(config)
161
+ self.config = config
162
+ self.add_pooling_layer = add_pooling_layer
163
+ depths = config.depths
164
+ dims = config.hidden_sizes
165
+ patch_size = config.patch_size
166
+ num_stages = len(depths)
167
+ self.downsample_layers = nn.ModuleList()
168
+ if config.use_orig_stem:
169
+ self.stem_orig = nn.Sequential(
170
+ nn.Conv2d(
171
+ config.num_channels,
172
+ dims[0],
173
+ kernel_size=patch_size // (2 ** (num_stages - 1)),
174
+ stride=patch_size // (2 ** (num_stages - 1)),
175
+ ),
176
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
177
+ )
178
+ self.initial_conv = None
179
+ self.stem = None
180
+ else:
181
+ self.stem_orig = None
182
+ self.initial_conv = nn.Sequential(
183
+ nn.Conv2d(config.num_channels, dims[0], kernel_size=3, stride=1),
184
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
185
+ nn.GELU(),
186
+ )
187
+ stem_kernel = patch_size // (2 ** (num_stages - 1))
188
+ self.stem = nn.Sequential(
189
+ nn.Conv2d(
190
+ dims[0],
191
+ dims[0],
192
+ kernel_size=stem_kernel,
193
+ stride=stem_kernel,
194
+ padding=stem_kernel // 2,
195
+ groups=dims[0],
196
+ ),
197
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
198
+ )
199
+ for i in range(3):
200
+ self.downsample_layers.append(
201
+ nn.Sequential(
202
+ MMEarthLayerNorm(dims[i], eps=config.layer_norm_eps, data_format="channels_first"),
203
+ nn.Conv2d(dims[i], dims[i + 1], kernel_size=2, stride=2),
204
+ )
205
+ )
206
+ dp_rates = [
207
+ x.item()
208
+ for x in torch.linspace(0, config.drop_path_rate, sum(depths), device=torch.device("cpu"))
209
+ ]
210
+ cur = 0
211
+ self.stages = nn.ModuleList()
212
+ for i in range(num_stages):
213
+ stage = nn.Sequential(
214
+ *[MMEarthConvNeXtBlock(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]
215
+ )
216
+ self.stages.append(stage)
217
+ cur += depths[i]
218
+ self.norm = nn.LayerNorm(dims[-1], eps=config.layer_norm_eps)
219
+ self.post_init()
220
+
221
+ def _forward_stem(self, x: torch.Tensor) -> torch.Tensor:
222
+ if self.config.use_orig_stem:
223
+ return self.stem_orig(x)
224
+ x = self.initial_conv(x)
225
+ return self.stem(x)
226
+
227
+ def forward_features(self, pixel_values: torch.Tensor) -> torch.Tensor:
228
+ x = self._forward_stem(pixel_values)
229
+ x = self.stages[0](x)
230
+ for i in range(3):
231
+ x = self.downsample_layers[i](x)
232
+ x = self.stages[i + 1](x)
233
+ return x
234
+
235
+ def forward(
236
+ self,
237
+ pixel_values: Optional[torch.Tensor] = None,
238
+ return_dict: Optional[bool] = None,
239
+ **kwargs: Unpack[TransformersKwargs],
240
+ ) -> BaseModelOutputWithPooling:
241
+ if pixel_values is None:
242
+ raise ValueError("You must specify `pixel_values`")
243
+ pixel_values = pixel_values.to(dtype=self.dtype)
244
+ if return_dict is None:
245
+ return_dict = self.config.use_return_dict
246
+ spatial_features = self.forward_features(pixel_values)
247
+ last_hidden_state = spatial_features.flatten(2).transpose(1, 2)
248
+ pooled_output = self.norm(spatial_features.mean([-2, -1])) if self.add_pooling_layer else None
249
+ if not return_dict:
250
+ return (last_hidden_state, pooled_output)
251
+ return BaseModelOutputWithPooling(last_hidden_state=last_hidden_state, pooler_output=pooled_output)
252
+
253
+
254
+ class MMEarthForImageClassification(MMEarthPreTrainedModel):
255
+ def __init__(self, config: MMEarthConfig):
256
+ super().__init__(config)
257
+ self.num_labels = config.num_labels
258
+ self.mmearth = MMEarthModel(config, add_pooling_layer=True)
259
+ self.classifier = (
260
+ nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
261
+ )
262
+ self.post_init()
263
+
264
+ def forward(
265
+ self,
266
+ pixel_values: Optional[torch.Tensor] = None,
267
+ labels: Optional[torch.Tensor] = None,
268
+ return_dict: Optional[bool] = None,
269
+ **kwargs: Unpack[TransformersKwargs],
270
+ ) -> ImageClassifierOutput:
271
+ outputs = self.mmearth(pixel_values=pixel_values, return_dict=True, **kwargs)
272
+ logits = self.classifier(outputs.pooler_output)
273
+ loss = None
274
+ if labels is not None:
275
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
276
+ if not return_dict:
277
+ output = (logits,) + outputs[1:]
278
+ return ((loss,) + output) if loss is not None else output
279
+ return ImageClassifierOutput(
280
+ loss=loss,
281
+ logits=logits,
282
+ hidden_states=outputs.hidden_states,
283
+ attentions=outputs.attentions,
284
+ )
285
+
286
+
287
+ __all__ = [
288
+ "MMEarthConfig",
289
+ "MMEarthForImageClassification",
290
+ "MMEarthModel",
291
+ "MMEarthPreTrainedModel",
292
+ ]
mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/pipeline_mmearth.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """MMEarth image feature extraction pipeline."""
6
+
7
+ from typing import Any, Union
8
+
9
+ from transformers.pipelines.base import GenericTensor, build_pipeline_init_args
10
+ from transformers.pipelines.image_feature_extraction import ImageFeatureExtractionPipeline
11
+ from transformers.utils import add_end_docstrings, is_vision_available
12
+
13
+
14
+ if is_vision_available():
15
+ from transformers.image_utils import load_image
16
+
17
+
18
+ @add_end_docstrings(
19
+ build_pipeline_init_args(has_image_processor=True),
20
+ """
21
+ pool (`bool`, *optional*, defaults to `False`):
22
+ Whether or not to return the pooled output. If `False`, the model will return the raw hidden states.
23
+ """,
24
+ )
25
+ class MMEarthImageFeatureExtractionPipeline(ImageFeatureExtractionPipeline):
26
+ """
27
+ MMEarth image feature extraction pipeline.
28
+
29
+ This pipeline wraps [`MMEarthModel`] for Sentinel-2 multispectral and RGB/BGR geospatial feature extraction.
30
+ It extends [`ImageFeatureExtractionPipeline`] with support for numpy arrays and file paths in addition to
31
+ standard image inputs.
32
+ """
33
+
34
+ def _sanitize_parameters(
35
+ self,
36
+ image_processor_kwargs=None,
37
+ return_tensors=None,
38
+ pool=None,
39
+ **kwargs,
40
+ ):
41
+ preprocess_params = {} if image_processor_kwargs is None else dict(image_processor_kwargs)
42
+ if "timeout" in kwargs:
43
+ preprocess_params["timeout"] = kwargs["timeout"]
44
+
45
+ postprocess_params = {}
46
+ if pool is not None:
47
+ postprocess_params["pool"] = pool
48
+ if return_tensors is not None:
49
+ postprocess_params["return_tensors"] = return_tensors
50
+
51
+ return preprocess_params, {}, postprocess_params
52
+
53
+ def preprocess(self, image, timeout=None, **image_processor_kwargs) -> dict[str, GenericTensor]:
54
+ if not isinstance(image, (list, tuple)) and not hasattr(image, "shape"):
55
+ image = load_image(image, timeout=timeout)
56
+ model_inputs = self.image_processor(image, return_tensors="pt", **image_processor_kwargs)
57
+ model_inputs = model_inputs.to(self.dtype)
58
+ return model_inputs
59
+
60
+ def __call__(
61
+ self,
62
+ *args: Union[str, Any, list[Any]],
63
+ **kwargs: Any,
64
+ ) -> list[Any]:
65
+ return super().__call__(*args, **kwargs)
66
+
67
+
68
+ __all__ = ["MMEarthImageFeatureExtractionPipeline"]
mmearth-convnextv2-atto-rgb-1m-128-uncertainty-112x16/preprocessor_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "MMEarthImageProcessor",
3
+ "size": {
4
+ "height": 112,
5
+ "width": 112
6
+ },
7
+ "do_resize": false,
8
+ "do_rescale": false,
9
+ "rescale_factor": 1.0,
10
+ "do_normalize": false,
11
+ "do_convert_rgb": false,
12
+ "channel_order": "bgr",
13
+ "image_mean": null,
14
+ "image_std": null,
15
+ "auto_map": {
16
+ "AutoImageProcessor": "image_processing_mmearth.MMEarthImageProcessor"
17
+ }
18
+ }
mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/config.json ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MMEarthModel"
4
+ ],
5
+ "band_names": [
6
+ "B4",
7
+ "B3",
8
+ "B2"
9
+ ],
10
+ "channel_order": "bgr",
11
+ "checkpoint_stage": "pretrain",
12
+ "dataset": "1M_64",
13
+ "depths": [
14
+ 2,
15
+ 2,
16
+ 6,
17
+ 2
18
+ ],
19
+ "do_rescale": false,
20
+ "drop_path_rate": 0.0,
21
+ "dtype": "float32",
22
+ "hidden_act": "gelu",
23
+ "hidden_size": 320,
24
+ "hidden_sizes": [
25
+ 40,
26
+ 80,
27
+ 160,
28
+ 320
29
+ ],
30
+ "id2label": {},
31
+ "image_mean": null,
32
+ "image_size": 56,
33
+ "image_std": null,
34
+ "input_modality": "rgb",
35
+ "label2id": {},
36
+ "layer_norm_eps": 1e-06,
37
+ "loss_aggr": "uncertainty",
38
+ "model_size": "atto",
39
+ "model_type": "mmearth",
40
+ "num_channels": 3,
41
+ "num_stages": 4,
42
+ "patch_size": 8,
43
+ "rescale_factor": 1.0,
44
+ "transformers_version": "5.0.0",
45
+ "use_orig_stem": false,
46
+ "auto_map": {
47
+ "AutoConfig": "modeling_mmearth.MMEarthConfig",
48
+ "AutoModel": "modeling_mmearth.MMEarthModel",
49
+ "AutoModelForImageClassification": "modeling_mmearth.MMEarthForImageClassification"
50
+ },
51
+ "custom_pipelines": {
52
+ "mmearth-feature-extraction": {
53
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
54
+ "pt": [
55
+ "AutoModel"
56
+ ]
57
+ }
58
+ },
59
+ "legacy_checkpoint": "checkpoint-199.pth",
60
+ "legacy_source": "pt-rgb_atto_1M_64_uncertainty_56-8"
61
+ }
mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/image_processing_mmearth.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """Image processor for MMEarth models."""
6
+
7
+ from typing import Optional, Union
8
+
9
+ import numpy as np
10
+
11
+ from transformers.image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict
12
+ from transformers.image_transforms import resize, to_channel_dimension_format
13
+ from transformers.image_utils import (
14
+ ChannelDimension,
15
+ ImageInput,
16
+ PILImageResampling,
17
+ infer_channel_dimension_format,
18
+ make_flat_list_of_images,
19
+ to_numpy_array,
20
+ valid_images,
21
+ validate_preprocess_arguments,
22
+ )
23
+ from transformers.utils import TensorType, filter_out_non_signature_kwargs, logging
24
+
25
+
26
+ logger = logging.get_logger(__name__)
27
+
28
+
29
+ def _resize_multispectral(image: np.ndarray, size: dict[str, int], input_data_format: ChannelDimension) -> np.ndarray:
30
+ target_height, target_width = size["height"], size["width"]
31
+
32
+ if input_data_format == ChannelDimension.FIRST:
33
+ image = np.transpose(image, (1, 2, 0))
34
+
35
+ height, width, _ = image.shape
36
+ if height == target_height and width == target_width:
37
+ resized = image
38
+ else:
39
+ try:
40
+ import cv2
41
+ except ImportError as exc:
42
+ raise ImportError(
43
+ "Multispectral resize requires OpenCV (`opencv-python`) when input has more than 4 channels."
44
+ ) from exc
45
+ resized = cv2.resize(image, (target_width, target_height), interpolation=cv2.INTER_LINEAR)
46
+
47
+ if input_data_format == ChannelDimension.FIRST:
48
+ return np.transpose(resized, (2, 0, 1))
49
+ return resized
50
+
51
+
52
+ def _reorder_channels(image: np.ndarray, channel_order: str, input_data_format: ChannelDimension) -> np.ndarray:
53
+ if channel_order != "bgr":
54
+ return image
55
+
56
+ if input_data_format == ChannelDimension.FIRST:
57
+ if image.shape[0] < 3:
58
+ return image
59
+ return image[[2, 1, 0], ...]
60
+ if image.shape[-1] < 3:
61
+ return image
62
+ return image[..., [2, 1, 0]]
63
+
64
+
65
+ class MMEarthImageProcessor(BaseImageProcessor):
66
+ """
67
+ Image processor for MMEarth ConvNeXt V2 encoders.
68
+
69
+ RGB checkpoints were trained with BGR channel order. Set `channel_order="bgr"` (default for RGB models) to swap
70
+ the first three channels from RGB to BGR before inference.
71
+ """
72
+
73
+ model_input_names = ["pixel_values"]
74
+
75
+ def __init__(
76
+ self,
77
+ do_resize: bool = True,
78
+ size: Optional[dict[str, int]] = None,
79
+ resample: PILImageResampling = PILImageResampling.BILINEAR,
80
+ do_rescale: bool = False,
81
+ rescale_factor: float = 1.0,
82
+ do_normalize: bool = True,
83
+ image_mean: Optional[Union[float, list[float]]] = None,
84
+ image_std: Optional[Union[float, list[float]]] = None,
85
+ do_convert_rgb: bool = False,
86
+ channel_order: str = "rgb",
87
+ **kwargs,
88
+ ):
89
+ super().__init__(**kwargs)
90
+ size = size if size is not None else {"height": 112, "width": 112}
91
+ self.do_resize = do_resize
92
+ self.size = size
93
+ self.resample = resample
94
+ self.do_rescale = do_rescale
95
+ self.rescale_factor = rescale_factor
96
+ self.do_normalize = do_normalize
97
+ self.image_mean = image_mean
98
+ self.image_std = image_std
99
+ self.do_convert_rgb = do_convert_rgb
100
+ self.channel_order = channel_order
101
+
102
+ @filter_out_non_signature_kwargs()
103
+ def preprocess(
104
+ self,
105
+ images: ImageInput,
106
+ do_resize: Optional[bool] = None,
107
+ size: Optional[dict[str, int]] = None,
108
+ resample: Optional[PILImageResampling] = None,
109
+ do_rescale: Optional[bool] = None,
110
+ rescale_factor: Optional[float] = None,
111
+ do_normalize: Optional[bool] = None,
112
+ image_mean: Optional[Union[float, list[float]]] = None,
113
+ image_std: Optional[Union[float, list[float]]] = None,
114
+ return_tensors: Optional[Union[str, TensorType]] = None,
115
+ data_format: Union[str, ChannelDimension] = ChannelDimension.FIRST,
116
+ input_data_format: Optional[Union[str, ChannelDimension]] = None,
117
+ do_convert_rgb: Optional[bool] = None,
118
+ channel_order: Optional[str] = None,
119
+ ):
120
+ do_resize = do_resize if do_resize is not None else self.do_resize
121
+ size = size if size is not None else self.size
122
+ size = get_size_dict(size, default_to_square=True)
123
+ resample = resample if resample is not None else self.resample
124
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
125
+ rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor
126
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
127
+ image_mean = image_mean if image_mean is not None else self.image_mean
128
+ image_std = image_std if image_std is not None else self.image_std
129
+ do_convert_rgb = do_convert_rgb if do_convert_rgb is not None else self.do_convert_rgb
130
+ channel_order = channel_order if channel_order is not None else self.channel_order
131
+
132
+ if do_normalize and (image_mean is None or image_std is None):
133
+ raise ValueError("Normalization requires `image_mean` and `image_std` with one value per channel.")
134
+
135
+ images = make_flat_list_of_images(images)
136
+ if not valid_images(images):
137
+ raise ValueError("Invalid image type. Must be PIL, numpy, or torch tensor.")
138
+
139
+ validate_preprocess_arguments(
140
+ do_rescale=do_rescale,
141
+ rescale_factor=rescale_factor,
142
+ do_normalize=do_normalize,
143
+ image_mean=image_mean,
144
+ image_std=image_std,
145
+ do_resize=do_resize,
146
+ size=size,
147
+ resample=resample,
148
+ )
149
+
150
+ processed_images = []
151
+ for image in images:
152
+ image = to_numpy_array(image)
153
+ if do_convert_rgb:
154
+ image = self._convert_image_to_rgb(image)
155
+
156
+ if input_data_format is None:
157
+ try:
158
+ input_data_format = infer_channel_dimension_format(image)
159
+ except ValueError:
160
+ input_data_format = ChannelDimension.LAST
161
+
162
+ image = _reorder_channels(image, channel_order=channel_order, input_data_format=input_data_format)
163
+
164
+ if do_resize:
165
+ num_channels = image.shape[0] if input_data_format == ChannelDimension.FIRST else image.shape[-1]
166
+ if num_channels > 4:
167
+ image = _resize_multispectral(image, size=size, input_data_format=input_data_format)
168
+ else:
169
+ image = resize(
170
+ image,
171
+ size=(size["height"], size["width"]),
172
+ resample=resample,
173
+ input_data_format=input_data_format,
174
+ )
175
+
176
+ if do_rescale:
177
+ image = image * rescale_factor
178
+
179
+ if do_normalize:
180
+ image = self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)
181
+
182
+ image = to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)
183
+ processed_images.append(image)
184
+
185
+ data = {"pixel_values": processed_images}
186
+ return BatchFeature(data=data, tensor_type=return_tensors)
187
+
188
+
189
+ __all__ = ["MMEarthImageProcessor"]
mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:72548211ba30d3d39b82cd43c286a1c1ec94d46ae3ac7af873b16594bf0e052f
3
+ size 13559736
mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/modeling_mmearth.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ """Self-contained MMEarth model and config for trust_remote_code loading."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Optional
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from timm.models.layers import DropPath, trunc_normal_
11
+ from torch import nn
12
+
13
+ from transformers.configuration_utils import PretrainedConfig as PreTrainedConfig
14
+ from transformers.modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
15
+ from transformers.modeling_utils import PreTrainedModel
16
+ from transformers.processing_utils import Unpack
17
+ from transformers.utils import TransformersKwargs, logging
18
+
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+ MODEL_SIZE_PRESETS = {
23
+ "atto": {"depths": [2, 2, 6, 2], "hidden_sizes": [40, 80, 160, 320]},
24
+ "femto": {"depths": [2, 2, 6, 2], "hidden_sizes": [48, 96, 192, 384]},
25
+ "pico": {"depths": [2, 2, 6, 2], "hidden_sizes": [64, 128, 256, 512]},
26
+ "nano": {"depths": [2, 2, 8, 2], "hidden_sizes": [80, 160, 320, 640]},
27
+ "tiny": {"depths": [3, 3, 9, 3], "hidden_sizes": [96, 192, 384, 768]},
28
+ "base": {"depths": [3, 3, 27, 3], "hidden_sizes": [128, 256, 512, 1024]},
29
+ "large": {"depths": [3, 3, 27, 3], "hidden_sizes": [192, 384, 768, 1536]},
30
+ "huge": {"depths": [3, 3, 27, 3], "hidden_sizes": [352, 704, 1408, 2816]},
31
+ }
32
+
33
+
34
+ class MMEarthConfig(PreTrainedConfig):
35
+ model_type = "mmearth"
36
+
37
+ def __init__(
38
+ self,
39
+ depths: list[int] | None = None,
40
+ hidden_sizes: list[int] | None = None,
41
+ num_channels: int = 12,
42
+ image_size: int = 112,
43
+ patch_size: int = 16,
44
+ drop_path_rate: float = 0.0,
45
+ layer_norm_eps: float = 1e-6,
46
+ hidden_act: str = "gelu",
47
+ use_orig_stem: bool = False,
48
+ model_size: str = "atto",
49
+ input_modality: str = "all_mod",
50
+ channel_order: str = "rgb",
51
+ dataset: str = "1M_128",
52
+ loss_aggr: str = "uncertainty",
53
+ checkpoint_stage: str = "pretrain",
54
+ image_mean: list[float] | None = None,
55
+ image_std: list[float] | None = None,
56
+ do_rescale: bool = False,
57
+ rescale_factor: float = 1.0,
58
+ num_labels: int = 0,
59
+ **kwargs,
60
+ ):
61
+ super().__init__(**kwargs)
62
+ preset = MODEL_SIZE_PRESETS[model_size]
63
+ self.model_size = model_size
64
+ self.input_modality = input_modality
65
+ self.channel_order = channel_order
66
+ self.dataset = dataset
67
+ self.loss_aggr = loss_aggr
68
+ self.checkpoint_stage = checkpoint_stage
69
+ self.num_channels = num_channels
70
+ self.image_size = image_size
71
+ self.patch_size = patch_size
72
+ self.drop_path_rate = drop_path_rate
73
+ self.layer_norm_eps = layer_norm_eps
74
+ self.hidden_act = hidden_act
75
+ self.use_orig_stem = use_orig_stem
76
+ self.num_labels = num_labels
77
+ self.do_rescale = do_rescale
78
+ self.rescale_factor = rescale_factor
79
+ self.depths = depths if depths is not None else preset["depths"]
80
+ self.hidden_sizes = hidden_sizes if hidden_sizes is not None else preset["hidden_sizes"]
81
+ self.hidden_size = self.hidden_sizes[-1]
82
+ self.num_stages = len(self.depths)
83
+ self.image_mean = image_mean
84
+ self.image_std = image_std
85
+
86
+
87
+ class MMEarthLayerNorm(nn.Module):
88
+ def __init__(self, normalized_shape: int, eps: float = 1e-6, data_format: str = "channels_last"):
89
+ super().__init__()
90
+ self.weight = nn.Parameter(torch.ones(normalized_shape))
91
+ self.bias = nn.Parameter(torch.zeros(normalized_shape))
92
+ self.eps = eps
93
+ self.data_format = data_format
94
+ self.normalized_shape = (normalized_shape,)
95
+
96
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
97
+ if self.data_format == "channels_last":
98
+ return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
99
+ u = x.mean(1, keepdim=True)
100
+ s = (x - u).pow(2).mean(1, keepdim=True)
101
+ x = (x - u) / torch.sqrt(s + self.eps)
102
+ return self.weight[:, None, None] * x + self.bias[:, None, None]
103
+
104
+
105
+ class MMEarthGRN(nn.Module):
106
+ def __init__(self, dim: int):
107
+ super().__init__()
108
+ self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))
109
+ self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))
110
+
111
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
112
+ gx = torch.norm(x, p=2, dim=(1, 2), keepdim=True)
113
+ nx = gx / (gx.mean(dim=-1, keepdim=True) + 1e-4)
114
+ return self.gamma * (x * nx) + self.beta + x
115
+
116
+
117
+ class MMEarthConvNeXtBlock(nn.Module):
118
+ def __init__(self, dim: int, drop_path: float = 0.0):
119
+ super().__init__()
120
+ self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
121
+ self.norm = MMEarthLayerNorm(dim, eps=1e-6)
122
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
123
+ self.act = nn.GELU()
124
+ self.grn = MMEarthGRN(4 * dim)
125
+ self.pwconv2 = nn.Linear(4 * dim, dim)
126
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
127
+
128
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
129
+ input_tensor = x
130
+ x = self.dwconv(x)
131
+ x = x.permute(0, 2, 3, 1)
132
+ x = self.norm(x)
133
+ x = self.pwconv1(x)
134
+ x = self.act(x)
135
+ x = self.grn(x)
136
+ x = self.pwconv2(x)
137
+ x = x.permute(0, 3, 1, 2)
138
+ return input_tensor + self.drop_path(x)
139
+
140
+
141
+ class MMEarthPreTrainedModel(PreTrainedModel):
142
+ config_class = MMEarthConfig
143
+ config: MMEarthConfig
144
+ base_model_prefix = "mmearth"
145
+ main_input_name = "pixel_values"
146
+ input_modalities = ("image",)
147
+ supports_gradient_checkpointing = True
148
+ _no_split_modules = ["MMEarthConvNeXtBlock"]
149
+
150
+ def _init_weights(self, module):
151
+ super()._init_weights(module)
152
+ if isinstance(module, (nn.Conv2d, nn.Linear)):
153
+ trunc_normal_(module.weight, std=0.02)
154
+ if module.bias is not None:
155
+ nn.init.constant_(module.bias, 0)
156
+
157
+
158
+ class MMEarthModel(MMEarthPreTrainedModel):
159
+ def __init__(self, config: MMEarthConfig, add_pooling_layer: bool = True):
160
+ super().__init__(config)
161
+ self.config = config
162
+ self.add_pooling_layer = add_pooling_layer
163
+ depths = config.depths
164
+ dims = config.hidden_sizes
165
+ patch_size = config.patch_size
166
+ num_stages = len(depths)
167
+ self.downsample_layers = nn.ModuleList()
168
+ if config.use_orig_stem:
169
+ self.stem_orig = nn.Sequential(
170
+ nn.Conv2d(
171
+ config.num_channels,
172
+ dims[0],
173
+ kernel_size=patch_size // (2 ** (num_stages - 1)),
174
+ stride=patch_size // (2 ** (num_stages - 1)),
175
+ ),
176
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
177
+ )
178
+ self.initial_conv = None
179
+ self.stem = None
180
+ else:
181
+ self.stem_orig = None
182
+ self.initial_conv = nn.Sequential(
183
+ nn.Conv2d(config.num_channels, dims[0], kernel_size=3, stride=1),
184
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
185
+ nn.GELU(),
186
+ )
187
+ stem_kernel = patch_size // (2 ** (num_stages - 1))
188
+ self.stem = nn.Sequential(
189
+ nn.Conv2d(
190
+ dims[0],
191
+ dims[0],
192
+ kernel_size=stem_kernel,
193
+ stride=stem_kernel,
194
+ padding=stem_kernel // 2,
195
+ groups=dims[0],
196
+ ),
197
+ MMEarthLayerNorm(dims[0], eps=config.layer_norm_eps, data_format="channels_first"),
198
+ )
199
+ for i in range(3):
200
+ self.downsample_layers.append(
201
+ nn.Sequential(
202
+ MMEarthLayerNorm(dims[i], eps=config.layer_norm_eps, data_format="channels_first"),
203
+ nn.Conv2d(dims[i], dims[i + 1], kernel_size=2, stride=2),
204
+ )
205
+ )
206
+ dp_rates = [
207
+ x.item()
208
+ for x in torch.linspace(0, config.drop_path_rate, sum(depths), device=torch.device("cpu"))
209
+ ]
210
+ cur = 0
211
+ self.stages = nn.ModuleList()
212
+ for i in range(num_stages):
213
+ stage = nn.Sequential(
214
+ *[MMEarthConvNeXtBlock(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]
215
+ )
216
+ self.stages.append(stage)
217
+ cur += depths[i]
218
+ self.norm = nn.LayerNorm(dims[-1], eps=config.layer_norm_eps)
219
+ self.post_init()
220
+
221
+ def _forward_stem(self, x: torch.Tensor) -> torch.Tensor:
222
+ if self.config.use_orig_stem:
223
+ return self.stem_orig(x)
224
+ x = self.initial_conv(x)
225
+ return self.stem(x)
226
+
227
+ def forward_features(self, pixel_values: torch.Tensor) -> torch.Tensor:
228
+ x = self._forward_stem(pixel_values)
229
+ x = self.stages[0](x)
230
+ for i in range(3):
231
+ x = self.downsample_layers[i](x)
232
+ x = self.stages[i + 1](x)
233
+ return x
234
+
235
+ def forward(
236
+ self,
237
+ pixel_values: Optional[torch.Tensor] = None,
238
+ return_dict: Optional[bool] = None,
239
+ **kwargs: Unpack[TransformersKwargs],
240
+ ) -> BaseModelOutputWithPooling:
241
+ if pixel_values is None:
242
+ raise ValueError("You must specify `pixel_values`")
243
+ pixel_values = pixel_values.to(dtype=self.dtype)
244
+ if return_dict is None:
245
+ return_dict = self.config.use_return_dict
246
+ spatial_features = self.forward_features(pixel_values)
247
+ last_hidden_state = spatial_features.flatten(2).transpose(1, 2)
248
+ pooled_output = self.norm(spatial_features.mean([-2, -1])) if self.add_pooling_layer else None
249
+ if not return_dict:
250
+ return (last_hidden_state, pooled_output)
251
+ return BaseModelOutputWithPooling(last_hidden_state=last_hidden_state, pooler_output=pooled_output)
252
+
253
+
254
+ class MMEarthForImageClassification(MMEarthPreTrainedModel):
255
+ def __init__(self, config: MMEarthConfig):
256
+ super().__init__(config)
257
+ self.num_labels = config.num_labels
258
+ self.mmearth = MMEarthModel(config, add_pooling_layer=True)
259
+ self.classifier = (
260
+ nn.Linear(config.hidden_size, config.num_labels) if config.num_labels > 0 else nn.Identity()
261
+ )
262
+ self.post_init()
263
+
264
+ def forward(
265
+ self,
266
+ pixel_values: Optional[torch.Tensor] = None,
267
+ labels: Optional[torch.Tensor] = None,
268
+ return_dict: Optional[bool] = None,
269
+ **kwargs: Unpack[TransformersKwargs],
270
+ ) -> ImageClassifierOutput:
271
+ outputs = self.mmearth(pixel_values=pixel_values, return_dict=True, **kwargs)
272
+ logits = self.classifier(outputs.pooler_output)
273
+ loss = None
274
+ if labels is not None:
275
+ loss = self.loss_function(labels, logits, self.config, **kwargs)
276
+ if not return_dict:
277
+ output = (logits,) + outputs[1:]
278
+ return ((loss,) + output) if loss is not None else output
279
+ return ImageClassifierOutput(
280
+ loss=loss,
281
+ logits=logits,
282
+ hidden_states=outputs.hidden_states,
283
+ attentions=outputs.attentions,
284
+ )
285
+
286
+
287
+ __all__ = [
288
+ "MMEarthConfig",
289
+ "MMEarthForImageClassification",
290
+ "MMEarthModel",
291
+ "MMEarthPreTrainedModel",
292
+ ]
mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/pipeline_mmearth.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 MMEarth Authors and The HuggingFace Inc. team.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ """MMEarth image feature extraction pipeline."""
6
+
7
+ from typing import Any, Union
8
+
9
+ from transformers.pipelines.base import GenericTensor, build_pipeline_init_args
10
+ from transformers.pipelines.image_feature_extraction import ImageFeatureExtractionPipeline
11
+ from transformers.utils import add_end_docstrings, is_vision_available
12
+
13
+
14
+ if is_vision_available():
15
+ from transformers.image_utils import load_image
16
+
17
+
18
+ @add_end_docstrings(
19
+ build_pipeline_init_args(has_image_processor=True),
20
+ """
21
+ pool (`bool`, *optional*, defaults to `False`):
22
+ Whether or not to return the pooled output. If `False`, the model will return the raw hidden states.
23
+ """,
24
+ )
25
+ class MMEarthImageFeatureExtractionPipeline(ImageFeatureExtractionPipeline):
26
+ """
27
+ MMEarth image feature extraction pipeline.
28
+
29
+ This pipeline wraps [`MMEarthModel`] for Sentinel-2 multispectral and RGB/BGR geospatial feature extraction.
30
+ It extends [`ImageFeatureExtractionPipeline`] with support for numpy arrays and file paths in addition to
31
+ standard image inputs.
32
+ """
33
+
34
+ def _sanitize_parameters(
35
+ self,
36
+ image_processor_kwargs=None,
37
+ return_tensors=None,
38
+ pool=None,
39
+ **kwargs,
40
+ ):
41
+ preprocess_params = {} if image_processor_kwargs is None else dict(image_processor_kwargs)
42
+ if "timeout" in kwargs:
43
+ preprocess_params["timeout"] = kwargs["timeout"]
44
+
45
+ postprocess_params = {}
46
+ if pool is not None:
47
+ postprocess_params["pool"] = pool
48
+ if return_tensors is not None:
49
+ postprocess_params["return_tensors"] = return_tensors
50
+
51
+ return preprocess_params, {}, postprocess_params
52
+
53
+ def preprocess(self, image, timeout=None, **image_processor_kwargs) -> dict[str, GenericTensor]:
54
+ if not isinstance(image, (list, tuple)) and not hasattr(image, "shape"):
55
+ image = load_image(image, timeout=timeout)
56
+ model_inputs = self.image_processor(image, return_tensors="pt", **image_processor_kwargs)
57
+ model_inputs = model_inputs.to(self.dtype)
58
+ return model_inputs
59
+
60
+ def __call__(
61
+ self,
62
+ *args: Union[str, Any, list[Any]],
63
+ **kwargs: Any,
64
+ ) -> list[Any]:
65
+ return super().__call__(*args, **kwargs)
66
+
67
+
68
+ __all__ = ["MMEarthImageFeatureExtractionPipeline"]
mmearth-convnextv2-atto-rgb-1m-64-uncertainty-56x8/preprocessor_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "MMEarthImageProcessor",
3
+ "size": {
4
+ "height": 56,
5
+ "width": 56
6
+ },
7
+ "do_resize": false,
8
+ "do_rescale": false,
9
+ "rescale_factor": 1.0,
10
+ "do_normalize": false,
11
+ "do_convert_rgb": false,
12
+ "channel_order": "bgr",
13
+ "image_mean": null,
14
+ "image_std": null,
15
+ "auto_map": {
16
+ "AutoImageProcessor": "image_processing_mmearth.MMEarthImageProcessor"
17
+ }
18
+ }
mmearth-convnextv2-atto-s2-1m-64-uncertainty-56x8/config.json ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "MMEarthModel"
4
+ ],
5
+ "band_names": [
6
+ "B1",
7
+ "B2",
8
+ "B3",
9
+ "B4",
10
+ "B5",
11
+ "B6",
12
+ "B7",
13
+ "B8A",
14
+ "B8",
15
+ "B9",
16
+ "B11",
17
+ "B12"
18
+ ],
19
+ "channel_order": "rgb",
20
+ "checkpoint_stage": "pretrain",
21
+ "dataset": "1M_64",
22
+ "depths": [
23
+ 2,
24
+ 2,
25
+ 6,
26
+ 2
27
+ ],
28
+ "do_rescale": false,
29
+ "drop_path_rate": 0.0,
30
+ "dtype": "float32",
31
+ "hidden_act": "gelu",
32
+ "hidden_size": 320,
33
+ "hidden_sizes": [
34
+ 40,
35
+ 80,
36
+ 160,
37
+ 320
38
+ ],
39
+ "id2label": {},
40
+ "image_mean": null,
41
+ "image_size": 56,
42
+ "image_std": null,
43
+ "input_modality": "S2",
44
+ "label2id": {},
45
+ "layer_norm_eps": 1e-06,
46
+ "loss_aggr": "uncertainty",
47
+ "model_size": "atto",
48
+ "model_type": "mmearth",
49
+ "num_channels": 12,
50
+ "num_stages": 4,
51
+ "patch_size": 8,
52
+ "rescale_factor": 1.0,
53
+ "transformers_version": "5.0.0",
54
+ "use_orig_stem": false,
55
+ "auto_map": {
56
+ "AutoConfig": "modeling_mmearth.MMEarthConfig",
57
+ "AutoModel": "modeling_mmearth.MMEarthModel",
58
+ "AutoModelForImageClassification": "modeling_mmearth.MMEarthForImageClassification"
59
+ },
60
+ "custom_pipelines": {
61
+ "mmearth-feature-extraction": {
62
+ "impl": "pipeline_mmearth.MMEarthImageFeatureExtractionPipeline",
63
+ "pt": [
64
+ "AutoModel"
65
+ ]
66
+ }
67
+ },
68
+ "legacy_checkpoint": "checkpoint-199.pth",
69
+ "legacy_source": "pt-S2_atto_1M_64_uncertainty_56-8"
70
+ }