ckadirt commited on
Commit
4ee2f3d
·
verified ·
1 Parent(s): 6e2ebb0

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. fMRI-foundation-model-old/fMRI-MAE-mamba/__init__.py +0 -0
  2. fMRI-foundation-model-old/fMRI-MAE-mamba/accel.slurm +65 -0
  3. fMRI-foundation-model-old/fMRI-MAE-mamba/config.yaml +53 -0
  4. fMRI-foundation-model-old/fMRI-MAE-mamba/dataloader.py +772 -0
  5. fMRI-foundation-model-old/fMRI-MAE-mamba/mamba.ipynb +0 -0
  6. fMRI-foundation-model-old/fMRI-MAE-mamba/mindeye_config.yaml +30 -0
  7. fMRI-foundation-model-old/fMRI-MAE-mamba/mindeye_models.py +276 -0
  8. fMRI-foundation-model-old/fMRI-MAE-mamba/models.py +504 -0
  9. fMRI-foundation-model-old/fMRI-MAE-mamba/utils.py +267 -0
  10. fMRI-foundation-model-old/fMRI-MAE/.ipynb_checkpoints/main-checkpoint.ipynb +1350 -0
  11. fMRI-foundation-model-old/fMRI-MAE/accel.slurm +65 -0
  12. fMRI-foundation-model-old/fMRI-MAE/dataloader.py +772 -0
  13. fMRI-foundation-model-old/fMRI-MAE/downstream.ipynb +1162 -0
  14. fMRI-foundation-model-old/fMRI-MAE/downstream.py +731 -0
  15. fMRI-foundation-model-old/fMRI-MAE/downstream.slurm +29 -0
  16. fMRI-foundation-model-old/fMRI-MAE/mindeye_models.py +276 -0
  17. fMRI-foundation-model-old/fMRI-MAE/models.py +423 -0
  18. fMRI-foundation-model-old/fMRI-MAE/rope.py +111 -0
  19. fMRI-foundation-model-old/fMRI-MAE/utils.py +412 -0
  20. fMRI-foundation-model-old/flat/.ipynb_checkpoints/main-checkpoint.py +593 -0
  21. fMRI-foundation-model-old/flat/.ipynb_checkpoints/main2-checkpoint.ipynb +6 -0
  22. fMRI-foundation-model-old/flat/__pycache__/flat_models.cpython-310.pyc +0 -0
  23. fMRI-foundation-model-old/flat/__pycache__/utils.cpython-310.pyc +0 -0
  24. fMRI-foundation-model-old/flat/accel.slurm +44 -0
  25. fMRI-foundation-model-old/flat/config.yaml +58 -0
  26. fMRI-foundation-model-old/flat/flat_models.py +784 -0
  27. fMRI-foundation-model-old/flat/main.ipynb +0 -0
  28. fMRI-foundation-model-old/flat/main.py +593 -0
  29. fMRI-foundation-model-old/flat/main2.ipynb +450 -0
  30. fMRI-foundation-model-old/flat/parquet_probe.ipynb +407 -0
  31. fMRI-foundation-model-old/flat/probe.ipynb +0 -0
  32. fMRI-foundation-model-old/flat/probe_prepper.ipynb +587 -0
  33. fMRI-foundation-model-old/flat/unique_tasks.npy +3 -0
  34. fMRI-foundation-model-old/flat/util/.ipynb_checkpoints/hcp_flat-checkpoint.py +120 -0
  35. fMRI-foundation-model-old/flat/util/.ipynb_checkpoints/misc-checkpoint.py +508 -0
  36. fMRI-foundation-model-old/flat/util/.ipynb_checkpoints/visualize-checkpoint.py +123 -0
  37. fMRI-foundation-model-old/flat/util/__pycache__/hcp_flat.cpython-310.pyc +0 -0
  38. fMRI-foundation-model-old/flat/util/__pycache__/logging.cpython-310.pyc +0 -0
  39. fMRI-foundation-model-old/flat/util/__pycache__/losses.cpython-310.pyc +0 -0
  40. fMRI-foundation-model-old/flat/util/__pycache__/video_vit.cpython-310.pyc +0 -0
  41. fMRI-foundation-model-old/flat/util/__pycache__/visualize.cpython-310.pyc +0 -0
  42. fMRI-foundation-model-old/flat/util/hcp_flat.py +121 -0
  43. fMRI-foundation-model-old/flat/util/logging.py +117 -0
  44. fMRI-foundation-model-old/flat/util/losses.py +132 -0
  45. fMRI-foundation-model-old/flat/util/lr_decay.py +83 -0
  46. fMRI-foundation-model-old/flat/util/lr_sched.py +28 -0
  47. fMRI-foundation-model-old/flat/util/meters.py +234 -0
  48. fMRI-foundation-model-old/flat/util/misc.py +508 -0
  49. fMRI-foundation-model-old/flat/util/pos_embed.py +53 -0
  50. fMRI-foundation-model-old/flat/util/video_vit.py +173 -0
fMRI-foundation-model-old/fMRI-MAE-mamba/__init__.py ADDED
File without changes
fMRI-foundation-model-old/fMRI-MAE-mamba/accel.slurm ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #SBATCH --account=fmri
3
+ #SBATCH --partition=a40
4
+ #SBATCH --job-name=fmrimamba
5
+ #SBATCH --ntasks-per-node=1
6
+ #SBATCH --nodes=1
7
+ #SBATCH --gres=gpu:1
8
+ #SBATCH --gpus-per-task=1 # Set to equal gres=gpu:#!
9
+ #SBATCH --time=20:00:00 # total run time limit (HH:MM:SS)
10
+ #SBATCH -e slurms/%j.err # first create a "slurms" folder in current directory to store logs
11
+ #SBATCH -o slurms/%j.out
12
+ #SBATCH --comment=medarc
13
+ #SBATCH --no-requeue
14
+ #SBATCH --exclusive
15
+
16
+ export NUM_GPUS=1 # Set to equal gres=gpu:#!
17
+ echo NUM_GPUS=$NUM_GPUS
18
+
19
+ source ~/.bashrc
20
+
21
+ cd /weka/proj-fmri/paulscotti/fMRI-foundation-model/fMRI-MAE/
22
+ jupyter nbconvert mamba.ipynb --to python
23
+ if [ $? -ne 0 ]; then
24
+ echo "Error: Conversion of ipynb to Python failed. Exiting."
25
+ exit 1
26
+ fi
27
+
28
+ # Make sure another job doesnt use same port, here using random number
29
+ export MASTER_PORT=$((RANDOM % (19000 - 11000 + 1) + 11000))
30
+ export HOSTNAMES=$(scontrol show hostnames "$SLURM_JOB_NODELIST")
31
+ export MASTER_ADDR=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n 1)
32
+ echo MASTER_ADDR=${MASTER_ADDR}
33
+ echo MASTER_PORT=${MASTER_PORT}
34
+ echo WORLD_SIZE=$((${SLURM_NNODES} * ${NUM_GPUS}))
35
+
36
+ # Other settings relevant for multi-node
37
+ export NCCL_DEBUG=WARN #INFO
38
+ export NCCL_PROTO=simple
39
+ export FI_EFA_USE_DEVICE_RDMA=1
40
+ export FI_EFA_FORK_SAFE=1
41
+ export FI_LOG_LEVEL=1
42
+ export PYTHONFAULTHANDLER=1
43
+ export CUDA_LAUNCH_BLOCKING=0
44
+ export OMPI_MCA_mtl_base_verbose=1
45
+ export FI_EFA_ENABLE_SHM_TRANSFER=0
46
+ export FI_PROVIDER=efa
47
+ export FI_EFA_TX_MIN_CREDITS=64
48
+ export NCCL_TREE_THRESHOLD=0
49
+ export TORCH_DISTRIBUTED_DEBUG=DETAIL
50
+ export NCCL_P2P_DISABLE=1
51
+ module load openmpi cuda/12.1
52
+
53
+ srun torchrun \
54
+ --nproc_per_node=$NUM_GPUS \
55
+ --nnodes=$SLURM_NNODES \
56
+ --rdzv_id=$SLURM_JOBID \
57
+ --rdzv_backend=c10d \
58
+ --rdzv_endpoint=${MASTER_ADDR}:$MASTER_PORT \
59
+ --rdzv_conf=timeout=90 \
60
+ mamba.py
61
+
62
+ if [ $? -ne 0 ]; then
63
+ echo "Error: srun command failed. Please check the logs for more details."
64
+ exit 1
65
+ fi
fMRI-foundation-model-old/fMRI-MAE-mamba/config.yaml ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Model Config
2
+ model_name: "framepatchsize4_bs8_300ep"
3
+ use_cls_token: False
4
+ use_contrastive_loss: False
5
+ constrastive_loss_weight: 0.01
6
+
7
+ # Training Configs
8
+ batch_size: 8
9
+ num_workers: 10
10
+ num_epochs: 300
11
+ seed: 42
12
+ max_lr: 3.0e-6 # Keep the x.0 else will be converted to string
13
+ num_samples_per_epoch: 1024
14
+
15
+ # Saving progress
16
+ ckpt_saving: True
17
+ ckpt_interval: 50
18
+ resume_from_ckpt: True
19
+ wandb_log: True
20
+
21
+ # MAE Config
22
+ tube_start_masking_ratio: 0.95
23
+ tube_end_masking_ratio: 0.95
24
+ decoder_mask_ratio: 0.85
25
+
26
+ # Model Config
27
+ patch_size: 8
28
+ frame_patch_size: 4
29
+ use_rope_emb: False
30
+ masking_strategy: "MNI"
31
+
32
+ # ViT Config
33
+ encoder_model: "vit_base"
34
+ decoder_model: "vit_small"
35
+
36
+ # Mamba config
37
+ #embed_dim: 512
38
+ #depth: 32
39
+
40
+ # Data Config
41
+ img_size: [88, 104, 72] # Image Size
42
+ num_frames: 4
43
+ is_s3: False
44
+ # 000495.tar is missing??
45
+ train_urls: ["/weka/proj-fmri/shared/NSD_MNI_wds/{000000..000738}.tar"]
46
+ # train_urls: ["s3://proj-fmri/fmri_foundation_datasets/NSD_MNI_wds/{000000..000494}.tar","s3://proj-fmri/fmri_foundation_datasets/NSD_MNI_wds/{000496..000740}.tar"]
47
+
48
+ #"/weka/proj-fmri/paulscotti/old_fMRI-foundation-model/dataset_creation/wds_creation/wds/000000.tar"
49
+ #"s3://proj-fmri/fmri_foundation_datasets/NSD_MNI_wds/{000000..000740}.tar"
50
+ # test_urls: "s3://proj-fmri/fmri_foundation_datasets/openneuro_MNI_wds/000000.tar"
51
+ # train_urls: "/weka/proj-fmri/paulscotti/old_fMRI-foundation-model/dataset_creation/wds_creation/wds/{000001..000240}.tar"
52
+ # train_urls: "/weka/proj-fmri/paulscotti/fMRI-foundation-model/dataset_creation/wds_creation/wds/{000000..000382}.tar"
53
+ # test_urls: "/weka/proj-fmri/paulscotti/old_fMRI-foundation-model/dataset_creation/wds_creation/wds/000000.tar"
fMRI-foundation-model-old/fMRI-MAE-mamba/dataloader.py ADDED
@@ -0,0 +1,772 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from torch import Tensor, stack
3
+ from torch.utils.data import IterDataPipe, DataLoader
4
+ from torch.utils.data.datapipes._decorator import functional_datapipe
5
+ from torch.utils.data.datapipes.iter.callable import MapperIterDataPipe
6
+ from torch.utils.data.datapipes.utils.common import StreamWrapper, match_masks
7
+ from torchdata.datapipes.iter import IterableWrapper, IterDataPipe, TarArchiveLoader
8
+ from torchdata.datapipes.iter.load.s3io import S3FileLoaderIterDataPipe
9
+ from torchdata.datapipes.utils.common import validate_pathname_binary_tuple
10
+ import warnings
11
+ import subprocess
12
+ import tarfile
13
+ import tempfile
14
+ import time
15
+ import random
16
+ from omegaconf import DictConfig, ListConfig
17
+ import webdataset as wds
18
+ import re
19
+ import os
20
+ import copy
21
+ from io import BufferedIOBase, BytesIO, RawIOBase
22
+ from typing import (
23
+ IO,
24
+ Any,
25
+ Callable,
26
+ Dict,
27
+ Iterable,
28
+ Iterator,
29
+ List,
30
+ Optional,
31
+ Sequence,
32
+ Tuple,
33
+ TypeVar,
34
+ Union,
35
+ cast,
36
+ )
37
+ T_co = TypeVar("T_co", covariant=True)
38
+
39
+ class ShuffledListDataPipe(IterDataPipe[T_co]):
40
+ def __init__(
41
+ self,
42
+ source_list: List[T_co],
43
+ *,
44
+ shuffle: bool = True,
45
+ cycle: Union[bool, int] = True,
46
+ ):
47
+ super().__init__()
48
+ self.source = source_list
49
+ self._enabled = shuffle
50
+ self._seed = None
51
+ self._rng = random.Random()
52
+ if isinstance(cycle, bool):
53
+ if cycle:
54
+ self._cycle = -1
55
+ else:
56
+ self._cycle = 1
57
+ else:
58
+ assert isinstance(cycle, int)
59
+ self._cycle = cycle
60
+
61
+ def __iter__(self) -> Iterator[T_co]:
62
+ source = copy.copy(self.source)
63
+ cycle = self._cycle
64
+ epochs = 0
65
+ while cycle == -1 or epochs < cycle:
66
+ if self._enabled:
67
+ self._rng.shuffle(source)
68
+ yield from source
69
+ epochs += 1
70
+
71
+ def __len__(self):
72
+ if self.count == -1:
73
+ raise TypeError(
74
+ f"This {type(self).__name__} instance cycles forever, and "
75
+ f"therefore doesn't have valid length."
76
+ )
77
+ else:
78
+ return self.count * len(self.source)
79
+
80
+ def __getstate__(self):
81
+ state = (
82
+ self.source,
83
+ self._enabled,
84
+ self._seed,
85
+ self._rng.getstate(),
86
+ self._valid_iterator_id,
87
+ self._number_of_samples_yielded,
88
+ )
89
+ if IterDataPipe.getstate_hook is not None:
90
+ return IterDataPipe.getstate_hook(state)
91
+ return state
92
+
93
+ def __setstate__(self, state):
94
+ (
95
+ self.source,
96
+ self._enabled,
97
+ self._seed,
98
+ rng_state,
99
+ self._valid_iterator_id,
100
+ self._number_of_samples_yielded,
101
+ ) = state
102
+ self._rng = random.Random()
103
+ self._rng.setstate(rng_state)
104
+
105
+ def get_job_temp_dir(dl_root: str) -> str:
106
+ try:
107
+ job_or_array_id = (
108
+ os.environ.get("SLURM_ARRAY_JOB_ID", "") or os.environ["SLURM_JOB_ID"]
109
+ )
110
+ task_id = os.environ.get("SLURM_ARRAY_TASK_ID", "") or "0"
111
+ return os.path.join(dl_root, f"JOB_{job_or_array_id}/TASK_{task_id}")
112
+ except KeyError as e:
113
+ print("SLURM_JOB_ID env var not set: You sure this job is healthy?")
114
+ raise e
115
+
116
+ def _shard_expand(s: str) -> List[str]:
117
+ expansion = r"\{[0-9]+\.\.[0-9]+\}"
118
+ m = re.search(expansion, s)
119
+ if not m:
120
+ return [s]
121
+ prefix = s[: m.start()]
122
+ rest = _shard_expand(s[m.end() :])
123
+ rng = s[m.start() + 1 : m.end() - 1]
124
+ lohi = rng.split("..")
125
+ if len(lohi[0]) == len(lohi[1]) and lohi[0].startswith("0"):
126
+ fmt = "{prefix}{i:0>{l}d}{r}"
127
+ elif len(lohi[0]) <= len(lohi[1]):
128
+ if lohi[0].startswith("0") and lohi[0] != "0":
129
+ raise ValueError(
130
+ "shard_expand: low bound must not start with 0 if low bound is shorter"
131
+ )
132
+ fmt = "{prefix}{i}{r}"
133
+ else:
134
+ raise ValueError("shard_expand: low bound must be shorter than high bound")
135
+ lo, hi = (int(x) for x in lohi)
136
+ if lo >= hi:
137
+ raise ValueError(f"shard_expand: bad range in in shard spec {s}.")
138
+ result = []
139
+ for i in range(lo, hi + 1):
140
+ for r in rest:
141
+ expanded: str = fmt.format(prefix=prefix, i=i, r=r, l=len(lohi[1]))
142
+ result.append(expanded)
143
+ return result
144
+
145
+ @functional_datapipe("custom_shard_expand")
146
+ class CustomShardExpanderIterDataPipe(IterDataPipe[str]):
147
+ def __init__(self, source_datapipe: IterDataPipe[str]) -> None:
148
+ super().__init__()
149
+ self.source_datapipe: IterDataPipe[str] = source_datapipe
150
+
151
+ def __iter__(self) -> Iterator[str]:
152
+ for path in self.source_datapipe:
153
+ yield from _shard_expand(path)
154
+
155
+ def warn_and_continue(exn):
156
+ """Call in an exception handler to ignore any exception, issue a warning, and continue."""
157
+ print(exn)
158
+ warnings.warn(repr(exn))
159
+ time.sleep(0.05)
160
+ return True
161
+ error_handler = warn_and_continue
162
+
163
+ def is_stream_handle(data):
164
+ obj_to_check = data.file_obj if isinstance(data, StreamWrapper) else data
165
+ return isinstance(obj_to_check, (BufferedIOBase, RawIOBase))
166
+
167
+ def read_stream_handles(data):
168
+ if not is_stream_handle(data):
169
+ return data
170
+ else:
171
+ ds = data
172
+ data = b"".join(data)
173
+ ds.close()
174
+ del ds
175
+ return data
176
+
177
+ def stream_reader(sample):
178
+ return {k: read_stream_handles(v) for k, v in sample.items()}
179
+
180
+ def to_our_format(sample):
181
+ wds_key = sample.pop("__key__")
182
+ sample = {k.lstrip("."): v for k, v in sample.items()}
183
+ sample["__key__"] = wds_key.split("/")[-1]
184
+ sample["__url__"] = "/".join(wds_key.split("/")[:-1])
185
+ return sample
186
+
187
+ def add_processors(
188
+ datapipeline,
189
+ processors: Optional[ListConfig],
190
+ description: str,
191
+ error_handler: Callable = warn_and_continue,
192
+ ):
193
+ if not processors:
194
+ return datapipeline
195
+ else:
196
+ for i, processor_config in enumerate(processors):
197
+ processor = instantiate(processor_config)
198
+ if isinstance(processor, AbstractFilter):
199
+ print(
200
+ f"Adding filter {processor.__class__.__name__} as {description} #{i} "
201
+ f"to the datapipeline"
202
+ )
203
+ datapipeline = datapipeline.filter(processor.filter)
204
+ elif isinstance(processor, AbstractMapper):
205
+ print(
206
+ f"Adding mapper {processor.__class__.__name__} as {description} #{i} "
207
+ f"to the datapipeline"
208
+ )
209
+ datapipeline = datapipeline.map_with_handler(
210
+ processor.map,
211
+ handler=error_handler,
212
+ called_cls_name=processor.__class__.__name__,
213
+ )
214
+ else:
215
+ raise TypeError(
216
+ f"chosen {description} {processor.__class__.__name__} should be either subclass"
217
+ "AbstractMapper or AbstractFilter"
218
+ )
219
+ return datapipeline
220
+
221
+ @functional_datapipe("map_with_handler")
222
+ class MapperWithErrorHandlingIterDataPipe(MapperIterDataPipe):
223
+ def __init__(
224
+ self,
225
+ datapipe: IterDataPipe,
226
+ fn: Callable,
227
+ handler: Callable = wds.reraise_exception,
228
+ input_col: Optional[Union[str, int]] = None,
229
+ output_col: Optional[Union[str, int]] = None,
230
+ called_cls_name: Optional[str] = None,
231
+ ):
232
+ # for now, disbable input and output col since this is never used anyways
233
+ if input_col is not None:
234
+ raise NotImplementedError("`input_col` argument currently not supported")
235
+
236
+ if output_col is not None:
237
+ raise NotImplementedError("`output_col` argument currently not supported")
238
+
239
+ super().__init__(datapipe, fn)
240
+ self.handler = handler
241
+ self._apply_fn_ = self._apply_fn
242
+
243
+ def __iter__(self) -> Iterator[T_co]:
244
+ for data in self.datapipe:
245
+ try:
246
+ res = self._apply_fn_(data)
247
+ if res is None:
248
+ continue
249
+ yield res
250
+ except Exception as e:
251
+ if self.handler(e):
252
+ continue
253
+ else:
254
+ raise e
255
+
256
+ __S3_TOOLS__ = {
257
+ "s3": ["/usr/local/bin/aws", "s3"],
258
+ }
259
+
260
+ def is_tar(x: str) -> bool:
261
+ return x.endswith(".tar")
262
+
263
+ def ls_aws(
264
+ path: str,
265
+ tool: str = "s3",
266
+ recursive: bool = True,
267
+ raise_errors: bool = True,
268
+ skip_files: bool = True,
269
+ ):
270
+ assert path.startswith("s3://"), path
271
+
272
+ # in case we have a file, set recursive to false
273
+ isfile = bool(os.path.splitext(path)[1])
274
+ if isfile and skip_files:
275
+ return [path]
276
+
277
+ if not path.endswith("/") and not isfile:
278
+ path = path + "/"
279
+ cmd = [*__S3_TOOLS__[tool], "ls", f"{path}"]
280
+
281
+ if recursive and not isfile:
282
+ cmd += ["--recursive"]
283
+ try:
284
+ result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
285
+ result.check_returncode()
286
+
287
+ stdout = result.stdout.decode().split("\n")
288
+ # return empty list in case path is not a real file/directory
289
+ if not stdout[0]:
290
+ return []
291
+
292
+ # return path if it is an exisiting file
293
+ if isfile:
294
+ return [path]
295
+
296
+ # strip timestamp and object size
297
+ out = [line.split(" ")[-1] for line in stdout if line]
298
+ if recursive:
299
+ # aws s3 ls returns prefix+filename
300
+ bucket = path[: path.find("/", len("s3://"))] # s3://<bucket>/...
301
+ out = [os.path.join(bucket, o) for o in out]
302
+ else:
303
+ # aws s3 ls returns filename only
304
+ out = [os.path.join(path, o) for i in out]
305
+
306
+ return out
307
+ except subprocess.CalledProcessError as e:
308
+ print(f"Got exception while trying to load data! {e.__class__.__name__}: {e}")
309
+ if raise_errors:
310
+ raise e
311
+ else:
312
+ return []
313
+
314
+ @functional_datapipe("wrapped_load_files_by_s3")
315
+ class WrappedS3FileLoaderIterDataPipe(S3FileLoaderIterDataPipe):
316
+ def __init__(
317
+ self,
318
+ *args,
319
+ ignore_missing_files: bool = False,
320
+ **kwargs,
321
+ ):
322
+ super().__init__(*args, **kwargs)
323
+ self.ignore_missing_files = ignore_missing_files
324
+
325
+ def __iter__(self) -> Iterator[Tuple[str, StreamWrapper]]:
326
+ # same as parent but maybe ignoring errors
327
+ for url in self.source_datapipe:
328
+ try:
329
+ yield url, StreamWrapper(BytesIO(self.handler.s3_read(url)))
330
+ except ValueError as e:
331
+ if not self.ignore_missing_files:
332
+ raise FileNotFoundError(url)
333
+ else:
334
+ print(f"Warning: Could not download {url}")
335
+
336
+ @functional_datapipe("list_files_with_aws_cli")
337
+ class S3CLIFileListerIterDataPipe(IterDataPipe[T_co]):
338
+ def __init__(
339
+ self,
340
+ source_datapipe: Union[str, Sequence[str], IterDataPipe],
341
+ s3_tool: str = "s3",
342
+ n_retries: int = 10,
343
+ sleep_interval: float = 0.01,
344
+ ):
345
+ if isinstance(source_datapipe, str):
346
+ source_datapipe = [
347
+ source_datapipe,
348
+ ]
349
+ if not isinstance(source_datapipe, IterDataPipe):
350
+ self.datapipe: IterDataPipe = IterableWrapper(source_datapipe) # type: ignore[assignment]
351
+ else:
352
+ self.datapipe = source_datapipe
353
+
354
+ assert s3_tool in __S3_TOOLS__, f"`s3_tool` has to be in {list(__S3_TOOLS__)}"
355
+
356
+ self.s3_tool = s3_tool
357
+
358
+ self.n_retries = n_retries
359
+ self.sleep = sleep_interval
360
+
361
+ def __iter__(self) -> Iterator[str]:
362
+ for root in self.datapipe:
363
+ files = None
364
+ for _ in range(self.n_retries):
365
+ try:
366
+ files = ls_aws(
367
+ path=root, tool=self.s3_tool, recursive=True, raise_errors=True
368
+ )
369
+ break
370
+ except subprocess.CalledProcessError:
371
+ time.sleep(self.sleep)
372
+
373
+ if files is None:
374
+ print(
375
+ f"Could not ls data expected under {root} in {self.n_retries} tries. Not yielding ..."
376
+ )
377
+ continue
378
+
379
+ for file in files:
380
+ yield file
381
+
382
+ def _download_tar(url, scratch, verbose=False, n_retries=100):
383
+ start = time.perf_counter()
384
+ if verbose:
385
+ _log(f"downloading {url}")
386
+ uid = "".join(filter(str.isalnum, os.path.splitext(url)[0]))
387
+ idx = 0
388
+ path = os.path.join(scratch, f"{os.getpid()}.{uid}.{idx}.tar")
389
+ while os.path.exists(path):
390
+ idx += 1
391
+ path = os.path.join(scratch, f"{os.getpid()}.{uid}.{idx}.tar")
392
+
393
+ os.makedirs(os.path.dirname(path), exist_ok=True)
394
+ for i_try in range(n_retries):
395
+ p = subprocess.call(
396
+ [
397
+ "/usr/local/bin/aws",
398
+ "s3",
399
+ "cp",
400
+ url,
401
+ path,
402
+ "--quiet",
403
+ ]
404
+ )
405
+
406
+ if p != 0:
407
+ # mostly to work around
408
+ # Error when retrieving credentials from Ec2InstanceMetadata: No
409
+ # credentials found in credential_source referenced in profile
410
+ # default
411
+ if i_try == 0:
412
+ # check if the requested file actually exists
413
+ if not ls_aws(url, skip_files=False, raise_errors=False):
414
+ raise FileNotFoundError(url)
415
+
416
+ if i_try + 1 < n_retries:
417
+ time.sleep(random.uniform(0.0, 0.1))
418
+ continue
419
+ else:
420
+ raise Exception(
421
+ f"File {url} should exist but failed to download after trying {i_try+1} times."
422
+ )
423
+ break
424
+
425
+ if verbose:
426
+ _log(
427
+ f"wrote {path} in {time.perf_counter() - start} secs after {i_try+1} tries."
428
+ )
429
+ return path
430
+
431
+
432
+ @functional_datapipe("download_with_s3_cli")
433
+ class S3CLITarDownloader(IterDataPipe[T_co]):
434
+ def __init__(
435
+ self,
436
+ source_datapipe,
437
+ n_retries: int = 10,
438
+ verbose: bool = False,
439
+ dl_root: str = "/scratch",
440
+ mode: str = "r:*",
441
+ aws_kwargs: Optional[dict] = None,
442
+ ignore_missing_files: bool = False,
443
+ ):
444
+ self.source_datapipe = source_datapipe
445
+ assert os.path.isdir(dl_root), f"`dl_root` {dl_root} is not a valid directory"
446
+ # get slurm job id and create a subdir in `dl_root` to download all the tars to
447
+ dl_root = get_job_temp_dir(dl_root)
448
+ os.makedirs(dl_root, exist_ok=True)
449
+ self.dl_root = dl_root
450
+ self.mode = mode
451
+ self.n_retries = n_retries
452
+ self.ignore_missing_files = ignore_missing_files
453
+
454
+ self.verbose = verbose
455
+
456
+ def _yield_next(self, url: str, local_path: str):
457
+ if self.verbose:
458
+ _log("popping queue")
459
+
460
+ if self.verbose:
461
+ _log(f"loading {local_path}")
462
+ tarstream = tarfile.open(local_path, self.mode)
463
+
464
+ if self.verbose:
465
+ _log(f"yielding {url}")
466
+
467
+ yield (url, StreamWrapper(tarstream))
468
+ if self.verbose:
469
+ _log("new tar request")
470
+
471
+ if self.verbose:
472
+ _log(f"removing previous tar at {local_path}")
473
+ try:
474
+ os.remove(local_path)
475
+ except FileNotFoundError:
476
+ _log(
477
+ f"WARNING: Could not find previous tar for deletion. Unless a clean-up was triggered this is unexpected. The location was {local_path}"
478
+ )
479
+
480
+ def __iter__(self):
481
+ with tempfile.TemporaryDirectory(dir=self.dl_root) as scratch:
482
+ for url in self.source_datapipe:
483
+ try:
484
+ local_path = _download_tar(
485
+ url, scratch, verbose=self.verbose, n_retries=self.n_retries
486
+ )
487
+ except FileNotFoundError as e:
488
+ if not self.ignore_missing_files:
489
+ raise e
490
+ else:
491
+ yield from self._yield_next(url, local_path)
492
+
493
+
494
+ @functional_datapipe("load_from_tar_and_handle_error")
495
+ class TarArchiveLoaderWithErrorHandlingIterDataPipe(TarArchiveLoader):
496
+ def __init__(
497
+ self,
498
+ datapipe: Iterable[Tuple[str, BufferedIOBase]],
499
+ mode: str = "r:*",
500
+ length: int = -1,
501
+ handler: Callable = wds.reraise_exception,
502
+ ):
503
+ super().__init__(datapipe=datapipe, mode=mode, length=length)
504
+ self.handler = handler
505
+
506
+ def __iter__(self) -> Iterator[Tuple[str, BufferedIOBase]]:
507
+ for data in self.datapipe:
508
+ validate_pathname_binary_tuple(data)
509
+ pathname, data_stream = data
510
+ try:
511
+ if isinstance(data_stream, StreamWrapper) and isinstance(
512
+ data_stream.file_obj, tarfile.TarFile
513
+ ):
514
+ tar = data_stream.file_obj
515
+ else:
516
+ reading_mode = (
517
+ self.mode
518
+ if hasattr(data_stream, "seekable") and data_stream.seekable()
519
+ else self.mode.replace(":", "|")
520
+ )
521
+ # typing.cast is used here to silence mypy's type checker
522
+ tar = tarfile.open(
523
+ fileobj=cast(Optional[IO[bytes]], data_stream),
524
+ mode=reading_mode,
525
+ )
526
+ for tarinfo in tar:
527
+ if not tarinfo.isfile():
528
+ continue
529
+ extracted_fobj = tar.extractfile(tarinfo)
530
+ if extracted_fobj is None:
531
+ warnings.warn(
532
+ f"failed to extract file {tarinfo.name} from source tarfile {pathname}"
533
+ )
534
+ raise tarfile.ExtractError
535
+ inner_pathname = os.path.normpath(
536
+ os.path.join(pathname, tarinfo.name)
537
+ )
538
+
539
+ yield inner_pathname, StreamWrapper(extracted_fobj, data_stream, name=inner_pathname) # type: ignore[misc]
540
+ except Exception as e:
541
+ warnings.warn(
542
+ f"Unable to extract files from corrupted tarfile stream {pathname} due to: {e}, abort!"
543
+ )
544
+ if self.handler(e):
545
+ if hasattr(e, "args") and len(e.args) > 0:
546
+ e.args = (e.args[0] + " @ " + str(pathname),) + e.args[1:]
547
+ finally:
548
+ if isinstance(data_stream, StreamWrapper):
549
+ data_stream.autoclose()
550
+
551
+ class DictCollator:
552
+ def __init__(
553
+ self,
554
+ combine_tensors: bool = True,
555
+ combine_scalars: bool = True,
556
+ timeout=None,
557
+ ):
558
+ self.combine_tensors = combine_tensors
559
+ self.combine_scalars = combine_scalars
560
+
561
+ collate = self._collate
562
+ if timeout is not None:
563
+ collate = timeout_wrapper(collate, timeout=timeout)
564
+ self.collate = collate
565
+
566
+ def __call__(self, samples):
567
+ return self.collate(samples)
568
+
569
+ def _collate(self, samples):
570
+ keys = set.intersection(*[set(sample.keys()) for sample in samples])
571
+ batched = {key: [] for key in keys}
572
+
573
+ for s in samples:
574
+ [batched[key].append(s[key]) for key in batched]
575
+
576
+ result = {}
577
+ for key in batched:
578
+ if isinstance(batched[key][0], (int, float)):
579
+ if self.combine_scalars:
580
+ result[key] = np.array(list(batched[key]))
581
+ elif isinstance(batched[key][0], Tensor):
582
+ if self.combine_tensors:
583
+ result[key] = stack(list(batched[key]))
584
+ elif isinstance(batched[key][0], np.ndarray):
585
+ if self.combine_tensors:
586
+ result[key] = np.array(list(batched[key]))
587
+ else:
588
+ result[key] = list(batched[key])
589
+
590
+ del samples
591
+ del batched
592
+ return result
593
+
594
+ def timeout_wrapper(func: Callable, timeout: Optional[float] = None) -> Callable:
595
+ if timeout is None or timeout <= 0.0:
596
+ return func
597
+
598
+ @functools.wraps(func)
599
+ def wrapper(*args, **kwargs):
600
+ result = [None]
601
+ exception = [None]
602
+ event = threading.Event()
603
+
604
+ def wrapped_func():
605
+ try:
606
+ result[0] = func(*args, **kwargs)
607
+ except Exception as e:
608
+ exception[0] = e
609
+ finally:
610
+ event.set()
611
+
612
+ thread = threading.Thread(target=wrapped_func)
613
+ thread.start()
614
+ event.wait(timeout)
615
+
616
+ if not event.is_set():
617
+ raise TimeoutError(f"Function call timed out (longer than {timeout} secs).")
618
+
619
+ thread.join()
620
+
621
+ if exception[0] is not None:
622
+ err = exception[0]
623
+ del exception
624
+ raise err
625
+
626
+ del thread
627
+ del exception
628
+ del wrapped_func
629
+ del event
630
+ del args
631
+ del kwargs
632
+
633
+ ret = result[0]
634
+ del result
635
+ return ret
636
+
637
+ return wrapper
638
+
639
+ class DecoderWithTimeout(wds.Decoder):
640
+ """Decode samples using a list of handlers.
641
+
642
+ For each key/data item, this iterates through the list of
643
+ handlers until some handler returns something other than None.
644
+ """
645
+
646
+ def __init__(self, *args, timeout: Optional[float] = None, **kwargs):
647
+ super().__init__(*args, **kwargs)
648
+
649
+ if timeout is not None:
650
+ self.decode = timeout_wrapper(self.decode, timeout)
651
+
652
+ def setup_decoder(
653
+ decoders,
654
+ additional_decoder_kwargs: Optional[Union[DictConfig, Dict]] = None,
655
+ ):
656
+ if not additional_decoder_kwargs:
657
+ additional_decoder_kwargs = {}
658
+
659
+ if not isinstance(decoders, (List, ListConfig)):
660
+ decoders = [decoders]
661
+ handlers = []
662
+ for decoder_spec in decoders:
663
+ if isinstance(decoder_spec, (Dict, DictConfig)):
664
+ decoder = instantiate(decoder_spec)
665
+ elif isinstance(decoder_spec, str):
666
+ decoder = wds.autodecode.ImageHandler(decoder_spec)
667
+ else:
668
+ raise TypeError(f"{decoder_spec} not a thing for decoders.")
669
+ handlers.append(decoder)
670
+ print(f"Adding decoder {decoder.__class__.__name__} to decoders.")
671
+
672
+ decoder = DecoderWithTimeout(
673
+ handlers,
674
+ partial=additional_decoder_kwargs.pop("partial", True),
675
+ **additional_decoder_kwargs, # todo
676
+ )
677
+ return decoder
678
+
679
+ def create_dataset(urls, is_s3=False, prefetch=None,
680
+ s3_buffer_size=None, s3_ignore_missing_files=True,
681
+ sample_shuffle=1, shard_shuffle=1000, cycle=True,
682
+ split_workers_along_tars=True, decoders="torch"):
683
+ if isinstance(urls, str):
684
+ urls = [urls]
685
+ dp = IterableWrapper(urls)
686
+ dp = dp.custom_shard_expand()
687
+ if is_s3:
688
+ dp = dp.list_files_with_aws_cli().filter(is_tar)
689
+ else:
690
+ dp = dp.list_files(masks="*.tar", recursive=True)
691
+
692
+ dp = ShuffledListDataPipe(list(dp), shuffle=shard_shuffle > 1, cycle=cycle)
693
+
694
+ if split_workers_along_tars:
695
+ dp = dp.sharding_filter()
696
+
697
+ if is_s3:
698
+ # if prefetch is None:
699
+ # s3_buffer_size = int(s3_buffer_size) if s3_buffer_size is not None else None
700
+ # dp = dp.wrapped_load_files_by_s3(
701
+ # buffer_size=s3_buffer_size,
702
+ # ignore_missing_files=s3_ignore_missing_files,
703
+ # )
704
+ dp = dp.download_with_s3_cli(ignore_missing_files=s3_ignore_missing_files)
705
+ else:
706
+ dp = dp.open_files(mode="b")
707
+
708
+ if prefetch is not None:
709
+ dp = dp.custom_prefetch(buffer_size=prefetch)
710
+
711
+ dp = dp.load_from_tar_and_handle_error(handler=error_handler)
712
+ dp = dp.webdataset()
713
+
714
+ if not split_workers_along_tars:
715
+ dp = dp.sharding_filter()
716
+
717
+ dp = dp.map_with_handler(
718
+ stream_reader,
719
+ handler=error_handler,
720
+ called_cls_name="StreamReader",
721
+ )
722
+
723
+ dp = dp.shuffle(buffer_size=sample_shuffle) if sample_shuffle > 1 else dp
724
+
725
+ dp = dp.map_with_handler(
726
+ to_our_format,
727
+ handler=error_handler,
728
+ called_cls_name="ToOurFormat",
729
+ )
730
+
731
+ decoder: DecoderWithTimeout = setup_decoder(decoders) # ,additional_decoder_kwargs)
732
+
733
+ dp = dp.map_with_handler(
734
+ decoder.decode,
735
+ handler=error_handler,
736
+ called_cls_name="Decoder",
737
+ )
738
+
739
+ return dp
740
+
741
+ def create_loader(
742
+ datapipeline: IterDataPipe,
743
+ batch_size: int,
744
+ num_workers: int,
745
+ partial: bool = False,
746
+ collation_fn: Optional[Union[Callable, Dict, DictConfig]] = DictCollator(),
747
+ batched_transforms: Optional[ListConfig] = None,
748
+ loader_kwargs: Optional[Union[Dict, DictConfig]] = None,
749
+ ) -> DataLoader:
750
+ if not loader_kwargs:
751
+ loader_kwargs = {}
752
+
753
+ loader_kwargs.pop("shuffle", None)
754
+ if not batched_transforms:
755
+ batched_transforms = []
756
+
757
+ print("#" * 100)
758
+ print("Building dataloader with the following parameters")
759
+ print(f"batch_size: {batch_size}, num_workers: {num_workers}")
760
+ for key in loader_kwargs:
761
+ print(key, ": ", loader_kwargs[key])
762
+ print("#" * 100)
763
+
764
+ datapipeline = datapipeline.batch(batch_size, drop_last=not partial)
765
+
766
+ if isinstance(collation_fn, (Dict, DictConfig)):
767
+ collation_fn = instantiate(collation_fn)
768
+ datapipeline = datapipeline.collate(collate_fn=collation_fn)
769
+ loader = DataLoader(
770
+ datapipeline, batch_size=None, num_workers=num_workers, **loader_kwargs
771
+ )
772
+ return loader
fMRI-foundation-model-old/fMRI-MAE-mamba/mamba.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
fMRI-foundation-model-old/fMRI-MAE-mamba/mindeye_config.yaml ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Model Config
2
+ model_name: "pretrain_framepatchsize4"
3
+ mae_model_name: "framepatchsize4_bs8_300ep"
4
+
5
+ # Training Configs
6
+ global_batch_size: 32
7
+ mixed_precision: "fp16"
8
+ num_epochs: 12
9
+ seed: 42
10
+ max_lr: 3.0e-4 # Keep the x.0 else will be converted to string
11
+ multi_subject: False
12
+ multisubject_ckpt: None
13
+
14
+ # Saving progress
15
+ ckpt_saving: False
16
+ ckpt_interval: 99
17
+ resume_from_ckpt: False
18
+ wandb_log: True
19
+
20
+ # Model Config
21
+ in_dim: 370176
22
+ hidden_dim: 512
23
+ drop: .15
24
+ mixup_pct: 0.33
25
+
26
+ # Data Config
27
+ nsd_wds_path: "/weka/proj-fmri/shared/mindeyev2_dataset/wds"
28
+ nsd_raw_path: "/weka/proj-fmri/shared/mindeyev2_dataset"
29
+ nsd_image_path: "/weka/proj-fmri/shared/mindeyev2_dataset"
30
+ num_sessions: 40
fMRI-foundation-model-old/fMRI-MAE-mamba/mindeye_models.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+
6
+ class MindEyeModule(nn.Module):
7
+ def __init__(self):
8
+ super().__init__()
9
+ def forward(self, x):
10
+ return x
11
+ class RidgeRegression(nn.Module):
12
+ # make sure to add weight_decay when initializing optimizer
13
+ def __init__(self, input_sizes, out_features, seq_len=1):
14
+ super().__init__()
15
+ self.out_features = out_features
16
+ self.linears = torch.nn.ModuleList([
17
+ torch.nn.Linear(input_size, out_features) for input_size in input_sizes
18
+ ])
19
+ self.seq_len = seq_len
20
+ def forward(self, x, subj_idx):
21
+ out = torch.cat([self.linears[subj_idx](x[:,seq]).unsqueeze(1) for seq in range(self.seq_len)], dim=1)
22
+ return out
23
+ class BrainNetwork(nn.Module):
24
+ def __init__(self, h=1024, in_dim=15724, out_dim=768, seq_len=1, n_blocks=4, drop=.15, clip_size=768, clip_scale=1):
25
+ super().__init__()
26
+ self.seq_len = seq_len
27
+ self.h = h
28
+ self.clip_size = clip_size
29
+ self.clip_scale = clip_scale
30
+ self.mixer_blocks1 = nn.ModuleList([
31
+ self.mixer_block1(h, drop) for _ in range(n_blocks)
32
+ ])
33
+ self.mixer_blocks2 = nn.ModuleList([
34
+ self.mixer_block2(seq_len, drop) for _ in range(n_blocks)
35
+ ])
36
+
37
+ # Output linear layer
38
+ self.backbone_linear = nn.Linear(h * seq_len, out_dim, bias=True)
39
+ self.clip_proj = self.projector(clip_size, clip_size, h=clip_size)
40
+
41
+ def projector(self, in_dim, out_dim, h=2048):
42
+ return nn.Sequential(
43
+ nn.LayerNorm(in_dim),
44
+ nn.GELU(),
45
+ nn.Linear(in_dim, h),
46
+ nn.LayerNorm(h),
47
+ nn.GELU(),
48
+ nn.Linear(h, h),
49
+ nn.LayerNorm(h),
50
+ nn.GELU(),
51
+ nn.Linear(h, out_dim)
52
+ )
53
+
54
+ def mlp(self, in_dim, out_dim, drop):
55
+ return nn.Sequential(
56
+ nn.Linear(in_dim, out_dim),
57
+ nn.GELU(),
58
+ nn.Dropout(drop),
59
+ nn.Linear(out_dim, out_dim),
60
+ )
61
+
62
+ def mixer_block1(self, h, drop):
63
+ return nn.Sequential(
64
+ nn.LayerNorm(h),
65
+ self.mlp(h, h, drop), # Token mixing
66
+ )
67
+
68
+ def mixer_block2(self, seq_len, drop):
69
+ return nn.Sequential(
70
+ nn.LayerNorm(seq_len),
71
+ self.mlp(seq_len, seq_len, drop) # Channel mixing
72
+ )
73
+
74
+ def forward(self, x):
75
+ # make empty tensors
76
+ c = torch.Tensor([0.])
77
+
78
+ # Mixer blocks
79
+ residual1 = x
80
+ residual2 = x.permute(0,2,1)
81
+ for block1, block2 in zip(self.mixer_blocks1,self.mixer_blocks2):
82
+ x = block1(x) + residual1
83
+ residual1 = x
84
+ x = x.permute(0,2,1)
85
+
86
+ x = block2(x) + residual2
87
+ residual2 = x
88
+ x = x.permute(0,2,1)
89
+
90
+ x = x.reshape(x.size(0), -1)
91
+ backbone = self.backbone_linear(x).reshape(len(x), -1, self.clip_size)
92
+ if self.clip_scale>0:
93
+ c = self.clip_proj(backbone)
94
+
95
+ return backbone, c
96
+
97
+
98
+ import open_clip
99
+ import kornia
100
+ class FrozenOpenCLIPImageEmbedder(nn.Module):
101
+ """
102
+ Uses the OpenCLIP vision transformer encoder for images
103
+ """
104
+
105
+ def __init__(
106
+ self,
107
+ arch="ViT-H-14",
108
+ version="laion2b_s32b_b79k",
109
+ device="cuda",
110
+ init_device="cpu",
111
+ max_length=77,
112
+ freeze=True,
113
+ antialias=True,
114
+ ucg_rate=0.0,
115
+ unsqueeze_dim=False,
116
+ repeat_to_max_len=False,
117
+ num_image_crops=0,
118
+ output_tokens=False,
119
+ l2_norm_tokens=False,
120
+ only_tokens=False,
121
+ cache_dir: Optional[str] = None,
122
+ ):
123
+ super().__init__()
124
+ model, _, _ = open_clip.create_model_and_transforms(
125
+ arch,
126
+ device=torch.device(init_device),
127
+ pretrained=version,
128
+ cache_dir=cache_dir,
129
+ )
130
+ del model.transformer
131
+ self.model = model
132
+ self.max_crops = num_image_crops
133
+ self.pad_to_max_len = self.max_crops > 0
134
+ self.repeat_to_max_len = repeat_to_max_len and (not self.pad_to_max_len)
135
+ self.device = device
136
+ self.max_length = max_length
137
+ if freeze:
138
+ self.freeze()
139
+
140
+ self.antialias = antialias
141
+
142
+ self.register_buffer(
143
+ "mean", torch.Tensor([0.48145466, 0.4578275, 0.40821073]), persistent=False
144
+ )
145
+ self.register_buffer(
146
+ "std", torch.Tensor([0.26862954, 0.26130258, 0.27577711]), persistent=False
147
+ )
148
+ self.ucg_rate = ucg_rate
149
+ self.unsqueeze_dim = unsqueeze_dim
150
+ self.stored_batch = None
151
+ self.model.visual.output_tokens = output_tokens
152
+ self.output_tokens = output_tokens
153
+ if only_tokens:
154
+ assert output_tokens
155
+ self.only_tokens = only_tokens
156
+ self.l2_norm_tokens = l2_norm_tokens
157
+ if l2_norm_tokens:
158
+ assert output_tokens
159
+
160
+ def preprocess(self, x):
161
+ # normalize to [0,1]
162
+ x = kornia.geometry.resize(
163
+ x,
164
+ (224, 224),
165
+ interpolation="bicubic",
166
+ align_corners=True,
167
+ antialias=self.antialias,
168
+ )
169
+ x = (x + 1.0) / 2.0
170
+ # renormalize according to clip
171
+ x = kornia.enhance.normalize(x, self.mean, self.std)
172
+ return x
173
+
174
+ def freeze(self):
175
+ self.model = self.model.eval()
176
+ for param in self.parameters():
177
+ param.requires_grad = False
178
+
179
+ def forward(self, image, no_dropout=False):
180
+ z = self.encode_with_vision_transformer(image)
181
+ tokens = None
182
+ if self.output_tokens:
183
+ z, tokens = z[0], z[1]
184
+ z = z.to(image.dtype)
185
+ if self.ucg_rate > 0.0 and not no_dropout and not (self.max_crops > 0):
186
+ z = (
187
+ torch.bernoulli(
188
+ (1.0 - self.ucg_rate) * torch.ones(z.shape[0], device=z.device)
189
+ )[:, None]
190
+ * z
191
+ )
192
+ if tokens is not None:
193
+ tokens = (
194
+ expand_dims_like(
195
+ torch.bernoulli(
196
+ (1.0 - self.ucg_rate)
197
+ * torch.ones(tokens.shape[0], device=tokens.device)
198
+ ),
199
+ tokens,
200
+ )
201
+ * tokens
202
+ )
203
+ if self.unsqueeze_dim:
204
+ z = z[:, None, :]
205
+ if self.output_tokens:
206
+ assert not self.repeat_to_max_len
207
+ assert not self.pad_to_max_len
208
+ if self.only_tokens:
209
+ return tokens
210
+ return tokens, z
211
+ if self.repeat_to_max_len:
212
+ if z.dim() == 2:
213
+ z_ = z[:, None, :]
214
+ else:
215
+ z_ = z
216
+ return repeat(z_, "b 1 d -> b n d", n=self.max_length), z
217
+ elif self.pad_to_max_len:
218
+ assert z.dim() == 3
219
+ z_pad = torch.cat(
220
+ (
221
+ z,
222
+ torch.zeros(
223
+ z.shape[0],
224
+ self.max_length - z.shape[1],
225
+ z.shape[2],
226
+ device=z.device,
227
+ ),
228
+ ),
229
+ 1,
230
+ )
231
+ return z_pad, z_pad[:, 0, ...]
232
+ return z
233
+
234
+ def encode_with_vision_transformer(self, img):
235
+ # if self.max_crops > 0:
236
+ # img = self.preprocess_by_cropping(img)
237
+ if img.dim() == 5:
238
+ assert self.max_crops == img.shape[1]
239
+ img = rearrange(img, "b n c h w -> (b n) c h w")
240
+ img = self.preprocess(img)
241
+ if not self.output_tokens:
242
+ assert not self.model.visual.output_tokens
243
+ x = self.model.visual(img)
244
+ tokens = None
245
+ else:
246
+ assert self.model.visual.output_tokens
247
+ x, tokens = self.model.visual(img)
248
+ if self.l2_norm_tokens:
249
+ token_shape = tokens.shape
250
+ tokens = tokens.flatten(1)
251
+ tokens = torch.nn.functional.normalize(tokens, dim=-1)
252
+ tokens = (tokens - .0002) / .0015
253
+ tokens = tokens.view(token_shape)
254
+ tokens = (tokens * 1.0957) + .1598
255
+ if self.max_crops > 0:
256
+ x = rearrange(x, "(b n) d -> b n d", n=self.max_crops)
257
+ # drop out between 0 and all along the sequence axis
258
+ x = (
259
+ torch.bernoulli(
260
+ (1.0 - self.ucg_rate)
261
+ * torch.ones(x.shape[0], x.shape[1], 1, device=x.device)
262
+ )
263
+ * x
264
+ )
265
+ if tokens is not None:
266
+ tokens = rearrange(tokens, "(b n) t d -> b t (n d)", n=self.max_crops)
267
+ logpy.warning(
268
+ f"You are running very experimental token-concat in {self.__class__.__name__}. "
269
+ f"Check what you are doing, and then remove this message."
270
+ )
271
+ if self.output_tokens:
272
+ return x, tokens
273
+ return x
274
+
275
+ def encode(self, text):
276
+ return self(text)
fMRI-foundation-model-old/fMRI-MAE-mamba/models.py ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2015-present, Facebook, Inc.
2
+ # All rights reserved.
3
+ import os
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+ from functools import partial
9
+ from torch import Tensor
10
+ from typing import Optional
11
+ import torch.utils.checkpoint as checkpoint
12
+
13
+ from einops import rearrange
14
+ from einops.layers.torch import Rearrange
15
+ import numpy as np
16
+ from timm.models.vision_transformer import _cfg
17
+ from timm.models.layers import trunc_normal_
18
+
19
+ from timm.models.layers import DropPath, to_2tuple
20
+ from timm.models.registry import register_model
21
+ from timm.models.vision_transformer import _load_weights
22
+
23
+ import math
24
+
25
+ from mamba_ssm.modules.mamba_simple import Mamba
26
+
27
+ try:
28
+ from mamba_ssm.ops.triton.layernorm import RMSNorm, layer_norm_fn, rms_norm_fn
29
+ except ImportError:
30
+ RMSNorm, layer_norm_fn, rms_norm_fn = None, None, None
31
+
32
+ def posemb_sincos_4d(patches, temperature=10000, dtype=torch.float32):
33
+ _, f, d, h, w, dim, device, dtype = (*patches.shape, patches.device, patches.dtype)
34
+
35
+ z, y, x, t = torch.meshgrid(
36
+ torch.arange(f, device=device),
37
+ torch.arange(d, device=device),
38
+ torch.arange(h, device=device),
39
+ torch.arange(w, device=device),
40
+ indexing="ij",
41
+ )
42
+
43
+ fourier_dim = dim // 8
44
+
45
+ omega = torch.arange(fourier_dim, device=device) / (fourier_dim - 1)
46
+ omega = 1.0 / (temperature**omega)
47
+
48
+ z, y, x, t = [v.flatten()[:, None] * omega[None, :] for v in [z, y, x, t]]
49
+
50
+ pe = torch.cat(
51
+ (z.sin(), z.cos(), y.sin(), y.cos(), x.sin(), x.cos(), t.sin(), t.cos()), dim=1
52
+ )
53
+ pe = F.pad(pe, (0, dim - (fourier_dim * 8)))
54
+ return pe.type(dtype)
55
+
56
+
57
+
58
+ class Block(nn.Module):
59
+ def __init__(
60
+ self, dim, mixer_cls, norm_cls=nn.LayerNorm, fused_add_norm=False, residual_in_fp32=False,drop_path=0.,
61
+ ):
62
+ """
63
+ Simple block wrapping a mixer class with LayerNorm/RMSNorm and residual connection"
64
+
65
+ This Block has a slightly different structure compared to a regular
66
+ prenorm Transformer block.
67
+ The standard block is: LN -> MHA/MLP -> Add.
68
+ [Ref: https://arxiv.org/abs/2002.04745]
69
+ Here we have: Add -> LN -> Mixer, returning both
70
+ the hidden_states (output of the mixer) and the residual.
71
+ This is purely for performance reasons, as we can fuse add and LayerNorm.
72
+ The residual needs to be provided (except for the very first block).
73
+ """
74
+ super().__init__()
75
+ self.residual_in_fp32 = residual_in_fp32
76
+ self.fused_add_norm = fused_add_norm
77
+ self.mixer = mixer_cls(dim)
78
+ self.norm = norm_cls(dim)
79
+ self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
80
+ if self.fused_add_norm:
81
+ assert RMSNorm is not None, "RMSNorm import fails"
82
+ assert isinstance(
83
+ self.norm, (nn.LayerNorm, RMSNorm)
84
+ ), "Only LayerNorm and RMSNorm are supported for fused_add_norm"
85
+
86
+ def forward(
87
+ self, hidden_states: Tensor, residual: Optional[Tensor] = None, inference_params=None,
88
+ use_checkpoint=False
89
+ ):
90
+ r"""Pass the input through the encoder layer.
91
+
92
+ Args:
93
+ hidden_states: the sequence to the encoder layer (required).
94
+ residual: hidden_states = Mixer(LN(residual))
95
+ """
96
+ if not self.fused_add_norm:
97
+ residual = (residual + self.drop_path(hidden_states)) if residual is not None else hidden_states
98
+ hidden_states = self.norm(residual.to(dtype=self.norm.weight.dtype))
99
+ if self.residual_in_fp32:
100
+ residual = residual.to(torch.float32)
101
+ else:
102
+ fused_add_norm_fn = rms_norm_fn if isinstance(self.norm, RMSNorm) else layer_norm_fn
103
+ hidden_states, residual = fused_add_norm_fn(
104
+ hidden_states if residual is None else self.drop_path(hidden_states),
105
+ self.norm.weight,
106
+ self.norm.bias,
107
+ residual=residual,
108
+ prenorm=True,
109
+ residual_in_fp32=self.residual_in_fp32,
110
+ eps=self.norm.eps,
111
+ )
112
+ if use_checkpoint:
113
+ hidden_states = checkpoint.checkpoint(self.mixer, hidden_states, inference_params)
114
+ else:
115
+ hidden_states = self.mixer(hidden_states, inference_params=inference_params)
116
+ return hidden_states, residual
117
+
118
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
119
+ return self.mixer.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs)
120
+
121
+
122
+ def create_block(
123
+ d_model,
124
+ ssm_cfg=None,
125
+ norm_epsilon=1e-5,
126
+ drop_path=0.,
127
+ rms_norm=True,
128
+ residual_in_fp32=True,
129
+ fused_add_norm=True,
130
+ layer_idx=None,
131
+ bimamba=True,
132
+ device=None,
133
+ dtype=None,
134
+ ):
135
+ import inspect
136
+ inspect.getsourcefile(Mamba)
137
+ factory_kwargs = {"device": device, "dtype": dtype}
138
+ if ssm_cfg is None:
139
+ ssm_cfg = {}
140
+ mixer_cls = partial(Mamba, layer_idx=layer_idx, bimamba=bimamba, **ssm_cfg, **factory_kwargs)
141
+ norm_cls = partial(nn.LayerNorm if not rms_norm else RMSNorm, eps=norm_epsilon)
142
+ block = Block(
143
+ d_model,
144
+ mixer_cls,
145
+ norm_cls=norm_cls,
146
+ drop_path=drop_path,
147
+ fused_add_norm=fused_add_norm,
148
+ residual_in_fp32=residual_in_fp32,
149
+ )
150
+ block.layer_idx = layer_idx
151
+ return block
152
+
153
+
154
+ # https://github.com/huggingface/transformers/blob/c28d04e9e252a1a099944e325685f14d242ecdcd/src/transformers/models/gpt2/modeling_gpt2.py#L454
155
+ def _init_weights(
156
+ module,
157
+ n_layer,
158
+ initializer_range=0.02, # Now only used for embedding layer.
159
+ rescale_prenorm_residual=True,
160
+ n_residuals_per_layer=1, # Change to 2 if we have MLP
161
+ ):
162
+ if isinstance(module, nn.Linear):
163
+ if module.bias is not None:
164
+ if not getattr(module.bias, "_no_reinit", False):
165
+ nn.init.zeros_(module.bias)
166
+ elif isinstance(module, nn.Embedding):
167
+ nn.init.normal_(module.weight, std=initializer_range)
168
+
169
+ if rescale_prenorm_residual:
170
+ # Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
171
+ # > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
172
+ # > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
173
+ # > -- GPT-2 :: https://openai.com/blog/better-language-models/
174
+ #
175
+ # Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
176
+ for name, p in module.named_parameters():
177
+ if name in ["out_proj.weight", "fc2.weight"]:
178
+ # Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
179
+ # Following Pytorch init, except scale by 1/sqrt(2 * n_layer)
180
+ # We need to reinit p since this code could be called multiple times
181
+ # Having just p *= scale would repeatedly scale it down
182
+ nn.init.kaiming_uniform_(p, a=math.sqrt(5))
183
+ with torch.no_grad():
184
+ p /= math.sqrt(n_residuals_per_layer * n_layer)
185
+
186
+
187
+ def segm_init_weights(m):
188
+ if isinstance(m, nn.Linear):
189
+ trunc_normal_(m.weight, std=0.02)
190
+ if isinstance(m, nn.Linear) and m.bias is not None:
191
+ nn.init.constant_(m.bias, 0)
192
+ elif isinstance(m, nn.LayerNorm):
193
+ nn.init.constant_(m.bias, 0)
194
+ nn.init.constant_(m.weight, 1.0)
195
+
196
+
197
+ class PatchEmbed(nn.Module):
198
+ """ Image to Patch Embedding
199
+ """
200
+ def __init__(self, img_size=224, patch_size=16, kernel_size=1, in_chans=3, embed_dim=768):
201
+ super().__init__()
202
+ img_size = to_2tuple(img_size)
203
+ patch_size = to_2tuple(patch_size)
204
+ num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0])
205
+ self.img_size = img_size
206
+ self.patch_size = patch_size
207
+ self.num_patches = num_patches
208
+ self.tubelet_size = kernel_size
209
+
210
+ self.proj = nn.Conv3d(
211
+ in_chans, embed_dim,
212
+ kernel_size=(kernel_size, patch_size[0], patch_size[1]),
213
+ stride=(kernel_size, patch_size[0], patch_size[1])
214
+ )
215
+
216
+ def forward(self, x):
217
+ x = self.proj(x)
218
+ return x
219
+
220
+
221
+ # sin-cos position encoding
222
+ # https://github.com/jadore801120/attention-is-all-you-need-pytorch/blob/master/transformer/Models.py#L31
223
+ def get_sinusoid_encoding_table(n_position, d_hid):
224
+ ''' Sinusoid position encoding table '''
225
+ # TODO: make it with torch instead of numpy
226
+ def get_position_angle_vec(position):
227
+ return [position / np.power(10000, 2 * (hid_j // 2) / d_hid) for hid_j in range(d_hid)]
228
+
229
+ sinusoid_table = np.array([get_position_angle_vec(pos_i) for pos_i in range(n_position)])
230
+ sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2]) # dim 2i
231
+ sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) # dim 2i+1
232
+
233
+ return torch.tensor(sinusoid_table, dtype=torch.float, requires_grad=False).unsqueeze(0)
234
+
235
+
236
+ class VisionMamba(nn.Module):
237
+ def __init__(
238
+ self,
239
+ img_size=224,
240
+ patch_size=16,
241
+ frame_patch_size=4,
242
+ depth=24,
243
+ embed_dim=192,
244
+ channels=3,
245
+ drop_rate=0.,
246
+ drop_path_rate=0.1,
247
+ ssm_cfg=None,
248
+ norm_epsilon=1e-5,
249
+ initializer_cfg=None,
250
+ fused_add_norm=True,
251
+ rms_norm=True,
252
+ residual_in_fp32=True,
253
+ bimamba=True,
254
+ # video
255
+ kernel_size=1,
256
+ num_frames=8,
257
+ use_rope_emb=False,
258
+ device=None,
259
+ dtype=None,
260
+ # checkpoint
261
+ use_checkpoint=False,
262
+ checkpoint_num=0,
263
+ ):
264
+ factory_kwargs = {"device": device, "dtype": dtype} # follow MambaLMHeadModel
265
+ super().__init__()
266
+ self.residual_in_fp32 = residual_in_fp32
267
+ self.fused_add_norm = fused_add_norm
268
+ self.use_checkpoint = use_checkpoint
269
+ self.checkpoint_num = checkpoint_num
270
+ print(f'Use checkpoint: {use_checkpoint}')
271
+ print(f'Checkpoint number: {checkpoint_num}')
272
+
273
+
274
+ # pretrain parameters
275
+ self.d_model = self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models
276
+ patch_depth, patch_height, patch_width = patch_size
277
+ self.patchify = Rearrange(
278
+ "b c (f pf) (d pd) (h ph) (w pw) -> b f d h w (pd ph pw pf c)",
279
+ pd=patch_depth,
280
+ ph=patch_height,
281
+ pw=patch_width,
282
+ pf=frame_patch_size,
283
+ )
284
+ # self.patch_embed = PatchEmbed(
285
+ # img_size=img_size, patch_size=patch_size,
286
+ # kernel_size=kernel_size,
287
+ # in_chans=channels, embed_dim=embed_dim
288
+ # )
289
+ self.patch_dim = channels * patch_depth * patch_height * patch_width * frame_patch_size
290
+ self.patch_to_emb = nn.Sequential(
291
+ nn.LayerNorm(self.patch_dim),
292
+ nn.Linear(self.patch_dim, self.embed_dim),
293
+ nn.LayerNorm(self.embed_dim),
294
+ )
295
+
296
+ self.encoder_proj = nn.Sequential(
297
+ nn.LayerNorm(self.embed_dim),
298
+ nn.GELU(),
299
+ nn.Linear(self.embed_dim, self.embed_dim),
300
+ )
301
+
302
+ self.decoder_proj = nn.Sequential(
303
+ nn.LayerNorm(self.embed_dim),
304
+ nn.GELU(),
305
+ nn.Linear(self.embed_dim, self.patch_dim),
306
+ )
307
+
308
+ self.cls_token = nn.Parameter(torch.randn(1, 1, self.embed_dim))
309
+ image_depth, image_height, image_width = img_size
310
+ self.use_rope_emb = use_rope_emb
311
+ self.posemb_sincos_4d = posemb_sincos_4d(
312
+ torch.zeros(
313
+ 1,
314
+ num_frames,
315
+ image_depth // patch_depth,
316
+ image_height // patch_height,
317
+ image_width // patch_width,
318
+ self.embed_dim,
319
+ )
320
+ ).to(device)
321
+ print ("posemb_sincos_4d", self.posemb_sincos_4d.shape)
322
+ self.encoder_to_decoder = nn.Linear(self.embed_dim, self.embed_dim, bias=False)
323
+ self.mask_token = nn.Parameter(torch.zeros(1, 1, self.embed_dim))
324
+
325
+ # self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, self.embed_dim))
326
+ # self.temporal_pos_embedding = nn.Parameter(torch.zeros(1, num_frames // kernel_size, embed_dim))
327
+ self.pos_drop = nn.Dropout(p=drop_rate)
328
+
329
+ dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
330
+ inter_dpr = [0.0] + dpr
331
+ self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
332
+ # mamba blocks
333
+ print ("depth", depth, "d_model", embed_dim, "rms_norm", rms_norm, "residual_in_fp32", residual_in_fp32, "fused_add_norm", fused_add_norm, "bimamba", bimamba, "ssm_cfg", ssm_cfg)
334
+ self.encoder_layers = nn.ModuleList(
335
+ [
336
+ create_block(
337
+ embed_dim,
338
+ ssm_cfg=ssm_cfg,
339
+ norm_epsilon=norm_epsilon,
340
+ rms_norm=rms_norm,
341
+ residual_in_fp32=residual_in_fp32,
342
+ fused_add_norm=fused_add_norm,
343
+ layer_idx=i,
344
+ bimamba=bimamba,
345
+ drop_path=inter_dpr[i],
346
+ **factory_kwargs,
347
+ )
348
+ for i in range(depth)
349
+ ]
350
+ )
351
+ self.decoder_layers = nn.ModuleList(
352
+ [
353
+ create_block(
354
+ embed_dim,
355
+ ssm_cfg=ssm_cfg,
356
+ norm_epsilon=norm_epsilon,
357
+ rms_norm=rms_norm,
358
+ residual_in_fp32=residual_in_fp32,
359
+ fused_add_norm=fused_add_norm,
360
+ layer_idx=i,
361
+ bimamba=bimamba,
362
+ drop_path=inter_dpr[i],
363
+ **factory_kwargs,
364
+ )
365
+ for i in range(depth)
366
+ ]
367
+ )
368
+
369
+ # output head
370
+ self.norm_f = (nn.LayerNorm if not rms_norm else RMSNorm)(embed_dim, eps=norm_epsilon, **factory_kwargs)
371
+
372
+
373
+ # original init
374
+ self.apply(segm_init_weights)
375
+ # trunc_normal_(self.pos_embed, std=.02)
376
+
377
+ # mamba init
378
+ self.apply(
379
+ partial(
380
+ _init_weights,
381
+ n_layer=depth,
382
+ **(initializer_cfg if initializer_cfg is not None else {}),
383
+ )
384
+ )
385
+
386
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
387
+ return {
388
+ i: layer.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs)
389
+ for i, layer in enumerate(self.layers)
390
+ }
391
+
392
+ @torch.jit.ignore
393
+ def no_weight_decay(self):
394
+ return {"pos_embed", "cls_token", "temporal_pos_embedding"}
395
+
396
+ def get_num_layers(self):
397
+ return (len(self.encoder_layers), len(self.decoder_layers))
398
+
399
+ @torch.jit.ignore()
400
+ def load_pretrained(self, checkpoint_path, prefix=""):
401
+ _load_weights(self, checkpoint_path, prefix)
402
+
403
+ def forward(self, x, inference_params=None, encoder_mask=None, decoder_mask=None, verbose=False):
404
+ # x = self.forward_features(x, inference_params, encoder_mask, decoder_mask, verbose)
405
+ # # x = self.head(self.head_drop(x))
406
+
407
+ # ENCODER
408
+ if decoder_mask is None:
409
+ if verbose: print(x.shape)
410
+ x = self.patchify(x)
411
+ if verbose: print("patched", x.shape)
412
+ x = self.patch_to_emb(x)
413
+ if verbose: print("patched_emb", x.shape)
414
+ x = rearrange(x, "b ... d -> b (...) d")
415
+ if verbose: print("reshaped", x.shape)
416
+ if not self.use_rope_emb:
417
+ if verbose: print("pe", self.posemb_sincos_4d.shape)
418
+ x = x + self.posemb_sincos_4d
419
+ if verbose: print("x", x.shape)
420
+ x = x[:, encoder_mask]
421
+ # if self.use_cls_token:
422
+ cls_tokens = self.cls_token.expand(len(x), -1, -1)
423
+ x = torch.cat((cls_tokens, x), dim=1)
424
+ if verbose: print("masked", x.shape)
425
+
426
+ # Mamba below
427
+ residual = None
428
+ hidden_states = x
429
+ for idx, layer in enumerate(self.encoder_layers):
430
+ if self.use_checkpoint and idx < self.checkpoint_num:
431
+ hidden_states, residual = layer(
432
+ hidden_states, residual, inference_params=inference_params,
433
+ use_checkpoint=True
434
+ )
435
+ else:
436
+ hidden_states, residual = layer(
437
+ hidden_states, residual, inference_params=inference_params
438
+ )
439
+ # assuming fused_add_norm is True
440
+ fused_add_norm_fn = rms_norm_fn if isinstance(self.norm_f, RMSNorm) else layer_norm_fn
441
+ hidden_states = fused_add_norm_fn(
442
+ self.drop_path(hidden_states),
443
+ self.norm_f.weight,
444
+ self.norm_f.bias,
445
+ eps=self.norm_f.eps,
446
+ residual=residual,
447
+ prenorm=False, # Set prenorm=False here since we don't need the residual
448
+ residual_in_fp32=self.residual_in_fp32,
449
+ )
450
+ else: # DECODER
451
+ if verbose: print(x.shape)
452
+ x = self.encoder_to_decoder(x)
453
+ B, _, _ = x.shape
454
+ N = decoder_mask.sum()
455
+ mask = None
456
+ if not self.use_rope_emb:
457
+ pos_embed = self.posemb_sincos_4d
458
+ if verbose: print("pe", pos_embed.shape)
459
+ pos_emd_encoder = pos_embed[encoder_mask]
460
+ pos_emd_decoder = pos_embed[decoder_mask]
461
+ if verbose: print("pos_emd_encoder", pos_emd_encoder.shape)
462
+ if verbose: print("pos_emd_decoder", pos_emd_decoder.shape)
463
+ # if self.use_cls_token:
464
+ cls_tokens = x[:,:1,:]
465
+ x = x[:,1:,:]
466
+ x = torch.cat([x + pos_emd_encoder,
467
+ self.mask_token.repeat(B, N, 1) + pos_emd_decoder],
468
+ dim=1)
469
+ # if self.use_cls_token:
470
+ x = torch.cat([cls_tokens, x], dim=1)
471
+ else:
472
+ mask = torch.cat((torch.where(encoder_mask)[0], torch.where(decoder_mask)[0]))
473
+ # No abs positional embeddings for RoPE
474
+ x = torch.cat([x,self.mask_token.repeat(B, N-1, 1)],dim=1) # N-1 due to cls token
475
+
476
+ # Mamba below
477
+ residual = None
478
+ hidden_states = x
479
+ for idx, layer in enumerate(self.decoder_layers):
480
+ if self.use_checkpoint and idx < self.checkpoint_num:
481
+ hidden_states, residual = layer(
482
+ hidden_states, residual, inference_params=inference_params,
483
+ use_checkpoint=True
484
+ )
485
+ else:
486
+ hidden_states, residual = layer(
487
+ hidden_states, residual, inference_params=inference_params
488
+ )
489
+ # assuming fused_add_norm is True
490
+ fused_add_norm_fn = rms_norm_fn if isinstance(self.norm_f, RMSNorm) else layer_norm_fn
491
+ hidden_states = fused_add_norm_fn(
492
+ self.drop_path(hidden_states),
493
+ self.norm_f.weight,
494
+ self.norm_f.bias,
495
+ eps=self.norm_f.eps,
496
+ residual=residual,
497
+ prenorm=False, # Set prenorm=False here since we don't need the residual
498
+ residual_in_fp32=self.residual_in_fp32,
499
+ )
500
+
501
+ if decoder_mask is None:
502
+ return self.encoder_proj(hidden_states)
503
+ else:
504
+ return self.decoder_proj(hidden_states)
fMRI-foundation-model-old/fMRI-MAE-mamba/utils.py ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from io import BytesIO
2
+ import os
3
+ import random
4
+ import numpy as np
5
+ import torch
6
+ from einops import rearrange
7
+ from nilearn import plotting
8
+ from PIL import Image
9
+ from skimage import filters
10
+ from torchvision import transforms
11
+ import nibabel as nib
12
+ from nilearn import plotting
13
+ import matplotlib.pyplot as plt
14
+ import re
15
+
16
+ def my_split_by_node(urls): return urls
17
+
18
+ def is_interactive():
19
+ import __main__ as main
20
+
21
+ return not hasattr(main, "__file__")
22
+
23
+ def my_split_by_node(urls): return urls
24
+
25
+ def seed_everything(seed=0, cudnn_deterministic=True):
26
+ random.seed(seed)
27
+ os.environ['PYTHONHASHSEED'] = str(seed)
28
+ np.random.seed(seed)
29
+ torch.manual_seed(seed)
30
+ torch.cuda.manual_seed(seed)
31
+ torch.cuda.manual_seed_all(seed)
32
+
33
+
34
+ def grayscale_decoder(image_data):
35
+ return np.array(Image.open(BytesIO(image_data))).astype(np.float32) / 65535
36
+
37
+
38
+ def numpy_decoder(npy_data):
39
+ return np.load(BytesIO(npy_data))
40
+
41
+
42
+ def reshape_to_2d(tensor):
43
+ if tensor.ndim == 5:
44
+ tensor = tensor[0]
45
+ assert tensor.ndim == 4
46
+ return rearrange(tensor, "b h w c -> (b h) (c w)")
47
+
48
+
49
+ def reshape_to_original(tensor_2d, h=64, w=64, c=48):
50
+ # print(tensor_2d.shape) # torch.Size([1, 256, 3072])
51
+ return rearrange(tensor_2d, "(tr h) (c w) -> tr h w c", h=h, w=w, c=c)
52
+
53
+
54
+ def plot_numpy_nii(image):
55
+ while image.ndim > 3:
56
+ image = image[0]
57
+ nii = nib.Nifti1Image(image.astype(np.float32), np.eye(4)) # noqa
58
+ plotting.plot_epi(nii, cmap="gray")
59
+
60
+
61
+ def threshold_based_masking(org_images):
62
+ thresholds = filters.threshold_multiotsu(org_images.numpy(), classes=3)
63
+ brain_segmentation = org_images > thresholds.min()
64
+ return brain_segmentation
65
+
66
+
67
+ def get_brain_pos_patches(
68
+ func,
69
+ patch_depth=8,
70
+ patch_height=8,
71
+ patch_width=8,
72
+ frame_patch_size=1,
73
+ masking_strategy="conservative",
74
+ ):
75
+ _, _, depth = func.shape
76
+ if masking_strategy == "conservative":
77
+ func = func.sum(axis=(-1), keepdim=True).repeat(1, 1, depth)
78
+ else:
79
+ raise Exception("Not implemented other masking strategies than conservative.")
80
+
81
+ return func
82
+
83
+
84
+ class DataPrepper:
85
+ def __init__(
86
+ self,
87
+ num_frames=4,
88
+ masking_strategy="MNI",
89
+ patch_depth=8,
90
+ patch_height=8,
91
+ patch_width=8,
92
+ frame_patch_size=1,
93
+ ):
94
+ self.num_frames = num_frames
95
+ self.masking_strategy = masking_strategy
96
+ self.patch_depth = 8
97
+ self.patch_height = 8
98
+ self.patch_width = 8
99
+ self.frame_patch_size = 1
100
+
101
+ def __call__(self, func):
102
+ start_timepoint = np.random.choice(np.arange(func.shape[1] - self.num_frames))
103
+ timepoints = np.arange(start_timepoint, start_timepoint + self.num_frames)
104
+
105
+ func = func[:,timepoints]
106
+
107
+ if self.masking_strategy=="MNI":
108
+ return func, None
109
+
110
+ brain_segmentation = threshold_based_masking(func.mean(1))
111
+ pos_patches = None
112
+ for brain in brain_segmentation:
113
+ output = get_brain_pos_patches(
114
+ brain,
115
+ patch_depth=self.patch_depth,
116
+ patch_height=self.patch_height,
117
+ patch_width=self.patch_width,
118
+ frame_patch_size=self.frame_patch_size,
119
+ masking_strategy=self.masking_strategy,
120
+ )
121
+ if pos_patches is None:
122
+ pos_patches = output[None]
123
+ else:
124
+ pos_patches = torch.vstack((pos_patches, output[None]))
125
+ return func, pos_patches
126
+
127
+
128
+ def plot_slices(unpatches):
129
+ if unpatches.ndim == 5:
130
+ unpatches = unpatches[0]
131
+ return transforms.ToPILImage()(reshape_to_2d(unpatches))
132
+
133
+
134
+ def check_loss(loss):
135
+ if loss.isnan().any():
136
+ raise ValueError('NaN loss')
137
+
138
+
139
+ def count_params(model):
140
+ total = sum(p.numel() for p in model.parameters())
141
+ trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
142
+ print("param counts:\n{:,} total\n{:,} trainable".format(total, trainable))
143
+ return trainable
144
+
145
+
146
+ def contrastive_loss(
147
+ cls_token1: torch.Tensor, cls_token2: torch.Tensor, temperature: torch.Tensor
148
+ ):
149
+ feat1 = cls_token1 / cls_token1.norm(dim=1, keepdim=True)
150
+ feat2 = cls_token2 / cls_token2.norm(dim=1, keepdim=True)
151
+
152
+ cosine_sim = feat1 @ feat2.T
153
+ logit_scale = temperature.exp() # log scale, learned during training
154
+ feat1 = cosine_sim * logit_scale
155
+ feat2 = feat1.T
156
+
157
+ labels = torch.arange(feat1.shape[0]).to(feat1.device)
158
+ loss = (
159
+ torch.nn.functional.cross_entropy(feat1, labels)
160
+ + torch.nn.functional.cross_entropy(feat2, labels)
161
+ ) / 2
162
+ return loss
163
+
164
+ ### MindEye functions ###
165
+
166
+ def soft_clip_loss(preds, targs, temp=0.006):
167
+ clip_clip = (targs @ targs.T)/temp
168
+ brain_clip = (preds @ targs.T)/temp
169
+ loss1 = -(brain_clip.log_softmax(-1) * clip_clip.softmax(-1)).sum(-1).mean()
170
+ loss2 = -(brain_clip.T.log_softmax(-1) * clip_clip.softmax(-1)).sum(-1).mean()
171
+
172
+ loss = (loss1 + loss2)/2
173
+ return loss
174
+
175
+ def cosine_anneal(start, end, steps):
176
+ return end + (start - end)/2 * (1 + torch.cos(torch.pi*torch.arange(steps)/(steps-1)))
177
+
178
+ def mixco(voxels, beta=0.15, s_thresh=0.5, perm=None, betas=None, select=None):
179
+ if perm is None:
180
+ perm = torch.randperm(voxels.shape[0])
181
+ voxels_shuffle = voxels[perm].to(voxels.device,dtype=voxels.dtype)
182
+ if betas is None:
183
+ betas = torch.distributions.Beta(beta, beta).sample([voxels.shape[0]]).to(voxels.device,dtype=voxels.dtype)
184
+ if select is None:
185
+ select = (torch.rand(voxels.shape[0]) <= s_thresh).to(voxels.device)
186
+ betas_shape = [-1] + [1]*(len(voxels.shape)-1)
187
+ voxels[select] = voxels[select] * betas[select].reshape(*betas_shape) + \
188
+ voxels_shuffle[select] * (1 - betas[select]).reshape(*betas_shape)
189
+ betas[~select] = 1
190
+ return voxels, perm, betas, select
191
+
192
+ def mixco_nce(preds, targs, temp=0.1, perm=None, betas=None, select=None, distributed=False,
193
+ accelerator=None, local_rank=None, bidirectional=True):
194
+ brain_clip = (preds @ targs.T)/temp
195
+
196
+ if perm is not None and betas is not None and select is not None:
197
+ probs = torch.diag(betas)
198
+ probs[torch.arange(preds.shape[0]).to(preds.device), perm] = 1 - betas
199
+
200
+ loss = -(brain_clip.log_softmax(-1) * probs).sum(-1).mean()
201
+ if bidirectional:
202
+ loss2 = -(brain_clip.T.log_softmax(-1) * probs.T).sum(-1).mean()
203
+ loss = (loss + loss2)/2
204
+ return loss
205
+ else:
206
+ loss = F.cross_entropy(brain_clip, torch.arange(brain_clip.shape[0]).to(brain_clip.device))
207
+ if bidirectional:
208
+ loss2 = F.cross_entropy(brain_clip.T, torch.arange(brain_clip.shape[0]).to(brain_clip.device))
209
+ loss = (loss + loss2)/2
210
+ return loss
211
+
212
+ def topk(similarities,labels,k=5):
213
+ if k > similarities.shape[0]:
214
+ k = similarities.shape[0]
215
+ topsum=0
216
+ for i in range(k):
217
+ topsum += torch.sum(torch.argsort(similarities,axis=1)[:,-(i+1)] == labels)/len(labels)
218
+ return topsum
219
+
220
+ def batchwise_cosine_similarity(Z,B):
221
+ Z = Z.flatten(1)
222
+ B = B.flatten(1).T
223
+ Z_norm = torch.linalg.norm(Z, dim=1, keepdim=True) # Size (n, 1).
224
+ B_norm = torch.linalg.norm(B, dim=0, keepdim=True) # Size (1, b).
225
+ cosine_similarity = ((Z @ B) / (Z_norm @ B_norm)).T
226
+ return cosine_similarity
227
+
228
+ def prenormed_batchwise_cosine_similarity(Z,B):
229
+ return (Z @ B.T).T
230
+
231
+ def torch_to_Image(x):
232
+ if x.ndim==4:
233
+ x=x[0]
234
+ return transforms.ToPILImage()(x)
235
+
236
+ def get_masking_ratio(current_epoch, total_epochs, start_masking_ratio, end_masking_ratio):
237
+ """Returns the masking ratio for the current epochs. Linearly increase the masking ratio over the span of the training"""
238
+ return start_masking_ratio + (end_masking_ratio-start_masking_ratio) * ((current_epoch+1)/total_epochs)
239
+
240
+ def view_brain(data):
241
+ if torch.is_tensor(data):
242
+ data = data.numpy()
243
+ if data.ndim==5:
244
+ new_nii = nib.Nifti1Image((data[0,0].astype(np.float32)-.5)*2, np.eye(4))
245
+ elif data.ndim==4:
246
+ new_nii = nib.Nifti1Image((data[0].astype(np.float32)-.5)*2, np.eye(4))
247
+ elif data.ndim==3:
248
+ new_nii = nib.Nifti1Image((data.astype(np.float32)-.5)*2, np.eye(4))
249
+ else:
250
+ raise Exception("Check dimensionality of your brain data")
251
+ return plotting.view_img(new_nii, bg_img=None, vmax=1, cmap=plt.cm.gray, threshold=None)
252
+
253
+ def get_first_tar(train_urls):
254
+ if isinstance(train_urls, list):
255
+ # If train_urls is a list, get the first element
256
+ url = train_urls[0]
257
+ else:
258
+ # If train_urls is a string, treat it as the only element
259
+ url = train_urls
260
+
261
+ # Extract the first tar file using regular expression
262
+ match = re.search(r'\{(\d+)\.\.', url)
263
+ if match:
264
+ first_tar = match.group(1)
265
+ return f"/scratch/fmri_foundation_datasets/NSD_MNI_wds/{first_tar}.tar"
266
+ else:
267
+ return None
fMRI-foundation-model-old/fMRI-MAE/.ipynb_checkpoints/main-checkpoint.ipynb ADDED
@@ -0,0 +1,1350 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "b8e236f1-385a-4d93-bb39-bea3ee384d76",
7
+ "metadata": {
8
+ "tags": []
9
+ },
10
+ "outputs": [
11
+ {
12
+ "name": "stdout",
13
+ "output_type": "stream",
14
+ "text": [
15
+ "Number of available CUDA devices: 1\n",
16
+ "LOCAL RANK=0\n",
17
+ "NUM GPUS=1\n",
18
+ "NODE=0\n",
19
+ "GLOBAL RANK=0\n",
20
+ "WORLD_SIZE=1\n",
21
+ "PID of this process = 1339581\n",
22
+ "device = cuda distributed = False num_devices = 1 local rank = 0 world size = 1 data_type = torch.float16\n"
23
+ ]
24
+ }
25
+ ],
26
+ "source": [
27
+ "# Import packages and setup gpu configuration.\n",
28
+ "# This code block shouldnt need to be adjusted!\n",
29
+ "import os\n",
30
+ "import sys\n",
31
+ "import json\n",
32
+ "import yaml\n",
33
+ "import numpy as np\n",
34
+ "import pandas as pd\n",
35
+ "import copy\n",
36
+ "import math\n",
37
+ "from einops import rearrange\n",
38
+ "from einops.layers.torch import Rearrange\n",
39
+ "import time\n",
40
+ "import random\n",
41
+ "import h5py\n",
42
+ "import webdataset as wds\n",
43
+ "import gc\n",
44
+ "import matplotlib.pyplot as plt\n",
45
+ "\n",
46
+ "import torch\n",
47
+ "import torch.nn as nn\n",
48
+ "import torch.nn.functional as F\n",
49
+ "from torchvision import transforms\n",
50
+ "import utils\n",
51
+ "from models import *\n",
52
+ "import nibabel as nib\n",
53
+ "from nilearn import plotting\n",
54
+ "\n",
55
+ "import schedulefree\n",
56
+ "\n",
57
+ "# tf32 data type is faster than standard float32\n",
58
+ "torch.backends.cuda.matmul.allow_tf32 = True\n",
59
+ "\n",
60
+ "### Multi-GPU config ###\n",
61
+ "device_count = torch.cuda.device_count()\n",
62
+ "print(f\"Number of available CUDA devices: {device_count}\")\n",
63
+ "\n",
64
+ "local_rank = os.getenv('LOCAL_RANK')\n",
65
+ "if local_rank is None: \n",
66
+ " local_rank = 0\n",
67
+ "else:\n",
68
+ " local_rank = int(local_rank)\n",
69
+ "print(f\"LOCAL RANK={local_rank}\")\n",
70
+ "\n",
71
+ "num_devices = os.getenv('NUM_GPUS')\n",
72
+ "if num_devices is None: \n",
73
+ " num_devices = 1\n",
74
+ "else:\n",
75
+ " num_devices = int(num_devices)\n",
76
+ "print(f\"NUM GPUS={num_devices}\")\n",
77
+ "distributed = True if num_devices>1 else False\n",
78
+ "if distributed: assert device_count==num_devices\n",
79
+ "\n",
80
+ "node = os.getenv('SLURM_NODEID')\n",
81
+ "if node is None:\n",
82
+ " node = 0\n",
83
+ "else:\n",
84
+ " node = int(node)\n",
85
+ "print(f\"NODE={node}\")\n",
86
+ "\n",
87
+ "global_rank = os.getenv('RANK')\n",
88
+ "if global_rank is None:\n",
89
+ " global_rank = 0\n",
90
+ "else:\n",
91
+ " global_rank = int(global_rank)\n",
92
+ "print(f\"GLOBAL RANK={global_rank}\")\n",
93
+ "\n",
94
+ "world_size = os.getenv('WORLD_SIZE')\n",
95
+ "if world_size is None: \n",
96
+ " world_size = 1\n",
97
+ "else:\n",
98
+ " world_size = int(world_size)\n",
99
+ "print(f\"WORLD_SIZE={world_size}\")\n",
100
+ "\n",
101
+ "if utils.is_interactive():\n",
102
+ " # Following allows you to change functions in models.py or utils.py and \n",
103
+ " # have this notebook automatically update with your revisions\n",
104
+ " %load_ext autoreload\n",
105
+ " %autoreload 2\n",
106
+ " from tqdm.notebook import tqdm\n",
107
+ "else:\n",
108
+ " from tqdm import tqdm\n",
109
+ "\n",
110
+ "# Load parameters from yaml config\n",
111
+ "config = yaml.load(open('config.yaml', 'r'), Loader=yaml.FullLoader)\n",
112
+ "\n",
113
+ "# create global variables from the config\n",
114
+ "for attribute_name in config.keys():\n",
115
+ " globals()[attribute_name] = config[f'{attribute_name}']\n",
116
+ "\n",
117
+ "data_type = torch.float16 # change depending on your mixed_precision\n",
118
+ "# batch_size = global_batch_size // num_devices\n",
119
+ "global_batch_size = batch_size * world_size\n",
120
+ "\n",
121
+ "# FSDP Setup\n",
122
+ "if distributed:\n",
123
+ " import torch.distributed as dist\n",
124
+ " import torch.multiprocessing as mp\n",
125
+ " from torch.distributed.fsdp import FullyShardedDataParallel as FSDP\n",
126
+ " from torch.distributed.fsdp.api import BackwardPrefetch, CPUOffload, ShardingStrategy\n",
127
+ " import functools\n",
128
+ " from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy, transformer_auto_wrap_policy\n",
129
+ " print(\"starting init_process_group...\")\n",
130
+ " dist.init_process_group(\"nccl\", rank=global_rank, world_size=world_size)\n",
131
+ " print(f\"setting device to cuda:{local_rank}\")\n",
132
+ " try:\n",
133
+ " torch.cuda.set_device(local_rank)\n",
134
+ " device = torch.device('cuda',local_rank)\n",
135
+ " print(f\"\\nSuccessfully set cuda:{local_rank} | global_rank{global_rank} | node{node}\")\n",
136
+ " except Exception as error: \n",
137
+ " print(f\"\\nFAILED TO SET DEVICE cuda:{local_rank} | global_rank{global_rank} | node{node}\")\n",
138
+ " print(\"An exception occurred:\", error)\n",
139
+ " \n",
140
+ "else:\n",
141
+ " device = torch.device('cuda')\n",
142
+ "\n",
143
+ "print(\"PID of this process =\",os.getpid())\n",
144
+ "print(\"device =\", device, \"distributed =\",distributed, \"num_devices =\", num_devices, \"local rank =\", local_rank, \"world size =\", world_size, \"data_type =\", data_type)"
145
+ ]
146
+ },
147
+ {
148
+ "cell_type": "markdown",
149
+ "id": "d08179db-9c6a-4bc6-a245-79fae6884ca2",
150
+ "metadata": {},
151
+ "source": [
152
+ "# Configuration"
153
+ ]
154
+ },
155
+ {
156
+ "cell_type": "code",
157
+ "execution_count": 2,
158
+ "id": "e6b1c3fe-28ab-40b7-8906-c6a9c8070d00",
159
+ "metadata": {
160
+ "tags": []
161
+ },
162
+ "outputs": [
163
+ {
164
+ "name": "stdout",
165
+ "output_type": "stream",
166
+ "text": [
167
+ "{'model_name': 'mini_nomask_logitsCLS_downstream_40ep_l', 'use_cls_token': True, 'use_contrastive_loss': True, 'contrastive_loss_weight': 0.1, 'batch_size': 256, 'num_workers': 10, 'num_epochs': 20, 'seed': 42, 'max_lr': 3e-06, 'num_samples_per_epoch': 1024, 'test_num_samples_per_epoch': 384, 'ckpt_saving': True, 'ckpt_interval': 50, 'resume_from_ckpt': True, 'wandb_log': True, 'tube_start_masking_ratio': 0.75, 'tube_end_masking_ratio': 0.75, 'decoder_mask_ratio': 0.75, 'patch_size': [8, 8, 8], 'frame_patch_size': 4, 'use_rope_emb': False, 'masking_strategy': 'None', 'encoder_model': 'vit_mini', 'decoder_model': 'vit_mini', 'img_size': [88, 104, 72], 'num_frames': 4, 'is_s3': False, 'train_urls': ['/weka/proj-fmri/shared/NSD_MNI_wds/{000000..000699}.tar'], 'test_urls': ['/weka/proj-fmri/shared/NSD_MNI_wds/{000700..000738}.tar']}\n",
168
+ "outdir /weka/proj-fmri/paulscotti/fMRI-foundation-model/ckpts/mini_nomask_logitsCLS_downstream_40ep_l\n",
169
+ "global_batch_size 256\n",
170
+ "use_cls_token True\n",
171
+ "num_patches 5148\n",
172
+ "num_patches_per_timepoint 1287\n",
173
+ "num_encoder_patches 321\n",
174
+ "num_decoder_patches 321\n"
175
+ ]
176
+ }
177
+ ],
178
+ "source": [
179
+ "print(config)\n",
180
+ "\n",
181
+ "# seed all random functions\n",
182
+ "utils.seed_everything(seed)\n",
183
+ "\n",
184
+ "outdir = os.path.abspath(f'../ckpts/{model_name}')\n",
185
+ "os.makedirs(outdir,exist_ok=True)\n",
186
+ "print(\"outdir\", outdir)\n",
187
+ "print(\"global_batch_size\", global_batch_size)\n",
188
+ "print(\"use_cls_token\", use_cls_token)\n",
189
+ "\n",
190
+ "if type(patch_size) == int:\n",
191
+ " patch_size = [patch_size,patch_size,patch_size]\n",
192
+ "patch_depth = patch_size[0]\n",
193
+ "patch_height = patch_size[1]\n",
194
+ "patch_width = patch_size[2]\n",
195
+ "\n",
196
+ "num_patches = int(\n",
197
+ " (img_size[0] / patch_depth)\n",
198
+ " * (img_size[1] / patch_height)\n",
199
+ " * (img_size[2] / patch_width)\n",
200
+ " * num_frames\n",
201
+ ")\n",
202
+ "num_patches_per_timepoint = num_patches // frame_patch_size\n",
203
+ "num_encoder_patches = int(np.floor((num_patches_per_timepoint * num_frames // frame_patch_size) * (1 - tube_start_masking_ratio)))\n",
204
+ "num_decoder_patches = int(np.floor((num_patches_per_timepoint * num_frames // frame_patch_size) * (1 - decoder_mask_ratio)))\n",
205
+ "print(\"num_patches\", num_patches)\n",
206
+ "print(\"num_patches_per_timepoint\", num_patches_per_timepoint)\n",
207
+ "print(\"num_encoder_patches\", num_encoder_patches)\n",
208
+ "print(\"num_decoder_patches\", num_decoder_patches)"
209
+ ]
210
+ },
211
+ {
212
+ "cell_type": "markdown",
213
+ "id": "1ae8419d-988f-42c6-acb6-b258e0694eee",
214
+ "metadata": {},
215
+ "source": [
216
+ "# Prep models"
217
+ ]
218
+ },
219
+ {
220
+ "cell_type": "code",
221
+ "execution_count": 3,
222
+ "id": "5e37b6b9-5b91-4c4a-af85-ac2af69704e7",
223
+ "metadata": {
224
+ "tags": []
225
+ },
226
+ "outputs": [
227
+ {
228
+ "name": "stdout",
229
+ "output_type": "stream",
230
+ "text": [
231
+ "param counts:\n",
232
+ "1,693,008 total\n",
233
+ "1,693,008 trainable\n",
234
+ "\n",
235
+ "encoder\n",
236
+ "torch.Size([256, 1, 4, 88, 104, 72])\n",
237
+ "patched torch.Size([256, 1, 11, 13, 9, 2048])\n",
238
+ "reshaped torch.Size([256, 1287, 2048])\n",
239
+ "masked torch.Size([256, 321, 2048])\n",
240
+ "patched_emb torch.Size([256, 321, 48])\n",
241
+ "pe torch.Size([1287, 48])\n",
242
+ "masked torch.Size([256, 322, 48])\n",
243
+ "torch.Size([256, 322, 48])\n",
244
+ "\n",
245
+ "decoder\n",
246
+ "torch.Size([256, 322, 48])\n",
247
+ "pe torch.Size([1287, 48])\n",
248
+ "pos_emd_encoder torch.Size([321, 48])\n",
249
+ "pos_emd_decoder torch.Size([321, 48])\n",
250
+ "x_concat torch.Size([256, 643, 48])\n",
251
+ "torch.Size([256, 643, 48])\n",
252
+ "proj torch.Size([256, 643, 2048])\n",
253
+ "\n",
254
+ "enc_cls_token torch.Size([256, 1, 48])\n",
255
+ "encoder_patches torch.Size([256, 321, 48])\n",
256
+ "dec_cls_token torch.Size([256, 1, 2048])\n",
257
+ "decoder_patches torch.Size([256, 642, 2048])\n"
258
+ ]
259
+ }
260
+ ],
261
+ "source": [
262
+ "vit_size = {\n",
263
+ " \"encoder\": encoder_model,\n",
264
+ " \"decoder\": decoder_model\n",
265
+ "}\n",
266
+ " \n",
267
+ "model = get_vit(\n",
268
+ " size=vit_size,\n",
269
+ " image_size=img_size, # depth, height, width\n",
270
+ " image_patch_size=(patch_depth,patch_height,patch_width), # depth, height, width patch size\n",
271
+ " frames=num_frames,\n",
272
+ " frame_patch_size=frame_patch_size,\n",
273
+ " channels=1,\n",
274
+ " use_rope_emb=use_rope_emb,\n",
275
+ " use_cls_token=use_cls_token,\n",
276
+ ")\n",
277
+ "utils.count_params(model)\n",
278
+ "\n",
279
+ "# function to select random num_frames from sample and obtain brain-positive patches\n",
280
+ "aug_transform = utils.DataPrepper(\n",
281
+ " num_frames=num_frames*2,\n",
282
+ " masking_strategy=masking_strategy,\n",
283
+ " patch_depth=patch_depth,\n",
284
+ " patch_height=patch_height,\n",
285
+ " patch_width=patch_width,\n",
286
+ " frame_patch_size=frame_patch_size,\n",
287
+ ")\n",
288
+ "\n",
289
+ "# test that the model works without error\n",
290
+ "model = model.to(device)\n",
291
+ "encoder_mask = torch.zeros(num_patches_per_timepoint).to(torch.bool)\n",
292
+ "encoder_mask[:num_encoder_patches] = True\n",
293
+ "decoder_mask = torch.zeros(num_patches_per_timepoint).to(torch.bool)\n",
294
+ "decoder_mask[-num_decoder_patches:] = True\n",
295
+ "decoder_mask[encoder_mask] = False\n",
296
+ "with torch.no_grad():\n",
297
+ " print(\"\\nencoder\")\n",
298
+ " encoder_out = model(\n",
299
+ " torch.randn(batch_size, 1, num_frames, img_size[0], img_size[1], img_size[2]).to(device),\n",
300
+ " encoder_mask=encoder_mask,\n",
301
+ " verbose=True)\n",
302
+ " print(\"\\ndecoder\")\n",
303
+ " decoder_out = model(\n",
304
+ " encoder_out, \n",
305
+ " encoder_mask=encoder_mask, \n",
306
+ " decoder_mask=decoder_mask, \n",
307
+ " verbose=True)\n",
308
+ " if use_cls_token:\n",
309
+ " enc_cls_token = encoder_out[:, :1, :]\n",
310
+ " encoder_patches = encoder_out[:, 1:, :]\n",
311
+ " dec_cls_token = decoder_out[:, :1, :]\n",
312
+ " decoder_patches = decoder_out[:, 1:, :]\n",
313
+ " print(\"\\nenc_cls_token\", enc_cls_token.shape)\n",
314
+ " print(\"encoder_patches\", encoder_patches.shape)\n",
315
+ " print(\"dec_cls_token\", dec_cls_token.shape)\n",
316
+ " print(\"decoder_patches\", decoder_patches.shape)"
317
+ ]
318
+ },
319
+ {
320
+ "cell_type": "markdown",
321
+ "id": "414cb007-5552-4c5a-8fbd-9b158565770c",
322
+ "metadata": {
323
+ "tags": []
324
+ },
325
+ "source": [
326
+ "### Add \"linear\" probe"
327
+ ]
328
+ },
329
+ {
330
+ "cell_type": "code",
331
+ "execution_count": 4,
332
+ "id": "bb3df855-81c4-4d2c-aff2-42a19dd0e67e",
333
+ "metadata": {
334
+ "tags": []
335
+ },
336
+ "outputs": [],
337
+ "source": [
338
+ "class LinearProbe(nn.Module):\n",
339
+ " def __init__(self, input_dim, h=256, num_classes=8):\n",
340
+ " super(LinearProbe, self).__init__()\n",
341
+ " # self.classifier = nn.Linear(input_dim, num_classes)\n",
342
+ " self.classifier = nn.Sequential(\n",
343
+ " nn.LayerNorm(input_dim),\n",
344
+ " nn.GELU(),\n",
345
+ " nn.Linear(input_dim, h),\n",
346
+ " nn.LayerNorm(h),\n",
347
+ " nn.GELU(),\n",
348
+ " nn.Linear(h, h),\n",
349
+ " nn.LayerNorm(h),\n",
350
+ " nn.GELU(),\n",
351
+ " nn.Linear(h, num_classes)\n",
352
+ " )\n",
353
+ " def forward(self, x):\n",
354
+ " x = self.classifier(x)\n",
355
+ " return x"
356
+ ]
357
+ },
358
+ {
359
+ "cell_type": "code",
360
+ "execution_count": 5,
361
+ "id": "2e6a865b-8348-486e-ab88-040a87fa2ee5",
362
+ "metadata": {},
363
+ "outputs": [],
364
+ "source": [
365
+ "# if use_cls_token:\n",
366
+ "# model.cont = LinearProbe((num_encoder_patches+1)*model.encoder_embed_dim,h=768,num_classes=768)\n",
367
+ "# else:\n",
368
+ "# model.cont = LinearProbe(model.encoder_embed_dim,h=256,num_classes=256)\n",
369
+ "# model = model.to(device)"
370
+ ]
371
+ },
372
+ {
373
+ "cell_type": "markdown",
374
+ "id": "3dd51ddf-fb71-48f4-bdbd-88753b44d2aa",
375
+ "metadata": {},
376
+ "source": [
377
+ "## Create dataset and data loaders"
378
+ ]
379
+ },
380
+ {
381
+ "cell_type": "code",
382
+ "execution_count": 6,
383
+ "id": "75dd0398-0eb8-464e-8bb8-13e7ce1a8480",
384
+ "metadata": {
385
+ "tags": []
386
+ },
387
+ "outputs": [],
388
+ "source": [
389
+ "# from dataloader import create_dataset, create_loader\n",
390
+ "# train_urls = train_urls[0]\n",
391
+ "# print(train_urls)\n",
392
+ "\n",
393
+ "# train_dp = create_dataset(train_urls, \n",
394
+ "# is_s3=train_urls[:2]==\"s3\", \n",
395
+ "# sample_shuffle=100, shard_shuffle=100)\n",
396
+ "# train_dl = create_loader(train_dp, batch_size=batch_size, num_workers=num_workers)"
397
+ ]
398
+ },
399
+ {
400
+ "cell_type": "code",
401
+ "execution_count": 7,
402
+ "id": "cadb1eb6-dc41-416b-ace5-70bd7c7d887c",
403
+ "metadata": {
404
+ "tags": []
405
+ },
406
+ "outputs": [
407
+ {
408
+ "name": "stdout",
409
+ "output_type": "stream",
410
+ "text": [
411
+ "['/weka/proj-fmri/shared/NSD_MNI_wds/{000000..000699}.tar']\n",
412
+ "['/weka/proj-fmri/shared/NSD_MNI_wds/{000700..000738}.tar']\n"
413
+ ]
414
+ }
415
+ ],
416
+ "source": [
417
+ "def log_and_continue(exn):\n",
418
+ " \"\"\"Call in an exception handler to ignore any exception, issue a warning, and continue.\"\"\"\n",
419
+ " print(f'Handling webdataset error ({repr(exn)}). Ignoring.')\n",
420
+ " return True\n",
421
+ "\n",
422
+ "def filter_corrupted_images(sample):\n",
423
+ " \"\"\"If all the required files are not present don't use them.\"\"\"\n",
424
+ " correct_data = (\"func.npy\" in sample)\n",
425
+ " return correct_data\n",
426
+ "\n",
427
+ "### ================ Train Dataset and DataLoader ====================\n",
428
+ "from braceexpand import braceexpand\n",
429
+ "print(train_urls)\n",
430
+ "if is_s3:\n",
431
+ " expanded_urls = [f\"pipe:aws s3 cp {url} -\" for pattern in train_urls for url in braceexpand(pattern)]\n",
432
+ "else:\n",
433
+ " expanded_urls = [str(url) for pattern in train_urls for url in braceexpand(pattern)]\n",
434
+ "\n",
435
+ "train_data = (\n",
436
+ " wds.WebDataset(expanded_urls, resampled=True, nodesplitter=wds.split_by_node, handler=log_and_continue)\n",
437
+ " .shuffle(100, initial=100, rng=random.Random(seed))\n",
438
+ " .select(filter_corrupted_images)\n",
439
+ " .decode(\"torch\")\n",
440
+ ")\n",
441
+ "train_dl = torch.utils.data.DataLoader(train_data, batch_size=batch_size, shuffle=False, drop_last=True, pin_memory=True)\n",
442
+ "\n",
443
+ "### ================ Test Dataset and DataLoader ====================\n",
444
+ "print(test_urls)\n",
445
+ "if is_s3:\n",
446
+ " expanded_urls = [f\"pipe:aws s3 cp {url} -\" for pattern in test_urls for url in braceexpand(pattern)]\n",
447
+ "else:\n",
448
+ " expanded_urls = [str(url) for pattern in train_urls for url in braceexpand(pattern)]\n",
449
+ "\n",
450
+ "test_data = (\n",
451
+ " wds.WebDataset(expanded_urls, resampled=True, nodesplitter=wds.split_by_node, handler=log_and_continue)\n",
452
+ " .shuffle(100, initial=100, rng=random.Random(seed))\n",
453
+ " .select(filter_corrupted_images)\n",
454
+ " .decode(\"torch\")\n",
455
+ ")\n",
456
+ "test_dl = torch.utils.data.DataLoader(test_data, batch_size=batch_size, shuffle=False, drop_last=True, pin_memory=True)"
457
+ ]
458
+ },
459
+ {
460
+ "cell_type": "markdown",
461
+ "id": "15d05a32-12eb-494a-82df-dce4c7e9924c",
462
+ "metadata": {},
463
+ "source": [
464
+ "### Check data loaders work"
465
+ ]
466
+ },
467
+ {
468
+ "cell_type": "code",
469
+ "execution_count": 8,
470
+ "id": "480e7c0b-f58e-4c35-80fe-1284ee0e3966",
471
+ "metadata": {
472
+ "tags": []
473
+ },
474
+ "outputs": [],
475
+ "source": [
476
+ "# if utils.is_interactive():\n",
477
+ "# start_time = time.time() \n",
478
+ "# num_it = 2\n",
479
+ "# print(f\"Yielding {num_it} batches\")\n",
480
+ " \n",
481
+ "# for i, batch in enumerate(test_dl):\n",
482
+ "# print(\"iter\",i)\n",
483
+ "# input_func = batch['func.npy']\n",
484
+ "# subject_id = batch['subject_id.txt']\n",
485
+ "# subject_id = [int(subject[-2:]) for subject in subject_id]\n",
486
+ "# # session_id = batch['session_id.txt']\n",
487
+ "# # session_id = [int(session[-2:]) for session in session_id]\n",
488
+ "# func, brain_pos_pats = aug_transform(input_func)\n",
489
+ "# if i >= (num_it-1):\n",
490
+ "# break\n",
491
+ " \n",
492
+ "# print(\"Done!\")\n",
493
+ "# print(\"input_func\", input_func.shape)\n",
494
+ "# print(\"func\", func.shape)\n",
495
+ "# print(\"subject_id\", subject_id)\n",
496
+ "\n",
497
+ "# end_time = time.time() \n",
498
+ "# execution_time = end_time - start_time \n",
499
+ "# print(f\"Execution time: {execution_time} seconds\")"
500
+ ]
501
+ },
502
+ {
503
+ "cell_type": "markdown",
504
+ "id": "349c6fba-5494-4964-b03d-e01c3afe47db",
505
+ "metadata": {},
506
+ "source": [
507
+ "### Playing with the data, visualization of patching + masking"
508
+ ]
509
+ },
510
+ {
511
+ "cell_type": "code",
512
+ "execution_count": 9,
513
+ "id": "fea69a0b-f0a0-4309-b689-553b911a8da5",
514
+ "metadata": {
515
+ "tags": []
516
+ },
517
+ "outputs": [],
518
+ "source": [
519
+ "# if utils.is_interactive():\n",
520
+ "# func, brain_pos_pats = aug_transform(input_func)\n",
521
+ "# print(func.shape)\n",
522
+ "# display(utils.view_brain(func,cut_coords=(44,44,44)))\n",
523
+ "# # plt.hist(func[0,0].flatten().clamp(.25,3),bins=100)"
524
+ ]
525
+ },
526
+ {
527
+ "cell_type": "markdown",
528
+ "id": "2b8e6baa-4b1c-4f38-b078-70b2b092d14d",
529
+ "metadata": {},
530
+ "source": [
531
+ "# Set up optimizer and saving functions"
532
+ ]
533
+ },
534
+ {
535
+ "cell_type": "code",
536
+ "execution_count": 10,
537
+ "id": "9553ad47-e76d-4ee1-a238-aad13a4be043",
538
+ "metadata": {},
539
+ "outputs": [],
540
+ "source": [
541
+ "if distributed: \n",
542
+ " # my_auto_wrap_policy = functools.partial(\n",
543
+ " # size_based_auto_wrap_policy, min_num_params=200000\n",
544
+ " # )\n",
545
+ " my_auto_wrap_policy = functools.partial(\n",
546
+ " transformer_auto_wrap_policy, \n",
547
+ " transformer_layer_cls={\n",
548
+ " Attention, # <--- Your Transformer layer class\n",
549
+ " },\n",
550
+ " )\n",
551
+ " print(f\"\\nPrepping FSDP on {global_rank} {node}...\\n\")\n",
552
+ " model = model.to(device)\n",
553
+ " model = FSDP(\n",
554
+ " model,\n",
555
+ " sharding_strategy=ShardingStrategy.HYBRID_SHARD,\n",
556
+ " auto_wrap_policy=my_auto_wrap_policy,\n",
557
+ " use_orig_params=False,\n",
558
+ " cpu_offload=None, #CPUOffload(offload_params=True)\n",
559
+ " sync_module_states=True,\n",
560
+ " limit_all_gathers=True, # See https://github.com/pytorch/pytorch/issues/91165\n",
561
+ " device_id=device,\n",
562
+ " )\n",
563
+ " print(f\"\\nSuccessfully loaded FSDP model to device on global_rank {global_rank}\\n\")\n",
564
+ " dist.barrier()\n",
565
+ " print(f\"\\nSuccessfully loaded FSDP model to device on global_rank {global_rank}\\n\")"
566
+ ]
567
+ },
568
+ {
569
+ "cell_type": "code",
570
+ "execution_count": 11,
571
+ "id": "4da73c08-ca61-48ef-9e63-b70db6f07a59",
572
+ "metadata": {
573
+ "tags": []
574
+ },
575
+ "outputs": [
576
+ {
577
+ "name": "stdout",
578
+ "output_type": "stream",
579
+ "text": [
580
+ "num_iterations_per_epoch 4\n",
581
+ "probe_num_iterations_per_epoch 1\n",
582
+ "total_steps 80\n",
583
+ "\n",
584
+ "Done with model preparations!\n",
585
+ "param counts:\n",
586
+ "1,838,256 total\n",
587
+ "1,838,256 trainable\n"
588
+ ]
589
+ }
590
+ ],
591
+ "source": [
592
+ "no_decay = [\"bias\", \"LayerNorm.bias\", \"LayerNorm.weight\"]\n",
593
+ "opt_grouped_parameters = [\n",
594
+ " {'params': [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 1e-2},\n",
595
+ " {'params': [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0},\n",
596
+ "]\n",
597
+ "\n",
598
+ "if distributed:\n",
599
+ " max_lr = max_lr * global_batch_size\n",
600
+ " print(f\"multiply lr {max_lr} by global batch size: max_lr={max_lr}\")\n",
601
+ "\n",
602
+ "# optimizer = torch.optim.AdamW(opt_grouped_parameters, lr=max_lr)\n",
603
+ "optimizer = schedulefree.AdamWScheduleFree(opt_grouped_parameters, lr=max_lr)\n",
604
+ "\n",
605
+ "num_iterations_per_epoch = num_samples_per_epoch // global_batch_size\n",
606
+ "print(\"num_iterations_per_epoch\", num_iterations_per_epoch)\n",
607
+ "\n",
608
+ "probe_num_iterations_per_epoch = test_num_samples_per_epoch // global_batch_size\n",
609
+ "print(\"probe_num_iterations_per_epoch\", probe_num_iterations_per_epoch)\n",
610
+ "\n",
611
+ "total_steps = num_epochs * num_iterations_per_epoch * num_devices\n",
612
+ "print(\"total_steps\", total_steps)\n",
613
+ "\n",
614
+ "# lr_scheduler = torch.optim.lr_scheduler.OneCycleLR(\n",
615
+ "# optimizer,\n",
616
+ "# max_lr=max_lr,\n",
617
+ "# total_steps=total_steps,\n",
618
+ "# )\n",
619
+ "\n",
620
+ "print(\"\\nDone with model preparations!\")\n",
621
+ "num_params = utils.count_params(model)"
622
+ ]
623
+ },
624
+ {
625
+ "cell_type": "code",
626
+ "execution_count": 12,
627
+ "id": "f51f674d-7cd4-49d1-bd8d-c697d9614f65",
628
+ "metadata": {
629
+ "tags": []
630
+ },
631
+ "outputs": [],
632
+ "source": [
633
+ "def save_ckpt(model,tag=\"last\"):\n",
634
+ " if distributed: dist.barrier()\n",
635
+ " model_states = model.state_dict()\n",
636
+ " if global_rank == 0:\n",
637
+ " os.makedirs(outdir,exist_ok=True)\n",
638
+ " ckpt_path = outdir+f'/{tag}.pth'\n",
639
+ " torch.save({\n",
640
+ " 'epoch': epoch,\n",
641
+ " 'model_state_dict': model_states,\n",
642
+ " 'optimizer_state_dict': optimizer.state_dict(),\n",
643
+ " }, ckpt_path)\n",
644
+ " print(f\"\\n---saved {ckpt_path}!---\\n\")"
645
+ ]
646
+ },
647
+ {
648
+ "cell_type": "markdown",
649
+ "id": "ef9b4262-435b-4872-ab4a-424cb9dfd37a",
650
+ "metadata": {},
651
+ "source": [
652
+ "# Start wandb (if enabled)"
653
+ ]
654
+ },
655
+ {
656
+ "cell_type": "code",
657
+ "execution_count": 13,
658
+ "id": "56431733-4fb5-4072-840e-22536608f9f1",
659
+ "metadata": {
660
+ "tags": []
661
+ },
662
+ "outputs": [
663
+ {
664
+ "name": "stdout",
665
+ "output_type": "stream",
666
+ "text": [
667
+ "wandb found run mini_nomask_logitsCLS_downstream_40ep_l\n",
668
+ "wandb_config:\n",
669
+ " {'model_name': 'mini_nomask_logitsCLS_downstream_40ep_l', 'global_batch_size': 256, 'batch_size': 256, 'num_epochs': 20, 'num_samples_per_epoch': 1024, 'test_num_samples_per_epoch': 384, 'num_iterations_per_epoch': 4, 'encoder_model': 'vit_mini', 'decoder_model': 'vit_mini', 'tube_start_masking_ratio': 0.75, 'tube_end_masking_ratio': 0.75, 'decoder_mask_ratio': 0.75, 'num_frames': 4, 'patch_size': [8, 8, 8], 'frame_patch_size': 4, 'use_contrastive_loss': True, 'use_cls_token': True, 'contrastive_loss_weight': 0.1, 'num_params': 1838256, 'max_lr': 3e-06, 'ckpt_interval': 50, 'ckpt_saving': False, 'seed': 42, 'distributed': False, 'num_devices': 1, 'world_size': 1, 'train_urls': ['/weka/proj-fmri/shared/NSD_MNI_wds/{000000..000699}.tar']}\n",
670
+ "wandb_id: mini_nomask_logitsCLS_downstream_40ep_l\n"
671
+ ]
672
+ },
673
+ {
674
+ "name": "stderr",
675
+ "output_type": "stream",
676
+ "text": [
677
+ "\u001b[34m\u001b[1mwandb\u001b[0m: Currently logged in as: \u001b[33mpaul-scotti\u001b[0m. Use \u001b[1m`wandb login --relogin`\u001b[0m to force relogin\n"
678
+ ]
679
+ },
680
+ {
681
+ "data": {
682
+ "text/html": [
683
+ "wandb version 0.16.6 is available! To upgrade, please run:\n",
684
+ " $ pip install wandb --upgrade"
685
+ ],
686
+ "text/plain": [
687
+ "<IPython.core.display.HTML object>"
688
+ ]
689
+ },
690
+ "metadata": {},
691
+ "output_type": "display_data"
692
+ },
693
+ {
694
+ "data": {
695
+ "text/html": [
696
+ "Tracking run with wandb version 0.16.4"
697
+ ],
698
+ "text/plain": [
699
+ "<IPython.core.display.HTML object>"
700
+ ]
701
+ },
702
+ "metadata": {},
703
+ "output_type": "display_data"
704
+ },
705
+ {
706
+ "data": {
707
+ "text/html": [
708
+ "Run data is saved locally in <code>/weka/proj-fmri/paulscotti/MindEyeV2/wandb/run-20240421_203216-mini_nomask_logitsCLS_downstream_40ep_l</code>"
709
+ ],
710
+ "text/plain": [
711
+ "<IPython.core.display.HTML object>"
712
+ ]
713
+ },
714
+ "metadata": {},
715
+ "output_type": "display_data"
716
+ },
717
+ {
718
+ "data": {
719
+ "text/html": [
720
+ "Syncing run <strong><a href='https://wandb.ai/paul-scotti/found/runs/mini_nomask_logitsCLS_downstream_40ep_l' target=\"_blank\">mini_nomask_logitsCLS_downstream_40ep_l</a></strong> to <a href='https://wandb.ai/paul-scotti/found' target=\"_blank\">Weights & Biases</a> (<a href='https://wandb.me/run' target=\"_blank\">docs</a>)<br/>"
721
+ ],
722
+ "text/plain": [
723
+ "<IPython.core.display.HTML object>"
724
+ ]
725
+ },
726
+ "metadata": {},
727
+ "output_type": "display_data"
728
+ },
729
+ {
730
+ "data": {
731
+ "text/html": [
732
+ " View project at <a href='https://wandb.ai/paul-scotti/found' target=\"_blank\">https://wandb.ai/paul-scotti/found</a>"
733
+ ],
734
+ "text/plain": [
735
+ "<IPython.core.display.HTML object>"
736
+ ]
737
+ },
738
+ "metadata": {},
739
+ "output_type": "display_data"
740
+ },
741
+ {
742
+ "data": {
743
+ "text/html": [
744
+ " View run at <a href='https://wandb.ai/paul-scotti/found/runs/mini_nomask_logitsCLS_downstream_40ep_l' target=\"_blank\">https://wandb.ai/paul-scotti/found/runs/mini_nomask_logitsCLS_downstream_40ep_l</a>"
745
+ ],
746
+ "text/plain": [
747
+ "<IPython.core.display.HTML object>"
748
+ ]
749
+ },
750
+ "metadata": {},
751
+ "output_type": "display_data"
752
+ }
753
+ ],
754
+ "source": [
755
+ "if utils.is_interactive():\n",
756
+ "# wandb_log = False\n",
757
+ " ckpt_saving = False\n",
758
+ "if local_rank==0 and wandb_log: # only use main process for wandb logging\n",
759
+ " import wandb\n",
760
+ " wandb_project = 'found'\n",
761
+ " print(f\"wandb {wandb_project} run {model_name}\")\n",
762
+ " # need to configure wandb beforehand in terminal with \"wandb init\"!\n",
763
+ " wandb_config = {\n",
764
+ " \"model_name\": model_name,\n",
765
+ " \"global_batch_size\": global_batch_size,\n",
766
+ " \"batch_size\": batch_size,\n",
767
+ " \"num_epochs\": num_epochs,\n",
768
+ " \"num_samples_per_epoch\": num_samples_per_epoch,\n",
769
+ " \"test_num_samples_per_epoch\": test_num_samples_per_epoch,\n",
770
+ " \"num_iterations_per_epoch\": num_iterations_per_epoch,\n",
771
+ " \"encoder_model\": encoder_model,\n",
772
+ " \"decoder_model\": decoder_model,\n",
773
+ " \"tube_start_masking_ratio\": tube_start_masking_ratio,\n",
774
+ " \"tube_end_masking_ratio\": tube_end_masking_ratio,\n",
775
+ " \"decoder_mask_ratio\": decoder_mask_ratio,\n",
776
+ " \"num_frames\": num_frames,\n",
777
+ " \"patch_size\": patch_size,\n",
778
+ " \"frame_patch_size\": frame_patch_size,\n",
779
+ " \"use_contrastive_loss\": use_contrastive_loss,\n",
780
+ " \"use_cls_token\": use_cls_token,\n",
781
+ " \"contrastive_loss_weight\": contrastive_loss_weight,\n",
782
+ " \"num_params\": num_params,\n",
783
+ " \"max_lr\": max_lr,\n",
784
+ " \"ckpt_interval\": ckpt_interval,\n",
785
+ " \"ckpt_saving\": ckpt_saving,\n",
786
+ " \"seed\": seed,\n",
787
+ " \"distributed\": distributed,\n",
788
+ " \"num_devices\": num_devices,\n",
789
+ " \"world_size\": world_size,\n",
790
+ " \"train_urls\": train_urls,\n",
791
+ " }\n",
792
+ " print(\"wandb_config:\\n\",wandb_config)\n",
793
+ " print(\"wandb_id:\",model_name)\n",
794
+ " wandb.init(\n",
795
+ " id=model_name,\n",
796
+ " project=wandb_project,\n",
797
+ " name=model_name,\n",
798
+ " config=wandb_config,\n",
799
+ " resume=\"allow\",\n",
800
+ " )\n",
801
+ "else:\n",
802
+ " wandb_log = False"
803
+ ]
804
+ },
805
+ {
806
+ "cell_type": "markdown",
807
+ "id": "c43a5055-8afd-468a-93bf-32f94bd1d042",
808
+ "metadata": {},
809
+ "source": [
810
+ "# Start training"
811
+ ]
812
+ },
813
+ {
814
+ "cell_type": "code",
815
+ "execution_count": 14,
816
+ "id": "f5e394dd-2745-41fa-a5aa-54b7b1373a8f",
817
+ "metadata": {
818
+ "tags": []
819
+ },
820
+ "outputs": [],
821
+ "source": [
822
+ "epoch = 0\n",
823
+ "lrs, train_losses, recon_losses, contrastive_losses = [], [], [], []\n",
824
+ "cos_sim_encoder_output, cos_sim_decoder_output, cos_sim_encoder_output_patchwise = [], [], []\n",
825
+ "probe_losses, probe_accs, test_losses, test_accs = [], [], [], []"
826
+ ]
827
+ },
828
+ {
829
+ "cell_type": "code",
830
+ "execution_count": 15,
831
+ "id": "0b9a5605-fd81-41d4-964e-1752ed6e1289",
832
+ "metadata": {
833
+ "tags": []
834
+ },
835
+ "outputs": [],
836
+ "source": [
837
+ "# # resume from ckpt (e.g., if you are resuming from a run that got pre-empted)\n",
838
+ "# load_progress = False\n",
839
+ "# if wandb_log:\n",
840
+ "# if wandb.run.resumed:\n",
841
+ "# load_checkpoint_in_model(model, outdir+\"/last\")\n",
842
+ "# load_progress = True\n",
843
+ "# elif resume_from_ckpt: # if resuming without using wandb\n",
844
+ "# load_checkpoint_in_model(model, outdir+\"/last\")\n",
845
+ "# load_progress = True"
846
+ ]
847
+ },
848
+ {
849
+ "cell_type": "code",
850
+ "execution_count": 16,
851
+ "id": "154e8f51-6918-45df-b6b8-ca46c89ba34e",
852
+ "metadata": {},
853
+ "outputs": [],
854
+ "source": [
855
+ "if masking_strategy==\"MNI\":\n",
856
+ " from einops.layers.torch import Rearrange\n",
857
+ " MNI_brain = nib.load(\"/weka/proj-fmri/paulscotti/fMRI-foundation-model/dataset_creation/afni_conversion/tpl-MNI152NLin2009cAsym_res-02_T1w_brain.nii.gz\").get_fdata()\n",
858
+ " brain_pos_voxels = MNI_brain[6:94,8:112,10:82]\n",
859
+ " brain_pos_pats = Rearrange(\n",
860
+ " \"b c (f pf) (d pd) (h ph) (w pw) -> b f d h w (pd ph pw pf c)\",\n",
861
+ " pd=patch_depth,\n",
862
+ " ph=patch_height,\n",
863
+ " pw=patch_width,\n",
864
+ " pf=1,\n",
865
+ " )(torch.Tensor(brain_pos_voxels)[None,None,None])\n",
866
+ " brain_pos_pats_vit = rearrange(brain_pos_pats, \"b ... d -> b (...) d\").mean(-1)[0]"
867
+ ]
868
+ },
869
+ {
870
+ "cell_type": "code",
871
+ "execution_count": 18,
872
+ "id": "c3461199-e805-4e9c-8c91-894e83cf8bc3",
873
+ "metadata": {
874
+ "tags": []
875
+ },
876
+ "outputs": [
877
+ {
878
+ "data": {
879
+ "application/vnd.jupyter.widget-view+json": {
880
+ "model_id": "2778883b6d5b442dba4d66aa5a0be678",
881
+ "version_major": 2,
882
+ "version_minor": 0
883
+ },
884
+ "text/plain": [
885
+ "Overall: 0%| | 0/20 [00:00<?, ?it/s]"
886
+ ]
887
+ },
888
+ "metadata": {},
889
+ "output_type": "display_data"
890
+ },
891
+ {
892
+ "name": "stdout",
893
+ "output_type": "stream",
894
+ "text": [
895
+ "0 0.078125 2.080991744995117\n",
896
+ "test 0 0.146484375 2.087881088256836\n",
897
+ "test 1 0.033203125 2.1139793395996094\n",
898
+ "{'train/loss': 0.6546683609485626, 'train/recon_losses': nan, 'train/contrastive_losses': 6.546683311462402, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6011962890625, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.2939453125, 'train/probe_losses': 2.080991744995117, 'train/probe_accs': 0.078125, 'test/probe_losses': 2.1009302139282227, 'test/probe_accs': 0.08984375, 'lr': 3e-06, 'epoch': 0, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n"
899
+ ]
900
+ },
901
+ {
902
+ "name": "stderr",
903
+ "output_type": "stream",
904
+ "text": [
905
+ "/admin/home-paulscotti/found/lib/python3.10/site-packages/numpy/core/fromnumeric.py:3504: RuntimeWarning: Mean of empty slice.\n",
906
+ " return _methods._mean(a, axis=axis, dtype=dtype,\n",
907
+ "/admin/home-paulscotti/found/lib/python3.10/site-packages/numpy/core/_methods.py:129: RuntimeWarning: invalid value encountered in scalar divide\n",
908
+ " ret = ret.dtype.type(ret / rcount)\n"
909
+ ]
910
+ },
911
+ {
912
+ "name": "stdout",
913
+ "output_type": "stream",
914
+ "text": [
915
+ "0 0.2265625 2.132457733154297\n",
916
+ "test 0 0.10546875 2.0733165740966797\n",
917
+ "test 1 0.1875 1.9566822052001953\n",
918
+ "{'train/loss': 0.646026149392128, 'train/recon_losses': nan, 'train/contrastive_losses': 6.460261344909668, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6077880859375, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.294189453125, 'train/probe_losses': 2.132457733154297, 'train/probe_accs': 0.2265625, 'test/probe_losses': 2.0149993896484375, 'test/probe_accs': 0.146484375, 'lr': 3e-06, 'epoch': 1, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
919
+ "0 0.123046875 2.136058807373047\n",
920
+ "test 0 0.119140625 2.041532516479492\n",
921
+ "test 1 0.275390625 1.9539203643798828\n",
922
+ "{'train/loss': 0.6089650392532349, 'train/recon_losses': nan, 'train/contrastive_losses': 6.0896501541137695, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.609375, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.3001708984375, 'train/probe_losses': 2.136058807373047, 'train/probe_accs': 0.123046875, 'test/probe_losses': 1.9977264404296875, 'test/probe_accs': 0.197265625, 'lr': 3e-06, 'epoch': 2, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
923
+ "0 0.294921875 1.9231395721435547\n",
924
+ "test 0 0.123046875 1.9924964904785156\n",
925
+ "test 1 0.232421875 2.002117156982422\n",
926
+ "{'train/loss': 0.6081273704767227, 'train/recon_losses': nan, 'train/contrastive_losses': 6.081273555755615, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6173095703125, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.29730224609375, 'train/probe_losses': 1.9231395721435547, 'train/probe_accs': 0.294921875, 'test/probe_losses': 1.9973068237304688, 'test/probe_accs': 0.177734375, 'lr': 3e-06, 'epoch': 3, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
927
+ "0 0.15625 1.9835872650146484\n",
928
+ "test 0 0.162109375 2.0044002532958984\n",
929
+ "test 1 0.181640625 1.993692398071289\n",
930
+ "{'train/loss': 0.6120030879974365, 'train/recon_losses': nan, 'train/contrastive_losses': 6.120030879974365, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.61279296875, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.30224609375, 'train/probe_losses': 1.9835872650146484, 'train/probe_accs': 0.15625, 'test/probe_losses': 1.9990463256835938, 'test/probe_accs': 0.171875, 'lr': 3e-06, 'epoch': 4, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
931
+ "0 0.08203125 2.095348358154297\n",
932
+ "test 0 0.052734375 2.236387252807617\n",
933
+ "test 1 0.0703125 2.2432994842529297\n",
934
+ "{'train/loss': 0.6117484122514725, 'train/recon_losses': nan, 'train/contrastive_losses': 6.117484092712402, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6046142578125, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.3111572265625, 'train/probe_losses': 2.095348358154297, 'train/probe_accs': 0.08203125, 'test/probe_losses': 2.2398433685302734, 'test/probe_accs': 0.0615234375, 'lr': 3e-06, 'epoch': 5, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
935
+ "0 0.15625 2.082639694213867\n",
936
+ "test 0 0.01171875 2.237241744995117\n",
937
+ "test 1 0.05859375 2.2262916564941406\n",
938
+ "{'train/loss': 0.610412523150444, 'train/recon_losses': nan, 'train/contrastive_losses': 6.104125022888184, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6123046875, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.30657958984375, 'train/probe_losses': 2.082639694213867, 'train/probe_accs': 0.15625, 'test/probe_losses': 2.231766700744629, 'test/probe_accs': 0.03515625, 'lr': 3e-06, 'epoch': 6, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
939
+ "0 0.00390625 2.2390995025634766\n",
940
+ "test 0 0.1015625 2.056934356689453\n",
941
+ "test 1 0.064453125 1.9691143035888672\n",
942
+ "{'train/loss': 0.6085265427827835, 'train/recon_losses': nan, 'train/contrastive_losses': 6.085265159606934, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.63623046875, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.31298828125, 'train/probe_losses': 2.2390995025634766, 'train/probe_accs': 0.00390625, 'test/probe_losses': 2.01302433013916, 'test/probe_accs': 0.0830078125, 'lr': 3e-06, 'epoch': 7, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
943
+ "0 0.494140625 1.8124122619628906\n",
944
+ "test 0 0.392578125 1.8616065979003906\n",
945
+ "test 1 0.3828125 1.8176498413085938\n",
946
+ "{'train/loss': 0.6146479845046997, 'train/recon_losses': nan, 'train/contrastive_losses': 6.146479606628418, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.613525390625, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.31793212890625, 'train/probe_losses': 1.8124122619628906, 'train/probe_accs': 0.494140625, 'test/probe_losses': 1.8396282196044922, 'test/probe_accs': 0.3876953125, 'lr': 3e-06, 'epoch': 8, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
947
+ "0 0.021484375 2.2320098876953125\n",
948
+ "test 0 0.025390625 2.181182861328125\n",
949
+ "test 1 0.033203125 2.175107955932617\n",
950
+ "{'train/loss': 0.6042157411575317, 'train/recon_losses': nan, 'train/contrastive_losses': 6.042157173156738, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6165771484375, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.31536865234375, 'train/probe_losses': 2.2320098876953125, 'train/probe_accs': 0.021484375, 'test/probe_losses': 2.178145408630371, 'test/probe_accs': 0.029296875, 'lr': 3e-06, 'epoch': 9, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
951
+ "0 0.04296875 2.068845748901367\n",
952
+ "test 0 0.095703125 2.147918701171875\n",
953
+ "test 1 0.275390625 2.0189361572265625\n",
954
+ "{'train/loss': 0.6057839542627335, 'train/recon_losses': nan, 'train/contrastive_losses': 6.057839393615723, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6259765625, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.30718994140625, 'train/probe_losses': 2.068845748901367, 'train/probe_accs': 0.04296875, 'test/probe_losses': 2.0834274291992188, 'test/probe_accs': 0.185546875, 'lr': 3e-06, 'epoch': 10, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
955
+ "0 0.248046875 2.012929916381836\n",
956
+ "test 0 0.1875 2.1199779510498047\n",
957
+ "test 1 0.177734375 2.0317916870117188\n",
958
+ "{'train/loss': 0.6008661240339279, 'train/recon_losses': nan, 'train/contrastive_losses': 6.008661270141602, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.624267578125, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.3172607421875, 'train/probe_losses': 2.012929916381836, 'train/probe_accs': 0.248046875, 'test/probe_losses': 2.0758848190307617, 'test/probe_accs': 0.1826171875, 'lr': 3e-06, 'epoch': 11, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
959
+ "0 0.15625 2.024129867553711\n",
960
+ "test 0 0.361328125 1.8665599822998047\n",
961
+ "test 1 0.22265625 1.9156570434570312\n",
962
+ "{'train/loss': 0.6274944394826889, 'train/recon_losses': nan, 'train/contrastive_losses': 6.274944305419922, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.62109375, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.3148193359375, 'train/probe_losses': 2.024129867553711, 'train/probe_accs': 0.15625, 'test/probe_losses': 1.891108512878418, 'test/probe_accs': 0.2919921875, 'lr': 3e-06, 'epoch': 12, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
963
+ "0 0.046875 2.0176239013671875\n",
964
+ "test 0 0.140625 1.9483413696289062\n",
965
+ "test 1 0.287109375 1.853952407836914\n",
966
+ "{'train/loss': 0.5929619073867798, 'train/recon_losses': nan, 'train/contrastive_losses': 5.929618835449219, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6199951171875, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.31878662109375, 'train/probe_losses': 2.0176239013671875, 'train/probe_accs': 0.046875, 'test/probe_losses': 1.9011468887329102, 'test/probe_accs': 0.2138671875, 'lr': 3e-06, 'epoch': 13, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
967
+ "0 0.025390625 2.3530330657958984\n",
968
+ "test 0 0.22265625 1.9583473205566406\n",
969
+ "test 1 0.26171875 2.0058231353759766\n",
970
+ "{'train/loss': 0.6034502238035202, 'train/recon_losses': nan, 'train/contrastive_losses': 6.034502029418945, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.636962890625, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.31683349609375, 'train/probe_losses': 2.3530330657958984, 'train/probe_accs': 0.025390625, 'test/probe_losses': 1.9820852279663086, 'test/probe_accs': 0.2421875, 'lr': 3e-06, 'epoch': 14, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
971
+ "0 0.037109375 2.5336132049560547\n",
972
+ "test 0 0.06640625 2.2813949584960938\n",
973
+ "test 1 0.345703125 1.9364681243896484\n",
974
+ "{'train/loss': 0.5937714576721191, 'train/recon_losses': nan, 'train/contrastive_losses': 5.937714576721191, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6195068359375, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.31719970703125, 'train/probe_losses': 2.5336132049560547, 'train/probe_accs': 0.037109375, 'test/probe_losses': 2.108931541442871, 'test/probe_accs': 0.2060546875, 'lr': 3e-06, 'epoch': 15, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
975
+ "0 0.080078125 2.155559539794922\n",
976
+ "test 0 0.056640625 2.157787322998047\n",
977
+ "test 1 0.146484375 2.017892837524414\n",
978
+ "{'train/loss': 0.6005350202322006, 'train/recon_losses': nan, 'train/contrastive_losses': 6.005350112915039, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6202392578125, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.3182373046875, 'train/probe_losses': 2.155559539794922, 'train/probe_accs': 0.080078125, 'test/probe_losses': 2.0878400802612305, 'test/probe_accs': 0.1015625, 'lr': 3e-06, 'epoch': 16, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
979
+ "0 0.111328125 2.099821090698242\n",
980
+ "test 0 0.4296875 1.8980712890625\n",
981
+ "test 1 0.294921875 1.9661064147949219\n",
982
+ "{'train/loss': 0.6011319160461426, 'train/recon_losses': nan, 'train/contrastive_losses': 6.011319160461426, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6202392578125, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.31549072265625, 'train/probe_losses': 2.099821090698242, 'train/probe_accs': 0.111328125, 'test/probe_losses': 1.932088851928711, 'test/probe_accs': 0.3623046875, 'lr': 3e-06, 'epoch': 17, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
983
+ "0 0.119140625 2.0135326385498047\n",
984
+ "test 0 0.265625 1.861032485961914\n",
985
+ "test 1 0.158203125 1.972625732421875\n",
986
+ "{'train/loss': 0.5891992747783661, 'train/recon_losses': nan, 'train/contrastive_losses': 5.891992568969727, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.6190185546875, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.316650390625, 'train/probe_losses': 2.0135326385498047, 'train/probe_accs': 0.119140625, 'test/probe_losses': 1.9168291091918945, 'test/probe_accs': 0.2119140625, 'lr': 3e-06, 'epoch': 18, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n",
987
+ "0 0.181640625 2.0815296173095703\n",
988
+ "test 0 0.154296875 2.060464859008789\n",
989
+ "test 1 0.46875 1.8413829803466797\n",
990
+ "{'train/loss': 0.5968149304389954, 'train/recon_losses': nan, 'train/contrastive_losses': 5.968149185180664, 'train/num_steps': 0, 'train/cos_sim_encoder_output': 0.61767578125, 'train/cos_sim_decoder_output': nan, 'train/cos_sim_encoder_output_patchwise': 0.31536865234375, 'train/probe_losses': 2.0815296173095703, 'train/probe_accs': 0.181640625, 'test/probe_losses': 1.9509239196777344, 'test/probe_accs': 0.3115234375, 'lr': 3e-06, 'epoch': 19, 'tube_mask_ratio': 0.75, 'decoder_mask_ratio': 0.75}\n"
991
+ ]
992
+ }
993
+ ],
994
+ "source": [
995
+ "mse = nn.MSELoss()\n",
996
+ "l1 = nn.L1Loss()\n",
997
+ "crossentropy = nn.CrossEntropyLoss()\n",
998
+ "if use_contrastive_loss:\n",
999
+ " contrastive_temps = utils.cosine_anneal(0.004, 0.0075, num_epochs)\n",
1000
+ "progress_bar = tqdm(range(epoch, num_epochs), disable=local_rank!=0, desc=\"Overall\")\n",
1001
+ "for epoch in progress_bar:\n",
1002
+ " # get the masking ratio for the current epoch\n",
1003
+ " tube_mask_ratio = utils.get_masking_ratio(\n",
1004
+ " current_epoch=epoch, \n",
1005
+ " total_epochs=num_epochs, \n",
1006
+ " start_masking_ratio=tube_start_masking_ratio, \n",
1007
+ " end_masking_ratio=tube_end_masking_ratio\n",
1008
+ " )\n",
1009
+ " with torch.cuda.amp.autocast(dtype=data_type):\n",
1010
+ " model.train()\n",
1011
+ " optimizer.train()\n",
1012
+ " for train_i, batch in enumerate(train_dl):\n",
1013
+ " optimizer.zero_grad()\n",
1014
+ "\n",
1015
+ " input_func = batch['func.npy']\n",
1016
+ "\n",
1017
+ " subject_id = batch['subject_id.txt']\n",
1018
+ " subject_id = torch.Tensor([int(subject[-2:]) for subject in subject_id]).long()\n",
1019
+ " subject_id = torch.repeat_interleave(subject_id.long(), 2).to(device)\n",
1020
+ " # session_id = batch['session_id.txt']\n",
1021
+ " # session_id = torch.Tensor([int(session[-2:]) for session in session_id]).long().repeat(2).to(device)\n",
1022
+ " # session_id = torch.repeat_interleave(session_id.long(), 2)\n",
1023
+ "\n",
1024
+ " if masking_strategy==\"None\":\n",
1025
+ " func, _ = aug_transform(input_func)\n",
1026
+ " brain_pos_pats_vit = torch.ones(num_patches_per_timepoint)\n",
1027
+ " elif masking_strategy==\"MNI\":\n",
1028
+ " func, _ = aug_transform(input_func)\n",
1029
+ " else:\n",
1030
+ " func, brain_pos_voxels = aug_transform(input_func)\n",
1031
+ " brain_pos_pats = model.patchify(torch.Tensor(brain_pos_voxels)[None,None,None])\n",
1032
+ " brain_pos_pats_vit = rearrange(brain_pos_pats, \"b ... d -> b (...) d\").mean(-1)[0]\n",
1033
+ "\n",
1034
+ " func = func.reshape(-1, num_frames, \n",
1035
+ " func.shape[-3], func.shape[-2], func.shape[-1])\n",
1036
+ " func = func.unsqueeze(1).clamp(0,1)\n",
1037
+ " \n",
1038
+ " # create encoder and decoder masks\n",
1039
+ " rand_patches = torch.randperm(num_patches_per_timepoint)\n",
1040
+ " \n",
1041
+ " encoder_mask = torch.zeros(num_patches_per_timepoint).to(torch.bool)\n",
1042
+ " encoder_mask[rand_patches[:num_encoder_patches]] = True\n",
1043
+ " encoder_mask = encoder_mask.tile(num_frames//frame_patch_size)\n",
1044
+ " \n",
1045
+ " decoder_mask = torch.zeros(num_patches_per_timepoint).to(torch.bool)\n",
1046
+ " decoder_mask[rand_patches[num_encoder_patches:num_encoder_patches+num_decoder_patches]] = True\n",
1047
+ " decoder_mask = decoder_mask.tile(num_frames//frame_patch_size)\n",
1048
+ "\n",
1049
+ " # encode the tube patches\n",
1050
+ " encoder_out = model(func, encoder_mask=encoder_mask, device=device)\n",
1051
+ " if use_cls_token:\n",
1052
+ " enc_cls_token = encoder_out[:,:1,:]\n",
1053
+ "\n",
1054
+ " # decode both the encoder_out patches and masked decoder patches\n",
1055
+ " decoder_out = model(encoder_out, encoder_mask=encoder_mask, decoder_mask=decoder_mask, device=device)\n",
1056
+ " # subset only the reconstructed decoder patches\n",
1057
+ " output = decoder_out[:, -decoder_mask.sum():]\n",
1058
+ "\n",
1059
+ " # compare to ground truth and calculate loss\n",
1060
+ " target_patches = model.patchify(func)\n",
1061
+ " target_patches_vit = rearrange(target_patches, \"b ... d -> b (...) d\")\n",
1062
+ " target = target_patches_vit.to(device)[:, decoder_mask]\n",
1063
+ "\n",
1064
+ " target_mean = target.mean(0)\n",
1065
+ " target_std = target.std(0)\n",
1066
+ " target_normed = (target - target_mean) / (target_std + 1e-6)\n",
1067
+ "\n",
1068
+ " recon_loss = mse(output, target_normed)\n",
1069
+ " recon_losses.append(recon_loss.item())\n",
1070
+ " loss = recon_loss\n",
1071
+ "\n",
1072
+ " # contrastive loss\n",
1073
+ " if use_contrastive_loss:\n",
1074
+ " # encode the decoder patches\n",
1075
+ " encoder_out2 = model(func, encoder_mask=decoder_mask, device=device)\n",
1076
+ " enc_cls_token2 = encoder_out2[:,:1,:]\n",
1077
+ " \n",
1078
+ " temp = contrastive_temps[epoch]\n",
1079
+ " \n",
1080
+ " logits = (nn.functional.normalize(enc_cls_token.flatten(1),dim=-1) @\n",
1081
+ " nn.functional.normalize(enc_cls_token2.flatten(1),dim=-1).T) / temp\n",
1082
+ " \n",
1083
+ " # logits = (nn.functional.normalize(model.cont(encoder_out.flatten(1)),dim=-1) @\n",
1084
+ " # nn.functional.normalize(model.cont(encoder_out2.flatten(1)),dim=-1).T) / temp\n",
1085
+ " \n",
1086
+ " labels = torch.arange(len(logits)).long().to(device)\n",
1087
+ " loss1 = crossentropy(logits, labels)\n",
1088
+ " # loss1 = -(logits.log_softmax(-1) * labels.softmax(-1)).sum(-1).mean()\n",
1089
+ " loss2 = crossentropy(logits.T, labels)\n",
1090
+ " contr_loss = (loss1 + loss2)/2\n",
1091
+ " \n",
1092
+ " contrastive_losses.append(contr_loss.item())\n",
1093
+ " loss += (contr_loss * contrastive_loss_weight)\n",
1094
+ "\n",
1095
+ " cos_sim_encoder_output_patchwise.append(utils.patchwise_cosine_similarity(encoder_out).mean().item())\n",
1096
+ " cos_sim_encoder_output.append(utils.batchwise_cosine_similarity(encoder_out.flatten(1)/1e3,encoder_out.flatten(1)/1e3)[~torch.eye(len(encoder_out),dtype=torch.bool)].mean().item())\n",
1097
+ " cos_sim_decoder_output.append(utils.batchwise_cosine_similarity(output,output)[~torch.eye(len(output),dtype=torch.bool)].mean().item())\n",
1098
+ "\n",
1099
+ " loss.backward()\n",
1100
+ " optimizer.step()\n",
1101
+ " lrs.append(optimizer.param_groups[0][\"lr\"])\n",
1102
+ " train_losses.append(loss.item())\n",
1103
+ "\n",
1104
+ " if train_i >= (num_iterations_per_epoch-1):\n",
1105
+ " break\n",
1106
+ "\n",
1107
+ " # reset linear_probe\n",
1108
+ " # if use_cls_token:\n",
1109
+ " # linear_probe = LinearProbe((num_patches_per_timepoint+1)*model.encoder_embed_dim)\n",
1110
+ " # else:\n",
1111
+ " # linear_probe = LinearProbe(num_patches_per_timepoint*model.encoder_embed_dim)\n",
1112
+ " linear_probe = LinearProbe(model.encoder_embed_dim)\n",
1113
+ " linear_probe = linear_probe.to(device)\n",
1114
+ " probe_opt_grouped_parameters = [\n",
1115
+ " {'params': [p for n, p in linear_probe.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 1e-2},\n",
1116
+ " {'params': [p for n, p in linear_probe.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0},\n",
1117
+ " ]\n",
1118
+ " probe_optimizer = torch.optim.AdamW(probe_opt_grouped_parameters, lr=3e-3)\n",
1119
+ "\n",
1120
+ " if True:#(epoch % 5 == 0) or (epoch == num_epochs-1):\n",
1121
+ " model.eval()\n",
1122
+ " optimizer.eval()\n",
1123
+ " linear_probe.train()\n",
1124
+ " for probe_i, batch in enumerate(train_dl):\n",
1125
+ " probe_optimizer.zero_grad()\n",
1126
+ "\n",
1127
+ " input_func = batch['func.npy']\n",
1128
+ "\n",
1129
+ " subject_id = batch['subject_id.txt']\n",
1130
+ " subject_id = torch.Tensor([int(subject[-2:]) for subject in subject_id]).long()\n",
1131
+ " subject_id = torch.repeat_interleave(subject_id.long(), 2).to(device)\n",
1132
+ "\n",
1133
+ " func, _ = aug_transform(input_func)\n",
1134
+ " func = func.reshape(-1, num_frames, \n",
1135
+ " func.shape[-3], func.shape[-2], func.shape[-1])\n",
1136
+ " func = func.unsqueeze(1).clamp(0,1)\n",
1137
+ "\n",
1138
+ " encoder_mask = torch.ones(num_patches_per_timepoint).to(torch.bool)\n",
1139
+ " encoder_mask = encoder_mask.tile(num_frames//frame_patch_size)\n",
1140
+ "\n",
1141
+ " # encode the tube patches\n",
1142
+ " with torch.no_grad():\n",
1143
+ " encoder_out = model(func, encoder_mask=encoder_mask, device=device)\n",
1144
+ " encoder_out = encoder_out[:,:1,:]\n",
1145
+ " encoder_out = nn.functional.normalize(encoder_out,dim=-1)\n",
1146
+ "\n",
1147
+ " # linear probe\n",
1148
+ " subject_pred = linear_probe(encoder_out.flatten(1).to(device))\n",
1149
+ " probe_loss = crossentropy(subject_pred, subject_id-1) # minus 1 because subject_id is 1-indexed\n",
1150
+ "\n",
1151
+ " probe_accuracy = (torch.max(subject_pred,1).indices == (subject_id-1)).sum() / len(subject_id)\n",
1152
+ " probe_accs.append(probe_accuracy.item())\n",
1153
+ " probe_losses.append(probe_loss.item())\n",
1154
+ "\n",
1155
+ " print(probe_i, probe_accuracy.item(), probe_loss.item())\n",
1156
+ "\n",
1157
+ " probe_loss.backward()\n",
1158
+ " probe_optimizer.step()\n",
1159
+ "\n",
1160
+ " if probe_i >= (probe_num_iterations_per_epoch-1):\n",
1161
+ " break\n",
1162
+ "\n",
1163
+ " for test_i, batch in enumerate(test_dl):\n",
1164
+ " input_func = batch['func.npy']\n",
1165
+ "\n",
1166
+ " subject_id = batch['subject_id.txt']\n",
1167
+ " subject_id = torch.Tensor([int(subject[-2:]) for subject in subject_id]).long()\n",
1168
+ " subject_id = torch.repeat_interleave(subject_id.long(), 2).to(device)\n",
1169
+ "\n",
1170
+ " func, _ = aug_transform(input_func)\n",
1171
+ " func = func.reshape(-1, num_frames, \n",
1172
+ " func.shape[-3], func.shape[-2], func.shape[-1])\n",
1173
+ " func = func.unsqueeze(1).clamp(0,1)\n",
1174
+ "\n",
1175
+ " encoder_mask = torch.ones(num_patches_per_timepoint).to(torch.bool)\n",
1176
+ " encoder_mask = encoder_mask.tile(num_frames//frame_patch_size)\n",
1177
+ "\n",
1178
+ " # encode the tube patches\n",
1179
+ " with torch.no_grad():\n",
1180
+ " encoder_out = model(func, encoder_mask=encoder_mask, device=device)\n",
1181
+ " encoder_out = encoder_out[:,:1,:]\n",
1182
+ " encoder_out = nn.functional.normalize(encoder_out,dim=-1)\n",
1183
+ "\n",
1184
+ " # linear probe\n",
1185
+ " subject_pred = linear_probe(encoder_out.flatten(1).to(device))\n",
1186
+ " test_loss = crossentropy(subject_pred, subject_id-1) # minus 1 because subject_id is 1-indexed\n",
1187
+ "\n",
1188
+ " test_accuracy = (torch.max(subject_pred,1).indices == (subject_id-1)).sum() / len(subject_id)\n",
1189
+ " test_accs.append(test_accuracy.item())\n",
1190
+ " test_losses.append(test_loss.item())\n",
1191
+ "\n",
1192
+ " print(\"test\", test_i, test_accuracy.item(), test_loss.item())\n",
1193
+ "\n",
1194
+ " if test_i >= 1:\n",
1195
+ " break\n",
1196
+ "\n",
1197
+ " logs = {\n",
1198
+ " \"train/loss\": np.mean(train_losses[-(train_i + 1) :]),\n",
1199
+ " \"train/recon_losses\": np.mean(recon_losses[-(train_i + 1) :]),\n",
1200
+ " \"train/contrastive_losses\": np.mean(contrastive_losses[-(train_i + 1) :]),\n",
1201
+ " \"train/num_steps\": len(recon_losses),\n",
1202
+ " \"train/cos_sim_encoder_output\": np.mean(cos_sim_encoder_output[-(train_i + 1) :]),\n",
1203
+ " \"train/cos_sim_decoder_output\": np.mean(cos_sim_decoder_output[-(train_i + 1) :]),\n",
1204
+ " \"train/cos_sim_encoder_output_patchwise\": np.mean(cos_sim_encoder_output_patchwise[-(train_i + 1) :]),\n",
1205
+ " \"train/probe_losses\": np.mean(probe_losses[-(probe_i + 1) :]),\n",
1206
+ " \"train/probe_accs\": np.mean(probe_accs[-(probe_i + 1) :]),\n",
1207
+ " \"test/probe_losses\": np.mean(test_losses[-(test_i + 1) :]),\n",
1208
+ " \"test/probe_accs\": np.mean(test_accs[-(test_i + 1) :]),\n",
1209
+ " \"lr\": np.mean(lrs[-(train_i + 1) :]),\n",
1210
+ " \"epoch\": epoch,\n",
1211
+ " \"tube_mask_ratio\": tube_mask_ratio,\n",
1212
+ " \"decoder_mask_ratio\": decoder_mask_ratio,\n",
1213
+ " }\n",
1214
+ " progress_bar.set_postfix(**logs)\n",
1215
+ " if utils.is_interactive(): print(logs)\n",
1216
+ "\n",
1217
+ " # Plot progress (first sample in batch)\n",
1218
+ " with torch.no_grad():\n",
1219
+ " if utils.is_interactive() or wandb_log:\n",
1220
+ " if epoch % 50 == 0:\n",
1221
+ " output = (output * target_std) + target_mean\n",
1222
+ " idx = 0\n",
1223
+ " \n",
1224
+ " decode_vis = torch.zeros_like(target_patches_vit)\n",
1225
+ " decode_vis[:, decoder_mask] = output.to(decode_vis.device).to(decode_vis.dtype)\n",
1226
+ " decoder_unpatches = rearrange(\n",
1227
+ " decode_vis,\n",
1228
+ " \"b (f d h w) c -> b f d h w c\",\n",
1229
+ " d=img_size[0]//patch_depth,\n",
1230
+ " h=img_size[1]//patch_height,\n",
1231
+ " w=img_size[2]//patch_width,\n",
1232
+ " )\n",
1233
+ " decoder_func = rearrange(\n",
1234
+ " decoder_unpatches,\n",
1235
+ " \"b f d h w (pd ph pw pf c) -> b c (f pf) (d pd) (h ph) (w pw)\",\n",
1236
+ " b=batch_size*2,\n",
1237
+ " f=num_frames//frame_patch_size,\n",
1238
+ " d=img_size[0]//patch_depth,\n",
1239
+ " h=img_size[1]//patch_height,\n",
1240
+ " w=img_size[2]//patch_width,\n",
1241
+ " pd=patch_depth,\n",
1242
+ " ph=patch_height,\n",
1243
+ " pw=patch_width,\n",
1244
+ " pf=frame_patch_size,\n",
1245
+ " )\n",
1246
+ " orig_image = utils.reshape_to_2d(func[idx])\n",
1247
+ " recon_image = utils.reshape_to_2d(decoder_func[idx])\n",
1248
+ "\n",
1249
+ " combined_image = orig_image.clone()\n",
1250
+ " combined_image[recon_image!=0] = recon_image[recon_image!=0]\n",
1251
+ "\n",
1252
+ " random_start = np.arange(3100,3450)\n",
1253
+ " orig_image = transforms.ToPILImage()(orig_image[:,random_start])\n",
1254
+ " recon_image = transforms.ToPILImage()(recon_image[:,random_start])\n",
1255
+ " combined_image = transforms.ToPILImage()(combined_image[:,random_start])\n",
1256
+ "\n",
1257
+ " if wandb_log:\n",
1258
+ " logs[f\"train/orig\"] = wandb.Image(orig_image, caption=f\"epoch{epoch:03d}\")\n",
1259
+ " logs[f\"train/recon\"] = wandb.Image(recon_image, caption=f\"epoch{epoch:03d}\")\n",
1260
+ " logs[f\"train/combined\"] = wandb.Image(combined_image, caption=f\"epoch{epoch:03d}\")\n",
1261
+ " else:\n",
1262
+ " if epoch==0:\n",
1263
+ " print(\"orig_image\")\n",
1264
+ " display(orig_image)\n",
1265
+ " print(\"recon_image\")\n",
1266
+ " display(recon_image)\n",
1267
+ " print(\"combined_image\")\n",
1268
+ " display(combined_image)\n",
1269
+ "\n",
1270
+ " if wandb_log: wandb.log(logs)\n",
1271
+ "\n",
1272
+ " # Save model checkpoint\n",
1273
+ " if (ckpt_saving) and ((epoch % ckpt_interval == 0) or (epoch==num_epochs-1)):\n",
1274
+ " save_ckpt(model,\"last\")\n",
1275
+ "\n",
1276
+ " # wait for other GPUs to catch up if needed\n",
1277
+ " if distributed: dist.barrier()\n",
1278
+ " torch.cuda.empty_cache()\n",
1279
+ " \n",
1280
+ "if distributed:\n",
1281
+ " dist.destroy_process_group()"
1282
+ ]
1283
+ },
1284
+ {
1285
+ "cell_type": "code",
1286
+ "execution_count": null,
1287
+ "id": "a6779720-0d08-4aec-87e2-439e0c1e9dca",
1288
+ "metadata": {
1289
+ "tags": []
1290
+ },
1291
+ "outputs": [],
1292
+ "source": [
1293
+ "encoder_mask.device"
1294
+ ]
1295
+ },
1296
+ {
1297
+ "cell_type": "code",
1298
+ "execution_count": null,
1299
+ "id": "1840c426-d7f5-4d0c-acd1-5ea47d453b91",
1300
+ "metadata": {
1301
+ "tags": []
1302
+ },
1303
+ "outputs": [],
1304
+ "source": [
1305
+ "decoder_mask.sum()"
1306
+ ]
1307
+ },
1308
+ {
1309
+ "cell_type": "code",
1310
+ "execution_count": null,
1311
+ "id": "fff6e847-2511-4f69-aa70-f1471a5b7d07",
1312
+ "metadata": {
1313
+ "tags": []
1314
+ },
1315
+ "outputs": [],
1316
+ "source": [
1317
+ "plt.figure(figsize=(8, 3))\n",
1318
+ "plt.plot(probe_losses)\n",
1319
+ "# plt.title(\"Training re-construction losses\")\n",
1320
+ "plt.show()\n",
1321
+ "if use_contrastive_loss:\n",
1322
+ " plt.figure(figsize=(8, 3))\n",
1323
+ " plt.plot(contrastive_losses)\n",
1324
+ " plt.title(\"Training contrastive losses\")\n",
1325
+ " plt.show()"
1326
+ ]
1327
+ }
1328
+ ],
1329
+ "metadata": {
1330
+ "kernelspec": {
1331
+ "display_name": "fmri_new",
1332
+ "language": "python",
1333
+ "name": "fmri_new"
1334
+ },
1335
+ "language_info": {
1336
+ "codemirror_mode": {
1337
+ "name": "ipython",
1338
+ "version": 3
1339
+ },
1340
+ "file_extension": ".py",
1341
+ "mimetype": "text/x-python",
1342
+ "name": "python",
1343
+ "nbconvert_exporter": "python",
1344
+ "pygments_lexer": "ipython3",
1345
+ "version": "3.8.10"
1346
+ }
1347
+ },
1348
+ "nbformat": 4,
1349
+ "nbformat_minor": 5
1350
+ }
fMRI-foundation-model-old/fMRI-MAE/accel.slurm ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #SBATCH --account=fmri
3
+ #SBATCH --partition=h80i
4
+ #SBATCH --job-name=found
5
+ #SBATCH --ntasks-per-node=1
6
+ #SBATCH --nodes=1
7
+ #SBATCH --gres=gpu:1
8
+ #SBATCH --gpus-per-task=1 # Set to equal gres=gpu:#!
9
+ #SBATCH --time=35:00:00 # total run time limit (HH:MM:SS)
10
+ #SBATCH -e slurms/%j.err # first create a "slurms" folder in current directory to store logs
11
+ #SBATCH -o slurms/%j.out
12
+ #SBATCH --comment=medarc
13
+ #SBATCH --no-requeue
14
+ #SBATCH --exclude=ip-10-0-134-244,ip-10-0-139-120,ip-10-0-139-20,ip-10-0-168-162
15
+
16
+ export NUM_GPUS=1 # Set to equal gres=gpu:#!
17
+ echo NUM_GPUS=$NUM_GPUS
18
+
19
+ source ~/.bashrc
20
+
21
+ cd /weka/proj-fmri/paulscotti/fMRI-foundation-model/fMRI-MAE/
22
+ jupyter nbconvert main.ipynb --to python
23
+ if [ $? -ne 0 ]; then
24
+ echo "Error: Conversion of ipynb to Python failed. Exiting."
25
+ exit 1
26
+ fi
27
+
28
+ # Make sure another job doesnt use same port, here using random number
29
+ export MASTER_PORT=$((RANDOM % (19000 - 11000 + 1) + 11000))
30
+ export HOSTNAMES=$(scontrol show hostnames "$SLURM_JOB_NODELIST")
31
+ export MASTER_ADDR=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n 1)
32
+ echo MASTER_ADDR=${MASTER_ADDR}
33
+ echo MASTER_PORT=${MASTER_PORT}
34
+ echo WORLD_SIZE=$((${SLURM_NNODES} * ${NUM_GPUS}))
35
+
36
+ # Other settings relevant for multi-node
37
+ export NCCL_DEBUG=WARN #INFO
38
+ export NCCL_PROTO=simple
39
+ export FI_EFA_USE_DEVICE_RDMA=1
40
+ export FI_EFA_FORK_SAFE=1
41
+ export FI_LOG_LEVEL=1
42
+ export PYTHONFAULTHANDLER=1
43
+ export CUDA_LAUNCH_BLOCKING=0
44
+ export OMPI_MCA_mtl_base_verbose=1
45
+ export FI_EFA_ENABLE_SHM_TRANSFER=0
46
+ export FI_PROVIDER=efa
47
+ export FI_EFA_TX_MIN_CREDITS=64
48
+ export NCCL_TREE_THRESHOLD=0
49
+ export TORCH_DISTRIBUTED_DEBUG=DETAIL
50
+ export NCCL_P2P_DISABLE=1
51
+ module load openmpi cuda/12.1
52
+
53
+ srun torchrun \
54
+ --nproc_per_node=$NUM_GPUS \
55
+ --nnodes=$SLURM_NNODES \
56
+ --rdzv_id=$SLURM_JOBID \
57
+ --rdzv_backend=c10d \
58
+ --rdzv_endpoint=${MASTER_ADDR}:$MASTER_PORT \
59
+ --rdzv_conf=timeout=90 \
60
+ main.py
61
+
62
+ if [ $? -ne 0 ]; then
63
+ echo "Error: srun command failed. Please check the logs for more details."
64
+ exit 1
65
+ fi
fMRI-foundation-model-old/fMRI-MAE/dataloader.py ADDED
@@ -0,0 +1,772 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from torch import Tensor, stack
3
+ from torch.utils.data import IterDataPipe, DataLoader
4
+ from torch.utils.data.datapipes._decorator import functional_datapipe
5
+ from torch.utils.data.datapipes.iter.callable import MapperIterDataPipe
6
+ from torch.utils.data.datapipes.utils.common import StreamWrapper, match_masks
7
+ from torchdata.datapipes.iter import IterableWrapper, IterDataPipe, TarArchiveLoader
8
+ from torchdata.datapipes.iter.load.s3io import S3FileLoaderIterDataPipe
9
+ from torchdata.datapipes.utils.common import validate_pathname_binary_tuple
10
+ import warnings
11
+ import subprocess
12
+ import tarfile
13
+ import tempfile
14
+ import time
15
+ import random
16
+ from omegaconf import DictConfig, ListConfig
17
+ import webdataset as wds
18
+ import re
19
+ import os
20
+ import copy
21
+ from io import BufferedIOBase, BytesIO, RawIOBase
22
+ from typing import (
23
+ IO,
24
+ Any,
25
+ Callable,
26
+ Dict,
27
+ Iterable,
28
+ Iterator,
29
+ List,
30
+ Optional,
31
+ Sequence,
32
+ Tuple,
33
+ TypeVar,
34
+ Union,
35
+ cast,
36
+ )
37
+ T_co = TypeVar("T_co", covariant=True)
38
+
39
+ class ShuffledListDataPipe(IterDataPipe[T_co]):
40
+ def __init__(
41
+ self,
42
+ source_list: List[T_co],
43
+ *,
44
+ shuffle: bool = True,
45
+ cycle: Union[bool, int] = True,
46
+ ):
47
+ super().__init__()
48
+ self.source = source_list
49
+ self._enabled = shuffle
50
+ self._seed = None
51
+ self._rng = random.Random()
52
+ if isinstance(cycle, bool):
53
+ if cycle:
54
+ self._cycle = -1
55
+ else:
56
+ self._cycle = 1
57
+ else:
58
+ assert isinstance(cycle, int)
59
+ self._cycle = cycle
60
+
61
+ def __iter__(self) -> Iterator[T_co]:
62
+ source = copy.copy(self.source)
63
+ cycle = self._cycle
64
+ epochs = 0
65
+ while cycle == -1 or epochs < cycle:
66
+ if self._enabled:
67
+ self._rng.shuffle(source)
68
+ yield from source
69
+ epochs += 1
70
+
71
+ def __len__(self):
72
+ if self.count == -1:
73
+ raise TypeError(
74
+ f"This {type(self).__name__} instance cycles forever, and "
75
+ f"therefore doesn't have valid length."
76
+ )
77
+ else:
78
+ return self.count * len(self.source)
79
+
80
+ def __getstate__(self):
81
+ state = (
82
+ self.source,
83
+ self._enabled,
84
+ self._seed,
85
+ self._rng.getstate(),
86
+ self._valid_iterator_id,
87
+ self._number_of_samples_yielded,
88
+ )
89
+ if IterDataPipe.getstate_hook is not None:
90
+ return IterDataPipe.getstate_hook(state)
91
+ return state
92
+
93
+ def __setstate__(self, state):
94
+ (
95
+ self.source,
96
+ self._enabled,
97
+ self._seed,
98
+ rng_state,
99
+ self._valid_iterator_id,
100
+ self._number_of_samples_yielded,
101
+ ) = state
102
+ self._rng = random.Random()
103
+ self._rng.setstate(rng_state)
104
+
105
+ def get_job_temp_dir(dl_root: str) -> str:
106
+ try:
107
+ job_or_array_id = (
108
+ os.environ.get("SLURM_ARRAY_JOB_ID", "") or os.environ["SLURM_JOB_ID"]
109
+ )
110
+ task_id = os.environ.get("SLURM_ARRAY_TASK_ID", "") or "0"
111
+ return os.path.join(dl_root, f"JOB_{job_or_array_id}/TASK_{task_id}")
112
+ except KeyError as e:
113
+ print("SLURM_JOB_ID env var not set: You sure this job is healthy?")
114
+ raise e
115
+
116
+ def _shard_expand(s: str) -> List[str]:
117
+ expansion = r"\{[0-9]+\.\.[0-9]+\}"
118
+ m = re.search(expansion, s)
119
+ if not m:
120
+ return [s]
121
+ prefix = s[: m.start()]
122
+ rest = _shard_expand(s[m.end() :])
123
+ rng = s[m.start() + 1 : m.end() - 1]
124
+ lohi = rng.split("..")
125
+ if len(lohi[0]) == len(lohi[1]) and lohi[0].startswith("0"):
126
+ fmt = "{prefix}{i:0>{l}d}{r}"
127
+ elif len(lohi[0]) <= len(lohi[1]):
128
+ if lohi[0].startswith("0") and lohi[0] != "0":
129
+ raise ValueError(
130
+ "shard_expand: low bound must not start with 0 if low bound is shorter"
131
+ )
132
+ fmt = "{prefix}{i}{r}"
133
+ else:
134
+ raise ValueError("shard_expand: low bound must be shorter than high bound")
135
+ lo, hi = (int(x) for x in lohi)
136
+ if lo >= hi:
137
+ raise ValueError(f"shard_expand: bad range in in shard spec {s}.")
138
+ result = []
139
+ for i in range(lo, hi + 1):
140
+ for r in rest:
141
+ expanded: str = fmt.format(prefix=prefix, i=i, r=r, l=len(lohi[1]))
142
+ result.append(expanded)
143
+ return result
144
+
145
+ @functional_datapipe("custom_shard_expand")
146
+ class CustomShardExpanderIterDataPipe(IterDataPipe[str]):
147
+ def __init__(self, source_datapipe: IterDataPipe[str]) -> None:
148
+ super().__init__()
149
+ self.source_datapipe: IterDataPipe[str] = source_datapipe
150
+
151
+ def __iter__(self) -> Iterator[str]:
152
+ for path in self.source_datapipe:
153
+ yield from _shard_expand(path)
154
+
155
+ def warn_and_continue(exn):
156
+ """Call in an exception handler to ignore any exception, issue a warning, and continue."""
157
+ print(exn)
158
+ warnings.warn(repr(exn))
159
+ time.sleep(0.05)
160
+ return True
161
+ error_handler = warn_and_continue
162
+
163
+ def is_stream_handle(data):
164
+ obj_to_check = data.file_obj if isinstance(data, StreamWrapper) else data
165
+ return isinstance(obj_to_check, (BufferedIOBase, RawIOBase))
166
+
167
+ def read_stream_handles(data):
168
+ if not is_stream_handle(data):
169
+ return data
170
+ else:
171
+ ds = data
172
+ data = b"".join(data)
173
+ ds.close()
174
+ del ds
175
+ return data
176
+
177
+ def stream_reader(sample):
178
+ return {k: read_stream_handles(v) for k, v in sample.items()}
179
+
180
+ def to_our_format(sample):
181
+ wds_key = sample.pop("__key__")
182
+ sample = {k.lstrip("."): v for k, v in sample.items()}
183
+ sample["__key__"] = wds_key.split("/")[-1]
184
+ sample["__url__"] = "/".join(wds_key.split("/")[:-1])
185
+ return sample
186
+
187
+ def add_processors(
188
+ datapipeline,
189
+ processors: Optional[ListConfig],
190
+ description: str,
191
+ error_handler: Callable = warn_and_continue,
192
+ ):
193
+ if not processors:
194
+ return datapipeline
195
+ else:
196
+ for i, processor_config in enumerate(processors):
197
+ processor = instantiate(processor_config)
198
+ if isinstance(processor, AbstractFilter):
199
+ print(
200
+ f"Adding filter {processor.__class__.__name__} as {description} #{i} "
201
+ f"to the datapipeline"
202
+ )
203
+ datapipeline = datapipeline.filter(processor.filter)
204
+ elif isinstance(processor, AbstractMapper):
205
+ print(
206
+ f"Adding mapper {processor.__class__.__name__} as {description} #{i} "
207
+ f"to the datapipeline"
208
+ )
209
+ datapipeline = datapipeline.map_with_handler(
210
+ processor.map,
211
+ handler=error_handler,
212
+ called_cls_name=processor.__class__.__name__,
213
+ )
214
+ else:
215
+ raise TypeError(
216
+ f"chosen {description} {processor.__class__.__name__} should be either subclass"
217
+ "AbstractMapper or AbstractFilter"
218
+ )
219
+ return datapipeline
220
+
221
+ @functional_datapipe("map_with_handler")
222
+ class MapperWithErrorHandlingIterDataPipe(MapperIterDataPipe):
223
+ def __init__(
224
+ self,
225
+ datapipe: IterDataPipe,
226
+ fn: Callable,
227
+ handler: Callable = wds.reraise_exception,
228
+ input_col: Optional[Union[str, int]] = None,
229
+ output_col: Optional[Union[str, int]] = None,
230
+ called_cls_name: Optional[str] = None,
231
+ ):
232
+ # for now, disbable input and output col since this is never used anyways
233
+ if input_col is not None:
234
+ raise NotImplementedError("`input_col` argument currently not supported")
235
+
236
+ if output_col is not None:
237
+ raise NotImplementedError("`output_col` argument currently not supported")
238
+
239
+ super().__init__(datapipe, fn)
240
+ self.handler = handler
241
+ self._apply_fn_ = self._apply_fn
242
+
243
+ def __iter__(self) -> Iterator[T_co]:
244
+ for data in self.datapipe:
245
+ try:
246
+ res = self._apply_fn_(data)
247
+ if res is None:
248
+ continue
249
+ yield res
250
+ except Exception as e:
251
+ if self.handler(e):
252
+ continue
253
+ else:
254
+ raise e
255
+
256
+ __S3_TOOLS__ = {
257
+ "s3": ["/usr/local/bin/aws", "s3"],
258
+ }
259
+
260
+ def is_tar(x: str) -> bool:
261
+ return x.endswith(".tar")
262
+
263
+ def ls_aws(
264
+ path: str,
265
+ tool: str = "s3",
266
+ recursive: bool = True,
267
+ raise_errors: bool = True,
268
+ skip_files: bool = True,
269
+ ):
270
+ assert path.startswith("s3://"), path
271
+
272
+ # in case we have a file, set recursive to false
273
+ isfile = bool(os.path.splitext(path)[1])
274
+ if isfile and skip_files:
275
+ return [path]
276
+
277
+ if not path.endswith("/") and not isfile:
278
+ path = path + "/"
279
+ cmd = [*__S3_TOOLS__[tool], "ls", f"{path}"]
280
+
281
+ if recursive and not isfile:
282
+ cmd += ["--recursive"]
283
+ try:
284
+ result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
285
+ result.check_returncode()
286
+
287
+ stdout = result.stdout.decode().split("\n")
288
+ # return empty list in case path is not a real file/directory
289
+ if not stdout[0]:
290
+ return []
291
+
292
+ # return path if it is an exisiting file
293
+ if isfile:
294
+ return [path]
295
+
296
+ # strip timestamp and object size
297
+ out = [line.split(" ")[-1] for line in stdout if line]
298
+ if recursive:
299
+ # aws s3 ls returns prefix+filename
300
+ bucket = path[: path.find("/", len("s3://"))] # s3://<bucket>/...
301
+ out = [os.path.join(bucket, o) for o in out]
302
+ else:
303
+ # aws s3 ls returns filename only
304
+ out = [os.path.join(path, o) for i in out]
305
+
306
+ return out
307
+ except subprocess.CalledProcessError as e:
308
+ print(f"Got exception while trying to load data! {e.__class__.__name__}: {e}")
309
+ if raise_errors:
310
+ raise e
311
+ else:
312
+ return []
313
+
314
+ @functional_datapipe("wrapped_load_files_by_s3")
315
+ class WrappedS3FileLoaderIterDataPipe(S3FileLoaderIterDataPipe):
316
+ def __init__(
317
+ self,
318
+ *args,
319
+ ignore_missing_files: bool = False,
320
+ **kwargs,
321
+ ):
322
+ super().__init__(*args, **kwargs)
323
+ self.ignore_missing_files = ignore_missing_files
324
+
325
+ def __iter__(self) -> Iterator[Tuple[str, StreamWrapper]]:
326
+ # same as parent but maybe ignoring errors
327
+ for url in self.source_datapipe:
328
+ try:
329
+ yield url, StreamWrapper(BytesIO(self.handler.s3_read(url)))
330
+ except ValueError as e:
331
+ if not self.ignore_missing_files:
332
+ raise FileNotFoundError(url)
333
+ else:
334
+ print(f"Warning: Could not download {url}")
335
+
336
+ @functional_datapipe("list_files_with_aws_cli")
337
+ class S3CLIFileListerIterDataPipe(IterDataPipe[T_co]):
338
+ def __init__(
339
+ self,
340
+ source_datapipe: Union[str, Sequence[str], IterDataPipe],
341
+ s3_tool: str = "s3",
342
+ n_retries: int = 10,
343
+ sleep_interval: float = 0.01,
344
+ ):
345
+ if isinstance(source_datapipe, str):
346
+ source_datapipe = [
347
+ source_datapipe,
348
+ ]
349
+ if not isinstance(source_datapipe, IterDataPipe):
350
+ self.datapipe: IterDataPipe = IterableWrapper(source_datapipe) # type: ignore[assignment]
351
+ else:
352
+ self.datapipe = source_datapipe
353
+
354
+ assert s3_tool in __S3_TOOLS__, f"`s3_tool` has to be in {list(__S3_TOOLS__)}"
355
+
356
+ self.s3_tool = s3_tool
357
+
358
+ self.n_retries = n_retries
359
+ self.sleep = sleep_interval
360
+
361
+ def __iter__(self) -> Iterator[str]:
362
+ for root in self.datapipe:
363
+ files = None
364
+ for _ in range(self.n_retries):
365
+ try:
366
+ files = ls_aws(
367
+ path=root, tool=self.s3_tool, recursive=True, raise_errors=True
368
+ )
369
+ break
370
+ except subprocess.CalledProcessError:
371
+ time.sleep(self.sleep)
372
+
373
+ if files is None:
374
+ print(
375
+ f"Could not ls data expected under {root} in {self.n_retries} tries. Not yielding ..."
376
+ )
377
+ continue
378
+
379
+ for file in files:
380
+ yield file
381
+
382
+ def _download_tar(url, scratch, verbose=False, n_retries=100):
383
+ start = time.perf_counter()
384
+ if verbose:
385
+ _log(f"downloading {url}")
386
+ uid = "".join(filter(str.isalnum, os.path.splitext(url)[0]))
387
+ idx = 0
388
+ path = os.path.join(scratch, f"{os.getpid()}.{uid}.{idx}.tar")
389
+ while os.path.exists(path):
390
+ idx += 1
391
+ path = os.path.join(scratch, f"{os.getpid()}.{uid}.{idx}.tar")
392
+
393
+ os.makedirs(os.path.dirname(path), exist_ok=True)
394
+ for i_try in range(n_retries):
395
+ p = subprocess.call(
396
+ [
397
+ "/usr/local/bin/aws",
398
+ "s3",
399
+ "cp",
400
+ url,
401
+ path,
402
+ "--quiet",
403
+ ]
404
+ )
405
+
406
+ if p != 0:
407
+ # mostly to work around
408
+ # Error when retrieving credentials from Ec2InstanceMetadata: No
409
+ # credentials found in credential_source referenced in profile
410
+ # default
411
+ if i_try == 0:
412
+ # check if the requested file actually exists
413
+ if not ls_aws(url, skip_files=False, raise_errors=False):
414
+ raise FileNotFoundError(url)
415
+
416
+ if i_try + 1 < n_retries:
417
+ time.sleep(random.uniform(0.0, 0.1))
418
+ continue
419
+ else:
420
+ raise Exception(
421
+ f"File {url} should exist but failed to download after trying {i_try+1} times."
422
+ )
423
+ break
424
+
425
+ if verbose:
426
+ _log(
427
+ f"wrote {path} in {time.perf_counter() - start} secs after {i_try+1} tries."
428
+ )
429
+ return path
430
+
431
+
432
+ @functional_datapipe("download_with_s3_cli")
433
+ class S3CLITarDownloader(IterDataPipe[T_co]):
434
+ def __init__(
435
+ self,
436
+ source_datapipe,
437
+ n_retries: int = 10,
438
+ verbose: bool = False,
439
+ dl_root: str = "/scratch",
440
+ mode: str = "r:*",
441
+ aws_kwargs: Optional[dict] = None,
442
+ ignore_missing_files: bool = False,
443
+ ):
444
+ self.source_datapipe = source_datapipe
445
+ assert os.path.isdir(dl_root), f"`dl_root` {dl_root} is not a valid directory"
446
+ # get slurm job id and create a subdir in `dl_root` to download all the tars to
447
+ dl_root = get_job_temp_dir(dl_root)
448
+ os.makedirs(dl_root, exist_ok=True)
449
+ self.dl_root = dl_root
450
+ self.mode = mode
451
+ self.n_retries = n_retries
452
+ self.ignore_missing_files = ignore_missing_files
453
+
454
+ self.verbose = verbose
455
+
456
+ def _yield_next(self, url: str, local_path: str):
457
+ if self.verbose:
458
+ _log("popping queue")
459
+
460
+ if self.verbose:
461
+ _log(f"loading {local_path}")
462
+ tarstream = tarfile.open(local_path, self.mode)
463
+
464
+ if self.verbose:
465
+ _log(f"yielding {url}")
466
+
467
+ yield (url, StreamWrapper(tarstream))
468
+ if self.verbose:
469
+ _log("new tar request")
470
+
471
+ if self.verbose:
472
+ _log(f"removing previous tar at {local_path}")
473
+ try:
474
+ os.remove(local_path)
475
+ except FileNotFoundError:
476
+ _log(
477
+ f"WARNING: Could not find previous tar for deletion. Unless a clean-up was triggered this is unexpected. The location was {local_path}"
478
+ )
479
+
480
+ def __iter__(self):
481
+ with tempfile.TemporaryDirectory(dir=self.dl_root) as scratch:
482
+ for url in self.source_datapipe:
483
+ try:
484
+ local_path = _download_tar(
485
+ url, scratch, verbose=self.verbose, n_retries=self.n_retries
486
+ )
487
+ except FileNotFoundError as e:
488
+ if not self.ignore_missing_files:
489
+ raise e
490
+ else:
491
+ yield from self._yield_next(url, local_path)
492
+
493
+
494
+ @functional_datapipe("load_from_tar_and_handle_error")
495
+ class TarArchiveLoaderWithErrorHandlingIterDataPipe(TarArchiveLoader):
496
+ def __init__(
497
+ self,
498
+ datapipe: Iterable[Tuple[str, BufferedIOBase]],
499
+ mode: str = "r:*",
500
+ length: int = -1,
501
+ handler: Callable = wds.reraise_exception,
502
+ ):
503
+ super().__init__(datapipe=datapipe, mode=mode, length=length)
504
+ self.handler = handler
505
+
506
+ def __iter__(self) -> Iterator[Tuple[str, BufferedIOBase]]:
507
+ for data in self.datapipe:
508
+ validate_pathname_binary_tuple(data)
509
+ pathname, data_stream = data
510
+ try:
511
+ if isinstance(data_stream, StreamWrapper) and isinstance(
512
+ data_stream.file_obj, tarfile.TarFile
513
+ ):
514
+ tar = data_stream.file_obj
515
+ else:
516
+ reading_mode = (
517
+ self.mode
518
+ if hasattr(data_stream, "seekable") and data_stream.seekable()
519
+ else self.mode.replace(":", "|")
520
+ )
521
+ # typing.cast is used here to silence mypy's type checker
522
+ tar = tarfile.open(
523
+ fileobj=cast(Optional[IO[bytes]], data_stream),
524
+ mode=reading_mode,
525
+ )
526
+ for tarinfo in tar:
527
+ if not tarinfo.isfile():
528
+ continue
529
+ extracted_fobj = tar.extractfile(tarinfo)
530
+ if extracted_fobj is None:
531
+ warnings.warn(
532
+ f"failed to extract file {tarinfo.name} from source tarfile {pathname}"
533
+ )
534
+ raise tarfile.ExtractError
535
+ inner_pathname = os.path.normpath(
536
+ os.path.join(pathname, tarinfo.name)
537
+ )
538
+
539
+ yield inner_pathname, StreamWrapper(extracted_fobj, data_stream, name=inner_pathname) # type: ignore[misc]
540
+ except Exception as e:
541
+ warnings.warn(
542
+ f"Unable to extract files from corrupted tarfile stream {pathname} due to: {e}, abort!"
543
+ )
544
+ if self.handler(e):
545
+ if hasattr(e, "args") and len(e.args) > 0:
546
+ e.args = (e.args[0] + " @ " + str(pathname),) + e.args[1:]
547
+ finally:
548
+ if isinstance(data_stream, StreamWrapper):
549
+ data_stream.autoclose()
550
+
551
+ class DictCollator:
552
+ def __init__(
553
+ self,
554
+ combine_tensors: bool = True,
555
+ combine_scalars: bool = True,
556
+ timeout=None,
557
+ ):
558
+ self.combine_tensors = combine_tensors
559
+ self.combine_scalars = combine_scalars
560
+
561
+ collate = self._collate
562
+ if timeout is not None:
563
+ collate = timeout_wrapper(collate, timeout=timeout)
564
+ self.collate = collate
565
+
566
+ def __call__(self, samples):
567
+ return self.collate(samples)
568
+
569
+ def _collate(self, samples):
570
+ keys = set.intersection(*[set(sample.keys()) for sample in samples])
571
+ batched = {key: [] for key in keys}
572
+
573
+ for s in samples:
574
+ [batched[key].append(s[key]) for key in batched]
575
+
576
+ result = {}
577
+ for key in batched:
578
+ if isinstance(batched[key][0], (int, float)):
579
+ if self.combine_scalars:
580
+ result[key] = np.array(list(batched[key]))
581
+ elif isinstance(batched[key][0], Tensor):
582
+ if self.combine_tensors:
583
+ result[key] = stack(list(batched[key]))
584
+ elif isinstance(batched[key][0], np.ndarray):
585
+ if self.combine_tensors:
586
+ result[key] = np.array(list(batched[key]))
587
+ else:
588
+ result[key] = list(batched[key])
589
+
590
+ del samples
591
+ del batched
592
+ return result
593
+
594
+ def timeout_wrapper(func: Callable, timeout: Optional[float] = None) -> Callable:
595
+ if timeout is None or timeout <= 0.0:
596
+ return func
597
+
598
+ @functools.wraps(func)
599
+ def wrapper(*args, **kwargs):
600
+ result = [None]
601
+ exception = [None]
602
+ event = threading.Event()
603
+
604
+ def wrapped_func():
605
+ try:
606
+ result[0] = func(*args, **kwargs)
607
+ except Exception as e:
608
+ exception[0] = e
609
+ finally:
610
+ event.set()
611
+
612
+ thread = threading.Thread(target=wrapped_func)
613
+ thread.start()
614
+ event.wait(timeout)
615
+
616
+ if not event.is_set():
617
+ raise TimeoutError(f"Function call timed out (longer than {timeout} secs).")
618
+
619
+ thread.join()
620
+
621
+ if exception[0] is not None:
622
+ err = exception[0]
623
+ del exception
624
+ raise err
625
+
626
+ del thread
627
+ del exception
628
+ del wrapped_func
629
+ del event
630
+ del args
631
+ del kwargs
632
+
633
+ ret = result[0]
634
+ del result
635
+ return ret
636
+
637
+ return wrapper
638
+
639
+ class DecoderWithTimeout(wds.Decoder):
640
+ """Decode samples using a list of handlers.
641
+
642
+ For each key/data item, this iterates through the list of
643
+ handlers until some handler returns something other than None.
644
+ """
645
+
646
+ def __init__(self, *args, timeout: Optional[float] = None, **kwargs):
647
+ super().__init__(*args, **kwargs)
648
+
649
+ if timeout is not None:
650
+ self.decode = timeout_wrapper(self.decode, timeout)
651
+
652
+ def setup_decoder(
653
+ decoders,
654
+ additional_decoder_kwargs: Optional[Union[DictConfig, Dict]] = None,
655
+ ):
656
+ if not additional_decoder_kwargs:
657
+ additional_decoder_kwargs = {}
658
+
659
+ if not isinstance(decoders, (List, ListConfig)):
660
+ decoders = [decoders]
661
+ handlers = []
662
+ for decoder_spec in decoders:
663
+ if isinstance(decoder_spec, (Dict, DictConfig)):
664
+ decoder = instantiate(decoder_spec)
665
+ elif isinstance(decoder_spec, str):
666
+ decoder = wds.autodecode.ImageHandler(decoder_spec)
667
+ else:
668
+ raise TypeError(f"{decoder_spec} not a thing for decoders.")
669
+ handlers.append(decoder)
670
+ print(f"Adding decoder {decoder.__class__.__name__} to decoders.")
671
+
672
+ decoder = DecoderWithTimeout(
673
+ handlers,
674
+ partial=additional_decoder_kwargs.pop("partial", True),
675
+ **additional_decoder_kwargs, # todo
676
+ )
677
+ return decoder
678
+
679
+ def create_dataset(urls, is_s3=False, prefetch=None,
680
+ s3_buffer_size=None, s3_ignore_missing_files=True,
681
+ sample_shuffle=1, shard_shuffle=1000, cycle=True,
682
+ split_workers_along_tars=True, decoders="torch"):
683
+ if isinstance(urls, str):
684
+ urls = [urls]
685
+ dp = IterableWrapper(urls)
686
+ dp = dp.custom_shard_expand()
687
+ if is_s3:
688
+ dp = dp.list_files_with_aws_cli().filter(is_tar)
689
+ else:
690
+ dp = dp.list_files(masks="*.tar", recursive=True)
691
+
692
+ dp = ShuffledListDataPipe(list(dp), shuffle=shard_shuffle > 1, cycle=cycle)
693
+
694
+ if split_workers_along_tars:
695
+ dp = dp.sharding_filter()
696
+
697
+ if is_s3:
698
+ # if prefetch is None:
699
+ # s3_buffer_size = int(s3_buffer_size) if s3_buffer_size is not None else None
700
+ # dp = dp.wrapped_load_files_by_s3(
701
+ # buffer_size=s3_buffer_size,
702
+ # ignore_missing_files=s3_ignore_missing_files,
703
+ # )
704
+ dp = dp.download_with_s3_cli(ignore_missing_files=s3_ignore_missing_files)
705
+ else:
706
+ dp = dp.open_files(mode="b")
707
+
708
+ if prefetch is not None:
709
+ dp = dp.custom_prefetch(buffer_size=prefetch)
710
+
711
+ dp = dp.load_from_tar_and_handle_error(handler=error_handler)
712
+ dp = dp.webdataset()
713
+
714
+ if not split_workers_along_tars:
715
+ dp = dp.sharding_filter()
716
+
717
+ dp = dp.map_with_handler(
718
+ stream_reader,
719
+ handler=error_handler,
720
+ called_cls_name="StreamReader",
721
+ )
722
+
723
+ dp = dp.shuffle(buffer_size=sample_shuffle) if sample_shuffle > 1 else dp
724
+
725
+ dp = dp.map_with_handler(
726
+ to_our_format,
727
+ handler=error_handler,
728
+ called_cls_name="ToOurFormat",
729
+ )
730
+
731
+ decoder: DecoderWithTimeout = setup_decoder(decoders) # ,additional_decoder_kwargs)
732
+
733
+ dp = dp.map_with_handler(
734
+ decoder.decode,
735
+ handler=error_handler,
736
+ called_cls_name="Decoder",
737
+ )
738
+
739
+ return dp
740
+
741
+ def create_loader(
742
+ datapipeline: IterDataPipe,
743
+ batch_size: int,
744
+ num_workers: int,
745
+ partial: bool = False,
746
+ collation_fn: Optional[Union[Callable, Dict, DictConfig]] = DictCollator(),
747
+ batched_transforms: Optional[ListConfig] = None,
748
+ loader_kwargs: Optional[Union[Dict, DictConfig]] = None,
749
+ ) -> DataLoader:
750
+ if not loader_kwargs:
751
+ loader_kwargs = {}
752
+
753
+ loader_kwargs.pop("shuffle", None)
754
+ if not batched_transforms:
755
+ batched_transforms = []
756
+
757
+ print("#" * 100)
758
+ print("Building dataloader with the following parameters")
759
+ print(f"batch_size: {batch_size}, num_workers: {num_workers}")
760
+ for key in loader_kwargs:
761
+ print(key, ": ", loader_kwargs[key])
762
+ print("#" * 100)
763
+
764
+ datapipeline = datapipeline.batch(batch_size, drop_last=not partial)
765
+
766
+ if isinstance(collation_fn, (Dict, DictConfig)):
767
+ collation_fn = instantiate(collation_fn)
768
+ datapipeline = datapipeline.collate(collate_fn=collation_fn)
769
+ loader = DataLoader(
770
+ datapipeline, batch_size=None, num_workers=num_workers, **loader_kwargs
771
+ )
772
+ return loader
fMRI-foundation-model-old/fMRI-MAE/downstream.ipynb ADDED
@@ -0,0 +1,1162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "0859a181-0681-489e-9939-8d47614b9fa8",
7
+ "metadata": {
8
+ "tags": []
9
+ },
10
+ "outputs": [
11
+ {
12
+ "name": "stdout",
13
+ "output_type": "stream",
14
+ "text": [
15
+ "LOCAL RANK=0\n",
16
+ "NUM GPUS=1\n",
17
+ "GLOBAL RANK=0\n",
18
+ "batch_size 32\n",
19
+ "skipping deepspeed reconfiguration...\n",
20
+ "PID of this process = 617152\n",
21
+ "device: cuda\n",
22
+ "Distributed environment: NO\n",
23
+ "Num processes: 1\n",
24
+ "Process index: 0\n",
25
+ "Local process index: 0\n",
26
+ "Device: cuda\n",
27
+ "\n",
28
+ "Mixed precision type: fp16\n",
29
+ "\n",
30
+ "distributed = False num_devices = 1 local rank = 0 world size = 1 data_type = torch.float16\n"
31
+ ]
32
+ },
33
+ {
34
+ "name": "stderr",
35
+ "output_type": "stream",
36
+ "text": [
37
+ "/admin/home-paulscotti/found/lib/python3.10/site-packages/accelerate/accelerator.py:432: FutureWarning: Passing the following arguments to `Accelerator` is deprecated and will be removed in version 1.0 of Accelerate: dict_keys(['split_batches']). Please pass an `accelerate.DataLoaderConfiguration` instead: \n",
38
+ "dataloader_config = DataLoaderConfiguration(split_batches=False)\n",
39
+ " warnings.warn(\n"
40
+ ]
41
+ }
42
+ ],
43
+ "source": [
44
+ "# Import packages and setup gpu configuration.\n",
45
+ "# This code block shouldnt need to be adjusted!\n",
46
+ "import os\n",
47
+ "import sys\n",
48
+ "import json\n",
49
+ "import yaml\n",
50
+ "import numpy as np\n",
51
+ "import pandas as pd\n",
52
+ "import copy\n",
53
+ "import math\n",
54
+ "from einops import rearrange\n",
55
+ "from einops.layers.torch import Rearrange\n",
56
+ "import time\n",
57
+ "import random\n",
58
+ "import h5py\n",
59
+ "import webdataset as wds\n",
60
+ "import gc\n",
61
+ "import matplotlib.pyplot as plt\n",
62
+ "import torch\n",
63
+ "import torch.nn as nn\n",
64
+ "import torch.nn.functional as F\n",
65
+ "from torchvision import transforms\n",
66
+ "import utils\n",
67
+ "from models import *\n",
68
+ "from mindeye_models import *\n",
69
+ "import nibabel as nib\n",
70
+ "from nilearn import plotting\n",
71
+ "\n",
72
+ "# tf32 data type is faster than standard float32\n",
73
+ "torch.backends.cuda.matmul.allow_tf32 = True\n",
74
+ "\n",
75
+ "### Multi-GPU config ###\n",
76
+ "local_rank = os.getenv('LOCAL_RANK')\n",
77
+ "if local_rank is None: \n",
78
+ " local_rank = 0\n",
79
+ "else:\n",
80
+ " local_rank = int(local_rank)\n",
81
+ "print(f\"LOCAL RANK={local_rank}\")\n",
82
+ "\n",
83
+ "num_devices = os.getenv('NUM_GPUS')\n",
84
+ "if num_devices is None: \n",
85
+ " num_devices = 1\n",
86
+ "else:\n",
87
+ " num_devices = int(num_devices)\n",
88
+ "print(f\"NUM GPUS={num_devices}\")\n",
89
+ "distributed = True if num_devices>1 else False\n",
90
+ "\n",
91
+ "global_rank = os.getenv('RANK')\n",
92
+ "if global_rank is None:\n",
93
+ " global_rank = 0\n",
94
+ "else:\n",
95
+ " global_rank = int(global_rank)\n",
96
+ "print(f\"GLOBAL RANK={global_rank}\")\n",
97
+ "\n",
98
+ "if utils.is_interactive():\n",
99
+ " # Following allows you to change functions in models.py or utils.py and \n",
100
+ " # have this notebook automatically update with your revisions\n",
101
+ " %load_ext autoreload\n",
102
+ " %autoreload 2\n",
103
+ " from tqdm.notebook import tqdm\n",
104
+ "else:\n",
105
+ " from tqdm import tqdm\n",
106
+ "\n",
107
+ "# Load parameters from yaml config\n",
108
+ "config = yaml.load(open('config.yaml', 'r'), Loader=yaml.FullLoader)\n",
109
+ "\n",
110
+ "# create global variables from the config\n",
111
+ "for attribute_name in config.keys():\n",
112
+ " globals()[attribute_name] = config[f'{attribute_name}']\n",
113
+ " \n",
114
+ "# Load MindEye parameters from yaml config (will override any params with same name)\n",
115
+ "mindeye_config = yaml.load(open('mindeye_config.yaml', 'r'), Loader=yaml.FullLoader)\n",
116
+ "\n",
117
+ "# create global variables from the config\n",
118
+ "for attribute_name in mindeye_config.keys():\n",
119
+ " globals()[attribute_name] = mindeye_config[f'{attribute_name}']\n",
120
+ "\n",
121
+ "data_type = torch.float16 # change depending on your mixed_precision\n",
122
+ "\n",
123
+ "batch_size = global_batch_size // num_devices\n",
124
+ "print(\"batch_size\", batch_size)\n",
125
+ " \n",
126
+ "# First use \"accelerate config\" in terminal and setup using deepspeed stage 2 with CPU offloading\n",
127
+ "from accelerate import Accelerator\n",
128
+ "accelerator = Accelerator(split_batches=False, mixed_precision=\"fp16\")\n",
129
+ "\n",
130
+ "from accelerate.state import AcceleratorState\n",
131
+ "try:\n",
132
+ " AcceleratorState().deepspeed_plugin.deepspeed_config['train_micro_batch_size_per_gpu'] = batch_size\n",
133
+ " print(\"deepspeed reconfigured, train_micro_batch_size_per_gpu = \", batch_size)\n",
134
+ "except:\n",
135
+ " print(\"skipping deepspeed reconfiguration...\")\n",
136
+ "\n",
137
+ "print(\"PID of this process =\",os.getpid())\n",
138
+ "device = accelerator.device\n",
139
+ "print(\"device:\",device)\n",
140
+ "world_size = accelerator.state.num_processes\n",
141
+ "num_workers = num_devices\n",
142
+ "print(accelerator.state)\n",
143
+ "\n",
144
+ "print(\"distributed =\",distributed, \"num_devices =\", num_devices, \"local rank =\", local_rank, \"world size =\", world_size, \"data_type =\", data_type)\n",
145
+ "print = accelerator.print # only print if local_rank=0"
146
+ ]
147
+ },
148
+ {
149
+ "cell_type": "markdown",
150
+ "id": "c7b35bc5-8674-4061-a606-f44249d167df",
151
+ "metadata": {},
152
+ "source": [
153
+ "# Configuration"
154
+ ]
155
+ },
156
+ {
157
+ "cell_type": "code",
158
+ "execution_count": 2,
159
+ "id": "d9fa256a-cc44-4346-8d6c-1bed3779d5b0",
160
+ "metadata": {
161
+ "tags": []
162
+ },
163
+ "outputs": [
164
+ {
165
+ "name": "stdout",
166
+ "output_type": "stream",
167
+ "text": [
168
+ "mae config\n",
169
+ "\n",
170
+ " {'model_name': 'framepatchsize1_4gpu_bs8_3e7_norm_rope_atomcos_001contrastiveweight', 'use_cls_token': False, 'use_contrastive_loss': True, 'contrastive_loss_weight': 0.001, 'batch_size': 8, 'num_workers': 10, 'num_epochs': 100, 'seed': 42, 'max_lr': 3e-07, 'num_samples_per_epoch': 1024, 'ckpt_saving': True, 'ckpt_interval': 50, 'resume_from_ckpt': True, 'wandb_log': True, 'tube_start_masking_ratio': 0.9, 'tube_end_masking_ratio': 0.9, 'decoder_mask_ratio': 0.85, 'patch_size': [8, 8, 8], 'frame_patch_size': 1, 'use_rope_emb': True, 'masking_strategy': 'MNI', 'encoder_model': 'vit_base', 'decoder_model': 'vit_base', 'img_size': [88, 104, 72], 'num_frames': 4, 'is_s3': False, 'train_urls': ['/weka/proj-fmri/shared/NSD_MNI_wds/{000000..000738}.tar']}\n",
171
+ "mindeye_config\n",
172
+ " {'model_name': 'downstream_framepatchsize4_bs8_300ep_contr_nolinear', 'mae_model_name': 'framepatchsize1_4gpu_bs8_300ep_NEWcont_3e8_norm_rope_LONG', 'global_batch_size': 32, 'mixed_precision': 'fp16', 'num_epochs': 12, 'seed': 42, 'max_lr': 0.0003, 'multi_subject': False, 'multisubject_ckpt': 'None', 'ckpt_saving': False, 'ckpt_interval': 99, 'resume_from_ckpt': False, 'wandb_log': False, 'in_dim': 393216, 'hidden_dim': 2056, 'drop': 0.15, 'mixup_pct': 0.0, 'nsd_wds_path': '/weka/proj-fmri/shared/mindeyev2_dataset/wds', 'nsd_raw_path': '/weka/proj-fmri/shared/mindeyev2_dataset', 'nsd_image_path': '/weka/proj-fmri/shared/mindeyev2_dataset', 'num_sessions': 40}\n",
173
+ "mae_ckpt_pth /weka/proj-fmri/paulscotti/fMRI-foundation-model/ckpts/framepatchsize1_4gpu_bs8_300ep_NEWcont_3e8_norm_rope_LONG/last.pth\n",
174
+ "outdir /weka/proj-fmri/paulscotti/fMRI-foundation-model/ckpts/downstream_framepatchsize4_bs8_300ep_contr_nolinear\n",
175
+ "use_cls_token False\n",
176
+ "num_patches 5148\n"
177
+ ]
178
+ }
179
+ ],
180
+ "source": [
181
+ "print(\"mae config\\n\\n\",config)\n",
182
+ "print(\"mindeye_config\\n\",mindeye_config)\n",
183
+ "\n",
184
+ "# if utils.is_interactive():\n",
185
+ "# ckpt_saving = False\n",
186
+ "# wandb_log = False\n",
187
+ "\n",
188
+ "# seed all random functions\n",
189
+ "utils.seed_everything(seed)\n",
190
+ "\n",
191
+ "mae_ckpt_pth = os.path.abspath(f'../ckpts/{mae_model_name}/last.pth')\n",
192
+ "print(\"mae_ckpt_pth\", mae_ckpt_pth)\n",
193
+ "\n",
194
+ "outdir = os.path.abspath(f'../ckpts/{model_name}')\n",
195
+ "print(\"outdir\", outdir)\n",
196
+ "\n",
197
+ "use_cls_token = False #True if use_contrastive_loss else use_cls_token\n",
198
+ "print(\"use_cls_token\", use_cls_token)\n",
199
+ "\n",
200
+ "if type(patch_size) == int:\n",
201
+ " patch_size = [patch_size,patch_size,patch_size]\n",
202
+ "patch_depth = patch_size[0]\n",
203
+ "patch_height = patch_size[1]\n",
204
+ "patch_width = patch_size[2]\n",
205
+ "\n",
206
+ "num_patches = int(\n",
207
+ " (img_size[0] / patch_depth)\n",
208
+ " * (img_size[1] / patch_height)\n",
209
+ " * (img_size[2] / patch_width)\n",
210
+ " * num_frames\n",
211
+ ")\n",
212
+ "print(\"num_patches\", num_patches)"
213
+ ]
214
+ },
215
+ {
216
+ "cell_type": "markdown",
217
+ "id": "8115109a-30a1-448e-a1ea-1b115ed8cb6d",
218
+ "metadata": {},
219
+ "source": [
220
+ "# Load pretrained foundation model"
221
+ ]
222
+ },
223
+ {
224
+ "cell_type": "code",
225
+ "execution_count": 3,
226
+ "id": "1b40287e-bab2-4791-9407-e63ad5b0a3b4",
227
+ "metadata": {
228
+ "tags": []
229
+ },
230
+ "outputs": [
231
+ {
232
+ "name": "stdout",
233
+ "output_type": "stream",
234
+ "text": [
235
+ "param counts:\n",
236
+ "170,830,848 total\n",
237
+ "170,830,848 trainable\n"
238
+ ]
239
+ },
240
+ {
241
+ "data": {
242
+ "text/plain": [
243
+ "170830848"
244
+ ]
245
+ },
246
+ "execution_count": 3,
247
+ "metadata": {},
248
+ "output_type": "execute_result"
249
+ }
250
+ ],
251
+ "source": [
252
+ "vit_size = {\n",
253
+ " \"encoder\": encoder_model,\n",
254
+ " \"decoder\": decoder_model\n",
255
+ "}\n",
256
+ " \n",
257
+ "model = get_vit(\n",
258
+ " size=vit_size,\n",
259
+ " image_size=img_size, # depth, height, width\n",
260
+ " image_patch_size=(patch_depth,patch_height,patch_width), # depth, height, width patch size\n",
261
+ " frames=num_frames,\n",
262
+ " frame_patch_size=frame_patch_size,\n",
263
+ " channels=1,\n",
264
+ " use_rope_emb=use_rope_emb,\n",
265
+ " use_cls_token=use_cls_token,\n",
266
+ ")\n",
267
+ "utils.count_params(model)"
268
+ ]
269
+ },
270
+ {
271
+ "cell_type": "markdown",
272
+ "id": "97b45ad3-e8d5-40cd-b041-1defdbf15155",
273
+ "metadata": {},
274
+ "source": [
275
+ "## Load pretrained ckpt for MAE foundation model"
276
+ ]
277
+ },
278
+ {
279
+ "cell_type": "code",
280
+ "execution_count": 4,
281
+ "id": "62bb3f5f-cf7f-42c5-ab8c-d4bf143ba727",
282
+ "metadata": {
283
+ "tags": []
284
+ },
285
+ "outputs": [],
286
+ "source": [
287
+ "def save_ckpt(tag):\n",
288
+ " ckpt_path = outdir+f'/{tag}.pth'\n",
289
+ " if accelerator.is_main_process:\n",
290
+ " unwrapped_model = accelerator.unwrap_model(model)\n",
291
+ " torch.save({\n",
292
+ " 'epoch': epoch,\n",
293
+ " 'model_state_dict': unwrapped_model.state_dict(),\n",
294
+ " 'optimizer_state_dict': optimizer.state_dict(),\n",
295
+ " 'lr_scheduler': lr_scheduler.state_dict(),\n",
296
+ " 'train_losses': losses,\n",
297
+ " 'test_losses': test_losses,\n",
298
+ " 'lrs': lrs,\n",
299
+ " }, ckpt_path)\n",
300
+ " print(f\"\\n---saved {outdir}/{tag} ckpt!---\\n\")\n",
301
+ "\n",
302
+ "def load_ckpt(tag,load_lr=True,load_optimizer=True,load_epoch=True,strict=True,outdir=outdir,multisubj_loading=False): \n",
303
+ " print(f\"\\n---loading {outdir}/{tag}.pth ckpt---\\n\")\n",
304
+ " checkpoint = torch.load(outdir+'/last.pth', map_location='cpu')\n",
305
+ " state_dict = checkpoint['model_state_dict']\n",
306
+ " if multisubj_loading: # remove incompatible ridge layer that will otherwise error\n",
307
+ " state_dict.pop('ridge.linears.0.weight',None)\n",
308
+ " model.load_state_dict(state_dict, strict=strict)\n",
309
+ " if load_epoch:\n",
310
+ " globals()[\"epoch\"] = checkpoint['epoch']\n",
311
+ " print(\"Epoch\",epoch)\n",
312
+ " if load_optimizer:\n",
313
+ " optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n",
314
+ " if load_lr:\n",
315
+ " lr_scheduler.load_state_dict(checkpoint['lr_scheduler'])\n",
316
+ " del checkpoint"
317
+ ]
318
+ },
319
+ {
320
+ "cell_type": "code",
321
+ "execution_count": 5,
322
+ "id": "6c426853-d268-4daf-ac19-faa40c2cd362",
323
+ "metadata": {
324
+ "tags": []
325
+ },
326
+ "outputs": [],
327
+ "source": [
328
+ "checkpoint = torch.load(mae_ckpt_pth, map_location=device)\n",
329
+ "model.load_state_dict(checkpoint['model_state_dict'])#, strict=False)\n",
330
+ "\n",
331
+ "# set foundation model to evaluation\n",
332
+ "model.eval()\n",
333
+ "model.requires_grad_(False)\n",
334
+ "model.to(device)\n",
335
+ "pass"
336
+ ]
337
+ },
338
+ {
339
+ "cell_type": "markdown",
340
+ "id": "12fd89a4-955f-439d-b3c9-8f261c06eed2",
341
+ "metadata": {},
342
+ "source": [
343
+ "# Setup MindEye model"
344
+ ]
345
+ },
346
+ {
347
+ "cell_type": "code",
348
+ "execution_count": 6,
349
+ "id": "1dae53d1-abff-4ddb-98cd-2cab04b9f0cb",
350
+ "metadata": {
351
+ "tags": []
352
+ },
353
+ "outputs": [],
354
+ "source": [
355
+ "nsddata_raw_stimuli = pd.read_csv(f\"{nsd_raw_path}/nsddata_rawdata.csv\")\n",
356
+ "TR_delay = 3 # to account for bold hrf\n",
357
+ "train_TRs = np.round(nsddata_raw_stimuli[nsddata_raw_stimuli['shared1000'] == False]['global_TR_onsets'].values + TR_delay).astype(np.int32)\n",
358
+ "test_TRs = np.round(nsddata_raw_stimuli[nsddata_raw_stimuli['shared1000'] == True]['global_TR_onsets'].values + TR_delay).astype(np.int32)"
359
+ ]
360
+ },
361
+ {
362
+ "cell_type": "code",
363
+ "execution_count": 7,
364
+ "id": "4d5015e9-120e-4240-886c-6959796e8faf",
365
+ "metadata": {
366
+ "tags": []
367
+ },
368
+ "outputs": [
369
+ {
370
+ "name": "stdout",
371
+ "output_type": "stream",
372
+ "text": [
373
+ "Loaded all 73k possible NSD images! torch.Size([73000, 3, 224, 224])\n"
374
+ ]
375
+ }
376
+ ],
377
+ "source": [
378
+ "# Load 73k NSD images\n",
379
+ "f = h5py.File(f'{nsd_image_path}/coco_images_224_float16.hdf5', 'r')\n",
380
+ "images = f['images'][:] \n",
381
+ "images = torch.Tensor(images).to(\"cpu\").to(data_type)\n",
382
+ "print(\"Loaded all 73k possible NSD images!\", images.shape)\n",
383
+ "\n",
384
+ "# Load MindEye hdf5\n",
385
+ "f = h5py.File(f'{nsd_raw_path}/subj01_mnidata.h5', 'r') #subj01_rawdata_old.h5\n",
386
+ "mindeye_global_trs = f['global_trs'][:]\n",
387
+ "mindeye_funcs = f['funcs']"
388
+ ]
389
+ },
390
+ {
391
+ "cell_type": "code",
392
+ "execution_count": 8,
393
+ "id": "d79bcf35-45a6-4dc5-90da-c96c3a596401",
394
+ "metadata": {
395
+ "tags": []
396
+ },
397
+ "outputs": [
398
+ {
399
+ "data": {
400
+ "text/plain": [
401
+ "FrozenOpenCLIPImageEmbedder(\n",
402
+ " (model): CLIP(\n",
403
+ " (visual): VisionTransformer(\n",
404
+ " (conv1): Conv2d(3, 1664, kernel_size=(14, 14), stride=(14, 14), bias=False)\n",
405
+ " (patch_dropout): Identity()\n",
406
+ " (ln_pre): LayerNorm((1664,), eps=1e-05, elementwise_affine=True)\n",
407
+ " (transformer): Transformer(\n",
408
+ " (resblocks): ModuleList(\n",
409
+ " (0-47): 48 x ResidualAttentionBlock(\n",
410
+ " (ln_1): LayerNorm((1664,), eps=1e-05, elementwise_affine=True)\n",
411
+ " (attn): MultiheadAttention(\n",
412
+ " (out_proj): NonDynamicallyQuantizableLinear(in_features=1664, out_features=1664, bias=True)\n",
413
+ " )\n",
414
+ " (ls_1): Identity()\n",
415
+ " (ln_2): LayerNorm((1664,), eps=1e-05, elementwise_affine=True)\n",
416
+ " (mlp): Sequential(\n",
417
+ " (c_fc): Linear(in_features=1664, out_features=8192, bias=True)\n",
418
+ " (gelu): GELU(approximate='none')\n",
419
+ " (c_proj): Linear(in_features=8192, out_features=1664, bias=True)\n",
420
+ " )\n",
421
+ " (ls_2): Identity()\n",
422
+ " )\n",
423
+ " )\n",
424
+ " )\n",
425
+ " (ln_post): LayerNorm((1664,), eps=1e-05, elementwise_affine=True)\n",
426
+ " )\n",
427
+ " (token_embedding): Embedding(49408, 1280)\n",
428
+ " (ln_final): LayerNorm((1280,), eps=1e-05, elementwise_affine=True)\n",
429
+ " )\n",
430
+ ")"
431
+ ]
432
+ },
433
+ "execution_count": 8,
434
+ "metadata": {},
435
+ "output_type": "execute_result"
436
+ }
437
+ ],
438
+ "source": [
439
+ "# clip_img_embedder = FrozenOpenCLIPImageEmbedder(\n",
440
+ "# arch=\"ViT-bigG-14\",\n",
441
+ "# version=\"laion2b_s39b_b160k\",\n",
442
+ "# output_tokens=True,\n",
443
+ "# only_tokens=True,\n",
444
+ "# )\n",
445
+ "# clip_seq_dim = 256\n",
446
+ "# clip_emb_dim = 1664\n",
447
+ "# clip_img_embedder.to(device)\n",
448
+ "\n",
449
+ "clip_img_embedder = FrozenOpenCLIPImageEmbedder(\n",
450
+ " arch=\"ViT-bigG-14\",\n",
451
+ " version=\"laion2b_s39b_b160k\",\n",
452
+ " output_tokens=False,\n",
453
+ " only_tokens=False,\n",
454
+ " init_device=device,\n",
455
+ " device=device,\n",
456
+ ")\n",
457
+ "clip_seq_dim = 1\n",
458
+ "clip_emb_dim = 1280\n",
459
+ "clip_img_embedder.to(device)"
460
+ ]
461
+ },
462
+ {
463
+ "cell_type": "code",
464
+ "execution_count": 9,
465
+ "id": "d89d95f0-b52e-439f-ba5d-c13e2897fe31",
466
+ "metadata": {
467
+ "tags": []
468
+ },
469
+ "outputs": [
470
+ {
471
+ "name": "stdout",
472
+ "output_type": "stream",
473
+ "text": [
474
+ "dividing batch size by subj_list, which will then be concatenated across subj during training...\n",
475
+ "batch_size = 32 num_iterations_per_epoch = 11 num_samples_per_epoch = 375\n",
476
+ "Training with 40 sessions\n",
477
+ "/weka/proj-fmri/shared/mindeyev2_dataset/wds/subj01/train/{0..39}.tar\n",
478
+ "Loaded all subj train dls and betas!\n",
479
+ "\n",
480
+ "/weka/proj-fmri/shared/mindeyev2_dataset/wds/subj01/new_test/0.tar\n",
481
+ "Loaded test dl for subj1!\n",
482
+ "\n"
483
+ ]
484
+ }
485
+ ],
486
+ "source": [
487
+ "subj = s = 1\n",
488
+ "subj_list = [subj]\n",
489
+ "\n",
490
+ "# if multi_subject:\n",
491
+ "# nsessions_allsubj=np.array([40, 40, 32, 30, 40, 32, 40, 30])\n",
492
+ "# num_samples_per_epoch = (750*40) // num_devices \n",
493
+ "# else:\n",
494
+ "# num_samples_per_epoch = (750*num_sessions) // num_devices \n",
495
+ "\n",
496
+ "num_samples_per_epoch = 375 #(750*num_sessions) // num_devices\n",
497
+ "\n",
498
+ "print(\"dividing batch size by subj_list, which will then be concatenated across subj during training...\") \n",
499
+ "batch_size = batch_size // len(subj_list)\n",
500
+ "num_iterations_per_epoch = num_samples_per_epoch // (batch_size*len(subj_list))\n",
501
+ "print(\"batch_size =\", batch_size, \"num_iterations_per_epoch =\",num_iterations_per_epoch, \"num_samples_per_epoch =\",num_samples_per_epoch)\n",
502
+ "\n",
503
+ "train_data = {}\n",
504
+ "train_dl = {}\n",
505
+ "\n",
506
+ "print(f\"Training with {num_sessions} sessions\")\n",
507
+ "train_url = f\"{nsd_wds_path}/subj0{s}/train/\" + \"{0..\" + f\"{num_sessions-1}\" + \"}.tar\"\n",
508
+ "print(train_url)\n",
509
+ " \n",
510
+ "train_data[f'subj0{s}'] = wds.WebDataset(train_url,resampled=True,nodesplitter=utils.my_split_by_node)\\\n",
511
+ " .shuffle(750, initial=1500, rng=random.Random(42))\\\n",
512
+ " .decode(\"torch\")\\\n",
513
+ " .rename(behav=\"behav.npy\", past_behav=\"past_behav.npy\", future_behav=\"future_behav.npy\", olds_behav=\"olds_behav.npy\")\\\n",
514
+ " .to_tuple(*[\"behav\", \"past_behav\", \"future_behav\", \"olds_behav\"])\n",
515
+ "# train_dl[f'subj0{s}'] = torch.utils.data.DataLoader(train_data[f'subj0{s}'], batch_size=batch_size, shuffle=False, drop_last=True, pin_memory=True)\n",
516
+ "train_dl[f'subj0{s}'] = wds.WebLoader(\n",
517
+ " train_data[f'subj0{s}'].batched(batch_size), \n",
518
+ " pin_memory=True,\n",
519
+ " shuffle=False,\n",
520
+ " batch_size=None,\n",
521
+ " num_workers=num_workers, \n",
522
+ " persistent_workers=num_workers>0,\n",
523
+ ").with_epoch(num_iterations_per_epoch)\n",
524
+ "\n",
525
+ "print(\"Loaded all subj train dls and betas!\\n\")\n",
526
+ "if subj==3:\n",
527
+ " num_test=2371\n",
528
+ "elif subj==4:\n",
529
+ " num_test=2188\n",
530
+ "elif subj==6:\n",
531
+ " num_test=2371\n",
532
+ "elif subj==8:\n",
533
+ " num_test=2188\n",
534
+ "else:\n",
535
+ " num_test=3000\n",
536
+ "test_url = f\"{nsd_wds_path}/subj0{subj}/new_test/\" + \"0.tar\"\n",
537
+ "print(test_url)\n",
538
+ "test_data = wds.WebDataset(test_url,resampled=True,nodesplitter=utils.my_split_by_node)\\\n",
539
+ " .decode(\"torch\")\\\n",
540
+ " .rename(behav=\"behav.npy\", past_behav=\"past_behav.npy\", future_behav=\"future_behav.npy\", olds_behav=\"olds_behav.npy\")\\\n",
541
+ " .to_tuple(*[\"behav\", \"past_behav\", \"future_behav\", \"olds_behav\"])\n",
542
+ "# test_dl = torch.utils.data.DataLoader(test_data, batch_size=num_test, shuffle=False, drop_last=True, pin_memory=True)\n",
543
+ "test_dl = wds.WebLoader(\n",
544
+ " test_data.batched(num_test),\n",
545
+ " pin_memory=True,\n",
546
+ " shuffle=False,\n",
547
+ " batch_size=None,\n",
548
+ " num_workers=num_workers,\n",
549
+ " persistent_workers=num_workers>0,\n",
550
+ ").with_epoch(10)\n",
551
+ "print(f\"Loaded test dl for subj{subj}!\\n\")"
552
+ ]
553
+ },
554
+ {
555
+ "cell_type": "code",
556
+ "execution_count": 10,
557
+ "id": "e100e116-4b98-4c15-bd8e-252871e72517",
558
+ "metadata": {
559
+ "tags": []
560
+ },
561
+ "outputs": [
562
+ {
563
+ "name": "stdout",
564
+ "output_type": "stream",
565
+ "text": [
566
+ "param counts:\n",
567
+ "507,388,672 total\n",
568
+ "507,388,672 trainable\n",
569
+ "param counts:\n",
570
+ "507,388,672 total\n",
571
+ "507,388,672 trainable\n"
572
+ ]
573
+ },
574
+ {
575
+ "data": {
576
+ "text/plain": [
577
+ "507388672"
578
+ ]
579
+ },
580
+ "execution_count": 10,
581
+ "metadata": {},
582
+ "output_type": "execute_result"
583
+ }
584
+ ],
585
+ "source": [
586
+ "class MindEyeModule(nn.Module):\n",
587
+ " def __init__(self):\n",
588
+ " super(MindEyeModule, self).__init__()\n",
589
+ " def forward(self, x):\n",
590
+ " return x\n",
591
+ "\n",
592
+ "# class RidgeRegression(torch.nn.Module):\n",
593
+ "# # make sure to add weight_decay when initializing optimizer\n",
594
+ "# def __init__(self, input_sizes, out_features, seq_len=1): \n",
595
+ "# super(RidgeRegression, self).__init__()\n",
596
+ "# self.seq_len = seq_len\n",
597
+ "# self.out_features = out_features\n",
598
+ "# self.linears = torch.nn.ModuleList([\n",
599
+ "# torch.nn.Linear(input_size, out_features) for input_size in input_sizes\n",
600
+ "# ])\n",
601
+ "# def forward(self, x, subj_idx):\n",
602
+ "# out = torch.cat([self.linears[subj_idx](x[:,seq]).unsqueeze(1) for seq in range(self.seq_len)], dim=1)\n",
603
+ "# return out\n",
604
+ " \n",
605
+ "class MLP(torch.nn.Module):\n",
606
+ " def __init__(self, input_sizes, out_features, seq_len=0): \n",
607
+ " super(MLP, self).__init__()\n",
608
+ " self.input_sizes = input_sizes[0]\n",
609
+ " self.out_features = out_features\n",
610
+ " self.mlp = nn.Sequential(\n",
611
+ " nn.LayerNorm(self.input_sizes),\n",
612
+ " nn.GELU(),\n",
613
+ " nn.Linear(self.input_sizes, out_features),\n",
614
+ " nn.LayerNorm(out_features),\n",
615
+ " nn.GELU(),\n",
616
+ " nn.Linear(out_features, out_features),\n",
617
+ " nn.LayerNorm(out_features),\n",
618
+ " nn.GELU(),\n",
619
+ " nn.Linear(out_features, out_features)\n",
620
+ " )\n",
621
+ " def forward(self, x, z=None):\n",
622
+ " out = self.mlp(x[:,0])\n",
623
+ " return out\n",
624
+ "\n",
625
+ "\n",
626
+ "mindeye = MindEyeModule()\n",
627
+ "mindeye.ridge = MLP(np.array([in_dim]), out_features=clip_emb_dim*clip_seq_dim)\n",
628
+ "# mindeye.backbone = BrainNetwork(h=hidden_dim, in_dim=hidden_dim, n_blocks=4, drop=drop,\n",
629
+ "# clip_size=clip_emb_dim, out_dim=clip_emb_dim*clip_seq_dim, clip_scale=1)\n",
630
+ "utils.count_params(mindeye.ridge)\n",
631
+ "# utils.count_params(mindeye.backbone)\n",
632
+ "utils.count_params(mindeye)"
633
+ ]
634
+ },
635
+ {
636
+ "cell_type": "code",
637
+ "execution_count": 11,
638
+ "id": "47ad9ec0-e0b5-4d2c-9d9b-027a85d176b1",
639
+ "metadata": {
640
+ "tags": []
641
+ },
642
+ "outputs": [
643
+ {
644
+ "name": "stdout",
645
+ "output_type": "stream",
646
+ "text": [
647
+ "total_steps 132\n",
648
+ "\n",
649
+ "Done with model preparations!\n",
650
+ "param counts:\n",
651
+ "507,388,672 total\n",
652
+ "507,388,672 trainable\n"
653
+ ]
654
+ }
655
+ ],
656
+ "source": [
657
+ "no_decay = [\"bias\", \"LayerNorm.bias\", \"LayerNorm.weight\"]\n",
658
+ "opt_grouped_parameters = [\n",
659
+ " {'params': [p for n, p in mindeye.ridge.named_parameters()], 'weight_decay': 1e-2},\n",
660
+ " # {'params': [p for n, p in mindeye.backbone.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 1e-2},\n",
661
+ " # {'params': [p for n, p in mindeye.backbone.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0},\n",
662
+ "]\n",
663
+ "\n",
664
+ "optimizer = torch.optim.AdamW(opt_grouped_parameters, lr=max_lr)\n",
665
+ "\n",
666
+ "total_steps = num_epochs * num_iterations_per_epoch\n",
667
+ "print(\"total_steps\", total_steps)\n",
668
+ "pct_start = 2/num_epochs if num_epochs>1 else 1.\n",
669
+ "lr_scheduler = torch.optim.lr_scheduler.OneCycleLR(\n",
670
+ " optimizer,\n",
671
+ " max_lr=max_lr,\n",
672
+ " total_steps=total_steps,\n",
673
+ ")\n",
674
+ "\n",
675
+ "print(\"\\nDone with model preparations!\")\n",
676
+ "num_params = utils.count_params(mindeye)"
677
+ ]
678
+ },
679
+ {
680
+ "cell_type": "markdown",
681
+ "id": "2661e670-89b3-4598-a0ba-774e376d0047",
682
+ "metadata": {},
683
+ "source": [
684
+ "# Start wandb (if enabled)"
685
+ ]
686
+ },
687
+ {
688
+ "cell_type": "code",
689
+ "execution_count": 12,
690
+ "id": "eae9f959-4457-428a-afb5-45e852490ffe",
691
+ "metadata": {},
692
+ "outputs": [],
693
+ "source": [
694
+ "if accelerator.is_main_process and wandb_log: # only use main process for wandb logging\n",
695
+ " import wandb\n",
696
+ " wandb_project = 'found_downstream'\n",
697
+ " print(f\"wandb {wandb_project} run {model_name}\")\n",
698
+ " # need to configure wandb beforehand in terminal with \"wandb init\"!\n",
699
+ " wandb_config = {\n",
700
+ " \"model_name\": model_name,\n",
701
+ " \"mae_model_name\": mae_model_name,\n",
702
+ " \"global_batch_size\": global_batch_size,\n",
703
+ " \"batch_size\": batch_size,\n",
704
+ " \"num_epochs\": num_epochs,\n",
705
+ " \"num_sessions\": num_sessions,\n",
706
+ " \"num_samples_per_epoch\": num_samples_per_epoch,\n",
707
+ " \"in_dim\": in_dim,\n",
708
+ " \"hidden_dim\": hidden_dim,\n",
709
+ " \"mixup_pct\": mixup_pct,\n",
710
+ " \"num_params\": num_params,\n",
711
+ " \"max_lr\": max_lr,\n",
712
+ " \"ckpt_interval\": ckpt_interval,\n",
713
+ " \"ckpt_saving\": ckpt_saving,\n",
714
+ " \"seed\": seed,\n",
715
+ " \"distributed\": distributed,\n",
716
+ " \"num_devices\": num_devices,\n",
717
+ " \"world_size\": world_size,\n",
718
+ " \"train_url\": train_url,\n",
719
+ " \"test_url\": test_url,\n",
720
+ " }\n",
721
+ " print(\"wandb_config:\\n\",wandb_config)\n",
722
+ " print(\"wandb_id:\",model_name)\n",
723
+ " wandb.init(\n",
724
+ " id=model_name,\n",
725
+ " project=wandb_project,\n",
726
+ " name=model_name,\n",
727
+ " config=wandb_config,\n",
728
+ " resume=\"allow\",\n",
729
+ " )\n",
730
+ "else:\n",
731
+ " wandb_log = False"
732
+ ]
733
+ },
734
+ {
735
+ "cell_type": "markdown",
736
+ "id": "7230f232-1700-4967-951d-fb566ae637b3",
737
+ "metadata": {},
738
+ "source": [
739
+ "# Train MindEye model using foundation model inputs"
740
+ ]
741
+ },
742
+ {
743
+ "cell_type": "code",
744
+ "execution_count": 13,
745
+ "id": "a8b849b8-04b2-4908-9b46-a4fa9a565d5a",
746
+ "metadata": {
747
+ "tags": []
748
+ },
749
+ "outputs": [],
750
+ "source": [
751
+ "epoch = 0\n",
752
+ "losses, test_losses, lrs = [], [], []\n",
753
+ "best_test_loss = 1e9\n",
754
+ "torch.cuda.empty_cache()"
755
+ ]
756
+ },
757
+ {
758
+ "cell_type": "code",
759
+ "execution_count": 14,
760
+ "id": "b91b0e3c-c939-4717-8abc-2f3b8c39d012",
761
+ "metadata": {},
762
+ "outputs": [],
763
+ "source": [
764
+ "# if masking_strategy==\"MNI\":\n",
765
+ "from einops.layers.torch import Rearrange\n",
766
+ "\n",
767
+ "MNI_brain = nib.load(\"/weka/proj-fmri/paulscotti/fMRI-foundation-model/dataset_creation/afni_conversion/tpl-MNI152NLin2009cAsym_res-02_T1w_brain.nii.gz\").get_fdata()\n",
768
+ "brain_pos_voxels = MNI_brain[6:94,8:112,10:82]\n",
769
+ "\n",
770
+ "# brain_pos_voxels = brain_pos_voxels[:,30:31,:]\n",
771
+ "\n",
772
+ "brain_pos_pats = Rearrange(\n",
773
+ " \"b c (f pf) (d pd) (h ph) (w pw) -> b f d h w (pd ph pw pf c)\",\n",
774
+ " pd=patch_depth,\n",
775
+ " ph=patch_height,\n",
776
+ " pw=patch_width,\n",
777
+ " pf=1,\n",
778
+ " )(torch.Tensor(brain_pos_voxels)[None,None,None])\n",
779
+ "\n",
780
+ "brain_pos_pats_vit = rearrange(brain_pos_pats, \"b ... d -> b (...) d\").mean(-1)[0]\n",
781
+ " \n",
782
+ "tube_mask = torch.zeros(num_patches // num_frames).to(torch.bool)\n",
783
+ "batch_positive_approx = (brain_pos_pats_vit > 0)\n",
784
+ "mask_idx_candidates = torch.where(batch_positive_approx)[0]\n",
785
+ "mask_idx_candidates = mask_idx_candidates[torch.randperm(len(mask_idx_candidates))]\n",
786
+ "tube_idx = mask_idx_candidates[:int(num_patches / num_frames * (1 - tube_end_masking_ratio))]\n",
787
+ "tube_mask[tube_idx] = True\n",
788
+ "tube_mask = tube_mask.tile(num_frames//frame_patch_size)"
789
+ ]
790
+ },
791
+ {
792
+ "cell_type": "code",
793
+ "execution_count": 15,
794
+ "id": "cac228e7-bdf8-4cc7-ab9d-891b5bd5abbd",
795
+ "metadata": {},
796
+ "outputs": [],
797
+ "source": [
798
+ "# load multisubject stage1 ckpt if set\n",
799
+ "if multisubject_ckpt!=\"None\" and not resume_from_ckpt:\n",
800
+ " load_ckpt(\"last\",outdir=multisubject_ckpt,load_lr=False,load_optimizer=False,load_epoch=False,strict=False,multisubj_loading=True)\n",
801
+ " \n",
802
+ "# load saved ckpt model weights into current model\n",
803
+ "if resume_from_ckpt:\n",
804
+ " load_ckpt(\"last\",load_lr=True,load_optimizer=True,load_epoch=True)\n",
805
+ "elif wandb_log:\n",
806
+ " if wandb.run.resumed:\n",
807
+ " load_ckpt(\"last\",load_lr=True,load_optimizer=True,load_epoch=True)"
808
+ ]
809
+ },
810
+ {
811
+ "cell_type": "code",
812
+ "execution_count": 16,
813
+ "id": "b0a23e2f-eb06-4940-9b41-ca0e36dd210f",
814
+ "metadata": {},
815
+ "outputs": [],
816
+ "source": [
817
+ "train_dls = [train_dl[f'subj0{s}'] for s in subj_list]\n",
818
+ "\n",
819
+ "mindeye, optimizer, *train_dls, lr_scheduler = accelerator.prepare(mindeye, optimizer, *train_dls, lr_scheduler)\n",
820
+ "# leaving out test_dl since we will only have local_rank 0 device do evals"
821
+ ]
822
+ },
823
+ {
824
+ "cell_type": "code",
825
+ "execution_count": 17,
826
+ "id": "dd08879b-0177-4cac-9438-a82ce0805bc1",
827
+ "metadata": {
828
+ "tags": []
829
+ },
830
+ "outputs": [
831
+ {
832
+ "name": "stdout",
833
+ "output_type": "stream",
834
+ "text": [
835
+ "downstream_framepatchsize4_bs8_300ep_contr_nolinear starting with epoch 0 / 12\n"
836
+ ]
837
+ },
838
+ {
839
+ "data": {
840
+ "application/vnd.jupyter.widget-view+json": {
841
+ "model_id": "60a889b1ee2d4d7a8acff2ffaf3a1ca1",
842
+ "version_major": 2,
843
+ "version_minor": 0
844
+ },
845
+ "text/plain": [
846
+ " 0%| | 0/12 [00:00<?, ?it/s]"
847
+ ]
848
+ },
849
+ "metadata": {},
850
+ "output_type": "display_data"
851
+ },
852
+ {
853
+ "data": {
854
+ "application/vnd.jupyter.widget-view+json": {
855
+ "model_id": "6cc02dd1cf08486892e038df7de47cf8",
856
+ "version_major": 2,
857
+ "version_minor": 0
858
+ },
859
+ "text/plain": [
860
+ " 0%| | 0/11 [00:00<?, ?it/s]"
861
+ ]
862
+ },
863
+ "metadata": {},
864
+ "output_type": "display_data"
865
+ },
866
+ {
867
+ "ename": "NameError",
868
+ "evalue": "name 'err' is not defined",
869
+ "output_type": "error",
870
+ "traceback": [
871
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
872
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
873
+ "Cell \u001b[0;32mIn[17], line 51\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[38;5;66;03m## Process it through pretrained MAE ##\u001b[39;00m\n\u001b[1;32m 50\u001b[0m encoder_out \u001b[38;5;241m=\u001b[39m model(voxels_raw, encoder_mask\u001b[38;5;241m=\u001b[39mtube_mask)\n\u001b[0;32m---> 51\u001b[0m \u001b[43merr\u001b[49m\n\u001b[1;32m 52\u001b[0m \u001b[38;5;66;03m# encoder_out = bn(encoder_out)\u001b[39;00m\n\u001b[1;32m 54\u001b[0m voxel0 \u001b[38;5;241m=\u001b[39m encoder_out\u001b[38;5;241m.\u001b[39mflatten(\u001b[38;5;241m1\u001b[39m)\u001b[38;5;241m.\u001b[39munsqueeze(\u001b[38;5;241m1\u001b[39m)\n",
874
+ "\u001b[0;31mNameError\u001b[0m: name 'err' is not defined"
875
+ ]
876
+ }
877
+ ],
878
+ "source": [
879
+ "print(f\"{model_name} starting with epoch {epoch} / {num_epochs}\")\n",
880
+ "progress_bar = tqdm(range(epoch, num_epochs), disable=not accelerator.is_main_process)\n",
881
+ "mse = nn.MSELoss()\n",
882
+ "soft_loss_temps = utils.cosine_anneal(0.004, 0.0075, num_epochs - int(mixup_pct * num_epochs))\n",
883
+ "\n",
884
+ "bn = nn.BatchNorm1d(512,affine=False).to(device)\n",
885
+ "\n",
886
+ "test_image=None\n",
887
+ "num_test_eval=batch_size # should instead be average same-image 300 to mimic MindEye2 retrieval evaluation\n",
888
+ "\n",
889
+ "for epoch in progress_bar:\n",
890
+ " mindeye.train()\n",
891
+ "\n",
892
+ " fwd_percent_correct = 0.\n",
893
+ " bwd_percent_correct = 0.\n",
894
+ " test_fwd_percent_correct = 0.\n",
895
+ " test_bwd_percent_correct = 0.\n",
896
+ " loss_clip_total = 0.\n",
897
+ " test_loss_clip_total = 0.\n",
898
+ "\n",
899
+ " # pre-load all batches for this epoch (it's MUCH faster to pre-load in bulk than to separate loading per batch)\n",
900
+ " voxel_iters = {} # empty dict because diff subjects have differing # of voxels\n",
901
+ " image_iters = torch.zeros(num_iterations_per_epoch, batch_size*len(subj_list), 3, 224, 224).float()\n",
902
+ " annot_iters = {}\n",
903
+ " perm_iters, betas_iters, select_iters = {}, {}, {}\n",
904
+ " for s, train_dl in enumerate(train_dls):\n",
905
+ " with torch.cuda.amp.autocast(dtype=data_type):\n",
906
+ " for iter, (behav0, past_behav0, future_behav0, old_behav0) in enumerate(tqdm(train_dl,total=num_iterations_per_epoch)):\n",
907
+ " image0 = images[behav0[:,0,0].cpu().long()].float()\n",
908
+ " image_iters[iter,s*batch_size:s*batch_size+batch_size] = image0\n",
909
+ "\n",
910
+ " # if images are not fully preloaded, then can do this inefficient but more memory friendly approach\n",
911
+ " # for ib,b in enumerate(behav0[:,0,0].cpu().long()):\n",
912
+ " # if ib==0:\n",
913
+ " # image0 = torch.Tensor(images[[b]])\n",
914
+ " # else:\n",
915
+ " # image0 = torch.vstack((image0, torch.Tensor(images[[b]])))\n",
916
+ " # image_iters[iter,s*batch_size:s*batch_size+batch_size] = image0\n",
917
+ " \n",
918
+ " # get the corresponding raw voxel time series\n",
919
+ " for ib,b in enumerate(behav0[:,0,5].cpu().long().numpy()):\n",
920
+ " tr = (nsddata_raw_stimuli[nsddata_raw_stimuli['global_trial'].isin([b.item()])]['global_TR_onsets'].values + TR_delay).astype(np.int32).item()\n",
921
+ " if ib==0:\n",
922
+ " voxels_raw = mindeye_funcs[tr-2:tr+2][None][None]\n",
923
+ " else:\n",
924
+ " voxels_raw = np.vstack((voxels_raw, mindeye_funcs[tr-2:tr+2][None][None]))\n",
925
+ " voxels_raw = torch.Tensor(voxels_raw).clamp(0,1).to(device)\n",
926
+ " \n",
927
+ " ## Process it through pretrained MAE ##\n",
928
+ " encoder_out = model(voxels_raw, encoder_mask=tube_mask)\n",
929
+ " err\n",
930
+ " # encoder_out = bn(encoder_out)\n",
931
+ " \n",
932
+ " voxel0 = encoder_out.flatten(1).unsqueeze(1)\n",
933
+ " voxel0 = nn.functional.normalize(voxel0,dim=-1).cpu()\n",
934
+ " \n",
935
+ " assert len(voxel0) == batch_size\n",
936
+ "\n",
937
+ " if epoch < int(mixup_pct * num_epochs):\n",
938
+ " voxel0, perm, betas, select = utils.mixco(voxel0)\n",
939
+ " perm_iters[f\"subj0{subj_list[s]}_iter{iter}\"] = perm\n",
940
+ " betas_iters[f\"subj0{subj_list[s]}_iter{iter}\"] = betas\n",
941
+ " select_iters[f\"subj0{subj_list[s]}_iter{iter}\"] = select\n",
942
+ "\n",
943
+ " voxel_iters[f\"subj0{subj_list[s]}_iter{iter}\"] = voxel0\n",
944
+ "\n",
945
+ " if iter >= num_iterations_per_epoch:\n",
946
+ " break\n",
947
+ "\n",
948
+ " # you now have voxel_iters and image_iters with num_iterations_per_epoch batches each\n",
949
+ " for train_i in range(num_iterations_per_epoch):\n",
950
+ " with torch.cuda.amp.autocast(dtype=data_type):\n",
951
+ " optimizer.zero_grad()\n",
952
+ " loss=0.\n",
953
+ "\n",
954
+ " voxel_list = [voxel_iters[f\"subj0{s}_iter{train_i}\"].detach().to(device) for s in subj_list]\n",
955
+ " image = image_iters[train_i].detach()\n",
956
+ " image = image.to(device)\n",
957
+ "\n",
958
+ " clip_target = clip_img_embedder(image)\n",
959
+ " assert not torch.any(torch.isnan(clip_target))\n",
960
+ "\n",
961
+ " if epoch < int(mixup_pct * num_epochs):\n",
962
+ " perm_list = [perm_iters[f\"subj0{s}_iter{train_i}\"].detach().to(device) for s in subj_list]\n",
963
+ " perm = torch.cat(perm_list, dim=0)\n",
964
+ " betas_list = [betas_iters[f\"subj0{s}_iter{train_i}\"].detach().to(device) for s in subj_list]\n",
965
+ " betas = torch.cat(betas_list, dim=0)\n",
966
+ " select_list = [select_iters[f\"subj0{s}_iter{train_i}\"].detach().to(device) for s in subj_list]\n",
967
+ " select = torch.cat(select_list, dim=0)\n",
968
+ "\n",
969
+ " voxel_ridge_list = [mindeye.ridge(voxel_list[si],si) for si,s in enumerate(subj_list)]\n",
970
+ " clip_voxels = torch.cat(voxel_ridge_list, dim=0)\n",
971
+ "\n",
972
+ "# backbone, clip_voxels = mindeye.backbone(voxel_ridge)\n",
973
+ "\n",
974
+ " clip_voxels_norm = nn.functional.normalize(clip_voxels.flatten(1), dim=-1)\n",
975
+ " clip_target_norm = nn.functional.normalize(clip_target.flatten(1), dim=-1)\n",
976
+ "\n",
977
+ " if epoch < int(mixup_pct * num_epochs): \n",
978
+ " loss_clip = utils.mixco_nce(\n",
979
+ " clip_voxels_norm,\n",
980
+ " clip_target_norm,\n",
981
+ " temp=.006,\n",
982
+ " perm=perm, betas=betas, select=select)\n",
983
+ " else:\n",
984
+ " epoch_temp = soft_loss_temps[epoch-int(mixup_pct*num_epochs)]\n",
985
+ " loss_clip = utils.soft_clip_loss(\n",
986
+ " clip_voxels_norm,\n",
987
+ " clip_target_norm,\n",
988
+ " temp=epoch_temp)\n",
989
+ "\n",
990
+ " loss_clip_total += loss_clip.item()\n",
991
+ " loss += loss_clip\n",
992
+ "\n",
993
+ " # forward and backward top 1 accuracy \n",
994
+ " labels = torch.arange(len(clip_voxels_norm)).to(clip_voxels_norm.device) \n",
995
+ " fwd_percent_correct += utils.topk(utils.prenormed_batchwise_cosine_similarity(clip_voxels_norm, clip_target_norm), labels, k=1).item()\n",
996
+ " bwd_percent_correct += utils.topk(utils.prenormed_batchwise_cosine_similarity(clip_target_norm, clip_voxels_norm), labels, k=1).item()\n",
997
+ "\n",
998
+ " utils.check_loss(loss)\n",
999
+ " accelerator.backward(loss)\n",
1000
+ " optimizer.step()\n",
1001
+ "\n",
1002
+ " losses.append(loss.item())\n",
1003
+ " lrs.append(optimizer.param_groups[0]['lr'])\n",
1004
+ "\n",
1005
+ " lr_scheduler.step()\n",
1006
+ "\n",
1007
+ " mindeye.eval()\n",
1008
+ " if local_rank==0:\n",
1009
+ " with torch.no_grad(), torch.cuda.amp.autocast(dtype=data_type): \n",
1010
+ " for test_i, (behav, past_behav, future_behav, old_behav) in enumerate(test_dl): \n",
1011
+ " loss=0. \n",
1012
+ "\n",
1013
+ " coco_idx = behav[:,0,0].cpu().long()\n",
1014
+ " _,test_indices = np.unique(coco_idx, return_index=True)\n",
1015
+ " test_indices = np.random.permutation(test_indices)[:num_test_eval]\n",
1016
+ " image = images[coco_idx[test_indices]].float().to(device)\n",
1017
+ " \n",
1018
+ " # get the corresponding raw voxel time series\n",
1019
+ " for ib,b in enumerate(behav[test_indices,0,5].cpu().long().numpy()):\n",
1020
+ " tr = (nsddata_raw_stimuli[nsddata_raw_stimuli['global_trial'].isin([b.item()])]['global_TR_onsets'].values + TR_delay).astype(np.int32).item()\n",
1021
+ " if ib==0:\n",
1022
+ " voxels_raw = mindeye_funcs[tr-2:tr+2][None][None]\n",
1023
+ " else:\n",
1024
+ " voxels_raw = np.vstack((voxels_raw, mindeye_funcs[tr-2:tr+2][None][None]))\n",
1025
+ " voxels_raw = torch.Tensor(voxels_raw).clamp(0,1).to(device)\n",
1026
+ " \n",
1027
+ " ## Process it through pretrained MAE ##\n",
1028
+ " encoder_out = model(voxels_raw, encoder_mask=tube_mask)\n",
1029
+ " # encoder_out = bn(encoder_out)\n",
1030
+ " \n",
1031
+ " voxel = encoder_out.flatten(1).unsqueeze(1)\n",
1032
+ " voxel = nn.functional.normalize(voxel,dim=-1)\n",
1033
+ "\n",
1034
+ " assert len(image) == num_test_eval\n",
1035
+ "\n",
1036
+ " clip_target = clip_img_embedder(image.float())\n",
1037
+ " \n",
1038
+ " clip_voxels_norm = nn.functional.normalize(voxel.flatten(1), dim=-1)\n",
1039
+ "\n",
1040
+ " clip_voxels = mindeye.ridge(voxel,0) # 0th index of subj_list\n",
1041
+ " \n",
1042
+ "# backbone, clip_voxels = mindeye.backbone(voxel_ridge)\n",
1043
+ "\n",
1044
+ " clip_voxels_norm = nn.functional.normalize(clip_voxels.flatten(1), dim=-1)\n",
1045
+ " clip_target_norm = nn.functional.normalize(clip_target.flatten(1), dim=-1)\n",
1046
+ " \n",
1047
+ " loss_clip = utils.soft_clip_loss(\n",
1048
+ " clip_voxels_norm,\n",
1049
+ " clip_target_norm,\n",
1050
+ " temp=.006)\n",
1051
+ "\n",
1052
+ " test_loss_clip_total += loss_clip.item()\n",
1053
+ " loss += loss_clip\n",
1054
+ "\n",
1055
+ " # forward and backward top 1 accuracy \n",
1056
+ " labels = torch.arange(len(clip_voxels_norm)).to(clip_voxels_norm.device) \n",
1057
+ " test_fwd_percent_correct += utils.topk(utils.prenormed_batchwise_cosine_similarity(clip_voxels_norm, clip_target_norm), labels, k=1).item()\n",
1058
+ " test_bwd_percent_correct += utils.topk(utils.prenormed_batchwise_cosine_similarity(clip_target_norm, clip_voxels_norm), labels, k=1).item()\n",
1059
+ " \n",
1060
+ " utils.check_loss(loss) \n",
1061
+ " test_losses.append(loss.item())\n",
1062
+ "\n",
1063
+ " logs = {\"train/loss\": np.mean(losses[-(train_i+1):]),\n",
1064
+ " \"test/loss\": np.mean(test_losses[-(test_i+1):]),\n",
1065
+ " \"train/lr\": lrs[-1],\n",
1066
+ " \"train/num_steps\": len(losses),\n",
1067
+ " \"test/num_steps\": len(test_losses),\n",
1068
+ " \"train/fwd_pct_correct\": fwd_percent_correct / (train_i + 1),\n",
1069
+ " \"train/bwd_pct_correct\": bwd_percent_correct / (train_i + 1),\n",
1070
+ " \"test/test_fwd_pct_correct\": test_fwd_percent_correct / (test_i + 1),\n",
1071
+ " \"test/test_bwd_pct_correct\": test_bwd_percent_correct / (test_i + 1),\n",
1072
+ " \"train/loss_clip_total\": loss_clip_total / (train_i + 1),\n",
1073
+ " \"test/loss_clip_total\": test_loss_clip_total / (test_i + 1),\n",
1074
+ " }\n",
1075
+ "\n",
1076
+ " progress_bar.set_postfix(**logs)\n",
1077
+ " if wandb_log: wandb.log(logs)\n",
1078
+ " \n",
1079
+ " # Save model checkpoint\n",
1080
+ " if (ckpt_saving) and (epoch % ckpt_interval == 0):\n",
1081
+ " save_ckpt()\n",
1082
+ "\n",
1083
+ " # wait for other GPUs to catch up if needed\n",
1084
+ " accelerator.wait_for_everyone()\n",
1085
+ " torch.cuda.empty_cache()\n",
1086
+ " gc.collect()"
1087
+ ]
1088
+ },
1089
+ {
1090
+ "cell_type": "code",
1091
+ "execution_count": null,
1092
+ "id": "6277d8b8-63ab-4741-942f-69bb0caff816",
1093
+ "metadata": {},
1094
+ "outputs": [],
1095
+ "source": [
1096
+ "# torch.save(image0,\"image0.pt\")\n",
1097
+ "# torch.save(encoder_out,\"encoder_out0.pt\")"
1098
+ ]
1099
+ },
1100
+ {
1101
+ "cell_type": "code",
1102
+ "execution_count": null,
1103
+ "id": "a06050bc-86a2-4134-a661-ead4afe3e1e7",
1104
+ "metadata": {
1105
+ "tags": []
1106
+ },
1107
+ "outputs": [],
1108
+ "source": [
1109
+ "plt.figure(figsize=(8, 3))\n",
1110
+ "plt.plot(losses)\n",
1111
+ "plt.title(\"Training losses\")\n",
1112
+ "plt.show()\n",
1113
+ "\n",
1114
+ "plt.figure(figsize=(8, 3))\n",
1115
+ "plt.plot(test_losses)\n",
1116
+ "plt.title(\"Test losses\")\n",
1117
+ "plt.show()"
1118
+ ]
1119
+ },
1120
+ {
1121
+ "cell_type": "code",
1122
+ "execution_count": null,
1123
+ "id": "d38371ee-6e16-4db4-a096-f3f9ebdd73cf",
1124
+ "metadata": {
1125
+ "tags": []
1126
+ },
1127
+ "outputs": [],
1128
+ "source": [
1129
+ "voxel_list[0].shape"
1130
+ ]
1131
+ },
1132
+ {
1133
+ "cell_type": "code",
1134
+ "execution_count": null,
1135
+ "id": "0885cc21-c8b2-4b6f-950d-913d6317ea40",
1136
+ "metadata": {},
1137
+ "outputs": [],
1138
+ "source": []
1139
+ }
1140
+ ],
1141
+ "metadata": {
1142
+ "kernelspec": {
1143
+ "display_name": "found",
1144
+ "language": "python",
1145
+ "name": "found"
1146
+ },
1147
+ "language_info": {
1148
+ "codemirror_mode": {
1149
+ "name": "ipython",
1150
+ "version": 3
1151
+ },
1152
+ "file_extension": ".py",
1153
+ "mimetype": "text/x-python",
1154
+ "name": "python",
1155
+ "nbconvert_exporter": "python",
1156
+ "pygments_lexer": "ipython3",
1157
+ "version": "3.10.13"
1158
+ }
1159
+ },
1160
+ "nbformat": 4,
1161
+ "nbformat_minor": 5
1162
+ }
fMRI-foundation-model-old/fMRI-MAE/downstream.py ADDED
@@ -0,0 +1,731 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Import packages and setup gpu configuration.
2
+ # This code block shouldnt need to be adjusted!
3
+ import os
4
+ import shutil
5
+ import sys
6
+ import json
7
+ import yaml
8
+ import numpy as np
9
+ import pandas as pd
10
+ import copy
11
+ import math
12
+ from einops import rearrange
13
+ from einops.layers.torch import Rearrange
14
+ import time
15
+ import random
16
+ import h5py
17
+ import webdataset as wds
18
+ import gc
19
+ import matplotlib.pyplot as plt
20
+ import torch
21
+ import torch.nn as nn
22
+ import torch.nn.functional as F
23
+ from torchvision import transforms
24
+ import utils
25
+ from models import *
26
+ from mindeye_models import *
27
+ import nibabel as nib
28
+ from nilearn import plotting
29
+ from functools import partial
30
+
31
+ # tf32 data type is faster than standard float32
32
+ torch.backends.cuda.matmul.allow_tf32 = True
33
+
34
+ ### Multi-GPU config ###
35
+ local_rank = os.getenv('LOCAL_RANK')
36
+ if local_rank is None:
37
+ local_rank = 0
38
+ else:
39
+ local_rank = int(local_rank)
40
+ print(f"LOCAL RANK={local_rank}")
41
+
42
+ num_devices = os.getenv('NUM_GPUS')
43
+ if num_devices is None:
44
+ num_devices = 1
45
+ else:
46
+ num_devices = int(num_devices)
47
+ print(f"NUM GPUS={num_devices}")
48
+ distributed = True if num_devices>1 else False
49
+
50
+ global_rank = os.getenv('RANK')
51
+ if global_rank is None:
52
+ global_rank = 0
53
+ else:
54
+ global_rank = int(global_rank)
55
+ print(f"GLOBAL RANK={global_rank}")
56
+
57
+ from tqdm import tqdm
58
+
59
+ # Load parameters from yaml config
60
+ config = yaml.load(open('config.yaml', 'r'), Loader=yaml.FullLoader)
61
+
62
+ # create global variables from the config
63
+ for attribute_name in config.keys():
64
+ globals()[attribute_name] = config[f'{attribute_name}']
65
+
66
+ # Load MindEye parameters from yaml config (will override any params with same name)
67
+ mindeye_config = yaml.load(open('mindeye_config.yaml', 'r'), Loader=yaml.FullLoader)
68
+
69
+ # create global variables from the config
70
+ for attribute_name in mindeye_config.keys():
71
+ globals()[attribute_name] = mindeye_config[f'{attribute_name}']
72
+
73
+ data_type = torch.float32 # change depending on your mixed_precision
74
+
75
+ batch_size = global_batch_size // num_devices
76
+ print("batch_size", batch_size)
77
+
78
+ # First use "accelerate config" in terminal and setup using deepspeed stage 2 with CPU offloading
79
+ from accelerate import Accelerator
80
+ accelerator = Accelerator(split_batches=False, mixed_precision="fp16")
81
+
82
+ from accelerate.state import AcceleratorState
83
+ try:
84
+ AcceleratorState().deepspeed_plugin.deepspeed_config['train_micro_batch_size_per_gpu'] = batch_size
85
+ print("deepspeed reconfigured, train_micro_batch_size_per_gpu = ", batch_size)
86
+ except:
87
+ print("skipping deepspeed reconfiguration...")
88
+
89
+ print("PID of this process =",os.getpid())
90
+ device = accelerator.device
91
+ print("device:",device)
92
+ world_size = accelerator.state.num_processes
93
+ num_workers = num_devices
94
+ print(accelerator.state)
95
+
96
+ print("distributed =",distributed, "num_devices =", num_devices, "local rank =", local_rank, "world size =", world_size, "data_type =", data_type)
97
+ print = accelerator.print # only print if local_rank=0
98
+
99
+ print("mae config\n\n",config)
100
+ print("mindeye_config\n",mindeye_config)
101
+
102
+ # if utils.is_interactive():
103
+ # ckpt_saving = False
104
+ # wandb_log = False
105
+
106
+ # seed all random functions
107
+ utils.seed_everything(seed)
108
+
109
+ mae_ckpt_pth = os.path.abspath(f'../ckpts/{mae_model_name}/last.pth')
110
+ print("mae_ckpt_pth", mae_ckpt_pth)
111
+
112
+ outdir = os.path.abspath(f'../ckpts/{model_name}')
113
+ os.makedirs(outdir,exist_ok=True)
114
+ print("outdir", outdir)
115
+
116
+ if type(patch_size) == int:
117
+ patch_size = [patch_size,patch_size,patch_size]
118
+ patch_depth = patch_size[0]
119
+ patch_height = patch_size[1]
120
+ patch_width = patch_size[2]
121
+
122
+ num_patches = int(
123
+ (img_size[0] / patch_depth)
124
+ * (img_size[1] / patch_height)
125
+ * (img_size[2] / patch_width)
126
+ * num_frames
127
+ )
128
+ print("num_patches", num_patches)
129
+
130
+ vit_size = {
131
+ "encoder": encoder_model,
132
+ "decoder": decoder_model
133
+ }
134
+
135
+ model = get_vit(
136
+ size=vit_size,
137
+ image_size=img_size, # depth, height, width
138
+ image_patch_size=(patch_depth,patch_height,patch_width), # depth, height, width patch size
139
+ frames=num_frames,
140
+ frame_patch_size=frame_patch_size,
141
+ channels=1,
142
+ use_rope_emb=use_rope_emb,
143
+ use_cls_token=use_cls_token,
144
+ use_decoder_same_emb_dim=use_decoder_same_emb_dim,
145
+ decoder_depth=decoder_depth
146
+ )
147
+ utils.count_params(model)
148
+
149
+
150
+ def save_ckpt(tag):
151
+ ckpt_path = outdir+f'/{tag}.pth'
152
+ if accelerator.is_main_process:
153
+ unwrapped_model = accelerator.unwrap_model(model)
154
+ if tag == "last" and os.path.exists(ckpt_path):
155
+ shutil.copyfile(ckpt_path, os.path.join(outdir, f'{tag}_old.pth'))
156
+ torch.save({
157
+ 'epoch': epoch,
158
+ 'model_state_dict': unwrapped_model.state_dict(),
159
+ 'optimizer_state_dict': optimizer.state_dict(),
160
+ 'lr_scheduler': lr_scheduler.state_dict(),
161
+ 'train_losses': losses,
162
+ 'test_losses': test_losses,
163
+ 'lrs': lrs,
164
+ }, ckpt_path)
165
+ if tag == "last" and os.path.exists(os.path.join(outdir, f'{tag}_old.pth')):
166
+ os.remove(os.path.join(outdir, f'{tag}_old.pth'))
167
+ print(f"\n---saved {outdir}/{tag} ckpt!---\n")
168
+
169
+ def load_ckpt(tag,load_lr=True,load_optimizer=True,load_epoch=True,strict=True,outdir=outdir,multisubj_loading=False):
170
+ print(f"\n---loading {outdir}/{tag}.pth ckpt---\n")
171
+ checkpoint = torch.load(outdir+'/last.pth', map_location='cpu')
172
+ state_dict = checkpoint['model_state_dict']
173
+ if multisubj_loading: # remove incompatible ridge layer that will otherwise error
174
+ state_dict.pop('ridge.linears.0.weight',None)
175
+ model.load_state_dict(state_dict, strict=strict)
176
+ if load_epoch:
177
+ globals()["epoch"] = checkpoint['epoch']
178
+ print("Epoch",epoch)
179
+ if load_optimizer:
180
+ optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
181
+ if load_lr:
182
+ lr_scheduler.load_state_dict(checkpoint['lr_scheduler'])
183
+ del checkpoint
184
+
185
+
186
+ checkpoint = torch.load(mae_ckpt_pth, map_location=device)
187
+ try:
188
+ model.load_state_dict(checkpoint['model_state_dict'], strict=False)
189
+ except:
190
+ pass
191
+
192
+ # set foundation model to evaluation
193
+ model.eval()
194
+ model.requires_grad_(False)
195
+ model.to(device)
196
+ pass
197
+
198
+
199
+ nsddata_raw_stimuli = pd.read_csv(f"{nsd_raw_path}/nsddata_rawdata.csv")
200
+ TR_delay = 3 # to account for bold hrf
201
+ train_TRs = np.round(nsddata_raw_stimuli[nsddata_raw_stimuli['shared1000'] == False]['global_TR_onsets'].values + TR_delay).astype(np.int32)
202
+ test_TRs = np.round(nsddata_raw_stimuli[nsddata_raw_stimuli['shared1000'] == True]['global_TR_onsets'].values + TR_delay).astype(np.int32)
203
+
204
+
205
+ # Load 73k NSD images
206
+ f = h5py.File(f'{nsd_image_path}/coco_images_224_float16.hdf5', 'r')
207
+ images = f['images'][:]
208
+ images = torch.Tensor(images).to("cpu").to(data_type)
209
+ print("Loaded all 73k possible NSD images!", images.shape)
210
+
211
+ # Load MindEye hdf5
212
+ f = h5py.File(f'{nsd_raw_path}/subj01_mnidata.h5', 'r') #subj01_rawdata_old.h5
213
+ mindeye_global_trs = f['global_trs'][:]
214
+ mindeye_funcs = f['funcs']
215
+
216
+
217
+ # clip_img_embedder = FrozenOpenCLIPImageEmbedder(
218
+ # arch="ViT-bigG-14",
219
+ # version="laion2b_s39b_b160k",
220
+ # output_tokens=True,
221
+ # only_tokens=True,
222
+ # )
223
+ # clip_seq_dim = 256
224
+ # clip_emb_dim = 1664
225
+ # clip_img_embedder.to(device)
226
+
227
+ clip_img_embedder = FrozenOpenCLIPImageEmbedder(
228
+ arch="ViT-bigG-14",
229
+ version="laion2b_s39b_b160k",
230
+ output_tokens=False,
231
+ only_tokens=False,
232
+ init_device=device,
233
+ device=device,
234
+ cache_dir="/weka/proj-fmri/shared/cache"
235
+ )
236
+ clip_seq_dim = 1
237
+ clip_emb_dim = 1280
238
+ clip_img_embedder.to(device)
239
+
240
+
241
+ subj = s = 1
242
+ subj_list = [subj]
243
+
244
+ # if multi_subject:
245
+ # nsessions_allsubj=np.array([40, 40, 32, 30, 40, 32, 40, 30])
246
+ # num_samples_per_epoch = (750*40) // num_devices
247
+ # else:
248
+ # num_samples_per_epoch = (750*num_sessions) // num_devices
249
+
250
+ # num_samples_per_epoch = 1024 #(750*num_sessions) // num_devices
251
+
252
+ print("dividing batch size by subj_list, which will then be concatenated across subj during training...")
253
+ batch_size = batch_size // len(subj_list)
254
+ num_iterations_per_epoch = num_samples_per_epoch // (batch_size*len(subj_list))
255
+ print("batch_size =", batch_size, "num_iterations_per_epoch =",num_iterations_per_epoch, "num_samples_per_epoch =",num_samples_per_epoch)
256
+
257
+ train_data = {}
258
+ train_dl = {}
259
+
260
+ print(f"Training with {num_sessions} sessions")
261
+ train_url = f"{nsd_wds_path}/subj0{s}/train/" + "{0.." + f"{num_sessions-1}" + "}.tar"
262
+ print(train_url)
263
+
264
+ train_data[f'subj0{s}'] = wds.WebDataset(train_url,resampled=True,nodesplitter=utils.my_split_by_node)\
265
+ .shuffle(750, initial=1500, rng=random.Random(42))\
266
+ .decode("torch")\
267
+ .rename(behav="behav.npy", past_behav="past_behav.npy", future_behav="future_behav.npy", olds_behav="olds_behav.npy")\
268
+ .to_tuple(*["behav", "past_behav", "future_behav", "olds_behav"])
269
+ # train_dl[f'subj0{s}'] = torch.utils.data.DataLoader(train_data[f'subj0{s}'], batch_size=batch_size, shuffle=False, drop_last=True, pin_memory=True)
270
+ train_dl[f'subj0{s}'] = wds.WebLoader(
271
+ train_data[f'subj0{s}'].batched(batch_size),
272
+ pin_memory=True,
273
+ shuffle=False,
274
+ batch_size=None,
275
+ num_workers=num_workers,
276
+ persistent_workers=num_workers>0,
277
+ ).with_epoch(num_iterations_per_epoch)
278
+
279
+ print("Loaded all subj train dls and betas!\n")
280
+ if subj==3:
281
+ num_test=2371
282
+ elif subj==4:
283
+ num_test=2188
284
+ elif subj==6:
285
+ num_test=2371
286
+ elif subj==8:
287
+ num_test=2188
288
+ else:
289
+ num_test=300 # 3000
290
+ test_url = f"{nsd_wds_path}/subj0{subj}/new_test/" + "0.tar"
291
+ print(test_url)
292
+ test_data = wds.WebDataset(test_url,resampled=True,nodesplitter=utils.my_split_by_node)\
293
+ .decode("torch")\
294
+ .rename(behav="behav.npy", past_behav="past_behav.npy", future_behav="future_behav.npy", olds_behav="olds_behav.npy")\
295
+ .to_tuple(*["behav", "past_behav", "future_behav", "olds_behav"])
296
+ # test_dl = torch.utils.data.DataLoader(test_data, batch_size=num_test, shuffle=False, drop_last=True, pin_memory=True)
297
+ test_dl = wds.WebLoader(
298
+ test_data.batched(num_test),
299
+ pin_memory=True,
300
+ shuffle=False,
301
+ batch_size=None,
302
+ num_workers=num_workers,
303
+ persistent_workers=num_workers>0,
304
+ ).with_epoch(10)
305
+ print(f"Loaded test dl for subj{subj}!\n")
306
+
307
+
308
+ class MindEyeModule(nn.Module):
309
+ def __init__(self):
310
+ super(MindEyeModule, self).__init__()
311
+ def forward(self, x):
312
+ return x
313
+
314
+ # class RidgeRegression(torch.nn.Module):
315
+ # # make sure to add weight_decay when initializing optimizer
316
+ # def __init__(self, input_sizes, out_features, seq_len=1):
317
+ # super(RidgeRegression, self).__init__()
318
+ # self.seq_len = seq_len
319
+ # self.out_features = out_features
320
+ # self.linears = torch.nn.ModuleList([
321
+ # torch.nn.Linear(input_size, out_features) for input_size in input_sizes
322
+ # ])
323
+ # def forward(self, x, subj_idx):
324
+ # out = torch.cat([self.linears[subj_idx](x[:,seq]).unsqueeze(1) for seq in range(self.seq_len)], dim=1)
325
+ # return out
326
+
327
+ class MLP(torch.nn.Module):
328
+ def __init__(self, input_sizes, out_features, seq_len=0):
329
+ super(MLP, self).__init__()
330
+ self.input_sizes = input_sizes[0]
331
+ self.out_features = out_features
332
+ self.mlp = nn.Sequential(
333
+ nn.LayerNorm(self.input_sizes),
334
+ nn.GELU(),
335
+ nn.Linear(self.input_sizes, hidden_dim//2),
336
+ nn.LayerNorm(hidden_dim//2),
337
+ nn.GELU(),
338
+ nn.Dropout(drop),
339
+ nn.Linear(hidden_dim//2, hidden_dim),
340
+ nn.LayerNorm(hidden_dim),
341
+ nn.GELU(),
342
+ nn.Dropout(drop),
343
+ nn.Linear(hidden_dim, out_features)
344
+ )
345
+ def forward(self, x, z=None):
346
+ out = self.mlp(x[:,0])
347
+ return out
348
+
349
+ class ReversibleBrainNetwork(nn.Module):
350
+ def __init__(self, out_dim=768, in_dim=15724, h=4096, n_blocks=4, norm_type='bn', act_first=True,
351
+ encoder_tokens=257, reverse=True, **kwargs):
352
+ super().__init__()
353
+ norm_func = partial(nn.BatchNorm1d, num_features=h) if norm_type == 'bn' else partial(nn.LayerNorm, normalized_shape=h)
354
+ act_fn = partial(nn.ReLU, inplace=True) if norm_type == 'bn' else nn.GELU
355
+ act_and_norm = (act_fn, norm_func) if act_first else (norm_func, act_fn)
356
+ # self.temp = nn.Parameter(torch.tensor(.006))
357
+ start_dim, end_dim = (out_dim*encoder_tokens, in_dim) if reverse else (in_dim, out_dim*encoder_tokens)
358
+ self.lin0 = nn.Sequential(
359
+ nn.Linear(start_dim, h),
360
+ *[item() for item in act_and_norm],
361
+ nn.Dropout(0.5),
362
+ )
363
+
364
+ self.mlp = nn.ModuleList([
365
+ nn.Sequential(
366
+ nn.Linear(h, h),
367
+ *[item() for item in act_and_norm],
368
+ nn.Dropout(drop)
369
+ ) for _ in range(n_blocks)
370
+ ])
371
+
372
+ self.lin1 = nn.Linear(h, end_dim, bias=True)
373
+ self.n_blocks = n_blocks
374
+
375
+ def forward(self, x, *args, **kwargs):
376
+ x = self.lin0(x.flatten(1)) # bs, h
377
+ residual = x
378
+ for res_block in range(self.n_blocks):
379
+ x = self.mlp[res_block](x)
380
+ x += residual
381
+ residual = x
382
+ x = x.reshape(len(x), -1)
383
+ x = self.lin1(x)
384
+ return x
385
+
386
+
387
+ mindeye = MindEyeModule()
388
+ # mindeye.ridge = MLP(np.array([in_dim]), out_features=clip_emb_dim*clip_seq_dim)
389
+ mindeye.ridge = ReversibleBrainNetwork(in_dim=in_dim, h=hidden_dim, out_dim=clip_emb_dim,
390
+ encoder_tokens=1, norm_type='ln', act_first=False, reverse=False)
391
+
392
+ # mindeye.backbone = BrainNetwork(h=hidden_dim, in_dim=hidden_dim, n_blocks=4, drop=drop,
393
+ # clip_size=clip_emb_dim, out_dim=clip_emb_dim*clip_seq_dim, clip_scale=1)
394
+ utils.count_params(mindeye.ridge)
395
+ # utils.count_params(mindeye.backbone)
396
+ utils.count_params(mindeye)
397
+
398
+
399
+ no_decay = ["bias", "LayerNorm.bias", "LayerNorm.weight"]
400
+ opt_grouped_parameters = [
401
+ {'params': [p for n, p in mindeye.ridge.named_parameters()], 'weight_decay': 1e-2},
402
+ # {'params': [p for n, p in mindeye.backbone.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 1e-2},
403
+ # {'params': [p for n, p in mindeye.backbone.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0},
404
+ ]
405
+
406
+ optimizer = torch.optim.AdamW(opt_grouped_parameters, lr=max_lr)
407
+
408
+ total_steps = iters_scale_factor * num_epochs * num_iterations_per_epoch
409
+ print("total_steps", total_steps)
410
+ pct_start = 2/num_epochs if num_epochs>1 else 1.
411
+ lr_scheduler = torch.optim.lr_scheduler.OneCycleLR(
412
+ optimizer,
413
+ max_lr=max_lr,
414
+ total_steps=total_steps,
415
+ pct_start=0.1
416
+ )
417
+
418
+ print("\nDone with model preparations!")
419
+ num_params = utils.count_params(mindeye)
420
+
421
+
422
+ if accelerator.is_main_process and wandb_log: # only use main process for wandb logging
423
+ import wandb
424
+ wandb_project = 'found_downstream'
425
+ print(f"wandb {wandb_project} run {model_name}")
426
+ # need to configure wandb beforehand in terminal with "wandb init"!
427
+ wandb_config = {
428
+ "model_name": model_name,
429
+ "mae_model_name": mae_model_name,
430
+ "global_batch_size": global_batch_size,
431
+ "batch_size": batch_size,
432
+ "num_epochs": num_epochs,
433
+ "num_sessions": num_sessions,
434
+ "num_samples_per_epoch": num_samples_per_epoch,
435
+ "in_dim": in_dim,
436
+ "hidden_dim": hidden_dim,
437
+ "mixup_pct": mixup_pct,
438
+ "num_params": num_params,
439
+ "max_lr": max_lr,
440
+ "ckpt_interval": ckpt_interval,
441
+ "ckpt_saving": ckpt_saving,
442
+ "seed": seed,
443
+ "distributed": distributed,
444
+ "num_devices": num_devices,
445
+ "world_size": world_size,
446
+ "train_url": train_url,
447
+ "test_url": test_url,
448
+ }
449
+ print("wandb_config:\n",wandb_config)
450
+ print("wandb_id:",model_name)
451
+ wandb.init(
452
+ id=model_name,
453
+ project=wandb_project,
454
+ name=model_name,
455
+ config=wandb_config,
456
+ resume="allow",
457
+ )
458
+ else:
459
+ wandb_log = False
460
+
461
+
462
+ epoch = 0
463
+ losses, test_losses, lrs = [], [], []
464
+ best_test_loss = 1e9
465
+ torch.cuda.empty_cache()
466
+
467
+ # if masking_strategy=="MNI":
468
+ from einops.layers.torch import Rearrange
469
+
470
+ MNI_brain = nib.load("/weka/proj-fmri/paulscotti/fMRI-foundation-model/dataset_creation/afni_conversion/tpl-MNI152NLin2009cAsym_res-02_T1w_brain.nii.gz").get_fdata()
471
+ brain_pos_voxels = utils.crop_or_pad(torch.from_numpy(MNI_brain[6:94,8:112,10:82]), img_size)
472
+ # brain_pos_voxels = MNI_brain[10:90,12:108,14:78]
473
+
474
+ # brain_pos_voxels = brain_pos_voxels[:,30:31,:]
475
+
476
+ brain_pos_pats = Rearrange(
477
+ "b c (f pf) (d pd) (h ph) (w pw) -> b f d h w (pd ph pw pf c)",
478
+ pd=patch_depth,
479
+ ph=patch_height,
480
+ pw=patch_width,
481
+ pf=1,
482
+ )(brain_pos_voxels[None,None,None])
483
+
484
+ brain_pos_pats_vit = rearrange(brain_pos_pats, "b ... d -> b (...) d").mean(-1)[0]
485
+
486
+ tube_mask = torch.zeros(num_patches // num_frames).to(torch.bool)
487
+ batch_positive_approx = (brain_pos_pats_vit > 0)
488
+ mask_idx_candidates = torch.where(batch_positive_approx)[0]
489
+ mask_idx_candidates = mask_idx_candidates[torch.randperm(len(mask_idx_candidates))]
490
+ tube_idx = mask_idx_candidates[:int(num_patches / num_frames * (1 - tube_end_masking_ratio))]
491
+ tube_mask[tube_idx] = True
492
+ tube_mask = tube_mask.tile(num_frames//frame_patch_size)
493
+
494
+ # load multisubject stage1 ckpt if set
495
+ if multisubject_ckpt!="None" and not resume_from_ckpt:
496
+ load_ckpt("last",outdir=multisubject_ckpt,load_lr=False,load_optimizer=False,load_epoch=False,strict=False,multisubj_loading=True)
497
+
498
+ # load saved ckpt model weights into current model
499
+ if resume_from_ckpt:
500
+ load_ckpt("last",load_lr=True,load_optimizer=True,load_epoch=True)
501
+ elif wandb_log:
502
+ if wandb.run.resumed:
503
+ if os.path.exists(os.path.join(outdir, 'last.pth')) or os.path.exists(os.path.join(outdir, 'last_old.pth')):
504
+ if os.path.exists(os.path.join(outdir, 'last_old.pth')):
505
+ if os.path.exists(os.path.join(outdir, 'last.pth')):
506
+ # this is corrupted
507
+ os.remove(os.path.join(outdir, f'last.pth'))
508
+ # set last_old as last
509
+ shutil.move(os.path.join(outdir, f'last_old.pth'), os.path.join(outdir, f'last.pth'))
510
+
511
+ # ckpt_path = os.path.join(outdir, 'last.pth')
512
+ # resume_from_ckpt = True
513
+ load_ckpt("last",load_lr=True,load_optimizer=True,load_epoch=True)
514
+
515
+ train_dls = [train_dl[f'subj0{s}'] for s in subj_list]
516
+
517
+ mindeye, optimizer, *train_dls, lr_scheduler = accelerator.prepare(mindeye, optimizer, *train_dls, lr_scheduler)
518
+ # leaving out test_dl since we will only have local_rank 0 device do evals
519
+
520
+ print(f"{model_name} starting with epoch {epoch} / {num_epochs}")
521
+ progress_bar = tqdm(range(epoch, num_epochs), disable=not accelerator.is_main_process)
522
+ mse = nn.MSELoss()
523
+ soft_loss_temps = utils.cosine_anneal(0.004, 0.0075, num_epochs - int(mixup_pct * num_epochs))
524
+
525
+ bn = nn.BatchNorm1d(512,affine=False).to(device)
526
+
527
+ test_image=None
528
+ num_test_eval=batch_size # should instead be average same-image 300 to mimic MindEye2 retrieval evaluation
529
+
530
+ for epoch in progress_bar:
531
+ mindeye.train()
532
+
533
+ fwd_percent_correct = 0.
534
+ bwd_percent_correct = 0.
535
+ test_fwd_percent_correct = 0.
536
+ test_bwd_percent_correct = 0.
537
+ loss_clip_total = 0.
538
+ test_loss_clip_total = 0.
539
+
540
+ # pre-load all batches for this epoch (it's MUCH faster to pre-load in bulk than to separate loading per batch)
541
+ voxel_iters = {} # empty dict because diff subjects have differing # of voxels
542
+ image_iters = torch.zeros(num_iterations_per_epoch, batch_size*len(subj_list), 3, 224, 224).float()
543
+ annot_iters = {}
544
+ perm_iters, betas_iters, select_iters = {}, {}, {}
545
+ for s, train_dl in enumerate(train_dls):
546
+ with torch.cuda.amp.autocast(dtype=data_type):
547
+ for iter, (behav0, past_behav0, future_behav0, old_behav0) in enumerate(tqdm(train_dl,total=num_iterations_per_epoch)):
548
+ image0 = images[behav0[:,0,0].cpu().long()].float()
549
+ image_iters[iter,s*batch_size:s*batch_size+batch_size] = image0
550
+
551
+ # if images are not fully preloaded, then can do this inefficient but more memory friendly approach
552
+ # for ib,b in enumerate(behav0[:,0,0].cpu().long()):
553
+ # if ib==0:
554
+ # image0 = torch.Tensor(images[[b]])
555
+ # else:
556
+ # image0 = torch.vstack((image0, torch.Tensor(images[[b]])))
557
+ # image_iters[iter,s*batch_size:s*batch_size+batch_size] = image0
558
+
559
+ # get the corresponding raw voxel time series
560
+ for ib,b in enumerate(behav0[:,0,5].cpu().long().numpy()):
561
+ tr = (nsddata_raw_stimuli[nsddata_raw_stimuli['global_trial'].isin([b.item()])]['global_TR_onsets'].values + TR_delay).astype(np.int32).item()
562
+ if ib==0:
563
+ voxels_raw = mindeye_funcs[tr-2:tr+2][None][None]
564
+ else:
565
+ voxels_raw = np.vstack((voxels_raw, mindeye_funcs[tr-2:tr+2][None][None]))
566
+ voxels_raw = torch.Tensor(voxels_raw).clamp(0,1).to(device)
567
+ voxels_raw = utils.crop_or_pad(voxels_raw, img_size)
568
+
569
+ ## Process it through pretrained MAE ##
570
+ encoder_out = model(voxels_raw, encoder_mask=tube_mask)
571
+ # encoder_out = bn(encoder_out)
572
+
573
+ voxel0 = encoder_out.flatten(1).unsqueeze(1).cpu()
574
+ # voxel0 = nn.functional.normalize(voxel0,dim=-1).cpu()
575
+
576
+ assert len(voxel0) == batch_size
577
+
578
+ if epoch < int(mixup_pct * num_epochs):
579
+ voxel0, perm, betas, select = utils.mixco(voxel0)
580
+ perm_iters[f"subj0{subj_list[s]}_iter{iter}"] = perm
581
+ betas_iters[f"subj0{subj_list[s]}_iter{iter}"] = betas
582
+ select_iters[f"subj0{subj_list[s]}_iter{iter}"] = select
583
+
584
+ voxel_iters[f"subj0{subj_list[s]}_iter{iter}"] = voxel0
585
+
586
+ if iter >= num_iterations_per_epoch:
587
+ break
588
+
589
+ # you now have voxel_iters and image_iters with num_iterations_per_epoch batches each
590
+ for train_i in range(iters_scale_factor * num_iterations_per_epoch):
591
+ with torch.cuda.amp.autocast(dtype=data_type):
592
+ optimizer.zero_grad()
593
+ loss=0.
594
+
595
+ actual_idx = train_i % num_iterations_per_epoch
596
+
597
+ voxel_list = [voxel_iters[f"subj0{s}_iter{actual_idx}"].detach().to(device) for s in subj_list]
598
+ image = image_iters[actual_idx].detach()
599
+ image = image.to(device)
600
+
601
+ clip_target = clip_img_embedder(image)
602
+ assert not torch.any(torch.isnan(clip_target))
603
+
604
+ if epoch < int(mixup_pct * num_epochs):
605
+ perm_list = [perm_iters[f"subj0{s}_iter{actual_idx}"].detach().to(device) for s in subj_list]
606
+ perm = torch.cat(perm_list, dim=0)
607
+ betas_list = [betas_iters[f"subj0{s}_iter{actual_idx}"].detach().to(device) for s in subj_list]
608
+ betas = torch.cat(betas_list, dim=0)
609
+ select_list = [select_iters[f"subj0{s}_iter{actual_idx}"].detach().to(device) for s in subj_list]
610
+ select = torch.cat(select_list, dim=0)
611
+
612
+ voxel_ridge_list = [mindeye.ridge(voxel_list[si],si) for si,s in enumerate(subj_list)]
613
+ clip_voxels = torch.cat(voxel_ridge_list, dim=0)
614
+
615
+ # backbone, clip_voxels = mindeye.backbone(voxel_ridge)
616
+
617
+ clip_voxels_norm = nn.functional.normalize(clip_voxels.flatten(1), dim=-1)
618
+ clip_target_norm = nn.functional.normalize(clip_target.flatten(1), dim=-1)
619
+
620
+ if epoch < int(mixup_pct * num_epochs):
621
+ loss_clip = utils.mixco_nce(
622
+ clip_voxels_norm,
623
+ clip_target_norm,
624
+ temp=.006,
625
+ perm=perm, betas=betas, select=select)
626
+ else:
627
+ epoch_temp = soft_loss_temps[epoch-int(mixup_pct*num_epochs)]
628
+ loss_clip = utils.soft_clip_loss(
629
+ clip_voxels_norm,
630
+ clip_target_norm,
631
+ temp=epoch_temp)
632
+
633
+ loss_clip_total += loss_clip.item()
634
+ loss += loss_clip
635
+
636
+ # forward and backward top 1 accuracy
637
+ labels = torch.arange(len(clip_voxels_norm)).to(clip_voxels_norm.device)
638
+ fwd_percent_correct += utils.topk(utils.prenormed_batchwise_cosine_similarity(clip_voxels_norm, clip_target_norm), labels, k=1).item()
639
+ bwd_percent_correct += utils.topk(utils.prenormed_batchwise_cosine_similarity(clip_target_norm, clip_voxels_norm), labels, k=1).item()
640
+
641
+ utils.check_loss(loss)
642
+ accelerator.backward(loss)
643
+ optimizer.step()
644
+
645
+ losses.append(loss.item())
646
+ lrs.append(optimizer.param_groups[0]['lr'])
647
+
648
+ lr_scheduler.step()
649
+
650
+ mindeye.eval()
651
+ if local_rank==0:
652
+ with torch.no_grad(), torch.cuda.amp.autocast(dtype=data_type):
653
+ for test_i, (behav, past_behav, future_behav, old_behav) in enumerate(test_dl):
654
+ loss=0.
655
+
656
+ coco_idx = behav[:,0,0].cpu().long()
657
+ _,test_indices = np.unique(coco_idx, return_index=True)
658
+ test_indices = np.random.permutation(test_indices)[:num_test_eval]
659
+ image = images[coco_idx[test_indices]].float().to(device)
660
+
661
+ # get the corresponding raw voxel time series
662
+ for ib,b in enumerate(behav[test_indices,0,5].cpu().long().numpy()):
663
+ tr = (nsddata_raw_stimuli[nsddata_raw_stimuli['global_trial'].isin([b.item()])]['global_TR_onsets'].values + TR_delay).astype(np.int32).item()
664
+ if ib==0:
665
+ voxels_raw = mindeye_funcs[tr-2:tr+2][None][None]
666
+ else:
667
+ voxels_raw = np.vstack((voxels_raw, mindeye_funcs[tr-2:tr+2][None][None]))
668
+ voxels_raw = torch.Tensor(voxels_raw).clamp(0,1).to(device)
669
+ voxels_raw = utils.crop_or_pad(voxels_raw, img_size)
670
+
671
+ ## Process it through pretrained MAE ##
672
+ encoder_out = model(voxels_raw, encoder_mask=tube_mask)
673
+ # encoder_out = bn(encoder_out)
674
+
675
+ voxel = encoder_out.flatten(1).unsqueeze(1)
676
+ # voxel = nn.functional.normalize(voxel,dim=-1)
677
+
678
+ assert len(image) == num_test_eval
679
+
680
+ clip_target = clip_img_embedder(image.float())
681
+
682
+ clip_voxels_norm = nn.functional.normalize(voxel.flatten(1), dim=-1)
683
+
684
+ clip_voxels = mindeye.ridge(voxel,0) # 0th index of subj_list
685
+
686
+ # backbone, clip_voxels = mindeye.backbone(voxel_ridge)
687
+
688
+ clip_voxels_norm = nn.functional.normalize(clip_voxels.flatten(1), dim=-1)
689
+ clip_target_norm = nn.functional.normalize(clip_target.flatten(1), dim=-1)
690
+
691
+ loss_clip = utils.soft_clip_loss(
692
+ clip_voxels_norm,
693
+ clip_target_norm,
694
+ temp=.006)
695
+
696
+ test_loss_clip_total += loss_clip.item()
697
+ loss += loss_clip
698
+
699
+ # forward and backward top 1 accuracy
700
+ labels = torch.arange(len(clip_voxels_norm)).to(clip_voxels_norm.device)
701
+ test_fwd_percent_correct += utils.topk(utils.prenormed_batchwise_cosine_similarity(clip_voxels_norm, clip_target_norm), labels, k=1).item()
702
+ test_bwd_percent_correct += utils.topk(utils.prenormed_batchwise_cosine_similarity(clip_target_norm, clip_voxels_norm), labels, k=1).item()
703
+
704
+ utils.check_loss(loss)
705
+ test_losses.append(loss.item())
706
+
707
+ logs = {"train/loss": np.mean(losses[-(train_i+1):]),
708
+ "test/loss": np.mean(test_losses[-(test_i+1):]),
709
+ "train/lr": lrs[-1],
710
+ "train/num_steps": len(losses),
711
+ "test/num_steps": len(test_losses),
712
+ "train/fwd_pct_correct": fwd_percent_correct / (train_i + 1),
713
+ "train/bwd_pct_correct": bwd_percent_correct / (train_i + 1),
714
+ "test/test_fwd_pct_correct": test_fwd_percent_correct / (test_i + 1),
715
+ "test/test_bwd_pct_correct": test_bwd_percent_correct / (test_i + 1),
716
+ "train/loss_clip_total": loss_clip_total / (train_i + 1),
717
+ "test/loss_clip_total": test_loss_clip_total / (test_i + 1),
718
+ }
719
+
720
+ progress_bar.set_postfix(**logs)
721
+ if wandb_log: wandb.log(logs)
722
+
723
+ # Save model checkpoint
724
+ if (ckpt_saving) and (epoch % ckpt_interval == 0):
725
+ save_ckpt('last')
726
+
727
+ # wait for other GPUs to catch up if needed
728
+ accelerator.wait_for_everyone()
729
+ torch.cuda.empty_cache()
730
+ gc.collect()
731
+
fMRI-foundation-model-old/fMRI-MAE/downstream.slurm ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #SBATCH --account=fmri
3
+ #SBATCH --partition=a40
4
+ #SBATCH --job-name=downMAE
5
+ #SBATCH --nodes=1
6
+ #SBATCH --gres=gpu:1
7
+ #SBATCH --time=20:00:00 # total run time limit (HH:MM:SS)
8
+ #SBATCH -e slurms/%j.err # first create a "slurms" folder in current directory to store logs
9
+ #SBATCH -o slurms/%j.out
10
+ #SBATCH --comment=medarc
11
+ #SBATCH --no-requeue
12
+ #SBATCH --exclusive
13
+
14
+ export NUM_GPUS=1 # Set to equal gres=gpu:#!
15
+
16
+ source ~/.bashrc
17
+ cd /weka/proj-fmri/paulscotti/fMRI-foundation-model/fMRI-MAE/
18
+ jupyter nbconvert downstream.ipynb --to python
19
+
20
+ # Make sure another job doesnt use same port, here using random number
21
+ export MASTER_PORT=$((RANDOM % (19000 - 11000 + 1) + 11000))
22
+ export HOSTNAMES=$(scontrol show hostnames "$SLURM_JOB_NODELIST")
23
+ export MASTER_ADDR=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n 1)
24
+ export COUNT_NODE=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | wc -l)
25
+ echo MASTER_ADDR=${MASTER_ADDR}
26
+ echo MASTER_PORT=${MASTER_PORT}
27
+ echo WORLD_SIZE=${COUNT_NODE}
28
+
29
+ accelerate launch --num_processes=$(($NUM_GPUS * $COUNT_NODE)) --num_machines=$COUNT_NODE --main_process_ip=$MASTER_ADDR --main_process_port=$MASTER_PORT --mixed_precision=fp16 downstream.py
fMRI-foundation-model-old/fMRI-MAE/mindeye_models.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+
6
+ class MindEyeModule(nn.Module):
7
+ def __init__(self):
8
+ super().__init__()
9
+ def forward(self, x):
10
+ return x
11
+ class RidgeRegression(nn.Module):
12
+ # make sure to add weight_decay when initializing optimizer
13
+ def __init__(self, input_sizes, out_features, seq_len=1):
14
+ super().__init__()
15
+ self.out_features = out_features
16
+ self.linears = torch.nn.ModuleList([
17
+ torch.nn.Linear(input_size, out_features) for input_size in input_sizes
18
+ ])
19
+ self.seq_len = seq_len
20
+ def forward(self, x, subj_idx):
21
+ out = torch.cat([self.linears[subj_idx](x[:,seq]).unsqueeze(1) for seq in range(self.seq_len)], dim=1)
22
+ return out
23
+ class BrainNetwork(nn.Module):
24
+ def __init__(self, h=1024, in_dim=15724, out_dim=768, seq_len=1, n_blocks=4, drop=.15, clip_size=768, clip_scale=1):
25
+ super().__init__()
26
+ self.seq_len = seq_len
27
+ self.h = h
28
+ self.clip_size = clip_size
29
+ self.clip_scale = clip_scale
30
+ self.mixer_blocks1 = nn.ModuleList([
31
+ self.mixer_block1(h, drop) for _ in range(n_blocks)
32
+ ])
33
+ self.mixer_blocks2 = nn.ModuleList([
34
+ self.mixer_block2(seq_len, drop) for _ in range(n_blocks)
35
+ ])
36
+
37
+ # Output linear layer
38
+ self.backbone_linear = nn.Linear(h * seq_len, out_dim, bias=True)
39
+ self.clip_proj = self.projector(clip_size, clip_size, h=clip_size)
40
+
41
+ def projector(self, in_dim, out_dim, h=2048):
42
+ return nn.Sequential(
43
+ nn.LayerNorm(in_dim),
44
+ nn.GELU(),
45
+ nn.Linear(in_dim, h),
46
+ nn.LayerNorm(h),
47
+ nn.GELU(),
48
+ nn.Linear(h, h),
49
+ nn.LayerNorm(h),
50
+ nn.GELU(),
51
+ nn.Linear(h, out_dim)
52
+ )
53
+
54
+ def mlp(self, in_dim, out_dim, drop):
55
+ return nn.Sequential(
56
+ nn.Linear(in_dim, out_dim),
57
+ nn.GELU(),
58
+ nn.Dropout(drop),
59
+ nn.Linear(out_dim, out_dim),
60
+ )
61
+
62
+ def mixer_block1(self, h, drop):
63
+ return nn.Sequential(
64
+ nn.LayerNorm(h),
65
+ self.mlp(h, h, drop), # Token mixing
66
+ )
67
+
68
+ def mixer_block2(self, seq_len, drop):
69
+ return nn.Sequential(
70
+ nn.LayerNorm(seq_len),
71
+ self.mlp(seq_len, seq_len, drop) # Channel mixing
72
+ )
73
+
74
+ def forward(self, x):
75
+ # make empty tensors
76
+ c = torch.Tensor([0.])
77
+
78
+ # Mixer blocks
79
+ residual1 = x
80
+ residual2 = x.permute(0,2,1)
81
+ for block1, block2 in zip(self.mixer_blocks1,self.mixer_blocks2):
82
+ x = block1(x) + residual1
83
+ residual1 = x
84
+ x = x.permute(0,2,1)
85
+
86
+ x = block2(x) + residual2
87
+ residual2 = x
88
+ x = x.permute(0,2,1)
89
+
90
+ x = x.reshape(x.size(0), -1)
91
+ backbone = self.backbone_linear(x).reshape(len(x), -1, self.clip_size)
92
+ if self.clip_scale>0:
93
+ c = self.clip_proj(backbone)
94
+
95
+ return backbone, c
96
+
97
+
98
+ import open_clip
99
+ import kornia
100
+ class FrozenOpenCLIPImageEmbedder(nn.Module):
101
+ """
102
+ Uses the OpenCLIP vision transformer encoder for images
103
+ """
104
+
105
+ def __init__(
106
+ self,
107
+ arch="ViT-H-14",
108
+ version="laion2b_s32b_b79k",
109
+ device="cuda",
110
+ init_device="cpu",
111
+ max_length=77,
112
+ freeze=True,
113
+ antialias=True,
114
+ ucg_rate=0.0,
115
+ unsqueeze_dim=False,
116
+ repeat_to_max_len=False,
117
+ num_image_crops=0,
118
+ output_tokens=False,
119
+ l2_norm_tokens=False,
120
+ only_tokens=False,
121
+ cache_dir: Optional[str] = None,
122
+ ):
123
+ super().__init__()
124
+ model, _, _ = open_clip.create_model_and_transforms(
125
+ arch,
126
+ device=torch.device(init_device),
127
+ pretrained=version,
128
+ cache_dir=cache_dir,
129
+ )
130
+ del model.transformer
131
+ self.model = model
132
+ self.max_crops = num_image_crops
133
+ self.pad_to_max_len = self.max_crops > 0
134
+ self.repeat_to_max_len = repeat_to_max_len and (not self.pad_to_max_len)
135
+ self.device = device
136
+ self.max_length = max_length
137
+ if freeze:
138
+ self.freeze()
139
+
140
+ self.antialias = antialias
141
+
142
+ self.register_buffer(
143
+ "mean", torch.Tensor([0.48145466, 0.4578275, 0.40821073]), persistent=False
144
+ )
145
+ self.register_buffer(
146
+ "std", torch.Tensor([0.26862954, 0.26130258, 0.27577711]), persistent=False
147
+ )
148
+ self.ucg_rate = ucg_rate
149
+ self.unsqueeze_dim = unsqueeze_dim
150
+ self.stored_batch = None
151
+ self.model.visual.output_tokens = output_tokens
152
+ self.output_tokens = output_tokens
153
+ if only_tokens:
154
+ assert output_tokens
155
+ self.only_tokens = only_tokens
156
+ self.l2_norm_tokens = l2_norm_tokens
157
+ if l2_norm_tokens:
158
+ assert output_tokens
159
+
160
+ def preprocess(self, x):
161
+ # normalize to [0,1]
162
+ x = kornia.geometry.resize(
163
+ x,
164
+ (224, 224),
165
+ interpolation="bicubic",
166
+ align_corners=True,
167
+ antialias=self.antialias,
168
+ )
169
+ x = (x + 1.0) / 2.0
170
+ # renormalize according to clip
171
+ x = kornia.enhance.normalize(x, self.mean, self.std)
172
+ return x
173
+
174
+ def freeze(self):
175
+ self.model = self.model.eval()
176
+ for param in self.parameters():
177
+ param.requires_grad = False
178
+
179
+ def forward(self, image, no_dropout=False):
180
+ z = self.encode_with_vision_transformer(image)
181
+ tokens = None
182
+ if self.output_tokens:
183
+ z, tokens = z[0], z[1]
184
+ z = z.to(image.dtype)
185
+ if self.ucg_rate > 0.0 and not no_dropout and not (self.max_crops > 0):
186
+ z = (
187
+ torch.bernoulli(
188
+ (1.0 - self.ucg_rate) * torch.ones(z.shape[0], device=z.device)
189
+ )[:, None]
190
+ * z
191
+ )
192
+ if tokens is not None:
193
+ tokens = (
194
+ expand_dims_like(
195
+ torch.bernoulli(
196
+ (1.0 - self.ucg_rate)
197
+ * torch.ones(tokens.shape[0], device=tokens.device)
198
+ ),
199
+ tokens,
200
+ )
201
+ * tokens
202
+ )
203
+ if self.unsqueeze_dim:
204
+ z = z[:, None, :]
205
+ if self.output_tokens:
206
+ assert not self.repeat_to_max_len
207
+ assert not self.pad_to_max_len
208
+ if self.only_tokens:
209
+ return tokens
210
+ return tokens, z
211
+ if self.repeat_to_max_len:
212
+ if z.dim() == 2:
213
+ z_ = z[:, None, :]
214
+ else:
215
+ z_ = z
216
+ return repeat(z_, "b 1 d -> b n d", n=self.max_length), z
217
+ elif self.pad_to_max_len:
218
+ assert z.dim() == 3
219
+ z_pad = torch.cat(
220
+ (
221
+ z,
222
+ torch.zeros(
223
+ z.shape[0],
224
+ self.max_length - z.shape[1],
225
+ z.shape[2],
226
+ device=z.device,
227
+ ),
228
+ ),
229
+ 1,
230
+ )
231
+ return z_pad, z_pad[:, 0, ...]
232
+ return z
233
+
234
+ def encode_with_vision_transformer(self, img):
235
+ # if self.max_crops > 0:
236
+ # img = self.preprocess_by_cropping(img)
237
+ if img.dim() == 5:
238
+ assert self.max_crops == img.shape[1]
239
+ img = rearrange(img, "b n c h w -> (b n) c h w")
240
+ img = self.preprocess(img)
241
+ if not self.output_tokens:
242
+ assert not self.model.visual.output_tokens
243
+ x = self.model.visual(img)
244
+ tokens = None
245
+ else:
246
+ assert self.model.visual.output_tokens
247
+ x, tokens = self.model.visual(img)
248
+ if self.l2_norm_tokens:
249
+ token_shape = tokens.shape
250
+ tokens = tokens.flatten(1)
251
+ tokens = torch.nn.functional.normalize(tokens, dim=-1)
252
+ tokens = (tokens - .0002) / .0015
253
+ tokens = tokens.view(token_shape)
254
+ tokens = (tokens * 1.0957) + .1598
255
+ if self.max_crops > 0:
256
+ x = rearrange(x, "(b n) d -> b n d", n=self.max_crops)
257
+ # drop out between 0 and all along the sequence axis
258
+ x = (
259
+ torch.bernoulli(
260
+ (1.0 - self.ucg_rate)
261
+ * torch.ones(x.shape[0], x.shape[1], 1, device=x.device)
262
+ )
263
+ * x
264
+ )
265
+ if tokens is not None:
266
+ tokens = rearrange(tokens, "(b n) t d -> b t (n d)", n=self.max_crops)
267
+ logpy.warning(
268
+ f"You are running very experimental token-concat in {self.__class__.__name__}. "
269
+ f"Check what you are doing, and then remove this message."
270
+ )
271
+ if self.output_tokens:
272
+ return x, tokens
273
+ return x
274
+
275
+ def encode(self, text):
276
+ return self(text)
fMRI-foundation-model-old/fMRI-MAE/models.py ADDED
@@ -0,0 +1,423 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ from einops import rearrange
7
+ from einops.layers.torch import Rearrange
8
+ from rope import RotaryPositionalEmbeddings4D
9
+
10
+ def posemb_sincos_4d(patches, temperature=10000, dtype=torch.float32):
11
+ _, f, d, h, w, dim, device, dtype = (*patches.shape, patches.device, patches.dtype)
12
+
13
+ z, y, x, t = torch.meshgrid(
14
+ torch.arange(f, device=device),
15
+ torch.arange(d, device=device),
16
+ torch.arange(h, device=device),
17
+ torch.arange(w, device=device),
18
+ indexing="ij",
19
+ )
20
+
21
+ fourier_dim = dim // 8
22
+
23
+ omega = torch.arange(fourier_dim, device=device) / (fourier_dim - 1)
24
+ omega = 1.0 / (temperature**omega)
25
+
26
+ z, y, x, t = [v.flatten()[:, None] * omega[None, :] for v in [z, y, x, t]]
27
+
28
+ pe = torch.cat(
29
+ (z.sin(), z.cos(), y.sin(), y.cos(), x.sin(), x.cos(), t.sin(), t.cos()), dim=1
30
+ )
31
+ pe = F.pad(pe, (0, dim - (fourier_dim * 8)))
32
+ return pe.type(dtype)
33
+
34
+
35
+ class FeedForward(nn.Module):
36
+ def __init__(self, embed_dim, hidden_dim):
37
+ super().__init__()
38
+ self.net = nn.Sequential(
39
+ nn.LayerNorm(embed_dim),
40
+ nn.Linear(embed_dim, hidden_dim),
41
+ nn.GELU(),
42
+ nn.Linear(hidden_dim, embed_dim),
43
+ )
44
+
45
+ def forward(self, x):
46
+ return self.net(x)
47
+
48
+
49
+ class Attention(nn.Module):
50
+ def __init__(
51
+ self,
52
+ embed_dim: int,
53
+ num_heads: int = 8,
54
+ dim_head: int = 64,
55
+ use_rope: bool = False,
56
+ cls_token: bool = False,
57
+ ):
58
+ super().__init__()
59
+ inner_dim = dim_head * num_heads
60
+ self.num_heads = num_heads
61
+ self.scale = dim_head**-0.5
62
+ self.use_rope = use_rope
63
+ self.cls_token = cls_token
64
+ self.norm = nn.LayerNorm(embed_dim)
65
+
66
+ self.attend = nn.Softmax(dim=-1)
67
+
68
+ self.to_qkv = nn.Linear(embed_dim, inner_dim * 3, bias=False)
69
+ self.to_out = nn.Linear(inner_dim, embed_dim, bias=False)
70
+
71
+ def forward(
72
+ self,
73
+ x: torch.Tensor,
74
+ pos_embed: Optional[nn.Module],
75
+ mask: Optional[torch.Tensor] = None,
76
+ ):
77
+ x = self.norm(x)
78
+
79
+ qkv = self.to_qkv(x).chunk(3, dim=-1)
80
+ q, k, v = map(lambda t: rearrange(t, "b n (h d) -> b h n d", h=self.num_heads), qkv)
81
+ if self.use_rope:
82
+ if pos_embed is None:
83
+ raise ValueError(
84
+ "For RoPE embeddings `pos_embed` should be \
85
+ passed to the Attention forward."
86
+ )
87
+ # apply RoPE other than CLS token if it's included.
88
+ if self.cls_token:
89
+ q_cls = q[:, :, :1, :]
90
+ k_cls = k[:, :, :1, :]
91
+ q = q[:, :, 1:, :]
92
+ k = k[:, :, 1:, :]
93
+ q = pos_embed(q, mask=mask)
94
+ k = pos_embed(k, mask=mask)
95
+ if self.cls_token:
96
+ q = torch.cat([q_cls, q], dim=2)
97
+ k = torch.cat([k_cls, k], dim=2)
98
+
99
+ dots = torch.matmul(q, k.transpose(-1, -2)) * self.scale
100
+
101
+ # print("q", q.shape) # B num_heads N D
102
+ # print("dots", dots.shape) # B num_heads N N
103
+ attn = self.attend(dots)
104
+
105
+ out = torch.matmul(attn, v)
106
+ out = rearrange(out, "b h n d -> b n (h d)")
107
+ return self.to_out(out)
108
+
109
+
110
+ class Transformer(nn.Module):
111
+ def __init__(
112
+ self,
113
+ embed_dim: int,
114
+ depth: int,
115
+ num_heads: int,
116
+ dim_head: int,
117
+ mlp_dim: int,
118
+ use_rope: bool = False,
119
+ grid_height: Optional[int] = None,
120
+ grid_width: Optional[int] = None,
121
+ grid_depth: Optional[int] = None,
122
+ grid_time: Optional[int] = None,
123
+ cls_token: bool = False,
124
+ **args,
125
+ ):
126
+ super().__init__()
127
+ self.embed_dim = embed_dim
128
+ self.num_heads = num_heads
129
+ self.mlp_dim = mlp_dim
130
+ self.dim_head = dim_head
131
+ self.norm = nn.LayerNorm(embed_dim)
132
+ self.layers = nn.ModuleList([])
133
+ for _ in range(depth):
134
+ self.layers.append(
135
+ nn.ModuleList(
136
+ [
137
+ Attention(
138
+ embed_dim,
139
+ num_heads=num_heads,
140
+ dim_head=dim_head,
141
+ use_rope=use_rope,
142
+ cls_token=cls_token,
143
+ ),
144
+ FeedForward(embed_dim, mlp_dim),
145
+ ]
146
+ )
147
+ )
148
+ # RoPE positional embeddings
149
+ self.use_rope = use_rope
150
+ if self.use_rope:
151
+ self.rope_pos_emb = RotaryPositionalEmbeddings4D(
152
+ d=dim_head,
153
+ grid_depth=grid_depth,
154
+ grid_height=grid_height,
155
+ grid_width=grid_width,
156
+ grid_time=grid_time,
157
+ )
158
+
159
+ def forward(self, x, mask: Optional[torch.Tensor] = None):
160
+ for attn, ff in self.layers:
161
+ x = (
162
+ attn(
163
+ x,
164
+ pos_embed=self.rope_pos_emb if self.use_rope else None,
165
+ mask=mask,
166
+ )
167
+ + x
168
+ )
169
+ x = ff(x) + x
170
+ return self.norm(x)
171
+
172
+
173
+ class VisionTransformerMAE(nn.Module):
174
+ def __init__(
175
+ self,
176
+ *,
177
+ encoder,
178
+ decoder,
179
+ image_size,
180
+ image_patch_size,
181
+ frames,
182
+ frame_patch_size,
183
+ channels=1,
184
+ use_rope_emb: bool = False,
185
+ use_cls_token: bool = False,
186
+ num_ids=1000,
187
+ **args,
188
+ ):
189
+ super().__init__()
190
+ image_depth, image_height, image_width = image_size
191
+ patch_depth, patch_height, patch_width = image_patch_size
192
+
193
+ self.encoder_transformer = encoder
194
+ self.decoder_transformer = decoder
195
+
196
+ # Check divisibility
197
+ assert (image_depth % patch_depth == 0 and image_height % patch_height == 0 and image_width %
198
+ patch_width == 0), "Image dimensions must be divisible by the patch size."
199
+ assert (frames % frame_patch_size == 0), "Frames must be divisible by the frame patch size"
200
+
201
+ self.patch_dim = channels * patch_depth * patch_height * patch_width * frame_patch_size
202
+
203
+ self.num_patches = image_size[0]//image_patch_size[0] * image_size[1]//image_patch_size[1] * image_size[2]//image_patch_size[2] * frames
204
+
205
+ self.patchify = Rearrange(
206
+ "b c (f pf) (d pd) (h ph) (w pw) -> b f d h w (pd ph pw pf c)",
207
+ pd=patch_depth,
208
+ ph=patch_height,
209
+ pw=patch_width,
210
+ pf=frame_patch_size,
211
+ )
212
+
213
+ self.unpatchify = nn.Sequential(
214
+ Rearrange(
215
+ "b (f d h w) (pd ph pw pf c) -> b f d h w (pd ph pw pf c)",
216
+ c=channels,
217
+ d=image_depth,
218
+ h=image_height,
219
+ w=image_width,
220
+ pd=patch_depth,
221
+ ph=patch_height,
222
+ pw=patch_width,
223
+ pf=frame_patch_size,
224
+ )
225
+ )
226
+ self.encoder_embed_dim = self.encoder_transformer.embed_dim
227
+
228
+ self.patch_to_emb = nn.Sequential(
229
+ nn.LayerNorm(self.patch_dim),
230
+ nn.Linear(self.patch_dim, self.encoder_embed_dim),
231
+ nn.LayerNorm(self.encoder_embed_dim),
232
+ )
233
+
234
+ self.use_rope_emb = use_rope_emb
235
+ if not self.use_rope_emb:
236
+ self.posemb_sincos_4d = posemb_sincos_4d(
237
+ torch.zeros(
238
+ 1,
239
+ frames // frame_patch_size,
240
+ image_depth // patch_depth,
241
+ image_height // patch_height,
242
+ image_width // patch_width,
243
+ self.encoder_embed_dim,
244
+ )
245
+ )
246
+
247
+ # cls token
248
+ self.use_cls_token = use_cls_token
249
+ if use_cls_token:
250
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, self.encoder_embed_dim))
251
+
252
+ if not isinstance(self.decoder_transformer, nn.Identity):
253
+ self.decoder_embed_dim = self.decoder_transformer.embed_dim
254
+ if self.encoder_embed_dim != self.decoder_embed_dim:
255
+ self.encoder_to_decoder = nn.Linear(self.encoder_embed_dim, self.decoder_embed_dim, bias=False)
256
+ self.mask_token = nn.Parameter(torch.zeros(1, 1, self.decoder_embed_dim))
257
+
258
+ self.decoder_proj = nn.Sequential(
259
+ nn.LayerNorm(self.decoder_embed_dim),
260
+ nn.GELU(),
261
+ nn.Linear(self.decoder_embed_dim, self.patch_dim),
262
+ )
263
+
264
+ def forward(self, x, encoder_mask=None, decoder_mask=None, device="cuda", verbose=False):
265
+ # ENCODER
266
+ if decoder_mask is None:
267
+ if verbose: print(x.shape)
268
+ x = self.patchify(x)
269
+ if verbose: print("patched", x.shape)
270
+ x = rearrange(x, "b ... d -> b (...) d")
271
+ if verbose: print("reshaped", x.shape)
272
+
273
+ x = x[:, encoder_mask]
274
+ if verbose: print("masked", x.shape)
275
+
276
+ x = self.patch_to_emb(x.to(device))
277
+ if verbose: print("patched_emb", x.shape)
278
+
279
+ if not self.use_rope_emb:
280
+ if verbose: print("pe", self.posemb_sincos_4d.shape)
281
+ x = x + self.posemb_sincos_4d[encoder_mask].to(device)
282
+ if self.use_cls_token:
283
+ cls_tokens = self.cls_token.expand(len(x), -1, -1)
284
+ x = torch.cat((cls_tokens, x), dim=1)
285
+ if verbose: print("masked", x.shape)
286
+ x = self.encoder_transformer(x, mask=encoder_mask if self.use_rope_emb else None)
287
+ if verbose: print(x.shape)
288
+
289
+
290
+ # if verbose: print(x.shape)
291
+ # x = self.patchify(x)
292
+ # if verbose: print("patched", x.shape)
293
+ # x = self.patch_to_emb(x.to(device))
294
+ # if verbose: print("patched_emb", x.shape)
295
+ # x = rearrange(x, "b ... d -> b (...) d")
296
+ # if verbose: print("reshaped", x.shape)
297
+ # if not self.use_rope_emb:
298
+ # if verbose: print("pe", self.posemb_sincos_4d.shape)
299
+ # x = x + self.posemb_sincos_4d.to(x.device)
300
+ # if verbose: print("x", x.shape)
301
+ # x = x[:, encoder_mask]
302
+ # if self.use_cls_token:
303
+ # cls_tokens = self.cls_token.expand(len(x), -1, -1)
304
+ # x = torch.cat((cls_tokens, x), dim=1)
305
+ # if verbose: print("masked", x.shape)
306
+ # x = self.encoder_transformer(x, mask=encoder_mask if self.use_rope_emb else None)
307
+ # if verbose: print(x.shape)
308
+ else: # DECODER
309
+ if verbose: print(x.shape)
310
+ if self.encoder_embed_dim != self.decoder_embed_dim:
311
+ x = self.encoder_to_decoder(x.to(device))
312
+ if verbose: print("Linear", x.shape)
313
+ B, _, _ = x.shape
314
+ N = decoder_mask.sum()
315
+ mask = None
316
+ if not self.use_rope_emb:
317
+ pos_embed = self.posemb_sincos_4d.to(x.device)
318
+ if verbose: print("pe", pos_embed.shape)
319
+ if self.encoder_embed_dim != self.decoder_embed_dim:
320
+ pos_embed = self.encoder_to_decoder(pos_embed)
321
+ if verbose: print("Linear pe", pos_embed.shape)
322
+ pos_emd_encoder = pos_embed[encoder_mask]
323
+ pos_emd_decoder = pos_embed[decoder_mask]
324
+ if verbose: print("pos_emd_encoder", pos_emd_encoder.shape)
325
+ if verbose: print("pos_emd_decoder", pos_emd_decoder.shape)
326
+ if self.use_cls_token:
327
+ cls_tokens = x[:,:1,:]
328
+ x = x[:,1:,:]
329
+
330
+ x = torch.cat([x + pos_emd_encoder,
331
+ self.mask_token.repeat(B, N, 1) + pos_emd_decoder],
332
+ dim=1)
333
+ if self.use_cls_token:
334
+ x = torch.cat([cls_tokens, x], dim=1)
335
+ else:
336
+ mask = torch.cat((torch.where(encoder_mask)[0], torch.where(decoder_mask)[0]))
337
+ # No abs positional embeddings for RoPE
338
+ x = torch.cat([x,
339
+ self.mask_token.repeat(B, N - 1 if self.use_cls_token else N, 1)],
340
+ dim=1)
341
+ if verbose: print("x_concat", x.shape)
342
+ x = self.decoder_transformer(x, mask=mask)
343
+ if verbose: print(x.shape)
344
+ x = self.decoder_proj(x)
345
+ if verbose: print("proj", x.shape)
346
+ return x
347
+
348
+ def transformer_mini(**args):
349
+ return Transformer(
350
+ embed_dim=48,
351
+ depth=6,
352
+ num_heads=2,
353
+ mlp_dim=1024,
354
+ dim_head=64,
355
+ **args
356
+ )
357
+
358
+ def transformer_small(**args):
359
+ return Transformer(
360
+ embed_dim=384,
361
+ depth=12,
362
+ num_heads=6,
363
+ mlp_dim=1536,
364
+ dim_head=64,
365
+ **args
366
+ )
367
+
368
+ def transformer_base(**args):
369
+ return Transformer(
370
+ embed_dim=768,
371
+ depth=12,
372
+ num_heads=12,
373
+ mlp_dim=3072,
374
+ dim_head=64,
375
+ **args
376
+ )
377
+
378
+ def transformer_large(**args):
379
+ return Transformer(
380
+ embed_dim=1024,
381
+ depth=24,
382
+ num_heads=16,
383
+ mlp_dim=4096,
384
+ dim_head=64,
385
+ **args
386
+ )
387
+
388
+ def transformer_huge(**args):
389
+ return Transformer(
390
+ embed_dim=1280,
391
+ depth=32,
392
+ num_heads=16,
393
+ mlp_dim=5120,
394
+ dim_head=64,
395
+ **args
396
+ )
397
+
398
+ transformer_mapping = {
399
+ "vit_mini": transformer_mini,
400
+ "vit_small": transformer_small,
401
+ "vit_base": transformer_base,
402
+ "vit_large": transformer_large,
403
+ "vit_huge": transformer_huge,
404
+ "none": nn.Identity
405
+ }
406
+
407
+ def get_vit(size, **args):
408
+ encoder = transformer_mapping[size["encoder"]](**args)
409
+ if args['use_decoder_same_emb_dim']:
410
+ depth = args.pop('decoder_depth', None)
411
+ assert depth is not None and depth > 0 , "for same emb dim decoder depth has to be provided and be >0"
412
+ decoder = Transformer(
413
+ embed_dim=encoder.embed_dim,
414
+ depth=depth,
415
+ num_heads=encoder.num_heads,
416
+ mlp_dim=encoder.mlp_dim,
417
+ dim_head=encoder.dim_head,
418
+ **args
419
+ )
420
+ else:
421
+ decoder = transformer_mapping[size["decoder"]](**args)
422
+ return VisionTransformerMAE(encoder=encoder, decoder=decoder, **args)
423
+
fMRI-foundation-model-old/fMRI-MAE/rope.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ import torch
4
+ import torch.nn as nn
5
+
6
+
7
+ class RotaryPositionalEmbeddings4D(nn.Module):
8
+ def __init__(
9
+ self,
10
+ d: int,
11
+ grid_height: int,
12
+ grid_width: int,
13
+ grid_depth: int,
14
+ grid_time: int,
15
+ base: int = 10_000,
16
+ ):
17
+ super().__init__()
18
+ assert d % 4 == 0, f"{d} is not divisible by 4."
19
+ self.base = base
20
+ self.d = d
21
+ self.grid_height = grid_height
22
+ self.grid_width = grid_width
23
+ self.grid_depth = grid_depth
24
+ self.grid_time = grid_time
25
+ self.cos_cached = None
26
+ self.sin_cached = None
27
+
28
+ def _build_cache_1d(self, effective_d: int, seq_idx: torch.Tensor) -> torch.Tensor:
29
+ seq_idx = seq_idx.reshape(-1) # List of positions
30
+ theta = 1.0 / (
31
+ self.base ** (torch.arange(0, effective_d, 2).float() / effective_d)
32
+ )
33
+
34
+ idx_theta = torch.einsum("n,d->nd", seq_idx, theta)
35
+ idx_theta2 = torch.cat([idx_theta, idx_theta], dim=1)
36
+ return idx_theta2
37
+
38
+ def _build_cache(self, x: torch.Tensor) -> None:
39
+ # x: batched tensor
40
+ # x.shape -> Batch, Seq Length, Embed Dim
41
+ if self.cos_cached is not None and x.shape[1] <= self.cos_cached.shape[1]:
42
+ # if cache is already built
43
+ return
44
+ # get the positions
45
+ grid_h = torch.arange(self.grid_height, dtype=torch.float32)
46
+ grid_w = torch.arange(self.grid_width, dtype=torch.float32)
47
+ grid_d = torch.arange(self.grid_depth, dtype=torch.float32)
48
+ grid_t = torch.arange(self.grid_time, dtype=torch.float32)
49
+
50
+ grid = torch.meshgrid(
51
+ grid_t, grid_d, grid_h, grid_w, indexing="xy"
52
+ ) # This order should match with i/p
53
+ grid = torch.stack(grid, axis=0)
54
+ grid = grid.reshape(
55
+ [4, 1, self.grid_time, self.grid_depth, self.grid_height, self.grid_width]
56
+ )
57
+
58
+ # Get the embedings
59
+ emb_t = self._build_cache_1d(self.d // 4, grid[0]) # 1/4 in-case of 4D
60
+ emb_d = self._build_cache_1d(self.d // 4, grid[1])
61
+ emb_h = self._build_cache_1d(self.d // 4, grid[2]) # (T*D*H*W, embedding_dim/4)
62
+ emb_w = self._build_cache_1d(self.d // 4, grid[3])
63
+ emb = torch.concatenate(
64
+ [emb_t, emb_d, emb_h, emb_w], axis=1
65
+ ) # (T*D*H*W, embedding_dim)
66
+ emb = emb.to(x.device)
67
+ # cache sin and cos
68
+ self.cos_cached = emb.cos()[
69
+ None, None, :, :
70
+ ] # batch, Num Heads, Seq Len, Embed Dim
71
+ self.sin_cached = emb.sin()[None, None, :, :]
72
+
73
+ def _neg_half(self, x: torch.Tensor) -> torch.Tensor:
74
+ d_2 = self.d // 2
75
+ return torch.cat([-x[:, :, :, d_2:], x[:, :, :, :d_2]], dim=-1)
76
+
77
+ def forward(self, x: torch.Tensor, mask: Optional[torch.Tensor]):
78
+ """
79
+ Args:
80
+ -----
81
+ x: query or key vector
82
+ mask: boolean vector of length sequence length.
83
+ True for the non-masked positions.
84
+ """
85
+ self._build_cache(x)
86
+ x_rope, x_pass = x[..., : self.d], x[..., self.d :]
87
+ neg_half_x = self._neg_half(x_rope)
88
+ x_rope = (
89
+ (x_rope * self.cos_cached[:, :, : x.shape[1], :])
90
+ + (neg_half_x * self.sin_cached[:, :, : x.shape[1], :])
91
+ if mask is None
92
+ else (
93
+ x_rope * self.cos_cached[:, :, mask, :]
94
+ + neg_half_x * self.sin_cached[:, :, mask, :]
95
+ )
96
+ )
97
+
98
+ return torch.cat((x_rope, x_pass), dim=-1)
99
+
100
+
101
+ if __name__ == "__main__":
102
+ # img size 4, 64 64 48
103
+ # patch size 1, 8, 8, 8
104
+ # num of patches across time, depth, height, and width -> 4, 8, 8, 6
105
+ rot_embed = RotaryPositionalEmbeddings4D(
106
+ d=512, grid_depth=8, grid_height=8, grid_width=6, grid_time=4
107
+ )
108
+ query_ = torch.randn(
109
+ 10, 1, 8 * 8 * 6 * 4, 512
110
+ ) # Batch, Heads, Num Tokens/Seq Length, Embedding Dims
111
+ print(rot_embed(query_).shape)
fMRI-foundation-model-old/fMRI-MAE/utils.py ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from io import BytesIO
2
+ import os
3
+ import random
4
+ import numpy as np
5
+ import torch
6
+ from einops import rearrange
7
+ from PIL import Image
8
+ from skimage import filters
9
+ from torchvision import transforms
10
+ import nibabel as nib
11
+ from nilearn import plotting
12
+ import matplotlib.pyplot as plt
13
+ import re
14
+ import torch.nn.functional as F
15
+ import torch.nn as nn
16
+
17
+ def my_split_by_node(urls): return urls
18
+
19
+ def is_interactive():
20
+ import __main__ as main
21
+
22
+ return not hasattr(main, "__file__")
23
+
24
+ def my_split_by_node(urls): return urls
25
+
26
+ def seed_everything(seed=0, cudnn_deterministic=True):
27
+ random.seed(seed)
28
+ os.environ['PYTHONHASHSEED'] = str(seed)
29
+ np.random.seed(seed)
30
+ torch.manual_seed(seed)
31
+ torch.cuda.manual_seed(seed)
32
+ torch.cuda.manual_seed_all(seed)
33
+
34
+
35
+ def grayscale_decoder(image_data):
36
+ return np.array(Image.open(BytesIO(image_data))).astype(np.float32) / 65535
37
+
38
+
39
+ def numpy_decoder(npy_data):
40
+ return np.load(BytesIO(npy_data))
41
+
42
+
43
+ def reshape_to_2d(tensor):
44
+ if tensor.ndim == 5:
45
+ tensor = tensor[0]
46
+ assert tensor.ndim == 4
47
+ return rearrange(tensor, "b h w c -> (b h) (c w)")
48
+
49
+
50
+ def reshape_to_original(tensor_2d, h=64, w=64, c=48):
51
+ # print(tensor_2d.shape) # torch.Size([1, 256, 3072])
52
+ return rearrange(tensor_2d, "(tr h) (c w) -> tr h w c", h=h, w=w, c=c)
53
+
54
+
55
+ def plot_numpy_nii(image):
56
+ while image.ndim > 3:
57
+ image = image[0]
58
+ nii = nib.Nifti1Image(image.astype(np.float32), np.eye(4)) # noqa
59
+ plotting.plot_epi(nii, cmap="gray")
60
+
61
+
62
+ def threshold_based_masking(org_images):
63
+ thresholds = filters.threshold_multiotsu(org_images.numpy(), classes=3)
64
+ brain_segmentation = org_images > thresholds.min()
65
+ return brain_segmentation
66
+
67
+
68
+ def get_brain_pos_patches(
69
+ func,
70
+ patch_depth=8,
71
+ patch_height=8,
72
+ patch_width=8,
73
+ frame_patch_size=1,
74
+ masking_strategy="conservative",
75
+ ):
76
+ _, _, depth = func.shape
77
+ if masking_strategy == "conservative":
78
+ func = func.sum(axis=(-1), keepdim=True).repeat(1, 1, depth)
79
+ else:
80
+ raise Exception("Not implemented other masking strategies than conservative.")
81
+
82
+ return func
83
+
84
+ def crop_or_pad(tensor, new_shape):
85
+ # Ensure the tensor has at least three dimensions
86
+ if tensor.dim() < 3:
87
+ raise ValueError("Tensor must have at least 3 dimensions")
88
+
89
+ # Current dimensions of the last three axes
90
+ current_shape = tensor.shape[-3:]
91
+
92
+ # Compute padding and cropping needed for each dimension
93
+ padding_crop = [(ns - cs) for ns, cs in zip(new_shape, current_shape)]
94
+ if sum(padding_crop)==0:
95
+ return tensor
96
+
97
+ # Apply cropping if necessary
98
+ if any(pc < 0 for pc in padding_crop):
99
+ crop_slices = [slice(-pc//2, ns-pc//2) if pc < 0 else slice(None) for pc, ns in zip(padding_crop, new_shape)]
100
+ tensor = tensor[..., crop_slices[0], crop_slices[1], crop_slices[2]]
101
+
102
+ # Calculate padding to apply after cropping if necessary
103
+ pad_values = [(max(0, pc)//2, max(0, pc) - max(0, pc)//2) for pc in padding_crop]
104
+
105
+ # Apply padding
106
+ tensor = F.pad(tensor, pad_values[2] + pad_values[1] + pad_values[0])
107
+
108
+ return tensor
109
+
110
+
111
+ class DataPrepper:
112
+ def __init__(
113
+ self,
114
+ num_frames=4,
115
+ masking_strategy="MNI",
116
+ patch_depth=8,
117
+ patch_height=8,
118
+ patch_width=8,
119
+ frame_patch_size=1,
120
+ image_size=[88, 104, 72]
121
+ ):
122
+ self.num_frames = num_frames
123
+ self.masking_strategy = masking_strategy
124
+ self.patch_depth = 8
125
+ self.patch_height = 8
126
+ self.patch_width = 8
127
+ self.frame_patch_size = 1
128
+ self.image_size=image_size
129
+
130
+ def __call__(self, func):
131
+ start_timepoint = np.random.choice(np.arange(func.shape[1] - self.num_frames))
132
+ timepoints = np.arange(start_timepoint, start_timepoint + self.num_frames)
133
+
134
+ func = func[:,timepoints]
135
+
136
+ # crop image_size acc to config
137
+ func = crop_or_pad(func, self.image_size)
138
+
139
+ if self.masking_strategy=="MNI" or self.masking_strategy=="None":
140
+ return func, None
141
+
142
+ brain_segmentation = threshold_based_masking(func.mean(1))
143
+ pos_patches = None
144
+ for brain in brain_segmentation:
145
+ output = get_brain_pos_patches(
146
+ brain,
147
+ patch_depth=self.patch_depth,
148
+ patch_height=self.patch_height,
149
+ patch_width=self.patch_width,
150
+ frame_patch_size=self.frame_patch_size,
151
+ masking_strategy=self.masking_strategy,
152
+ )
153
+ if pos_patches is None:
154
+ pos_patches = output[None]
155
+ else:
156
+ pos_patches = torch.vstack((pos_patches, output[None]))
157
+ return func, pos_patches
158
+
159
+
160
+ def plot_slices(unpatches):
161
+ if unpatches.ndim == 5:
162
+ unpatches = unpatches[0]
163
+ return transforms.ToPILImage()(reshape_to_2d(unpatches))
164
+
165
+
166
+ def check_loss(loss):
167
+ if loss.isnan().any():
168
+ raise ValueError('NaN loss')
169
+
170
+
171
+ def count_params(model):
172
+ total = sum(p.numel() for p in model.parameters())
173
+ trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
174
+ print("param counts:\n{:,} total\n{:,} trainable".format(total, trainable))
175
+ return trainable
176
+
177
+
178
+ def contrastive_loss(
179
+ cls_token1: torch.Tensor, cls_token2: torch.Tensor, temperature: torch.Tensor
180
+ ):
181
+ feat1 = cls_token1 / cls_token1.norm(dim=1, keepdim=True)
182
+ feat2 = cls_token2 / cls_token2.norm(dim=1, keepdim=True)
183
+
184
+ cosine_sim = feat1 @ feat2.T
185
+ logit_scale = temperature.exp() # log scale, learned during training
186
+ feat1 = cosine_sim * logit_scale
187
+ feat2 = feat1.T
188
+
189
+ labels = torch.arange(feat1.shape[0]).to(feat1.device)
190
+ loss = (
191
+ torch.nn.functional.cross_entropy(feat1, labels)
192
+ + torch.nn.functional.cross_entropy(feat2, labels)
193
+ ) / 2
194
+ return loss
195
+
196
+ ### MindEye functions ###
197
+
198
+ def soft_clip_loss(preds, targs, temp=0.006):
199
+ clip_clip = (targs @ targs.T)/temp
200
+ brain_clip = (preds @ targs.T)/temp
201
+ loss1 = -(brain_clip.log_softmax(-1) * clip_clip.softmax(-1)).sum(-1).mean()
202
+ loss2 = -(brain_clip.T.log_softmax(-1) * clip_clip.softmax(-1)).sum(-1).mean()
203
+
204
+ loss = (loss1 + loss2)/2
205
+ return loss
206
+
207
+ def cosine_anneal(start, end, steps):
208
+ return end + (start - end)/2 * (1 + torch.cos(torch.pi*torch.arange(steps)/(steps-1)))
209
+
210
+ def mixco(voxels, beta=0.15, s_thresh=0.5, perm=None, betas=None, select=None):
211
+ if perm is None:
212
+ perm = torch.randperm(voxels.shape[0])
213
+ voxels_shuffle = voxels[perm].to(voxels.device,dtype=voxels.dtype)
214
+ if betas is None:
215
+ betas = torch.distributions.Beta(beta, beta).sample([voxels.shape[0]]).to(voxels.device,dtype=voxels.dtype)
216
+ if select is None:
217
+ select = (torch.rand(voxels.shape[0]) <= s_thresh).to(voxels.device)
218
+ betas_shape = [-1] + [1]*(len(voxels.shape)-1)
219
+ voxels[select] = voxels[select] * betas[select].reshape(*betas_shape) + \
220
+ voxels_shuffle[select] * (1 - betas[select]).reshape(*betas_shape)
221
+ betas[~select] = 1
222
+ return voxels, perm, betas, select
223
+
224
+ def mixco_nce(preds, targs, temp=0.1, perm=None, betas=None, select=None, distributed=False,
225
+ accelerator=None, local_rank=None, bidirectional=True):
226
+ brain_clip = (preds @ targs.T)/temp
227
+
228
+ if perm is not None and betas is not None and select is not None:
229
+ probs = torch.diag(betas)
230
+ probs[torch.arange(preds.shape[0]).to(preds.device), perm] = 1 - betas
231
+
232
+ loss = -(brain_clip.log_softmax(-1) * probs).sum(-1).mean()
233
+ if bidirectional:
234
+ loss2 = -(brain_clip.T.log_softmax(-1) * probs.T).sum(-1).mean()
235
+ loss = (loss + loss2)/2
236
+ return loss
237
+ else:
238
+ loss = F.cross_entropy(brain_clip, torch.arange(brain_clip.shape[0]).to(brain_clip.device))
239
+ if bidirectional:
240
+ loss2 = F.cross_entropy(brain_clip.T, torch.arange(brain_clip.shape[0]).to(brain_clip.device))
241
+ loss = (loss + loss2)/2
242
+ return loss
243
+
244
+ def topk(similarities,labels,k=5):
245
+ if k > similarities.shape[0]:
246
+ k = similarities.shape[0]
247
+ topsum=0
248
+ for i in range(k):
249
+ topsum += torch.sum(torch.argsort(similarities,axis=1)[:,-(i+1)] == labels)/len(labels)
250
+ return topsum
251
+
252
+ def patchwise_cosine_similarity(latents1,latents2=None):
253
+ if latents2 is None:
254
+ latents_norm = latents1/latents1.norm(dim=-1, keepdim=True)
255
+ cos_sim = torch.bmm(latents_norm, latents_norm.permute(0,2,1))
256
+ else:
257
+ latents_norm1 = latents1/latents1.norm(dim=-1, keepdim=True)
258
+ latents_norm2 = latents2/latents2.norm(dim=-1, keepdim=True)
259
+ cos_sim = latents_norm1 @ latents_norm2.T
260
+ return cos_sim
261
+
262
+ def batchwise_cosine_similarity(Z,B):
263
+ Z = Z.flatten(1)
264
+ B = B.flatten(1).T
265
+ Z_norm = torch.linalg.norm(Z, dim=1, keepdim=True) # Size (n, 1).
266
+ B_norm = torch.linalg.norm(B, dim=0, keepdim=True) # Size (1, b).
267
+ cosine_similarity = ((Z @ B) / (Z_norm @ B_norm)).T
268
+ return cosine_similarity
269
+
270
+ def prenormed_batchwise_cosine_similarity(Z,B):
271
+ return (Z @ B.T).T
272
+
273
+ def torch_to_Image(x):
274
+ if x.ndim==4:
275
+ x=x[0]
276
+ return transforms.ToPILImage()(x)
277
+
278
+ def get_masking_ratio(current_epoch, total_epochs, start_masking_ratio, end_masking_ratio):
279
+ """Returns the masking ratio for the current epochs. Linearly increase the masking ratio over the span of the training"""
280
+ return start_masking_ratio + (end_masking_ratio-start_masking_ratio) * ((current_epoch+1)/total_epochs)
281
+
282
+ def view_brain(data,cut_coords=None):
283
+ if torch.is_tensor(data):
284
+ data = data.numpy()
285
+ if data.ndim==5:
286
+ new_nii = nib.Nifti1Image((data[0,0].astype(np.float32)-.5)*2, np.eye(4))
287
+ elif data.ndim==4:
288
+ new_nii = nib.Nifti1Image((data[0].astype(np.float32)-.5)*2, np.eye(4))
289
+ elif data.ndim==3:
290
+ new_nii = nib.Nifti1Image((data.astype(np.float32)-.5)*2, np.eye(4))
291
+ else:
292
+ raise Exception("Check dimensionality of your brain data")
293
+ return plotting.view_img(new_nii, bg_img=None, cut_coords=cut_coords, vmax=1, cmap=plt.cm.gray, threshold=None)
294
+
295
+ def get_first_tar(train_urls):
296
+ if isinstance(train_urls, list):
297
+ # If train_urls is a list, get the first element
298
+ url = train_urls[0]
299
+ else:
300
+ # If train_urls is a string, treat it as the only element
301
+ url = train_urls
302
+
303
+ # Extract the first tar file using regular expression
304
+ match = re.search(r'\{(\d+)\.\.', url)
305
+ if match:
306
+ first_tar = match.group(1)
307
+ return f"/scratch/fmri_foundation_datasets/NSD_MNI_wds/{first_tar}.tar"
308
+ else:
309
+ return None
310
+
311
+
312
+ class VICRegHandler(nn.Module):
313
+ def __init__(self, in_dim, num_layers=3, act=nn.GELU, h=1024, out_dim=4096):
314
+ super().__init__()
315
+ self.projector = nn.Sequential(
316
+ nn.Linear(in_dim, h),
317
+ nn.LayerNorm(h),
318
+ act(),
319
+ nn.Linear(h, h),
320
+ nn.LayerNorm(h),
321
+ act(),
322
+ nn.Linear(h, out_dim),
323
+ )
324
+
325
+ def forward(self, x):
326
+ return self.projector(x)
327
+
328
+ @staticmethod
329
+ def filter_global_to_local(l, enc_mask, dec_mask):
330
+ '''Get the subset of global tokens that correspond to encoder mask only'''
331
+ comb_mask = enc_mask | dec_mask
332
+ comb_indices = torch.where(comb_mask)[0]
333
+ enc_indices = torch.where(enc_mask)[0]
334
+ # enc_set = set(enc_indices.cpu().tolist())
335
+
336
+ # new_mask = torch.zeros_like(comb_indices, dtype=bool)
337
+ # for i, idx in enumerate(comb_indices):
338
+ # if idx in enc_set:
339
+ # new_mask[i] = True
340
+
341
+ new_mask = torch.isin(comb_indices, enc_indices)
342
+ return l[:, new_mask]
343
+
344
+ @staticmethod
345
+ def vicreg_loss(l1, l2, gamma=1.0, lamda=25, mu=25, nu=1, rand_frac=0.2, use_vic_cls=True, eps=1e-4):
346
+ if use_vic_cls:
347
+ # always keep cls and pick a random set of tokens
348
+ rand_indices = torch.cat([torch.tensor([0]), 1+torch.randperm(l1.shape[1]-1)])[:int(rand_frac*l1.shape[1])]
349
+ else:
350
+ # drop cls tokens from loss calc
351
+ l1 = l1[:, 1:]
352
+ l2 = l2[:, 1:]
353
+ rand_indices = torch.randperm(l1.shape[1])[:int(rand_frac*l1.shape[1])]
354
+
355
+ std_l1 = torch.sqrt(l1.flatten(1).var(dim=0)+eps) # nxd
356
+ std_l2 = torch.sqrt(l2.flatten(1).var(dim=0)+eps) # nxd
357
+ var_loss = F.relu(gamma - std_l1).mean() + F.relu(gamma - std_l2).mean()
358
+ del std_l1, std_l2
359
+
360
+ sim_loss = F.mse_loss(l1, l2)
361
+
362
+ l1 = l1 - l1.mean(0, keepdim=True) # b,n,d
363
+ l2 = l2 - l2.mean(0, keepdim=True)
364
+
365
+ l1_sub = l1[:, rand_indices]
366
+ del l1
367
+ cov_l1 = torch.bmm(l1_sub.permute(1,2,0), l1_sub.permute(1,0,2))/(l1_sub.shape[0]-1) # 0.1*n,d,d
368
+ cov_loss = ((cov_l1**2).sum() - (torch.diagonal(cov_l1, dim1=1,dim2=2)**2).sum())/(l1_sub.shape[1]*l1_sub.shape[2])
369
+ del cov_l1, l1_sub
370
+
371
+ l2_sub = l2[:, rand_indices]
372
+ del l2
373
+ cov_l2 = torch.bmm(l2_sub.permute(1,2,0), l2_sub.permute(1,0,2))/(l2_sub.shape[0]-1)
374
+ cov_loss = cov_loss + ((cov_l2**2).sum() - (torch.diagonal(cov_l2, dim1=1,dim2=2)**2).sum())/(l2_sub.shape[1]*l2_sub.shape[2]) # div by nxd
375
+ del cov_l2, l2_sub
376
+
377
+ vic_loss = lamda * sim_loss + mu * var_loss + nu * cov_loss
378
+
379
+ return vic_loss
380
+
381
+
382
+ class SimCLRHandler(nn.Module):
383
+ def __init__(self, in_dim, num_layers=2, act=nn.GELU, out_dim=1024):
384
+ super().__init__()
385
+ self.projector = nn.Sequential(
386
+ nn.Linear(in_dim, in_dim),
387
+ act(),
388
+ nn.Linear(in_dim, max(in_dim,out_dim)),
389
+ )
390
+
391
+ def forward(self, x):
392
+ return self.projector(x)
393
+
394
+ @staticmethod
395
+ def simclr_loss(lats, temp=0.006):
396
+ logits = (nn.functional.normalize(lats.flatten(1),dim=-1) @
397
+ nn.functional.normalize(lats.flatten(1),dim=-1).T) / temp
398
+
399
+ labels = torch.diag_embed(
400
+ torch.ones(logits.shape[0] // 2), offset=logits.shape[0] // 2
401
+ ) + torch.diag_embed(torch.ones(logits.shape[0] // 2), offset=-logits.shape[0] // 2)
402
+ labels = labels.to(lats.device)
403
+
404
+ mask = torch.ones_like(logits).bool()
405
+ torch.diagonal(mask).fill_(False)
406
+
407
+ labels = labels[mask].reshape(logits.shape[0], logits.shape[0]-1)
408
+ logits = logits[mask].reshape(*labels.shape)
409
+
410
+ contr_loss = -(logits.log_softmax(-1) * labels).sum(-1).mean()
411
+
412
+ return contr_loss
fMRI-foundation-model-old/flat/.ipynb_checkpoints/main-checkpoint.py ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ import sys
4
+ import json
5
+ import yaml
6
+ import numpy as np
7
+ import copy
8
+ import math
9
+ import time
10
+ import random
11
+ from tqdm import tqdm
12
+ import webdataset as wds
13
+ import matplotlib.pyplot as plt
14
+
15
+ import torch
16
+ import torch.nn as nn
17
+ from torchvision import transforms
18
+ import utils
19
+ from flat_models import *
20
+
21
+ # tf32 data type is faster than standard float32
22
+ torch.backends.cuda.matmul.allow_tf32 = True
23
+ # following fixes a Conv3D CUDNN_NOT_SUPPORTED error
24
+ torch.backends.cudnn.benchmark = True
25
+
26
+ ### Multi-GPU config ###
27
+ device_count = torch.cuda.device_count()
28
+ print(f"Number of available CUDA devices: {device_count}")
29
+
30
+ local_rank = os.getenv('LOCAL_RANK')
31
+ if local_rank is None:
32
+ local_rank = 0
33
+ else:
34
+ local_rank = int(local_rank)
35
+ print(f"LOCAL RANK={local_rank}")
36
+
37
+ num_devices = os.getenv('NUM_GPUS')
38
+ if num_devices is None:
39
+ num_devices = 1
40
+ else:
41
+ num_devices = int(num_devices)
42
+ print(f"NUM GPUS={num_devices}")
43
+ distributed = True if num_devices>1 else False
44
+ if distributed: assert device_count==num_devices
45
+
46
+ node = os.getenv('SLURM_NODEID')
47
+ if node is None:
48
+ node = 0
49
+ else:
50
+ node = int(node)
51
+ print(f"NODE={node}")
52
+
53
+ global_rank = os.getenv('RANK')
54
+ if global_rank is None:
55
+ global_rank = 0
56
+ else:
57
+ global_rank = int(global_rank)
58
+ print(f"GLOBAL RANK={global_rank}")
59
+
60
+ world_size = os.getenv('WORLD_SIZE')
61
+ if world_size is None:
62
+ world_size = 1
63
+ else:
64
+ world_size = int(world_size)
65
+ print(f"WORLD_SIZE={world_size}")
66
+
67
+ # Load parameters from yaml config
68
+ config = yaml.load(open('config.yaml', 'r'), Loader=yaml.FullLoader)
69
+
70
+ print("\n__CONFIG__")
71
+ for attribute_name in config.keys():
72
+ print(f"{attribute_name} = {config[attribute_name]}")
73
+ globals()[attribute_name] = config[f'{attribute_name}']
74
+ print("\n")
75
+
76
+ # Create outdir for ckpt and config.yaml
77
+ outdir = os.path.abspath(f'checkpoints/{model_name}')
78
+ print("outdir", outdir)
79
+
80
+ # Load previous config.yaml if available
81
+ if os.path.exists(f"{outdir}/config.yaml"):
82
+ config = yaml.load(open(f"{outdir}/config.yaml", 'r'), Loader=yaml.FullLoader)
83
+ print(f"Loaded config.yaml from ckpt folder {outdir}")
84
+
85
+ # create global variables from the config
86
+ print("\n__REPLACING_CONFIG__")
87
+ for attribute_name in config.keys():
88
+ print(f"{attribute_name} = {config[attribute_name]}")
89
+ globals()[attribute_name] = config[f'{attribute_name}']
90
+ print("\n")
91
+
92
+ data_type = torch.float32 # change depending on your mixed_precision
93
+ global_batch_size = batch_size * world_size
94
+
95
+ # FSDP Setup
96
+ if distributed:
97
+ import torch.distributed as dist
98
+ from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
99
+ from torch.distributed.fsdp.api import BackwardPrefetch, CPUOffload, ShardingStrategy
100
+ import functools
101
+ from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy, transformer_auto_wrap_policy
102
+ print(f"setting device to cuda:{local_rank}")
103
+ torch.cuda.set_device(local_rank)
104
+ device = torch.device('cuda',local_rank)
105
+ dist.init_process_group("nccl", rank=global_rank, world_size=world_size)
106
+ print(f"\nSuccessfully set cuda:{local_rank} | global_rank{global_rank} | node{node}")
107
+ dist.barrier()
108
+ print(f"global_rank{global_rank} passed barrier")
109
+ else:
110
+ device = torch.device('cuda')
111
+
112
+ print("PID of this process =",os.getpid())
113
+ print("device =", device, "distributed =",distributed, "num_devices =", num_devices, "local rank =", local_rank, "world size =", world_size, "data_type =", data_type)
114
+
115
+ # seed all random functions
116
+ utils.seed_everything(seed + global_rank)
117
+
118
+ from util.hcp_flat import load_hcp_flat_mask
119
+ from util.hcp_flat import create_hcp_flat
120
+ from util.losses import *
121
+ import util.visualize as vis
122
+
123
+ if utils.is_interactive(): # Use less samples per epoch for debugging
124
+ num_samples_per_epoch = 2000
125
+ test_num_samples_per_epoch = 2000
126
+
127
+ model = mae_vit_small_fmri(
128
+ decoder_embed_dim=decoder_embed_dim,
129
+ t_patch_size=t_patch_size,
130
+ pred_t_dim=pred_t_dim,
131
+ decoder_depth=4,
132
+ cls_embed=cls_embed,
133
+ norm_pix_loss=norm_pix_loss,
134
+ no_qkv_bias=no_qkv_bias,
135
+ sep_pos_embed=sep_pos_embed,
136
+ trunc_init=trunc_init,
137
+ )
138
+
139
+ if use_contrastive_loss:
140
+ model.simclr_handler = SimCLRHandler(model.embed_dim).to(device)
141
+ if use_vic_loss:
142
+ model.vicreg_handler = VICRegHandler(model.embed_dim).to(device)
143
+
144
+ # state = torch.load("checkpoints/checkpoint-00099.pth", map_location="cpu")
145
+ # model.load_state_dict(state["model"])
146
+
147
+ num_batches = num_samples_per_epoch // (num_devices * batch_size)
148
+ test_num_batches = test_num_samples_per_epoch // (num_devices * batch_size)
149
+ print("num_batches", num_batches)
150
+ print("test_num_batches", test_num_batches)
151
+
152
+ ## Train ##
153
+ train_dataset = create_hcp_flat(root=hcp_flat_path,
154
+ training=True, frames=num_frames)
155
+ train_dl = wds.WebLoader(
156
+ train_dataset.batched(batch_size, partial=False),
157
+ batch_size=None,
158
+ shuffle=False,
159
+ num_workers=num_workers,
160
+ pin_memory=True,
161
+ )
162
+ train_dl = train_dl.with_epoch(num_batches)
163
+
164
+ ## Test ##
165
+ test_dataset = create_hcp_flat(root=hcp_flat_path,
166
+ training=False, frames=num_frames)
167
+ test_dl = wds.WebLoader(
168
+ test_dataset.batched(batch_size, partial=False),
169
+ batch_size=None,
170
+ shuffle=False,
171
+ num_workers=num_workers,
172
+ pin_memory=True,
173
+ )
174
+ test_dl = test_dl.with_epoch(test_num_batches)
175
+
176
+ print(f"\nChecking distributed setup on global_rank {global_rank}...")
177
+ from util.video_vit import Attention
178
+ if distributed:
179
+ my_auto_wrap_policy = functools.partial(
180
+ transformer_auto_wrap_policy,
181
+ transformer_layer_cls={
182
+ Attention, # <--- Your Transformer layer class
183
+ },
184
+ )
185
+ print(f"\nPrepping FSDP on {global_rank} {node}...\n")
186
+ model = model.to(device)
187
+ model = FSDP(
188
+ model,
189
+ sharding_strategy=ShardingStrategy.HYBRID_SHARD,
190
+ auto_wrap_policy=my_auto_wrap_policy,
191
+ use_orig_params=False,
192
+ cpu_offload=None, #CPUOffload(offload_params=True)
193
+ sync_module_states=True,
194
+ limit_all_gathers=True, # See https://github.com/pytorch/pytorch/issues/91165
195
+ device_id=device,
196
+ )
197
+ print(f"\nSuccessfully loaded FSDP model to device on global_rank {global_rank}\n")
198
+ dist.barrier()
199
+ else:
200
+ print(f"\nNot training distributed! global_rank {global_rank}")
201
+ model.to(device)
202
+
203
+ no_decay = ["bias", "LayerNorm.bias", "LayerNorm.weight"]
204
+ opt_grouped_parameters = [
205
+ {'params': [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 0.05},
206
+ {'params': [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0},
207
+ ]
208
+
209
+ lr = base_lr * global_batch_size / 256
210
+ print(f"multiply base lr {base_lr} by effective batch size {global_batch_size}")
211
+ print(f"lr = {lr}")
212
+
213
+ optimizer = torch.optim.AdamW(opt_grouped_parameters, lr=lr, betas=(0.9, 0.95))
214
+
215
+ def adjust_learning_rate(optimizer, epoch, warmup_epochs=5, min_lr=0.0):
216
+ """Decay the learning rate with half-cycle cosine after warmup"""
217
+ if epoch < warmup_epochs:
218
+ lr_ = lr * epoch / warmup_epochs
219
+ else:
220
+ lr_ = min_lr + (lr - min_lr) * 0.5 * (
221
+ 1.0
222
+ + math.cos(
223
+ math.pi
224
+ * (epoch - warmup_epochs)
225
+ / (num_epochs - warmup_epochs)
226
+ )
227
+ )
228
+ for param_group in optimizer.param_groups:
229
+ param_group["lr"] = lr_
230
+ return lr_
231
+
232
+ print("\nDone with model preparations!")
233
+ num_params = utils.count_params(model)
234
+
235
+ def save_ckpt(model,tag="last"):
236
+ if distributed: dist.barrier()
237
+ model_states = model.state_dict()
238
+ if global_rank == 0:
239
+ os.makedirs(outdir,exist_ok=True)
240
+ ckpt_path = outdir+f'/{tag}.pth'
241
+
242
+ if tag == "last" and os.path.exists(ckpt_path):
243
+ shutil.copyfile(os.path.join(outdir, f'{tag}.pth'), os.path.join(outdir, f'{tag}_old.pth'))
244
+ # print(f'saving {ckpt_path}',flush=True)
245
+ if tag=='last':
246
+ torch.save({
247
+ 'epoch': epoch,
248
+ 'model_state_dict': model_states,
249
+ 'optimizer_state_dict': optimizer.state_dict(),
250
+ }, ckpt_path)
251
+ else:
252
+ torch.save({
253
+ 'epoch': epoch,
254
+ 'model_state_dict': model_states,
255
+ }, ckpt_path)
256
+
257
+ if tag == "last" and os.path.exists(os.path.join(outdir, f'{tag}_old.pth')):
258
+ os.remove(os.path.join(outdir, f'{tag}_old.pth'))
259
+ print(f"\n---saved {ckpt_path}!---\n")
260
+
261
+ # save the config.yaml
262
+ if not os.path.exists(f"{outdir}/config.yaml"):
263
+ with open(f"{outdir}/config.yaml", 'w') as file:
264
+ yaml.dump(config, file)
265
+ print(f"saved {outdir}/config.yaml!")
266
+
267
+ # auto resume
268
+ if os.path.exists(os.path.join(outdir, 'last.pth')) or os.path.exists(os.path.join(outdir, 'last_old.pth')):
269
+ if os.path.exists(os.path.join(outdir, 'last_old.pth')):
270
+ if os.path.exists(os.path.join(outdir, 'last.pth')):
271
+ # this is corrupted
272
+ os.remove(os.path.join(outdir, f'last.pth'))
273
+ # set last_old as last
274
+ shutil.move(os.path.join(outdir, f'last_old.pth'), os.path.join(outdir, f'last.pth'))
275
+
276
+ ckpt_path = os.path.join(outdir, 'last.pth')
277
+ resume_from_ckpt = True
278
+
279
+ epoch = 0
280
+ if resume_from_ckpt:
281
+ print("\n---resuming from ckpt_path---\n", ckpt_path)
282
+ checkpoint = torch.load(ckpt_path, map_location=device)
283
+ epoch = checkpoint['epoch']+1
284
+ optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
285
+ model.load_state_dict(checkpoint['model_state_dict'])
286
+ # total_steps_done = epoch*num_iterations_per_epoch
287
+ # for _ in range(total_steps_done):
288
+ # lr_scheduler.step()
289
+ del checkpoint
290
+ torch.cuda.empty_cache()
291
+
292
+ if utils.is_interactive():
293
+ wandb_log = False
294
+ ckpt_saving = False
295
+ if local_rank==0 and wandb_log: # only use main process for wandb logging
296
+ import wandb
297
+ wandb_project = 'fMRI-foundation-model'
298
+ print(f"wandb {wandb_project} run {model_name}")
299
+ # need to configure wandb beforehand in terminal with "wandb init"!
300
+ wandb_config = {
301
+ "model_name": model_name,
302
+ "global_batch_size": global_batch_size,
303
+ "batch_size": batch_size,
304
+ "num_epochs": num_epochs,
305
+ "cls_embed": cls_embed,
306
+ "norm_pix_loss": norm_pix_loss,
307
+ "t_patch_size": t_patch_size,
308
+ "pred_t_dim": pred_t_dim,
309
+ "mask_ratio": mask_ratio,
310
+ "num_frames": num_frames,
311
+ "sep_pos_embed": sep_pos_embed,
312
+ "decoder_embed_dim": decoder_embed_dim,
313
+ "use_contrastive_loss": use_contrastive_loss,
314
+ "num_params": num_params,
315
+ "base_lr": base_lr,
316
+ "lr": lr,
317
+ "num_samples_per_epoch": num_samples_per_epoch,
318
+ "test_num_samples_per_epoch": test_num_samples_per_epoch,
319
+ "num_epochs": num_epochs,
320
+ "grad_clip": grad_clip,
321
+ "ckpt_interval": ckpt_interval,
322
+ "ckpt_saving": ckpt_saving,
323
+ "print_interval": print_interval,
324
+ "seed": seed,
325
+ "distributed": distributed,
326
+ "num_devices": num_devices,
327
+ "world_size": world_size,
328
+ }
329
+ print("wandb_config:\n",wandb_config)
330
+ print("wandb_id:",model_name)
331
+ wandb.init(
332
+ id=model_name,
333
+ project=wandb_project,
334
+ name=model_name,
335
+ config=wandb_config,
336
+ resume="allow",
337
+ )
338
+ else:
339
+ wandb_log = False
340
+
341
+ # lrs, train_losses, test_losses = [], [], []
342
+ # train_losses1, train_losses2, train_losses3, train_losses4 = [], [], [], []
343
+
344
+ epoch = 0
345
+ lrs, train_losses, recon_losses, contrastive_losses, vic_losses = [], [], [], [], []
346
+ cos_sim_encoder_output, cos_sim_decoder_output, cos_sim_encoder_output_patchwise = [], [], []
347
+ probe_losses, probe_accs, test_losses, test_accs = [], [], [], []
348
+ cos_sim_encoder_output_patchwise_test, cos_sim_encoder_output_test = [], []
349
+
350
+ mse = nn.MSELoss()
351
+ l1 = nn.L1Loss()
352
+ crossentropy = nn.CrossEntropyLoss()
353
+
354
+ if use_contrastive_loss:
355
+ contrastive_temps = utils.cosine_anneal(0.004, 0.0075, num_epochs)
356
+
357
+ grad_scaler = torch.cuda.amp.GradScaler(enabled=True)
358
+ progress_bar = tqdm(range(epoch, num_epochs), disable=local_rank!=0, desc="Overall")
359
+ for epoch in progress_bar:
360
+ model.train()
361
+ for train_i, batch in enumerate(train_dl):
362
+ optimizer.zero_grad()
363
+ adjust_learning_rate(optimizer, train_i / num_batches + epoch)
364
+
365
+ input_func = batch[1]
366
+
367
+ input_func = input_func.to(dtype=data_type, device=device, non_blocking=True)
368
+ if len(input_func.shape) == 6:
369
+ b, r, c, t, h, w = input_func.shape
370
+ input_func = input_func.reshape(b * r, c, t, h, w)
371
+
372
+ ids_shuffle, ids_restore = get_ids_shuffle(input_func.shape[0], input_func.device, model)
373
+
374
+ # adjust number to keep relative to image mask
375
+ if model.img_mask is not None:
376
+ len_keep = int(model.patch_embed.t_grid_size * model.n_mask_patches * (1 - mask_ratio))
377
+ else:
378
+ len_keep = int(ids_restore.shape[1] * (1 - mask_ratio))
379
+
380
+ # keep the first subset
381
+ ids_keep = ids_shuffle[:, :len_keep]
382
+ lats = model.forward_encoder_with_mask(input_func, ids_keep)
383
+
384
+ if use_decoder:
385
+ mask = torch.ones_like(ids_restore)
386
+ mask[:, :len_keep] = 0
387
+ # unshuffle to get the binary mask
388
+ mask = torch.gather(mask, dim=1, index=ids_restore)
389
+
390
+ pred = model.forward_decoder(lats, ids_restore)
391
+ recon_loss = model.forward_loss(input_func, pred, mask)
392
+
393
+ recon_losses.append(recon_loss.item())
394
+ loss = recon_loss
395
+ else:
396
+ recon_loss = torch.nan
397
+ recon_losses.append(recon_loss)
398
+ loss = 0
399
+
400
+ if use_contrastive_loss and not use_vic_loss:
401
+ ids_keep2 = ids_shuffle[:, :2*len_keep]
402
+ lats2 = model.forward_encoder_with_mask(input_func, ids_keep2)
403
+
404
+ enc_cls_token = lats[:, :1]
405
+ enc_cls_token2 = lats2[:,:1]
406
+
407
+ temp = contrastive_temps[epoch]
408
+
409
+ all_cls = torch.cat([enc_cls_token, enc_cls_token2], dim=0)
410
+ all_cls_proj = model.simclr_handler(all_cls)
411
+
412
+ contr_loss = SimCLRHandler.simclr_loss(all_cls_proj, temp)
413
+
414
+ contrastive_losses.append(contr_loss.item())
415
+ loss += (contr_loss * contrastive_loss_weight)
416
+ # new loss
417
+ elif use_vic_loss:
418
+ ids_keep2 = ids_shuffle[:, :2*len_keep]
419
+ lats2 = model.forward_encoder_with_mask(input_func, ids_keep2)
420
+
421
+ enc_cls_token = lats[:, :1]
422
+ enc_cls_token2 = lats2[:,:1]
423
+
424
+ l1 = lats
425
+ l2 = lats2[:, :lats.shape[1]]
426
+
427
+ l1_proj = model.vicreg_handler(l1)
428
+ l2_proj = model.vicreg_handler(l2)
429
+
430
+ vic_loss = VICRegHandler.vicreg_loss(l1_proj, l2_proj, gamma=gamma, lamda=lamda, mu=mu, nu=nu,
431
+ rand_frac=rand_frac, use_vic_cls=use_vic_cls)
432
+
433
+ if use_contrastive_loss:
434
+ temp = contrastive_temps[epoch]
435
+ all_cls = torch.cat([enc_cls_token, enc_cls_token2], dim=0)
436
+ all_cls_proj = model.simclr_handler(all_cls)
437
+
438
+ contr_loss = SimCLRHandler.simclr_loss(all_cls_proj, temp)
439
+ contrastive_losses.append(contr_loss.item())
440
+ else:
441
+ contr_loss = 0
442
+ contrastive_losses.append(0)
443
+
444
+ vic_losses.append(vic_loss.item())
445
+ loss += (contr_loss * contrastive_loss_weight + vic_loss * vic_loss_weight)
446
+ else:
447
+ vic_losses.append(0)
448
+ contrastive_losses.append(0)
449
+
450
+ grad_scaler.scale(loss).backward()
451
+ grad_scaler.unscale_(optimizer)
452
+ if grad_clip:
453
+ nn.utils.clip_grad_norm_(model.parameters(), max_norm=grad_clip)
454
+ grad_scaler.step(optimizer)
455
+ grad_scaler.update()
456
+ torch.cuda.synchronize()
457
+
458
+ cos_sim_encoder_output_patchwise.append(utils.patchwise_cosine_similarity(lats)[~torch.eye(lats.shape[1], dtype=bool)[None].expand(lats.shape[0],-1,-1)].mean().item())
459
+ cos_sim_encoder_output.append(utils.batchwise_cosine_similarity(lats.flatten(1)/1e3,lats.flatten(1)/1e3)[~torch.eye(len(lats),dtype=torch.bool)].mean().item())
460
+ if use_decoder:
461
+ cos_sim_decoder_output.append(utils.batchwise_cosine_similarity(pred, pred)[~torch.eye(len(pred),dtype=torch.bool)].mean().item())
462
+
463
+ lrs.append(optimizer.param_groups[0]["lr"])
464
+ train_losses.append(loss.item())
465
+
466
+ if ((train_i%print_interval)==0 or (train_i==num_batches-1)) and train_i>0:
467
+ print(f"Ep. {epoch} | loss {np.mean(train_losses[-print_interval:]):.3f} | lr {optimizer.param_groups[0]['lr']} | {train_i}/{num_batches}")
468
+ if wandb_log:
469
+ logs = {"train/loss": np.mean(train_losses[-print_interval:])}
470
+ # epoch_1000x as x-axis calibrates different curves when batch size changes
471
+ epoch_1000x = int((train_i / num_batches + epoch) * 1000)
472
+ wandb.log(logs, step=epoch_1000x)
473
+
474
+ if utils.is_interactive() or wandb_log:
475
+ print(f"Ep. {epoch} | loss {np.mean(train_losses[-print_interval:]):.3f} | lr {optimizer.param_groups[0]['lr']} | {train_i}/{num_batches}")
476
+ with torch.no_grad():
477
+ if norm_pix_loss:
478
+ normed_input_func, patch_info = model.patchify(input_func, alter_patch_info=False, return_patch_info=True)
479
+ target_mean = normed_input_func.mean(dim=-1, keepdim=True)
480
+ target_var = normed_input_func.var(dim=-1, keepdim=True)
481
+ normed_input_func = (normed_input_func - target_mean) / (target_var + 1.0e-6) ** 0.5
482
+ normed_input_func = model.unpatchify(normed_input_func, patch_info=patch_info)
483
+
484
+ vis_out = vis.plot_mask_pred(
485
+ model, normed_input_func, pred, mask,
486
+ mean=0.5, std=0.2,
487
+ )
488
+ else:
489
+ vis_out = vis.plot_mask_pred(
490
+ model, input_func, pred, mask,
491
+ mean=0.5, std=0.2,
492
+ )
493
+
494
+ if wandb_log:
495
+ logs = {"train/mask_pred": wandb.Image(vis_out)}
496
+ epoch_1000x = int((train_i / num_batches + epoch) * 1000)
497
+ wandb.log(logs, step=epoch_1000x)
498
+ else:
499
+ display(vis_out)
500
+
501
+ logs = {
502
+ "train/loss": np.mean(train_losses[-(train_i + 1) :]),
503
+ "train/recon_losses": np.mean(recon_losses[-(train_i + 1) :]),
504
+ "train/contrastive_losses": np.mean(contrastive_losses[-(train_i + 1) :]),
505
+ "train/vic_losses": np.mean(vic_losses[-(train_i + 1) :]),
506
+ "train/num_steps": len(recon_losses),
507
+ "train/cos_sim_encoder_output": np.mean(cos_sim_encoder_output[-(train_i + 1) :]),
508
+ "train/cos_sim_decoder_output": np.mean(cos_sim_decoder_output[-(train_i + 1) :]) if use_decoder else np.nan,
509
+ "train/cos_sim_encoder_output_patchwise": np.mean(cos_sim_encoder_output_patchwise[-(train_i + 1) :]),
510
+ "lr": np.mean(lrs[-(train_i + 1) :]),
511
+ "epoch": epoch,
512
+ }
513
+
514
+ # Evaluate performance on held-out test dataset
515
+ model.eval()
516
+ with torch.no_grad():
517
+ for test_i, batch in enumerate(test_dl):
518
+ input_func = batch[1]
519
+
520
+ input_func = input_func.to(dtype=data_type, device=device, non_blocking=True)
521
+ if len(input_func.shape) == 6:
522
+ b, r, c, t, h, w = input_func.shape
523
+ input_func = input_func.reshape(b * r, c, t, h, w)
524
+
525
+ if not use_contrastive_loss:
526
+ loss, pred, mask, latent = model(input_func, mask_ratio=mask_ratio, use_contrastive_loss=use_contrastive_loss)
527
+ else:
528
+ loss1, loss2, loss3, pred1, pred2, mask1, mask2, true_mask, latent1, latent2 = model(input_func, mask_ratio=mask_ratio, use_contrastive_loss=use_contrastive_loss)
529
+ pred, mask, latent = pred1, mask1, latent1
530
+
531
+ # contrastive loss
532
+ temp = contrastive_temps[epoch]
533
+ logits = (nn.functional.normalize(latent1[:,:1].flatten(1),dim=-1) @
534
+ nn.functional.normalize(latent2[:,:1].flatten(1),dim=-1).T) / temp
535
+ labels = torch.arange(len(logits)).long().to(device)
536
+ contr_loss = (crossentropy(logits, labels) + crossentropy(logits.T, labels)) / 2
537
+
538
+ loss = loss1 + loss2 + loss3 + contr_loss
539
+
540
+ test_losses.append(loss.item())
541
+
542
+ cos_sim_encoder_output_patchwise_test.append(utils.patchwise_cosine_similarity(latent)[~torch.eye(latent.shape[1], dtype=bool)[None].expand(latent.shape[0],-1,-1)].mean().item())
543
+ cos_sim_encoder_output_test.append(utils.batchwise_cosine_similarity(latent.flatten(1)/1e3, latent.flatten(1)/1e3)[~torch.eye(len(latent),dtype=torch.bool)].mean().item())
544
+
545
+ if test_i%print_interval==0 and test_i>0:
546
+ print(f"Test | loss {np.mean(test_losses[-print_interval:]):.3f} | {test_i}/{test_num_batches}")
547
+
548
+ print(f"Test | iter {test_i} | loss {np.mean(test_losses[-test_i:]):.3f}")
549
+ if wandb_log:
550
+ logs.update({
551
+ "test/loss": np.mean(test_losses[-test_i:]),
552
+ "test/cos_sim_encoder_output": np.mean(cos_sim_encoder_output_test[-(test_i + 1) :]),
553
+ "test/cos_sim_encoder_output_patchwise": np.mean(cos_sim_encoder_output_patchwise_test[-(test_i + 1) :]),
554
+ })
555
+ wandb.log(logs)
556
+
557
+ # Plot progress (first sample in batch)
558
+ if utils.is_interactive() or wandb_log:
559
+ with torch.no_grad():
560
+ if norm_pix_loss:
561
+ normed_input_func, patch_info = model.patchify(input_func, alter_patch_info=False, return_patch_info=True)
562
+ target_mean = normed_input_func.mean(dim=-1, keepdim=True)
563
+ target_var = normed_input_func.var(dim=-1, keepdim=True)
564
+ normed_input_func = (normed_input_func - target_mean) / (target_var + 1.0e-6) ** 0.5
565
+ normed_input_func = model.unpatchify(normed_input_func, patch_info=patch_info)
566
+
567
+ vis_out = vis.plot_mask_pred(
568
+ model, normed_input_func, pred, mask,
569
+ mean=0.5, std=0.2,
570
+ )
571
+ else:
572
+ vis_out = vis.plot_mask_pred(
573
+ model, input_func, pred, mask,
574
+ mean=0.5, std=0.2,
575
+ )
576
+
577
+ if wandb_log:
578
+ logs = {"test/mask_pred": wandb.Image(vis_out)}
579
+ wandb.log(logs)
580
+ else:
581
+ display(vis_out)
582
+
583
+ # Save model checkpoint
584
+ if ckpt_saving and epoch>0 and ((epoch % ckpt_interval == 0) or (epoch==num_epochs-1)):
585
+ save_ckpt(model,f"epoch{epoch}")
586
+
587
+ # wait for other GPUs to catch up if needed
588
+ if distributed: dist.barrier()
589
+
590
+ # close any open plots
591
+ plt.close()
592
+
593
+ if distributed: dist.destroy_process_group()
fMRI-foundation-model-old/flat/.ipynb_checkpoints/main2-checkpoint.ipynb ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [],
3
+ "metadata": {},
4
+ "nbformat": 4,
5
+ "nbformat_minor": 5
6
+ }
fMRI-foundation-model-old/flat/__pycache__/flat_models.cpython-310.pyc ADDED
Binary file (15.2 kB). View file
 
fMRI-foundation-model-old/flat/__pycache__/utils.cpython-310.pyc ADDED
Binary file (9.53 kB). View file
 
fMRI-foundation-model-old/flat/accel.slurm ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #SBATCH --account=fmri
3
+ #SBATCH --partition=p5
4
+ #SBATCH --job-name=found
5
+ #SBATCH --ntasks-per-node=1
6
+ #SBATCH --nodes=1
7
+ #SBATCH --gres=gpu:1
8
+ #SBATCH --gpus-per-task=1 # Set to equal gres=gpu:#!
9
+ #SBATCH --cpus-per-task=176 # 40 / 80 / 176 distributed across node
10
+ #SBATCH --time=23:00:00 # total run time limit (HH:MM:SS)
11
+ #SBATCH -e slurms/%j.err # first create a "slurms" folder in current directory to store logs
12
+ #SBATCH -o slurms/%j.out
13
+ #SBATCH --comment=medarc
14
+ #SBATCH --no-requeue
15
+
16
+ export NUM_GPUS=1 # Set to equal gres=gpu:#!
17
+ echo NUM_GPUS=$NUM_GPUS
18
+
19
+ source ~/.bashrc
20
+
21
+ cd /weka/proj-fmri/paulscotti/fMRI-foundation-model/flat/
22
+ jupyter nbconvert main.ipynb --to python
23
+ if [ $? -ne 0 ]; then
24
+ echo "Error: Conversion of ipynb to Python failed. Exiting."
25
+ exit 1
26
+ fi
27
+
28
+ # Make sure another job doesnt use same port, here using random number
29
+ export MASTER_PORT=$((RANDOM % (19000 - 11000 + 1) + 11000))
30
+ export HOSTNAMES=$(scontrol show hostnames "$SLURM_JOB_NODELIST")
31
+ export MASTER_ADDR=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n 1)
32
+ echo MASTER_ADDR=${MASTER_ADDR}
33
+ echo MASTER_PORT=${MASTER_PORT}
34
+ echo WORLD_SIZE=$((${SLURM_NNODES} * ${NUM_GPUS}))
35
+
36
+ srun torchrun \
37
+ --nproc_per_node=$NUM_GPUS \
38
+ --nnodes=$SLURM_NNODES \
39
+ main.py
40
+
41
+ if [ $? -ne 0 ]; then
42
+ echo "Error: srun command failed. Please check the logs for more details."
43
+ exit 1
44
+ fi
fMRI-foundation-model-old/flat/config.yaml ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model_name: "flat_NORM_contrastive_"
2
+ hcp_flat_path: "/weka/proj-medarc/shared/hcp_flat"
3
+
4
+ use_contrastive_loss: True
5
+ use_decoder: True
6
+ patch_size: 16
7
+
8
+ decoder_embed_dim: 512
9
+ num_frames: 16
10
+ mask_ratio: .75
11
+ pred_t_dim: 8
12
+ t_patch_size: 2
13
+ cls_embed: True
14
+ no_qkv_bias: False
15
+ sep_pos_embed: True
16
+ trunc_init: False
17
+ norm_pix_loss: True
18
+ contrastive_loss_weight: 1.0 # 2.0
19
+
20
+ # VICReg params
21
+ use_vic_loss: True
22
+ vic_loss_weight: 1.0 # 0.05 # 1.0
23
+ gamma: 0.5 # 1.0 # 0.5
24
+ # mu->v, lamda->i, nu->c
25
+ mu: 25
26
+ lamda: 25
27
+ nu: 1
28
+ # fraction of tokens to use for cov loss (all tokens don't fit in mem)
29
+ rand_frac: 0.1 # 0.2
30
+ # whether to apply vic to cls tok (if cont is also on, 2 losses will apply to cls token)
31
+ use_vic_cls: True
32
+ norm_pix_loss: False
33
+ pct_masks_to_decode: 1
34
+
35
+ # Training Configs
36
+ batch_size: 32
37
+ num_workers: 10
38
+ num_epochs: 100
39
+ seed: 42
40
+ base_lr: 3.0e-4 # Keep the x.0 else will be converted to string
41
+ num_samples_per_epoch: 200000
42
+ test_num_samples_per_epoch: 50000
43
+ grad_clip: 1.0 # set 0 for no clip
44
+ grad_accumulation_steps: 1
45
+ test_set: False
46
+
47
+ # Downstream probe config
48
+ probe_num_samples_per_epoch: 100000
49
+ probe_num_epochs: 30
50
+ probe_batch_size: 8
51
+ probe_base_lr: 3.0e-4
52
+
53
+ # Saving progress
54
+ ckpt_saving: True
55
+ ckpt_interval: 5 # in epochs
56
+ print_interval: 20 # in steps
57
+ resume_from_ckpt: False
58
+ wandb_log: False
fMRI-foundation-model-old/flat/flat_models.py ADDED
@@ -0,0 +1,784 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ # --------------------------------------------------------
7
+ # References:
8
+ # timm: https://github.com/rwightman/pytorch-image-models/tree/master/timm
9
+ # DeiT: https://github.com/facebookresearch/deit
10
+ # MAE: https://github.com/facebookresearch/mae
11
+ # MAE-ST: https://github.com/facebookresearch/mae_st
12
+ # --------------------------------------------------------
13
+
14
+ from functools import partial
15
+
16
+ import torch
17
+ import torch.nn as nn
18
+ from einops import rearrange
19
+ from util import video_vit
20
+ from util.logging import master_print as print
21
+ from util.hcp_flat import load_hcp_flat_mask
22
+ import copy
23
+
24
+
25
+ class MaskedAutoencoderViT(nn.Module):
26
+ """Masked Autoencoder with VisionTransformer backbone"""
27
+
28
+ def __init__(
29
+ self,
30
+ img_size=224,
31
+ patch_size=16,
32
+ in_chans=3,
33
+ embed_dim=1024,
34
+ depth=24,
35
+ num_heads=16,
36
+ decoder_embed_dim=512,
37
+ decoder_depth=8,
38
+ decoder_num_heads=16,
39
+ mlp_ratio=4.0,
40
+ norm_layer=nn.LayerNorm,
41
+ norm_pix_loss=False,
42
+ num_frames=16,
43
+ t_patch_size=2,
44
+ patch_embed=video_vit.PatchEmbed,
45
+ no_qkv_bias=False,
46
+ sep_pos_embed=True,
47
+ trunc_init=False,
48
+ cls_embed=True,
49
+ pred_t_dim=8,
50
+ img_mask=None,
51
+ pct_masks_to_decode=1,
52
+ **kwargs,
53
+ ):
54
+ super().__init__()
55
+ self.trunc_init = trunc_init
56
+ self.sep_pos_embed = sep_pos_embed
57
+ self.cls_embed = cls_embed
58
+ self.pred_t_dim = pred_t_dim
59
+ self.t_pred_patch_size = t_patch_size * pred_t_dim // num_frames
60
+ self.embed_dim = embed_dim
61
+
62
+ self.pct_masks_to_decode = pct_masks_to_decode
63
+
64
+ self.patch_embed = patch_embed(
65
+ img_size,
66
+ patch_size,
67
+ in_chans,
68
+ embed_dim,
69
+ num_frames,
70
+ t_patch_size,
71
+ )
72
+ num_patches = self.patch_embed.num_patches
73
+ input_size = self.patch_embed.input_size
74
+ self.input_size = input_size
75
+
76
+ if self.cls_embed:
77
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
78
+ self.decoder_cls_token = nn.Parameter(torch.zeros(1, 1, decoder_embed_dim))
79
+
80
+ if sep_pos_embed:
81
+ self.pos_embed_spatial = nn.Parameter(
82
+ torch.zeros(1, input_size[1] * input_size[2], embed_dim)
83
+ )
84
+ self.pos_embed_temporal = nn.Parameter(
85
+ torch.zeros(1, input_size[0], embed_dim)
86
+ )
87
+ if self.cls_embed:
88
+ self.pos_embed_class = nn.Parameter(torch.zeros(1, 1, embed_dim))
89
+ else:
90
+ if self.cls_embed:
91
+ _num_patches = num_patches + 1
92
+ else:
93
+ _num_patches = num_patches
94
+
95
+ self.pos_embed = nn.Parameter(
96
+ torch.zeros(1, _num_patches, embed_dim),
97
+ )
98
+
99
+ self.blocks = nn.ModuleList(
100
+ [
101
+ video_vit.Block(
102
+ embed_dim,
103
+ num_heads,
104
+ mlp_ratio,
105
+ qkv_bias=not no_qkv_bias,
106
+ qk_scale=None,
107
+ norm_layer=norm_layer,
108
+ )
109
+ for i in range(depth)
110
+ ]
111
+ )
112
+ self.norm = norm_layer(embed_dim)
113
+
114
+ self.decoder_embed = nn.Linear(embed_dim, decoder_embed_dim, bias=True)
115
+
116
+ self.mask_token = nn.Parameter(torch.zeros(1, 1, decoder_embed_dim))
117
+
118
+ if sep_pos_embed:
119
+ self.decoder_pos_embed_spatial = nn.Parameter(
120
+ torch.zeros(1, input_size[1] * input_size[2], decoder_embed_dim)
121
+ )
122
+ self.decoder_pos_embed_temporal = nn.Parameter(
123
+ torch.zeros(1, input_size[0], decoder_embed_dim)
124
+ )
125
+ if self.cls_embed:
126
+ self.decoder_pos_embed_class = nn.Parameter(
127
+ torch.zeros(1, 1, decoder_embed_dim)
128
+ )
129
+ else:
130
+ if self.cls_embed:
131
+ _num_patches = num_patches + 1
132
+ else:
133
+ _num_patches = num_patches
134
+
135
+ self.decoder_pos_embed = nn.Parameter(
136
+ torch.zeros(1, _num_patches, decoder_embed_dim),
137
+ )
138
+
139
+ self.decoder_blocks = nn.ModuleList(
140
+ [
141
+ video_vit.Block(
142
+ decoder_embed_dim,
143
+ decoder_num_heads,
144
+ mlp_ratio,
145
+ qkv_bias=not no_qkv_bias,
146
+ qk_scale=None,
147
+ norm_layer=norm_layer,
148
+ )
149
+ for i in range(decoder_depth)
150
+ ]
151
+ )
152
+
153
+ self.decoder_norm = norm_layer(decoder_embed_dim)
154
+ self.decoder_pred = nn.Linear(
155
+ decoder_embed_dim,
156
+ self.t_pred_patch_size * patch_size**2 * in_chans,
157
+ bias=True,
158
+ )
159
+
160
+ self.norm_pix_loss = norm_pix_loss
161
+
162
+ self.initialize_mask(img_mask)
163
+ self.initialize_weights()
164
+
165
+ print("model initialized")
166
+
167
+ def initialize_weights(self):
168
+ if self.cls_embed:
169
+ torch.nn.init.trunc_normal_(self.cls_token, std=0.02)
170
+ if self.sep_pos_embed:
171
+ torch.nn.init.trunc_normal_(self.pos_embed_spatial, std=0.02)
172
+ torch.nn.init.trunc_normal_(self.pos_embed_temporal, std=0.02)
173
+
174
+ torch.nn.init.trunc_normal_(self.decoder_pos_embed_spatial, std=0.02)
175
+ torch.nn.init.trunc_normal_(self.decoder_pos_embed_temporal, std=0.02)
176
+
177
+ if self.cls_embed:
178
+ torch.nn.init.trunc_normal_(self.pos_embed_class, std=0.02)
179
+ torch.nn.init.trunc_normal_(self.decoder_pos_embed_class, std=0.02)
180
+ else:
181
+ torch.nn.init.trunc_normal_(self.pos_embed, std=0.02)
182
+ torch.nn.init.trunc_normal_(self.decoder_pos_embed, std=0.02)
183
+ w = self.patch_embed.proj.weight.data
184
+ if self.trunc_init:
185
+ torch.nn.init.trunc_normal_(w)
186
+ torch.nn.init.trunc_normal_(self.mask_token, std=0.02)
187
+ else:
188
+ torch.nn.init.xavier_uniform_(w.view([w.shape[0], -1]))
189
+ torch.nn.init.normal_(self.mask_token, std=0.02)
190
+
191
+ # initialize nn.Linear and nn.LayerNorm
192
+ self.apply(self._init_weights)
193
+
194
+ def _init_weights(self, m):
195
+ if isinstance(m, nn.Linear):
196
+ # we use xavier_uniform following official JAX ViT:
197
+ if self.trunc_init:
198
+ nn.init.trunc_normal_(m.weight, std=0.02)
199
+ else:
200
+ torch.nn.init.xavier_uniform_(m.weight)
201
+ if isinstance(m, nn.Linear) and m.bias is not None:
202
+ nn.init.constant_(m.bias, 0)
203
+ elif isinstance(m, nn.LayerNorm):
204
+ nn.init.constant_(m.bias, 0)
205
+ nn.init.constant_(m.weight, 1.0)
206
+
207
+ def initialize_mask(self, img_mask):
208
+ if img_mask is not None:
209
+ img_mask = torch.as_tensor(img_mask > 0).float()
210
+
211
+ H, W = img_mask.shape
212
+ img_mask_patches = self.patchify(
213
+ img_mask
214
+ .view(1, 1, 1, H, W)
215
+ .repeat(1, self.patch_embed.in_chans, self.pred_t_dim, 1, 1)
216
+ )
217
+
218
+ patch_mask = rearrange(
219
+ img_mask,
220
+ "(h ph) (w pw) -> (h w) (ph pw)",
221
+ ph=self.patch_embed.patch_size[0],
222
+ pw=self.patch_embed.patch_size[1],
223
+ ).any(dim=1).float()
224
+ patch_mask_indices, = patch_mask.nonzero(as_tuple=True)
225
+
226
+ self.register_buffer("img_mask", img_mask)
227
+ self.register_buffer("img_mask_patches", img_mask_patches)
228
+ self.register_buffer("patch_mask", patch_mask)
229
+ self.register_buffer("patch_mask_indices", patch_mask_indices)
230
+ self.n_mask_patches = int(len(patch_mask_indices) * self.pct_masks_to_decode)
231
+ else:
232
+ self.register_buffer("img_mask", None)
233
+ self.register_buffer("img_mask_patches", None)
234
+ self.register_buffer("patch_mask", None)
235
+ self.register_buffer("patch_mask_indices", None)
236
+ self.n_mask_patches = None
237
+
238
+ def patchify(self, imgs):
239
+ """
240
+ imgs: (N, C, T, H, W)
241
+ x: (N, L, patch_size**2 *C)
242
+ """
243
+ N, C, T, H, W = imgs.shape
244
+ ph, pw = self.patch_embed.patch_size
245
+ u = self.t_pred_patch_size
246
+ assert H % ph == 0 and W % pw == 0 and T % u == 0
247
+ h = H // ph
248
+ w = W // pw
249
+ t = T // u
250
+
251
+ x = imgs.reshape(shape=(N, C, t, u, h, ph, w, pw))
252
+ x = torch.einsum("nctuhpwq->nthwupqc", x)
253
+ x = x.reshape(shape=(N, t * h * w, u * ph * pw * C))
254
+ self.patch_info = (N, C, T, H, W, ph, pw, u, t, h, w)
255
+ return x
256
+
257
+ def unpatchify(self, x):
258
+ """
259
+ x: (N, L, patch_size**2 *C)
260
+ imgs: (N, C, H, W)
261
+ """
262
+ N, C, T, H, W, ph, pw, u, t, h, w = self.patch_info
263
+
264
+ x = x.reshape(shape=(N, t, h, w, u, ph, pw, C))
265
+
266
+ x = torch.einsum("nthwupqc->nctuhpwq", x)
267
+ imgs = x.reshape(shape=(N, C, T, H, W))
268
+ return imgs
269
+
270
+ def random_masking(self, x, mask_ratio, use_contrastive_loss=False):
271
+ """
272
+ Perform per-sample random masking by per-sample shuffling.
273
+ Per-sample shuffling is done by argsort random noise.
274
+ x: [N, L, D], sequence
275
+ """
276
+ N, L, D = x.shape # batch, length, dim
277
+ T = self.patch_embed.t_grid_size
278
+ H, W = self.patch_embed.grid_size
279
+ assert L == T * H * W
280
+
281
+ # adjust number to keep relative to image mask
282
+ if self.img_mask is not None:
283
+ len_keep = int(T * self.n_mask_patches * (1 - mask_ratio))
284
+ else:
285
+ len_keep = int(L * (1 - mask_ratio))
286
+
287
+ noise = torch.rand(N, L, device=x.device) # noise in [0, 1]
288
+
289
+ # shift missing patches to not be selected
290
+ if self.img_mask is not None:
291
+ noise = noise.view(N, T, H * W)
292
+ noise = noise + (1.0 - self.patch_mask)
293
+ noise = noise.view(N, L)
294
+
295
+ # sort noise for each sample
296
+ ids_shuffle = torch.argsort(
297
+ noise, dim=1
298
+ ) # ascend: small is keep, large is remove
299
+ ids_restore = torch.argsort(ids_shuffle, dim=1)
300
+
301
+ # keep the first subset
302
+ ids_keep = ids_shuffle[:, :len_keep]
303
+ if not use_contrastive_loss:
304
+ x_masked = torch.gather(x, dim=1, index=ids_keep.unsqueeze(-1).repeat(1, 1, D))
305
+ else:
306
+ x_masked1 = torch.gather(x, dim=1, index=ids_keep[:,:len_keep//2].unsqueeze(-1).repeat(1, 1, D))
307
+ x_masked2 = torch.gather(x, dim=1, index=ids_keep[:,len_keep//2:len_keep].unsqueeze(-1).repeat(1, 1, D))
308
+
309
+ if not use_contrastive_loss:
310
+ # generate the binary mask: 0 is keep, 1 is remove
311
+ mask = torch.ones([N, L], device=x.device)
312
+ mask[:, :len_keep] = 0
313
+ # unshuffle to get the binary mask
314
+ mask = torch.gather(mask, dim=1, index=ids_restore)
315
+ else:
316
+ # generate the binary mask: 0 is keep, 1 is remove
317
+ mask1 = torch.ones([N, L], device=x.device)
318
+ mask2 = torch.ones([N, L], device=x.device)
319
+ mask1[:, :len_keep//2] = 0
320
+ mask2[:, len_keep//2:len_keep] = 0
321
+ # unshuffle to get the binary mask
322
+ mask1 = torch.gather(mask1, dim=1, index=ids_restore)
323
+ mask2 = torch.gather(mask2, dim=1, index=ids_restore)
324
+
325
+ if not use_contrastive_loss:
326
+ return x_masked, mask, ids_restore, ids_keep
327
+ else:
328
+ return [x_masked1,x_masked2], [mask1,mask2], ids_restore, ids_keep
329
+
330
+ def forward_encoder(self, x, mask_ratio, use_contrastive_loss=False):
331
+ x = self.patch_embed(x)
332
+
333
+ N, T, L, C = x.shape
334
+
335
+ x = x.reshape(N, T * L, C)
336
+
337
+ # masking: length -> length * mask_ratio
338
+ if not use_contrastive_loss:
339
+ x, mask, ids_restore, ids_keep = self.random_masking(x, mask_ratio)
340
+ x = x.view(N, -1, C)
341
+ else:
342
+ [x1,x2], [mask1,mask2], ids_restore, ids_keep = self.random_masking(x, mask_ratio, use_contrastive_loss=use_contrastive_loss)
343
+ x1 = x1.view(len(x1), -1, C)
344
+ x2 = x2.view(len(x2), -1, C)
345
+ # append cls token
346
+ if self.cls_embed:
347
+ cls_token = self.cls_token
348
+ cls_tokens = cls_token.expand(x.shape[0], -1, -1)
349
+ if not use_contrastive_loss:
350
+ x = torch.cat((cls_tokens, x), dim=1)
351
+ else:
352
+ x1 = torch.cat((cls_tokens, x1), dim=1)
353
+ x2 = torch.cat((cls_tokens, x2), dim=1)
354
+
355
+ # add pos embed w/o cls token
356
+ if self.sep_pos_embed:
357
+ pos_embed = self.pos_embed_spatial.repeat(
358
+ 1, self.input_size[0], 1
359
+ ) + torch.repeat_interleave(
360
+ self.pos_embed_temporal,
361
+ self.input_size[1] * self.input_size[2],
362
+ dim=1,
363
+ )
364
+ pos_embed = pos_embed.expand(x.shape[0], -1, -1)
365
+ pos_embed = torch.gather(
366
+ pos_embed,
367
+ dim=1,
368
+ index=ids_keep.unsqueeze(-1).repeat(1, 1, pos_embed.shape[2]),
369
+ )
370
+ if self.cls_embed:
371
+ pos_embed = torch.cat(
372
+ [
373
+ self.pos_embed_class.expand(pos_embed.shape[0], -1, -1),
374
+ pos_embed,
375
+ ],
376
+ 1,
377
+ )
378
+ else:
379
+ if self.cls_embed:
380
+ cls_ind = 1
381
+ else:
382
+ cls_ind = 0
383
+ pos_embed = self.pos_embed[:, cls_ind:, :].expand(x.shape[0], -1, -1)
384
+ pos_embed = torch.gather(
385
+ pos_embed,
386
+ dim=1,
387
+ index=ids_keep.unsqueeze(-1).repeat(1, 1, pos_embed.shape[2]),
388
+ )
389
+ if self.cls_embed:
390
+ pos_embed = torch.cat(
391
+ [
392
+ self.pos_embed[:, :1, :].expand(x.shape[0], -1, -1),
393
+ pos_embed,
394
+ ],
395
+ 1,
396
+ )
397
+ if not use_contrastive_loss:
398
+ x = x.view([N, -1, C]) + pos_embed
399
+ else:
400
+ x1 = x1.view([len(x1), -1, C]) + pos_embed[:,:x1.shape[1]]
401
+ x2 = x2.view([len(x2), -1, C]) + torch.cat((pos_embed[:,:1], pos_embed[:,x1.shape[1]:]),dim=1)
402
+
403
+ if not use_contrastive_loss:
404
+ # apply Transformer blocks
405
+ for blk in self.blocks:
406
+ x = blk(x)
407
+ x = self.norm(x)
408
+ else:
409
+ # apply Transformer blocks
410
+ for blk in self.blocks:
411
+ x1 = blk(x1)
412
+ x2 = blk(x2)
413
+ x1 = self.norm(x1)
414
+ x2 = self.norm(x2)
415
+
416
+ if not use_contrastive_loss:
417
+ if self.cls_embed:
418
+ # remove cls token
419
+ x = x[:, 1:, :]
420
+
421
+ return x, mask, ids_restore
422
+ else:
423
+ if self.cls_embed:
424
+ # remove cls token
425
+ x1 = x1[:, 1:, :]
426
+ x2 = x2[:, 1:, :]
427
+
428
+ return [x1,x2], [mask1,mask2], ids_restore
429
+
430
+ def forward_encoder_with_mask(self, x, ids_keep):
431
+ # embed patches
432
+ x = self.patch_embed(x)
433
+ N, T, L, C = x.shape
434
+
435
+ x = x.reshape(N, T * L, C)
436
+ # mask out tokens
437
+ x = torch.gather(x, dim=1, index=ids_keep.unsqueeze(-1).repeat(1, 1, C))
438
+
439
+ # append cls token
440
+ if self.cls_embed:
441
+ cls_token = self.cls_token
442
+ cls_tokens = cls_token.expand(x.shape[0], -1, -1)
443
+ x = torch.cat((cls_tokens, x), dim=1)
444
+
445
+ # add pos embed w/o cls token
446
+ if self.sep_pos_embed:
447
+ pos_embed = self.pos_embed_spatial.repeat(
448
+ 1, self.input_size[0], 1
449
+ ) + torch.repeat_interleave(
450
+ self.pos_embed_temporal,
451
+ self.input_size[1] * self.input_size[2],
452
+ dim=1,
453
+ )
454
+ pos_embed = pos_embed.expand(x.shape[0], -1, -1)
455
+ pos_embed = torch.gather(
456
+ pos_embed,
457
+ dim=1,
458
+ index=ids_keep.unsqueeze(-1).repeat(1, 1, pos_embed.shape[2]),
459
+ )
460
+ if self.cls_embed:
461
+ pos_embed = torch.cat(
462
+ [
463
+ self.pos_embed_class.expand(pos_embed.shape[0], -1, -1),
464
+ pos_embed,
465
+ ],
466
+ 1,
467
+ )
468
+ else:
469
+ if self.cls_embed:
470
+ cls_ind = 1
471
+ else:
472
+ cls_ind = 0
473
+ pos_embed = self.pos_embed[:, cls_ind:, :].expand(x.shape[0], -1, -1)
474
+ pos_embed = torch.gather(
475
+ pos_embed,
476
+ dim=1,
477
+ index=ids_keep.unsqueeze(-1).repeat(1, 1, pos_embed.shape[2]),
478
+ )
479
+ if self.cls_embed:
480
+ pos_embed = torch.cat(
481
+ [
482
+ self.pos_embed[:, :1, :].expand(x.shape[0], -1, -1),
483
+ pos_embed,
484
+ ],
485
+ 1,
486
+ )
487
+
488
+ x = x.view([N, -1, C]) + pos_embed
489
+
490
+ for blk in self.blocks:
491
+ x = blk(x)
492
+ x = self.norm(x)
493
+ return x
494
+
495
+ def forward_decoder(self, x, ids_restore, use_contrastive_loss=False):
496
+ N = x.shape[0]
497
+ T = self.patch_embed.t_grid_size
498
+ H, W = self.patch_embed.grid_size
499
+
500
+ # embed tokens
501
+ x = self.decoder_embed(x)
502
+ C = x.shape[-1]
503
+
504
+ # append mask tokens to sequence
505
+ mask_tokens = self.mask_token.repeat(N, T * H * W + 0 - x.shape[1], 1)
506
+ x_ = torch.cat([x[:, :, :], mask_tokens], dim=1) # no cls token
507
+ x_ = x_.view([N, T * H * W, C])
508
+ x_ = torch.gather(
509
+ x_, dim=1, index=ids_restore.unsqueeze(-1).repeat(1, 1, x_.shape[2])
510
+ ) # unshuffle
511
+ x = x_.view([N, T * H * W, C])
512
+ # append cls token
513
+ if self.cls_embed:
514
+ decoder_cls_token = self.decoder_cls_token
515
+ decoder_cls_tokens = decoder_cls_token.expand(x.shape[0], -1, -1)
516
+ x = torch.cat((decoder_cls_tokens, x), dim=1)
517
+
518
+ if self.sep_pos_embed:
519
+ decoder_pos_embed = self.decoder_pos_embed_spatial.repeat(
520
+ 1, self.input_size[0], 1
521
+ ) + torch.repeat_interleave(
522
+ self.decoder_pos_embed_temporal,
523
+ self.input_size[1] * self.input_size[2],
524
+ dim=1,
525
+ )
526
+ if self.cls_embed:
527
+ decoder_pos_embed = torch.cat(
528
+ [
529
+ self.decoder_pos_embed_class.expand(
530
+ decoder_pos_embed.shape[0], -1, -1
531
+ ),
532
+ decoder_pos_embed,
533
+ ],
534
+ 1,
535
+ )
536
+ else:
537
+ decoder_pos_embed = self.decoder_pos_embed[:, :, :]
538
+
539
+ # add pos embed
540
+ x = x + decoder_pos_embed
541
+
542
+ attn = self.decoder_blocks[0].attn
543
+
544
+ # drop patches outside image mask (and then only keep a subset a la VideoMAE2)
545
+ if self.img_mask is not None:
546
+ if self.cls_embed:
547
+ decoder_cls_tokens, x = x[:, :1, :], x[:, 1:, :]
548
+ x = x.view([N, T, H * W, C])
549
+ # x = x[:, :, self.patch_mask_indices]
550
+
551
+ # drop patches randomly to preserve memory (VideoMAE2 approach)
552
+ included_patches = self.patch_mask_indices
553
+ num_to_select = int(self.pct_masks_to_decode * len(included_patches))
554
+ selected_idx = torch.randperm(len(included_patches))[:num_to_select]
555
+ included_patches = included_patches[selected_idx]
556
+ x = x[:, :, included_patches]
557
+
558
+ x = x.view([N, T * self.n_mask_patches, C])
559
+ if self.cls_embed:
560
+ x = torch.cat((decoder_cls_tokens, x), dim=1)
561
+
562
+ # apply Transformer blocks
563
+ for blk in self.decoder_blocks:
564
+ x = blk(x)
565
+ x = self.decoder_norm(x)
566
+
567
+ # predictor projection
568
+ x = self.decoder_pred(x)
569
+
570
+ if self.cls_embed:
571
+ # remove cls token
572
+ x = x[:, 1:, :]
573
+
574
+ # fill outside mask with zeros
575
+ if self.img_mask is not None:
576
+ C = x.shape[-1]
577
+ x = x.view([N, T, self.n_mask_patches, C])
578
+ x_ = torch.zeros([N, T, H * W, C], dtype=x.dtype, device=x.device)
579
+ x = x_.scatter(
580
+ 2, included_patches.view(1, 1, -1, 1).expand(N, T, self.n_mask_patches, C), x,
581
+ )
582
+ x = x.view([N, T * H * W, C])
583
+
584
+ return x
585
+
586
+ def forward_loss(self, imgs, pred, mask):
587
+ """
588
+ imgs: [N, C, T, H, W]
589
+ pred: [N, t*h*w, u*p*p*C]
590
+ mask: [N, t*h*w], 0 is keep, 1 is remove,
591
+ """
592
+ _imgs = torch.index_select(
593
+ imgs,
594
+ 2,
595
+ torch.linspace(
596
+ 0,
597
+ imgs.shape[2] - 1,
598
+ self.pred_t_dim,
599
+ )
600
+ .long()
601
+ .to(imgs.device),
602
+ )
603
+ target = self.patchify(_imgs)
604
+ if self.norm_pix_loss:
605
+ mean = target.mean(dim=-1, keepdim=True)
606
+ var = target.var(dim=-1, keepdim=True)
607
+ target = (target - mean) / (var + 1.0e-6) ** 0.5
608
+
609
+ loss = (pred - target) ** 2
610
+ if self.img_mask is not None:
611
+ # exclude missing pixels from loss
612
+ mask = mask.unsqueeze(-1) * self.img_mask_patches
613
+ else:
614
+ loss = loss.mean(dim=-1) # [N, L], mean loss per patch
615
+
616
+ loss = (loss * mask).sum() / mask.sum() # mean loss on removed patches
617
+ return loss
618
+
619
+ def forward_cyclic_loss(self, pred1, pred2, mask):
620
+ """
621
+ mask1 and mask2 encoder outputs should be the same since they are predicting the same held-out true mask
622
+ """
623
+ loss = (pred1 - pred2) ** 2
624
+ if self.img_mask is not None:
625
+ # exclude missing pixels from loss
626
+ mask = mask.unsqueeze(-1) * self.img_mask_patches
627
+ else:
628
+ loss = loss.mean(dim=-1) # [N, L], mean loss per patch
629
+
630
+ loss = (loss * mask).sum() / mask.sum() # mean loss on removed patches
631
+ return loss
632
+
633
+ def forward(self, imgs, mask_ratio=0.75, use_contrastive_loss=False):
634
+ latent, mask, ids_restore = self.forward_encoder(imgs, mask_ratio, use_contrastive_loss=use_contrastive_loss)
635
+ if not use_contrastive_loss:
636
+ pred = self.forward_decoder(latent, ids_restore, use_contrastive_loss=use_contrastive_loss) # [N, L, p*p*C]
637
+ loss = self.forward_loss(imgs, pred, mask)
638
+ return loss, pred, mask, latent
639
+ else:
640
+ latent1, latent2 = latent
641
+ mask1, mask2 = mask
642
+ true_mask = copy.deepcopy(mask1)
643
+ true_mask[mask2==0]=0 # dont try to predict the masks that were fed to the other encoder
644
+ pred1 = self.forward_decoder(latent1, ids_restore, use_contrastive_loss=use_contrastive_loss) # [N, L, p*p*C]
645
+ pred2 = self.forward_decoder(latent2, ids_restore, use_contrastive_loss=use_contrastive_loss) # [N, L, p*p*C]
646
+ loss1 = self.forward_loss(imgs, pred1, true_mask)
647
+ loss2 = self.forward_loss(imgs, pred2, true_mask)
648
+ loss3 = self.forward_cyclic_loss(pred1, pred2, true_mask)
649
+ return loss1, loss2, loss3, pred1, pred2, mask1, mask2, true_mask, latent1, latent2
650
+
651
+ def forward_features(self, x, global_pool=True):
652
+ # embed patches
653
+ x = self.patch_embed(x)
654
+ N, T, L, C = x.shape # T: temporal; L: spatial
655
+
656
+ x = x.view([N, T * L, C])
657
+
658
+ # append cls token
659
+ if self.cls_embed:
660
+ cls_token = self.cls_token
661
+ cls_tokens = cls_token.expand(x.shape[0], -1, -1)
662
+ x = torch.cat((cls_tokens, x), dim=1)
663
+
664
+ if self.sep_pos_embed:
665
+ pos_embed = self.pos_embed_spatial.repeat(
666
+ 1, self.input_size[0], 1
667
+ ) + torch.repeat_interleave(
668
+ self.pos_embed_temporal,
669
+ self.input_size[1] * self.input_size[2],
670
+ dim=1,
671
+ )
672
+ if self.cls_embed:
673
+ pos_embed = torch.cat(
674
+ [
675
+ self.pos_embed_class.expand(pos_embed.shape[0], -1, -1),
676
+ pos_embed,
677
+ ],
678
+ 1,
679
+ )
680
+ else:
681
+ pos_embed = self.pos_embed[:, :, :]
682
+ x = x + pos_embed
683
+
684
+ # drop patches outside image mask
685
+ if self.img_mask is not None:
686
+ if self.cls_embed:
687
+ cls_tokens, x = x[:, :1, :], x[:, 1:, :]
688
+ x = x.view([N, T, L, C])
689
+ x = x[:, :, self.patch_mask_indices]
690
+ x = x.view([N, T * self.n_mask_patches, C])
691
+ if self.cls_embed:
692
+ x = torch.cat((cls_tokens, x), dim=1)
693
+
694
+ # apply Transformer blocks
695
+ for blk in self.blocks:
696
+ x = blk(x)
697
+
698
+ if global_pool:
699
+ if self.cls_embed:
700
+ x = x[:, 1:, :]
701
+ x = x.mean(dim=1)
702
+ return x
703
+
704
+ def forward_head(self, x):
705
+ # classifier
706
+ x = self.norm(x)
707
+ # x = self.fc_norm(x)
708
+ x = self.dropout(x)
709
+ x = self.head(x)
710
+
711
+ return x
712
+
713
+ def mask_fill(self, x):
714
+ N, L, C = x.shape
715
+ T = self.patch_embed.t_grid_size
716
+ H, W = self.patch_embed.grid_size
717
+ assert L == T * self.n_mask_patches
718
+
719
+ x = x.view(N, T, -1, C)
720
+ x_ = torch.zeros([N, T, H * W, C], dtype=x.dtype, device=x.device)
721
+ x = x_.scatter(
722
+ 2, self.patch_mask_indices.view(1, 1, -1, 1).expand(N, T, -1, C), x,
723
+ )
724
+ return x
725
+
726
+
727
+ def mae_vit_small_fmri(num_heads=6,**kwargs):
728
+ model = MaskedAutoencoderViT(
729
+ img_size=(144, 320),
730
+ in_chans=1,
731
+ embed_dim=384,
732
+ depth=12,
733
+ num_heads=num_heads,
734
+ mlp_ratio=4,
735
+ norm_layer=partial(nn.LayerNorm, eps=1e-6),
736
+ img_mask=load_hcp_flat_mask(),
737
+ **kwargs,
738
+ )
739
+ return model
740
+
741
+
742
+ def mae_vit_base_fmri(**kwargs):
743
+ model = MaskedAutoencoderViT(
744
+ img_size=(144, 320),
745
+ in_chans=1,
746
+ embed_dim=768,
747
+ depth=12,
748
+ num_heads=12,
749
+ mlp_ratio=4,
750
+ norm_layer=partial(nn.LayerNorm, eps=1e-6),
751
+ img_mask=load_hcp_flat_mask(),
752
+ **kwargs,
753
+ )
754
+ return model
755
+
756
+
757
+ def mae_vit_large_fmri(**kwargs):
758
+ model = MaskedAutoencoderViT(
759
+ img_size=(144, 320),
760
+ in_chans=1,
761
+ embed_dim=1024,
762
+ depth=24,
763
+ num_heads=16,
764
+ mlp_ratio=4,
765
+ norm_layer=partial(nn.LayerNorm, eps=1e-6),
766
+ img_mask=load_hcp_flat_mask(),
767
+ **kwargs,
768
+ )
769
+ return model
770
+
771
+
772
+ def mae_vit_huge_fmri(**kwargs):
773
+ model = MaskedAutoencoderViT(
774
+ img_size=(144, 320),
775
+ in_chans=1,
776
+ embed_dim=1280,
777
+ depth=32,
778
+ num_heads=16,
779
+ mlp_ratio=4,
780
+ norm_layer=partial(nn.LayerNorm, eps=1e-6),
781
+ img_mask=load_hcp_flat_mask(),
782
+ **kwargs,
783
+ )
784
+ return model
fMRI-foundation-model-old/flat/main.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
fMRI-foundation-model-old/flat/main.py ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ import sys
4
+ import json
5
+ import yaml
6
+ import numpy as np
7
+ import copy
8
+ import math
9
+ import time
10
+ import random
11
+ from tqdm import tqdm
12
+ import webdataset as wds
13
+ import matplotlib.pyplot as plt
14
+
15
+ import torch
16
+ import torch.nn as nn
17
+ from torchvision import transforms
18
+ import utils
19
+ from flat_models import *
20
+
21
+ # tf32 data type is faster than standard float32
22
+ torch.backends.cuda.matmul.allow_tf32 = True
23
+ # following fixes a Conv3D CUDNN_NOT_SUPPORTED error
24
+ torch.backends.cudnn.benchmark = True
25
+
26
+ ### Multi-GPU config ###
27
+ device_count = torch.cuda.device_count()
28
+ print(f"Number of available CUDA devices: {device_count}")
29
+
30
+ local_rank = os.getenv('LOCAL_RANK')
31
+ if local_rank is None:
32
+ local_rank = 0
33
+ else:
34
+ local_rank = int(local_rank)
35
+ print(f"LOCAL RANK={local_rank}")
36
+
37
+ num_devices = os.getenv('NUM_GPUS')
38
+ if num_devices is None:
39
+ num_devices = 1
40
+ else:
41
+ num_devices = int(num_devices)
42
+ print(f"NUM GPUS={num_devices}")
43
+ distributed = True if num_devices>1 else False
44
+ if distributed: assert device_count==num_devices
45
+
46
+ node = os.getenv('SLURM_NODEID')
47
+ if node is None:
48
+ node = 0
49
+ else:
50
+ node = int(node)
51
+ print(f"NODE={node}")
52
+
53
+ global_rank = os.getenv('RANK')
54
+ if global_rank is None:
55
+ global_rank = 0
56
+ else:
57
+ global_rank = int(global_rank)
58
+ print(f"GLOBAL RANK={global_rank}")
59
+
60
+ world_size = os.getenv('WORLD_SIZE')
61
+ if world_size is None:
62
+ world_size = 1
63
+ else:
64
+ world_size = int(world_size)
65
+ print(f"WORLD_SIZE={world_size}")
66
+
67
+ # Load parameters from yaml config
68
+ config = yaml.load(open('config.yaml', 'r'), Loader=yaml.FullLoader)
69
+
70
+ print("\n__CONFIG__")
71
+ for attribute_name in config.keys():
72
+ print(f"{attribute_name} = {config[attribute_name]}")
73
+ globals()[attribute_name] = config[f'{attribute_name}']
74
+ print("\n")
75
+
76
+ # Create outdir for ckpt and config.yaml
77
+ outdir = os.path.abspath(f'checkpoints/{model_name}')
78
+ print("outdir", outdir)
79
+
80
+ # Load previous config.yaml if available
81
+ if os.path.exists(f"{outdir}/config.yaml"):
82
+ config = yaml.load(open(f"{outdir}/config.yaml", 'r'), Loader=yaml.FullLoader)
83
+ print(f"Loaded config.yaml from ckpt folder {outdir}")
84
+
85
+ # create global variables from the config
86
+ print("\n__REPLACING_CONFIG__")
87
+ for attribute_name in config.keys():
88
+ print(f"{attribute_name} = {config[attribute_name]}")
89
+ globals()[attribute_name] = config[f'{attribute_name}']
90
+ print("\n")
91
+
92
+ data_type = torch.float32 # change depending on your mixed_precision
93
+ global_batch_size = batch_size * world_size
94
+
95
+ # FSDP Setup
96
+ if distributed:
97
+ import torch.distributed as dist
98
+ from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
99
+ from torch.distributed.fsdp.api import BackwardPrefetch, CPUOffload, ShardingStrategy
100
+ import functools
101
+ from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy, transformer_auto_wrap_policy
102
+ print(f"setting device to cuda:{local_rank}")
103
+ torch.cuda.set_device(local_rank)
104
+ device = torch.device('cuda',local_rank)
105
+ dist.init_process_group("nccl", rank=global_rank, world_size=world_size)
106
+ print(f"\nSuccessfully set cuda:{local_rank} | global_rank{global_rank} | node{node}")
107
+ dist.barrier()
108
+ print(f"global_rank{global_rank} passed barrier")
109
+ else:
110
+ device = torch.device('cuda')
111
+
112
+ print("PID of this process =",os.getpid())
113
+ print("device =", device, "distributed =",distributed, "num_devices =", num_devices, "local rank =", local_rank, "world size =", world_size, "data_type =", data_type)
114
+
115
+ # seed all random functions
116
+ utils.seed_everything(seed + global_rank)
117
+
118
+ from util.hcp_flat import load_hcp_flat_mask
119
+ from util.hcp_flat import create_hcp_flat
120
+ from util.losses import *
121
+ import util.visualize as vis
122
+
123
+ if utils.is_interactive(): # Use less samples per epoch for debugging
124
+ num_samples_per_epoch = 2000
125
+ test_num_samples_per_epoch = 2000
126
+
127
+ model = mae_vit_small_fmri(
128
+ decoder_embed_dim=decoder_embed_dim,
129
+ t_patch_size=t_patch_size,
130
+ pred_t_dim=pred_t_dim,
131
+ decoder_depth=4,
132
+ cls_embed=cls_embed,
133
+ norm_pix_loss=norm_pix_loss,
134
+ no_qkv_bias=no_qkv_bias,
135
+ sep_pos_embed=sep_pos_embed,
136
+ trunc_init=trunc_init,
137
+ )
138
+
139
+ if use_contrastive_loss:
140
+ model.simclr_handler = SimCLRHandler(model.embed_dim).to(device)
141
+ if use_vic_loss:
142
+ model.vicreg_handler = VICRegHandler(model.embed_dim).to(device)
143
+
144
+ # state = torch.load("checkpoints/checkpoint-00099.pth", map_location="cpu")
145
+ # model.load_state_dict(state["model"])
146
+
147
+ num_batches = num_samples_per_epoch // (num_devices * batch_size)
148
+ test_num_batches = test_num_samples_per_epoch // (num_devices * batch_size)
149
+ print("num_batches", num_batches)
150
+ print("test_num_batches", test_num_batches)
151
+
152
+ ## Train ##
153
+ train_dataset = create_hcp_flat(root=hcp_flat_path,
154
+ training=True, frames=num_frames)
155
+ train_dl = wds.WebLoader(
156
+ train_dataset.batched(batch_size, partial=False),
157
+ batch_size=None,
158
+ shuffle=False,
159
+ num_workers=num_workers,
160
+ pin_memory=True,
161
+ )
162
+ train_dl = train_dl.with_epoch(num_batches)
163
+
164
+ ## Test ##
165
+ test_dataset = create_hcp_flat(root=hcp_flat_path,
166
+ training=False, frames=num_frames)
167
+ test_dl = wds.WebLoader(
168
+ test_dataset.batched(batch_size, partial=False),
169
+ batch_size=None,
170
+ shuffle=False,
171
+ num_workers=num_workers,
172
+ pin_memory=True,
173
+ )
174
+ test_dl = test_dl.with_epoch(test_num_batches)
175
+
176
+ print(f"\nChecking distributed setup on global_rank {global_rank}...")
177
+ from util.video_vit import Attention
178
+ if distributed:
179
+ my_auto_wrap_policy = functools.partial(
180
+ transformer_auto_wrap_policy,
181
+ transformer_layer_cls={
182
+ Attention, # <--- Your Transformer layer class
183
+ },
184
+ )
185
+ print(f"\nPrepping FSDP on {global_rank} {node}...\n")
186
+ model = model.to(device)
187
+ model = FSDP(
188
+ model,
189
+ sharding_strategy=ShardingStrategy.HYBRID_SHARD,
190
+ auto_wrap_policy=my_auto_wrap_policy,
191
+ use_orig_params=False,
192
+ cpu_offload=None, #CPUOffload(offload_params=True)
193
+ sync_module_states=True,
194
+ limit_all_gathers=True, # See https://github.com/pytorch/pytorch/issues/91165
195
+ device_id=device,
196
+ )
197
+ print(f"\nSuccessfully loaded FSDP model to device on global_rank {global_rank}\n")
198
+ dist.barrier()
199
+ else:
200
+ print(f"\nNot training distributed! global_rank {global_rank}")
201
+ model.to(device)
202
+
203
+ no_decay = ["bias", "LayerNorm.bias", "LayerNorm.weight"]
204
+ opt_grouped_parameters = [
205
+ {'params': [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 0.05},
206
+ {'params': [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0},
207
+ ]
208
+
209
+ lr = base_lr * global_batch_size / 256
210
+ print(f"multiply base lr {base_lr} by effective batch size {global_batch_size}")
211
+ print(f"lr = {lr}")
212
+
213
+ optimizer = torch.optim.AdamW(opt_grouped_parameters, lr=lr, betas=(0.9, 0.95))
214
+
215
+ def adjust_learning_rate(optimizer, epoch, warmup_epochs=5, min_lr=0.0):
216
+ """Decay the learning rate with half-cycle cosine after warmup"""
217
+ if epoch < warmup_epochs:
218
+ lr_ = lr * epoch / warmup_epochs
219
+ else:
220
+ lr_ = min_lr + (lr - min_lr) * 0.5 * (
221
+ 1.0
222
+ + math.cos(
223
+ math.pi
224
+ * (epoch - warmup_epochs)
225
+ / (num_epochs - warmup_epochs)
226
+ )
227
+ )
228
+ for param_group in optimizer.param_groups:
229
+ param_group["lr"] = lr_
230
+ return lr_
231
+
232
+ print("\nDone with model preparations!")
233
+ num_params = utils.count_params(model)
234
+
235
+ def save_ckpt(model,tag="last"):
236
+ if distributed: dist.barrier()
237
+ model_states = model.state_dict()
238
+ if global_rank == 0:
239
+ os.makedirs(outdir,exist_ok=True)
240
+ ckpt_path = outdir+f'/{tag}.pth'
241
+
242
+ if tag == "last" and os.path.exists(ckpt_path):
243
+ shutil.copyfile(os.path.join(outdir, f'{tag}.pth'), os.path.join(outdir, f'{tag}_old.pth'))
244
+ # print(f'saving {ckpt_path}',flush=True)
245
+ if tag=='last':
246
+ torch.save({
247
+ 'epoch': epoch,
248
+ 'model_state_dict': model_states,
249
+ 'optimizer_state_dict': optimizer.state_dict(),
250
+ }, ckpt_path)
251
+ else:
252
+ torch.save({
253
+ 'epoch': epoch,
254
+ 'model_state_dict': model_states,
255
+ }, ckpt_path)
256
+
257
+ if tag == "last" and os.path.exists(os.path.join(outdir, f'{tag}_old.pth')):
258
+ os.remove(os.path.join(outdir, f'{tag}_old.pth'))
259
+ print(f"\n---saved {ckpt_path}!---\n")
260
+
261
+ # save the config.yaml
262
+ if not os.path.exists(f"{outdir}/config.yaml"):
263
+ with open(f"{outdir}/config.yaml", 'w') as file:
264
+ yaml.dump(config, file)
265
+ print(f"saved {outdir}/config.yaml!")
266
+
267
+ # auto resume
268
+ if os.path.exists(os.path.join(outdir, 'last.pth')) or os.path.exists(os.path.join(outdir, 'last_old.pth')):
269
+ if os.path.exists(os.path.join(outdir, 'last_old.pth')):
270
+ if os.path.exists(os.path.join(outdir, 'last.pth')):
271
+ # this is corrupted
272
+ os.remove(os.path.join(outdir, f'last.pth'))
273
+ # set last_old as last
274
+ shutil.move(os.path.join(outdir, f'last_old.pth'), os.path.join(outdir, f'last.pth'))
275
+
276
+ ckpt_path = os.path.join(outdir, 'last.pth')
277
+ resume_from_ckpt = True
278
+
279
+ epoch = 0
280
+ if resume_from_ckpt:
281
+ print("\n---resuming from ckpt_path---\n", ckpt_path)
282
+ checkpoint = torch.load(ckpt_path, map_location=device)
283
+ epoch = checkpoint['epoch']+1
284
+ optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
285
+ model.load_state_dict(checkpoint['model_state_dict'])
286
+ # total_steps_done = epoch*num_iterations_per_epoch
287
+ # for _ in range(total_steps_done):
288
+ # lr_scheduler.step()
289
+ del checkpoint
290
+ torch.cuda.empty_cache()
291
+
292
+ if utils.is_interactive():
293
+ wandb_log = False
294
+ ckpt_saving = False
295
+ if local_rank==0 and wandb_log: # only use main process for wandb logging
296
+ import wandb
297
+ wandb_project = 'fMRI-foundation-model'
298
+ print(f"wandb {wandb_project} run {model_name}")
299
+ # need to configure wandb beforehand in terminal with "wandb init"!
300
+ wandb_config = {
301
+ "model_name": model_name,
302
+ "global_batch_size": global_batch_size,
303
+ "batch_size": batch_size,
304
+ "num_epochs": num_epochs,
305
+ "cls_embed": cls_embed,
306
+ "norm_pix_loss": norm_pix_loss,
307
+ "t_patch_size": t_patch_size,
308
+ "pred_t_dim": pred_t_dim,
309
+ "mask_ratio": mask_ratio,
310
+ "num_frames": num_frames,
311
+ "sep_pos_embed": sep_pos_embed,
312
+ "decoder_embed_dim": decoder_embed_dim,
313
+ "use_contrastive_loss": use_contrastive_loss,
314
+ "num_params": num_params,
315
+ "base_lr": base_lr,
316
+ "lr": lr,
317
+ "num_samples_per_epoch": num_samples_per_epoch,
318
+ "test_num_samples_per_epoch": test_num_samples_per_epoch,
319
+ "num_epochs": num_epochs,
320
+ "grad_clip": grad_clip,
321
+ "ckpt_interval": ckpt_interval,
322
+ "ckpt_saving": ckpt_saving,
323
+ "print_interval": print_interval,
324
+ "seed": seed,
325
+ "distributed": distributed,
326
+ "num_devices": num_devices,
327
+ "world_size": world_size,
328
+ }
329
+ print("wandb_config:\n",wandb_config)
330
+ print("wandb_id:",model_name)
331
+ wandb.init(
332
+ id=model_name,
333
+ project=wandb_project,
334
+ name=model_name,
335
+ config=wandb_config,
336
+ resume="allow",
337
+ )
338
+ else:
339
+ wandb_log = False
340
+
341
+ # lrs, train_losses, test_losses = [], [], []
342
+ # train_losses1, train_losses2, train_losses3, train_losses4 = [], [], [], []
343
+
344
+ epoch = 0
345
+ lrs, train_losses, recon_losses, contrastive_losses, vic_losses = [], [], [], [], []
346
+ cos_sim_encoder_output, cos_sim_decoder_output, cos_sim_encoder_output_patchwise = [], [], []
347
+ probe_losses, probe_accs, test_losses, test_accs = [], [], [], []
348
+ cos_sim_encoder_output_patchwise_test, cos_sim_encoder_output_test = [], []
349
+
350
+ mse = nn.MSELoss()
351
+ l1 = nn.L1Loss()
352
+ crossentropy = nn.CrossEntropyLoss()
353
+
354
+ if use_contrastive_loss:
355
+ contrastive_temps = utils.cosine_anneal(0.004, 0.0075, num_epochs)
356
+
357
+ grad_scaler = torch.cuda.amp.GradScaler(enabled=True)
358
+ progress_bar = tqdm(range(epoch, num_epochs), disable=local_rank!=0, desc="Overall")
359
+ for epoch in progress_bar:
360
+ model.train()
361
+ for train_i, batch in enumerate(train_dl):
362
+ optimizer.zero_grad()
363
+ adjust_learning_rate(optimizer, train_i / num_batches + epoch)
364
+
365
+ input_func = batch[1]
366
+
367
+ input_func = input_func.to(dtype=data_type, device=device, non_blocking=True)
368
+ if len(input_func.shape) == 6:
369
+ b, r, c, t, h, w = input_func.shape
370
+ input_func = input_func.reshape(b * r, c, t, h, w)
371
+
372
+ ids_shuffle, ids_restore = get_ids_shuffle(input_func.shape[0], input_func.device, model)
373
+
374
+ # adjust number to keep relative to image mask
375
+ if model.img_mask is not None:
376
+ len_keep = int(model.patch_embed.t_grid_size * model.n_mask_patches * (1 - mask_ratio))
377
+ else:
378
+ len_keep = int(ids_restore.shape[1] * (1 - mask_ratio))
379
+
380
+ # keep the first subset
381
+ ids_keep = ids_shuffle[:, :len_keep]
382
+ lats = model.forward_encoder_with_mask(input_func, ids_keep)
383
+
384
+ if use_decoder:
385
+ mask = torch.ones_like(ids_restore)
386
+ mask[:, :len_keep] = 0
387
+ # unshuffle to get the binary mask
388
+ mask = torch.gather(mask, dim=1, index=ids_restore)
389
+
390
+ pred = model.forward_decoder(lats, ids_restore)
391
+ recon_loss = model.forward_loss(input_func, pred, mask)
392
+
393
+ recon_losses.append(recon_loss.item())
394
+ loss = recon_loss
395
+ else:
396
+ recon_loss = torch.nan
397
+ recon_losses.append(recon_loss)
398
+ loss = 0
399
+
400
+ if use_contrastive_loss and not use_vic_loss:
401
+ ids_keep2 = ids_shuffle[:, :2*len_keep]
402
+ lats2 = model.forward_encoder_with_mask(input_func, ids_keep2)
403
+
404
+ enc_cls_token = lats[:, :1]
405
+ enc_cls_token2 = lats2[:,:1]
406
+
407
+ temp = contrastive_temps[epoch]
408
+
409
+ all_cls = torch.cat([enc_cls_token, enc_cls_token2], dim=0)
410
+ all_cls_proj = model.simclr_handler(all_cls)
411
+
412
+ contr_loss = SimCLRHandler.simclr_loss(all_cls_proj, temp)
413
+
414
+ contrastive_losses.append(contr_loss.item())
415
+ loss += (contr_loss * contrastive_loss_weight)
416
+ # new loss
417
+ elif use_vic_loss:
418
+ ids_keep2 = ids_shuffle[:, :2*len_keep]
419
+ lats2 = model.forward_encoder_with_mask(input_func, ids_keep2)
420
+
421
+ enc_cls_token = lats[:, :1]
422
+ enc_cls_token2 = lats2[:,:1]
423
+
424
+ l1 = lats
425
+ l2 = lats2[:, :lats.shape[1]]
426
+
427
+ l1_proj = model.vicreg_handler(l1)
428
+ l2_proj = model.vicreg_handler(l2)
429
+
430
+ vic_loss = VICRegHandler.vicreg_loss(l1_proj, l2_proj, gamma=gamma, lamda=lamda, mu=mu, nu=nu,
431
+ rand_frac=rand_frac, use_vic_cls=use_vic_cls)
432
+
433
+ if use_contrastive_loss:
434
+ temp = contrastive_temps[epoch]
435
+ all_cls = torch.cat([enc_cls_token, enc_cls_token2], dim=0)
436
+ all_cls_proj = model.simclr_handler(all_cls)
437
+
438
+ contr_loss = SimCLRHandler.simclr_loss(all_cls_proj, temp)
439
+ contrastive_losses.append(contr_loss.item())
440
+ else:
441
+ contr_loss = 0
442
+ contrastive_losses.append(0)
443
+
444
+ vic_losses.append(vic_loss.item())
445
+ loss += (contr_loss * contrastive_loss_weight + vic_loss * vic_loss_weight)
446
+ else:
447
+ vic_losses.append(0)
448
+ contrastive_losses.append(0)
449
+
450
+ grad_scaler.scale(loss).backward()
451
+ grad_scaler.unscale_(optimizer)
452
+ if grad_clip:
453
+ nn.utils.clip_grad_norm_(model.parameters(), max_norm=grad_clip)
454
+ grad_scaler.step(optimizer)
455
+ grad_scaler.update()
456
+ torch.cuda.synchronize()
457
+
458
+ cos_sim_encoder_output_patchwise.append(utils.patchwise_cosine_similarity(lats)[~torch.eye(lats.shape[1], dtype=bool)[None].expand(lats.shape[0],-1,-1)].mean().item())
459
+ cos_sim_encoder_output.append(utils.batchwise_cosine_similarity(lats.flatten(1)/1e3,lats.flatten(1)/1e3)[~torch.eye(len(lats),dtype=torch.bool)].mean().item())
460
+ if use_decoder:
461
+ cos_sim_decoder_output.append(utils.batchwise_cosine_similarity(pred, pred)[~torch.eye(len(pred),dtype=torch.bool)].mean().item())
462
+
463
+ lrs.append(optimizer.param_groups[0]["lr"])
464
+ train_losses.append(loss.item())
465
+
466
+ if ((train_i%print_interval)==0 or (train_i==num_batches-1)) and train_i>0:
467
+ print(f"Ep. {epoch} | loss {np.mean(train_losses[-print_interval:]):.3f} | lr {optimizer.param_groups[0]['lr']} | {train_i}/{num_batches}")
468
+ if wandb_log:
469
+ logs = {"train/loss": np.mean(train_losses[-print_interval:])}
470
+ # epoch_1000x as x-axis calibrates different curves when batch size changes
471
+ epoch_1000x = int((train_i / num_batches + epoch) * 1000)
472
+ wandb.log(logs, step=epoch_1000x)
473
+
474
+ if utils.is_interactive() or wandb_log:
475
+ print(f"Ep. {epoch} | loss {np.mean(train_losses[-print_interval:]):.3f} | lr {optimizer.param_groups[0]['lr']} | {train_i}/{num_batches}")
476
+ with torch.no_grad():
477
+ if norm_pix_loss:
478
+ normed_input_func, patch_info = model.patchify(input_func, alter_patch_info=False, return_patch_info=True)
479
+ target_mean = normed_input_func.mean(dim=-1, keepdim=True)
480
+ target_var = normed_input_func.var(dim=-1, keepdim=True)
481
+ normed_input_func = (normed_input_func - target_mean) / (target_var + 1.0e-6) ** 0.5
482
+ normed_input_func = model.unpatchify(normed_input_func, patch_info=patch_info)
483
+
484
+ vis_out = vis.plot_mask_pred(
485
+ model, normed_input_func, pred, mask,
486
+ mean=0.5, std=0.2,
487
+ )
488
+ else:
489
+ vis_out = vis.plot_mask_pred(
490
+ model, input_func, pred, mask,
491
+ mean=0.5, std=0.2,
492
+ )
493
+
494
+ if wandb_log:
495
+ logs = {"train/mask_pred": wandb.Image(vis_out)}
496
+ epoch_1000x = int((train_i / num_batches + epoch) * 1000)
497
+ wandb.log(logs, step=epoch_1000x)
498
+ else:
499
+ display(vis_out)
500
+
501
+ logs = {
502
+ "train/loss": np.mean(train_losses[-(train_i + 1) :]),
503
+ "train/recon_losses": np.mean(recon_losses[-(train_i + 1) :]),
504
+ "train/contrastive_losses": np.mean(contrastive_losses[-(train_i + 1) :]),
505
+ "train/vic_losses": np.mean(vic_losses[-(train_i + 1) :]),
506
+ "train/num_steps": len(recon_losses),
507
+ "train/cos_sim_encoder_output": np.mean(cos_sim_encoder_output[-(train_i + 1) :]),
508
+ "train/cos_sim_decoder_output": np.mean(cos_sim_decoder_output[-(train_i + 1) :]) if use_decoder else np.nan,
509
+ "train/cos_sim_encoder_output_patchwise": np.mean(cos_sim_encoder_output_patchwise[-(train_i + 1) :]),
510
+ "lr": np.mean(lrs[-(train_i + 1) :]),
511
+ "epoch": epoch,
512
+ }
513
+
514
+ # Evaluate performance on held-out test dataset
515
+ model.eval()
516
+ with torch.no_grad():
517
+ for test_i, batch in enumerate(test_dl):
518
+ input_func = batch[1]
519
+
520
+ input_func = input_func.to(dtype=data_type, device=device, non_blocking=True)
521
+ if len(input_func.shape) == 6:
522
+ b, r, c, t, h, w = input_func.shape
523
+ input_func = input_func.reshape(b * r, c, t, h, w)
524
+
525
+ if not use_contrastive_loss:
526
+ loss, pred, mask, latent = model(input_func, mask_ratio=mask_ratio, use_contrastive_loss=use_contrastive_loss)
527
+ else:
528
+ loss1, loss2, loss3, pred1, pred2, mask1, mask2, true_mask, latent1, latent2 = model(input_func, mask_ratio=mask_ratio, use_contrastive_loss=use_contrastive_loss)
529
+ pred, mask, latent = pred1, mask1, latent1
530
+
531
+ # contrastive loss
532
+ temp = contrastive_temps[epoch]
533
+ logits = (nn.functional.normalize(latent1[:,:1].flatten(1),dim=-1) @
534
+ nn.functional.normalize(latent2[:,:1].flatten(1),dim=-1).T) / temp
535
+ labels = torch.arange(len(logits)).long().to(device)
536
+ contr_loss = (crossentropy(logits, labels) + crossentropy(logits.T, labels)) / 2
537
+
538
+ loss = loss1 + loss2 + loss3 + contr_loss
539
+
540
+ test_losses.append(loss.item())
541
+
542
+ cos_sim_encoder_output_patchwise_test.append(utils.patchwise_cosine_similarity(latent)[~torch.eye(latent.shape[1], dtype=bool)[None].expand(latent.shape[0],-1,-1)].mean().item())
543
+ cos_sim_encoder_output_test.append(utils.batchwise_cosine_similarity(latent.flatten(1)/1e3, latent.flatten(1)/1e3)[~torch.eye(len(latent),dtype=torch.bool)].mean().item())
544
+
545
+ if test_i%print_interval==0 and test_i>0:
546
+ print(f"Test | loss {np.mean(test_losses[-print_interval:]):.3f} | {test_i}/{test_num_batches}")
547
+
548
+ print(f"Test | iter {test_i} | loss {np.mean(test_losses[-test_i:]):.3f}")
549
+ if wandb_log:
550
+ logs.update({
551
+ "test/loss": np.mean(test_losses[-test_i:]),
552
+ "test/cos_sim_encoder_output": np.mean(cos_sim_encoder_output_test[-(test_i + 1) :]),
553
+ "test/cos_sim_encoder_output_patchwise": np.mean(cos_sim_encoder_output_patchwise_test[-(test_i + 1) :]),
554
+ })
555
+ wandb.log(logs)
556
+
557
+ # Plot progress (first sample in batch)
558
+ if utils.is_interactive() or wandb_log:
559
+ with torch.no_grad():
560
+ if norm_pix_loss:
561
+ normed_input_func, patch_info = model.patchify(input_func, alter_patch_info=False, return_patch_info=True)
562
+ target_mean = normed_input_func.mean(dim=-1, keepdim=True)
563
+ target_var = normed_input_func.var(dim=-1, keepdim=True)
564
+ normed_input_func = (normed_input_func - target_mean) / (target_var + 1.0e-6) ** 0.5
565
+ normed_input_func = model.unpatchify(normed_input_func, patch_info=patch_info)
566
+
567
+ vis_out = vis.plot_mask_pred(
568
+ model, normed_input_func, pred, mask,
569
+ mean=0.5, std=0.2,
570
+ )
571
+ else:
572
+ vis_out = vis.plot_mask_pred(
573
+ model, input_func, pred, mask,
574
+ mean=0.5, std=0.2,
575
+ )
576
+
577
+ if wandb_log:
578
+ logs = {"test/mask_pred": wandb.Image(vis_out)}
579
+ wandb.log(logs)
580
+ else:
581
+ display(vis_out)
582
+
583
+ # Save model checkpoint
584
+ if ckpt_saving and epoch>0 and ((epoch % ckpt_interval == 0) or (epoch==num_epochs-1)):
585
+ save_ckpt(model,f"epoch{epoch}")
586
+
587
+ # wait for other GPUs to catch up if needed
588
+ if distributed: dist.barrier()
589
+
590
+ # close any open plots
591
+ plt.close()
592
+
593
+ if distributed: dist.destroy_process_group()
fMRI-foundation-model-old/flat/main2.ipynb ADDED
@@ -0,0 +1,450 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 4,
6
+ "id": "a0834886-8336-4648-bd48-804b5e472b3d",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": [
10
+ "import os\n",
11
+ "import shutil\n",
12
+ "import sys\n",
13
+ "import json\n",
14
+ "import yaml\n",
15
+ "import numpy as np\n",
16
+ "import copy\n",
17
+ "import math\n",
18
+ "import time\n",
19
+ "import random\n",
20
+ "from tqdm import tqdm\n",
21
+ "import webdataset as wds\n",
22
+ "import matplotlib.pyplot as plt\n",
23
+ "import torch\n",
24
+ "import torch.nn as nn\n",
25
+ "from torchvision import transforms\n",
26
+ "import utils\n",
27
+ "from flat_models import *\n",
28
+ "\n",
29
+ "# tf32 data type is faster than standard float32\n",
30
+ "torch.backends.cuda.matmul.allow_tf32 = True\n",
31
+ "# following fixes a Conv3D CUDNN_NOT_SUPPORTED error\n",
32
+ "torch.backends.cudnn.benchmark = True\n"
33
+ ]
34
+ },
35
+ {
36
+ "cell_type": "code",
37
+ "execution_count": 5,
38
+ "id": "7a8e5e12-b9ac-4baf-91eb-b4c25740c578",
39
+ "metadata": {},
40
+ "outputs": [
41
+ {
42
+ "name": "stdout",
43
+ "output_type": "stream",
44
+ "text": [
45
+ "Number of available CUDA devices: 1\n",
46
+ "LOCAL RANK=0\n",
47
+ "NUM GPUS=1\n",
48
+ "NODE=0\n",
49
+ "GLOBAL RANK=0\n",
50
+ "WORLD_SIZE=1\n"
51
+ ]
52
+ }
53
+ ],
54
+ "source": [
55
+ "\n",
56
+ "### Multi-GPU config ###\n",
57
+ "device_count = torch.cuda.device_count()\n",
58
+ "print(f\"Number of available CUDA devices: {device_count}\")\n",
59
+ "\n",
60
+ "local_rank = os.getenv('LOCAL_RANK')\n",
61
+ "if local_rank is None: \n",
62
+ " local_rank = 0\n",
63
+ "else:\n",
64
+ " local_rank = int(local_rank)\n",
65
+ "print(f\"LOCAL RANK={local_rank}\")\n",
66
+ "\n",
67
+ "num_devices = os.getenv('NUM_GPUS')\n",
68
+ "if num_devices is None: \n",
69
+ " num_devices = 1\n",
70
+ "else:\n",
71
+ " num_devices = int(num_devices)\n",
72
+ "print(f\"NUM GPUS={num_devices}\")\n",
73
+ "distributed = True if num_devices>1 else False\n",
74
+ "if distributed: assert device_count==num_devices\n",
75
+ "\n",
76
+ "node = os.getenv('SLURM_NODEID')\n",
77
+ "if node is None:\n",
78
+ " node = 0\n",
79
+ "else:\n",
80
+ " node = int(node)\n",
81
+ "print(f\"NODE={node}\")\n",
82
+ "\n",
83
+ "global_rank = os.getenv('RANK')\n",
84
+ "if global_rank is None:\n",
85
+ " global_rank = 0\n",
86
+ "else:\n",
87
+ " global_rank = int(global_rank)\n",
88
+ "print(f\"GLOBAL RANK={global_rank}\")\n",
89
+ "\n",
90
+ "world_size = os.getenv('WORLD_SIZE')\n",
91
+ "if world_size is None: \n",
92
+ " world_size = 1\n",
93
+ "else:\n",
94
+ " world_size = int(world_size)\n",
95
+ "print(f\"WORLD_SIZE={world_size}\")"
96
+ ]
97
+ },
98
+ {
99
+ "cell_type": "code",
100
+ "execution_count": 6,
101
+ "id": "65ff5246-4ff5-457d-8509-646b319093c6",
102
+ "metadata": {},
103
+ "outputs": [
104
+ {
105
+ "name": "stdout",
106
+ "output_type": "stream",
107
+ "text": [
108
+ "\n",
109
+ "__CONFIG__\n",
110
+ "model_name = flat_NORM_contrastive_\n",
111
+ "hcp_flat_path = /weka/proj-medarc/shared/hcp_flat\n",
112
+ "use_contrastive_loss = True\n",
113
+ "use_decoder = True\n",
114
+ "patch_size = 16\n",
115
+ "decoder_embed_dim = 512\n",
116
+ "num_frames = 16\n",
117
+ "mask_ratio = 0.75\n",
118
+ "pred_t_dim = 8\n",
119
+ "t_patch_size = 2\n",
120
+ "cls_embed = True\n",
121
+ "no_qkv_bias = False\n",
122
+ "sep_pos_embed = True\n",
123
+ "trunc_init = False\n",
124
+ "norm_pix_loss = False\n",
125
+ "contrastive_loss_weight = 1.0\n",
126
+ "use_vic_loss = True\n",
127
+ "vic_loss_weight = 1.0\n",
128
+ "gamma = 0.5\n",
129
+ "mu = 25\n",
130
+ "lamda = 25\n",
131
+ "nu = 1\n",
132
+ "rand_frac = 0.1\n",
133
+ "use_vic_cls = True\n",
134
+ "pct_masks_to_decode = 1\n",
135
+ "batch_size = 32\n",
136
+ "num_workers = 10\n",
137
+ "num_epochs = 100\n",
138
+ "seed = 42\n",
139
+ "base_lr = 0.0003\n",
140
+ "num_samples_per_epoch = 200000\n",
141
+ "test_num_samples_per_epoch = 50000\n",
142
+ "grad_clip = 1.0\n",
143
+ "grad_accumulation_steps = 1\n",
144
+ "test_set = False\n",
145
+ "probe_num_samples_per_epoch = 100000\n",
146
+ "probe_num_epochs = 30\n",
147
+ "probe_batch_size = 8\n",
148
+ "probe_base_lr = 0.0003\n",
149
+ "ckpt_saving = True\n",
150
+ "ckpt_interval = 5\n",
151
+ "print_interval = 20\n",
152
+ "resume_from_ckpt = False\n",
153
+ "wandb_log = False\n",
154
+ "\n",
155
+ "\n",
156
+ "outdir /weka/proj-fmri/ckadirt/fMRI-foundation-model/flat/checkpoints/flat_NORM_contrastive_\n"
157
+ ]
158
+ }
159
+ ],
160
+ "source": [
161
+ "\n",
162
+ "# Load parameters from yaml config\n",
163
+ "config = yaml.load(open('config.yaml', 'r'), Loader=yaml.FullLoader)\n",
164
+ "\n",
165
+ "print(\"\\n__CONFIG__\")\n",
166
+ "for attribute_name in config.keys():\n",
167
+ " print(f\"{attribute_name} = {config[attribute_name]}\")\n",
168
+ " globals()[attribute_name] = config[f'{attribute_name}']\n",
169
+ "print(\"\\n\")\n",
170
+ "\n",
171
+ "# Create outdir for ckpt and config.yaml\n",
172
+ "outdir = os.path.abspath(f'checkpoints/{model_name}')\n",
173
+ "print(\"outdir\", outdir)\n",
174
+ "\n",
175
+ "# Load previous config.yaml if available\n",
176
+ "if os.path.exists(f\"{outdir}/config.yaml\"):\n",
177
+ " config = yaml.load(open(f\"{outdir}/config.yaml\", 'r'), Loader=yaml.FullLoader)\n",
178
+ " print(f\"Loaded config.yaml from ckpt folder {outdir}\")\n",
179
+ "\n",
180
+ " # create global variables from the config\n",
181
+ " print(\"\\n__REPLACING_CONFIG__\")\n",
182
+ " for attribute_name in config.keys():\n",
183
+ " print(f\"{attribute_name} = {config[attribute_name]}\")\n",
184
+ " globals()[attribute_name] = config[f'{attribute_name}']\n",
185
+ " print(\"\\n\")\n",
186
+ "\n",
187
+ "data_type = torch.float32 # change depending on your mixed_precision\n",
188
+ "global_batch_size = batch_size * world_size\n"
189
+ ]
190
+ },
191
+ {
192
+ "cell_type": "code",
193
+ "execution_count": 7,
194
+ "id": "b5b30c27-04e9-4566-ac33-cc2d69e27711",
195
+ "metadata": {},
196
+ "outputs": [
197
+ {
198
+ "name": "stdout",
199
+ "output_type": "stream",
200
+ "text": [
201
+ "PID of this process = 925118\n",
202
+ "device = cuda distributed = False num_devices = 1 local rank = 0 world size = 1 data_type = torch.float32\n"
203
+ ]
204
+ }
205
+ ],
206
+ "source": [
207
+ "\n",
208
+ "# FSDP Setup\n",
209
+ "if distributed:\n",
210
+ " import torch.distributed as dist\n",
211
+ " from torch.distributed.fsdp import FullyShardedDataParallel as FSDP\n",
212
+ " from torch.distributed.fsdp.api import BackwardPrefetch, CPUOffload, ShardingStrategy\n",
213
+ " import functools\n",
214
+ " from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy, transformer_auto_wrap_policy\n",
215
+ " print(f\"setting device to cuda:{local_rank}\")\n",
216
+ " torch.cuda.set_device(local_rank)\n",
217
+ " device = torch.device('cuda',local_rank)\n",
218
+ " dist.init_process_group(\"nccl\", rank=global_rank, world_size=world_size)\n",
219
+ " print(f\"\\nSuccessfully set cuda:{local_rank} | global_rank{global_rank} | node{node}\")\n",
220
+ " dist.barrier() \n",
221
+ " print(f\"global_rank{global_rank} passed barrier\")\n",
222
+ "else:\n",
223
+ " device = torch.device('cuda')\n",
224
+ "\n",
225
+ "print(\"PID of this process =\",os.getpid())\n",
226
+ "print(\"device =\", device, \"distributed =\",distributed, \"num_devices =\", num_devices, \"local rank =\", local_rank, \"world size =\", world_size, \"data_type =\", data_type)\n",
227
+ "\n",
228
+ "# seed all random functions\n",
229
+ "utils.seed_everything(seed + global_rank)"
230
+ ]
231
+ },
232
+ {
233
+ "cell_type": "code",
234
+ "execution_count": 8,
235
+ "id": "ad692bcb-a166-46d5-97f7-292c91cd4443",
236
+ "metadata": {},
237
+ "outputs": [
238
+ {
239
+ "name": "stdout",
240
+ "output_type": "stream",
241
+ "text": [
242
+ "img_size (144, 320) patch_size (16, 16) frames 16 t_patch_size 2\n",
243
+ "model initialized\n",
244
+ "num_batches 62\n",
245
+ "test_num_batches 62\n"
246
+ ]
247
+ }
248
+ ],
249
+ "source": [
250
+ "\n",
251
+ "from util.hcp_flat import load_hcp_flat_mask\n",
252
+ "from util.hcp_flat import create_hcp_flat\n",
253
+ "from util.losses import *\n",
254
+ "import util.visualize as vis\n",
255
+ "\n",
256
+ "if utils.is_interactive(): # Use less samples per epoch for debugging\n",
257
+ " num_samples_per_epoch = 2000\n",
258
+ " test_num_samples_per_epoch = 2000\n",
259
+ "\n",
260
+ "model = mae_vit_small_fmri(\n",
261
+ " decoder_embed_dim=decoder_embed_dim,\n",
262
+ " t_patch_size=t_patch_size,\n",
263
+ " pred_t_dim=pred_t_dim,\n",
264
+ " decoder_depth=4,\n",
265
+ " cls_embed=cls_embed,\n",
266
+ " norm_pix_loss=norm_pix_loss,\n",
267
+ " no_qkv_bias=no_qkv_bias,\n",
268
+ " sep_pos_embed=sep_pos_embed,\n",
269
+ " trunc_init=trunc_init,\n",
270
+ ")\n",
271
+ "\n",
272
+ "if use_contrastive_loss:\n",
273
+ " model.simclr_handler = SimCLRHandler(model.embed_dim).to(device)\n",
274
+ "if use_vic_loss:\n",
275
+ " model.vicreg_handler = VICRegHandler(model.embed_dim).to(device)\n",
276
+ "\n",
277
+ "# state = torch.load(\"checkpoints/checkpoint-00099.pth\", map_location=\"cpu\")\n",
278
+ "# model.load_state_dict(state[\"model\"])\n",
279
+ "\n",
280
+ "num_batches = num_samples_per_epoch // (num_devices * batch_size)\n",
281
+ "test_num_batches = test_num_samples_per_epoch // (num_devices * batch_size)\n",
282
+ "print(\"num_batches\", num_batches)\n",
283
+ "print(\"test_num_batches\", test_num_batches)"
284
+ ]
285
+ },
286
+ {
287
+ "cell_type": "code",
288
+ "execution_count": 9,
289
+ "id": "63da3ad3-116f-498d-ae01-d75585b010f2",
290
+ "metadata": {},
291
+ "outputs": [],
292
+ "source": [
293
+ "## Train ##\n",
294
+ "train_dataset = create_hcp_flat(root=hcp_flat_path, \n",
295
+ " training=True, frames=num_frames)\n",
296
+ "train_dl = wds.WebLoader(\n",
297
+ " train_dataset.batched(batch_size, partial=False),\n",
298
+ " batch_size=None,\n",
299
+ " shuffle=False,\n",
300
+ " num_workers=num_workers,\n",
301
+ " pin_memory=True,\n",
302
+ ")\n",
303
+ "train_dl = train_dl.with_epoch(num_batches)\n",
304
+ "\n",
305
+ "## Test ##\n",
306
+ "test_dataset = create_hcp_flat(root=hcp_flat_path,\n",
307
+ " training=False, frames=num_frames)\n",
308
+ "test_dl = wds.WebLoader(\n",
309
+ " test_dataset.batched(batch_size, partial=False),\n",
310
+ " batch_size=None,\n",
311
+ " shuffle=False,\n",
312
+ " num_workers=num_workers,\n",
313
+ " pin_memory=True,\n",
314
+ ")\n",
315
+ "test_dl = test_dl.with_epoch(test_num_batches)"
316
+ ]
317
+ },
318
+ {
319
+ "cell_type": "code",
320
+ "execution_count": 10,
321
+ "id": "9ad20649-e51e-4e5c-9fb4-f78d33edaea0",
322
+ "metadata": {},
323
+ "outputs": [
324
+ {
325
+ "name": "stdout",
326
+ "output_type": "stream",
327
+ "text": [
328
+ "\n",
329
+ "Checking distributed setup on global_rank 0...\n",
330
+ "\n",
331
+ "Not training distributed! global_rank 0\n",
332
+ "multiply base lr 0.0003 by effective batch size 32\n",
333
+ "lr = 3.75e-05\n"
334
+ ]
335
+ }
336
+ ],
337
+ "source": [
338
+ "\n",
339
+ "print(f\"\\nChecking distributed setup on global_rank {global_rank}...\")\n",
340
+ "from util.video_vit import Attention\n",
341
+ "if distributed:\n",
342
+ " my_auto_wrap_policy = functools.partial(\n",
343
+ " transformer_auto_wrap_policy, \n",
344
+ " transformer_layer_cls={\n",
345
+ " Attention, # <--- Your Transformer layer class\n",
346
+ " },\n",
347
+ " )\n",
348
+ " print(f\"\\nPrepping FSDP on {global_rank} {node}...\\n\")\n",
349
+ " model = model.to(device)\n",
350
+ " model = FSDP(\n",
351
+ " model,\n",
352
+ " sharding_strategy=ShardingStrategy.HYBRID_SHARD,\n",
353
+ " auto_wrap_policy=my_auto_wrap_policy,\n",
354
+ " use_orig_params=False,\n",
355
+ " cpu_offload=None, #CPUOffload(offload_params=True)\n",
356
+ " sync_module_states=True,\n",
357
+ " limit_all_gathers=True, # See https://github.com/pytorch/pytorch/issues/91165\n",
358
+ " device_id=device,\n",
359
+ " )\n",
360
+ " print(f\"\\nSuccessfully loaded FSDP model to device on global_rank {global_rank}\\n\")\n",
361
+ " dist.barrier()\n",
362
+ "else:\n",
363
+ " print(f\"\\nNot training distributed! global_rank {global_rank}\")\n",
364
+ " model.to(device)\n",
365
+ "\n",
366
+ "no_decay = [\"bias\", \"LayerNorm.bias\", \"LayerNorm.weight\"]\n",
367
+ "opt_grouped_parameters = [\n",
368
+ " {'params': [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 0.05},\n",
369
+ " {'params': [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0},\n",
370
+ "]\n",
371
+ "\n",
372
+ "lr = base_lr * global_batch_size / 256\n",
373
+ "print(f\"multiply base lr {base_lr} by effective batch size {global_batch_size}\")\n",
374
+ "print(f\"lr = {lr}\")\n",
375
+ "\n",
376
+ "optimizer = torch.optim.AdamW(opt_grouped_parameters, lr=lr, betas=(0.9, 0.95))"
377
+ ]
378
+ },
379
+ {
380
+ "cell_type": "code",
381
+ "execution_count": 11,
382
+ "id": "cef70e06-d181-46c0-a36c-12aa944a8441",
383
+ "metadata": {},
384
+ "outputs": [
385
+ {
386
+ "name": "stdout",
387
+ "output_type": "stream",
388
+ "text": [
389
+ "\n",
390
+ "Done with model preparations!\n",
391
+ "param counts:\n",
392
+ "40,789,504 total\n",
393
+ "40,789,504 trainable\n"
394
+ ]
395
+ }
396
+ ],
397
+ "source": [
398
+ "\n",
399
+ "def adjust_learning_rate(optimizer, epoch, warmup_epochs=5, min_lr=0.0):\n",
400
+ " \"\"\"Decay the learning rate with half-cycle cosine after warmup\"\"\"\n",
401
+ " if epoch < warmup_epochs:\n",
402
+ " lr_ = lr * epoch / warmup_epochs\n",
403
+ " else:\n",
404
+ " lr_ = min_lr + (lr - min_lr) * 0.5 * (\n",
405
+ " 1.0\n",
406
+ " + math.cos(\n",
407
+ " math.pi\n",
408
+ " * (epoch - warmup_epochs)\n",
409
+ " / (num_epochs - warmup_epochs)\n",
410
+ " )\n",
411
+ " )\n",
412
+ " for param_group in optimizer.param_groups:\n",
413
+ " param_group[\"lr\"] = lr_\n",
414
+ " return lr_\n",
415
+ "\n",
416
+ "print(\"\\nDone with model preparations!\")\n",
417
+ "num_params = utils.count_params(model)\n"
418
+ ]
419
+ },
420
+ {
421
+ "cell_type": "code",
422
+ "execution_count": null,
423
+ "id": "1deff37d-c9c6-48e8-b1c4-1cec41f23fca",
424
+ "metadata": {},
425
+ "outputs": [],
426
+ "source": []
427
+ }
428
+ ],
429
+ "metadata": {
430
+ "kernelspec": {
431
+ "display_name": "Python 3 (ipykernel)",
432
+ "language": "python",
433
+ "name": "python3"
434
+ },
435
+ "language_info": {
436
+ "codemirror_mode": {
437
+ "name": "ipython",
438
+ "version": 3
439
+ },
440
+ "file_extension": ".py",
441
+ "mimetype": "text/x-python",
442
+ "name": "python",
443
+ "nbconvert_exporter": "python",
444
+ "pygments_lexer": "ipython3",
445
+ "version": "3.10.14"
446
+ }
447
+ },
448
+ "nbformat": 4,
449
+ "nbformat_minor": 5
450
+ }
fMRI-foundation-model-old/flat/parquet_probe.ipynb ADDED
@@ -0,0 +1,407 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "b8e236f1-385a-4d93-bb39-bea3ee384d76",
7
+ "metadata": {
8
+ "tags": []
9
+ },
10
+ "outputs": [
11
+ {
12
+ "name": "stdout",
13
+ "output_type": "stream",
14
+ "text": [
15
+ "outdir /weka/proj-fmri/paulscotti/fMRI-foundation-model/flat/checkpoints/clane\n",
16
+ "Loaded config.yaml from ckpt folder /weka/proj-fmri/paulscotti/fMRI-foundation-model/flat/checkpoints/clane\n",
17
+ "\n",
18
+ "__CONFIG__\n",
19
+ "base_lr = 0.001\n",
20
+ "batch_size = 32\n",
21
+ "ckpt_interval = 5\n",
22
+ "ckpt_saving = True\n",
23
+ "cls_embed = True\n",
24
+ "decoder_embed_dim = 512\n",
25
+ "grad_clip = 1.0\n",
26
+ "hcp_flat_path = /weka/proj-fmri/shared/hcp_flat\n",
27
+ "mask_ratio = 0.75\n",
28
+ "model_name = clane\n",
29
+ "no_qkv_bias = False\n",
30
+ "norm_pix_loss = False\n",
31
+ "num_epochs = 50\n",
32
+ "num_frames = 16\n",
33
+ "num_samples_per_epoch = 200000\n",
34
+ "num_workers = 10\n",
35
+ "pred_t_dim = 8\n",
36
+ "print_interval = 20\n",
37
+ "probe_num_samples_per_epoch = 100000\n",
38
+ "probe_num_epochs = 30\n",
39
+ "probe_batch_size = 8\n",
40
+ "probe_base_lr = 0.0003\n",
41
+ "resume_from_ckpt = True\n",
42
+ "seed = 42\n",
43
+ "sep_pos_embed = True\n",
44
+ "t_patch_size = 2\n",
45
+ "test_num_samples_per_epoch = 50000\n",
46
+ "trunc_init = False\n",
47
+ "use_contrastive_loss = False\n",
48
+ "wandb_log = True\n",
49
+ "\n",
50
+ "\n",
51
+ "Number of available CUDA devices: 1\n",
52
+ "LOCAL RANK=0\n",
53
+ "NUM GPUS=1\n",
54
+ "NODE=0\n",
55
+ "GLOBAL RANK=0\n",
56
+ "WORLD_SIZE=1\n",
57
+ "PID of this process = 448416\n",
58
+ "device = cuda distributed = False num_devices = 1 local rank = 0 world size = 1 data_type = torch.float32\n"
59
+ ]
60
+ }
61
+ ],
62
+ "source": [
63
+ "# Import packages and setup gpu configuration.\n",
64
+ "# This code block shouldnt need to be adjusted!\n",
65
+ "import os\n",
66
+ "import sys\n",
67
+ "import json\n",
68
+ "import yaml\n",
69
+ "import numpy as np\n",
70
+ "import copy\n",
71
+ "import math\n",
72
+ "import time\n",
73
+ "import random\n",
74
+ "from tqdm import tqdm\n",
75
+ "import webdataset as wds\n",
76
+ "import matplotlib.pyplot as plt\n",
77
+ "\n",
78
+ "import torch\n",
79
+ "import torch.nn as nn\n",
80
+ "from torchvision import transforms\n",
81
+ "import utils\n",
82
+ "from flat_models import *\n",
83
+ "\n",
84
+ "# tf32 data type is faster than standard float32\n",
85
+ "torch.backends.cuda.matmul.allow_tf32 = True\n",
86
+ "# following fixes a Conv3D CUDNN_NOT_SUPPORTED error\n",
87
+ "torch.backends.cudnn.benchmark = True\n",
88
+ "\n",
89
+ "## MODEL TO LOAD ##\n",
90
+ "model_name = \"clane\"\n",
91
+ "parquet_folder = \"epoch99\"\n",
92
+ "outdir = os.path.abspath(f'checkpoints/{model_name}')\n",
93
+ "print(\"outdir\", outdir)\n",
94
+ "# Load previous config.yaml if available\n",
95
+ "if os.path.exists(f\"{outdir}/config.yaml\"):\n",
96
+ " config = yaml.load(open(f\"{outdir}/config.yaml\", 'r'), Loader=yaml.FullLoader)\n",
97
+ " print(f\"Loaded config.yaml from ckpt folder {outdir}\")\n",
98
+ " # create global variables from the config\n",
99
+ " print(\"\\n__CONFIG__\")\n",
100
+ " for attribute_name in config.keys():\n",
101
+ " print(f\"{attribute_name} = {config[attribute_name]}\")\n",
102
+ " globals()[attribute_name] = config[f'{attribute_name}']\n",
103
+ " print(\"\\n\")\n",
104
+ "\n",
105
+ "### Multi-GPU config ###\n",
106
+ "device_count = torch.cuda.device_count()\n",
107
+ "print(f\"Number of available CUDA devices: {device_count}\")\n",
108
+ "\n",
109
+ "local_rank = os.getenv('LOCAL_RANK')\n",
110
+ "if local_rank is None: \n",
111
+ " local_rank = 0\n",
112
+ "else:\n",
113
+ " local_rank = int(local_rank)\n",
114
+ "print(f\"LOCAL RANK={local_rank}\")\n",
115
+ "\n",
116
+ "num_devices = os.getenv('NUM_GPUS')\n",
117
+ "if num_devices is None: \n",
118
+ " num_devices = 1\n",
119
+ "else:\n",
120
+ " num_devices = int(num_devices)\n",
121
+ "print(f\"NUM GPUS={num_devices}\")\n",
122
+ "distributed = True if num_devices>1 else False\n",
123
+ "if distributed: assert device_count==num_devices\n",
124
+ "\n",
125
+ "node = os.getenv('SLURM_NODEID')\n",
126
+ "if node is None:\n",
127
+ " node = 0\n",
128
+ "else:\n",
129
+ " node = int(node)\n",
130
+ "print(f\"NODE={node}\")\n",
131
+ "\n",
132
+ "global_rank = os.getenv('RANK')\n",
133
+ "if global_rank is None:\n",
134
+ " global_rank = 0\n",
135
+ "else:\n",
136
+ " global_rank = int(global_rank)\n",
137
+ "print(f\"GLOBAL RANK={global_rank}\")\n",
138
+ "\n",
139
+ "world_size = os.getenv('WORLD_SIZE')\n",
140
+ "if world_size is None: \n",
141
+ " world_size = 1\n",
142
+ "else:\n",
143
+ " world_size = int(world_size)\n",
144
+ "print(f\"WORLD_SIZE={world_size}\")\n",
145
+ "\n",
146
+ "if utils.is_interactive():\n",
147
+ " # Following allows you to change functions in models.py or utils.py and \n",
148
+ " # have this notebook automatically update with your revisions\n",
149
+ " %load_ext autoreload\n",
150
+ " %autoreload 2\n",
151
+ "\n",
152
+ "batch_size = probe_batch_size\n",
153
+ "num_epochs = probe_num_epochs\n",
154
+ "\n",
155
+ "data_type = torch.float32 # change depending on your mixed_precision\n",
156
+ "global_batch_size = batch_size * world_size\n",
157
+ "\n",
158
+ "# FSDP Setup\n",
159
+ "if distributed:\n",
160
+ " import torch.distributed as dist\n",
161
+ " import torch.multiprocessing as mp\n",
162
+ " from torch.distributed.fsdp import FullyShardedDataParallel as FSDP\n",
163
+ " from torch.distributed.fsdp.api import BackwardPrefetch, CPUOffload, ShardingStrategy\n",
164
+ " import functools\n",
165
+ " from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy, transformer_auto_wrap_policy\n",
166
+ " print(f\"setting device to cuda:{local_rank}\")\n",
167
+ " torch.cuda.set_device(local_rank)\n",
168
+ " device = torch.device('cuda',local_rank)\n",
169
+ " dist.init_process_group(\"nccl\", rank=global_rank, world_size=world_size)\n",
170
+ " print(f\"\\nSuccessfully set cuda:{local_rank} | global_rank{global_rank} | node{node}\")\n",
171
+ " dist.barrier()\n",
172
+ " print(f\"global_rank{global_rank} passed barrier\")\n",
173
+ "else:\n",
174
+ " device = torch.device('cuda')\n",
175
+ "\n",
176
+ "print(\"PID of this process =\",os.getpid())\n",
177
+ "print(\"device =\", device, \"distributed =\",distributed, \"num_devices =\", num_devices, \"local rank =\", local_rank, \"world size =\", world_size, \"data_type =\", data_type)\n",
178
+ "\n",
179
+ "# seed all random functions\n",
180
+ "utils.seed_everything(seed + global_rank)"
181
+ ]
182
+ },
183
+ {
184
+ "cell_type": "markdown",
185
+ "id": "ab15aca0-148e-435f-b8f2-7a708b61a6d9",
186
+ "metadata": {},
187
+ "source": [
188
+ "# hcp_flat"
189
+ ]
190
+ },
191
+ {
192
+ "cell_type": "code",
193
+ "execution_count": 2,
194
+ "id": "de1a5b87-fa69-44e1-bdb9-bd9e257485c6",
195
+ "metadata": {
196
+ "tags": []
197
+ },
198
+ "outputs": [
199
+ {
200
+ "name": "stdout",
201
+ "output_type": "stream",
202
+ "text": [
203
+ "img_size (144, 320) patch_size (16, 16) frames 16 t_patch_size 2\n",
204
+ "model initialized\n",
205
+ "\n",
206
+ "Loaded checkpoint epoch99.pth from /weka/proj-fmri/paulscotti/fMRI-foundation-model/flat/checkpoints/clane\n",
207
+ "\n"
208
+ ]
209
+ }
210
+ ],
211
+ "source": [
212
+ "from util.hcp_flat import load_hcp_flat_mask\n",
213
+ "from util.hcp_flat import create_hcp_flat\n",
214
+ "import util.visualize as vis\n",
215
+ "\n",
216
+ "if utils.is_interactive(): # Use less samples per epoch for debugging\n",
217
+ " probe_num_samples_per_epoch = 100000\n",
218
+ " test_num_samples_per_epoch = 100000\n",
219
+ " num_epochs = 10\n",
220
+ "\n",
221
+ "model = mae_vit_small_fmri(\n",
222
+ " patch_size=16,\n",
223
+ " decoder_embed_dim=decoder_embed_dim,\n",
224
+ " t_patch_size=t_patch_size,\n",
225
+ " pred_t_dim=pred_t_dim,\n",
226
+ " decoder_depth=4,\n",
227
+ " cls_embed=cls_embed,\n",
228
+ " norm_pix_loss=norm_pix_loss,\n",
229
+ " no_qkv_bias=no_qkv_bias,\n",
230
+ " sep_pos_embed=sep_pos_embed,\n",
231
+ " trunc_init=trunc_init,\n",
232
+ ")\n",
233
+ "\n",
234
+ "# Load ckpt\n",
235
+ "if not os.path.exists(outdir) or not os.path.isdir(outdir):\n",
236
+ " print(f\"\\nCheckpoint folder {outdir} does not exist.\\n\")\n",
237
+ "else:\n",
238
+ " checkpoint_files = [f for f in os.listdir(outdir) if f.endswith('.pth')]\n",
239
+ "\n",
240
+ " # Find the latest ckpt to load\n",
241
+ " epoch_numbers = []\n",
242
+ " for file in checkpoint_files:\n",
243
+ " try:\n",
244
+ " epoch_number = int(file.split('epoch')[-1].split('.')[0])\n",
245
+ " epoch_numbers.append(epoch_number)\n",
246
+ " except ValueError:\n",
247
+ " continue\n",
248
+ " latest_epoch = max(epoch_numbers)\n",
249
+ " checkpoint_name = f\"epoch{latest_epoch}.pth\"\n",
250
+ " \n",
251
+ " ### Or provide the specific checkpoint you want to load\n",
252
+ " # checkpoint_name = \"epoch10.pth\" #\"epoch15.pth\"\n",
253
+ "\n",
254
+ " # Load the checkpoint\n",
255
+ " checkpoint_path = os.path.join(outdir, checkpoint_name)\n",
256
+ " state = torch.load(checkpoint_path)\n",
257
+ " model.load_state_dict(state[\"model\"], strict=True) #model_state_dict\n",
258
+ "\n",
259
+ " print(f\"\\nLoaded checkpoint {checkpoint_name} from {outdir}\\n\")\n",
260
+ "\n",
261
+ "model.eval()\n",
262
+ "model.requires_grad_(False)\n",
263
+ "model.to(device)\n",
264
+ "pass"
265
+ ]
266
+ },
267
+ {
268
+ "cell_type": "code",
269
+ "execution_count": 3,
270
+ "id": "c3461199-e805-4e9c-8c91-894e83cf8bc3",
271
+ "metadata": {
272
+ "tags": []
273
+ },
274
+ "outputs": [
275
+ {
276
+ "name": "stdout",
277
+ "output_type": "stream",
278
+ "text": [
279
+ "Target: trial_type\n",
280
+ "train: (118656, 9), test: (12816, 9)\n",
281
+ "X_train: (118656, 384), X_test: (12816, 384)\n",
282
+ "classes (21): ['0bk_body' '0bk_faces' '0bk_places' '0bk_tools' '2bk_body' '2bk_faces'\n",
283
+ " '2bk_places' '2bk_tools' 'fear' 'lf' 'lh' 'match' 'math' 'mental' 'neut'\n",
284
+ " 'relation' 'rf' 'rh' 'rnd' 'story' 't']\n",
285
+ "\n",
286
+ "y_train: (118656,) [14 8 14 8 14 13 13 13 13 18 18 13 15 11 11 15 11 15 15 11]\n",
287
+ "y_test: (12816,) [ 7 7 0 0 5 5 3 3 4 4 6 6 17 9 20 16 10 20 9 17]\n",
288
+ "\n",
289
+ "train_ind: 106790 [ 71756 44749 102949 6302 76306 43438 88189 46933 66308 102792]\n",
290
+ "val_ind: 11866 [73083 36571 52192 51200 2619 399 2375 2327 41809 84134]\n",
291
+ "Fitting PCA projection\n",
292
+ "Fitting logistic regression\n",
293
+ "Done:\n",
294
+ "{\"target\": \"trial_type\", \"train_acc\": 0.9191310047757281, \"val_acc\": 0.9014832293949099, \"test_acc\": 0.899812734082397}\n"
295
+ ]
296
+ }
297
+ ],
298
+ "source": [
299
+ "import argparse\n",
300
+ "import json\n",
301
+ "import os\n",
302
+ "import pickle\n",
303
+ "from pathlib import Path\n",
304
+ "\n",
305
+ "import pandas as pd\n",
306
+ "import numpy as np\n",
307
+ "from sklearn.decomposition import PCA\n",
308
+ "from sklearn.linear_model import LogisticRegressionCV\n",
309
+ "from sklearn.model_selection import train_test_split\n",
310
+ "from sklearn.preprocessing import LabelEncoder\n",
311
+ "\n",
312
+ "target = \"trial_type\"\n",
313
+ "print(f\"Target: {target}\")\n",
314
+ "\n",
315
+ "train_features = pd.read_parquet(f\"{outdir}/{parquet_folder}/train.parquet\")\n",
316
+ "test_features = pd.read_parquet(f\"{outdir}/{parquet_folder}/test.parquet\")\n",
317
+ "print(f\"train: {train_features.shape}, test: {test_features.shape}\")\n",
318
+ "\n",
319
+ "X_train = np.stack(train_features[\"feature\"])\n",
320
+ "X_test = np.stack(test_features[\"feature\"])\n",
321
+ "print(f\"X_train: {X_train.shape}, X_test: {X_test.shape}\")\n",
322
+ "\n",
323
+ "if target == \"task\":\n",
324
+ " labels_train = train_features[\"task\"].str.rstrip(\"1234\").values\n",
325
+ " labels_test = test_features[\"task\"].str.rstrip(\"1234\").values\n",
326
+ "elif target == \"trial_type\":\n",
327
+ " labels_train = train_features[\"trial_type\"].values\n",
328
+ " labels_test = test_features[\"trial_type\"].values\n",
329
+ "\n",
330
+ "label_enc = LabelEncoder()\n",
331
+ "y_train = label_enc.fit_transform(labels_train)\n",
332
+ "y_test = label_enc.transform(labels_test)\n",
333
+ "\n",
334
+ "print(f\"classes ({len(label_enc.classes_)}): {label_enc.classes_}\")\n",
335
+ "print(\n",
336
+ " f\"\\ny_train: {y_train.shape} {y_train[:20]}\\n\"\n",
337
+ " f\"y_test: {y_test.shape} {y_test[:20]}\"\n",
338
+ ")\n",
339
+ "del train_features, test_features\n",
340
+ "\n",
341
+ "train_ind, val_ind = train_test_split(\n",
342
+ " np.arange(len(X_train)), train_size=0.9, random_state=42\n",
343
+ ")\n",
344
+ "print(\n",
345
+ " f\"\\ntrain_ind: {len(train_ind)} {train_ind[:10]}\\n\"\n",
346
+ " f\"val_ind: {len(val_ind)} {val_ind[:10]}\"\n",
347
+ ")\n",
348
+ "X_train, X_val = X_train[train_ind], X_train[val_ind]\n",
349
+ "y_train, y_val = y_train[train_ind], y_train[val_ind]\n",
350
+ "\n",
351
+ "print(\"Fitting PCA projection\")\n",
352
+ "pca = PCA(n_components=384, whiten=True, svd_solver=\"randomized\")\n",
353
+ "pca.fit(X_train)\n",
354
+ "\n",
355
+ "X_train = pca.transform(X_train)\n",
356
+ "X_val = pca.transform(X_val)\n",
357
+ "X_test = pca.transform(X_test)\n",
358
+ "\n",
359
+ "print(\"Fitting logistic regression\")\n",
360
+ "clf = LogisticRegressionCV()\n",
361
+ "clf.fit(X_train, y_train)\n",
362
+ "\n",
363
+ "train_acc = clf.score(X_train, y_train)\n",
364
+ "val_acc = clf.score(X_val, y_val)\n",
365
+ "test_acc = clf.score(X_test, y_test)\n",
366
+ "\n",
367
+ "result = {\n",
368
+ " \"target\": target,\n",
369
+ " \"train_acc\": train_acc,\n",
370
+ " \"val_acc\": val_acc,\n",
371
+ " \"test_acc\": test_acc,\n",
372
+ "}\n",
373
+ "\n",
374
+ "print(f\"Done:\\n{json.dumps(result)}\")"
375
+ ]
376
+ },
377
+ {
378
+ "cell_type": "code",
379
+ "execution_count": null,
380
+ "id": "f9d84788-0ba5-42b9-9e04-b03790997504",
381
+ "metadata": {},
382
+ "outputs": [],
383
+ "source": []
384
+ }
385
+ ],
386
+ "metadata": {
387
+ "kernelspec": {
388
+ "display_name": "found2",
389
+ "language": "python",
390
+ "name": "found2"
391
+ },
392
+ "language_info": {
393
+ "codemirror_mode": {
394
+ "name": "ipython",
395
+ "version": 3
396
+ },
397
+ "file_extension": ".py",
398
+ "mimetype": "text/x-python",
399
+ "name": "python",
400
+ "nbconvert_exporter": "python",
401
+ "pygments_lexer": "ipython3",
402
+ "version": "3.10.14"
403
+ }
404
+ },
405
+ "nbformat": 4,
406
+ "nbformat_minor": 5
407
+ }
fMRI-foundation-model-old/flat/probe.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
fMRI-foundation-model-old/flat/probe_prepper.ipynb ADDED
@@ -0,0 +1,587 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "b8e236f1-385a-4d93-bb39-bea3ee384d76",
7
+ "metadata": {
8
+ "tags": []
9
+ },
10
+ "outputs": [
11
+ {
12
+ "name": "stdout",
13
+ "output_type": "stream",
14
+ "text": [
15
+ "outdir /weka/proj-fmri/paulscotti/fMRI-foundation-model/flat/checkpoints/ps16_mask9_3losses_bs32\n",
16
+ "Loaded config.yaml from ckpt folder /weka/proj-fmri/paulscotti/fMRI-foundation-model/flat/checkpoints/ps16_mask9_3losses_bs32\n",
17
+ "\n",
18
+ "__CONFIG__\n",
19
+ "base_lr = 0.001\n",
20
+ "batch_size = 32\n",
21
+ "ckpt_interval = 5\n",
22
+ "ckpt_saving = True\n",
23
+ "cls_embed = True\n",
24
+ "decoder_embed_dim = 512\n",
25
+ "grad_clip = 1.0\n",
26
+ "hcp_flat_path = /weka/proj-medarc/shared/hcp_flat\n",
27
+ "mask_ratio = 0.9\n",
28
+ "model_name = ps16_mask9_3losses_bs32\n",
29
+ "no_qkv_bias = False\n",
30
+ "norm_pix_loss = False\n",
31
+ "num_epochs = 100\n",
32
+ "num_frames = 16\n",
33
+ "num_samples_per_epoch = 200000\n",
34
+ "num_workers = 10\n",
35
+ "patch_size = 16\n",
36
+ "pred_t_dim = 8\n",
37
+ "print_interval = 20\n",
38
+ "probe_base_lr = 0.0003\n",
39
+ "probe_batch_size = 8\n",
40
+ "probe_num_epochs = 30\n",
41
+ "probe_num_samples_per_epoch = 100000\n",
42
+ "resume_from_ckpt = True\n",
43
+ "seed = 42\n",
44
+ "sep_pos_embed = True\n",
45
+ "t_patch_size = 2\n",
46
+ "test_num_samples_per_epoch = 50000\n",
47
+ "trunc_init = False\n",
48
+ "use_contrastive_loss = True\n",
49
+ "wandb_log = True\n",
50
+ "\n",
51
+ "\n",
52
+ "Number of available CUDA devices: 1\n",
53
+ "LOCAL RANK=0\n",
54
+ "NUM GPUS=1\n",
55
+ "NODE=0\n",
56
+ "GLOBAL RANK=0\n",
57
+ "WORLD_SIZE=1\n",
58
+ "PID of this process = 2361819\n",
59
+ "device = cuda distributed = False num_devices = 1 local rank = 0 world size = 1 data_type = torch.float32\n"
60
+ ]
61
+ }
62
+ ],
63
+ "source": [
64
+ "# Import packages and setup gpu configuration.\n",
65
+ "# This code block shouldnt need to be adjusted!\n",
66
+ "import os\n",
67
+ "import sys\n",
68
+ "import json\n",
69
+ "import yaml\n",
70
+ "import numpy as np\n",
71
+ "import copy\n",
72
+ "import math\n",
73
+ "import time\n",
74
+ "import datetime\n",
75
+ "import random\n",
76
+ "from tqdm import tqdm\n",
77
+ "import webdataset as wds\n",
78
+ "import matplotlib.pyplot as plt\n",
79
+ "\n",
80
+ "import torch\n",
81
+ "import torch.nn as nn\n",
82
+ "from torchvision import transforms\n",
83
+ "import utils\n",
84
+ "from flat_models import *\n",
85
+ "\n",
86
+ "from elbow.sinks import BufferedParquetWriter\n",
87
+ "\n",
88
+ "# tf32 data type is faster than standard float32\n",
89
+ "torch.backends.cuda.matmul.allow_tf32 = True\n",
90
+ "# following fixes a Conv3D CUDNN_NOT_SUPPORTED error\n",
91
+ "torch.backends.cudnn.benchmark = True\n",
92
+ "\n",
93
+ "## MODEL TO LOAD ##\n",
94
+ "model_name = \"ps16_mask9_3losses_bs32\"\n",
95
+ "outdir = os.path.abspath(f'checkpoints/{model_name}')\n",
96
+ "print(\"outdir\", outdir)\n",
97
+ "# Load previous config.yaml if available\n",
98
+ "if os.path.exists(f\"{outdir}/config.yaml\"):\n",
99
+ " config = yaml.load(open(f\"{outdir}/config.yaml\", 'r'), Loader=yaml.FullLoader)\n",
100
+ " print(f\"Loaded config.yaml from ckpt folder {outdir}\")\n",
101
+ " # create global variables from the config\n",
102
+ " print(\"\\n__CONFIG__\")\n",
103
+ " for attribute_name in config.keys():\n",
104
+ " print(f\"{attribute_name} = {config[attribute_name]}\")\n",
105
+ " globals()[attribute_name] = config[f'{attribute_name}']\n",
106
+ " print(\"\\n\")\n",
107
+ "\n",
108
+ "### Multi-GPU config ###\n",
109
+ "device_count = torch.cuda.device_count()\n",
110
+ "print(f\"Number of available CUDA devices: {device_count}\")\n",
111
+ "\n",
112
+ "local_rank = os.getenv('LOCAL_RANK')\n",
113
+ "if local_rank is None: \n",
114
+ " local_rank = 0\n",
115
+ "else:\n",
116
+ " local_rank = int(local_rank)\n",
117
+ "print(f\"LOCAL RANK={local_rank}\")\n",
118
+ "\n",
119
+ "num_devices = os.getenv('NUM_GPUS')\n",
120
+ "if num_devices is None: \n",
121
+ " num_devices = 1\n",
122
+ "else:\n",
123
+ " num_devices = int(num_devices)\n",
124
+ "print(f\"NUM GPUS={num_devices}\")\n",
125
+ "distributed = True if num_devices>1 else False\n",
126
+ "if distributed: assert device_count==num_devices\n",
127
+ "\n",
128
+ "node = os.getenv('SLURM_NODEID')\n",
129
+ "if node is None:\n",
130
+ " node = 0\n",
131
+ "else:\n",
132
+ " node = int(node)\n",
133
+ "print(f\"NODE={node}\")\n",
134
+ "\n",
135
+ "global_rank = os.getenv('RANK')\n",
136
+ "if global_rank is None:\n",
137
+ " global_rank = 0\n",
138
+ "else:\n",
139
+ " global_rank = int(global_rank)\n",
140
+ "print(f\"GLOBAL RANK={global_rank}\")\n",
141
+ "\n",
142
+ "world_size = os.getenv('WORLD_SIZE')\n",
143
+ "if world_size is None: \n",
144
+ " world_size = 1\n",
145
+ "else:\n",
146
+ " world_size = int(world_size)\n",
147
+ "print(f\"WORLD_SIZE={world_size}\")\n",
148
+ "\n",
149
+ "if utils.is_interactive():\n",
150
+ " # Following allows you to change functions in models.py or utils.py and \n",
151
+ " # have this notebook automatically update with your revisions\n",
152
+ " %load_ext autoreload\n",
153
+ " %autoreload 2\n",
154
+ "\n",
155
+ "batch_size = probe_batch_size\n",
156
+ "num_epochs = probe_num_epochs\n",
157
+ "hcp_flat_path = \"/weka/proj-medarc/shared/hcp_flat\"\n",
158
+ "\n",
159
+ "data_type = torch.float32 # change depending on your mixed_precision\n",
160
+ "global_batch_size = batch_size * world_size\n",
161
+ "\n",
162
+ "device = torch.device('cuda')\n",
163
+ "\n",
164
+ "print(\"PID of this process =\",os.getpid())\n",
165
+ "print(\"device =\", device, \"distributed =\",distributed, \"num_devices =\", num_devices, \"local rank =\", local_rank, \"world size =\", world_size, \"data_type =\", data_type)\n",
166
+ "\n",
167
+ "# seed all random functions\n",
168
+ "utils.seed_everything(seed + global_rank)"
169
+ ]
170
+ },
171
+ {
172
+ "cell_type": "markdown",
173
+ "id": "ab15aca0-148e-435f-b8f2-7a708b61a6d9",
174
+ "metadata": {},
175
+ "source": [
176
+ "# hcp_flat"
177
+ ]
178
+ },
179
+ {
180
+ "cell_type": "code",
181
+ "execution_count": 2,
182
+ "id": "de1a5b87-fa69-44e1-bdb9-bd9e257485c6",
183
+ "metadata": {
184
+ "tags": []
185
+ },
186
+ "outputs": [
187
+ {
188
+ "name": "stdout",
189
+ "output_type": "stream",
190
+ "text": [
191
+ "img_size (144, 320) patch_size (16, 16) frames 16 t_patch_size 2\n",
192
+ "model initialized\n"
193
+ ]
194
+ },
195
+ {
196
+ "data": {
197
+ "text/plain": [
198
+ "MaskedAutoencoderViT(\n",
199
+ " (patch_embed): PatchEmbed(\n",
200
+ " (proj): Conv3d(1, 384, kernel_size=(2, 16, 16), stride=(2, 16, 16))\n",
201
+ " )\n",
202
+ " (blocks): ModuleList(\n",
203
+ " (0-11): 12 x Block(\n",
204
+ " (norm1): LayerNorm((384,), eps=1e-06, elementwise_affine=True)\n",
205
+ " (attn): Attention(\n",
206
+ " (q): Linear(in_features=384, out_features=384, bias=True)\n",
207
+ " (k): Linear(in_features=384, out_features=384, bias=True)\n",
208
+ " (v): Linear(in_features=384, out_features=384, bias=True)\n",
209
+ " (proj): Linear(in_features=384, out_features=384, bias=True)\n",
210
+ " (proj_drop): Dropout(p=0.0, inplace=False)\n",
211
+ " )\n",
212
+ " (drop_path): Identity()\n",
213
+ " (norm2): LayerNorm((384,), eps=1e-06, elementwise_affine=True)\n",
214
+ " (mlp): Mlp(\n",
215
+ " (fc1): Linear(in_features=384, out_features=1536, bias=True)\n",
216
+ " (act): GELU(approximate='none')\n",
217
+ " (drop1): Dropout(p=0.0, inplace=False)\n",
218
+ " (norm): Identity()\n",
219
+ " (fc2): Linear(in_features=1536, out_features=384, bias=True)\n",
220
+ " (drop2): Dropout(p=0.0, inplace=False)\n",
221
+ " )\n",
222
+ " )\n",
223
+ " )\n",
224
+ " (norm): LayerNorm((384,), eps=1e-06, elementwise_affine=True)\n",
225
+ " (decoder_embed): Linear(in_features=384, out_features=512, bias=True)\n",
226
+ " (decoder_blocks): ModuleList(\n",
227
+ " (0-3): 4 x Block(\n",
228
+ " (norm1): LayerNorm((512,), eps=1e-06, elementwise_affine=True)\n",
229
+ " (attn): Attention(\n",
230
+ " (q): Linear(in_features=512, out_features=512, bias=True)\n",
231
+ " (k): Linear(in_features=512, out_features=512, bias=True)\n",
232
+ " (v): Linear(in_features=512, out_features=512, bias=True)\n",
233
+ " (proj): Linear(in_features=512, out_features=512, bias=True)\n",
234
+ " (proj_drop): Dropout(p=0.0, inplace=False)\n",
235
+ " )\n",
236
+ " (drop_path): Identity()\n",
237
+ " (norm2): LayerNorm((512,), eps=1e-06, elementwise_affine=True)\n",
238
+ " (mlp): Mlp(\n",
239
+ " (fc1): Linear(in_features=512, out_features=2048, bias=True)\n",
240
+ " (act): GELU(approximate='none')\n",
241
+ " (drop1): Dropout(p=0.0, inplace=False)\n",
242
+ " (norm): Identity()\n",
243
+ " (fc2): Linear(in_features=2048, out_features=512, bias=True)\n",
244
+ " (drop2): Dropout(p=0.0, inplace=False)\n",
245
+ " )\n",
246
+ " )\n",
247
+ " )\n",
248
+ " (decoder_norm): LayerNorm((512,), eps=1e-06, elementwise_affine=True)\n",
249
+ " (decoder_pred): Linear(in_features=512, out_features=256, bias=True)\n",
250
+ ")"
251
+ ]
252
+ },
253
+ "execution_count": 2,
254
+ "metadata": {},
255
+ "output_type": "execute_result"
256
+ }
257
+ ],
258
+ "source": [
259
+ "from util.hcp_flat import load_hcp_flat_mask\n",
260
+ "from util.hcp_flat import create_hcp_flat\n",
261
+ "import util.visualize as vis\n",
262
+ "\n",
263
+ "model = mae_vit_small_fmri(\n",
264
+ " patch_size=patch_size,\n",
265
+ " decoder_embed_dim=decoder_embed_dim,\n",
266
+ " t_patch_size=t_patch_size,\n",
267
+ " pred_t_dim=pred_t_dim,\n",
268
+ " decoder_depth=4,\n",
269
+ " cls_embed=cls_embed,\n",
270
+ " norm_pix_loss=norm_pix_loss,\n",
271
+ " no_qkv_bias=no_qkv_bias,\n",
272
+ " sep_pos_embed=sep_pos_embed,\n",
273
+ " trunc_init=trunc_init,\n",
274
+ ")\n",
275
+ "\n",
276
+ "model.eval()\n",
277
+ "model.requires_grad_(False)\n",
278
+ "model.to(device)"
279
+ ]
280
+ },
281
+ {
282
+ "cell_type": "markdown",
283
+ "id": "3dd51ddf-fb71-48f4-bdbd-88753b44d2aa",
284
+ "metadata": {},
285
+ "source": [
286
+ "## Create dataset and data loaders"
287
+ ]
288
+ },
289
+ {
290
+ "cell_type": "code",
291
+ "execution_count": 3,
292
+ "id": "3e330cd2-8f4f-4ca7-ae68-16698e90060f",
293
+ "metadata": {
294
+ "tags": []
295
+ },
296
+ "outputs": [
297
+ {
298
+ "name": "stdout",
299
+ "output_type": "stream",
300
+ "text": [
301
+ "changed batch_size to 64\n"
302
+ ]
303
+ }
304
+ ],
305
+ "source": [
306
+ "batch_size = 64 #12\n",
307
+ "print(f\"changed batch_size to {batch_size}\")\n",
308
+ "\n",
309
+ "## Train ##\n",
310
+ "train_dataset = create_hcp_flat(root=hcp_flat_path, \n",
311
+ " split=\"train\", frames=num_frames, \n",
312
+ " clip_mode=\"event\", shuffle=False)\n",
313
+ "train_dl = wds.WebLoader(\n",
314
+ " train_dataset.batched(batch_size, partial=False),\n",
315
+ " batch_size=None,\n",
316
+ " shuffle=False,\n",
317
+ " num_workers=num_workers,\n",
318
+ " pin_memory=True,\n",
319
+ ")\n",
320
+ "\n",
321
+ "## Test ##\n",
322
+ "test_dataset = create_hcp_flat(root=hcp_flat_path, \n",
323
+ " split=\"test\", frames=num_frames, \n",
324
+ " clip_mode=\"event\", shuffle=False)\n",
325
+ "test_dl = wds.WebLoader(\n",
326
+ " test_dataset.batched(batch_size, partial=False),\n",
327
+ " batch_size=None,\n",
328
+ " shuffle=False,\n",
329
+ " num_workers=num_workers,\n",
330
+ " pin_memory=True,\n",
331
+ ")"
332
+ ]
333
+ },
334
+ {
335
+ "cell_type": "markdown",
336
+ "id": "2b8e6baa-4b1c-4f38-b078-70b2b092d14d",
337
+ "metadata": {},
338
+ "source": [
339
+ "# Load checkpoint"
340
+ ]
341
+ },
342
+ {
343
+ "cell_type": "code",
344
+ "execution_count": 4,
345
+ "id": "4da73c08-ca61-48ef-9e63-b70db6f07a59",
346
+ "metadata": {
347
+ "tags": []
348
+ },
349
+ "outputs": [
350
+ {
351
+ "name": "stdout",
352
+ "output_type": "stream",
353
+ "text": [
354
+ "\n",
355
+ "Loaded checkpoint epoch99.pth from /weka/proj-fmri/paulscotti/fMRI-foundation-model/flat/checkpoints/ps16_mask9_3losses_bs32\n",
356
+ "\n"
357
+ ]
358
+ }
359
+ ],
360
+ "source": [
361
+ "if not os.path.exists(outdir) or not os.path.isdir(outdir):\n",
362
+ " print(f\"\\nCheckpoint folder {outdir} does not exist.\\n\")\n",
363
+ " err\n",
364
+ "else:\n",
365
+ " checkpoint_files = [f for f in os.listdir(outdir) if f.endswith('.pth')]\n",
366
+ "\n",
367
+ " # Extract epoch numbers and find the largest\n",
368
+ " epoch_numbers = []\n",
369
+ " for file in checkpoint_files:\n",
370
+ " try:\n",
371
+ " epoch_number = int(file.split('epoch')[-1].split('.')[0])\n",
372
+ " epoch_numbers.append(epoch_number)\n",
373
+ " except ValueError:\n",
374
+ " continue\n",
375
+ " latest_epoch = max(epoch_numbers)\n",
376
+ " latest_checkpoint = f\"epoch{latest_epoch}.pth\"\n",
377
+ " \n",
378
+ " # # Or specify epoch number \n",
379
+ " # latest_checkpoint = \"epoch15.pth\"\n",
380
+ "\n",
381
+ " # Load the checkpoint\n",
382
+ " checkpoint_path = os.path.join(outdir, latest_checkpoint)\n",
383
+ "\n",
384
+ " state = torch.load(checkpoint_path)\n",
385
+ " model.load_state_dict(state[\"model_state_dict\"], strict=True)\n",
386
+ "\n",
387
+ " print(f\"\\nLoaded checkpoint {latest_checkpoint} from {outdir}\\n\")"
388
+ ]
389
+ },
390
+ {
391
+ "cell_type": "markdown",
392
+ "id": "c43a5055-8afd-468a-93bf-32f94bd1d042",
393
+ "metadata": {},
394
+ "source": [
395
+ "# Start extraction"
396
+ ]
397
+ },
398
+ {
399
+ "cell_type": "code",
400
+ "execution_count": 5,
401
+ "id": "bf1e2df3-6c55-4218-89ea-348c5aa4f8b8",
402
+ "metadata": {
403
+ "tags": []
404
+ },
405
+ "outputs": [
406
+ {
407
+ "name": "stdout",
408
+ "output_type": "stream",
409
+ "text": [
410
+ "/weka/proj-fmri/paulscotti/fMRI-foundation-model/flat/checkpoints/ps16_mask9_3losses_bs32/epoch99\n"
411
+ ]
412
+ }
413
+ ],
414
+ "source": [
415
+ "outdir_parquet = f'{outdir}/{latest_checkpoint[:-4]}'\n",
416
+ "print(outdir_parquet)\n",
417
+ "\n",
418
+ "os.makedirs(outdir_parquet,exist_ok=True)"
419
+ ]
420
+ },
421
+ {
422
+ "cell_type": "code",
423
+ "execution_count": 6,
424
+ "id": "1e1bbe95-5fbf-4f8d-bc7a-17d5983a94d3",
425
+ "metadata": {},
426
+ "outputs": [],
427
+ "source": [
428
+ "# for i, (samples, samples_meta) in enumerate(tqdm(train_dl)):\n",
429
+ "# samples = samples.to(device, non_blocking=True)\n",
430
+ "## using batch_size=12: 49284it [14:03, 58.44it/s]"
431
+ ]
432
+ },
433
+ {
434
+ "cell_type": "code",
435
+ "execution_count": 7,
436
+ "id": "6507067f-1ece-46ac-ad8c-24ef7a9b3a58",
437
+ "metadata": {
438
+ "tags": []
439
+ },
440
+ "outputs": [],
441
+ "source": [
442
+ "@torch.no_grad()\n",
443
+ "def extract_features(dl):\n",
444
+ " for samples, samples_meta in tqdm(dl,total=49284):\n",
445
+ " samples = samples.to(device, non_blocking=True)\n",
446
+ "\n",
447
+ " features = model.forward_features(samples)\n",
448
+ "\n",
449
+ " features = features.cpu().numpy()\n",
450
+ "\n",
451
+ " for feat, meta in zip(features, samples_meta):\n",
452
+ " yield {\"feature\": feat, **meta}"
453
+ ]
454
+ },
455
+ {
456
+ "cell_type": "code",
457
+ "execution_count": 8,
458
+ "id": "c3461199-e805-4e9c-8c91-894e83cf8bc3",
459
+ "metadata": {
460
+ "tags": []
461
+ },
462
+ "outputs": [
463
+ {
464
+ "name": "stdout",
465
+ "output_type": "stream",
466
+ "text": [
467
+ "Start extract\n"
468
+ ]
469
+ },
470
+ {
471
+ "name": "stderr",
472
+ "output_type": "stream",
473
+ "text": [
474
+ " 0%|▏ | 197/49284 [00:29<2:03:32, 6.62it/s]\n"
475
+ ]
476
+ },
477
+ {
478
+ "name": "stdout",
479
+ "output_type": "stream",
480
+ "text": [
481
+ "Extract time 0:00:29\n",
482
+ "389945344\n"
483
+ ]
484
+ }
485
+ ],
486
+ "source": [
487
+ "utils.seed_everything(seed)\n",
488
+ "\n",
489
+ "print(\"Start extract\")\n",
490
+ "start_time = time.time()\n",
491
+ "\n",
492
+ "with BufferedParquetWriter(f\"{outdir_parquet}/test.parquet\", blocking=True) as writer:\n",
493
+ " for sample in extract_features(test_dl):\n",
494
+ " writer.write(sample)\n",
495
+ "\n",
496
+ "total_time = time.time() - start_time\n",
497
+ "total_time_str = str(datetime.timedelta(seconds=int(total_time)))\n",
498
+ "print(\"Extract time {}\".format(total_time_str))\n",
499
+ "print(torch.cuda.memory_allocated())"
500
+ ]
501
+ },
502
+ {
503
+ "cell_type": "code",
504
+ "execution_count": 9,
505
+ "id": "a898429c-b013-4989-86ef-fbf75ca15971",
506
+ "metadata": {
507
+ "tags": []
508
+ },
509
+ "outputs": [
510
+ {
511
+ "name": "stdout",
512
+ "output_type": "stream",
513
+ "text": [
514
+ "Start extract\n"
515
+ ]
516
+ },
517
+ {
518
+ "name": "stderr",
519
+ "output_type": "stream",
520
+ "text": [
521
+ " 4%|█▎ | 1849/49284 [03:59<1:42:32, 7.71it/s]\n"
522
+ ]
523
+ },
524
+ {
525
+ "name": "stdout",
526
+ "output_type": "stream",
527
+ "text": [
528
+ "Extract time 0:03:59\n",
529
+ "389945344\n"
530
+ ]
531
+ }
532
+ ],
533
+ "source": [
534
+ "utils.seed_everything(seed)\n",
535
+ "\n",
536
+ "print(\"Start extract\")\n",
537
+ "start_time = time.time()\n",
538
+ "\n",
539
+ "with BufferedParquetWriter(f\"{outdir_parquet}/train.parquet\", blocking=True) as writer:\n",
540
+ " for sample in extract_features(train_dl):\n",
541
+ " writer.write(sample)\n",
542
+ "\n",
543
+ "total_time = time.time() - start_time\n",
544
+ "total_time_str = str(datetime.timedelta(seconds=int(total_time)))\n",
545
+ "print(\"Extract time {}\".format(total_time_str))\n",
546
+ "print(torch.cuda.memory_allocated())"
547
+ ]
548
+ },
549
+ {
550
+ "cell_type": "code",
551
+ "execution_count": null,
552
+ "id": "6ffb97e6-c61c-4026-82fb-b1729f51c40a",
553
+ "metadata": {},
554
+ "outputs": [],
555
+ "source": []
556
+ },
557
+ {
558
+ "cell_type": "code",
559
+ "execution_count": null,
560
+ "id": "2404f355-090f-48b7-8b04-5c76e7bb7e04",
561
+ "metadata": {},
562
+ "outputs": [],
563
+ "source": []
564
+ }
565
+ ],
566
+ "metadata": {
567
+ "kernelspec": {
568
+ "display_name": "found",
569
+ "language": "python",
570
+ "name": "found"
571
+ },
572
+ "language_info": {
573
+ "codemirror_mode": {
574
+ "name": "ipython",
575
+ "version": 3
576
+ },
577
+ "file_extension": ".py",
578
+ "mimetype": "text/x-python",
579
+ "name": "python",
580
+ "nbconvert_exporter": "python",
581
+ "pygments_lexer": "ipython3",
582
+ "version": "3.10.14"
583
+ }
584
+ },
585
+ "nbformat": 4,
586
+ "nbformat_minor": 5
587
+ }
fMRI-foundation-model-old/flat/unique_tasks.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dcb37cf8032fa5fc0f90f567deeaa92890c59581d1710dd33da51da9fc2ebb53
3
+ size 688
fMRI-foundation-model-old/flat/util/.ipynb_checkpoints/hcp_flat-checkpoint.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+ from functools import partial
4
+ from pathlib import Path
5
+ from typing import Any, Dict, Iterable, Optional, Tuple, Union
6
+
7
+ import numpy as np
8
+ import torch
9
+ import webdataset as wds
10
+ from torch.utils.data import IterableDataset
11
+
12
+ HCP_FLAT_ROOT = "https://huggingface.co/datasets/bold-ai/HCP-Flat/resolve/main"
13
+ NUM_SHARDS = {"train": 1629, "test": 174}
14
+
15
+
16
+ def create_hcp_flat(
17
+ root: Optional[str] = None,
18
+ training: bool = True,
19
+ shards: Optional[Union[int, Iterable[int]]] = None,
20
+ frames: int = 16,
21
+ cache_dir: Optional[str] = None,
22
+ ) -> wds.WebDataset:
23
+ """
24
+ Create HCP-Flat dataset. Yields samples of (key, images) where key is the webdataset
25
+ sample key and images is shape (C, T, H, W).
26
+
27
+ References:
28
+ https://github.com/webdataset/webdataset/issues/250#issuecomment-1454094496
29
+ https://github.com/tmbdev-archive/webdataset-imagenet-2/blob/main/imagenet.py
30
+ https://github.com/huggingface/pytorch-image-models/blob/main/timm/data/readers/reader_wds.py
31
+ """
32
+ root = root or os.environ.get("HCP_FLAT_ROOT") or HCP_FLAT_ROOT
33
+ split = "train" if training else "test"
34
+
35
+ shards = shards or NUM_SHARDS[split]
36
+ if isinstance(shards, int):
37
+ shards = range(shards)
38
+ assert (
39
+ min(shards) >= 0 and max(shards) < NUM_SHARDS[split]
40
+ ), f"Invalid shards {shards}; expected in [0, {NUM_SHARDS[split]})"
41
+
42
+ urls = [f"{root}/{split}/hcp-flat_{split}_{shard:06d}.tar" for shard in shards]
43
+
44
+ # In training, we resample shards with replacement independently in every worker and
45
+ # yield batches up to the target number of samples. In test, we iterate over the
46
+ # shards in order, with workers getting interleaving shards, and yield batches up to
47
+ # the target samples. In a distributed setting with variable size shards, setting a
48
+ # fixed number of samples is the easiest way to get balanced batches per worker. In
49
+ # training we will still see all data eventually. But in test, it means we cut off
50
+ # some data.
51
+
52
+ # Note that in training this does not do deterministic shuffling, which we would
53
+ # need for exact reproducibility. They get determistic shuffling in timm, but it's
54
+ # more complicated.
55
+
56
+ # Note that we are splitting the long timeseries into clips after shuffling, which
57
+ # means clips from the same series will appear consecutively in the batch(es).
58
+ # I think this is not too bad. It is basically equivalent to training on longer
59
+ # sequences. Clipping before shuffling might be preferred, but it results in a bad
60
+ # system memory leak (https://github.com/webdataset/webdataset/issues/354).
61
+ dataset = (
62
+ wds.WebDataset(
63
+ urls,
64
+ resampled=training,
65
+ shardshuffle=1000 if training else False,
66
+ nodesplitter=wds.split_by_node,
67
+ select_files=select_extensions(("bold.npy","meta.json")),
68
+ cache_dir=cache_dir,
69
+ )
70
+ .shuffle(1000 if training else 0)
71
+ .decode()
72
+ .map(partial(extract_images, mask=load_hcp_flat_mask()))
73
+ .compose(to_clips(frames))
74
+ )
75
+ return dataset
76
+
77
+
78
+ def select_extensions(extensions: Tuple[str, ...]):
79
+ extensions_set = set(extensions)
80
+ def select_files(fname: str):
81
+ suffix = ".".join(fname.split(".")[1:])
82
+ return suffix in extensions_set
83
+ return select_files
84
+
85
+
86
+ def extract_images(sample: Dict[str, Any], mask: torch.Tensor):
87
+ key = sample["__key__"]
88
+ images = sample["bold.npy"]
89
+ meta = sample["meta.json"]['task']
90
+
91
+ images = torch.from_numpy(images) / 255.0
92
+ images = (images - 0.5) / 0.2
93
+ images = unmask(images, mask)
94
+ # (C, T, H, W,)
95
+ images = images.unsqueeze(0)
96
+
97
+ return key, images, meta
98
+
99
+
100
+ def unmask(images: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
101
+ images_unmasked = torch.zeros(
102
+ (images.shape[0], *mask.shape), dtype=images.dtype, device=images.device
103
+ )
104
+ images_unmasked[:, mask] = images
105
+ return images_unmasked
106
+
107
+
108
+ def to_clips(frames: int = 16):
109
+ def _filter(src: IterableDataset[Tuple[str, torch.Tensor, str]]):
110
+ for key, images, meta in src:
111
+ offset = random.randint(0, frames)
112
+ for start in range(offset, images.shape[1] - frames, frames):
113
+ yield key, images[:, start : start + frames], meta
114
+ return _filter
115
+
116
+
117
+ def load_hcp_flat_mask() -> torch.Tensor:
118
+ mask = np.load(Path(__file__).parents[1] / "hcp-flat_mask.npy")
119
+ mask = torch.as_tensor(mask)
120
+ return mask
fMRI-foundation-model-old/flat/util/.ipynb_checkpoints/misc-checkpoint.py ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ # --------------------------------------------------------
7
+ # References:
8
+ # DeiT: https://github.com/facebookresearch/deit
9
+ # BEiT: https://github.com/microsoft/unilm/tree/master/beit
10
+ # --------------------------------------------------------
11
+
12
+ import builtins
13
+ import datetime
14
+ import math
15
+ import os
16
+ import time
17
+ from collections import defaultdict, deque, OrderedDict
18
+
19
+ import util.logging as logging
20
+ import psutil
21
+ import torch
22
+ import torch.distributed as dist
23
+ from iopath.common.file_io import g_pathmgr as pathmgr
24
+ from util.logging import master_print as print
25
+ from torch import inf
26
+
27
+
28
+ logger = logging.get_logger(__name__)
29
+
30
+
31
+ class SmoothedValue:
32
+ """Track a series of values and provide access to smoothed values over a
33
+ window or the global series average.
34
+ """
35
+
36
+ def __init__(self, window_size=20, fmt=None):
37
+ if fmt is None:
38
+ fmt = "{median:.4f} ({global_avg:.4f})"
39
+ self.deque = deque(maxlen=window_size)
40
+ self.total = 0.0
41
+ self.count = 0
42
+ self.fmt = fmt
43
+
44
+ def update(self, value, n=1):
45
+ self.deque.append(value)
46
+ self.count += n
47
+ self.total += value * n
48
+
49
+ def synchronize_between_processes(self):
50
+ """
51
+ Warning: does not synchronize the deque!
52
+ """
53
+ if not is_dist_avail_and_initialized():
54
+ return
55
+ t = torch.tensor([self.count, self.total], dtype=torch.float64, device="cuda")
56
+ dist.barrier()
57
+ dist.all_reduce(t)
58
+ t = t.tolist()
59
+ self.count = int(t[0])
60
+ self.total = t[1]
61
+
62
+ @property
63
+ def median(self):
64
+ d = torch.tensor(list(self.deque))
65
+ return d.median().item()
66
+
67
+ @property
68
+ def avg(self):
69
+ d = torch.tensor(list(self.deque), dtype=torch.float32)
70
+ return d.mean().item()
71
+
72
+ @property
73
+ def global_avg(self):
74
+ return self.total / self.count
75
+
76
+ @property
77
+ def max(self):
78
+ return max(self.deque)
79
+
80
+ @property
81
+ def value(self):
82
+ return self.deque[-1]
83
+
84
+ def __str__(self):
85
+ return self.fmt.format(
86
+ median=self.median,
87
+ avg=self.avg,
88
+ global_avg=self.global_avg,
89
+ max=self.max,
90
+ value=self.value,
91
+ )
92
+
93
+
94
+ class MetricLogger:
95
+ def __init__(self, delimiter="\t"):
96
+ self.meters = defaultdict(SmoothedValue)
97
+ self.delimiter = delimiter
98
+
99
+ def update(self, **kwargs):
100
+ for k, v in kwargs.items():
101
+ if v is None:
102
+ continue
103
+ if isinstance(v, torch.Tensor):
104
+ v = v.item()
105
+ assert isinstance(v, (float, int))
106
+ self.meters[k].update(v)
107
+
108
+ def __getattr__(self, attr):
109
+ if attr in self.meters:
110
+ return self.meters[attr]
111
+ if attr in self.__dict__:
112
+ return self.__dict__[attr]
113
+ raise AttributeError(
114
+ "'{}' object has no attribute '{}'".format(type(self).__name__, attr)
115
+ )
116
+
117
+ def __str__(self):
118
+ loss_str = []
119
+ for name, meter in self.meters.items():
120
+ loss_str.append("{}: {}".format(name, str(meter)))
121
+ return self.delimiter.join(loss_str)
122
+
123
+ def synchronize_between_processes(self):
124
+ for meter in self.meters.values():
125
+ meter.synchronize_between_processes()
126
+
127
+ def add_meter(self, name, meter):
128
+ self.meters[name] = meter
129
+
130
+ def log_every(self, iterable, print_freq, header=None, total_steps=None):
131
+ i = 0
132
+ total_steps = total_steps or len(iterable)
133
+ if not header:
134
+ header = ""
135
+ start_time = time.time()
136
+ end = time.time()
137
+ iter_time = SmoothedValue(fmt="{avg:.4f}")
138
+ data_time = SmoothedValue(fmt="{avg:.4f}")
139
+ space_fmt = ":" + str(len(str(total_steps))) + "d"
140
+ log_msg = [
141
+ header,
142
+ "[{0" + space_fmt + "}/{1}]",
143
+ "eta: {eta}",
144
+ "{meters}",
145
+ "time: {time}",
146
+ "data: {data}",
147
+ ]
148
+ if torch.cuda.is_available():
149
+ log_msg.append("max mem: {memory:.0f}")
150
+ log_msg = self.delimiter.join(log_msg)
151
+ MB = 1024.0 * 1024.0
152
+ for obj in iterable:
153
+ data_time.update(time.time() - end)
154
+ yield obj
155
+ iter_time.update(time.time() - end)
156
+ if i % print_freq == 0 or i == total_steps - 1:
157
+ eta_seconds = iter_time.global_avg * (total_steps - i)
158
+ eta_string = str(datetime.timedelta(seconds=int(eta_seconds)))
159
+ if torch.cuda.is_available():
160
+ print(
161
+ log_msg.format(
162
+ i,
163
+ total_steps,
164
+ eta=eta_string,
165
+ meters=str(self),
166
+ time=str(iter_time),
167
+ data=str(data_time),
168
+ memory=torch.cuda.max_memory_allocated() / MB,
169
+ )
170
+ )
171
+
172
+ else:
173
+ print(
174
+ log_msg.format(
175
+ i,
176
+ total_steps,
177
+ eta=eta_string,
178
+ meters=str(self),
179
+ time=str(iter_time),
180
+ data=str(data_time),
181
+ )
182
+ )
183
+ i += 1
184
+ end = time.time()
185
+ total_time = time.time() - start_time
186
+ total_time_str = str(datetime.timedelta(seconds=int(total_time)))
187
+ print(
188
+ "{} Total time: {} ({:.4f} s / it)".format(
189
+ header, total_time_str, total_time / total_steps
190
+ )
191
+ )
192
+
193
+
194
+ def setup_for_distributed(is_master):
195
+ """
196
+ This function disables printing when not in master process
197
+ """
198
+ builtin_print = builtins.print
199
+
200
+ def print(*args, **kwargs):
201
+ force = kwargs.pop("force", False)
202
+ force = force or (get_world_size() > 8)
203
+ if is_master or force:
204
+ now = datetime.datetime.now().time()
205
+ builtin_print("[{}] ".format(now), end="") # print with time stamp
206
+ builtin_print(*args, **kwargs)
207
+
208
+ builtins.print = print
209
+
210
+
211
+ def is_dist_avail_and_initialized():
212
+ if not dist.is_available():
213
+ return False
214
+ if not dist.is_initialized():
215
+ return False
216
+ return True
217
+
218
+
219
+ def get_world_size():
220
+ if not is_dist_avail_and_initialized():
221
+ return 1
222
+ return dist.get_world_size()
223
+
224
+
225
+ def get_rank():
226
+ if not is_dist_avail_and_initialized():
227
+ return 0
228
+ return dist.get_rank()
229
+
230
+
231
+ def is_main_process():
232
+ return get_rank() == 0
233
+
234
+
235
+ def save_on_master(state, path):
236
+ if is_main_process():
237
+ print(f"save path {path}")
238
+ with pathmgr.open(path, "wb") as f:
239
+ torch.save(state, f)
240
+
241
+
242
+ def init_distributed_mode(args):
243
+ if args.no_env:
244
+ pass
245
+ elif args.dist_on_itp:
246
+ args.rank = int(os.environ["OMPI_COMM_WORLD_RANK"])
247
+ args.world_size = int(os.environ["OMPI_COMM_WORLD_SIZE"])
248
+ args.gpu = int(os.environ["OMPI_COMM_WORLD_LOCAL_RANK"])
249
+ args.dist_url = "tcp://%s:%s" % (
250
+ os.environ["MASTER_ADDR"],
251
+ os.environ["MASTER_PORT"],
252
+ )
253
+ os.environ["LOCAL_RANK"] = str(args.gpu)
254
+ os.environ["RANK"] = str(args.rank)
255
+ os.environ["WORLD_SIZE"] = str(args.world_size)
256
+ # ["RANK", "WORLD_SIZE", "MASTER_ADDR", "MASTER_PORT", "LOCAL_RANK"]
257
+ elif "RANK" in os.environ and "WORLD_SIZE" in os.environ:
258
+ args.rank = int(os.environ["RANK"])
259
+ args.world_size = int(os.environ["WORLD_SIZE"])
260
+ args.gpu = int(os.environ["LOCAL_RANK"])
261
+ elif "SLURM_PROCID" in os.environ:
262
+ args.rank = int(os.environ["SLURM_PROCID"])
263
+ args.gpu = args.rank % torch.cuda.device_count()
264
+ else:
265
+ print("Not using distributed mode")
266
+ setup_for_distributed(is_master=True) # hack
267
+ args.distributed = False
268
+ return
269
+
270
+ args.distributed = True
271
+
272
+ torch.cuda.set_device(args.gpu)
273
+ args.dist_backend = "nccl"
274
+ print(
275
+ "| distributed init (rank {}): {}, gpu {}".format(
276
+ args.rank, args.dist_url, args.gpu
277
+ ),
278
+ # flush=True,
279
+ )
280
+ torch.distributed.init_process_group(
281
+ backend=args.dist_backend,
282
+ world_size=args.world_size,
283
+ rank=args.rank,
284
+ )
285
+ # init_method=args.dist_url,
286
+ torch.distributed.barrier()
287
+ setup_for_distributed(args.rank == 0)
288
+
289
+
290
+ class NativeScalerWithGradNormCount:
291
+ state_dict_key = "amp_scaler"
292
+
293
+ def __init__(self, fp32=False):
294
+ self._scaler = torch.cuda.amp.GradScaler(enabled=not fp32)
295
+
296
+ def __call__(
297
+ self,
298
+ loss,
299
+ optimizer,
300
+ clip_grad=None,
301
+ parameters=None,
302
+ create_graph=False,
303
+ update_grad=True,
304
+ ):
305
+ self._scaler.scale(loss).backward(create_graph=create_graph)
306
+ if update_grad:
307
+ if clip_grad is not None:
308
+ assert parameters is not None
309
+ self._scaler.unscale_(
310
+ optimizer
311
+ ) # unscale the gradients of optimizer's assigned params in-place
312
+ norm = torch.nn.utils.clip_grad_norm_(parameters, clip_grad)
313
+ else:
314
+ self._scaler.unscale_(optimizer)
315
+ norm = get_grad_norm_(parameters)
316
+ self._scaler.step(optimizer)
317
+ self._scaler.update()
318
+ else:
319
+ norm = None
320
+ return norm
321
+
322
+ def state_dict(self):
323
+ return self._scaler.state_dict()
324
+
325
+ def load_state_dict(self, state_dict):
326
+ self._scaler.load_state_dict(state_dict)
327
+
328
+
329
+ def get_grad_norm_(parameters, norm_type: float = 2.0) -> torch.Tensor:
330
+ if isinstance(parameters, torch.Tensor):
331
+ parameters = [parameters]
332
+ parameters = [p for p in parameters if p.grad is not None]
333
+ norm_type = float(norm_type)
334
+ if len(parameters) == 0:
335
+ return torch.tensor(0.0)
336
+ device = parameters[0].grad.device
337
+ if norm_type == inf:
338
+ total_norm = max(p.grad.detach().abs().max().to(device) for p in parameters)
339
+ else:
340
+ total_norm = torch.norm(
341
+ torch.stack(
342
+ [torch.norm(p.grad.detach(), norm_type).to(device) for p in parameters]
343
+ ),
344
+ norm_type,
345
+ )
346
+ return total_norm
347
+
348
+
349
+ def save_model(args, epoch, model, model_without_ddp, optimizer, loss_scaler):
350
+ checkpoint_path = "{}/checkpoint-{:05d}.pth".format(args.output_dir, epoch)
351
+ to_save = {
352
+ "model": model_without_ddp.state_dict(),
353
+ "optimizer": optimizer.state_dict(),
354
+ "epoch": epoch,
355
+ "scaler": loss_scaler.state_dict(),
356
+ "args": args,
357
+ }
358
+
359
+ save_on_master(to_save, checkpoint_path)
360
+ return checkpoint_path
361
+
362
+
363
+ def get_last_checkpoint(args):
364
+ """
365
+ Get the last checkpoint from the checkpointing folder.
366
+ Args:
367
+ path_to_job (string): the path to the folder of the current job.
368
+ """
369
+ d = args.output_dir
370
+ names = pathmgr.ls(d) if pathmgr.exists(d) else []
371
+ names = [f for f in names if "checkpoint" in f]
372
+ if len(names) == 0:
373
+ print("No checkpoints found in '{}'.".format(d))
374
+ return None
375
+ else:
376
+ # Sort the checkpoints by epoch.
377
+ name = sorted(names)[-1]
378
+ return os.path.join(d, name)
379
+
380
+
381
+ def load_model(args, model_without_ddp, optimizer, loss_scaler):
382
+ if not args.resume:
383
+ args.resume = get_last_checkpoint(args)
384
+ if args.resume:
385
+ if args.resume.startswith("https"):
386
+ checkpoint = torch.hub.load_state_dict_from_url(
387
+ args.resume, map_location="cpu", check_hash=True
388
+ )
389
+ else:
390
+ with pathmgr.open(args.resume, "rb") as f:
391
+ checkpoint = torch.load(f, map_location="cpu")
392
+ model_without_ddp.load_state_dict(checkpoint["model"])
393
+ print("Resume checkpoint %s" % args.resume)
394
+ if (
395
+ "optimizer" in checkpoint
396
+ and "epoch" in checkpoint
397
+ and not (hasattr(args, "eval") and args.eval)
398
+ ):
399
+ optimizer.load_state_dict(checkpoint["optimizer"])
400
+ args.start_epoch = checkpoint["epoch"] + 1
401
+ if "scaler" in checkpoint:
402
+ loss_scaler.load_state_dict(checkpoint["scaler"])
403
+ print("With optim & sched!")
404
+
405
+
406
+ def all_reduce_mean(x):
407
+ world_size = get_world_size()
408
+ if world_size > 1:
409
+ x_reduce = torch.tensor(x).cuda()
410
+ dist.all_reduce(x_reduce)
411
+ x_reduce /= world_size
412
+ return x_reduce.item()
413
+ else:
414
+ return x
415
+
416
+
417
+ def gpu_mem_usage():
418
+ """
419
+ Compute the GPU memory usage for the current device (GB).
420
+ """
421
+ if torch.cuda.is_available():
422
+ mem_usage_bytes = torch.cuda.max_memory_allocated()
423
+ else:
424
+ mem_usage_bytes = 0
425
+ return mem_usage_bytes / 1024**3
426
+
427
+
428
+ def cpu_mem_usage():
429
+ """
430
+ Compute the system memory (RAM) usage for the current device (GB).
431
+ Returns:
432
+ usage (float): used memory (GB).
433
+ total (float): total memory (GB).
434
+ """
435
+ vram = psutil.virtual_memory()
436
+ usage = (vram.total - vram.available) / 1024**3
437
+ total = vram.total / 1024**3
438
+
439
+ return usage, total
440
+
441
+
442
+ def all_gather(tensors):
443
+ """
444
+ All gathers the provided tensors from all processes across machines.
445
+ Args:
446
+ tensors (list): tensors to perform all gather across all processes in
447
+ all machines.
448
+ """
449
+
450
+ gather_list = []
451
+ output_tensor = []
452
+ world_size = dist.get_world_size()
453
+ for tensor in tensors:
454
+ tensor_placeholder = [torch.ones_like(tensor) for _ in range(world_size)]
455
+ dist.all_gather(tensor_placeholder, tensor, async_op=False)
456
+ gather_list.append(tensor_placeholder)
457
+ for gathered_tensor in gather_list:
458
+ output_tensor.append(torch.cat(gathered_tensor, dim=0))
459
+ return output_tensor
460
+
461
+
462
+ def add_weight_decay(model, weight_decay=1e-5, skip_list=(), bias_wd=False):
463
+ decay = []
464
+ no_decay = []
465
+ for name, param in model.named_parameters():
466
+ if not param.requires_grad:
467
+ continue # frozen weights
468
+ if (
469
+ (not bias_wd)
470
+ and len(param.shape) == 1
471
+ or name.endswith(".bias")
472
+ or name in skip_list
473
+ ):
474
+ no_decay.append(param)
475
+ else:
476
+ decay.append(param)
477
+ return [
478
+ {"params": no_decay, "weight_decay": 0.0},
479
+ {"params": decay, "weight_decay": weight_decay},
480
+ ]
481
+
482
+
483
+ def inflate(model_2d, model_3d):
484
+ state_dict_inflated = OrderedDict()
485
+ for k, v2d in model_2d.items():
486
+ if "patch_embed.proj.weight" in k:
487
+ v3d = model_3d[k]
488
+ v3d = v2d.unsqueeze(2).repeat(1, 1, v3d.shape[2], 1, 1) / v3d.shape[2]
489
+ state_dict_inflated[k] = v3d.clone()
490
+ elif "pos_embed" in k:
491
+ pos_embed_cls, pos_embed_spatial = torch.split(v2d, [1, 196], dim=1)
492
+ state_dict_inflated["pos_embed_cls"] = pos_embed_cls.clone()
493
+ state_dict_inflated["pos_embed"] = pos_embed_spatial.clone()
494
+ else:
495
+ state_dict_inflated[k] = v2d.clone()
496
+ return state_dict_inflated
497
+
498
+
499
+ def convert_checkpoint(model_2d):
500
+ state_dict_inflated = OrderedDict()
501
+ for k, v2d in model_2d.items():
502
+ if "head.projection.weight" in k:
503
+ state_dict_inflated["head.weight"] = v2d.clone()
504
+ elif "head.projection.bias" in k:
505
+ state_dict_inflated["head.bias"] = v2d.clone()
506
+ else:
507
+ state_dict_inflated[k] = v2d.clone()
508
+ return state_dict_inflated
fMRI-foundation-model-old/flat/util/.ipynb_checkpoints/visualize-checkpoint.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ from typing import Any, Optional
3
+
4
+ import torch
5
+ from matplotlib import pyplot as plt
6
+ from matplotlib.figure import Figure
7
+ from PIL import Image
8
+
9
+ plt.rcParams["figure.dpi"] = 150
10
+
11
+
12
+ def plot_mask_pred(
13
+ model: torch.nn.Module,
14
+ imgs: torch.Tensor,
15
+ pred: torch.Tensor,
16
+ mask: torch.Tensor,
17
+ mean: Optional[Any] = None,
18
+ std: Optional[Any] = None,
19
+ nrow: int = 8,
20
+ norm_pix_loss: bool = False,
21
+ ):
22
+ # imgs: [N, C, T, H, W]
23
+ # pred: [N, t*h*w, u*p*p*C]
24
+ # mask: [N, t*h*w], 0 is keep, 1 is remove,
25
+ target = torch.index_select(
26
+ imgs,
27
+ 2,
28
+ torch.linspace(
29
+ 0,
30
+ imgs.shape[2] - 1,
31
+ model.pred_t_dim,
32
+ )
33
+ .long()
34
+ .to(imgs.device),
35
+ )
36
+ if norm_pix_loss:
37
+ target_mean = target.mean(dim=-1, keepdim=True)
38
+ target_var = target.var(dim=-1, keepdim=True)
39
+ target = torch.einsum("ncthw->nthwc", target)
40
+ target = target.flatten(0, 1)[:nrow].cpu()
41
+
42
+ pred = pred.detach()
43
+ if norm_pix_loss:
44
+ pred = (pred * target_var) + target_mean
45
+ pred = model.unpatchify(pred)
46
+ pred = torch.einsum("ncthw->nthwc", pred).cpu()
47
+ pred = pred.flatten(0, 1)[:nrow].cpu()
48
+
49
+ mask = mask.unsqueeze(-1).repeat(
50
+ 1, 1, model.patch_embed.patch_size[0]**2 * imgs.shape[1]
51
+ ) # (N, T*H*W, p*p*c)
52
+ mask = model.unpatchify(mask) # 1 is removing, 0 is keeping
53
+ mask = torch.einsum("ncthw->nthwc", mask).cpu()
54
+ mask = mask.flatten(0, 1)[:nrow].cpu()
55
+
56
+ # masked image
57
+ im_masked = target * (1 - mask)
58
+
59
+ # MAE reconstruction pasted with visible patches
60
+ im_paste = target * (1 - mask) + pred * mask
61
+
62
+ if model.img_mask is not None:
63
+ img_mask = model.img_mask.cpu()
64
+ else:
65
+ img_mask = None
66
+
67
+ H, W = target.shape[1:3]
68
+ ploth = 2.0
69
+ plotw = (W / H) * ploth
70
+ nrow = len(target)
71
+ ncol = 3
72
+ fig, axs = plt.subplots(
73
+ nrow, ncol, figsize=(plotw * ncol, ploth * nrow), squeeze=False
74
+ )
75
+
76
+ for ii in range(nrow):
77
+ plt.sca(axs[ii, 0])
78
+ imshow(im_masked[ii], mean=mean, std=std, mask=img_mask)
79
+
80
+ plt.sca(axs[ii, 1])
81
+ imshow(im_paste[ii], mean=mean, std=std, mask=img_mask)
82
+
83
+ plt.sca(axs[ii, 2])
84
+ imshow(target[ii], mean=mean, std=std, mask=img_mask)
85
+
86
+ plt.tight_layout(pad=0.25)
87
+ return fig
88
+
89
+
90
+ def imshow(
91
+ image: torch.Tensor,
92
+ mean: Optional[Any] = None,
93
+ std: Optional[Any] = None,
94
+ mask: Optional[torch.Tensor] = None,
95
+ **kwargs,
96
+ ):
97
+ # image: (H, W, C)
98
+ assert image.shape[2] in (1, 3)
99
+ if image.shape[2] == 1:
100
+ kwargs = {
101
+ "cmap": "gray",
102
+ "vmin": 0.0,
103
+ "vmax": 1.0,
104
+ "interpolation": "nearest",
105
+ **kwargs,
106
+ }
107
+ if mean is not None:
108
+ mean = torch.as_tensor(mean)
109
+ std = torch.as_tensor(std)
110
+ image = torch.clip(image * std + mean, 0.0, 1.0)
111
+ if mask is not None:
112
+ image = mask.unsqueeze(-1) * image
113
+ plt.imshow(image, **kwargs)
114
+ plt.axis("off")
115
+
116
+
117
+ def fig2pil(fig: Figure, format: str = "png") -> Image.Image:
118
+ with io.BytesIO() as f:
119
+ fig.savefig(f, format=format)
120
+ f.seek(0)
121
+ img = Image.open(f)
122
+ img.load()
123
+ return img
fMRI-foundation-model-old/flat/util/__pycache__/hcp_flat.cpython-310.pyc ADDED
Binary file (3.9 kB). View file
 
fMRI-foundation-model-old/flat/util/__pycache__/logging.cpython-310.pyc ADDED
Binary file (3.38 kB). View file
 
fMRI-foundation-model-old/flat/util/__pycache__/losses.cpython-310.pyc ADDED
Binary file (4.24 kB). View file
 
fMRI-foundation-model-old/flat/util/__pycache__/video_vit.cpython-310.pyc ADDED
Binary file (4.21 kB). View file
 
fMRI-foundation-model-old/flat/util/__pycache__/visualize.cpython-310.pyc ADDED
Binary file (2.57 kB). View file
 
fMRI-foundation-model-old/flat/util/hcp_flat.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+ from functools import partial
4
+ from pathlib import Path
5
+ from typing import Any, Dict, Iterable, Optional, Tuple, Union
6
+
7
+ import numpy as np
8
+ import torch
9
+ import webdataset as wds
10
+ from torch.utils.data import IterableDataset
11
+
12
+ HCP_FLAT_ROOT = "https://huggingface.co/datasets/bold-ai/HCP-Flat/resolve/main"
13
+ NUM_SHARDS = {"train": 1629, "test": 174}
14
+
15
+
16
+ def create_hcp_flat(
17
+ root: Optional[str] = None,
18
+ training: bool = True,
19
+ shards: Optional[Union[int, Iterable[int]]] = None,
20
+ frames: int = 16,
21
+ cache_dir: Optional[str] = None,
22
+ ) -> wds.WebDataset:
23
+ """
24
+ Create HCP-Flat dataset. Yields samples of (key, images) where key is the webdataset
25
+ sample key and images is shape (C, T, H, W).
26
+
27
+ References:
28
+ https://github.com/webdataset/webdataset/issues/250#issuecomment-1454094496
29
+ https://github.com/tmbdev-archive/webdataset-imagenet-2/blob/main/imagenet.py
30
+ https://github.com/huggingface/pytorch-image-models/blob/main/timm/data/readers/reader_wds.py
31
+ """
32
+ root = root or os.environ.get("HCP_FLAT_ROOT") or HCP_FLAT_ROOT
33
+ split = "train" if training else "test"
34
+
35
+ shards = shards or NUM_SHARDS[split]
36
+ if isinstance(shards, int):
37
+ shards = range(shards)
38
+ assert (
39
+ min(shards) >= 0 and max(shards) < NUM_SHARDS[split]
40
+ ), f"Invalid shards {shards}; expected in [0, {NUM_SHARDS[split]})"
41
+
42
+ urls = [f"{root}/{split}/hcp-flat_{split}_{shard:06d}.tar" for shard in shards]
43
+
44
+ # In training, we resample shards with replacement independently in every worker and
45
+ # yield batches up to the target number of samples. In test, we iterate over the
46
+ # shards in order, with workers getting interleaving shards, and yield batches up to
47
+ # the target samples. In a distributed setting with variable size shards, setting a
48
+ # fixed number of samples is the easiest way to get balanced batches per worker. In
49
+ # training we will still see all data eventually. But in test, it means we cut off
50
+ # some data.
51
+
52
+ # Note that in training this does not do deterministic shuffling, which we would
53
+ # need for exact reproducibility. They get determistic shuffling in timm, but it's
54
+ # more complicated.
55
+
56
+ # Note that we are splitting the long timeseries into clips after shuffling, which
57
+ # means clips from the same series will appear consecutively in the batch(es).
58
+ # I think this is not too bad. It is basically equivalent to training on longer
59
+ # sequences. Clipping before shuffling might be preferred, but it results in a bad
60
+ # system memory leak (https://github.com/webdataset/webdataset/issues/354).
61
+ dataset = (
62
+ wds.WebDataset(
63
+ urls,
64
+ resampled=training,
65
+ shardshuffle=1000 if training else False,
66
+ nodesplitter=wds.split_by_node,
67
+ select_files=select_extensions(("bold.npy","meta.json")),
68
+ cache_dir=cache_dir,
69
+ )
70
+ .decode()
71
+ .map(partial(extract_images, mask=load_hcp_flat_mask()))
72
+ .compose(to_clips(frames))
73
+ .shuffle(2500 if training else 0)
74
+ )
75
+ return dataset
76
+
77
+
78
+ def select_extensions(extensions: Tuple[str, ...]):
79
+ extensions_set = set(extensions)
80
+ def select_files(fname: str):
81
+ suffix = ".".join(fname.split(".")[1:])
82
+ return suffix in extensions_set
83
+ return select_files
84
+
85
+
86
+ def extract_images(sample: Dict[str, Any], mask: torch.Tensor):
87
+ key = sample["__key__"]
88
+ images = sample["bold.npy"]
89
+ meta = sample["meta.json"]['task']
90
+
91
+ images = torch.from_numpy(images) / 255.0
92
+ images = (images - 0.5) / 0.2
93
+ images = unmask(images, mask)
94
+ # (C, T, H, W,)
95
+ images = images.unsqueeze(0)
96
+
97
+ return key, images, meta
98
+
99
+
100
+ def unmask(images: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
101
+ images_unmasked = torch.zeros(
102
+ (images.shape[0], *mask.shape), dtype=images.dtype, device=images.device
103
+ )
104
+ images_unmasked[:, mask] = images
105
+ return images_unmasked
106
+
107
+
108
+ def to_clips(frames: int = 16):
109
+ def _filter(src: IterableDataset[Tuple[str, torch.Tensor, str]]):
110
+ for key, images, meta in src:
111
+ offset = random.randint(0, frames)
112
+ for start in range(offset, images.shape[1] - frames, frames):
113
+ # yield key, images[:, start : start + frames].copy(), meta
114
+ yield key, images[:, start : start + frames].clone(), meta
115
+ return _filter
116
+
117
+
118
+ def load_hcp_flat_mask() -> torch.Tensor:
119
+ mask = np.load(Path(__file__).parents[1] / "hcp-flat_mask.npy")
120
+ mask = torch.as_tensor(mask)
121
+ return mask
fMRI-foundation-model-old/flat/util/logging.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+
5
+ """Logging."""
6
+
7
+ import atexit
8
+ import builtins
9
+ import decimal
10
+ import functools
11
+ import logging
12
+ import os
13
+ import sys
14
+
15
+ import simplejson
16
+ import torch
17
+ import torch.distributed as dist
18
+ from iopath.common.file_io import g_pathmgr as pathmgr
19
+
20
+
21
+ def is_master_proc(multinode=False):
22
+ """
23
+ Determines if the current process is the master process.
24
+ """
25
+ if dist.is_initialized():
26
+ if multinode:
27
+ return dist.get_rank() % dist.get_world_size() == 0
28
+ else:
29
+ return dist.get_rank() % torch.cuda.device_count() == 0
30
+ else:
31
+ return True
32
+
33
+
34
+ def _suppress_print():
35
+ """
36
+ Suppresses printing from the current process.
37
+ """
38
+
39
+ def print_pass(*objects, sep=" ", end="\n", file=sys.stdout, flush=False):
40
+ pass
41
+
42
+ builtins.print = print_pass
43
+
44
+
45
+ @functools.lru_cache(maxsize=None)
46
+ def _cached_log_stream(filename):
47
+ # Use 1K buffer if writing to cloud storage.
48
+ io = pathmgr.open(filename, "a", buffering=1024 if "://" in filename else -1)
49
+ atexit.register(io.close)
50
+ return io
51
+
52
+
53
+ def setup_logging(output_dir=None):
54
+ """
55
+ Sets up the logging for multiple processes. Only enable the logging for the
56
+ master process, and suppress logging for the non-master processes.
57
+ """
58
+ # Set up logging format.
59
+ if is_master_proc():
60
+ # Enable logging for the master process.
61
+ logging.root.handlers = []
62
+ else:
63
+ # Suppress logging for non-master processes.
64
+ _suppress_print()
65
+
66
+ logger = logging.getLogger()
67
+ logger.setLevel(logging.DEBUG)
68
+ logger.propagate = False
69
+ plain_formatter = logging.Formatter(
70
+ "[%(asctime)s][%(levelname)s] %(filename)s: %(lineno)3d: %(message)s",
71
+ datefmt="%m/%d %H:%M:%S",
72
+ )
73
+
74
+ if is_master_proc():
75
+ ch = logging.StreamHandler(stream=sys.stdout)
76
+ ch.setLevel(logging.DEBUG)
77
+ ch.setFormatter(plain_formatter)
78
+ logger.addHandler(ch)
79
+
80
+ if output_dir is not None and is_master_proc(multinode=True):
81
+ filename = os.path.join(output_dir, "stdout.log")
82
+ fh = logging.StreamHandler(_cached_log_stream(filename))
83
+ fh.setLevel(logging.DEBUG)
84
+ fh.setFormatter(plain_formatter)
85
+ logger.addHandler(fh)
86
+
87
+
88
+ def get_logger(name):
89
+ """
90
+ Retrieve the logger with the specified name or, if name is None, return a
91
+ logger which is the root logger of the hierarchy.
92
+ Args:
93
+ name (string): name of the logger.
94
+ """
95
+ return logging.getLogger(name)
96
+
97
+
98
+ def log_json_stats(stats):
99
+ """
100
+ Logs json stats.
101
+ Args:
102
+ stats (dict): a dictionary of statistical information to log.
103
+ """
104
+ stats = {
105
+ k: decimal.Decimal("{:.5f}".format(v)) if isinstance(v, float) else v
106
+ for k, v in stats.items()
107
+ }
108
+ json_stats = simplejson.dumps(stats, sort_keys=True, use_decimal=True)
109
+ logger = get_logger(__name__)
110
+ print("json_stats: {:s}".format(json_stats))
111
+
112
+
113
+ def master_print(*args, **kwargs):
114
+ if is_master_proc():
115
+ print(*args, **kwargs)
116
+ else:
117
+ pass
fMRI-foundation-model-old/flat/util/losses.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+ import numpy as np
4
+ import torch
5
+ from einops import rearrange
6
+ import re
7
+ import torch.nn.functional as F
8
+ import torch.nn as nn
9
+
10
+ def get_ids_shuffle(batch_size, device, model, mask_ratio=0.75):
11
+ N = batch_size
12
+ T = model.patch_embed.t_grid_size
13
+ H, W = model.patch_embed.grid_size
14
+ L = T * H * W
15
+
16
+ noise = torch.rand(N, L, device=device) # noise in [0, 1]
17
+
18
+ # shift missing patches to not be selected
19
+ if model.img_mask is not None:
20
+ noise = noise.view(N, T, H * W)
21
+ noise = noise + (1.0 - model.patch_mask)
22
+ noise = noise.view(N, L)
23
+
24
+ # sort noise for each sample
25
+ ids_shuffle = torch.argsort(
26
+ noise, dim=1
27
+ ) # ascend: small is keep, large is remove
28
+ ids_restore = torch.argsort(ids_shuffle, dim=1)
29
+
30
+ return ids_shuffle, ids_restore
31
+
32
+ class VICRegHandler(nn.Module):
33
+ def __init__(self, in_dim, num_layers=3, act=nn.GELU, h=1024, out_dim=4096):
34
+ super().__init__()
35
+ self.projector = nn.Sequential(
36
+ nn.Linear(in_dim, h),
37
+ nn.LayerNorm(h),
38
+ act(),
39
+ nn.Linear(h, h),
40
+ nn.LayerNorm(h),
41
+ act(),
42
+ nn.Linear(h, out_dim),
43
+ )
44
+
45
+ def forward(self, x):
46
+ return self.projector(x)
47
+
48
+ @staticmethod
49
+ def filter_global_to_local(l, enc_mask, dec_mask):
50
+ '''Get the subset of global tokens that correspond to encoder mask only'''
51
+ comb_mask = enc_mask | dec_mask
52
+ comb_indices = torch.where(comb_mask)[0]
53
+ enc_indices = torch.where(enc_mask)[0]
54
+ # enc_set = set(enc_indices.cpu().tolist())
55
+
56
+ # new_mask = torch.zeros_like(comb_indices, dtype=bool)
57
+ # for i, idx in enumerate(comb_indices):
58
+ # if idx in enc_set:
59
+ # new_mask[i] = True
60
+
61
+ new_mask = torch.isin(comb_indices, enc_indices)
62
+ return l[:, new_mask]
63
+
64
+ @staticmethod
65
+ def vicreg_loss(l1, l2, gamma=1.0, lamda=25, mu=25, nu=1, rand_frac=0.2, use_vic_cls=True, eps=1e-4):
66
+ if use_vic_cls:
67
+ # always keep cls and pick a random set of tokens
68
+ rand_indices = torch.cat([torch.tensor([0]), 1+torch.randperm(l1.shape[1]-1)])[:int(rand_frac*l1.shape[1])]
69
+ else:
70
+ # drop cls tokens from loss calc
71
+ l1 = l1[:, 1:]
72
+ l2 = l2[:, 1:]
73
+ rand_indices = torch.randperm(l1.shape[1])[:int(rand_frac*l1.shape[1])]
74
+
75
+ std_l1 = torch.sqrt(l1.flatten(1).var(dim=0)+eps) # nxd
76
+ std_l2 = torch.sqrt(l2.flatten(1).var(dim=0)+eps) # nxd
77
+ var_loss = F.relu(gamma - std_l1).mean() + F.relu(gamma - std_l2).mean()
78
+ del std_l1, std_l2
79
+
80
+ sim_loss = F.mse_loss(l1, l2)
81
+
82
+ l1 = l1 - l1.mean(0, keepdim=True) # b,n,d
83
+ l2 = l2 - l2.mean(0, keepdim=True)
84
+
85
+ l1_sub = l1[:, rand_indices]
86
+ del l1
87
+ cov_l1 = torch.bmm(l1_sub.permute(1,2,0), l1_sub.permute(1,0,2))/(l1_sub.shape[0]-1) # 0.1*n,d,d
88
+ cov_loss = ((cov_l1**2).sum() - (torch.diagonal(cov_l1, dim1=1,dim2=2)**2).sum())/(l1_sub.shape[1]*l1_sub.shape[2])
89
+ del cov_l1, l1_sub
90
+
91
+ l2_sub = l2[:, rand_indices]
92
+ del l2
93
+ cov_l2 = torch.bmm(l2_sub.permute(1,2,0), l2_sub.permute(1,0,2))/(l2_sub.shape[0]-1)
94
+ cov_loss = cov_loss + ((cov_l2**2).sum() - (torch.diagonal(cov_l2, dim1=1,dim2=2)**2).sum())/(l2_sub.shape[1]*l2_sub.shape[2]) # div by nxd
95
+ del cov_l2, l2_sub
96
+
97
+ vic_loss = lamda * sim_loss + mu * var_loss + nu * cov_loss
98
+
99
+ return vic_loss
100
+
101
+
102
+ class SimCLRHandler(nn.Module):
103
+ def __init__(self, in_dim, num_layers=2, act=nn.GELU, out_dim=1024):
104
+ super().__init__()
105
+ self.projector = nn.Sequential(
106
+ nn.Linear(in_dim, in_dim),
107
+ act(),
108
+ nn.Linear(in_dim, max(in_dim,out_dim)),
109
+ )
110
+
111
+ def forward(self, x):
112
+ return self.projector(x)
113
+
114
+ @staticmethod
115
+ def simclr_loss(lats, temp=0.006):
116
+ logits = (nn.functional.normalize(lats.flatten(1),dim=-1) @
117
+ nn.functional.normalize(lats.flatten(1),dim=-1).T) / temp
118
+
119
+ labels = torch.diag_embed(
120
+ torch.ones(logits.shape[0] // 2), offset=logits.shape[0] // 2
121
+ ) + torch.diag_embed(torch.ones(logits.shape[0] // 2), offset=-logits.shape[0] // 2)
122
+ labels = labels.to(lats.device)
123
+
124
+ mask = torch.ones_like(logits).bool()
125
+ torch.diagonal(mask).fill_(False)
126
+
127
+ labels = labels[mask].reshape(logits.shape[0], logits.shape[0]-1)
128
+ logits = logits[mask].reshape(*labels.shape)
129
+
130
+ contr_loss = -(logits.log_softmax(-1) * labels).sum(-1).mean()
131
+
132
+ return contr_loss
fMRI-foundation-model-old/flat/util/lr_decay.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ # --------------------------------------------------------
7
+ # References:
8
+ # ELECTRA https://github.com/google-research/electra
9
+ # BEiT: https://github.com/microsoft/unilm/tree/master/beit
10
+ # --------------------------------------------------------
11
+
12
+ import json
13
+
14
+
15
+ def param_groups_lrd(
16
+ model, weight_decay=0.05, no_weight_decay_list=[], layer_decay=0.75
17
+ ):
18
+ """
19
+ Parameter groups for layer-wise lr decay
20
+ Following BEiT: https://github.com/microsoft/unilm/blob/master/beit/optim_factory.py#L58
21
+ """
22
+ param_group_names = {}
23
+ param_groups = {}
24
+
25
+ num_layers = len(model.blocks) + 1
26
+
27
+ layer_scales = list(layer_decay ** (num_layers - i) for i in range(num_layers + 1))
28
+
29
+ for n, p in model.named_parameters():
30
+ if not p.requires_grad:
31
+ continue
32
+
33
+ # no decay: all 1D parameters and model specific ones
34
+ if p.ndim == 1 or n in no_weight_decay_list:
35
+ g_decay = "no_decay"
36
+ this_decay = 0.0
37
+ else:
38
+ g_decay = "decay"
39
+ this_decay = weight_decay
40
+
41
+ layer_id = get_layer_id_for_vit(n, num_layers)
42
+ group_name = "layer_%d_%s" % (layer_id, g_decay)
43
+
44
+ if group_name not in param_group_names:
45
+ this_scale = layer_scales[layer_id]
46
+
47
+ param_group_names[group_name] = {
48
+ "lr_scale": this_scale,
49
+ "weight_decay": this_decay,
50
+ "params": [],
51
+ }
52
+ param_groups[group_name] = {
53
+ "lr_scale": this_scale,
54
+ "weight_decay": this_decay,
55
+ "params": [],
56
+ }
57
+
58
+ param_group_names[group_name]["params"].append(n)
59
+ param_groups[group_name]["params"].append(p)
60
+
61
+ print("parameter groups: \n%s" % json.dumps(param_group_names, indent=2))
62
+
63
+ return list(param_groups.values())
64
+
65
+
66
+ def get_layer_id_for_vit(name, num_layers):
67
+ """
68
+ Assign a parameter with its layer id
69
+ Following BEiT: https://github.com/microsoft/unilm/blob/master/beit/optim_factory.py#L33
70
+ """
71
+ if name in [
72
+ "cls_token",
73
+ "mask_token",
74
+ ]:
75
+ return 0
76
+ elif name.startswith("patch_embed"):
77
+ return 0
78
+ elif name.startswith("pos_embed"):
79
+ return 0
80
+ elif name.startswith("blocks"):
81
+ return int(name.split(".")[1]) + 1
82
+ else:
83
+ return num_layers
fMRI-foundation-model-old/flat/util/lr_sched.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ import math
8
+
9
+
10
+ def adjust_learning_rate(optimizer, epoch, args):
11
+ """Decay the learning rate with half-cycle cosine after warmup"""
12
+ if epoch < args.warmup_epochs:
13
+ lr = args.lr * epoch / args.warmup_epochs
14
+ else:
15
+ lr = args.min_lr + (args.lr - args.min_lr) * 0.5 * (
16
+ 1.0
17
+ + math.cos(
18
+ math.pi
19
+ * (epoch - args.warmup_epochs)
20
+ / (args.epochs - args.warmup_epochs)
21
+ )
22
+ )
23
+ for param_group in optimizer.param_groups:
24
+ if "lr_scale" in param_group:
25
+ param_group["lr"] = lr * param_group["lr_scale"]
26
+ else:
27
+ param_group["lr"] = lr
28
+ return lr
fMRI-foundation-model-old/flat/util/meters.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+
5
+ import numpy as np
6
+ import torch
7
+ from sklearn.metrics import average_precision_score
8
+
9
+
10
+ def topks_correct(preds, labels, ks):
11
+ """
12
+ Given the predictions, labels, and a list of top-k values, compute the
13
+ number of correct predictions for each top-k value.
14
+
15
+ Args:
16
+ preds (array): array of predictions. Dimension is batchsize
17
+ N x ClassNum.
18
+ labels (array): array of labels. Dimension is batchsize N.
19
+ ks (list): list of top-k values. For example, ks = [1, 5] correspods
20
+ to top-1 and top-5.
21
+
22
+ Returns:
23
+ topks_correct (list): list of numbers, where the `i`-th entry
24
+ corresponds to the number of top-`ks[i]` correct predictions.
25
+ """
26
+ assert preds.size(0) == labels.size(
27
+ 0
28
+ ), "Batch dim of predictions and labels must match"
29
+ # Find the top max_k predictions for each sample
30
+ _top_max_k_vals, top_max_k_inds = torch.topk(
31
+ preds, max(ks), dim=1, largest=True, sorted=True
32
+ )
33
+ # (batch_size, max_k) -> (max_k, batch_size).
34
+ top_max_k_inds = top_max_k_inds.t()
35
+ # (batch_size, ) -> (max_k, batch_size).
36
+ rep_max_k_labels = labels.view(1, -1).expand_as(top_max_k_inds)
37
+ # (i, j) = 1 if top i-th prediction for the j-th sample is correct.
38
+ top_max_k_correct = top_max_k_inds.eq(rep_max_k_labels)
39
+ # Compute the number of topk correct predictions for each k.
40
+ topks_correct = [top_max_k_correct[:k, :].float().sum() for k in ks]
41
+ return topks_correct
42
+
43
+
44
+ def topk_errors(preds, labels, ks):
45
+ """
46
+ Computes the top-k error for each k.
47
+ Args:
48
+ preds (array): array of predictions. Dimension is N.
49
+ labels (array): array of labels. Dimension is N.
50
+ ks (list): list of ks to calculate the top accuracies.
51
+ """
52
+ num_topks_correct = topks_correct(preds, labels, ks)
53
+ return [(1.0 - x / preds.size(0)) * 100.0 for x in num_topks_correct]
54
+
55
+
56
+ def topk_accuracies(preds, labels, ks):
57
+ """
58
+ Computes the top-k accuracy for each k.
59
+ Args:
60
+ preds (array): array of predictions. Dimension is N.
61
+ labels (array): array of labels. Dimension is N.
62
+ ks (list): list of ks to calculate the top accuracies.
63
+ """
64
+ num_topks_correct = topks_correct(preds, labels, ks)
65
+ return [(x / preds.size(0)) * 100.0 for x in num_topks_correct]
66
+
67
+
68
+ def get_map(preds, labels):
69
+ """
70
+ Compute mAP for multi-label case.
71
+ Args:
72
+ preds (numpy tensor): num_examples x num_classes.
73
+ labels (numpy tensor): num_examples x num_classes.
74
+ Returns:
75
+ mean_ap (int): final mAP score.
76
+ """
77
+
78
+ print("Getting mAP for {} examples".format(preds.shape[0]))
79
+
80
+ preds = preds[:, ~(np.all(labels == 0, axis=0))]
81
+ labels = labels[:, ~(np.all(labels == 0, axis=0))]
82
+ aps = [0]
83
+ try:
84
+ aps = average_precision_score(labels, preds, average=None)
85
+ except ValueError:
86
+ print(
87
+ "Average precision requires a sufficient number of samples \
88
+ in a batch which are missing in this sample."
89
+ )
90
+
91
+ mean_ap = np.mean(aps)
92
+ return mean_ap
93
+
94
+
95
+ class TestMeter:
96
+ """
97
+ Perform the multi-view ensemble for testing: each video with an unique index
98
+ will be sampled with multiple clips, and the predictions of the clips will
99
+ be aggregated to produce the final prediction for the video.
100
+ The accuracy is calculated with the given ground truth labels.
101
+ """
102
+
103
+ def __init__(
104
+ self,
105
+ num_videos,
106
+ num_clips,
107
+ num_cls,
108
+ overall_iters,
109
+ multi_label=False,
110
+ ensemble_method="sum",
111
+ ):
112
+ """
113
+ Construct tensors to store the predictions and labels. Expect to get
114
+ num_clips predictions from each video, and calculate the metrics on
115
+ num_videos videos.
116
+ Args:
117
+ num_videos (int): number of videos to test.
118
+ num_clips (int): number of clips sampled from each video for
119
+ aggregating the final prediction for the video.
120
+ num_cls (int): number of classes for each prediction.
121
+ overall_iters (int): overall iterations for testing.
122
+ multi_label (bool): if True, use map as the metric.
123
+ ensemble_method (str): method to perform the ensemble, options
124
+ include "sum", and "max".
125
+ """
126
+
127
+ self.num_clips = num_clips
128
+ self.overall_iters = overall_iters
129
+ self.multi_label = multi_label
130
+ self.ensemble_method = ensemble_method
131
+ # Initialize tensors.
132
+ self.video_preds = torch.zeros((num_videos, num_cls))
133
+ if multi_label:
134
+ self.video_preds -= 1e10
135
+
136
+ self.video_labels = (
137
+ torch.zeros((num_videos, num_cls))
138
+ if multi_label
139
+ else torch.zeros((num_videos)).long()
140
+ )
141
+ self.clip_count = torch.zeros((num_videos)).long()
142
+ self.topk_accs = []
143
+ self.stats = {}
144
+
145
+ # Reset metric.
146
+ self.reset()
147
+
148
+ def reset(self):
149
+ """
150
+ Reset the metric.
151
+ """
152
+ self.clip_count.zero_()
153
+ self.video_preds.zero_()
154
+ if self.multi_label:
155
+ self.video_preds -= 1e10
156
+ self.video_labels.zero_()
157
+
158
+ def update_stats(self, preds, labels, clip_ids):
159
+ """
160
+ Collect the predictions from the current batch and perform on-the-flight
161
+ summation as ensemble.
162
+ Args:
163
+ preds (tensor): predictions from the current batch. Dimension is
164
+ N x C where N is the batch size and C is the channel size
165
+ (num_cls).
166
+ labels (tensor): the corresponding labels of the current batch.
167
+ Dimension is N.
168
+ clip_ids (tensor): clip indexes of the current batch, dimension is
169
+ N.
170
+ """
171
+ for ind in range(preds.shape[0]):
172
+ vid_id = int(clip_ids[ind]) // self.num_clips
173
+ if self.video_labels[vid_id].sum() > 0:
174
+ assert torch.equal(
175
+ self.video_labels[vid_id].type(torch.FloatTensor),
176
+ labels[ind].type(torch.FloatTensor),
177
+ )
178
+ self.video_labels[vid_id] = labels[ind]
179
+ if self.ensemble_method == "sum":
180
+ self.video_preds[vid_id] += preds[ind]
181
+ elif self.ensemble_method == "max":
182
+ self.video_preds[vid_id] = torch.max(
183
+ self.video_preds[vid_id], preds[ind]
184
+ )
185
+ else:
186
+ raise NotImplementedError(
187
+ "Ensemble Method {} is not supported".format(self.ensemble_method)
188
+ )
189
+ self.clip_count[vid_id] += 1
190
+
191
+ def log_iter_stats(self, cur_iter):
192
+ """
193
+ Log the stats.
194
+ Args:
195
+ cur_iter (int): the current iteration of testing.
196
+ """
197
+ stats = {
198
+ "split": "test_iter",
199
+ "cur_iter": "{}".format(cur_iter + 1),
200
+ }
201
+ print(stats)
202
+
203
+ def finalize_metrics(self, ks=(1, 5)):
204
+ """
205
+ Calculate and log the final ensembled metrics.
206
+ ks (tuple): list of top-k values for topk_accuracies. For example,
207
+ ks = (1, 5) correspods to top-1 and top-5 accuracy.
208
+ """
209
+ if not all(self.clip_count == self.num_clips):
210
+ print(
211
+ "clip count {} ~= num clips {}".format(
212
+ ", ".join(
213
+ [
214
+ "{}: {}".format(i, k)
215
+ for i, k in enumerate(self.clip_count.tolist())
216
+ ]
217
+ ),
218
+ self.num_clips,
219
+ )
220
+ )
221
+
222
+ self.stats = {"split": "test_final"}
223
+ if self.multi_label:
224
+ map = get_map(
225
+ self.video_preds.cpu().numpy(), self.video_labels.cpu().numpy()
226
+ )
227
+ self.stats["map"] = map
228
+ else:
229
+ num_topks_correct = topks_correct(self.video_preds, self.video_labels, ks)
230
+ topks = [(x / self.video_preds.size(0)) * 100.0 for x in num_topks_correct]
231
+ assert len({len(ks), len(topks)}) == 1
232
+ for k, topk in zip(ks, topks):
233
+ self.stats["top{}_acc".format(k)] = "{:.{prec}f}".format(topk, prec=2)
234
+ print(self.stats)
fMRI-foundation-model-old/flat/util/misc.py ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ # --------------------------------------------------------
7
+ # References:
8
+ # DeiT: https://github.com/facebookresearch/deit
9
+ # BEiT: https://github.com/microsoft/unilm/tree/master/beit
10
+ # --------------------------------------------------------
11
+
12
+ import builtins
13
+ import datetime
14
+ import math
15
+ import os
16
+ import time
17
+ from collections import defaultdict, deque, OrderedDict
18
+
19
+ import util.logging as logging
20
+ import psutil
21
+ import torch
22
+ import torch.distributed as dist
23
+ from iopath.common.file_io import g_pathmgr as pathmgr
24
+ from util.logging import master_print as print
25
+ from torch import inf
26
+
27
+
28
+ logger = logging.get_logger(__name__)
29
+
30
+
31
+ class SmoothedValue:
32
+ """Track a series of values and provide access to smoothed values over a
33
+ window or the global series average.
34
+ """
35
+
36
+ def __init__(self, window_size=20, fmt=None):
37
+ if fmt is None:
38
+ fmt = "{median:.4f} ({global_avg:.4f})"
39
+ self.deque = deque(maxlen=window_size)
40
+ self.total = 0.0
41
+ self.count = 0
42
+ self.fmt = fmt
43
+
44
+ def update(self, value, n=1):
45
+ self.deque.append(value)
46
+ self.count += n
47
+ self.total += value * n
48
+
49
+ def synchronize_between_processes(self):
50
+ """
51
+ Warning: does not synchronize the deque!
52
+ """
53
+ if not is_dist_avail_and_initialized():
54
+ return
55
+ t = torch.tensor([self.count, self.total], dtype=torch.float64, device="cuda")
56
+ dist.barrier()
57
+ dist.all_reduce(t)
58
+ t = t.tolist()
59
+ self.count = int(t[0])
60
+ self.total = t[1]
61
+
62
+ @property
63
+ def median(self):
64
+ d = torch.tensor(list(self.deque))
65
+ return d.median().item()
66
+
67
+ @property
68
+ def avg(self):
69
+ d = torch.tensor(list(self.deque), dtype=torch.float32)
70
+ return d.mean().item()
71
+
72
+ @property
73
+ def global_avg(self):
74
+ return self.total / self.count
75
+
76
+ @property
77
+ def max(self):
78
+ return max(self.deque)
79
+
80
+ @property
81
+ def value(self):
82
+ return self.deque[-1]
83
+
84
+ def __str__(self):
85
+ return self.fmt.format(
86
+ median=self.median,
87
+ avg=self.avg,
88
+ global_avg=self.global_avg,
89
+ max=self.max,
90
+ value=self.value,
91
+ )
92
+
93
+
94
+ class MetricLogger:
95
+ def __init__(self, delimiter="\t"):
96
+ self.meters = defaultdict(SmoothedValue)
97
+ self.delimiter = delimiter
98
+
99
+ def update(self, **kwargs):
100
+ for k, v in kwargs.items():
101
+ if v is None:
102
+ continue
103
+ if isinstance(v, torch.Tensor):
104
+ v = v.item()
105
+ assert isinstance(v, (float, int))
106
+ self.meters[k].update(v)
107
+
108
+ def __getattr__(self, attr):
109
+ if attr in self.meters:
110
+ return self.meters[attr]
111
+ if attr in self.__dict__:
112
+ return self.__dict__[attr]
113
+ raise AttributeError(
114
+ "'{}' object has no attribute '{}'".format(type(self).__name__, attr)
115
+ )
116
+
117
+ def __str__(self):
118
+ loss_str = []
119
+ for name, meter in self.meters.items():
120
+ loss_str.append("{}: {}".format(name, str(meter)))
121
+ return self.delimiter.join(loss_str)
122
+
123
+ def synchronize_between_processes(self):
124
+ for meter in self.meters.values():
125
+ meter.synchronize_between_processes()
126
+
127
+ def add_meter(self, name, meter):
128
+ self.meters[name] = meter
129
+
130
+ def log_every(self, iterable, print_freq, header=None, total_steps=None):
131
+ i = 0
132
+ total_steps = total_steps or len(iterable)
133
+ if not header:
134
+ header = ""
135
+ start_time = time.time()
136
+ end = time.time()
137
+ iter_time = SmoothedValue(fmt="{avg:.4f}")
138
+ data_time = SmoothedValue(fmt="{avg:.4f}")
139
+ space_fmt = ":" + str(len(str(total_steps))) + "d"
140
+ log_msg = [
141
+ header,
142
+ "[{0" + space_fmt + "}/{1}]",
143
+ "eta: {eta}",
144
+ "{meters}",
145
+ "time: {time}",
146
+ "data: {data}",
147
+ ]
148
+ if torch.cuda.is_available():
149
+ log_msg.append("max mem: {memory:.0f}")
150
+ log_msg = self.delimiter.join(log_msg)
151
+ MB = 1024.0 * 1024.0
152
+ for obj in iterable:
153
+ data_time.update(time.time() - end)
154
+ yield obj
155
+ iter_time.update(time.time() - end)
156
+ if i % print_freq == 0 or i == total_steps - 1:
157
+ eta_seconds = iter_time.global_avg * (total_steps - i)
158
+ eta_string = str(datetime.timedelta(seconds=int(eta_seconds)))
159
+ if torch.cuda.is_available():
160
+ print(
161
+ log_msg.format(
162
+ i,
163
+ total_steps,
164
+ eta=eta_string,
165
+ meters=str(self),
166
+ time=str(iter_time),
167
+ data=str(data_time),
168
+ memory=torch.cuda.max_memory_allocated() / MB,
169
+ )
170
+ )
171
+
172
+ else:
173
+ print(
174
+ log_msg.format(
175
+ i,
176
+ total_steps,
177
+ eta=eta_string,
178
+ meters=str(self),
179
+ time=str(iter_time),
180
+ data=str(data_time),
181
+ )
182
+ )
183
+ i += 1
184
+ end = time.time()
185
+ total_time = time.time() - start_time
186
+ total_time_str = str(datetime.timedelta(seconds=int(total_time)))
187
+ print(
188
+ "{} Total time: {} ({:.4f} s / it)".format(
189
+ header, total_time_str, total_time / total_steps
190
+ )
191
+ )
192
+
193
+
194
+ def setup_for_distributed(is_master):
195
+ """
196
+ This function disables printing when not in master process
197
+ """
198
+ builtin_print = builtins.print
199
+
200
+ def print(*args, **kwargs):
201
+ force = kwargs.pop("force", False)
202
+ force = force or (get_world_size() > 8)
203
+ if is_master or force:
204
+ now = datetime.datetime.now().time()
205
+ builtin_print("[{}] ".format(now), end="") # print with time stamp
206
+ builtin_print(*args, **kwargs)
207
+
208
+ builtins.print = print
209
+
210
+
211
+ def is_dist_avail_and_initialized():
212
+ if not dist.is_available():
213
+ return False
214
+ if not dist.is_initialized():
215
+ return False
216
+ return True
217
+
218
+
219
+ def get_world_size():
220
+ if not is_dist_avail_and_initialized():
221
+ return 1
222
+ return dist.get_world_size()
223
+
224
+
225
+ def get_rank():
226
+ if not is_dist_avail_and_initialized():
227
+ return 0
228
+ return dist.get_rank()
229
+
230
+
231
+ def is_main_process():
232
+ return get_rank() == 0
233
+
234
+
235
+ def save_on_master(state, path):
236
+ if is_main_process():
237
+ print(f"save path {path}")
238
+ with pathmgr.open(path, "wb") as f:
239
+ torch.save(state, f)
240
+
241
+
242
+ def init_distributed_mode(args):
243
+ if args.no_env:
244
+ pass
245
+ elif args.dist_on_itp:
246
+ args.rank = int(os.environ["OMPI_COMM_WORLD_RANK"])
247
+ args.world_size = int(os.environ["OMPI_COMM_WORLD_SIZE"])
248
+ args.gpu = int(os.environ["OMPI_COMM_WORLD_LOCAL_RANK"])
249
+ args.dist_url = "tcp://%s:%s" % (
250
+ os.environ["MASTER_ADDR"],
251
+ os.environ["MASTER_PORT"],
252
+ )
253
+ os.environ["LOCAL_RANK"] = str(args.gpu)
254
+ os.environ["RANK"] = str(args.rank)
255
+ os.environ["WORLD_SIZE"] = str(args.world_size)
256
+ # ["RANK", "WORLD_SIZE", "MASTER_ADDR", "MASTER_PORT", "LOCAL_RANK"]
257
+ elif "RANK" in os.environ and "WORLD_SIZE" in os.environ:
258
+ args.rank = int(os.environ["RANK"])
259
+ args.world_size = int(os.environ["WORLD_SIZE"])
260
+ args.gpu = int(os.environ["LOCAL_RANK"])
261
+ elif "SLURM_PROCID" in os.environ:
262
+ args.rank = int(os.environ["SLURM_PROCID"])
263
+ args.gpu = args.rank % torch.cuda.device_count()
264
+ else:
265
+ print("Not using distributed mode")
266
+ setup_for_distributed(is_master=True) # hack
267
+ args.distributed = False
268
+ return
269
+
270
+ args.distributed = True
271
+
272
+ torch.cuda.set_device(args.gpu)
273
+ args.dist_backend = "nccl"
274
+ print(
275
+ "| distributed init (rank {}): {}, gpu {}".format(
276
+ args.rank, args.dist_url, args.gpu
277
+ ),
278
+ # flush=True,
279
+ )
280
+ torch.distributed.init_process_group(
281
+ backend=args.dist_backend,
282
+ world_size=args.world_size,
283
+ rank=args.rank,
284
+ )
285
+ # init_method=args.dist_url,
286
+ torch.distributed.barrier()
287
+ setup_for_distributed(args.rank == 0)
288
+
289
+
290
+ class NativeScalerWithGradNormCount:
291
+ state_dict_key = "amp_scaler"
292
+
293
+ def __init__(self, fp32=False):
294
+ self._scaler = torch.cuda.amp.GradScaler(enabled=not fp32)
295
+
296
+ def __call__(
297
+ self,
298
+ loss,
299
+ optimizer,
300
+ clip_grad=None,
301
+ parameters=None,
302
+ create_graph=False,
303
+ update_grad=True,
304
+ ):
305
+ self._scaler.scale(loss).backward(create_graph=create_graph)
306
+ if update_grad:
307
+ if clip_grad is not None:
308
+ assert parameters is not None
309
+ self._scaler.unscale_(
310
+ optimizer
311
+ ) # unscale the gradients of optimizer's assigned params in-place
312
+ norm = torch.nn.utils.clip_grad_norm_(parameters, clip_grad)
313
+ else:
314
+ self._scaler.unscale_(optimizer)
315
+ norm = get_grad_norm_(parameters)
316
+ self._scaler.step(optimizer)
317
+ self._scaler.update()
318
+ else:
319
+ norm = None
320
+ return norm
321
+
322
+ def state_dict(self):
323
+ return self._scaler.state_dict()
324
+
325
+ def load_state_dict(self, state_dict):
326
+ self._scaler.load_state_dict(state_dict)
327
+
328
+
329
+ def get_grad_norm_(parameters, norm_type: float = 2.0) -> torch.Tensor:
330
+ if isinstance(parameters, torch.Tensor):
331
+ parameters = [parameters]
332
+ parameters = [p for p in parameters if p.grad is not None]
333
+ norm_type = float(norm_type)
334
+ if len(parameters) == 0:
335
+ return torch.tensor(0.0)
336
+ device = parameters[0].grad.device
337
+ if norm_type == inf:
338
+ total_norm = max(p.grad.detach().abs().max().to(device) for p in parameters)
339
+ else:
340
+ total_norm = torch.norm(
341
+ torch.stack(
342
+ [torch.norm(p.grad.detach(), norm_type).to(device) for p in parameters]
343
+ ),
344
+ norm_type,
345
+ )
346
+ return total_norm
347
+
348
+
349
+ def save_model(args, epoch, model, model_without_ddp, optimizer, loss_scaler):
350
+ checkpoint_path = "{}/checkpoint-{:05d}.pth".format(args.output_dir, epoch)
351
+ to_save = {
352
+ "model": model_without_ddp.state_dict(),
353
+ "optimizer": optimizer.state_dict(),
354
+ "epoch": epoch,
355
+ "scaler": loss_scaler.state_dict(),
356
+ "args": args,
357
+ }
358
+
359
+ save_on_master(to_save, checkpoint_path)
360
+ return checkpoint_path
361
+
362
+
363
+ def get_last_checkpoint(args):
364
+ """
365
+ Get the last checkpoint from the checkpointing folder.
366
+ Args:
367
+ path_to_job (string): the path to the folder of the current job.
368
+ """
369
+ d = args.output_dir
370
+ names = pathmgr.ls(d) if pathmgr.exists(d) else []
371
+ names = [f for f in names if "checkpoint" in f]
372
+ if len(names) == 0:
373
+ print("No checkpoints found in '{}'.".format(d))
374
+ return None
375
+ else:
376
+ # Sort the checkpoints by epoch.
377
+ name = sorted(names)[-1]
378
+ return os.path.join(d, name)
379
+
380
+
381
+ def load_model(args, model_without_ddp, optimizer, loss_scaler):
382
+ if not args.resume:
383
+ args.resume = get_last_checkpoint(args)
384
+ if args.resume:
385
+ if args.resume.startswith("https"):
386
+ checkpoint = torch.hub.load_state_dict_from_url(
387
+ args.resume, map_location="cpu", check_hash=True
388
+ )
389
+ else:
390
+ with pathmgr.open(args.resume, "rb") as f:
391
+ checkpoint = torch.load(f, map_location="cpu")
392
+ model_without_ddp.load_state_dict(checkpoint["model"])
393
+ print("Resume checkpoint %s" % args.resume)
394
+ if (
395
+ "optimizer" in checkpoint
396
+ and "epoch" in checkpoint
397
+ and not (hasattr(args, "eval") and args.eval)
398
+ ):
399
+ optimizer.load_state_dict(checkpoint["optimizer"])
400
+ args.start_epoch = checkpoint["epoch"] + 1
401
+ if "scaler" in checkpoint:
402
+ loss_scaler.load_state_dict(checkpoint["scaler"])
403
+ print("With optim & sched!")
404
+
405
+
406
+ def all_reduce_mean(x):
407
+ world_size = get_world_size()
408
+ if world_size > 1:
409
+ x_reduce = torch.tensor(x).cuda()
410
+ dist.all_reduce(x_reduce)
411
+ x_reduce /= world_size
412
+ return x_reduce.item()
413
+ else:
414
+ return x
415
+
416
+
417
+ def gpu_mem_usage():
418
+ """
419
+ Compute the GPU memory usage for the current device (GB).
420
+ """
421
+ if torch.cuda.is_available():
422
+ mem_usage_bytes = torch.cuda.max_memory_allocated()
423
+ else:
424
+ mem_usage_bytes = 0
425
+ return mem_usage_bytes / 1024**3
426
+
427
+
428
+ def cpu_mem_usage():
429
+ """
430
+ Compute the system memory (RAM) usage for the current device (GB).
431
+ Returns:
432
+ usage (float): used memory (GB).
433
+ total (float): total memory (GB).
434
+ """
435
+ vram = psutil.virtual_memory()
436
+ usage = (vram.total - vram.available) / 1024**3
437
+ total = vram.total / 1024**3
438
+
439
+ return usage, total
440
+
441
+
442
+ def all_gather(tensors):
443
+ """
444
+ All gathers the provided tensors from all processes across machines.
445
+ Args:
446
+ tensors (list): tensors to perform all gather across all processes in
447
+ all machines.
448
+ """
449
+
450
+ gather_list = []
451
+ output_tensor = []
452
+ world_size = dist.get_world_size()
453
+ for tensor in tensors:
454
+ tensor_placeholder = [torch.ones_like(tensor) for _ in range(world_size)]
455
+ dist.all_gather(tensor_placeholder, tensor, async_op=False)
456
+ gather_list.append(tensor_placeholder)
457
+ for gathered_tensor in gather_list:
458
+ output_tensor.append(torch.cat(gathered_tensor, dim=0))
459
+ return output_tensor
460
+
461
+
462
+ def add_weight_decay(model, weight_decay=1e-5, skip_list=(), bias_wd=False):
463
+ decay = []
464
+ no_decay = []
465
+ for name, param in model.named_parameters():
466
+ if not param.requires_grad:
467
+ continue # frozen weights
468
+ if (
469
+ (not bias_wd)
470
+ and len(param.shape) == 1
471
+ or name.endswith(".bias")
472
+ or name in skip_list
473
+ ):
474
+ no_decay.append(param)
475
+ else:
476
+ decay.append(param)
477
+ return [
478
+ {"params": no_decay, "weight_decay": 0.0},
479
+ {"params": decay, "weight_decay": weight_decay},
480
+ ]
481
+
482
+
483
+ def inflate(model_2d, model_3d):
484
+ state_dict_inflated = OrderedDict()
485
+ for k, v2d in model_2d.items():
486
+ if "patch_embed.proj.weight" in k:
487
+ v3d = model_3d[k]
488
+ v3d = v2d.unsqueeze(2).repeat(1, 1, v3d.shape[2], 1, 1) / v3d.shape[2]
489
+ state_dict_inflated[k] = v3d.clone()
490
+ elif "pos_embed" in k:
491
+ pos_embed_cls, pos_embed_spatial = torch.split(v2d, [1, 196], dim=1)
492
+ state_dict_inflated["pos_embed_cls"] = pos_embed_cls.clone()
493
+ state_dict_inflated["pos_embed"] = pos_embed_spatial.clone()
494
+ else:
495
+ state_dict_inflated[k] = v2d.clone()
496
+ return state_dict_inflated
497
+
498
+
499
+ def convert_checkpoint(model_2d):
500
+ state_dict_inflated = OrderedDict()
501
+ for k, v2d in model_2d.items():
502
+ if "head.projection.weight" in k:
503
+ state_dict_inflated["head.weight"] = v2d.clone()
504
+ elif "head.projection.bias" in k:
505
+ state_dict_inflated["head.bias"] = v2d.clone()
506
+ else:
507
+ state_dict_inflated[k] = v2d.clone()
508
+ return state_dict_inflated
fMRI-foundation-model-old/flat/util/pos_embed.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ # --------------------------------------------------------
7
+ # Position embedding utils
8
+ # --------------------------------------------------------
9
+
10
+ import util.logging as logging
11
+ import numpy as np
12
+ import torch
13
+
14
+
15
+ logger = logging.get_logger(__name__)
16
+
17
+
18
+ # --------------------------------------------------------
19
+ # Interpolate position embeddings for high-resolution
20
+ # References:
21
+ # DeiT: https://github.com/facebookresearch/deit
22
+ # --------------------------------------------------------
23
+ def interpolate_pos_embed(model, checkpoint_model):
24
+ if "pos_embed" in checkpoint_model:
25
+ pos_embed_checkpoint = checkpoint_model["pos_embed"]
26
+ embedding_size = pos_embed_checkpoint.shape[-1]
27
+ num_patches = model.patch_embed.num_patches
28
+ num_extra_tokens = model.pos_embed.shape[-2] - num_patches
29
+ # height (== width) for the checkpoint position embedding
30
+ orig_size = int((pos_embed_checkpoint.shape[-2] - num_extra_tokens) ** 0.5)
31
+ # height (== width) for the new position embedding
32
+ new_size = int(num_patches**0.5)
33
+ # class_token and dist_token are kept unchanged
34
+ if orig_size != new_size:
35
+ print(
36
+ "Position interpolate from %dx%d to %dx%d"
37
+ % (orig_size, orig_size, new_size, new_size)
38
+ )
39
+ extra_tokens = pos_embed_checkpoint[:, :num_extra_tokens]
40
+ # only the position tokens are interpolated
41
+ pos_tokens = pos_embed_checkpoint[:, num_extra_tokens:]
42
+ pos_tokens = pos_tokens.reshape(
43
+ -1, orig_size, orig_size, embedding_size
44
+ ).permute(0, 3, 1, 2)
45
+ pos_tokens = torch.nn.functional.interpolate(
46
+ pos_tokens,
47
+ size=(new_size, new_size),
48
+ mode="bicubic",
49
+ align_corners=False,
50
+ )
51
+ pos_tokens = pos_tokens.permute(0, 2, 3, 1).flatten(1, 2)
52
+ new_pos_embed = torch.cat((extra_tokens, pos_tokens), dim=1)
53
+ checkpoint_model["pos_embed"] = new_pos_embed
fMRI-foundation-model-old/flat/util/video_vit.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+
5
+ import util.logging as logging
6
+ import torch
7
+ import torch.nn as nn
8
+ from timm.layers import to_2tuple
9
+ from timm.models.vision_transformer import DropPath, Mlp
10
+
11
+
12
+ logger = logging.get_logger(__name__)
13
+
14
+
15
+ class PatchEmbed(nn.Module):
16
+ """Image to Patch Embedding"""
17
+
18
+ def __init__(
19
+ self,
20
+ img_size=224,
21
+ patch_size=16,
22
+ in_chans=3,
23
+ embed_dim=768,
24
+ # temporal related:
25
+ frames=32,
26
+ t_patch_size=4,
27
+ ):
28
+ super().__init__()
29
+ img_size = to_2tuple(img_size)
30
+ patch_size = to_2tuple(patch_size)
31
+ assert img_size[1] % patch_size[1] == 0
32
+ assert img_size[0] % patch_size[0] == 0
33
+ assert frames % t_patch_size == 0
34
+ num_patches = (
35
+ (img_size[1] // patch_size[1])
36
+ * (img_size[0] // patch_size[0])
37
+ * (frames // t_patch_size)
38
+ )
39
+ self.input_size = (
40
+ frames // t_patch_size,
41
+ img_size[0] // patch_size[0],
42
+ img_size[1] // patch_size[1],
43
+ )
44
+ print(
45
+ f"img_size {img_size} patch_size {patch_size} frames {frames} t_patch_size {t_patch_size}"
46
+ )
47
+ self.img_size = img_size
48
+ self.patch_size = patch_size
49
+ self.in_chans = in_chans
50
+
51
+ self.frames = frames
52
+ self.t_patch_size = t_patch_size
53
+
54
+ self.num_patches = num_patches
55
+
56
+ self.grid_size = img_size[0] // patch_size[0], img_size[1] // patch_size[1]
57
+ self.t_grid_size = frames // t_patch_size
58
+
59
+ kernel_size = [t_patch_size] + list(patch_size)
60
+ self.proj = nn.Conv3d(
61
+ in_chans, embed_dim, kernel_size=kernel_size, stride=kernel_size
62
+ )
63
+
64
+ def forward(self, x):
65
+ B, C, T, H, W = x.shape
66
+ assert (
67
+ H == self.img_size[0] and W == self.img_size[1]
68
+ ), f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})."
69
+ assert T == self.frames
70
+ x = self.proj(x).flatten(3)
71
+ x = torch.einsum("ncts->ntsc", x) # [N, T, H*W, C]
72
+ return x
73
+
74
+
75
+ class Attention(nn.Module):
76
+ def __init__(
77
+ self,
78
+ dim,
79
+ num_heads=8,
80
+ qkv_bias=False,
81
+ qk_scale=None,
82
+ attn_drop=0.0,
83
+ proj_drop=0.0,
84
+ input_size=(4, 14, 14),
85
+ ):
86
+ super().__init__()
87
+ assert dim % num_heads == 0, "dim should be divisible by num_heads"
88
+ self.num_heads = num_heads
89
+ head_dim = dim // num_heads
90
+ self.scale = qk_scale or head_dim**-0.5
91
+
92
+ self.q = nn.Linear(dim, dim, bias=qkv_bias)
93
+ self.k = nn.Linear(dim, dim, bias=qkv_bias)
94
+ self.v = nn.Linear(dim, dim, bias=qkv_bias)
95
+ assert attn_drop == 0.0 # do not use
96
+ self.proj = nn.Linear(dim, dim)
97
+ self.proj_drop = nn.Dropout(proj_drop)
98
+ self.input_size = input_size
99
+ assert input_size[1] == input_size[2]
100
+
101
+ def forward(self, x):
102
+ B, N, C = x.shape
103
+ q = (
104
+ self.q(x)
105
+ .reshape(B, N, self.num_heads, C // self.num_heads)
106
+ .permute(0, 2, 1, 3)
107
+ )
108
+ k = (
109
+ self.k(x)
110
+ .reshape(B, N, self.num_heads, C // self.num_heads)
111
+ .permute(0, 2, 1, 3)
112
+ )
113
+ v = (
114
+ self.v(x)
115
+ .reshape(B, N, self.num_heads, C // self.num_heads)
116
+ .permute(0, 2, 1, 3)
117
+ )
118
+
119
+ attn = (q @ k.transpose(-2, -1)) * self.scale
120
+
121
+ attn = attn.softmax(dim=-1)
122
+
123
+ x = (attn @ v).transpose(1, 2).reshape(B, N, C)
124
+ x = self.proj(x)
125
+ x = self.proj_drop(x)
126
+ x = x.view(B, -1, C)
127
+ return x
128
+
129
+
130
+ class Block(nn.Module):
131
+ """
132
+ Transformer Block with specified Attention function
133
+ """
134
+
135
+ def __init__(
136
+ self,
137
+ dim,
138
+ num_heads,
139
+ mlp_ratio=4.0,
140
+ qkv_bias=False,
141
+ qk_scale=None,
142
+ drop=0.0,
143
+ attn_drop=0.0,
144
+ drop_path=0.0,
145
+ act_layer=nn.GELU,
146
+ norm_layer=nn.LayerNorm,
147
+ attn_func=Attention,
148
+ ):
149
+ super().__init__()
150
+ self.norm1 = norm_layer(dim)
151
+ self.attn = attn_func(
152
+ dim,
153
+ num_heads=num_heads,
154
+ qkv_bias=qkv_bias,
155
+ qk_scale=qk_scale,
156
+ attn_drop=attn_drop,
157
+ proj_drop=drop,
158
+ )
159
+ # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
160
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
161
+ self.norm2 = norm_layer(dim)
162
+ mlp_hidden_dim = int(dim * mlp_ratio)
163
+ self.mlp = Mlp(
164
+ in_features=dim,
165
+ hidden_features=mlp_hidden_dim,
166
+ act_layer=act_layer,
167
+ drop=drop,
168
+ )
169
+
170
+ def forward(self, x):
171
+ x = x + self.drop_path(self.attn(self.norm1(x)))
172
+ x = x + self.drop_path(self.mlp(self.norm2(x)))
173
+ return x