| #!/usr/bin/env python | |
| # coding: utf-8 | |
| # In[1]: | |
| # Import packages and setup gpu configuration. | |
| # This code block shouldnt need to be adjusted! | |
| import os | |
| import sys | |
| import json | |
| import yaml | |
| import numpy as np | |
| import copy | |
| import math | |
| import time | |
| import random | |
| from tqdm.auto import tqdm | |
| import webdataset as wds | |
| import matplotlib.pyplot as plt | |
| import pandas as pd | |
| import torch | |
| import torch.nn as nn | |
| from torchvision import transforms | |
| import utils | |
| from mae_utils.flat_models import * | |
| import h5py | |
| from typing import List, Dict, Any, Tuple | |
| from sklearn.preprocessing import StandardScaler | |
| import argparse | |
| # tf32 data type is faster than standard float32 | |
| torch.backends.cuda.matmul.allow_tf32 = True | |
| # following fixes a Conv3D CUDNN_NOT_SUPPORTED error | |
| torch.backends.cudnn.benchmark = True | |
| # ## MODEL TO LOAD ## | |
| # model_name = "HCPflat_large_gsrFalse_" | |
| # parquet_folder = "epoch99" | |
| # # outdir = os.path.abspath(f'checkpoints/{model_name}') | |
| # outdir = os.path.abspath(f'checkpoints/{model_name}') | |
| # print("outdir", outdir) | |
| # # Load previous config.yaml if available | |
| # if os.path.exists(f"{outdir}/config.yaml"): | |
| # config = yaml.load(open(f"{outdir}/config.yaml", 'r'), Loader=yaml.FullLoader) | |
| # print(f"Loaded config.yaml from ckpt folder {outdir}") | |
| # # create global variables from the config | |
| # print("\n__CONFIG__") | |
| # for attribute_name in config.keys(): | |
| # print(f"{attribute_name} = {config[attribute_name]}") | |
| # globals()[attribute_name] = config[f'{attribute_name}'] | |
| # print("\n") | |
| # world_size = os.getenv('WORLD_SIZE') | |
| # if world_size is None: | |
| # world_size = 1 | |
| # else: | |
| # world_size = int(world_size) | |
| # print(f"WORLD_SIZE={world_size}") | |
| # if utils.is_interactive(): | |
| # # Following allows you to change functions in models.py or utils.py and | |
| # # have this notebook automatically update with your revisions | |
| # %load_ext autoreload | |
| # %autoreload 2 | |
| # batch_size = probe_batch_size | |
| # num_epochs = probe_num_epochs | |
| # data_type = torch.float32 # change depending on your mixed_precision | |
| # global_batch_size = batch_size * world_size | |
| device = torch.device('cuda') | |
| # hcp_flat_path = "/weka/proj-medarc/shared/HCP-Flat" | |
| # seed = 42 | |
| num_frames = 16 | |
| gsr = False | |
| # num_workers = 5 | |
| batch_size = 128 | |
| # target = 'sex' # This can be 'trial_type' 'age' 'sex' | |
| print("PID of this process =",os.getpid()) | |
| # In[2]: | |
| # if running this interactively, can specify jupyter_args here for argparser to use | |
| if utils.is_interactive(): | |
| model_name_suffix = "testing" | |
| print("model_name_suffix:", model_name_suffix) | |
| # global_batch_size and batch_size should already be defined in the 2nd cell block | |
| jupyter_args = f"--hcp_flat_path=/weka/proj-medarc/shared/HCP-Flat \ | |
| --target=trial_type \ | |
| --model_suffix={model_name_suffix} \ | |
| --batch_size={batch_size} \ | |
| --max_lr=3e-4 --num_epochs=20 --no-save_ckpt --no-wandb_log --num_workers=10 \ | |
| --weight_decay=1e-5" | |
| # --multisubject_ckpt=../train_logs/multisubject_subj01_1024_24bs_nolow | |
| print(jupyter_args) | |
| jupyter_args = jupyter_args.split() | |
| from IPython.display import clear_output # function to clear print outputs in cell | |
| get_ipython().run_line_magic('load_ext', 'autoreload') | |
| # this allows you to change functions in models.py or utils.py and have this notebook automatically update with your revisions | |
| get_ipython().run_line_magic('autoreload', '2') | |
| # In[3]: | |
| parser = argparse.ArgumentParser(description="Model Training Configuration") | |
| parser.add_argument( | |
| "--model_suffix", type=str, default="Testing_flat", | |
| help="name of model, used for ckpt saving and wandb logging (if enabled)", | |
| ) | |
| parser.add_argument( | |
| "--hcp_flat_path", type=str, default=os.getcwd(), | |
| help="Path to where NSD data is stored / where to download it to", | |
| ) | |
| parser.add_argument( | |
| "--batch_size", type=int, default=128, | |
| help="Batch size can be increased by 10x if only training retreival submodule and not diffusion prior", | |
| ) | |
| parser.add_argument( | |
| "--wandb_log",action=argparse.BooleanOptionalAction,default=False, | |
| help="whether to log to wandb", | |
| ) | |
| parser.add_argument( | |
| "--num_epochs",type=int,default=150, | |
| help="number of epochs of training", | |
| ) | |
| parser.add_argument( | |
| "--lr_scheduler_type",type=str,default='cycle',choices=['cycle','linear'], | |
| ) | |
| parser.add_argument( | |
| "--save_ckpt",action=argparse.BooleanOptionalAction,default=True, | |
| ) | |
| parser.add_argument( | |
| "--seed",type=int,default=42, | |
| ) | |
| parser.add_argument( | |
| "--max_lr",type=float,default=3e-4, | |
| ) | |
| parser.add_argument( | |
| "--target",type=str,default='trial_type',choices=['trial_type','sex','age'], | |
| ) | |
| parser.add_argument( | |
| "--num_workers",type=int,default=10, | |
| ) | |
| parser.add_argument( | |
| "--weight_decay",type=float,default=1e-5, | |
| ) | |
| if utils.is_interactive(): | |
| args = parser.parse_args(jupyter_args) | |
| else: | |
| args = parser.parse_args() | |
| print(f"------ ARGS ------- \n {args}") | |
| # create global variables without the args prefix | |
| for attribute_name in vars(args).keys(): | |
| globals()[attribute_name] = getattr(args, attribute_name) | |
| # seed all random functions | |
| utils.seed_everything(seed) | |
| # In[4]: | |
| #### UNCOMMENT THIS TO SAVE THE HCP-FLAT IN HDF5 FORMAT | |
| # from torch.utils.data import default_collate | |
| # from mae_utils.flat import load_hcp_flat_mask | |
| # from mae_utils.flat import create_hcp_flat | |
| # from mae_utils.flat import batch_unmask | |
| # import mae_utils.visualize as vis | |
| # batch_size = 26 | |
| # print(f"changed batch_size to {batch_size}") | |
| # ## Test ## | |
| # datasets_to_include = "HCP" | |
| # assert "HCP" in datasets_to_include | |
| # test_dataset = create_hcp_flat(root=hcp_flat_path, | |
| # clip_mode="event", frames=num_frames, shuffle=False, gsr=gsr, sub_list = 'test') | |
| # test_dl = wds.WebLoader( | |
| # test_dataset.batched(batch_size, partial=False, collation_fn=default_collate), | |
| # batch_size=None, | |
| # shuffle=False, | |
| # num_workers=num_workers, | |
| # pin_memory=True, | |
| # ) | |
| # ## Train ## | |
| # assert "HCP" in datasets_to_include | |
| # train_dataset = create_hcp_flat(root=hcp_flat_path, | |
| # clip_mode="event", frames=num_frames, shuffle=False, gsr=gsr, sub_list = 'train') | |
| # train_dl = wds.WebLoader( | |
| # train_dataset.batched(batch_size, partial=False, collation_fn=default_collate), | |
| # batch_size=None, | |
| # shuffle=False, | |
| # num_workers=num_workers, | |
| # pin_memory=True, | |
| # ) | |
| # def flatten_meta(meta_dict): | |
| # """ | |
| # Flatten the meta dictionary by: | |
| # - Replacing single-item lists with the item itself. | |
| # - Converting tensors to scalar numbers. | |
| # """ | |
| # flattened = {} | |
| # for key, value in meta_dict.items(): | |
| # if isinstance(value, list): | |
| # if len(value) == 1: | |
| # flattened[key] = value[0] # Replace list with its single item | |
| # else: | |
| # flattened[key] = value # Keep as is if multiple items | |
| # elif isinstance(value, torch.Tensor): | |
| # # Convert tensor to scalar | |
| # if value.numel() == 1: | |
| # flattened[key] = value.item() | |
| # else: | |
| # flattened[key] = value.tolist() # Convert multi-element tensor to list | |
| # else: | |
| # flattened[key] = value # Keep the value as is | |
| # return flattened | |
| # import h5py | |
| # meta_array = np.array([], dtype=object) | |
| # # Open an HDF5 file in write mode | |
| # with h5py.File('train_hcp_raw_flatmaps.hdf5', 'w') as h5f: | |
| # flatmaps_dset = None | |
| # total_samples = 0 | |
| # for i, batch in tqdm(enumerate(train_dl), total = 120000): | |
| # images = batch['image'][0] | |
| # meta = batch['meta'] | |
| # batch_size = images.shape[0] | |
| # meta_serializable = meta.copy() | |
| # # Step 2: Serialize the dictionary to a JSON string | |
| # meta_str = json.dumps(flatten_meta(meta_serializable), indent=4) | |
| # meta_array = np.append(meta_array, meta_str) | |
| # if flatmaps_dset is None: | |
| # # Initialize datasets with unlimited (None) maxshape along the first axis | |
| # flatmaps_shape = (0,) + images.shape[1:] | |
| # flatmaps_maxshape = (None,) + images.shape[1:] | |
| # flatmaps_dset = h5f.create_dataset( | |
| # 'flatmaps', | |
| # shape=flatmaps_shape, | |
| # maxshape=flatmaps_maxshape, | |
| # dtype=np.float16, | |
| # chunks=True # Enable chunking for efficient resizing | |
| # ) | |
| # # Resize datasets to accommodate new data | |
| # flatmaps_dset.resize(total_samples + batch_size, axis=0) | |
| # # Write data to the datasets | |
| # flatmaps_dset[total_samples:total_samples + batch_size] = images.numpy().astype(np.float16) | |
| # total_samples += batch_size | |
| # print(f"Processed {total_samples} samples") | |
| # np.save('metadata_test_HCP_raw_flatmaps.npy', meta_array) | |
| # import h5py | |
| # meta_array = np.array([], dtype=object) | |
| # # Open an HDF5 file in write mode | |
| # with h5py.File('test_hcp_raw_flatmaps.hdf5', 'w') as h5f: | |
| # flatmaps_dset = None | |
| # total_samples = 0 | |
| # for i, batch in tqdm(enumerate(test_dl), total = 12000): | |
| # images = batch['image'][0] | |
| # meta = batch['meta'] | |
| # batch_size = images.shape[0] | |
| # meta_serializable = meta.copy() | |
| # # Step 2: Serialize the dictionary to a JSON string | |
| # meta_str = json.dumps(flatten_meta(meta_serializable), indent=4) | |
| # meta_array = np.append(meta_array, meta_str) | |
| # if flatmaps_dset is None: | |
| # # Initialize datasets with unlimited (None) maxshape along the first axis | |
| # flatmaps_shape = (0,) + images.shape[1:] | |
| # flatmaps_maxshape = (None,) + images.shape[1:] | |
| # flatmaps_dset = h5f.create_dataset( | |
| # 'flatmaps', | |
| # shape=flatmaps_shape, | |
| # maxshape=flatmaps_maxshape, | |
| # dtype=np.float16, | |
| # chunks=True # Enable chunking for efficient resizing | |
| # ) | |
| # # Resize datasets to accommodate new data | |
| # flatmaps_dset.resize(total_samples + batch_size, axis=0) | |
| # # Write data to the datasets | |
| # flatmaps_dset[total_samples:total_samples + batch_size] = images.numpy().astype(np.float16) | |
| # total_samples += batch_size | |
| # print(f"Processed {total_samples} samples") | |
| # np.save('metadata_train_HCP_raw_flatmaps.npy', meta_array) | |
| # ### Data | |
| # In[5]: | |
| f_train = h5py.File('/weka/proj-fmri/ckadirt/fMRI-foundation-model/src/train_hcp_raw_flatmaps.hdf5', 'r') | |
| flatmaps_train = f_train['flatmaps'] | |
| f_test = h5py.File('/weka/proj-fmri/ckadirt/fMRI-foundation-model/src/test_hcp_raw_flatmaps.hdf5', 'r') | |
| flatmaps_test = f_test['flatmaps'] | |
| metadata_train = np.load('/weka/proj-fmri/ckadirt/fMRI-foundation-model/src/metadata_train_HCP_raw_flatmaps.npy', allow_pickle=True) | |
| metadata_test = np.load('/weka/proj-fmri/ckadirt/fMRI-foundation-model/src/metadata_test_HCP_raw_flatmaps.npy', allow_pickle=True) | |
| # In[6]: | |
| # import argparse | |
| # import json | |
| # import os | |
| # import pickle | |
| # from pathlib import Path | |
| # import pandas as pd | |
| # import numpy as np | |
| # from sklearn.decomposition import PCA | |
| # from sklearn.linear_model import LogisticRegressionCV | |
| # from sklearn.model_selection import train_test_split | |
| # from sklearn.preprocessing import LabelEncoder | |
| # target = "trial_type" | |
| # print(f"Target: {target}") | |
| # # train_features = pd.read_parquet(f"{outdir}/{parquet_folder}/HCP/train.parquet") | |
| # # test_features = pd.read_parquet(f"{outdir}/{parquet_folder}/HCP_/test.parquet") | |
| # # print(f"train: {train_features.shape}, test: {test_features.shape}") | |
| # # print(f"test: {test_features.shape}") | |
| # X_train = np.array(flatmaps_train[0:5000]) | |
| # # flatten the flatmaps | |
| # X_train = X_train.reshape(X_train.shape[0], -1) | |
| # X_test = np.array(flatmaps_test[0:1000]) | |
| # X_test = X_test.reshape(X_test.shape[0], -1) | |
| # print(f"X_train: {X_train.shape}, X_test: {X_test.shape}") | |
| # print(f"X_test: {X_test.shape}") | |
| # # if target == "task": | |
| # # labels_train = train_features["task"].str.rstrip("1234").values | |
| # # labels_test = test_features["task"].str.rstrip("1234").values | |
| # # elif target == "trial_type": | |
| # # labels_train = train_features["trial_type"].values | |
| # # labels_test = test_features["trial_type"].values | |
| # labels_train = [json.loads(string)['trial_type'] for string in metadata_train[0:5000]] | |
| # labels_test = [json.loads(string)['trial_type'] for string in metadata_test[0:1000]] | |
| # label_enc = LabelEncoder() | |
| # y_train = label_enc.fit_transform(labels_train) | |
| # y_test = label_enc.transform(labels_test) | |
| # print(f"classes ({len(label_enc.classes_)}): {label_enc.classes_}") | |
| # print( | |
| # f"\ny_train: {y_train.shape} {y_train[:20]}\n" | |
| # f"y_test: {y_test.shape} {y_test[:20]}" | |
| # ) | |
| # # del train_features, test_features | |
| # train_ind, val_ind = train_test_split( | |
| # np.arange(len(X_train)), train_size=0.9, random_state=42 | |
| # ) | |
| # print( | |
| # f"\ntrain_ind: {len(train_ind)} {train_ind[:10]}\n" | |
| # f"val_ind: {len(val_ind)} {val_ind[:10]}" | |
| # ) | |
| # X_train, X_val = X_train[train_ind], X_train[val_ind] | |
| # y_train, y_val = y_train[train_ind], y_train[val_ind] | |
| # print("Fitting PCA projection") | |
| # pca = PCA(n_components=384, whiten=True, svd_solver="randomized") | |
| # pca.fit(X_train) | |
| # X_train = pca.transform(X_train) | |
| # X_val = pca.transform(X_val) | |
| # X_test = pca.transform(X_test) | |
| # print("Fitting logistic regression") | |
| # clf = LogisticRegressionCV() | |
| # clf.fit(X_train, y_train) | |
| # train_acc = clf.score(X_train, y_train) | |
| # val_acc = clf.score(X_val, y_val) | |
| # test_acc = clf.score(X_test, y_test) | |
| # result = { | |
| # "target": target, | |
| # "train_acc": train_acc, | |
| # "val_acc": val_acc, | |
| # "test_acc": test_acc, | |
| # } | |
| # print(f"Done:\n{json.dumps(result)}") | |
| # with open(f"{outdir}/{parquet_folder}/HCP/downstream.json", 'w') as out_json: | |
| # json.dump(result, out_json) | |
| # ### Create the dataloader | |
| # In[7]: | |
| from torch.utils.data import Dataset, DataLoader | |
| class HCPFlatDataset(Dataset): | |
| def __init__(self, flatmaps, metadata): | |
| self.flatmaps = flatmaps | |
| self.metadata = metadata | |
| def __len__(self): | |
| return len(self.metadata) | |
| def __getitem__(self, idx): | |
| return self.flatmaps[idx], json.loads(self.metadata[idx]) | |
| # Loading to cpu for faster training, this can take several minutes. Remove this [:] if you want to move one at the time. | |
| train_dataset = HCPFlatDataset(flatmaps_train, metadata_train) | |
| train_dl = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=10) | |
| test_dataset = HCPFlatDataset(flatmaps_test, metadata_test) | |
| test_dl = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=0) | |
| # ### Load subject information | |
| # In[8]: | |
| # open the file containing subject information | |
| if target == "age" or target == "sex": | |
| subject_information_HCP_path = os.path.join(hcp_flat_path, "subjects_data_restricted.csv") | |
| try: | |
| subject_information_HCP = pd.read_csv(subject_information_HCP_path) | |
| except: | |
| try: | |
| subject_information_HCP = pd.read_csv('./unrestricted_clane9_4_23_2024_13_28_14.csv') | |
| except: | |
| assert False, "Subject information file not found" | |
| ###### This is for unrestricted | |
| # age_related_columns = [ | |
| # 'Age', 'PicSeq_AgeAdj', 'CardSort_AgeAdj', 'Flanker_AgeAdj', | |
| # 'ReadEng_AgeAdj', 'PicVocab_AgeAdj', 'ProcSpeed_AgeAdj', | |
| # 'CogFluidComp_AgeAdj', 'CogEarlyComp_AgeAdj', 'CogTotalComp_AgeAdj', | |
| # 'CogCrystalComp_AgeAdj', 'Endurance_AgeAdj', 'Dexterity_AgeAdj', | |
| # 'Strength_AgeAdj', 'Odor_AgeAdj', 'Taste_AgeAdj' | |
| # ] | |
| # sex_related_columns = [ | |
| # 'Gender' | |
| # ] | |
| ###### This is for restricted | |
| gender_related_columns = [ | |
| 'Gender' | |
| ] | |
| age_related_columns = [ | |
| 'Age_in_Yrs', | |
| 'Menstrual_AgeBegan', | |
| 'Menstrual_AgeIrreg', | |
| 'Menstrual_AgeStop', | |
| 'SSAGA_Alc_Age_1st_Use', | |
| 'SSAGA_TB_Age_1st_Cig', | |
| 'SSAGA_Mj_Age_1st_Use', | |
| 'Endurance_AgeAdj', | |
| 'Dexterity_AgeAdj', | |
| 'Strength_AgeAdj', | |
| 'PicSeq_AgeAdj', | |
| 'CardSort_AgeAdj', | |
| 'Flanker_AgeAdj', | |
| 'ReadEng_AgeAdj', | |
| 'PicVocab_AgeAdj', | |
| 'ProcSpeed_AgeAdj', | |
| 'Odor_AgeAdj', | |
| 'Taste_AgeAdj' | |
| ] | |
| # # show the first few rows of the subject information | |
| # subject_information_HCP[age_related_columns + sex_related_columns].head() | |
| # Handle missing values (e.g., impute with mean) | |
| mean_age = subject_information_HCP['Age_in_Yrs'].mean() | |
| # Initialize the scaler | |
| scaler = StandardScaler() | |
| # Perform z-score normalization | |
| subject_information_HCP['Age_in_Yrs_z'] = scaler.fit_transform(subject_information_HCP[['Age_in_Yrs']]) | |
| def get_label_unrestricted(subject_id: List[str], target: str, method_for_age: str = 'mean') -> List: | |
| """ | |
| Get the label for the given subject id and target. | |
| For sex 0 is F and 1 is M | |
| """ | |
| # convert to list of ints | |
| subject_id = [int(x) for x in subject_id] | |
| if target == "age": | |
| age_array = [] | |
| for subject in subject_id: | |
| c_age = subject_information_HCP[subject_information_HCP['Subject'] == subject]['Age'].values | |
| # if the subject is not in the subject information file trigger an error | |
| if len(c_age) == 0: | |
| assert False, f"Subject {subject} not found in subject information file" | |
| if len(c_age) > 1: | |
| print(f"Warning: Multiple entries for subject {subject}") | |
| c_age = c_age[0].split('-') | |
| if len(c_age) < 2: | |
| c_age = c_age[0].split('+') | |
| age_array.append(int(c_age[0])) | |
| else: | |
| if method_for_age == 'mean': | |
| age_array.append(np.mean([int(x) for x in c_age])) | |
| elif method_for_age == 'min': | |
| age_array.append(np.min([int(x) for x in c_age])) | |
| elif method_for_age == 'max': | |
| age_array.append(np.max([int(x) for x in c_age])) | |
| else: | |
| assert False, f"Method {method_for_age} not recognized" | |
| return np.array(age_array) | |
| elif target == 'sex': | |
| sex_array = [] | |
| for subject in subject_id: | |
| c_sex = subject_information_HCP[subject_information_HCP['Subject'] == subject]['Gender'].values | |
| # if the subject is not in the subject information file trigger an error | |
| if len(c_sex) == 0: | |
| assert False, f"Subject {subject} not found in subject information file" | |
| if len(c_sex) > 1: | |
| print(f"Warning: Multiple entries for subject {subject}") | |
| sex_array.append(int(c_sex[0] == 'M')) | |
| return sex_array | |
| def get_label_restricted(subject_id: List[str], target: str, normalized: bool = True) -> List: | |
| """ | |
| Get the label for the given subject id and target. | |
| For sex 0 is F and 1 is M | |
| """ | |
| # convert to list of ints | |
| subject_id = [int(x) for x in subject_id] | |
| if target == "age": | |
| age_array = [] | |
| for subject in subject_id: | |
| c_age = subject_information_HCP[subject_information_HCP['Subject'] == subject]['Age_in_Yrs' if not normalized else 'Age_in_Yrs_z'].values | |
| # if the subject is not in the subject information file trigger an error | |
| if len(c_age) == 0: | |
| assert False, f"Subject {subject} not found in subject information file" | |
| if len(c_age) > 1: | |
| print(f"Warning: Multiple entries for subject {subject}") | |
| age_array.append(np.int8(c_age[0])) | |
| return np.array(age_array) | |
| elif target == 'sex': | |
| sex_array = [] | |
| for subject in subject_id: | |
| c_sex = subject_information_HCP[subject_information_HCP['Subject'] == subject]['Gender'].values | |
| # if the subject is not in the subject information file trigger an error | |
| if len(c_sex) == 0: | |
| assert False, f"Subject {subject} not found in subject information file" | |
| if len(c_sex) > 1: | |
| print(f"Warning: Multiple entries for subject {subject}") | |
| sex_array.append(int(c_sex[0] == 'M')) | |
| return sex_array | |
| # In[9]: | |
| from sklearn.preprocessing import LabelEncoder | |
| if target == "trial_type": | |
| INCLUDE_CONDS = { | |
| "fear", | |
| "neut", | |
| "math", | |
| "story", | |
| "lf", | |
| "lh", | |
| "rf", | |
| "rh", | |
| "t", | |
| "match", | |
| "relation", | |
| "mental", | |
| "rnd", | |
| "0bk_body", | |
| "2bk_body", | |
| "0bk_faces", | |
| "2bk_faces", | |
| "0bk_places", | |
| "2bk_places", | |
| "0bk_tools", | |
| "2bk_tools", | |
| } | |
| # test_data = [] | |
| # # Iterate over the DataLoader with a progress bar | |
| # for sample in tqdm(train_dl, desc="Processing samples"): | |
| # x = sample['image'] | |
| # y = sample['meta']['trial_type'] | |
| # key = sample['meta']['key'] | |
| # print(x.shape, y, key) | |
| # break | |
| # Initialize the label encoder | |
| label_encoder = LabelEncoder() | |
| label_encoder.fit(sorted(INCLUDE_CONDS)) # Ensure consistent ordering | |
| num_classes = len(label_encoder.classes_) | |
| print(f"Number of classes: {num_classes}") | |
| # In[10]: | |
| # for sample in tqdm(train_dl): | |
| # x = sample[0] | |
| # subject_id = sample[1]['sub'] | |
| # # benchmark time | |
| # start = time.time() | |
| # y = get_label(subject_id, 'age') | |
| # end = time.time() | |
| # print(f"Time taken: {end - start}") | |
| # print(x.shape, y, subject_id, torch.Tensor(y).shape) | |
| # break | |
| # ### Create pytorch model | |
| # In[11]: | |
| class LinearClassifier(nn.Module): | |
| def __init__(self, input_dim, num_classes): | |
| super(LinearClassifier, self).__init__() | |
| self.linear = nn.Linear(input_dim, num_classes) | |
| def forward(self, x): | |
| # Flatten the input except for the batch dimension | |
| x = x.view(x.size(0), -1) | |
| out = self.linear(x) | |
| return out # Raw logits | |
| # Determine the input dimension from a single sample | |
| # Assuming images are of shape [1, 16, 144, 320] | |
| sample_batch = next(iter(train_dl)) | |
| sample_image = sample_batch[0][0] # Shape: [1, 16, 144, 320] | |
| input_dim = sample_image.view(-1).size(0) | |
| print(f"Input dimension: {input_dim}") | |
| # In[12]: | |
| # Initialize the model | |
| if target == "trial_type": | |
| model = LinearClassifier(input_dim=input_dim, num_classes=num_classes) | |
| criterion = nn.CrossEntropyLoss() | |
| elif target == "age": | |
| model = LinearClassifier(input_dim=input_dim, num_classes=1) | |
| criterion = nn.MSELoss() | |
| elif target == "sex": | |
| model = LinearClassifier(input_dim=input_dim, num_classes=1) | |
| criterion = nn.BCEWithLogitsLoss() | |
| # Move the model to GPU if available | |
| device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') | |
| model.to(device) | |
| # import schedulefree | |
| # optimizer = schedulefree.AdamWScheduleFree(model.parameters(), lr=learning_rate, weight_decay=weight_decay) | |
| optimizer = torch.optim.AdamW(model.parameters(), lr=max_lr, weight_decay=weight_decay) | |
| num_iterations_per_epoch = math.ceil(flatmaps_train.shape[0]/batch_size) | |
| if lr_scheduler_type == 'linear': | |
| lr_scheduler = torch.optim.lr_scheduler.LinearLR( | |
| optimizer, | |
| total_iters=int(np.floor(num_epochs*num_iterations_per_epoch)), | |
| last_epoch=-1 | |
| ) | |
| elif lr_scheduler_type == 'cycle': | |
| total_steps=int(np.floor(num_epochs*num_iterations_per_epoch)) | |
| print("total_steps", total_steps) | |
| lr_scheduler = torch.optim.lr_scheduler.OneCycleLR( | |
| optimizer, | |
| max_lr=max_lr, | |
| total_steps=total_steps, | |
| final_div_factor=1000, | |
| last_epoch=-1, pct_start=2/num_epochs | |
| ) | |
| # ### Wandb logging | |
| # In[13]: | |
| import wandb | |
| import uuid | |
| myuuid = uuid.uuid4() | |
| str(myuuid) | |
| if utils.is_interactive(): | |
| print("Running in interactive notebook. Disabling W&B and ckpt saving.") | |
| wandb_log = False | |
| save_ckpt = False | |
| if wandb_log: | |
| wandb_project = 'fMRI-foundation-model' | |
| wandb_config = { | |
| "model_name": f"HCPflat_raw_{target}", | |
| "batch_size": batch_size, | |
| "weight_decay": weight_decay, | |
| "num_epochs": num_epochs, | |
| "seed": seed, | |
| "lr_scheduler_type": lr_scheduler_type, | |
| "save_ckpt": save_ckpt, | |
| "seed": seed, | |
| "max_lr": max_lr, | |
| "target": target, | |
| "num_workers": num_workers, | |
| "weight_decay": weight_decay | |
| } | |
| print("wandb_config:\n", wandb_config) | |
| random_id = random.randint(0, 100000) | |
| wandb_id = "HCPflat_raw" + f"_{model_suffix}_{target}_{myuuid}" | |
| print("wandb_id:", wandb_id) | |
| wandb.init( | |
| id=wandb_id, | |
| project=wandb_project, | |
| name="HCPflat_raw"+ f"_{model_suffix}_{target}", | |
| config=wandb_config, | |
| resume="allow", | |
| ) | |
| # ### Training loop | |
| # In[23]: | |
| for epoch in range(num_epochs): | |
| running_train_loss = 0.0 | |
| correct_train = 0 | |
| mse_age_train = 0.0 | |
| total_train = 0 | |
| step = 0 | |
| # Training Phase | |
| model.train() | |
| optimizer.zero_grad() # Reset gradients before starting training | |
| for batch in tqdm(train_dl, desc=f"Epoch {epoch+1}/{num_epochs} - Training"): | |
| optimizer.zero_grad() | |
| images = batch[0].to(device).float() # Shape: [batch_size, 1, 16, 144, 320] | |
| # Prepare labels based on target type | |
| if target == "trial_type": | |
| labels = batch[1]['trial_type'] # List of labels | |
| labels = label_encoder.transform(labels) | |
| labels = torch.tensor(labels, dtype=torch.long).to(device) # Shape: [batch_size] | |
| elif target == "age": | |
| labels = get_label_restricted(batch[1]['sub'], 'age') | |
| labels = torch.tensor(labels, dtype=torch.float).to(device) # Shape: [batch_size] | |
| elif target == "sex": | |
| labels = get_label_restricted(batch[1]['sub'], 'sex') | |
| labels = torch.tensor(labels, dtype=torch.float).to(device) # Shape: [batch_size] | |
| # labels = labels.unsqueeze(1) | |
| # Forward pass | |
| outputs = model(images) # Output shape depends on the target | |
| # Compute loss | |
| if target in ["trial_type", "sex"]: | |
| # For classification, ensure outputs are logits | |
| loss = criterion(outputs, labels.squeeze()) | |
| elif target == "age": | |
| # For regression, ensure outputs are single values | |
| loss = criterion(outputs.squeeze(), labels.squeeze()) | |
| # Backward pass and optimization | |
| loss.backward() | |
| optimizer.step() | |
| # Accumulate loss | |
| running_train_loss += loss.item() * images.size(0) | |
| # Calculate and accumulate metrics | |
| if target == "trial_type": | |
| _, predicted = torch.max(outputs, 1) | |
| correct_train += (predicted == labels).sum().item() | |
| elif target == "age": | |
| mse_age_train += (torch.sum((outputs.squeeze() - labels) ** 2).item()) / outputs.shape[0] | |
| elif target == "sex": | |
| threshold = 0.5 | |
| predicted = (torch.sigmoid(outputs) > threshold).float() | |
| correct_train += (predicted == labels).sum().item() | |
| total_train += labels.size(0) | |
| step += 1 | |
| # Print intermediate metrics every 100 steps | |
| if step % 100 == 0: | |
| if target in ["trial_type", "sex"]: | |
| current_accuracy = 100 * correct_train / total_train if total_train > 0 else 0.0 | |
| print(f"Step [{step}/{len(train_dl)}] - Training Loss: {loss.item():.4f} - Training Accuracy: {current_accuracy:.2f}%") | |
| elif target == "age": | |
| current_mse = mse_age_train / total_train if total_train > 0 else 0.0 | |
| print(f"Step [{step}/{len(train_dl)}] - Training Loss: {loss.item():.4f} - Training MSE: {current_mse:.4f}") | |
| if lr_scheduler_type is not None: | |
| lr_scheduler.step() | |
| # Calculate epoch-level metrics | |
| epoch_train_loss = running_train_loss / total_train if total_train > 0 else 0.0 | |
| if target in ["trial_type", "sex"]: | |
| train_accuracy = 100 * correct_train / total_train if total_train > 0 else 0.0 | |
| elif target == "age": | |
| train_mse = mse_age_train / total_train if total_train > 0 else 0.0 | |
| # Validation Phase | |
| model.eval() | |
| running_val_loss = 0.0 | |
| correct_val = 0 | |
| mse_age_val = 0.0 | |
| total_val = 0 | |
| with torch.no_grad(): | |
| for batch in tqdm(test_dl, desc=f"Epoch {epoch+1}/{num_epochs} - Validation"): | |
| images = batch[0].to(device).float() # Removed unsqueeze(1) unless specifically needed | |
| # Prepare labels based on target type | |
| if target == "trial_type": | |
| labels = batch[1]['trial_type'] # List of labels | |
| labels = label_encoder.transform(labels) | |
| labels = torch.tensor(labels, dtype=torch.long).to(device) # Shape: [batch_size] | |
| elif target == "age": | |
| labels = get_label_restricted(batch[1]['sub'], 'age') | |
| labels = torch.tensor(labels, dtype=torch.float).to(device) # Shape: [batch_size] | |
| elif target == "sex": | |
| labels = get_label_restricted(batch[1]['sub'], 'sex') | |
| labels = torch.tensor(labels, dtype=torch.float).to(device) # Shape: [batch_size] | |
| # labels = labels.unsqueeze(1) | |
| # Forward pass | |
| outputs = model(images) | |
| # Compute loss | |
| if target in ["trial_type", "sex"]: | |
| loss = criterion(outputs, labels.squeeze()) | |
| elif target == "age": | |
| loss = criterion(outputs.squeeze(), labels.squeeze()) | |
| # Accumulate loss | |
| running_val_loss += loss.item() * images.size(0) | |
| # Calculate and accumulate metrics | |
| if target == "trial_type": | |
| _, predicted = torch.max(outputs, 1) | |
| correct_val += (predicted == labels).sum().item() | |
| elif target == "age": | |
| mse_age_val += (torch.sum((outputs.squeeze() - labels) ** 2).item()) / outputs.shape[0] | |
| elif target == "sex": | |
| threshold = 0.5 | |
| predicted = (torch.sigmoid(outputs) > threshold).float() | |
| correct_val += (predicted == labels).sum().item() | |
| total_val += labels.size(0) | |
| # Calculate epoch-level validation metrics | |
| epoch_val_loss = running_val_loss / total_val if total_val > 0 else 0.0 | |
| if target in ["trial_type", "sex"]: | |
| val_accuracy = 100 * correct_val / total_val if total_val > 0 else 0.0 | |
| elif target == "age": | |
| val_mse = mse_age_val / total_val if total_val > 0 else 0.0 | |
| # Print epoch-level metrics | |
| if target in ["trial_type", "sex"]: | |
| print(f"Epoch [{epoch+1}/{num_epochs}] " | |
| f"- Training Loss: {epoch_train_loss:.4f}, Training Accuracy: {train_accuracy:.2f}% " | |
| f"- Validation Loss: {epoch_val_loss:.4f}, Validation Accuracy: {val_accuracy:.2f}%") | |
| elif target == "age": | |
| print(f"Epoch [{epoch+1}/{num_epochs}] " | |
| f"- Training Loss: {epoch_train_loss:.4f}, Training MSE: {train_mse:.4f} " | |
| f"- Validation Loss: {epoch_val_loss:.4f}, Validation MSE: {val_mse:.4f}") | |
| # Log metrics with wandb | |
| if wandb_log: | |
| log_dict = { | |
| "epoch_train_loss": epoch_train_loss, | |
| "epoch_val_loss": epoch_val_loss, | |
| } | |
| if target in ["trial_type", "sex"]: | |
| log_dict.update({ | |
| f"train_accuracy_{target}": train_accuracy, | |
| f"val_accuracy_{target}": val_accuracy, | |
| }) | |
| elif target == "age": | |
| log_dict.update({ | |
| f"train_mse_{target}": train_mse, | |
| f"val_mse_{target}": val_mse, | |
| }) | |
| wandb.log(log_dict) | |
| # Save checkpoint if required | |
| if save_ckpt: | |
| outdir = os.path.abspath(f'checkpoints/{"HCPflat_raw"+ f"_{model_suffix}_{target}"}_{random_id}') | |
| os.makedirs(outdir, exist_ok=True) | |
| print("Saving checkpoint to:", outdir) | |
| # Save model state | |
| torch.save(model.state_dict(), os.path.join(outdir, "model.pth")) | |
| # Save configuration | |
| with open(os.path.join(outdir, "config.yaml"), 'w') as f: | |
| yaml.dump(wandb_config, f) | |
| print(f"Model and config saved to {outdir}") | |
| # In[ ]: | |
| # loss = criterion(outputs, labels) | |
| # In[ ]: | |
| # outputs.shape, labels.squeeze().shape | |
| # In[ ]: | |
| # loss = criterion(outputs, labels) | |
| # In[ ]: | |
| # if target == 'trial_type': | |
| # key = 'trial_type' | |
| # elif target == 'sex' or target == 'age': | |
| # key = 'sub' | |
| # y_train = [json.loads(metadata_train[i])[key] for i in range(0,2000)] | |
| # y_val = [json.loads(metadata_train[i])[key] for i in range(10000,11000)] | |
| # y_test = [json.loads(metadata_test[i])[key] for i in range(0,1000)] | |
| # In[ ]: | |
| # In[ ]: | |
| # X_train = flatmaps_train[0:2000] | |
| # X_val = flatmaps_train[10000:11000] | |
| # X_test = flatmaps_test[0:1000] | |
| # y_test = get_label_restricted(y_test, target = 'sex') | |
| # y_train = get_label_restricted(y_train, target = 'sex') | |
| # y_val = get_label_restricted(y_val, target = 'sex') | |
| # # y_train = label_encoder.transform(y_train) | |
| # # y_val = label_encoder.transform(y_val) | |
| # # y_test = label_encoder.transform(y_test) | |
| # In[ ]: | |
| # X_train, X_val, X_test = X_train.reshape(X_train.shape[0],-1), X_val.reshape(X_val.shape[0],-1), X_test.reshape(X_test.shape[0],-1) | |
| # In[ ]: | |
| # X_train.shape | |
| # In[ ]: | |
| # import numpy as np | |
| # import matplotlib.pyplot as plt | |
| # from sklearn.preprocessing import StandardScaler | |
| # from sklearn.decomposition import PCA | |
| # from sklearn.linear_model import LogisticRegressionCV | |
| # from sklearn.metrics import accuracy_score | |
| # # Supongamos que ya tienes tus datos divididos: | |
| # # X_train, y_train, X_val, y_val, X_test, y_test | |
| # # 1. Estandarizar los Datos | |
| # print("Estandarizando los datos...") | |
| # scaler = StandardScaler() | |
| # X_train_scaled = scaler.fit_transform(X_train) | |
| # X_val_scaled = scaler.transform(X_val) | |
| # X_test_scaled = scaler.transform(X_test) | |
| # # 2. Aplicar PCA | |
| # print("Aplicando PCA...") | |
| # # Decidir el n煤mero de componentes. Por ejemplo, mantener el 95% de la varianza. | |
| # pca = PCA(n_components=0.95, svd_solver='full') # 'full' para compatibilidad | |
| # X_train_pca = pca.fit_transform(X_train_scaled) | |
| # X_val_pca = pca.transform(X_val_scaled) | |
| # X_test_pca = pca.transform(X_test_scaled) | |
| # print(f"N煤mero de componentes seleccionados: {pca.n_components_}") | |
| # # Opcional: Visualizar la varianza explicada | |
| # cumulative_variance = np.cumsum(pca.explained_variance_ratio_) | |
| # plt.figure(figsize=(8, 5)) | |
| # plt.plot(range(1, len(cumulative_variance) + 1), cumulative_variance, marker='o', linestyle='--') | |
| # plt.xlabel('N煤mero de Componentes') | |
| # plt.ylabel('Varianza Acumulada') | |
| # plt.title('Varianza Explicada por PCA') | |
| # plt.grid(True) | |
| # plt.show() | |
| # # 3. Entrenar el Modelo de Regresi贸n Log铆stica con Validaci贸n Cruzada | |
| # print("Entrenando el modelo de Regresi贸n Log铆stica con PCA...") | |
| # clf = LogisticRegressionCV(max_iter=100, cv=5, scoring='accuracy', n_jobs=-1) | |
| # clf.fit(X_train_pca, y_train) | |
| # # 4. Evaluar el Modelo | |
| # print("Calculando precisi贸n...") | |
| # # Precisi贸n en entrenamiento | |
| # y_train_pred = clf.predict(X_train_pca) | |
| # train_acc = accuracy_score(y_train, y_train_pred) | |
| # # Precisi贸n en validaci贸n | |
| # y_val_pred = clf.predict(X_val_pca) | |
| # val_acc = accuracy_score(y_val, y_val_pred) | |
| # # Precisi贸n en prueba | |
| # y_test_pred = clf.predict(X_test_pca) | |
| # test_acc = accuracy_score(y_test, y_test_pred) | |
| # print(f"Precisi贸n en entrenamiento: {train_acc:.4f}") | |
| # print(f"Precisi贸n en validaci贸n: {val_acc:.4f}") | |
| # print(f"Precisi贸n en prueba: {test_acc:.4f}") | |
| # In[ ]: | |
| # X_train_scaled.shape | |
| # In[ ]: | |
| # from sklearn.linear_model import LogisticRegressionCV, Ridge | |
| # print("fitting") | |
| # clf = LogisticRegressionCV(max_iter=100) | |
| # clf.fit(X_train, y_train) | |
| # print("calculating accuracy") | |
| # train_acc = clf.score(X_train, y_train) | |
| # val_acc = clf.score(X_val, y_val) | |
| # test_acc = clf.score(X_test, y_test) | |
| # In[ ]: | |
| # print(train_acc, val_acc, test_acc) | |
| # In[ ]: | |
| ### AGE | |
| # Sklearn No pca just 1k examples: 1.0 0.534 0.5066666666666667 | |
| # Sklearn Pca 1800 features, 2k examples 1.0000 0.5130 0.4590 | |
| # All data pytorch 0.93 no_val 0.55 | |
| ### TRIAL TYPE | |
| # Sklearn No pca just 1k examples: 1.0 0.61 0.63 | |
| # Sklearn Pca 500 features, 2k examples 1.0000 ~0.73 ~0.73 | |
| # All data pytorch 0.9911 no_val 0.94 | |
| # In[ ]: | |
| # a = model.linear.weight[0][10:20] | |
| # a | |
| # In[ ]: | |
| # loss = criterion(outputs, labels.unsqueeze(1)) | |
| # loss | |